1//
2// Copyright (c) 2002-2010 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/localintermediate.h"
8
9//
10// Two purposes:
11// 1.  Show an example of how to iterate tree.  Functions can
12//     also directly call Traverse() on children themselves to
13//     have finer grained control over the process than shown here.
14//     See the last function for how to get started.
15// 2.  Print out a text based description of the tree.
16//
17
18//
19// Use this class to carry along data from node to node in
20// the traversal
21//
22class TOutputTraverser : public TIntermTraverser {
23public:
24    TOutputTraverser(TInfoSinkBase& i) : sink(i) { }
25    TInfoSinkBase& sink;
26
27protected:
28    void visitSymbol(TIntermSymbol*);
29    void visitConstantUnion(TIntermConstantUnion*);
30    bool visitBinary(Visit visit, TIntermBinary*);
31    bool visitUnary(Visit visit, TIntermUnary*);
32    bool visitSelection(Visit visit, TIntermSelection*);
33    bool visitAggregate(Visit visit, TIntermAggregate*);
34    bool visitLoop(Visit visit, TIntermLoop*);
35    bool visitBranch(Visit visit, TIntermBranch*);
36};
37
38TString TType::getCompleteString() const
39{
40    TStringStream stream;
41
42    if (qualifier != EvqTemporary && qualifier != EvqGlobal)
43        stream << getQualifierString() << " " << getPrecisionString() << " ";
44    if (array)
45        stream << "array of ";
46    if (matrix)
47        stream << size << "X" << size << " matrix of ";
48    else if (size > 1)
49        stream << size << "-component vector of ";
50
51    stream << getBasicString();
52    return stream.str();
53}
54
55//
56// Helper functions for printing, not part of traversing.
57//
58
59void OutputTreeText(TInfoSinkBase& sink, TIntermNode* node, const int depth)
60{
61    int i;
62
63    sink.location(node->getLine());
64
65    for (i = 0; i < depth; ++i)
66        sink << "  ";
67}
68
69//
70// The rest of the file are the traversal functions.  The last one
71// is the one that starts the traversal.
72//
73// Return true from interior nodes to have the external traversal
74// continue on to children.  If you process children yourself,
75// return false.
76//
77
78void TOutputTraverser::visitSymbol(TIntermSymbol* node)
79{
80    OutputTreeText(sink, node, depth);
81
82    sink << "'" << node->getSymbol() << "' ";
83    sink << "(" << node->getCompleteString() << ")\n";
84}
85
86bool TOutputTraverser::visitBinary(Visit visit, TIntermBinary* node)
87{
88    TInfoSinkBase& out = sink;
89
90    OutputTreeText(out, node, depth);
91
92    switch (node->getOp()) {
93        case EOpAssign:                   out << "move second child to first child";           break;
94        case EOpInitialize:               out << "initialize first child with second child";   break;
95        case EOpAddAssign:                out << "add second child into first child";          break;
96        case EOpSubAssign:                out << "subtract second child into first child";     break;
97        case EOpMulAssign:                out << "multiply second child into first child";     break;
98        case EOpVectorTimesMatrixAssign:  out << "matrix mult second child into first child";  break;
99        case EOpVectorTimesScalarAssign:  out << "vector scale second child into first child"; break;
100        case EOpMatrixTimesScalarAssign:  out << "matrix scale second child into first child"; break;
101        case EOpMatrixTimesMatrixAssign:  out << "matrix mult second child into first child"; break;
102        case EOpDivAssign:                out << "divide second child into first child";       break;
103        case EOpIndexDirect:   out << "direct index";   break;
104        case EOpIndexIndirect: out << "indirect index"; break;
105        case EOpIndexDirectStruct:   out << "direct index for structure";   break;
106        case EOpVectorSwizzle: out << "vector swizzle"; break;
107
108        case EOpAdd:    out << "add";                     break;
109        case EOpSub:    out << "subtract";                break;
110        case EOpMul:    out << "component-wise multiply"; break;
111        case EOpDiv:    out << "divide";                  break;
112        case EOpEqual:            out << "Compare Equal";                 break;
113        case EOpNotEqual:         out << "Compare Not Equal";             break;
114        case EOpLessThan:         out << "Compare Less Than";             break;
115        case EOpGreaterThan:      out << "Compare Greater Than";          break;
116        case EOpLessThanEqual:    out << "Compare Less Than or Equal";    break;
117        case EOpGreaterThanEqual: out << "Compare Greater Than or Equal"; break;
118
119        case EOpVectorTimesScalar: out << "vector-scale";          break;
120        case EOpVectorTimesMatrix: out << "vector-times-matrix";   break;
121        case EOpMatrixTimesVector: out << "matrix-times-vector";   break;
122        case EOpMatrixTimesScalar: out << "matrix-scale";          break;
123        case EOpMatrixTimesMatrix: out << "matrix-multiply";       break;
124
125        case EOpLogicalOr:  out << "logical-or";   break;
126        case EOpLogicalXor: out << "logical-xor"; break;
127        case EOpLogicalAnd: out << "logical-and"; break;
128        default: out << "<unknown op>";
129    }
130
131    out << " (" << node->getCompleteString() << ")";
132
133    out << "\n";
134
135    return true;
136}
137
138bool TOutputTraverser::visitUnary(Visit visit, TIntermUnary* node)
139{
140    TInfoSinkBase& out = sink;
141
142    OutputTreeText(out, node, depth);
143
144    switch (node->getOp()) {
145        case EOpNegative:       out << "Negate value";         break;
146        case EOpVectorLogicalNot:
147        case EOpLogicalNot:     out << "Negate conditional";   break;
148
149        case EOpPostIncrement:  out << "Post-Increment";       break;
150        case EOpPostDecrement:  out << "Post-Decrement";       break;
151        case EOpPreIncrement:   out << "Pre-Increment";        break;
152        case EOpPreDecrement:   out << "Pre-Decrement";        break;
153
154        case EOpConvIntToBool:  out << "Convert int to bool";  break;
155        case EOpConvFloatToBool:out << "Convert float to bool";break;
156        case EOpConvBoolToFloat:out << "Convert bool to float";break;
157        case EOpConvIntToFloat: out << "Convert int to float"; break;
158        case EOpConvFloatToInt: out << "Convert float to int"; break;
159        case EOpConvBoolToInt:  out << "Convert bool to int";  break;
160
161        case EOpRadians:        out << "radians";              break;
162        case EOpDegrees:        out << "degrees";              break;
163        case EOpSin:            out << "sine";                 break;
164        case EOpCos:            out << "cosine";               break;
165        case EOpTan:            out << "tangent";              break;
166        case EOpAsin:           out << "arc sine";             break;
167        case EOpAcos:           out << "arc cosine";           break;
168        case EOpAtan:           out << "arc tangent";          break;
169
170        case EOpExp:            out << "exp";                  break;
171        case EOpLog:            out << "log";                  break;
172        case EOpExp2:           out << "exp2";                 break;
173        case EOpLog2:           out << "log2";                 break;
174        case EOpSqrt:           out << "sqrt";                 break;
175        case EOpInverseSqrt:    out << "inverse sqrt";         break;
176
177        case EOpAbs:            out << "Absolute value";       break;
178        case EOpSign:           out << "Sign";                 break;
179        case EOpFloor:          out << "Floor";                break;
180        case EOpCeil:           out << "Ceiling";              break;
181        case EOpFract:          out << "Fraction";             break;
182
183        case EOpLength:         out << "length";               break;
184        case EOpNormalize:      out << "normalize";            break;
185            //	case EOpDPdx:           out << "dPdx";                 break;
186            //	case EOpDPdy:           out << "dPdy";                 break;
187            //	case EOpFwidth:         out << "fwidth";               break;
188
189        case EOpAny:            out << "any";                  break;
190        case EOpAll:            out << "all";                  break;
191
192        default: out.message(EPrefixError, "Bad unary op");
193    }
194
195    out << " (" << node->getCompleteString() << ")";
196
197    out << "\n";
198
199    return true;
200}
201
202bool TOutputTraverser::visitAggregate(Visit visit, TIntermAggregate* node)
203{
204    TInfoSinkBase& out = sink;
205
206    if (node->getOp() == EOpNull) {
207        out.message(EPrefixError, "node is still EOpNull!");
208        return true;
209    }
210
211    OutputTreeText(out, node, depth);
212
213    switch (node->getOp()) {
214        case EOpSequence:      out << "Sequence\n"; return true;
215        case EOpComma:         out << "Comma\n"; return true;
216        case EOpFunction:      out << "Function Definition: " << node->getName(); break;
217        case EOpFunctionCall:  out << "Function Call: " << node->getName(); break;
218        case EOpParameters:    out << "Function Parameters: ";              break;
219
220        case EOpConstructFloat: out << "Construct float"; break;
221        case EOpConstructVec2:  out << "Construct vec2";  break;
222        case EOpConstructVec3:  out << "Construct vec3";  break;
223        case EOpConstructVec4:  out << "Construct vec4";  break;
224        case EOpConstructBool:  out << "Construct bool";  break;
225        case EOpConstructBVec2: out << "Construct bvec2"; break;
226        case EOpConstructBVec3: out << "Construct bvec3"; break;
227        case EOpConstructBVec4: out << "Construct bvec4"; break;
228        case EOpConstructInt:   out << "Construct int";   break;
229        case EOpConstructIVec2: out << "Construct ivec2"; break;
230        case EOpConstructIVec3: out << "Construct ivec3"; break;
231        case EOpConstructIVec4: out << "Construct ivec4"; break;
232        case EOpConstructMat2:  out << "Construct mat2";  break;
233        case EOpConstructMat3:  out << "Construct mat3";  break;
234        case EOpConstructMat4:  out << "Construct mat4";  break;
235        case EOpConstructStruct:  out << "Construct structure";  break;
236
237        case EOpLessThan:         out << "Compare Less Than";             break;
238        case EOpGreaterThan:      out << "Compare Greater Than";          break;
239        case EOpLessThanEqual:    out << "Compare Less Than or Equal";    break;
240        case EOpGreaterThanEqual: out << "Compare Greater Than or Equal"; break;
241        case EOpVectorEqual:      out << "Equal";                         break;
242        case EOpVectorNotEqual:   out << "NotEqual";                      break;
243
244        case EOpMod:           out << "mod";         break;
245        case EOpPow:           out << "pow";         break;
246
247        case EOpAtan:          out << "arc tangent"; break;
248
249        case EOpMin:           out << "min";         break;
250        case EOpMax:           out << "max";         break;
251        case EOpClamp:         out << "clamp";       break;
252        case EOpMix:           out << "mix";         break;
253        case EOpStep:          out << "step";        break;
254        case EOpSmoothStep:    out << "smoothstep";  break;
255
256        case EOpDistance:      out << "distance";                break;
257        case EOpDot:           out << "dot-product";             break;
258        case EOpCross:         out << "cross-product";           break;
259        case EOpFaceForward:   out << "face-forward";            break;
260        case EOpReflect:       out << "reflect";                 break;
261        case EOpRefract:       out << "refract";                 break;
262        case EOpMul:           out << "component-wise multiply"; break;
263
264        default: out.message(EPrefixError, "Bad aggregation op");
265    }
266
267    if (node->getOp() != EOpSequence && node->getOp() != EOpParameters)
268        out << " (" << node->getCompleteString() << ")";
269
270    out << "\n";
271
272    return true;
273}
274
275bool TOutputTraverser::visitSelection(Visit visit, TIntermSelection* node)
276{
277    TInfoSinkBase& out = sink;
278
279    OutputTreeText(out, node, depth);
280
281    out << "Test condition and select";
282    out << " (" << node->getCompleteString() << ")\n";
283
284    ++depth;
285
286    OutputTreeText(sink, node, depth);
287    out << "Condition\n";
288    node->getCondition()->traverse(this);
289
290    OutputTreeText(sink, node, depth);
291    if (node->getTrueBlock()) {
292        out << "true case\n";
293        node->getTrueBlock()->traverse(this);
294    } else
295        out << "true case is null\n";
296
297    if (node->getFalseBlock()) {
298        OutputTreeText(sink, node, depth);
299        out << "false case\n";
300        node->getFalseBlock()->traverse(this);
301    }
302
303    --depth;
304
305    return false;
306}
307
308void TOutputTraverser::visitConstantUnion(TIntermConstantUnion* node)
309{
310    TInfoSinkBase& out = sink;
311
312    int size = node->getType().getObjectSize();
313
314    for (int i = 0; i < size; i++) {
315        OutputTreeText(out, node, depth);
316        switch (node->getUnionArrayPointer()[i].getType()) {
317            case EbtBool:
318                if (node->getUnionArrayPointer()[i].getBConst())
319                    out << "true";
320                else
321                    out << "false";
322
323                out << " (" << "const bool" << ")";
324                out << "\n";
325                break;
326            case EbtFloat:
327                out << node->getUnionArrayPointer()[i].getFConst();
328                out << " (const float)\n";
329                break;
330            case EbtInt:
331                out << node->getUnionArrayPointer()[i].getIConst();
332                out << " (const int)\n";
333                break;
334            default:
335                out.message(EPrefixInternalError, "Unknown constant", node->getLine());
336                break;
337        }
338    }
339}
340
341bool TOutputTraverser::visitLoop(Visit visit, TIntermLoop* node)
342{
343    TInfoSinkBase& out = sink;
344
345    OutputTreeText(out, node, depth);
346
347    out << "Loop with condition ";
348    if (node->getType() == ELoopDoWhile)
349        out << "not ";
350    out << "tested first\n";
351
352    ++depth;
353
354    OutputTreeText(sink, node, depth);
355    if (node->getCondition()) {
356        out << "Loop Condition\n";
357        node->getCondition()->traverse(this);
358    } else
359        out << "No loop condition\n";
360
361    OutputTreeText(sink, node, depth);
362    if (node->getBody()) {
363        out << "Loop Body\n";
364        node->getBody()->traverse(this);
365    } else
366        out << "No loop body\n";
367
368    if (node->getExpression()) {
369        OutputTreeText(sink, node, depth);
370        out << "Loop Terminal Expression\n";
371        node->getExpression()->traverse(this);
372    }
373
374    --depth;
375
376    return false;
377}
378
379bool TOutputTraverser::visitBranch(Visit visit, TIntermBranch* node)
380{
381    TInfoSinkBase& out = sink;
382
383    OutputTreeText(out, node, depth);
384
385    switch (node->getFlowOp()) {
386        case EOpKill:      out << "Branch: Kill";           break;
387        case EOpBreak:     out << "Branch: Break";          break;
388        case EOpContinue:  out << "Branch: Continue";       break;
389        case EOpReturn:    out << "Branch: Return";         break;
390        default:           out << "Branch: Unknown Branch"; break;
391    }
392
393    if (node->getExpression()) {
394        out << " with expression\n";
395        ++depth;
396        node->getExpression()->traverse(this);
397        --depth;
398    } else
399        out << "\n";
400
401    return false;
402}
403
404//
405// This function is the one to call externally to start the traversal.
406// Individual functions can be initialized to 0 to skip processing of that
407// type of node.  It's children will still be processed.
408//
409void TIntermediate::outputTree(TIntermNode* root)
410{
411    if (root == 0)
412        return;
413
414    TOutputTraverser it(infoSink.info);
415
416    root->traverse(&it);
417}
418