| // | |
| // Copyright 2002 The ANGLE Project Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| // | |
| #include "compiler/translator/SymbolTable.h" | |
| #include "compiler/translator/tree_util/IntermTraverse.h" | |
| namespace sh | |
| { | |
| namespace | |
| { | |
| void OutputFunction(TInfoSinkBase &out, const char *str, const TFunction *func) | |
| { | |
| const char *internal = | |
| (func->symbolType() == SymbolType::AngleInternal) ? " (internal function)" : ""; | |
| out << str << internal << ": " << func->name() << " (symbol id " << func->uniqueId().get() | |
| << ")"; | |
| } | |
| // Two purposes: | |
| // 1. Show an example of how to iterate tree. Functions can also directly call traverse() on | |
| // children themselves to have finer grained control over the process than shown here, though | |
| // that's not recommended if it can be avoided. | |
| // 2. Print out a text based description of the tree. | |
| // The traverser subclass is used to carry along data from node to node in the traversal. | |
| class TOutputTraverser : public TIntermTraverser | |
| { | |
| public: | |
| TOutputTraverser(TInfoSinkBase &out) | |
| : TIntermTraverser(true, false, false), mOut(out), mIndentDepth(0) | |
| {} | |
| protected: | |
| void visitSymbol(TIntermSymbol *) override; | |
| void visitConstantUnion(TIntermConstantUnion *) override; | |
| bool visitSwizzle(Visit visit, TIntermSwizzle *node) override; | |
| bool visitBinary(Visit visit, TIntermBinary *) override; | |
| bool visitUnary(Visit visit, TIntermUnary *) override; | |
| bool visitTernary(Visit visit, TIntermTernary *node) override; | |
| bool visitIfElse(Visit visit, TIntermIfElse *node) override; | |
| bool visitSwitch(Visit visit, TIntermSwitch *node) override; | |
| bool visitCase(Visit visit, TIntermCase *node) override; | |
| void visitFunctionPrototype(TIntermFunctionPrototype *node) override; | |
| bool visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node) override; | |
| bool visitAggregate(Visit visit, TIntermAggregate *) override; | |
| bool visitBlock(Visit visit, TIntermBlock *) override; | |
| bool visitGlobalQualifierDeclaration(Visit visit, | |
| TIntermGlobalQualifierDeclaration *node) override; | |
| bool visitDeclaration(Visit visit, TIntermDeclaration *node) override; | |
| bool visitLoop(Visit visit, TIntermLoop *) override; | |
| bool visitBranch(Visit visit, TIntermBranch *) override; | |
| int getCurrentIndentDepth() const { return mIndentDepth + getCurrentTraversalDepth(); } | |
| TInfoSinkBase &mOut; | |
| int mIndentDepth; | |
| }; | |
| // | |
| // Helper functions for printing, not part of traversing. | |
| // | |
| void OutputTreeText(TInfoSinkBase &out, TIntermNode *node, const int depth) | |
| { | |
| int i; | |
| out.location(node->getLine().first_file, node->getLine().first_line); | |
| for (i = 0; i < depth; ++i) | |
| out << " "; | |
| } | |
| // | |
| // The rest of the file are the traversal functions. The last one | |
| // is the one that starts the traversal. | |
| // | |
| // Return true from interior nodes to have the external traversal | |
| // continue on to children. If you process children yourself, | |
| // return false. | |
| // | |
| void TOutputTraverser::visitSymbol(TIntermSymbol *node) | |
| { | |
| OutputTreeText(mOut, node, getCurrentIndentDepth()); | |
| if (node->variable().symbolType() == SymbolType::Empty) | |
| { | |
| mOut << "''"; | |
| } | |
| else | |
| { | |
| mOut << "'" << node->getName() << "' "; | |
| } | |
| mOut << "(symbol id " << node->uniqueId().get() << ") "; | |
| mOut << "(" << node->getType() << ")"; | |
| mOut << "\n"; | |
| } | |
| bool TOutputTraverser::visitSwizzle(Visit visit, TIntermSwizzle *node) | |
| { | |
| OutputTreeText(mOut, node, getCurrentIndentDepth()); | |
| mOut << "vector swizzle ("; | |
| node->writeOffsetsAsXYZW(&mOut); | |
| mOut << ")"; | |
| mOut << " (" << node->getType() << ")"; | |
| mOut << "\n"; | |
| return true; | |
| } | |
| bool TOutputTraverser::visitBinary(Visit visit, TIntermBinary *node) | |
| { | |
| OutputTreeText(mOut, node, getCurrentIndentDepth()); | |
| switch (node->getOp()) | |
| { | |
| case EOpComma: | |
| mOut << "comma"; | |
| break; | |
| case EOpAssign: | |
| mOut << "move second child to first child"; | |
| break; | |
| case EOpInitialize: | |
| mOut << "initialize first child with second child"; | |
| break; | |
| case EOpAddAssign: | |
| mOut << "add second child into first child"; | |
| break; | |
| case EOpSubAssign: | |
| mOut << "subtract second child into first child"; | |
| break; | |
| case EOpMulAssign: | |
| mOut << "multiply second child into first child"; | |
| break; | |
| case EOpVectorTimesMatrixAssign: | |
| mOut << "matrix mult second child into first child"; | |
| break; | |
| case EOpVectorTimesScalarAssign: | |
| mOut << "vector scale second child into first child"; | |
| break; | |
| case EOpMatrixTimesScalarAssign: | |
| mOut << "matrix scale second child into first child"; | |
| break; | |
| case EOpMatrixTimesMatrixAssign: | |
| mOut << "matrix mult second child into first child"; | |
| break; | |
| case EOpDivAssign: | |
| mOut << "divide second child into first child"; | |
| break; | |
| case EOpIModAssign: | |
| mOut << "modulo second child into first child"; | |
| break; | |
| case EOpBitShiftLeftAssign: | |
| mOut << "bit-wise shift first child left by second child"; | |
| break; | |
| case EOpBitShiftRightAssign: | |
| mOut << "bit-wise shift first child right by second child"; | |
| break; | |
| case EOpBitwiseAndAssign: | |
| mOut << "bit-wise and second child into first child"; | |
| break; | |
| case EOpBitwiseXorAssign: | |
| mOut << "bit-wise xor second child into first child"; | |
| break; | |
| case EOpBitwiseOrAssign: | |
| mOut << "bit-wise or second child into first child"; | |
| break; | |
| case EOpIndexDirect: | |
| mOut << "direct index"; | |
| break; | |
| case EOpIndexIndirect: | |
| mOut << "indirect index"; | |
| break; | |
| case EOpIndexDirectStruct: | |
| mOut << "direct index for structure"; | |
| break; | |
| case EOpIndexDirectInterfaceBlock: | |
| mOut << "direct index for interface block"; | |
| break; | |
| case EOpAdd: | |
| mOut << "add"; | |
| break; | |
| case EOpSub: | |
| mOut << "subtract"; | |
| break; | |
| case EOpMul: | |
| mOut << "component-wise multiply"; | |
| break; | |
| case EOpDiv: | |
| mOut << "divide"; | |
| break; | |
| case EOpIMod: | |
| mOut << "modulo"; | |
| break; | |
| case EOpBitShiftLeft: | |
| mOut << "bit-wise shift left"; | |
| break; | |
| case EOpBitShiftRight: | |
| mOut << "bit-wise shift right"; | |
| break; | |
| case EOpBitwiseAnd: | |
| mOut << "bit-wise and"; | |
| break; | |
| case EOpBitwiseXor: | |
| mOut << "bit-wise xor"; | |
| break; | |
| case EOpBitwiseOr: | |
| mOut << "bit-wise or"; | |
| break; | |
| case EOpEqual: | |
| mOut << "Compare Equal"; | |
| break; | |
| case EOpNotEqual: | |
| mOut << "Compare Not Equal"; | |
| break; | |
| case EOpLessThan: | |
| mOut << "Compare Less Than"; | |
| break; | |
| case EOpGreaterThan: | |
| mOut << "Compare Greater Than"; | |
| break; | |
| case EOpLessThanEqual: | |
| mOut << "Compare Less Than or Equal"; | |
| break; | |
| case EOpGreaterThanEqual: | |
| mOut << "Compare Greater Than or Equal"; | |
| break; | |
| case EOpVectorTimesScalar: | |
| mOut << "vector-scale"; | |
| break; | |
| case EOpVectorTimesMatrix: | |
| mOut << "vector-times-matrix"; | |
| break; | |
| case EOpMatrixTimesVector: | |
| mOut << "matrix-times-vector"; | |
| break; | |
| case EOpMatrixTimesScalar: | |
| mOut << "matrix-scale"; | |
| break; | |
| case EOpMatrixTimesMatrix: | |
| mOut << "matrix-multiply"; | |
| break; | |
| case EOpLogicalOr: | |
| mOut << "logical-or"; | |
| break; | |
| case EOpLogicalXor: | |
| mOut << "logical-xor"; | |
| break; | |
| case EOpLogicalAnd: | |
| mOut << "logical-and"; | |
| break; | |
| default: | |
| mOut << "<unknown op>"; | |
| } | |
| mOut << " (" << node->getType() << ")"; | |
| mOut << "\n"; | |
| // Special handling for direct indexes. Because constant | |
| // unions are not aware they are struct indexes, treat them | |
| // here where we have that contextual knowledge. | |
| if (node->getOp() == EOpIndexDirectStruct || node->getOp() == EOpIndexDirectInterfaceBlock) | |
| { | |
| node->getLeft()->traverse(this); | |
| TIntermConstantUnion *intermConstantUnion = node->getRight()->getAsConstantUnion(); | |
| ASSERT(intermConstantUnion); | |
| OutputTreeText(mOut, intermConstantUnion, getCurrentIndentDepth() + 1); | |
| // The following code finds the field name from the constant union | |
| const TConstantUnion *constantUnion = intermConstantUnion->getConstantValue(); | |
| const TStructure *structure = node->getLeft()->getType().getStruct(); | |
| const TInterfaceBlock *interfaceBlock = node->getLeft()->getType().getInterfaceBlock(); | |
| ASSERT(structure || interfaceBlock); | |
| const TFieldList &fields = structure ? structure->fields() : interfaceBlock->fields(); | |
| const TField *field = fields[constantUnion->getIConst()]; | |
| mOut << constantUnion->getIConst() << " (field '" << field->name() << "')"; | |
| mOut << "\n"; | |
| return false; | |
| } | |
| return true; | |
| } | |
| bool TOutputTraverser::visitUnary(Visit visit, TIntermUnary *node) | |
| { | |
| OutputTreeText(mOut, node, getCurrentIndentDepth()); | |
| switch (node->getOp()) | |
| { | |
| // Give verbose names for ops that have special syntax and some built-in functions that are | |
| // easy to confuse with others, but mostly use GLSL names for functions. | |
| case EOpNegative: | |
| mOut << "Negate value"; | |
| break; | |
| case EOpPositive: | |
| mOut << "Positive sign"; | |
| break; | |
| case EOpLogicalNot: | |
| mOut << "negation"; | |
| break; | |
| case EOpBitwiseNot: | |
| mOut << "bit-wise not"; | |
| break; | |
| case EOpPostIncrement: | |
| mOut << "Post-Increment"; | |
| break; | |
| case EOpPostDecrement: | |
| mOut << "Post-Decrement"; | |
| break; | |
| case EOpPreIncrement: | |
| mOut << "Pre-Increment"; | |
| break; | |
| case EOpPreDecrement: | |
| mOut << "Pre-Decrement"; | |
| break; | |
| case EOpArrayLength: | |
| mOut << "Array length"; | |
| break; | |
| case EOpLogicalNotComponentWise: | |
| mOut << "component-wise not"; | |
| break; | |
| default: | |
| mOut << GetOperatorString(node->getOp()); | |
| break; | |
| } | |
| mOut << " (" << node->getType() << ")"; | |
| mOut << "\n"; | |
| return true; | |
| } | |
| bool TOutputTraverser::visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node) | |
| { | |
| OutputTreeText(mOut, node, getCurrentIndentDepth()); | |
| mOut << "Function Definition:\n"; | |
| return true; | |
| } | |
| bool TOutputTraverser::visitGlobalQualifierDeclaration(Visit visit, | |
| TIntermGlobalQualifierDeclaration *node) | |
| { | |
| OutputTreeText(mOut, node, getCurrentIndentDepth()); | |
| mOut << "Invariant Declaration:\n"; | |
| return true; | |
| } | |
| void TOutputTraverser::visitFunctionPrototype(TIntermFunctionPrototype *node) | |
| { | |
| OutputTreeText(mOut, node, getCurrentIndentDepth()); | |
| OutputFunction(mOut, "Function Prototype", node->getFunction()); | |
| mOut << " (" << node->getType() << ")"; | |
| mOut << "\n"; | |
| size_t paramCount = node->getFunction()->getParamCount(); | |
| for (size_t i = 0; i < paramCount; ++i) | |
| { | |
| const TVariable *param = node->getFunction()->getParam(i); | |
| OutputTreeText(mOut, node, getCurrentIndentDepth() + 1); | |
| mOut << "parameter: " << param->name() << " (" << param->getType() << ")"; | |
| } | |
| } | |
| bool TOutputTraverser::visitAggregate(Visit visit, TIntermAggregate *node) | |
| { | |
| OutputTreeText(mOut, node, getCurrentIndentDepth()); | |
| if (node->getOp() == EOpNull) | |
| { | |
| mOut.prefix(SH_ERROR); | |
| mOut << "node is still EOpNull!\n"; | |
| return true; | |
| } | |
| // Give verbose names for some built-in functions that are easy to confuse with others, but | |
| // mostly use GLSL names for functions. | |
| switch (node->getOp()) | |
| { | |
| case EOpCallFunctionInAST: | |
| OutputFunction(mOut, "Call an user-defined function", node->getFunction()); | |
| break; | |
| case EOpCallInternalRawFunction: | |
| OutputFunction(mOut, "Call an internal function with raw implementation", | |
| node->getFunction()); | |
| break; | |
| case EOpCallBuiltInFunction: | |
| OutputFunction(mOut, "Call a built-in function", node->getFunction()); | |
| break; | |
| case EOpConstruct: | |
| // The type of the constructor will be printed below. | |
| mOut << "Construct"; | |
| break; | |
| case EOpEqualComponentWise: | |
| mOut << "component-wise equal"; | |
| break; | |
| case EOpNotEqualComponentWise: | |
| mOut << "component-wise not equal"; | |
| break; | |
| case EOpLessThanComponentWise: | |
| mOut << "component-wise less than"; | |
| break; | |
| case EOpGreaterThanComponentWise: | |
| mOut << "component-wise greater than"; | |
| break; | |
| case EOpLessThanEqualComponentWise: | |
| mOut << "component-wise less than or equal"; | |
| break; | |
| case EOpGreaterThanEqualComponentWise: | |
| mOut << "component-wise greater than or equal"; | |
| break; | |
| case EOpDot: | |
| mOut << "dot product"; | |
| break; | |
| case EOpCross: | |
| mOut << "cross product"; | |
| break; | |
| case EOpMulMatrixComponentWise: | |
| mOut << "component-wise multiply"; | |
| break; | |
| default: | |
| mOut << GetOperatorString(node->getOp()); | |
| break; | |
| } | |
| mOut << " (" << node->getType() << ")"; | |
| mOut << "\n"; | |
| return true; | |
| } | |
| bool TOutputTraverser::visitBlock(Visit visit, TIntermBlock *node) | |
| { | |
| OutputTreeText(mOut, node, getCurrentIndentDepth()); | |
| mOut << "Code block\n"; | |
| return true; | |
| } | |
| bool TOutputTraverser::visitDeclaration(Visit visit, TIntermDeclaration *node) | |
| { | |
| OutputTreeText(mOut, node, getCurrentIndentDepth()); | |
| mOut << "Declaration\n"; | |
| return true; | |
| } | |
| bool TOutputTraverser::visitTernary(Visit visit, TIntermTernary *node) | |
| { | |
| OutputTreeText(mOut, node, getCurrentIndentDepth()); | |
| mOut << "Ternary selection"; | |
| mOut << " (" << node->getType() << ")\n"; | |
| ++mIndentDepth; | |
| OutputTreeText(mOut, node, getCurrentIndentDepth()); | |
| mOut << "Condition\n"; | |
| node->getCondition()->traverse(this); | |
| OutputTreeText(mOut, node, getCurrentIndentDepth()); | |
| if (node->getTrueExpression()) | |
| { | |
| mOut << "true case\n"; | |
| node->getTrueExpression()->traverse(this); | |
| } | |
| if (node->getFalseExpression()) | |
| { | |
| OutputTreeText(mOut, node, getCurrentIndentDepth()); | |
| mOut << "false case\n"; | |
| node->getFalseExpression()->traverse(this); | |
| } | |
| --mIndentDepth; | |
| return false; | |
| } | |
| bool TOutputTraverser::visitIfElse(Visit visit, TIntermIfElse *node) | |
| { | |
| OutputTreeText(mOut, node, getCurrentIndentDepth()); | |
| mOut << "If test\n"; | |
| ++mIndentDepth; | |
| OutputTreeText(mOut, node, getCurrentIndentDepth()); | |
| mOut << "Condition\n"; | |
| node->getCondition()->traverse(this); | |
| OutputTreeText(mOut, node, getCurrentIndentDepth()); | |
| if (node->getTrueBlock()) | |
| { | |
| mOut << "true case\n"; | |
| node->getTrueBlock()->traverse(this); | |
| } | |
| else | |
| { | |
| mOut << "true case is null\n"; | |
| } | |
| if (node->getFalseBlock()) | |
| { | |
| OutputTreeText(mOut, node, getCurrentIndentDepth()); | |
| mOut << "false case\n"; | |
| node->getFalseBlock()->traverse(this); | |
| } | |
| --mIndentDepth; | |
| return false; | |
| } | |
| bool TOutputTraverser::visitSwitch(Visit visit, TIntermSwitch *node) | |
| { | |
| OutputTreeText(mOut, node, getCurrentIndentDepth()); | |
| mOut << "Switch\n"; | |
| return true; | |
| } | |
| bool TOutputTraverser::visitCase(Visit visit, TIntermCase *node) | |
| { | |
| OutputTreeText(mOut, node, getCurrentIndentDepth()); | |
| if (node->getCondition() == nullptr) | |
| { | |
| mOut << "Default\n"; | |
| } | |
| else | |
| { | |
| mOut << "Case\n"; | |
| } | |
| return true; | |
| } | |
| void TOutputTraverser::visitConstantUnion(TIntermConstantUnion *node) | |
| { | |
| size_t size = node->getType().getObjectSize(); | |
| for (size_t i = 0; i < size; i++) | |
| { | |
| OutputTreeText(mOut, node, getCurrentIndentDepth()); | |
| switch (node->getConstantValue()[i].getType()) | |
| { | |
| case EbtBool: | |
| if (node->getConstantValue()[i].getBConst()) | |
| mOut << "true"; | |
| else | |
| mOut << "false"; | |
| mOut << " (" | |
| << "const bool" | |
| << ")"; | |
| mOut << "\n"; | |
| break; | |
| case EbtFloat: | |
| mOut << node->getConstantValue()[i].getFConst(); | |
| mOut << " (const float)\n"; | |
| break; | |
| case EbtInt: | |
| mOut << node->getConstantValue()[i].getIConst(); | |
| mOut << " (const int)\n"; | |
| break; | |
| case EbtUInt: | |
| mOut << node->getConstantValue()[i].getUConst(); | |
| mOut << " (const uint)\n"; | |
| break; | |
| case EbtYuvCscStandardEXT: | |
| mOut << getYuvCscStandardEXTString( | |
| node->getConstantValue()[i].getYuvCscStandardEXTConst()); | |
| mOut << " (const yuvCscStandardEXT)\n"; | |
| break; | |
| default: | |
| mOut.prefix(SH_ERROR); | |
| mOut << "Unknown constant\n"; | |
| break; | |
| } | |
| } | |
| } | |
| bool TOutputTraverser::visitLoop(Visit visit, TIntermLoop *node) | |
| { | |
| OutputTreeText(mOut, node, getCurrentIndentDepth()); | |
| mOut << "Loop with condition "; | |
| if (node->getType() == ELoopDoWhile) | |
| mOut << "not "; | |
| mOut << "tested first\n"; | |
| ++mIndentDepth; | |
| OutputTreeText(mOut, node, getCurrentIndentDepth()); | |
| if (node->getCondition()) | |
| { | |
| mOut << "Loop Condition\n"; | |
| node->getCondition()->traverse(this); | |
| } | |
| else | |
| { | |
| mOut << "No loop condition\n"; | |
| } | |
| OutputTreeText(mOut, node, getCurrentIndentDepth()); | |
| if (node->getBody()) | |
| { | |
| mOut << "Loop Body\n"; | |
| node->getBody()->traverse(this); | |
| } | |
| else | |
| { | |
| mOut << "No loop body\n"; | |
| } | |
| if (node->getExpression()) | |
| { | |
| OutputTreeText(mOut, node, getCurrentIndentDepth()); | |
| mOut << "Loop Terminal Expression\n"; | |
| node->getExpression()->traverse(this); | |
| } | |
| --mIndentDepth; | |
| return false; | |
| } | |
| bool TOutputTraverser::visitBranch(Visit visit, TIntermBranch *node) | |
| { | |
| OutputTreeText(mOut, node, getCurrentIndentDepth()); | |
| switch (node->getFlowOp()) | |
| { | |
| case EOpKill: | |
| mOut << "Branch: Kill"; | |
| break; | |
| case EOpBreak: | |
| mOut << "Branch: Break"; | |
| break; | |
| case EOpContinue: | |
| mOut << "Branch: Continue"; | |
| break; | |
| case EOpReturn: | |
| mOut << "Branch: Return"; | |
| break; | |
| default: | |
| mOut << "Branch: Unknown Branch"; | |
| break; | |
| } | |
| if (node->getExpression()) | |
| { | |
| mOut << " with expression\n"; | |
| ++mIndentDepth; | |
| node->getExpression()->traverse(this); | |
| --mIndentDepth; | |
| } | |
| else | |
| { | |
| mOut << "\n"; | |
| } | |
| return false; | |
| } | |
| } // anonymous namespace | |
| void OutputTree(TIntermNode *root, TInfoSinkBase &out) | |
| { | |
| TOutputTraverser it(out); | |
| ASSERT(root); | |
| root->traverse(&it); | |
| } | |
| } // namespace sh |