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