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