michael@0: // michael@0: // Copyright (c) 2002-2010 The ANGLE Project Authors. All rights reserved. michael@0: // Use of this source code is governed by a BSD-style license that can be michael@0: // found in the LICENSE file. michael@0: // michael@0: michael@0: #include "compiler/localintermediate.h" michael@0: michael@0: // michael@0: // Two purposes: michael@0: // 1. Show an example of how to iterate tree. Functions can michael@0: // also directly call Traverse() on children themselves to michael@0: // have finer grained control over the process than shown here. michael@0: // See the last function for how to get started. michael@0: // 2. Print out a text based description of the tree. michael@0: // michael@0: michael@0: // michael@0: // Use this class to carry along data from node to node in michael@0: // the traversal michael@0: // michael@0: class TOutputTraverser : public TIntermTraverser { michael@0: public: michael@0: TOutputTraverser(TInfoSinkBase& i) : sink(i) { } michael@0: TInfoSinkBase& sink; michael@0: michael@0: protected: michael@0: void visitSymbol(TIntermSymbol*); michael@0: void visitConstantUnion(TIntermConstantUnion*); michael@0: bool visitBinary(Visit visit, TIntermBinary*); michael@0: bool visitUnary(Visit visit, TIntermUnary*); michael@0: bool visitSelection(Visit visit, TIntermSelection*); michael@0: bool visitAggregate(Visit visit, TIntermAggregate*); michael@0: bool visitLoop(Visit visit, TIntermLoop*); michael@0: bool visitBranch(Visit visit, TIntermBranch*); michael@0: }; michael@0: michael@0: TString TType::getCompleteString() const michael@0: { michael@0: TStringStream stream; michael@0: michael@0: if (qualifier != EvqTemporary && qualifier != EvqGlobal) michael@0: stream << getQualifierString() << " " << getPrecisionString() << " "; michael@0: if (array) michael@0: stream << "array[" << getArraySize() << "] of "; michael@0: if (matrix) michael@0: stream << size << "X" << size << " matrix of "; michael@0: else if (size > 1) michael@0: stream << size << "-component vector of "; michael@0: michael@0: stream << getBasicString(); michael@0: return stream.str(); michael@0: } michael@0: michael@0: // michael@0: // Helper functions for printing, not part of traversing. michael@0: // michael@0: michael@0: void OutputTreeText(TInfoSinkBase& sink, TIntermNode* node, const int depth) michael@0: { michael@0: int i; michael@0: michael@0: sink.location(node->getLine()); michael@0: michael@0: for (i = 0; i < depth; ++i) michael@0: sink << " "; michael@0: } michael@0: michael@0: // michael@0: // The rest of the file are the traversal functions. The last one michael@0: // is the one that starts the traversal. michael@0: // michael@0: // Return true from interior nodes to have the external traversal michael@0: // continue on to children. If you process children yourself, michael@0: // return false. michael@0: // michael@0: michael@0: void TOutputTraverser::visitSymbol(TIntermSymbol* node) michael@0: { michael@0: OutputTreeText(sink, node, depth); michael@0: michael@0: sink << "'" << node->getSymbol() << "' "; michael@0: sink << "(" << node->getCompleteString() << ")\n"; michael@0: } michael@0: michael@0: bool TOutputTraverser::visitBinary(Visit visit, TIntermBinary* node) michael@0: { michael@0: TInfoSinkBase& out = sink; michael@0: michael@0: OutputTreeText(out, node, depth); michael@0: michael@0: switch (node->getOp()) { michael@0: case EOpAssign: out << "move second child to first child"; break; michael@0: case EOpInitialize: out << "initialize first child with second child"; break; michael@0: case EOpAddAssign: out << "add second child into first child"; break; michael@0: case EOpSubAssign: out << "subtract second child into first child"; break; michael@0: case EOpMulAssign: out << "multiply second child into first child"; break; michael@0: case EOpVectorTimesMatrixAssign: out << "matrix mult second child into first child"; break; michael@0: case EOpVectorTimesScalarAssign: out << "vector scale second child into first child"; break; michael@0: case EOpMatrixTimesScalarAssign: out << "matrix scale second child into first child"; break; michael@0: case EOpMatrixTimesMatrixAssign: out << "matrix mult second child into first child"; break; michael@0: case EOpDivAssign: out << "divide second child into first child"; break; michael@0: case EOpIndexDirect: out << "direct index"; break; michael@0: case EOpIndexIndirect: out << "indirect index"; break; michael@0: case EOpIndexDirectStruct: out << "direct index for structure"; break; michael@0: case EOpVectorSwizzle: out << "vector swizzle"; break; michael@0: michael@0: case EOpAdd: out << "add"; break; michael@0: case EOpSub: out << "subtract"; break; michael@0: case EOpMul: out << "component-wise multiply"; break; michael@0: case EOpDiv: out << "divide"; break; michael@0: case EOpEqual: out << "Compare Equal"; break; michael@0: case EOpNotEqual: out << "Compare Not Equal"; break; michael@0: case EOpLessThan: out << "Compare Less Than"; break; michael@0: case EOpGreaterThan: out << "Compare Greater Than"; break; michael@0: case EOpLessThanEqual: out << "Compare Less Than or Equal"; break; michael@0: case EOpGreaterThanEqual: out << "Compare Greater Than or Equal"; break; michael@0: michael@0: case EOpVectorTimesScalar: out << "vector-scale"; break; michael@0: case EOpVectorTimesMatrix: out << "vector-times-matrix"; break; michael@0: case EOpMatrixTimesVector: out << "matrix-times-vector"; break; michael@0: case EOpMatrixTimesScalar: out << "matrix-scale"; break; michael@0: case EOpMatrixTimesMatrix: out << "matrix-multiply"; break; michael@0: michael@0: case EOpLogicalOr: out << "logical-or"; break; michael@0: case EOpLogicalXor: out << "logical-xor"; break; michael@0: case EOpLogicalAnd: out << "logical-and"; break; michael@0: default: out << ""; michael@0: } michael@0: michael@0: out << " (" << node->getCompleteString() << ")"; michael@0: michael@0: out << "\n"; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool TOutputTraverser::visitUnary(Visit visit, TIntermUnary* node) michael@0: { michael@0: TInfoSinkBase& out = sink; michael@0: michael@0: OutputTreeText(out, node, depth); michael@0: michael@0: switch (node->getOp()) { michael@0: case EOpNegative: out << "Negate value"; break; michael@0: case EOpVectorLogicalNot: michael@0: case EOpLogicalNot: out << "Negate conditional"; break; michael@0: michael@0: case EOpPostIncrement: out << "Post-Increment"; break; michael@0: case EOpPostDecrement: out << "Post-Decrement"; break; michael@0: case EOpPreIncrement: out << "Pre-Increment"; break; michael@0: case EOpPreDecrement: out << "Pre-Decrement"; break; michael@0: michael@0: case EOpConvIntToBool: out << "Convert int to bool"; break; michael@0: case EOpConvFloatToBool:out << "Convert float to bool";break; michael@0: case EOpConvBoolToFloat:out << "Convert bool to float";break; michael@0: case EOpConvIntToFloat: out << "Convert int to float"; break; michael@0: case EOpConvFloatToInt: out << "Convert float to int"; break; michael@0: case EOpConvBoolToInt: out << "Convert bool to int"; break; michael@0: michael@0: case EOpRadians: out << "radians"; break; michael@0: case EOpDegrees: out << "degrees"; break; michael@0: case EOpSin: out << "sine"; break; michael@0: case EOpCos: out << "cosine"; break; michael@0: case EOpTan: out << "tangent"; break; michael@0: case EOpAsin: out << "arc sine"; break; michael@0: case EOpAcos: out << "arc cosine"; break; michael@0: case EOpAtan: out << "arc tangent"; break; michael@0: michael@0: case EOpExp: out << "exp"; break; michael@0: case EOpLog: out << "log"; break; michael@0: case EOpExp2: out << "exp2"; break; michael@0: case EOpLog2: out << "log2"; break; michael@0: case EOpSqrt: out << "sqrt"; break; michael@0: case EOpInverseSqrt: out << "inverse sqrt"; break; michael@0: michael@0: case EOpAbs: out << "Absolute value"; break; michael@0: case EOpSign: out << "Sign"; break; michael@0: case EOpFloor: out << "Floor"; break; michael@0: case EOpCeil: out << "Ceiling"; break; michael@0: case EOpFract: out << "Fraction"; break; michael@0: michael@0: case EOpLength: out << "length"; break; michael@0: case EOpNormalize: out << "normalize"; break; michael@0: // case EOpDPdx: out << "dPdx"; break; michael@0: // case EOpDPdy: out << "dPdy"; break; michael@0: // case EOpFwidth: out << "fwidth"; break; michael@0: michael@0: case EOpAny: out << "any"; break; michael@0: case EOpAll: out << "all"; break; michael@0: michael@0: default: michael@0: out.prefix(EPrefixError); michael@0: out << "Bad unary op"; michael@0: } michael@0: michael@0: out << " (" << node->getCompleteString() << ")"; michael@0: michael@0: out << "\n"; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool TOutputTraverser::visitAggregate(Visit visit, TIntermAggregate* node) michael@0: { michael@0: TInfoSinkBase& out = sink; michael@0: michael@0: if (node->getOp() == EOpNull) { michael@0: out.prefix(EPrefixError); michael@0: out << "node is still EOpNull!"; michael@0: return true; michael@0: } michael@0: michael@0: OutputTreeText(out, node, depth); michael@0: michael@0: switch (node->getOp()) { michael@0: case EOpSequence: out << "Sequence\n"; return true; michael@0: case EOpComma: out << "Comma\n"; return true; michael@0: case EOpFunction: out << "Function Definition: " << node->getName(); break; michael@0: case EOpFunctionCall: out << "Function Call: " << node->getName(); break; michael@0: case EOpParameters: out << "Function Parameters: "; break; michael@0: michael@0: case EOpConstructFloat: out << "Construct float"; break; michael@0: case EOpConstructVec2: out << "Construct vec2"; break; michael@0: case EOpConstructVec3: out << "Construct vec3"; break; michael@0: case EOpConstructVec4: out << "Construct vec4"; break; michael@0: case EOpConstructBool: out << "Construct bool"; break; michael@0: case EOpConstructBVec2: out << "Construct bvec2"; break; michael@0: case EOpConstructBVec3: out << "Construct bvec3"; break; michael@0: case EOpConstructBVec4: out << "Construct bvec4"; break; michael@0: case EOpConstructInt: out << "Construct int"; break; michael@0: case EOpConstructIVec2: out << "Construct ivec2"; break; michael@0: case EOpConstructIVec3: out << "Construct ivec3"; break; michael@0: case EOpConstructIVec4: out << "Construct ivec4"; break; michael@0: case EOpConstructMat2: out << "Construct mat2"; break; michael@0: case EOpConstructMat3: out << "Construct mat3"; break; michael@0: case EOpConstructMat4: out << "Construct mat4"; break; michael@0: case EOpConstructStruct: out << "Construct structure"; break; michael@0: michael@0: case EOpLessThan: out << "Compare Less Than"; break; michael@0: case EOpGreaterThan: out << "Compare Greater Than"; break; michael@0: case EOpLessThanEqual: out << "Compare Less Than or Equal"; break; michael@0: case EOpGreaterThanEqual: out << "Compare Greater Than or Equal"; break; michael@0: case EOpVectorEqual: out << "Equal"; break; michael@0: case EOpVectorNotEqual: out << "NotEqual"; break; michael@0: michael@0: case EOpMod: out << "mod"; break; michael@0: case EOpPow: out << "pow"; break; michael@0: michael@0: case EOpAtan: out << "arc tangent"; break; michael@0: michael@0: case EOpMin: out << "min"; break; michael@0: case EOpMax: out << "max"; break; michael@0: case EOpClamp: out << "clamp"; break; michael@0: case EOpMix: out << "mix"; break; michael@0: case EOpStep: out << "step"; break; michael@0: case EOpSmoothStep: out << "smoothstep"; break; michael@0: michael@0: case EOpDistance: out << "distance"; break; michael@0: case EOpDot: out << "dot-product"; break; michael@0: case EOpCross: out << "cross-product"; break; michael@0: case EOpFaceForward: out << "face-forward"; break; michael@0: case EOpReflect: out << "reflect"; break; michael@0: case EOpRefract: out << "refract"; break; michael@0: case EOpMul: out << "component-wise multiply"; break; michael@0: michael@0: case EOpDeclaration: out << "Declaration: "; break; michael@0: michael@0: default: michael@0: out.prefix(EPrefixError); michael@0: out << "Bad aggregation op"; michael@0: } michael@0: michael@0: if (node->getOp() != EOpSequence && node->getOp() != EOpParameters) michael@0: out << " (" << node->getCompleteString() << ")"; michael@0: michael@0: out << "\n"; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool TOutputTraverser::visitSelection(Visit visit, TIntermSelection* node) michael@0: { michael@0: TInfoSinkBase& out = sink; michael@0: michael@0: OutputTreeText(out, node, depth); michael@0: michael@0: out << "Test condition and select"; michael@0: out << " (" << node->getCompleteString() << ")\n"; michael@0: michael@0: ++depth; michael@0: michael@0: OutputTreeText(sink, node, depth); michael@0: out << "Condition\n"; michael@0: node->getCondition()->traverse(this); michael@0: michael@0: OutputTreeText(sink, node, depth); michael@0: if (node->getTrueBlock()) { michael@0: out << "true case\n"; michael@0: node->getTrueBlock()->traverse(this); michael@0: } else michael@0: out << "true case is null\n"; michael@0: michael@0: if (node->getFalseBlock()) { michael@0: OutputTreeText(sink, node, depth); michael@0: out << "false case\n"; michael@0: node->getFalseBlock()->traverse(this); michael@0: } michael@0: michael@0: --depth; michael@0: michael@0: return false; michael@0: } michael@0: michael@0: void TOutputTraverser::visitConstantUnion(TIntermConstantUnion* node) michael@0: { michael@0: TInfoSinkBase& out = sink; michael@0: michael@0: size_t size = node->getType().getObjectSize(); michael@0: michael@0: for (size_t i = 0; i < size; i++) { michael@0: OutputTreeText(out, node, depth); michael@0: switch (node->getUnionArrayPointer()[i].getType()) { michael@0: case EbtBool: michael@0: if (node->getUnionArrayPointer()[i].getBConst()) michael@0: out << "true"; michael@0: else michael@0: out << "false"; michael@0: michael@0: out << " (" << "const bool" << ")"; michael@0: out << "\n"; michael@0: break; michael@0: case EbtFloat: michael@0: out << node->getUnionArrayPointer()[i].getFConst(); michael@0: out << " (const float)\n"; michael@0: break; michael@0: case EbtInt: michael@0: out << node->getUnionArrayPointer()[i].getIConst(); michael@0: out << " (const int)\n"; michael@0: break; michael@0: default: michael@0: out.message(EPrefixInternalError, node->getLine(), "Unknown constant"); michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: michael@0: bool TOutputTraverser::visitLoop(Visit visit, TIntermLoop* node) michael@0: { michael@0: TInfoSinkBase& out = sink; michael@0: michael@0: OutputTreeText(out, node, depth); michael@0: michael@0: out << "Loop with condition "; michael@0: if (node->getType() == ELoopDoWhile) michael@0: out << "not "; michael@0: out << "tested first\n"; michael@0: michael@0: ++depth; michael@0: michael@0: OutputTreeText(sink, node, depth); michael@0: if (node->getCondition()) { michael@0: out << "Loop Condition\n"; michael@0: node->getCondition()->traverse(this); michael@0: } else michael@0: out << "No loop condition\n"; michael@0: michael@0: OutputTreeText(sink, node, depth); michael@0: if (node->getBody()) { michael@0: out << "Loop Body\n"; michael@0: node->getBody()->traverse(this); michael@0: } else michael@0: out << "No loop body\n"; michael@0: michael@0: if (node->getExpression()) { michael@0: OutputTreeText(sink, node, depth); michael@0: out << "Loop Terminal Expression\n"; michael@0: node->getExpression()->traverse(this); michael@0: } michael@0: michael@0: --depth; michael@0: michael@0: return false; michael@0: } michael@0: michael@0: bool TOutputTraverser::visitBranch(Visit visit, TIntermBranch* node) michael@0: { michael@0: TInfoSinkBase& out = sink; michael@0: michael@0: OutputTreeText(out, node, depth); michael@0: michael@0: switch (node->getFlowOp()) { michael@0: case EOpKill: out << "Branch: Kill"; break; michael@0: case EOpBreak: out << "Branch: Break"; break; michael@0: case EOpContinue: out << "Branch: Continue"; break; michael@0: case EOpReturn: out << "Branch: Return"; break; michael@0: default: out << "Branch: Unknown Branch"; break; michael@0: } michael@0: michael@0: if (node->getExpression()) { michael@0: out << " with expression\n"; michael@0: ++depth; michael@0: node->getExpression()->traverse(this); michael@0: --depth; michael@0: } else michael@0: out << "\n"; michael@0: michael@0: return false; michael@0: } michael@0: michael@0: // michael@0: // This function is the one to call externally to start the traversal. michael@0: // Individual functions can be initialized to 0 to skip processing of that michael@0: // type of node. It's children will still be processed. michael@0: // michael@0: void TIntermediate::outputTree(TIntermNode* root) michael@0: { michael@0: if (root == 0) michael@0: return; michael@0: michael@0: TOutputTraverser it(infoSink.info); michael@0: michael@0: root->traverse(&it); michael@0: }