Home | History | Annotate | Download | only in compiler
      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/OutputGLSL.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 TOutputGLSL::TOutputGLSL(TInfoSinkBase& objSink)
     70     : TIntermTraverser(true, true, true),
     71       mObjSink(objSink),
     72       mDeclaringVariables(false)
     73 {
     74 }
     75 
     76 void TOutputGLSL::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 TOutputGLSL::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 
    101     // Declare the struct if we have not done so already.
    102     if ((type.getBasicType() == EbtStruct) &&
    103         (mDeclaredStructs.find(type.getTypeName()) == mDeclaredStructs.end()))
    104     {
    105         out << "struct " << type.getTypeName() << "{\n";
    106         const TTypeList* structure = type.getStruct();
    107         ASSERT(structure != NULL);
    108         for (size_t i = 0; i < structure->size(); ++i)
    109         {
    110             const TType* fieldType = (*structure)[i].type;
    111             ASSERT(fieldType != NULL);
    112             out << getTypeName(*fieldType) << " " << fieldType->getFieldName();
    113             if (fieldType->isArray())
    114                 out << arrayBrackets(*fieldType);
    115             out << ";\n";
    116         }
    117         out << "}";
    118         mDeclaredStructs.insert(type.getTypeName());
    119     }
    120     else
    121     {
    122         out << getTypeName(type);
    123     }
    124 }
    125 
    126 void TOutputGLSL::writeFunctionParameters(const TIntermSequence& args)
    127 {
    128     TInfoSinkBase& out = objSink();
    129     for (TIntermSequence::const_iterator iter = args.begin();
    130          iter != args.end(); ++iter)
    131     {
    132         const TIntermSymbol* arg = (*iter)->getAsSymbolNode();
    133         ASSERT(arg != NULL);
    134 
    135         const TType& type = arg->getType();
    136         TQualifier qualifier = type.getQualifier();
    137         // TODO(alokp): Validate qualifier for function arguments.
    138         if ((qualifier != EvqTemporary) && (qualifier != EvqGlobal))
    139             out << type.getQualifierString() << " ";
    140 
    141         out << getTypeName(type);
    142 
    143         const TString& name = arg->getSymbol();
    144         if (!name.empty())
    145             out << " " << name;
    146         if (type.isArray())
    147             out << arrayBrackets(type);
    148 
    149         // Put a comma if this is not the last argument.
    150         if (iter != args.end() - 1)
    151             out << ", ";
    152     }
    153 }
    154 
    155 const ConstantUnion* TOutputGLSL::writeConstantUnion(const TType& type,
    156                                                      const ConstantUnion* pConstUnion)
    157 {
    158     TInfoSinkBase& out = objSink();
    159 
    160     if (type.getBasicType() == EbtStruct)
    161     {
    162         out << type.getTypeName() << "(";
    163         const TTypeList* structure = type.getStruct();
    164         ASSERT(structure != NULL);
    165         for (size_t i = 0; i < structure->size(); ++i)
    166         {
    167             const TType* fieldType = (*structure)[i].type;
    168             ASSERT(fieldType != NULL);
    169             pConstUnion = writeConstantUnion(*fieldType, pConstUnion);
    170             if (i != structure->size() - 1) out << ", ";
    171         }
    172         out << ")";
    173     }
    174     else
    175     {
    176         int size = type.getObjectSize();
    177         bool writeType = size > 1;
    178         if (writeType) out << getTypeName(type) << "(";
    179         for (int i = 0; i < size; ++i, ++pConstUnion)
    180         {
    181             switch (pConstUnion->getType())
    182             {
    183                 case EbtFloat: out << pConstUnion->getFConst(); break;
    184                 case EbtInt: out << pConstUnion->getIConst(); break;
    185                 case EbtBool: out << pConstUnion->getBConst(); break;
    186                 default: UNREACHABLE();
    187             }
    188             if (i != size - 1) out << ", ";
    189         }
    190         if (writeType) out << ")";
    191     }
    192     return pConstUnion;
    193 }
    194 
    195 void TOutputGLSL::visitSymbol(TIntermSymbol* node)
    196 {
    197     TInfoSinkBase& out = objSink();
    198     out << node->getSymbol();
    199 
    200     if (mDeclaringVariables && node->getType().isArray())
    201         out << arrayBrackets(node->getType());
    202 }
    203 
    204 void TOutputGLSL::visitConstantUnion(TIntermConstantUnion* node)
    205 {
    206     writeConstantUnion(node->getType(), node->getUnionArrayPointer());
    207 }
    208 
    209 bool TOutputGLSL::visitBinary(Visit visit, TIntermBinary* node)
    210 {
    211     bool visitChildren = true;
    212     TInfoSinkBase& out = objSink();
    213     switch (node->getOp())
    214     {
    215         case EOpInitialize:
    216             if (visit == InVisit)
    217             {
    218                 out << " = ";
    219                 // RHS of initialize is not being declared.
    220                 mDeclaringVariables = false;
    221             }
    222             break;
    223         case EOpAssign: writeTriplet(visit, "(", " = ", ")"); break;
    224         case EOpAddAssign: writeTriplet(visit, "(", " += ", ")"); break;
    225         case EOpSubAssign: writeTriplet(visit, "(", " -= ", ")"); break;
    226         case EOpDivAssign: writeTriplet(visit, "(", " /= ", ")"); break;
    227         // Notice the fall-through.
    228         case EOpMulAssign:
    229         case EOpVectorTimesMatrixAssign:
    230         case EOpVectorTimesScalarAssign:
    231         case EOpMatrixTimesScalarAssign:
    232         case EOpMatrixTimesMatrixAssign:
    233             writeTriplet(visit, "(", " *= ", ")");
    234             break;
    235 
    236         case EOpIndexDirect:
    237         case EOpIndexIndirect:
    238             writeTriplet(visit, NULL, "[", "]");
    239             break;
    240         case EOpIndexDirectStruct:
    241             if (visit == InVisit)
    242             {
    243                 out << ".";
    244                 // TODO(alokp): ASSERT
    245                 out << node->getType().getFieldName();
    246                 visitChildren = false;
    247             }
    248             break;
    249         case EOpVectorSwizzle:
    250             if (visit == InVisit)
    251             {
    252                 out << ".";
    253                 TIntermAggregate* rightChild = node->getRight()->getAsAggregate();
    254                 TIntermSequence& sequence = rightChild->getSequence();
    255                 for (TIntermSequence::iterator sit = sequence.begin(); sit != sequence.end(); ++sit)
    256                 {
    257                     TIntermConstantUnion* element = (*sit)->getAsConstantUnion();
    258                     ASSERT(element->getBasicType() == EbtInt);
    259                     ASSERT(element->getNominalSize() == 1);
    260                     const ConstantUnion& data = element->getUnionArrayPointer()[0];
    261                     ASSERT(data.getType() == EbtInt);
    262                     switch (data.getIConst())
    263                     {
    264                         case 0: out << "x"; break;
    265                         case 1: out << "y"; break;
    266                         case 2: out << "z"; break;
    267                         case 3: out << "w"; break;
    268                         default: UNREACHABLE(); break;
    269                     }
    270                 }
    271                 visitChildren = false;
    272             }
    273             break;
    274 
    275         case EOpAdd: writeTriplet(visit, "(", " + ", ")"); break;
    276         case EOpSub: writeTriplet(visit, "(", " - ", ")"); break;
    277         case EOpMul: writeTriplet(visit, "(", " * ", ")"); break;
    278         case EOpDiv: writeTriplet(visit, "(", " / ", ")"); break;
    279         case EOpMod: UNIMPLEMENTED(); break;
    280         case EOpEqual: writeTriplet(visit, "(", " == ", ")"); break;
    281         case EOpNotEqual: writeTriplet(visit, "(", " != ", ")"); break;
    282         case EOpLessThan: writeTriplet(visit, "(", " < ", ")"); break;
    283         case EOpGreaterThan: writeTriplet(visit, "(", " > ", ")"); break;
    284         case EOpLessThanEqual: writeTriplet(visit, "(", " <= ", ")"); break;
    285         case EOpGreaterThanEqual: writeTriplet(visit, "(", " >= ", ")"); break;
    286 
    287         // Notice the fall-through.
    288         case EOpVectorTimesScalar:
    289         case EOpVectorTimesMatrix:
    290         case EOpMatrixTimesVector:
    291         case EOpMatrixTimesScalar:
    292         case EOpMatrixTimesMatrix:
    293             writeTriplet(visit, "(", " * ", ")");
    294             break;
    295 
    296         case EOpLogicalOr: writeTriplet(visit, "(", " || ", ")"); break;
    297         case EOpLogicalXor: writeTriplet(visit, "(", " ^^ ", ")"); break;
    298         case EOpLogicalAnd: writeTriplet(visit, "(", " && ", ")"); break;
    299         default: UNREACHABLE(); break;
    300     }
    301 
    302     return visitChildren;
    303 }
    304 
    305 bool TOutputGLSL::visitUnary(Visit visit, TIntermUnary* node)
    306 {
    307     switch (node->getOp())
    308     {
    309         case EOpNegative: writeTriplet(visit, "(-", NULL, ")"); break;
    310         case EOpVectorLogicalNot: writeTriplet(visit, "not(", NULL, ")"); break;
    311         case EOpLogicalNot: writeTriplet(visit, "(!", NULL, ")"); break;
    312 
    313         case EOpPostIncrement: writeTriplet(visit, "(", NULL, "++)"); break;
    314         case EOpPostDecrement: writeTriplet(visit, "(", NULL, "--)"); break;
    315         case EOpPreIncrement: writeTriplet(visit, "(++", NULL, ")"); break;
    316         case EOpPreDecrement: writeTriplet(visit, "(--", NULL, ")"); break;
    317 
    318         case EOpConvIntToBool:
    319         case EOpConvFloatToBool:
    320             switch (node->getOperand()->getType().getNominalSize())
    321             {
    322                 case 1: writeTriplet(visit, "bool(", NULL, ")");  break;
    323                 case 2: writeTriplet(visit, "bvec2(", NULL, ")"); break;
    324                 case 3: writeTriplet(visit, "bvec3(", NULL, ")"); break;
    325                 case 4: writeTriplet(visit, "bvec4(", NULL, ")"); break;
    326                 default: UNREACHABLE();
    327             }
    328             break;
    329         case EOpConvBoolToFloat:
    330         case EOpConvIntToFloat:
    331             switch (node->getOperand()->getType().getNominalSize())
    332             {
    333                 case 1: writeTriplet(visit, "float(", NULL, ")");  break;
    334                 case 2: writeTriplet(visit, "vec2(", NULL, ")"); break;
    335                 case 3: writeTriplet(visit, "vec3(", NULL, ")"); break;
    336                 case 4: writeTriplet(visit, "vec4(", NULL, ")"); break;
    337                 default: UNREACHABLE();
    338             }
    339             break;
    340         case EOpConvFloatToInt:
    341         case EOpConvBoolToInt:
    342             switch (node->getOperand()->getType().getNominalSize())
    343             {
    344                 case 1: writeTriplet(visit, "int(", NULL, ")");  break;
    345                 case 2: writeTriplet(visit, "ivec2(", NULL, ")"); break;
    346                 case 3: writeTriplet(visit, "ivec3(", NULL, ")"); break;
    347                 case 4: writeTriplet(visit, "ivec4(", NULL, ")"); break;
    348                 default: UNREACHABLE();
    349             }
    350             break;
    351 
    352         case EOpRadians: writeTriplet(visit, "radians(", NULL, ")"); break;
    353         case EOpDegrees: writeTriplet(visit, "degrees(", NULL, ")"); break;
    354         case EOpSin: writeTriplet(visit, "sin(", NULL, ")"); break;
    355         case EOpCos: writeTriplet(visit, "cos(", NULL, ")"); break;
    356         case EOpTan: writeTriplet(visit, "tan(", NULL, ")"); break;
    357         case EOpAsin: writeTriplet(visit, "asin(", NULL, ")"); break;
    358         case EOpAcos: writeTriplet(visit, "acos(", NULL, ")"); break;
    359         case EOpAtan: writeTriplet(visit, "atan(", NULL, ")"); break;
    360 
    361         case EOpExp: writeTriplet(visit, "exp(", NULL, ")"); break;
    362         case EOpLog: writeTriplet(visit, "log(", NULL, ")"); break;
    363         case EOpExp2: writeTriplet(visit, "exp2(", NULL, ")"); break;
    364         case EOpLog2: writeTriplet(visit, "log2(", NULL, ")"); break;
    365         case EOpSqrt: writeTriplet(visit, "sqrt(", NULL, ")"); break;
    366         case EOpInverseSqrt: writeTriplet(visit, "inversesqrt(", NULL, ")"); break;
    367 
    368         case EOpAbs: writeTriplet(visit, "abs(", NULL, ")"); break;
    369         case EOpSign: writeTriplet(visit, "sign(", NULL, ")"); break;
    370         case EOpFloor: writeTriplet(visit, "floor(", NULL, ")"); break;
    371         case EOpCeil: writeTriplet(visit, "ceil(", NULL, ")"); break;
    372         case EOpFract: writeTriplet(visit, "fract(", NULL, ")"); break;
    373 
    374         case EOpLength: writeTriplet(visit, "length(", NULL, ")"); break;
    375         case EOpNormalize: writeTriplet(visit, "normalize(", NULL, ")"); break;
    376 
    377         case EOpDFdx: writeTriplet(visit, "dFdx(", NULL, ")"); break;
    378         case EOpDFdy: writeTriplet(visit, "dFdy(", NULL, ")"); break;
    379         case EOpFwidth: writeTriplet(visit, "fwidth(", NULL, ")"); break;
    380 
    381         case EOpAny: writeTriplet(visit, "any(", NULL, ")"); break;
    382         case EOpAll: writeTriplet(visit, "all(", NULL, ")"); break;
    383 
    384         default: UNREACHABLE(); break;
    385     }
    386 
    387     return true;
    388 }
    389 
    390 bool TOutputGLSL::visitSelection(Visit visit, TIntermSelection* node)
    391 {
    392     TInfoSinkBase& out = objSink();
    393 
    394     if (node->usesTernaryOperator())
    395     {
    396         // Notice two brackets at the beginning and end. The outer ones
    397         // encapsulate the whole ternary expression. This preserves the
    398         // order of precedence when ternary expressions are used in a
    399         // compound expression, i.e., c = 2 * (a < b ? 1 : 2).
    400         out << "((";
    401         node->getCondition()->traverse(this);
    402         out << ") ? (";
    403         node->getTrueBlock()->traverse(this);
    404         out << ") : (";
    405         node->getFalseBlock()->traverse(this);
    406         out << "))";
    407     }
    408     else
    409     {
    410         out << "if (";
    411         node->getCondition()->traverse(this);
    412         out << ")\n";
    413 
    414         incrementDepth();
    415         visitCodeBlock(node->getTrueBlock());
    416 
    417         if (node->getFalseBlock())
    418         {
    419             out << "else\n";
    420             visitCodeBlock(node->getFalseBlock());
    421         }
    422         decrementDepth();
    423     }
    424     return false;
    425 }
    426 
    427 bool TOutputGLSL::visitAggregate(Visit visit, TIntermAggregate* node)
    428 {
    429     bool visitChildren = true;
    430     TInfoSinkBase& out = objSink();
    431     switch (node->getOp())
    432     {
    433         case EOpSequence: {
    434             // Scope the sequences except when at the global scope.
    435             if (depth > 0) out << "{\n";
    436 
    437             incrementDepth();
    438             const TIntermSequence& sequence = node->getSequence();
    439             for (TIntermSequence::const_iterator iter = sequence.begin();
    440                  iter != sequence.end(); ++iter)
    441             {
    442                 TIntermNode* node = *iter;
    443                 ASSERT(node != NULL);
    444                 node->traverse(this);
    445 
    446                 if (isSingleStatement(node))
    447                     out << ";\n";
    448             }
    449             decrementDepth();
    450 
    451             // Scope the sequences except when at the global scope.
    452             if (depth > 0) out << "}\n";
    453             visitChildren = false;
    454             break;
    455         }
    456         case EOpPrototype: {
    457             // Function declaration.
    458             ASSERT(visit == PreVisit);
    459             TString returnType = getTypeName(node->getType());
    460             out << returnType << " " << node->getName();
    461 
    462             out << "(";
    463             writeFunctionParameters(node->getSequence());
    464             out << ")";
    465 
    466             visitChildren = false;
    467             break;
    468         }
    469         case EOpFunction: {
    470             // Function definition.
    471             ASSERT(visit == PreVisit);
    472             TString returnType = getTypeName(node->getType());
    473             TString functionName = TFunction::unmangleName(node->getName());
    474             out << returnType << " " << functionName;
    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 TOutputGLSL::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         out << "for (";
    619         if (node->getInit())
    620             node->getInit()->traverse(this);
    621         out << "; ";
    622 
    623         if (node->getCondition())
    624             node->getCondition()->traverse(this);
    625         out << "; ";
    626 
    627         if (node->getExpression())
    628             node->getExpression()->traverse(this);
    629         out << ")\n";
    630     }
    631     else if (loopType == ELoopWhile)  // while loop
    632     {
    633         out << "while (";
    634         ASSERT(node->getCondition() != NULL);
    635         node->getCondition()->traverse(this);
    636         out << ")\n";
    637     }
    638     else  // do-while loop
    639     {
    640         ASSERT(loopType == ELoopDoWhile);
    641         out << "do\n";
    642     }
    643 
    644     // Loop body.
    645     visitCodeBlock(node->getBody());
    646 
    647     // Loop footer.
    648     if (loopType == ELoopDoWhile)  // do-while loop
    649     {
    650         out << "while (";
    651         ASSERT(node->getCondition() != NULL);
    652         node->getCondition()->traverse(this);
    653         out << ");\n";
    654     }
    655     decrementDepth();
    656 
    657     // No need to visit children. They have been already processed in
    658     // this function.
    659     return false;
    660 }
    661 
    662 bool TOutputGLSL::visitBranch(Visit visit, TIntermBranch* node)
    663 {
    664     switch (node->getFlowOp())
    665     {
    666         case EOpKill: writeTriplet(visit, "discard", NULL, NULL); break;
    667         case EOpBreak: writeTriplet(visit, "break", NULL, NULL); break;
    668         case EOpContinue: writeTriplet(visit, "continue", NULL, NULL); break;
    669         case EOpReturn: writeTriplet(visit, "return ", NULL, NULL); break;
    670         default: UNREACHABLE(); break;
    671     }
    672 
    673     return true;
    674 }
    675 
    676 void TOutputGLSL::visitCodeBlock(TIntermNode* node) {
    677     TInfoSinkBase &out = objSink();
    678     if (node != NULL)
    679     {
    680         node->traverse(this);
    681         // Single statements not part of a sequence need to be terminated
    682         // with semi-colon.
    683         if (isSingleStatement(node))
    684             out << ";\n";
    685     }
    686     else
    687     {
    688         out << "{\n}\n";  // Empty code block.
    689     }
    690 }
    691