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