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