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