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