initial import
[vuplus_webkit] / Source / ThirdParty / ANGLE / src / compiler / OutputGLSLBase.cpp
1 //
2 // Copyright (c) 2002-2011 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/OutputGLSLBase.h"
8 #include "compiler/debug.h"
9
10 namespace
11 {
12 TString getTypeName(const TType& type)
13 {
14     TInfoSinkBase out;
15     if (type.isMatrix())
16     {
17         out << "mat";
18         out << type.getNominalSize();
19     }
20     else if (type.isVector())
21     {
22         switch (type.getBasicType())
23         {
24             case EbtFloat: out << "vec"; break;
25             case EbtInt: out << "ivec"; break;
26             case EbtBool: out << "bvec"; break;
27             default: UNREACHABLE(); break;
28         }
29         out << type.getNominalSize();
30     }
31     else
32     {
33         if (type.getBasicType() == EbtStruct)
34             out << type.getTypeName();
35         else
36             out << type.getBasicString();
37     }
38     return TString(out.c_str());
39 }
40
41 TString arrayBrackets(const TType& type)
42 {
43     ASSERT(type.isArray());
44     TInfoSinkBase out;
45     out << "[" << type.getArraySize() << "]";
46     return TString(out.c_str());
47 }
48
49 bool isSingleStatement(TIntermNode* node) {
50     if (const TIntermAggregate* aggregate = node->getAsAggregate())
51     {
52         return (aggregate->getOp() != EOpFunction) &&
53                (aggregate->getOp() != EOpSequence);
54     }
55     else if (const TIntermSelection* selection = node->getAsSelectionNode())
56     {
57         // Ternary operators are usually part of an assignment operator.
58         // This handles those rare cases in which they are all by themselves.
59         return selection->usesTernaryOperator();
60     }
61     else if (node->getAsLoopNode())
62     {
63         return false;
64     }
65     return true;
66 }
67 }  // namespace
68
69 TOutputGLSLBase::TOutputGLSLBase(TInfoSinkBase& objSink)
70     : TIntermTraverser(true, true, true),
71       mObjSink(objSink),
72       mDeclaringVariables(false)
73 {
74 }
75
76 void TOutputGLSLBase::writeTriplet(Visit visit, const char* preStr, const char* inStr, const char* postStr)
77 {
78     TInfoSinkBase& out = objSink();
79     if (visit == PreVisit && preStr)
80     {
81         out << preStr;
82     }
83     else if (visit == InVisit && inStr)
84     {
85         out << inStr;
86     }
87     else if (visit == PostVisit && postStr)
88     {
89         out << postStr;
90     }
91 }
92
93 void TOutputGLSLBase::writeVariableType(const TType& type)
94 {
95     TInfoSinkBase& out = objSink();
96     TQualifier qualifier = type.getQualifier();
97     // TODO(alokp): Validate qualifier for variable declarations.
98     if ((qualifier != EvqTemporary) && (qualifier != EvqGlobal))
99         out << type.getQualifierString() << " ";
100     // Declare the struct if we have not done so already.
101     if ((type.getBasicType() == EbtStruct) &&
102         (mDeclaredStructs.find(type.getTypeName()) == mDeclaredStructs.end()))
103     {
104         out << "struct " << type.getTypeName() << "{\n";
105         const TTypeList* structure = type.getStruct();
106         ASSERT(structure != NULL);
107         for (size_t i = 0; i < structure->size(); ++i)
108         {
109             const TType* fieldType = (*structure)[i].type;
110             ASSERT(fieldType != NULL);
111             if (writeVariablePrecision(fieldType->getPrecision()))
112                 out << " ";
113             out << getTypeName(*fieldType) << " " << fieldType->getFieldName();
114             if (fieldType->isArray())
115                 out << arrayBrackets(*fieldType);
116             out << ";\n";
117         }
118         out << "}";
119         mDeclaredStructs.insert(type.getTypeName());
120     }
121     else
122     {
123         if (writeVariablePrecision(type.getPrecision()))
124             out << " ";
125         out << getTypeName(type);
126     }
127 }
128
129 void TOutputGLSLBase::writeFunctionParameters(const TIntermSequence& args)
130 {
131     TInfoSinkBase& out = objSink();
132     for (TIntermSequence::const_iterator iter = args.begin();
133          iter != args.end(); ++iter)
134     {
135         const TIntermSymbol* arg = (*iter)->getAsSymbolNode();
136         ASSERT(arg != NULL);
137
138         const TType& type = arg->getType();
139         writeVariableType(type);
140
141         const TString& name = arg->getSymbol();
142         if (!name.empty())
143             out << " " << name;
144         if (type.isArray())
145             out << arrayBrackets(type);
146
147         // Put a comma if this is not the last argument.
148         if (iter != args.end() - 1)
149             out << ", ";
150     }
151 }
152
153 const ConstantUnion* TOutputGLSLBase::writeConstantUnion(const TType& type,
154                                                          const ConstantUnion* pConstUnion)
155 {
156     TInfoSinkBase& out = objSink();
157
158     if (type.getBasicType() == EbtStruct)
159     {
160         out << type.getTypeName() << "(";
161         const TTypeList* structure = type.getStruct();
162         ASSERT(structure != NULL);
163         for (size_t i = 0; i < structure->size(); ++i)
164         {
165             const TType* fieldType = (*structure)[i].type;
166             ASSERT(fieldType != NULL);
167             pConstUnion = writeConstantUnion(*fieldType, pConstUnion);
168             if (i != structure->size() - 1) out << ", ";
169         }
170         out << ")";
171     }
172     else
173     {
174         int size = type.getObjectSize();
175         bool writeType = size > 1;
176         if (writeType) out << getTypeName(type) << "(";
177         for (int i = 0; i < size; ++i, ++pConstUnion)
178         {
179             switch (pConstUnion->getType())
180             {
181                 case EbtFloat: out << pConstUnion->getFConst(); break;
182                 case EbtInt: out << pConstUnion->getIConst(); break;
183                 case EbtBool: out << pConstUnion->getBConst(); break;
184                 default: UNREACHABLE();
185             }
186             if (i != size - 1) out << ", ";
187         }
188         if (writeType) out << ")";
189     }
190     return pConstUnion;
191 }
192
193 void TOutputGLSLBase::visitSymbol(TIntermSymbol* node)
194 {
195     TInfoSinkBase& out = objSink();
196     if (mLoopUnroll.NeedsToReplaceSymbolWithValue(node))
197         out << mLoopUnroll.GetLoopIndexValue(node);
198     else
199         out << node->getSymbol();
200
201     if (mDeclaringVariables && node->getType().isArray())
202         out << arrayBrackets(node->getType());
203 }
204
205 void TOutputGLSLBase::visitConstantUnion(TIntermConstantUnion* node)
206 {
207     writeConstantUnion(node->getType(), node->getUnionArrayPointer());
208 }
209
210 bool TOutputGLSLBase::visitBinary(Visit visit, TIntermBinary* node)
211 {
212     bool visitChildren = true;
213     TInfoSinkBase& out = objSink();
214     switch (node->getOp())
215     {
216         case EOpInitialize:
217             if (visit == InVisit)
218             {
219                 out << " = ";
220                 // RHS of initialize is not being declared.
221                 mDeclaringVariables = false;
222             }
223             break;
224         case EOpAssign: writeTriplet(visit, "(", " = ", ")"); break;
225         case EOpAddAssign: writeTriplet(visit, "(", " += ", ")"); break;
226         case EOpSubAssign: writeTriplet(visit, "(", " -= ", ")"); break;
227         case EOpDivAssign: writeTriplet(visit, "(", " /= ", ")"); break;
228         // Notice the fall-through.
229         case EOpMulAssign: 
230         case EOpVectorTimesMatrixAssign:
231         case EOpVectorTimesScalarAssign:
232         case EOpMatrixTimesScalarAssign:
233         case EOpMatrixTimesMatrixAssign:
234             writeTriplet(visit, "(", " *= ", ")");
235             break;
236
237         case EOpIndexDirect:
238         case EOpIndexIndirect:
239             writeTriplet(visit, NULL, "[", "]");
240             break;
241         case EOpIndexDirectStruct:
242             if (visit == InVisit)
243             {
244                 out << ".";
245                 // TODO(alokp): ASSERT
246                 out << node->getType().getFieldName();
247                 visitChildren = false;
248             }
249             break;
250         case EOpVectorSwizzle:
251             if (visit == InVisit)
252             {
253                 out << ".";
254                 TIntermAggregate* rightChild = node->getRight()->getAsAggregate();
255                 TIntermSequence& sequence = rightChild->getSequence();
256                 for (TIntermSequence::iterator sit = sequence.begin(); sit != sequence.end(); ++sit)
257                 {
258                     TIntermConstantUnion* element = (*sit)->getAsConstantUnion();
259                     ASSERT(element->getBasicType() == EbtInt);
260                     ASSERT(element->getNominalSize() == 1);
261                     const ConstantUnion& data = element->getUnionArrayPointer()[0];
262                     ASSERT(data.getType() == EbtInt);
263                     switch (data.getIConst())
264                     {
265                         case 0: out << "x"; break;
266                         case 1: out << "y"; break;
267                         case 2: out << "z"; break;
268                         case 3: out << "w"; break;
269                         default: UNREACHABLE(); break;
270                     }
271                 }
272                 visitChildren = false;
273             }
274             break;
275
276         case EOpAdd: writeTriplet(visit, "(", " + ", ")"); break;
277         case EOpSub: writeTriplet(visit, "(", " - ", ")"); break;
278         case EOpMul: writeTriplet(visit, "(", " * ", ")"); break;
279         case EOpDiv: writeTriplet(visit, "(", " / ", ")"); break;
280         case EOpMod: UNIMPLEMENTED(); break;
281         case EOpEqual: writeTriplet(visit, "(", " == ", ")"); break;
282         case EOpNotEqual: writeTriplet(visit, "(", " != ", ")"); break;
283         case EOpLessThan: writeTriplet(visit, "(", " < ", ")"); break;
284         case EOpGreaterThan: writeTriplet(visit, "(", " > ", ")"); break;
285         case EOpLessThanEqual: writeTriplet(visit, "(", " <= ", ")"); break;
286         case EOpGreaterThanEqual: writeTriplet(visit, "(", " >= ", ")"); break;
287
288         // Notice the fall-through.
289         case EOpVectorTimesScalar:
290         case EOpVectorTimesMatrix:
291         case EOpMatrixTimesVector:
292         case EOpMatrixTimesScalar:
293         case EOpMatrixTimesMatrix:
294             writeTriplet(visit, "(", " * ", ")");
295             break;
296
297         case EOpLogicalOr: writeTriplet(visit, "(", " || ", ")"); break;
298         case EOpLogicalXor: writeTriplet(visit, "(", " ^^ ", ")"); break;
299         case EOpLogicalAnd: writeTriplet(visit, "(", " && ", ")"); break;
300         default: UNREACHABLE(); break;
301     }
302
303     return visitChildren;
304 }
305
306 bool TOutputGLSLBase::visitUnary(Visit visit, TIntermUnary* node)
307 {
308     switch (node->getOp())
309     {
310         case EOpNegative: writeTriplet(visit, "(-", NULL, ")"); break;
311         case EOpVectorLogicalNot: writeTriplet(visit, "not(", NULL, ")"); break;
312         case EOpLogicalNot: writeTriplet(visit, "(!", NULL, ")"); break;
313
314         case EOpPostIncrement: writeTriplet(visit, "(", NULL, "++)"); break;
315         case EOpPostDecrement: writeTriplet(visit, "(", NULL, "--)"); break;
316         case EOpPreIncrement: writeTriplet(visit, "(++", NULL, ")"); break;
317         case EOpPreDecrement: writeTriplet(visit, "(--", NULL, ")"); break;
318
319         case EOpConvIntToBool:
320         case EOpConvFloatToBool:
321             switch (node->getOperand()->getType().getNominalSize())
322             {
323                 case 1: writeTriplet(visit, "bool(", NULL, ")");  break;
324                 case 2: writeTriplet(visit, "bvec2(", NULL, ")"); break;
325                 case 3: writeTriplet(visit, "bvec3(", NULL, ")"); break;
326                 case 4: writeTriplet(visit, "bvec4(", NULL, ")"); break;
327                 default: UNREACHABLE();
328             }
329             break;
330         case EOpConvBoolToFloat:
331         case EOpConvIntToFloat:
332             switch (node->getOperand()->getType().getNominalSize())
333             {
334                 case 1: writeTriplet(visit, "float(", NULL, ")");  break;
335                 case 2: writeTriplet(visit, "vec2(", NULL, ")"); break;
336                 case 3: writeTriplet(visit, "vec3(", NULL, ")"); break;
337                 case 4: writeTriplet(visit, "vec4(", NULL, ")"); break;
338                 default: UNREACHABLE();
339             }
340             break;
341         case EOpConvFloatToInt:
342         case EOpConvBoolToInt:
343             switch (node->getOperand()->getType().getNominalSize())
344             {
345                 case 1: writeTriplet(visit, "int(", NULL, ")");  break;
346                 case 2: writeTriplet(visit, "ivec2(", NULL, ")"); break;
347                 case 3: writeTriplet(visit, "ivec3(", NULL, ")"); break;
348                 case 4: writeTriplet(visit, "ivec4(", NULL, ")"); break;
349                 default: UNREACHABLE();
350             }
351             break;
352
353         case EOpRadians: writeTriplet(visit, "radians(", NULL, ")"); break;
354         case EOpDegrees: writeTriplet(visit, "degrees(", NULL, ")"); break;
355         case EOpSin: writeTriplet(visit, "sin(", NULL, ")"); break;
356         case EOpCos: writeTriplet(visit, "cos(", NULL, ")"); break;
357         case EOpTan: writeTriplet(visit, "tan(", NULL, ")"); break;
358         case EOpAsin: writeTriplet(visit, "asin(", NULL, ")"); break;
359         case EOpAcos: writeTriplet(visit, "acos(", NULL, ")"); break;
360         case EOpAtan: writeTriplet(visit, "atan(", NULL, ")"); break;
361
362         case EOpExp: writeTriplet(visit, "exp(", NULL, ")"); break;
363         case EOpLog: writeTriplet(visit, "log(", NULL, ")"); break;
364         case EOpExp2: writeTriplet(visit, "exp2(", NULL, ")"); break;
365         case EOpLog2: writeTriplet(visit, "log2(", NULL, ")"); break;
366         case EOpSqrt: writeTriplet(visit, "sqrt(", NULL, ")"); break;
367         case EOpInverseSqrt: writeTriplet(visit, "inversesqrt(", NULL, ")"); break;
368
369         case EOpAbs: writeTriplet(visit, "abs(", NULL, ")"); break;
370         case EOpSign: writeTriplet(visit, "sign(", NULL, ")"); break;
371         case EOpFloor: writeTriplet(visit, "floor(", NULL, ")"); break;
372         case EOpCeil: writeTriplet(visit, "ceil(", NULL, ")"); break;
373         case EOpFract: writeTriplet(visit, "fract(", NULL, ")"); break;
374
375         case EOpLength: writeTriplet(visit, "length(", NULL, ")"); break;
376         case EOpNormalize: writeTriplet(visit, "normalize(", NULL, ")"); break;
377
378         case EOpDFdx: writeTriplet(visit, "dFdx(", NULL, ")"); break;
379         case EOpDFdy: writeTriplet(visit, "dFdy(", NULL, ")"); break;
380         case EOpFwidth: writeTriplet(visit, "fwidth(", NULL, ")"); break;
381
382         case EOpAny: writeTriplet(visit, "any(", NULL, ")"); break;
383         case EOpAll: writeTriplet(visit, "all(", NULL, ")"); break;
384
385         default: UNREACHABLE(); break;
386     }
387
388     return true;
389 }
390
391 bool TOutputGLSLBase::visitSelection(Visit visit, TIntermSelection* node)
392 {
393     TInfoSinkBase& out = objSink();
394
395     if (node->usesTernaryOperator())
396     {
397         // Notice two brackets at the beginning and end. The outer ones
398         // encapsulate the whole ternary expression. This preserves the
399         // order of precedence when ternary expressions are used in a
400         // compound expression, i.e., c = 2 * (a < b ? 1 : 2).
401         out << "((";
402         node->getCondition()->traverse(this);
403         out << ") ? (";
404         node->getTrueBlock()->traverse(this);
405         out << ") : (";
406         node->getFalseBlock()->traverse(this);
407         out << "))";
408     }
409     else
410     {
411         out << "if (";
412         node->getCondition()->traverse(this);
413         out << ")\n";
414
415         incrementDepth();
416         visitCodeBlock(node->getTrueBlock());
417
418         if (node->getFalseBlock())
419         {
420             out << "else\n";
421             visitCodeBlock(node->getFalseBlock());
422         }
423         decrementDepth();
424     }
425     return false;
426 }
427
428 bool TOutputGLSLBase::visitAggregate(Visit visit, TIntermAggregate* node)
429 {
430     bool visitChildren = true;
431     TInfoSinkBase& out = objSink();
432     switch (node->getOp())
433     {
434         case EOpSequence: {
435             // Scope the sequences except when at the global scope.
436             if (depth > 0) out << "{\n";
437
438             incrementDepth();
439             const TIntermSequence& sequence = node->getSequence();
440             for (TIntermSequence::const_iterator iter = sequence.begin();
441                  iter != sequence.end(); ++iter)
442             {
443                 TIntermNode* node = *iter;
444                 ASSERT(node != NULL);
445                 node->traverse(this);
446
447                 if (isSingleStatement(node))
448                     out << ";\n";
449             }
450             decrementDepth();
451
452             // Scope the sequences except when at the global scope.
453             if (depth > 0) out << "}\n";
454             visitChildren = false;
455             break;
456         }
457         case EOpPrototype: {
458             // Function declaration.
459             ASSERT(visit == PreVisit);
460             TString returnType = getTypeName(node->getType());
461             out << returnType << " " << node->getName();
462
463             out << "(";
464             writeFunctionParameters(node->getSequence());
465             out << ")";
466
467             visitChildren = false;
468             break;
469         }
470         case EOpFunction: {
471             // Function definition.
472             ASSERT(visit == PreVisit);
473             writeVariableType(node->getType());
474             out << " " << TFunction::unmangleName(node->getName());
475
476             incrementDepth();
477             // Function definition node contains one or two children nodes
478             // representing function parameters and function body. The latter
479             // is not present in case of empty function bodies.
480             const TIntermSequence& sequence = node->getSequence();
481             ASSERT((sequence.size() == 1) || (sequence.size() == 2));
482             TIntermSequence::const_iterator seqIter = sequence.begin();
483
484             // Traverse function parameters.
485             TIntermAggregate* params = (*seqIter)->getAsAggregate();
486             ASSERT(params != NULL);
487             ASSERT(params->getOp() == EOpParameters);
488             params->traverse(this);
489
490             // Traverse function body.
491             TIntermAggregate* body = ++seqIter != sequence.end() ?
492                 (*seqIter)->getAsAggregate() : NULL;
493             visitCodeBlock(body);
494             decrementDepth();
495  
496             // Fully processed; no need to visit children.
497             visitChildren = false;
498             break;
499         }
500         case EOpFunctionCall:
501             // Function call.
502             if (visit == PreVisit)
503             {
504                 TString functionName = TFunction::unmangleName(node->getName());
505                 out << functionName << "(";
506             }
507             else if (visit == InVisit)
508             {
509                 out << ", ";
510             }
511             else
512             {
513                 out << ")";
514             }
515             break;
516         case EOpParameters: {
517             // Function parameters.
518             ASSERT(visit == PreVisit);
519             out << "(";
520             writeFunctionParameters(node->getSequence());
521             out << ")";
522             visitChildren = false;
523             break;
524         }
525         case EOpDeclaration: {
526             // Variable declaration.
527             if (visit == PreVisit)
528             {
529                 const TIntermSequence& sequence = node->getSequence();
530                 const TIntermTyped* variable = sequence.front()->getAsTyped();
531                 writeVariableType(variable->getType());
532                 out << " ";
533                 mDeclaringVariables = true;
534             }
535             else if (visit == InVisit)
536             {
537                 out << ", ";
538                 mDeclaringVariables = true;
539             }
540             else
541             {
542                 mDeclaringVariables = false;
543             }
544             break;
545         }
546         case EOpConstructFloat: writeTriplet(visit, "float(", NULL, ")"); break;
547         case EOpConstructVec2: writeTriplet(visit, "vec2(", ", ", ")"); break;
548         case EOpConstructVec3: writeTriplet(visit, "vec3(", ", ", ")"); break;
549         case EOpConstructVec4: writeTriplet(visit, "vec4(", ", ", ")"); break;
550         case EOpConstructBool: writeTriplet(visit, "bool(", NULL, ")"); break;
551         case EOpConstructBVec2: writeTriplet(visit, "bvec2(", ", ", ")"); break;
552         case EOpConstructBVec3: writeTriplet(visit, "bvec3(", ", ", ")"); break;
553         case EOpConstructBVec4: writeTriplet(visit, "bvec4(", ", ", ")"); break;
554         case EOpConstructInt: writeTriplet(visit, "int(", NULL, ")"); break;
555         case EOpConstructIVec2: writeTriplet(visit, "ivec2(", ", ", ")"); break;
556         case EOpConstructIVec3: writeTriplet(visit, "ivec3(", ", ", ")"); break;
557         case EOpConstructIVec4: writeTriplet(visit, "ivec4(", ", ", ")"); break;
558         case EOpConstructMat2: writeTriplet(visit, "mat2(", ", ", ")"); break;
559         case EOpConstructMat3: writeTriplet(visit, "mat3(", ", ", ")"); break;
560         case EOpConstructMat4: writeTriplet(visit, "mat4(", ", ", ")"); break;
561         case EOpConstructStruct:
562             if (visit == PreVisit)
563             {
564                 const TType& type = node->getType();
565                 ASSERT(type.getBasicType() == EbtStruct);
566                 out << type.getTypeName() << "(";
567             }
568             else if (visit == InVisit)
569             {
570                 out << ", ";
571             }
572             else
573             {
574                 out << ")";
575             }
576             break;
577
578         case EOpLessThan: writeTriplet(visit, "lessThan(", ", ", ")"); break;
579         case EOpGreaterThan: writeTriplet(visit, "greaterThan(", ", ", ")"); break;
580         case EOpLessThanEqual: writeTriplet(visit, "lessThanEqual(", ", ", ")"); break;
581         case EOpGreaterThanEqual: writeTriplet(visit, "greaterThanEqual(", ", ", ")"); break;
582         case EOpVectorEqual: writeTriplet(visit, "equal(", ", ", ")"); break;
583         case EOpVectorNotEqual: writeTriplet(visit, "notEqual(", ", ", ")"); break;
584         case EOpComma: writeTriplet(visit, NULL, ", ", NULL); break;
585
586         case EOpMod: writeTriplet(visit, "mod(", ", ", ")"); break;
587         case EOpPow: writeTriplet(visit, "pow(", ", ", ")"); break;
588         case EOpAtan: writeTriplet(visit, "atan(", ", ", ")"); break;
589         case EOpMin: writeTriplet(visit, "min(", ", ", ")"); break;
590         case EOpMax: writeTriplet(visit, "max(", ", ", ")"); break;
591         case EOpClamp: writeTriplet(visit, "clamp(", ", ", ")"); break;
592         case EOpMix: writeTriplet(visit, "mix(", ", ", ")"); break;
593         case EOpStep: writeTriplet(visit, "step(", ", ", ")"); break;
594         case EOpSmoothStep: writeTriplet(visit, "smoothstep(", ", ", ")"); break;
595
596         case EOpDistance: writeTriplet(visit, "distance(", ", ", ")"); break;
597         case EOpDot: writeTriplet(visit, "dot(", ", ", ")"); break;
598         case EOpCross: writeTriplet(visit, "cross(", ", ", ")"); break;
599         case EOpFaceForward: writeTriplet(visit, "faceforward(", ", ", ")"); break;
600         case EOpReflect: writeTriplet(visit, "reflect(", ", ", ")"); break;
601         case EOpRefract: writeTriplet(visit, "refract(", ", ", ")"); break;
602         case EOpMul: writeTriplet(visit, "matrixCompMult(", ", ", ")"); break;
603
604         default: UNREACHABLE(); break;
605     }
606     return visitChildren;
607 }
608
609 bool TOutputGLSLBase::visitLoop(Visit visit, TIntermLoop* node)
610 {
611     TInfoSinkBase& out = objSink();
612
613     incrementDepth();
614     // Loop header.
615     TLoopType loopType = node->getType();
616     if (loopType == ELoopFor)  // for loop
617     {
618         if (!node->getUnrollFlag()) {
619             out << "for (";
620             if (node->getInit())
621                 node->getInit()->traverse(this);
622             out << "; ";
623
624             if (node->getCondition())
625                 node->getCondition()->traverse(this);
626             out << "; ";
627
628             if (node->getExpression())
629                 node->getExpression()->traverse(this);
630             out << ")\n";
631         }
632     }
633     else if (loopType == ELoopWhile)  // while loop
634     {
635         out << "while (";
636         ASSERT(node->getCondition() != NULL);
637         node->getCondition()->traverse(this);
638         out << ")\n";
639     }
640     else  // do-while loop
641     {
642         ASSERT(loopType == ELoopDoWhile);
643         out << "do\n";
644     }
645
646     // Loop body.
647     if (node->getUnrollFlag())
648     {
649         TLoopIndexInfo indexInfo;
650         mLoopUnroll.FillLoopIndexInfo(node, indexInfo);
651         mLoopUnroll.Push(indexInfo);
652         while (mLoopUnroll.SatisfiesLoopCondition())
653         {
654             visitCodeBlock(node->getBody());
655             mLoopUnroll.Step();
656         }
657         mLoopUnroll.Pop();
658     }
659     else
660     {
661         visitCodeBlock(node->getBody());
662     }
663
664     // Loop footer.
665     if (loopType == ELoopDoWhile)  // do-while loop
666     {
667         out << "while (";
668         ASSERT(node->getCondition() != NULL);
669         node->getCondition()->traverse(this);
670         out << ");\n";
671     }
672     decrementDepth();
673
674     // No need to visit children. They have been already processed in
675     // this function.
676     return false;
677 }
678
679 bool TOutputGLSLBase::visitBranch(Visit visit, TIntermBranch* node)
680 {
681     switch (node->getFlowOp())
682     {
683         case EOpKill: writeTriplet(visit, "discard", NULL, NULL); break;
684         case EOpBreak: writeTriplet(visit, "break", NULL, NULL); break;
685         case EOpContinue: writeTriplet(visit, "continue", NULL, NULL); break;
686         case EOpReturn: writeTriplet(visit, "return ", NULL, NULL); break;
687         default: UNREACHABLE(); break;
688     }
689
690     return true;
691 }
692
693 void TOutputGLSLBase::visitCodeBlock(TIntermNode* node) {
694     TInfoSinkBase &out = objSink();
695     if (node != NULL)
696     {
697         node->traverse(this);
698         // Single statements not part of a sequence need to be terminated
699         // with semi-colon.
700         if (isSingleStatement(node))
701             out << ";\n";
702     }
703     else
704     {
705         out << "{\n}\n";  // Empty code block.
706     }
707 }