Home | History | Annotate | Download | only in compiler
      1 //
      2 // Copyright (c) 2002-2013 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/BuiltInFunctionEmulator.h"
      8 #include "compiler/DetectCallDepth.h"
      9 #include "compiler/ForLoopUnroll.h"
     10 #include "compiler/Initialize.h"
     11 #include "compiler/InitializeParseContext.h"
     12 #include "compiler/MapLongVariableNames.h"
     13 #include "compiler/ParseHelper.h"
     14 #include "compiler/RenameFunction.h"
     15 #include "compiler/ShHandle.h"
     16 #include "compiler/ValidateLimitations.h"
     17 #include "compiler/VariablePacker.h"
     18 #include "compiler/depgraph/DependencyGraph.h"
     19 #include "compiler/depgraph/DependencyGraphOutput.h"
     20 #include "compiler/timing/RestrictFragmentShaderTiming.h"
     21 #include "compiler/timing/RestrictVertexShaderTiming.h"
     22 #include "third_party/compiler/ArrayBoundsClamper.h"
     23 
     24 bool isWebGLBasedSpec(ShShaderSpec spec)
     25 {
     26      return spec == SH_WEBGL_SPEC || spec == SH_CSS_SHADERS_SPEC;
     27 }
     28 
     29 namespace {
     30 class TScopedPoolAllocator {
     31 public:
     32     TScopedPoolAllocator(TPoolAllocator* allocator, bool pushPop)
     33         : mAllocator(allocator), mPushPopAllocator(pushPop) {
     34         if (mPushPopAllocator) mAllocator->push();
     35         SetGlobalPoolAllocator(mAllocator);
     36     }
     37     ~TScopedPoolAllocator() {
     38         SetGlobalPoolAllocator(NULL);
     39         if (mPushPopAllocator) mAllocator->pop();
     40     }
     41 
     42 private:
     43     TPoolAllocator* mAllocator;
     44     bool mPushPopAllocator;
     45 };
     46 }  // namespace
     47 
     48 TShHandleBase::TShHandleBase() {
     49     allocator.push();
     50     SetGlobalPoolAllocator(&allocator);
     51 }
     52 
     53 TShHandleBase::~TShHandleBase() {
     54     SetGlobalPoolAllocator(NULL);
     55     allocator.popAll();
     56 }
     57 
     58 TCompiler::TCompiler(ShShaderType type, ShShaderSpec spec)
     59     : shaderType(type),
     60       shaderSpec(spec),
     61       maxUniformVectors(0),
     62       maxExpressionComplexity(0),
     63       maxCallStackDepth(0),
     64       fragmentPrecisionHigh(false),
     65       clampingStrategy(SH_CLAMP_WITH_CLAMP_INTRINSIC),
     66       builtInFunctionEmulator(type)
     67 {
     68     longNameMap = LongNameMap::GetInstance();
     69 }
     70 
     71 TCompiler::~TCompiler()
     72 {
     73     ASSERT(longNameMap);
     74     longNameMap->Release();
     75 }
     76 
     77 bool TCompiler::Init(const ShBuiltInResources& resources)
     78 {
     79     maxUniformVectors = (shaderType == SH_VERTEX_SHADER) ?
     80         resources.MaxVertexUniformVectors :
     81         resources.MaxFragmentUniformVectors;
     82     maxExpressionComplexity = resources.MaxExpressionComplexity;
     83     maxCallStackDepth = resources.MaxCallStackDepth;
     84     TScopedPoolAllocator scopedAlloc(&allocator, false);
     85 
     86     // Generate built-in symbol table.
     87     if (!InitBuiltInSymbolTable(resources))
     88         return false;
     89     InitExtensionBehavior(resources, extensionBehavior);
     90     fragmentPrecisionHigh = resources.FragmentPrecisionHigh == 1;
     91 
     92     arrayBoundsClamper.SetClampingStrategy(resources.ArrayIndexClampingStrategy);
     93     clampingStrategy = resources.ArrayIndexClampingStrategy;
     94 
     95     hashFunction = resources.HashFunction;
     96 
     97     return true;
     98 }
     99 
    100 bool TCompiler::compile(const char* const shaderStrings[],
    101                         size_t numStrings,
    102                         int compileOptions)
    103 {
    104     TScopedPoolAllocator scopedAlloc(&allocator, true);
    105     clearResults();
    106 
    107     if (numStrings == 0)
    108         return true;
    109 
    110     // If compiling for WebGL, validate loop and indexing as well.
    111     if (isWebGLBasedSpec(shaderSpec))
    112         compileOptions |= SH_VALIDATE_LOOP_INDEXING;
    113 
    114     // First string is path of source file if flag is set. The actual source follows.
    115     const char* sourcePath = NULL;
    116     size_t firstSource = 0;
    117     if (compileOptions & SH_SOURCE_PATH)
    118     {
    119         sourcePath = shaderStrings[0];
    120         ++firstSource;
    121     }
    122 
    123     TIntermediate intermediate(infoSink);
    124     TParseContext parseContext(symbolTable, extensionBehavior, intermediate,
    125                                shaderType, shaderSpec, compileOptions, true,
    126                                sourcePath, infoSink);
    127     parseContext.fragmentPrecisionHigh = fragmentPrecisionHigh;
    128     SetGlobalParseContext(&parseContext);
    129 
    130     // We preserve symbols at the built-in level from compile-to-compile.
    131     // Start pushing the user-defined symbols at global level.
    132     symbolTable.push();
    133     if (!symbolTable.atGlobalLevel()) {
    134         infoSink.info.prefix(EPrefixInternalError);
    135         infoSink.info << "Wrong symbol table level";
    136     }
    137 
    138     // Parse shader.
    139     bool success =
    140         (PaParseStrings(numStrings - firstSource, &shaderStrings[firstSource], NULL, &parseContext) == 0) &&
    141         (parseContext.treeRoot != NULL);
    142     if (success) {
    143         TIntermNode* root = parseContext.treeRoot;
    144         success = intermediate.postProcess(root);
    145 
    146         if (success)
    147             success = detectCallDepth(root, infoSink, (compileOptions & SH_LIMIT_CALL_STACK_DEPTH) != 0);
    148 
    149         if (success && (compileOptions & SH_VALIDATE_LOOP_INDEXING))
    150             success = validateLimitations(root);
    151 
    152         if (success && (compileOptions & SH_TIMING_RESTRICTIONS))
    153             success = enforceTimingRestrictions(root, (compileOptions & SH_DEPENDENCY_GRAPH) != 0);
    154 
    155         if (success && shaderSpec == SH_CSS_SHADERS_SPEC)
    156             rewriteCSSShader(root);
    157 
    158         // Unroll for-loop markup needs to happen after validateLimitations pass.
    159         if (success && (compileOptions & SH_UNROLL_FOR_LOOP_WITH_INTEGER_INDEX))
    160             ForLoopUnroll::MarkForLoopsWithIntegerIndicesForUnrolling(root);
    161 
    162         // Built-in function emulation needs to happen after validateLimitations pass.
    163         if (success && (compileOptions & SH_EMULATE_BUILT_IN_FUNCTIONS))
    164             builtInFunctionEmulator.MarkBuiltInFunctionsForEmulation(root);
    165 
    166         // Clamping uniform array bounds needs to happen after validateLimitations pass.
    167         if (success && (compileOptions & SH_CLAMP_INDIRECT_ARRAY_BOUNDS))
    168             arrayBoundsClamper.MarkIndirectArrayBoundsForClamping(root);
    169 
    170         // Disallow expressions deemed too complex.
    171         if (success && (compileOptions & SH_LIMIT_EXPRESSION_COMPLEXITY))
    172             success = limitExpressionComplexity(root);
    173 
    174         // Call mapLongVariableNames() before collectAttribsUniforms() so in
    175         // collectAttribsUniforms() we already have the mapped symbol names and
    176         // we could composite mapped and original variable names.
    177         // Also, if we hash all the names, then no need to do this for long names.
    178         if (success && (compileOptions & SH_MAP_LONG_VARIABLE_NAMES) && hashFunction == NULL)
    179             mapLongVariableNames(root);
    180 
    181         if (success && (compileOptions & SH_ATTRIBUTES_UNIFORMS)) {
    182             collectAttribsUniforms(root);
    183             if (compileOptions & SH_ENFORCE_PACKING_RESTRICTIONS) {
    184                 success = enforcePackingRestrictions();
    185                 if (!success) {
    186                     infoSink.info.prefix(EPrefixError);
    187                     infoSink.info << "too many uniforms";
    188                 }
    189             }
    190         }
    191 
    192         if (success && (compileOptions & SH_INTERMEDIATE_TREE))
    193             intermediate.outputTree(root);
    194 
    195         if (success && (compileOptions & SH_OBJECT_CODE))
    196             translate(root);
    197     }
    198 
    199     // Cleanup memory.
    200     intermediate.remove(parseContext.treeRoot);
    201     // Ensure symbol table is returned to the built-in level,
    202     // throwing away all but the built-ins.
    203     while (!symbolTable.atBuiltInLevel())
    204         symbolTable.pop();
    205 
    206     return success;
    207 }
    208 
    209 bool TCompiler::InitBuiltInSymbolTable(const ShBuiltInResources &resources)
    210 {
    211     compileResources = resources;
    212 
    213     assert(symbolTable.isEmpty());
    214     symbolTable.push();
    215 
    216     TPublicType integer;
    217     integer.type = EbtInt;
    218     integer.size = 1;
    219     integer.matrix = false;
    220     integer.array = false;
    221 
    222     TPublicType floatingPoint;
    223     floatingPoint.type = EbtFloat;
    224     floatingPoint.size = 1;
    225     floatingPoint.matrix = false;
    226     floatingPoint.array = false;
    227 
    228     switch(shaderType)
    229     {
    230       case SH_FRAGMENT_SHADER:
    231         symbolTable.setDefaultPrecision(integer, EbpMedium);
    232         break;
    233       case SH_VERTEX_SHADER:
    234         symbolTable.setDefaultPrecision(integer, EbpHigh);
    235         symbolTable.setDefaultPrecision(floatingPoint, EbpHigh);
    236         break;
    237       default: assert(false && "Language not supported");
    238     }
    239 
    240     InsertBuiltInFunctions(shaderType, shaderSpec, resources, symbolTable);
    241 
    242     IdentifyBuiltIns(shaderType, shaderSpec, resources, symbolTable);
    243 
    244     return true;
    245 }
    246 
    247 void TCompiler::clearResults()
    248 {
    249     arrayBoundsClamper.Cleanup();
    250     infoSink.info.erase();
    251     infoSink.obj.erase();
    252     infoSink.debug.erase();
    253 
    254     attribs.clear();
    255     uniforms.clear();
    256 
    257     builtInFunctionEmulator.Cleanup();
    258 
    259     nameMap.clear();
    260 }
    261 
    262 bool TCompiler::detectCallDepth(TIntermNode* root, TInfoSink& infoSink, bool limitCallStackDepth)
    263 {
    264     DetectCallDepth detect(infoSink, limitCallStackDepth, maxCallStackDepth);
    265     root->traverse(&detect);
    266     switch (detect.detectCallDepth()) {
    267         case DetectCallDepth::kErrorNone:
    268             return true;
    269         case DetectCallDepth::kErrorMissingMain:
    270             infoSink.info.prefix(EPrefixError);
    271             infoSink.info << "Missing main()";
    272             return false;
    273         case DetectCallDepth::kErrorRecursion:
    274             infoSink.info.prefix(EPrefixError);
    275             infoSink.info << "Function recursion detected";
    276             return false;
    277         case DetectCallDepth::kErrorMaxDepthExceeded:
    278             infoSink.info.prefix(EPrefixError);
    279             infoSink.info << "Function call stack too deep";
    280             return false;
    281         default:
    282             UNREACHABLE();
    283             return false;
    284     }
    285 }
    286 
    287 void TCompiler::rewriteCSSShader(TIntermNode* root)
    288 {
    289     RenameFunction renamer("main(", "css_main(");
    290     root->traverse(&renamer);
    291 }
    292 
    293 bool TCompiler::validateLimitations(TIntermNode* root) {
    294     ValidateLimitations validate(shaderType, infoSink.info);
    295     root->traverse(&validate);
    296     return validate.numErrors() == 0;
    297 }
    298 
    299 bool TCompiler::enforceTimingRestrictions(TIntermNode* root, bool outputGraph)
    300 {
    301     if (shaderSpec != SH_WEBGL_SPEC) {
    302         infoSink.info << "Timing restrictions must be enforced under the WebGL spec.";
    303         return false;
    304     }
    305 
    306     if (shaderType == SH_FRAGMENT_SHADER) {
    307         TDependencyGraph graph(root);
    308 
    309         // Output any errors first.
    310         bool success = enforceFragmentShaderTimingRestrictions(graph);
    311 
    312         // Then, output the dependency graph.
    313         if (outputGraph) {
    314             TDependencyGraphOutput output(infoSink.info);
    315             output.outputAllSpanningTrees(graph);
    316         }
    317 
    318         return success;
    319     }
    320     else {
    321         return enforceVertexShaderTimingRestrictions(root);
    322     }
    323 }
    324 
    325 bool TCompiler::limitExpressionComplexity(TIntermNode* root)
    326 {
    327     TIntermTraverser traverser;
    328     root->traverse(&traverser);
    329     TDependencyGraph graph(root);
    330 
    331     for (TFunctionCallVector::const_iterator iter = graph.beginUserDefinedFunctionCalls();
    332          iter != graph.endUserDefinedFunctionCalls();
    333          ++iter)
    334     {
    335         TGraphFunctionCall* samplerSymbol = *iter;
    336         TDependencyGraphTraverser graphTraverser;
    337         samplerSymbol->traverse(&graphTraverser);
    338     }
    339 
    340     if (traverser.getMaxDepth() > maxExpressionComplexity) {
    341         infoSink.info << "Expression too complex.";
    342         return false;
    343     }
    344     return true;
    345 }
    346 
    347 bool TCompiler::enforceFragmentShaderTimingRestrictions(const TDependencyGraph& graph)
    348 {
    349     RestrictFragmentShaderTiming restrictor(infoSink.info);
    350     restrictor.enforceRestrictions(graph);
    351     return restrictor.numErrors() == 0;
    352 }
    353 
    354 bool TCompiler::enforceVertexShaderTimingRestrictions(TIntermNode* root)
    355 {
    356     RestrictVertexShaderTiming restrictor(infoSink.info);
    357     restrictor.enforceRestrictions(root);
    358     return restrictor.numErrors() == 0;
    359 }
    360 
    361 void TCompiler::collectAttribsUniforms(TIntermNode* root)
    362 {
    363     CollectAttribsUniforms collect(attribs, uniforms, hashFunction);
    364     root->traverse(&collect);
    365 }
    366 
    367 bool TCompiler::enforcePackingRestrictions()
    368 {
    369     VariablePacker packer;
    370     return packer.CheckVariablesWithinPackingLimits(maxUniformVectors, uniforms);
    371 }
    372 
    373 void TCompiler::mapLongVariableNames(TIntermNode* root)
    374 {
    375     ASSERT(longNameMap);
    376     MapLongVariableNames map(longNameMap);
    377     root->traverse(&map);
    378 }
    379 
    380 int TCompiler::getMappedNameMaxLength() const
    381 {
    382     return MAX_SHORTENED_IDENTIFIER_SIZE + 1;
    383 }
    384 
    385 const TExtensionBehavior& TCompiler::getExtensionBehavior() const
    386 {
    387     return extensionBehavior;
    388 }
    389 
    390 const ShBuiltInResources& TCompiler::getResources() const
    391 {
    392     return compileResources;
    393 }
    394 
    395 const ArrayBoundsClamper& TCompiler::getArrayBoundsClamper() const
    396 {
    397     return arrayBoundsClamper;
    398 }
    399 
    400 ShArrayIndexClampingStrategy TCompiler::getArrayIndexClampingStrategy() const
    401 {
    402     return clampingStrategy;
    403 }
    404 
    405 const BuiltInFunctionEmulator& TCompiler::getBuiltInFunctionEmulator() const
    406 {
    407     return builtInFunctionEmulator;
    408 }
    409