blob: 6798b35d6a564becb3f79a92371bd7e1b32703f4 [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
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
Olli Etuahobb2bbfb2017-08-24 15:43:33 +0300321 case EOpArrayLength:
322 mOut << "Array length";
323 break;
324
Olli Etuahocccf2b02017-07-05 14:50:54 +0300325 case EOpLogicalNotComponentWise:
326 mOut << "component-wise not";
327 break;
328
329 default:
330 mOut << GetOperatorString(node->getOp());
331 break;
332 }
333
334 mOut << " (" << node->getCompleteString() << ")";
335
336 mOut << "\n";
337
338 return true;
339}
340
341bool TOutputTraverser::visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node)
342{
343 OutputTreeText(mOut, node, mDepth);
344 mOut << "Function Definition:\n";
345 mOut << "\n";
346 return true;
347}
348
349bool TOutputTraverser::visitInvariantDeclaration(Visit visit, TIntermInvariantDeclaration *node)
350{
351 OutputTreeText(mOut, node, mDepth);
352 mOut << "Invariant Declaration:\n";
353 return true;
354}
355
356bool TOutputTraverser::visitFunctionPrototype(Visit visit, TIntermFunctionPrototype *node)
357{
358 OutputTreeText(mOut, node, mDepth);
359 OutputFunction(mOut, "Function Prototype", node->getFunctionSymbolInfo());
360 mOut << " (" << node->getCompleteString() << ")";
361 mOut << "\n";
362
363 return true;
364}
365
366bool TOutputTraverser::visitAggregate(Visit visit, TIntermAggregate *node)
367{
368 OutputTreeText(mOut, node, mDepth);
369
370 if (node->getOp() == EOpNull)
371 {
372 mOut.prefix(SH_ERROR);
373 mOut << "node is still EOpNull!\n";
374 return true;
375 }
376
377 // Give verbose names for some built-in functions that are easy to confuse with others, but
378 // mostly use GLSL names for functions.
379 switch (node->getOp())
380 {
381 case EOpCallFunctionInAST:
382 OutputFunction(mOut, "Call an user-defined function", node->getFunctionSymbolInfo());
383 break;
384 case EOpCallInternalRawFunction:
385 OutputFunction(mOut, "Call an internal function with raw implementation",
386 node->getFunctionSymbolInfo());
387 break;
388 case EOpCallBuiltInFunction:
389 OutputFunction(mOut, "Call a built-in function", node->getFunctionSymbolInfo());
390 break;
391
392 case EOpConstruct:
393 // The type of the constructor will be printed below.
394 mOut << "Construct";
395 break;
396
397 case EOpEqualComponentWise:
398 mOut << "component-wise equal";
399 break;
400 case EOpNotEqualComponentWise:
401 mOut << "component-wise not equal";
402 break;
403 case EOpLessThanComponentWise:
404 mOut << "component-wise less than";
405 break;
406 case EOpGreaterThanComponentWise:
407 mOut << "component-wise greater than";
408 break;
409 case EOpLessThanEqualComponentWise:
410 mOut << "component-wise less than or equal";
411 break;
412 case EOpGreaterThanEqualComponentWise:
413 mOut << "component-wise greater than or equal";
414 break;
415
416 case EOpDot:
417 mOut << "dot product";
418 break;
419 case EOpCross:
420 mOut << "cross product";
421 break;
422 case EOpMulMatrixComponentWise:
423 mOut << "component-wise multiply";
424 break;
425
426 default:
427 mOut << GetOperatorString(node->getOp());
428 break;
429 }
430
431 mOut << " (" << node->getCompleteString() << ")";
432
433 mOut << "\n";
434
435 return true;
436}
437
438bool TOutputTraverser::visitBlock(Visit visit, TIntermBlock *node)
439{
440 OutputTreeText(mOut, node, mDepth);
441 mOut << "Code block\n";
442
443 return true;
444}
445
446bool TOutputTraverser::visitDeclaration(Visit visit, TIntermDeclaration *node)
447{
448 OutputTreeText(mOut, node, mDepth);
449 mOut << "Declaration\n";
450
451 return true;
452}
453
454bool TOutputTraverser::visitTernary(Visit visit, TIntermTernary *node)
455{
456 OutputTreeText(mOut, node, mDepth);
457
458 mOut << "Ternary selection";
459 mOut << " (" << node->getCompleteString() << ")\n";
460
461 ++mDepth;
462
463 OutputTreeText(mOut, node, mDepth);
464 mOut << "Condition\n";
465 node->getCondition()->traverse(this);
466
467 OutputTreeText(mOut, node, mDepth);
468 if (node->getTrueExpression())
469 {
470 mOut << "true case\n";
471 node->getTrueExpression()->traverse(this);
472 }
473 if (node->getFalseExpression())
474 {
475 OutputTreeText(mOut, node, mDepth);
476 mOut << "false case\n";
477 node->getFalseExpression()->traverse(this);
478 }
479
480 --mDepth;
481
482 return false;
483}
484
485bool TOutputTraverser::visitIfElse(Visit visit, TIntermIfElse *node)
486{
487 OutputTreeText(mOut, node, mDepth);
488
489 mOut << "If test\n";
490
491 ++mDepth;
492
493 OutputTreeText(mOut, node, mDepth);
494 mOut << "Condition\n";
495 node->getCondition()->traverse(this);
496
497 OutputTreeText(mOut, node, mDepth);
498 if (node->getTrueBlock())
499 {
500 mOut << "true case\n";
501 node->getTrueBlock()->traverse(this);
502 }
503 else
504 {
505 mOut << "true case is null\n";
506 }
507
508 if (node->getFalseBlock())
509 {
510 OutputTreeText(mOut, node, mDepth);
511 mOut << "false case\n";
512 node->getFalseBlock()->traverse(this);
513 }
514
515 --mDepth;
516
517 return false;
518}
519
520bool TOutputTraverser::visitSwitch(Visit visit, TIntermSwitch *node)
521{
522 OutputTreeText(mOut, node, mDepth);
523
524 mOut << "Switch\n";
525
526 return true;
527}
528
529bool TOutputTraverser::visitCase(Visit visit, TIntermCase *node)
530{
531 OutputTreeText(mOut, node, mDepth);
532
533 if (node->getCondition() == nullptr)
534 {
535 mOut << "Default\n";
536 }
537 else
538 {
539 mOut << "Case\n";
540 }
541
542 return true;
543}
544
545void TOutputTraverser::visitConstantUnion(TIntermConstantUnion *node)
546{
547 size_t size = node->getType().getObjectSize();
548
549 for (size_t i = 0; i < size; i++)
550 {
551 OutputTreeText(mOut, node, mDepth);
552 switch (node->getUnionArrayPointer()[i].getType())
553 {
554 case EbtBool:
555 if (node->getUnionArrayPointer()[i].getBConst())
556 mOut << "true";
557 else
558 mOut << "false";
559
560 mOut << " ("
561 << "const bool"
562 << ")";
563 mOut << "\n";
564 break;
565 case EbtFloat:
566 mOut << node->getUnionArrayPointer()[i].getFConst();
567 mOut << " (const float)\n";
568 break;
569 case EbtInt:
570 mOut << node->getUnionArrayPointer()[i].getIConst();
571 mOut << " (const int)\n";
572 break;
573 case EbtUInt:
574 mOut << node->getUnionArrayPointer()[i].getUConst();
575 mOut << " (const uint)\n";
576 break;
577 case EbtYuvCscStandardEXT:
578 mOut << getYuvCscStandardEXTString(
579 node->getUnionArrayPointer()[i].getYuvCscStandardEXTConst());
580 mOut << " (const yuvCscStandardEXT)\n";
581 break;
582 default:
583 mOut.prefix(SH_ERROR);
584 mOut << "Unknown constant\n";
585 break;
586 }
587 }
588}
589
590bool TOutputTraverser::visitLoop(Visit visit, TIntermLoop *node)
591{
592 OutputTreeText(mOut, node, mDepth);
593
594 mOut << "Loop with condition ";
595 if (node->getType() == ELoopDoWhile)
596 mOut << "not ";
597 mOut << "tested first\n";
598
599 ++mDepth;
600
601 OutputTreeText(mOut, node, mDepth);
602 if (node->getCondition())
603 {
604 mOut << "Loop Condition\n";
605 node->getCondition()->traverse(this);
606 }
607 else
608 {
609 mOut << "No loop condition\n";
610 }
611
612 OutputTreeText(mOut, node, mDepth);
613 if (node->getBody())
614 {
615 mOut << "Loop Body\n";
616 node->getBody()->traverse(this);
617 }
618 else
619 {
620 mOut << "No loop body\n";
621 }
622
623 if (node->getExpression())
624 {
625 OutputTreeText(mOut, node, mDepth);
626 mOut << "Loop Terminal Expression\n";
627 node->getExpression()->traverse(this);
628 }
629
630 --mDepth;
631
632 return false;
633}
634
635bool TOutputTraverser::visitBranch(Visit visit, TIntermBranch *node)
636{
637 OutputTreeText(mOut, node, mDepth);
638
639 switch (node->getFlowOp())
640 {
641 case EOpKill:
642 mOut << "Branch: Kill";
643 break;
644 case EOpBreak:
645 mOut << "Branch: Break";
646 break;
647 case EOpContinue:
648 mOut << "Branch: Continue";
649 break;
650 case EOpReturn:
651 mOut << "Branch: Return";
652 break;
653 default:
654 mOut << "Branch: Unknown Branch";
655 break;
656 }
657
658 if (node->getExpression())
659 {
660 mOut << " with expression\n";
661 ++mDepth;
662 node->getExpression()->traverse(this);
663 --mDepth;
664 }
665 else
666 {
667 mOut << "\n";
668 }
669
670 return false;
671}
672
673} // anonymous namespace
674
675void OutputTree(TIntermNode *root, TInfoSinkBase &out)
676{
677 TOutputTraverser it(out);
678 ASSERT(root);
679 root->traverse(&it);
680}
681
682} // namespace sh