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