blob: 8d2c127a72dd35cbc2ba3848eedea417e81ba793 [file] [log] [blame]
Olli Etuahocccf2b02017-07-05 14:50:54 +03001//
2// Copyright (c) 2002-2014 The ANGLE Project Authors. All rights reserved.
3// Use of this source code is governed by a BSD-style license that can be
4// found in the LICENSE file.
5//
6
Olli Etuahocccf2b02017-07-05 14:50:54 +03007#include "compiler/translator/SymbolTable.h"
Olli Etuahoc26214d2018-03-16 10:43:11 +02008#include "compiler/translator/tree_util/IntermTraverse.h"
Olli Etuahocccf2b02017-07-05 14:50:54 +03009
10namespace sh
11{
12
13namespace
14{
15
Olli Etuaho1bb85282017-12-14 13:39:53 +020016void OutputFunction(TInfoSinkBase &out, const char *str, const TFunction *func)
17{
18 const char *internal =
19 (func->symbolType() == SymbolType::AngleInternal) ? " (internal function)" : "";
Olli Etuahobed35d72017-12-20 16:36:26 +020020 out << str << internal << ": " << func->name() << " (symbol id " << func->uniqueId().get()
Olli Etuaho1bb85282017-12-14 13:39:53 +020021 << ")";
22}
23
Olli Etuahocccf2b02017-07-05 14:50:54 +030024// Two purposes:
25// 1. Show an example of how to iterate tree. Functions can also directly call traverse() on
26// children themselves to have finer grained control over the process than shown here, though
27// that's not recommended if it can be avoided.
28// 2. Print out a text based description of the tree.
29
30// The traverser subclass is used to carry along data from node to node in the traversal.
31class TOutputTraverser : public TIntermTraverser
32{
33 public:
34 TOutputTraverser(TInfoSinkBase &out) : TIntermTraverser(true, false, false), mOut(out) {}
35
36 protected:
37 void visitSymbol(TIntermSymbol *) override;
38 void visitConstantUnion(TIntermConstantUnion *) override;
39 bool visitSwizzle(Visit visit, TIntermSwizzle *node) override;
40 bool visitBinary(Visit visit, TIntermBinary *) override;
41 bool visitUnary(Visit visit, TIntermUnary *) override;
42 bool visitTernary(Visit visit, TIntermTernary *node) override;
43 bool visitIfElse(Visit visit, TIntermIfElse *node) override;
44 bool visitSwitch(Visit visit, TIntermSwitch *node) override;
45 bool visitCase(Visit visit, TIntermCase *node) override;
Olli Etuahod4bd9632018-03-08 16:32:44 +020046 void visitFunctionPrototype(TIntermFunctionPrototype *node) override;
Olli Etuahocccf2b02017-07-05 14:50:54 +030047 bool visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node) override;
48 bool visitAggregate(Visit visit, TIntermAggregate *) override;
49 bool visitBlock(Visit visit, TIntermBlock *) override;
50 bool visitInvariantDeclaration(Visit visit, TIntermInvariantDeclaration *node) override;
51 bool visitDeclaration(Visit visit, TIntermDeclaration *node) override;
52 bool visitLoop(Visit visit, TIntermLoop *) override;
53 bool visitBranch(Visit visit, TIntermBranch *) override;
54
55 TInfoSinkBase &mOut;
56};
57
58//
59// Helper functions for printing, not part of traversing.
60//
61void OutputTreeText(TInfoSinkBase &out, TIntermNode *node, const int depth)
62{
63 int i;
64
65 out.location(node->getLine().first_file, node->getLine().first_line);
66
67 for (i = 0; i < depth; ++i)
68 out << " ";
69}
70
71//
72// The rest of the file are the traversal functions. The last one
73// is the one that starts the traversal.
74//
75// Return true from interior nodes to have the external traversal
76// continue on to children. If you process children yourself,
77// return false.
78//
79
80void TOutputTraverser::visitSymbol(TIntermSymbol *node)
81{
82 OutputTreeText(mOut, node, mDepth);
83
Olli Etuaho8b5e8fd2017-12-15 14:59:15 +020084 if (node->variable().symbolType() == SymbolType::Empty)
85 {
86 mOut << "''";
87 }
88 else
89 {
90 mOut << "'" << node->getName() << "' ";
91 }
Olli Etuahob6af22b2017-12-15 14:05:44 +020092 mOut << "(symbol id " << node->uniqueId().get() << ") ";
Olli Etuahocccf2b02017-07-05 14:50:54 +030093 mOut << "(" << node->getCompleteString() << ")";
94 mOut << "\n";
95}
96
97bool TOutputTraverser::visitSwizzle(Visit visit, TIntermSwizzle *node)
98{
99 OutputTreeText(mOut, node, mDepth);
100 mOut << "vector swizzle (";
101 node->writeOffsetsAsXYZW(&mOut);
102 mOut << ")";
103
104 mOut << " (" << node->getCompleteString() << ")";
105 mOut << "\n";
106 return true;
107}
108
109bool TOutputTraverser::visitBinary(Visit visit, TIntermBinary *node)
110{
111 OutputTreeText(mOut, node, mDepth);
112
113 switch (node->getOp())
114 {
115 case EOpComma:
116 mOut << "comma";
117 break;
118 case EOpAssign:
119 mOut << "move second child to first child";
120 break;
121 case EOpInitialize:
122 mOut << "initialize first child with second child";
123 break;
124 case EOpAddAssign:
125 mOut << "add second child into first child";
126 break;
127 case EOpSubAssign:
128 mOut << "subtract second child into first child";
129 break;
130 case EOpMulAssign:
131 mOut << "multiply second child into first child";
132 break;
133 case EOpVectorTimesMatrixAssign:
134 mOut << "matrix mult second child into first child";
135 break;
136 case EOpVectorTimesScalarAssign:
137 mOut << "vector scale second child into first child";
138 break;
139 case EOpMatrixTimesScalarAssign:
140 mOut << "matrix scale second child into first child";
141 break;
142 case EOpMatrixTimesMatrixAssign:
143 mOut << "matrix mult second child into first child";
144 break;
145 case EOpDivAssign:
146 mOut << "divide second child into first child";
147 break;
148 case EOpIModAssign:
149 mOut << "modulo second child into first child";
150 break;
151 case EOpBitShiftLeftAssign:
152 mOut << "bit-wise shift first child left by second child";
153 break;
154 case EOpBitShiftRightAssign:
155 mOut << "bit-wise shift first child right by second child";
156 break;
157 case EOpBitwiseAndAssign:
158 mOut << "bit-wise and second child into first child";
159 break;
160 case EOpBitwiseXorAssign:
161 mOut << "bit-wise xor second child into first child";
162 break;
163 case EOpBitwiseOrAssign:
164 mOut << "bit-wise or second child into first child";
165 break;
166
167 case EOpIndexDirect:
168 mOut << "direct index";
169 break;
170 case EOpIndexIndirect:
171 mOut << "indirect index";
172 break;
173 case EOpIndexDirectStruct:
174 mOut << "direct index for structure";
175 break;
176 case EOpIndexDirectInterfaceBlock:
177 mOut << "direct index for interface block";
178 break;
179
180 case EOpAdd:
181 mOut << "add";
182 break;
183 case EOpSub:
184 mOut << "subtract";
185 break;
186 case EOpMul:
187 mOut << "component-wise multiply";
188 break;
189 case EOpDiv:
190 mOut << "divide";
191 break;
192 case EOpIMod:
193 mOut << "modulo";
194 break;
195 case EOpBitShiftLeft:
196 mOut << "bit-wise shift left";
197 break;
198 case EOpBitShiftRight:
199 mOut << "bit-wise shift right";
200 break;
201 case EOpBitwiseAnd:
202 mOut << "bit-wise and";
203 break;
204 case EOpBitwiseXor:
205 mOut << "bit-wise xor";
206 break;
207 case EOpBitwiseOr:
208 mOut << "bit-wise or";
209 break;
210
211 case EOpEqual:
212 mOut << "Compare Equal";
213 break;
214 case EOpNotEqual:
215 mOut << "Compare Not Equal";
216 break;
217 case EOpLessThan:
218 mOut << "Compare Less Than";
219 break;
220 case EOpGreaterThan:
221 mOut << "Compare Greater Than";
222 break;
223 case EOpLessThanEqual:
224 mOut << "Compare Less Than or Equal";
225 break;
226 case EOpGreaterThanEqual:
227 mOut << "Compare Greater Than or Equal";
228 break;
229
230 case EOpVectorTimesScalar:
231 mOut << "vector-scale";
232 break;
233 case EOpVectorTimesMatrix:
234 mOut << "vector-times-matrix";
235 break;
236 case EOpMatrixTimesVector:
237 mOut << "matrix-times-vector";
238 break;
239 case EOpMatrixTimesScalar:
240 mOut << "matrix-scale";
241 break;
242 case EOpMatrixTimesMatrix:
243 mOut << "matrix-multiply";
244 break;
245
246 case EOpLogicalOr:
247 mOut << "logical-or";
248 break;
249 case EOpLogicalXor:
250 mOut << "logical-xor";
251 break;
252 case EOpLogicalAnd:
253 mOut << "logical-and";
254 break;
255 default:
256 mOut << "<unknown op>";
257 }
258
259 mOut << " (" << node->getCompleteString() << ")";
260
261 mOut << "\n";
262
263 // Special handling for direct indexes. Because constant
264 // unions are not aware they are struct indexes, treat them
265 // here where we have that contextual knowledge.
266 if (node->getOp() == EOpIndexDirectStruct || node->getOp() == EOpIndexDirectInterfaceBlock)
267 {
268 node->getLeft()->traverse(this);
269
270 TIntermConstantUnion *intermConstantUnion = node->getRight()->getAsConstantUnion();
271 ASSERT(intermConstantUnion);
272
273 OutputTreeText(mOut, intermConstantUnion, mDepth + 1);
274
275 // The following code finds the field name from the constant union
Olli Etuahoea22b7a2018-01-04 17:09:11 +0200276 const TConstantUnion *constantUnion = intermConstantUnion->getConstantValue();
Olli Etuahocccf2b02017-07-05 14:50:54 +0300277 const TStructure *structure = node->getLeft()->getType().getStruct();
278 const TInterfaceBlock *interfaceBlock = node->getLeft()->getType().getInterfaceBlock();
279 ASSERT(structure || interfaceBlock);
280
281 const TFieldList &fields = structure ? structure->fields() : interfaceBlock->fields();
282
283 const TField *field = fields[constantUnion->getIConst()];
284
285 mOut << constantUnion->getIConst() << " (field '" << field->name() << "')";
286
287 mOut << "\n";
288
289 return false;
290 }
291
292 return true;
293}
294
295bool TOutputTraverser::visitUnary(Visit visit, TIntermUnary *node)
296{
297 OutputTreeText(mOut, node, mDepth);
298
299 switch (node->getOp())
300 {
301 // Give verbose names for ops that have special syntax and some built-in functions that are
302 // easy to confuse with others, but mostly use GLSL names for functions.
303 case EOpNegative:
304 mOut << "Negate value";
305 break;
306 case EOpPositive:
307 mOut << "Positive sign";
308 break;
309 case EOpLogicalNot:
310 mOut << "negation";
311 break;
312 case EOpBitwiseNot:
313 mOut << "bit-wise not";
314 break;
315
316 case EOpPostIncrement:
317 mOut << "Post-Increment";
318 break;
319 case EOpPostDecrement:
320 mOut << "Post-Decrement";
321 break;
322 case EOpPreIncrement:
323 mOut << "Pre-Increment";
324 break;
325 case EOpPreDecrement:
326 mOut << "Pre-Decrement";
327 break;
328
Olli Etuahobb2bbfb2017-08-24 15:43:33 +0300329 case EOpArrayLength:
330 mOut << "Array length";
331 break;
332
Olli Etuahocccf2b02017-07-05 14:50:54 +0300333 case EOpLogicalNotComponentWise:
334 mOut << "component-wise not";
335 break;
336
337 default:
338 mOut << GetOperatorString(node->getOp());
339 break;
340 }
341
342 mOut << " (" << node->getCompleteString() << ")";
343
344 mOut << "\n";
345
346 return true;
347}
348
349bool TOutputTraverser::visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node)
350{
351 OutputTreeText(mOut, node, mDepth);
352 mOut << "Function Definition:\n";
353 mOut << "\n";
354 return true;
355}
356
357bool TOutputTraverser::visitInvariantDeclaration(Visit visit, TIntermInvariantDeclaration *node)
358{
359 OutputTreeText(mOut, node, mDepth);
360 mOut << "Invariant Declaration:\n";
361 return true;
362}
363
Olli Etuahod4bd9632018-03-08 16:32:44 +0200364void TOutputTraverser::visitFunctionPrototype(TIntermFunctionPrototype *node)
Olli Etuahocccf2b02017-07-05 14:50:54 +0300365{
366 OutputTreeText(mOut, node, mDepth);
Olli Etuahobeb6dc72017-12-14 16:03:03 +0200367 OutputFunction(mOut, "Function Prototype", node->getFunction());
Olli Etuahocccf2b02017-07-05 14:50:54 +0300368 mOut << " (" << node->getCompleteString() << ")";
369 mOut << "\n";
Olli Etuahod4bd9632018-03-08 16:32:44 +0200370 size_t paramCount = node->getFunction()->getParamCount();
371 for (size_t i = 0; i < paramCount; ++i)
372 {
373 const TVariable *param = node->getFunction()->getParam(i);
374 OutputTreeText(mOut, node, mDepth + 1);
375 mOut << "parameter: " << param->name() << " (" << param->getType().getCompleteString()
376 << ")";
377 }
Olli Etuahocccf2b02017-07-05 14:50:54 +0300378}
379
380bool TOutputTraverser::visitAggregate(Visit visit, TIntermAggregate *node)
381{
382 OutputTreeText(mOut, node, mDepth);
383
384 if (node->getOp() == EOpNull)
385 {
386 mOut.prefix(SH_ERROR);
387 mOut << "node is still EOpNull!\n";
388 return true;
389 }
390
391 // Give verbose names for some built-in functions that are easy to confuse with others, but
392 // mostly use GLSL names for functions.
393 switch (node->getOp())
394 {
395 case EOpCallFunctionInAST:
Olli Etuaho1bb85282017-12-14 13:39:53 +0200396 OutputFunction(mOut, "Call an user-defined function", node->getFunction());
Olli Etuahocccf2b02017-07-05 14:50:54 +0300397 break;
398 case EOpCallInternalRawFunction:
399 OutputFunction(mOut, "Call an internal function with raw implementation",
Olli Etuaho1bb85282017-12-14 13:39:53 +0200400 node->getFunction());
Olli Etuahocccf2b02017-07-05 14:50:54 +0300401 break;
402 case EOpCallBuiltInFunction:
Olli Etuaho1bb85282017-12-14 13:39:53 +0200403 OutputFunction(mOut, "Call a built-in function", node->getFunction());
Olli Etuahocccf2b02017-07-05 14:50:54 +0300404 break;
405
406 case EOpConstruct:
407 // The type of the constructor will be printed below.
408 mOut << "Construct";
409 break;
410
411 case EOpEqualComponentWise:
412 mOut << "component-wise equal";
413 break;
414 case EOpNotEqualComponentWise:
415 mOut << "component-wise not equal";
416 break;
417 case EOpLessThanComponentWise:
418 mOut << "component-wise less than";
419 break;
420 case EOpGreaterThanComponentWise:
421 mOut << "component-wise greater than";
422 break;
423 case EOpLessThanEqualComponentWise:
424 mOut << "component-wise less than or equal";
425 break;
426 case EOpGreaterThanEqualComponentWise:
427 mOut << "component-wise greater than or equal";
428 break;
429
430 case EOpDot:
431 mOut << "dot product";
432 break;
433 case EOpCross:
434 mOut << "cross product";
435 break;
436 case EOpMulMatrixComponentWise:
437 mOut << "component-wise multiply";
438 break;
439
440 default:
441 mOut << GetOperatorString(node->getOp());
442 break;
443 }
444
445 mOut << " (" << node->getCompleteString() << ")";
446
447 mOut << "\n";
448
449 return true;
450}
451
452bool TOutputTraverser::visitBlock(Visit visit, TIntermBlock *node)
453{
454 OutputTreeText(mOut, node, mDepth);
455 mOut << "Code block\n";
456
457 return true;
458}
459
460bool TOutputTraverser::visitDeclaration(Visit visit, TIntermDeclaration *node)
461{
462 OutputTreeText(mOut, node, mDepth);
463 mOut << "Declaration\n";
464
465 return true;
466}
467
468bool TOutputTraverser::visitTernary(Visit visit, TIntermTernary *node)
469{
470 OutputTreeText(mOut, node, mDepth);
471
472 mOut << "Ternary selection";
473 mOut << " (" << node->getCompleteString() << ")\n";
474
475 ++mDepth;
476
477 OutputTreeText(mOut, node, mDepth);
478 mOut << "Condition\n";
479 node->getCondition()->traverse(this);
480
481 OutputTreeText(mOut, node, mDepth);
482 if (node->getTrueExpression())
483 {
484 mOut << "true case\n";
485 node->getTrueExpression()->traverse(this);
486 }
487 if (node->getFalseExpression())
488 {
489 OutputTreeText(mOut, node, mDepth);
490 mOut << "false case\n";
491 node->getFalseExpression()->traverse(this);
492 }
493
494 --mDepth;
495
496 return false;
497}
498
499bool TOutputTraverser::visitIfElse(Visit visit, TIntermIfElse *node)
500{
501 OutputTreeText(mOut, node, mDepth);
502
503 mOut << "If test\n";
504
505 ++mDepth;
506
507 OutputTreeText(mOut, node, mDepth);
508 mOut << "Condition\n";
509 node->getCondition()->traverse(this);
510
511 OutputTreeText(mOut, node, mDepth);
512 if (node->getTrueBlock())
513 {
514 mOut << "true case\n";
515 node->getTrueBlock()->traverse(this);
516 }
517 else
518 {
519 mOut << "true case is null\n";
520 }
521
522 if (node->getFalseBlock())
523 {
524 OutputTreeText(mOut, node, mDepth);
525 mOut << "false case\n";
526 node->getFalseBlock()->traverse(this);
527 }
528
529 --mDepth;
530
531 return false;
532}
533
534bool TOutputTraverser::visitSwitch(Visit visit, TIntermSwitch *node)
535{
536 OutputTreeText(mOut, node, mDepth);
537
538 mOut << "Switch\n";
539
540 return true;
541}
542
543bool TOutputTraverser::visitCase(Visit visit, TIntermCase *node)
544{
545 OutputTreeText(mOut, node, mDepth);
546
547 if (node->getCondition() == nullptr)
548 {
549 mOut << "Default\n";
550 }
551 else
552 {
553 mOut << "Case\n";
554 }
555
556 return true;
557}
558
559void TOutputTraverser::visitConstantUnion(TIntermConstantUnion *node)
560{
561 size_t size = node->getType().getObjectSize();
562
563 for (size_t i = 0; i < size; i++)
564 {
565 OutputTreeText(mOut, node, mDepth);
Olli Etuahoea22b7a2018-01-04 17:09:11 +0200566 switch (node->getConstantValue()[i].getType())
Olli Etuahocccf2b02017-07-05 14:50:54 +0300567 {
568 case EbtBool:
Olli Etuahoea22b7a2018-01-04 17:09:11 +0200569 if (node->getConstantValue()[i].getBConst())
Olli Etuahocccf2b02017-07-05 14:50:54 +0300570 mOut << "true";
571 else
572 mOut << "false";
573
574 mOut << " ("
575 << "const bool"
576 << ")";
577 mOut << "\n";
578 break;
579 case EbtFloat:
Olli Etuahoea22b7a2018-01-04 17:09:11 +0200580 mOut << node->getConstantValue()[i].getFConst();
Olli Etuahocccf2b02017-07-05 14:50:54 +0300581 mOut << " (const float)\n";
582 break;
583 case EbtInt:
Olli Etuahoea22b7a2018-01-04 17:09:11 +0200584 mOut << node->getConstantValue()[i].getIConst();
Olli Etuahocccf2b02017-07-05 14:50:54 +0300585 mOut << " (const int)\n";
586 break;
587 case EbtUInt:
Olli Etuahoea22b7a2018-01-04 17:09:11 +0200588 mOut << node->getConstantValue()[i].getUConst();
Olli Etuahocccf2b02017-07-05 14:50:54 +0300589 mOut << " (const uint)\n";
590 break;
591 case EbtYuvCscStandardEXT:
592 mOut << getYuvCscStandardEXTString(
Olli Etuahoea22b7a2018-01-04 17:09:11 +0200593 node->getConstantValue()[i].getYuvCscStandardEXTConst());
Olli Etuahocccf2b02017-07-05 14:50:54 +0300594 mOut << " (const yuvCscStandardEXT)\n";
595 break;
596 default:
597 mOut.prefix(SH_ERROR);
598 mOut << "Unknown constant\n";
599 break;
600 }
601 }
602}
603
604bool TOutputTraverser::visitLoop(Visit visit, TIntermLoop *node)
605{
606 OutputTreeText(mOut, node, mDepth);
607
608 mOut << "Loop with condition ";
609 if (node->getType() == ELoopDoWhile)
610 mOut << "not ";
611 mOut << "tested first\n";
612
613 ++mDepth;
614
615 OutputTreeText(mOut, node, mDepth);
616 if (node->getCondition())
617 {
618 mOut << "Loop Condition\n";
619 node->getCondition()->traverse(this);
620 }
621 else
622 {
623 mOut << "No loop condition\n";
624 }
625
626 OutputTreeText(mOut, node, mDepth);
627 if (node->getBody())
628 {
629 mOut << "Loop Body\n";
630 node->getBody()->traverse(this);
631 }
632 else
633 {
634 mOut << "No loop body\n";
635 }
636
637 if (node->getExpression())
638 {
639 OutputTreeText(mOut, node, mDepth);
640 mOut << "Loop Terminal Expression\n";
641 node->getExpression()->traverse(this);
642 }
643
644 --mDepth;
645
646 return false;
647}
648
649bool TOutputTraverser::visitBranch(Visit visit, TIntermBranch *node)
650{
651 OutputTreeText(mOut, node, mDepth);
652
653 switch (node->getFlowOp())
654 {
655 case EOpKill:
656 mOut << "Branch: Kill";
657 break;
658 case EOpBreak:
659 mOut << "Branch: Break";
660 break;
661 case EOpContinue:
662 mOut << "Branch: Continue";
663 break;
664 case EOpReturn:
665 mOut << "Branch: Return";
666 break;
667 default:
668 mOut << "Branch: Unknown Branch";
669 break;
670 }
671
672 if (node->getExpression())
673 {
674 mOut << " with expression\n";
675 ++mDepth;
676 node->getExpression()->traverse(this);
677 --mDepth;
678 }
679 else
680 {
681 mOut << "\n";
682 }
683
684 return false;
685}
686
687} // anonymous namespace
688
689void OutputTree(TIntermNode *root, TInfoSinkBase &out)
690{
691 TOutputTraverser it(out);
692 ASSERT(root);
693 root->traverse(&it);
694}
695
696} // namespace sh