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