1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/angle/src/compiler/Compiler.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,411 @@ 1.4 +// 1.5 +// Copyright (c) 2002-2013 The ANGLE Project Authors. All rights reserved. 1.6 +// Use of this source code is governed by a BSD-style license that can be 1.7 +// found in the LICENSE file. 1.8 +// 1.9 + 1.10 +#include "compiler/BuiltInFunctionEmulator.h" 1.11 +#include "compiler/DetectCallDepth.h" 1.12 +#include "compiler/ForLoopUnroll.h" 1.13 +#include "compiler/Initialize.h" 1.14 +#include "compiler/InitializeParseContext.h" 1.15 +#include "compiler/MapLongVariableNames.h" 1.16 +#include "compiler/ParseHelper.h" 1.17 +#include "compiler/RenameFunction.h" 1.18 +#include "compiler/ShHandle.h" 1.19 +#include "compiler/ValidateLimitations.h" 1.20 +#include "compiler/VariablePacker.h" 1.21 +#include "compiler/depgraph/DependencyGraph.h" 1.22 +#include "compiler/depgraph/DependencyGraphOutput.h" 1.23 +#include "compiler/timing/RestrictFragmentShaderTiming.h" 1.24 +#include "compiler/timing/RestrictVertexShaderTiming.h" 1.25 +#include "third_party/compiler/ArrayBoundsClamper.h" 1.26 + 1.27 +bool isWebGLBasedSpec(ShShaderSpec spec) 1.28 +{ 1.29 + return spec == SH_WEBGL_SPEC || spec == SH_CSS_SHADERS_SPEC; 1.30 +} 1.31 + 1.32 +namespace { 1.33 +class TScopedPoolAllocator { 1.34 +public: 1.35 + TScopedPoolAllocator(TPoolAllocator* allocator, bool pushPop) 1.36 + : mAllocator(allocator), mPushPopAllocator(pushPop) { 1.37 + if (mPushPopAllocator) mAllocator->push(); 1.38 + SetGlobalPoolAllocator(mAllocator); 1.39 + } 1.40 + ~TScopedPoolAllocator() { 1.41 + SetGlobalPoolAllocator(NULL); 1.42 + if (mPushPopAllocator) mAllocator->pop(); 1.43 + } 1.44 + 1.45 +private: 1.46 + TPoolAllocator* mAllocator; 1.47 + bool mPushPopAllocator; 1.48 +}; 1.49 +} // namespace 1.50 + 1.51 +TShHandleBase::TShHandleBase() { 1.52 + allocator.push(); 1.53 + SetGlobalPoolAllocator(&allocator); 1.54 +} 1.55 + 1.56 +TShHandleBase::~TShHandleBase() { 1.57 + SetGlobalPoolAllocator(NULL); 1.58 + allocator.popAll(); 1.59 +} 1.60 + 1.61 +TCompiler::TCompiler(ShShaderType type, ShShaderSpec spec) 1.62 + : shaderType(type), 1.63 + shaderSpec(spec), 1.64 + maxUniformVectors(0), 1.65 + maxExpressionComplexity(0), 1.66 + maxCallStackDepth(0), 1.67 + fragmentPrecisionHigh(false), 1.68 + clampingStrategy(SH_CLAMP_WITH_CLAMP_INTRINSIC), 1.69 + builtInFunctionEmulator(type) 1.70 +{ 1.71 + longNameMap = LongNameMap::GetInstance(); 1.72 +} 1.73 + 1.74 +TCompiler::~TCompiler() 1.75 +{ 1.76 + ASSERT(longNameMap); 1.77 + longNameMap->Release(); 1.78 +} 1.79 + 1.80 +bool TCompiler::Init(const ShBuiltInResources& resources) 1.81 +{ 1.82 + maxUniformVectors = (shaderType == SH_VERTEX_SHADER) ? 1.83 + resources.MaxVertexUniformVectors : 1.84 + resources.MaxFragmentUniformVectors; 1.85 + maxExpressionComplexity = resources.MaxExpressionComplexity; 1.86 + maxCallStackDepth = resources.MaxCallStackDepth; 1.87 + TScopedPoolAllocator scopedAlloc(&allocator, false); 1.88 + 1.89 + // Generate built-in symbol table. 1.90 + if (!InitBuiltInSymbolTable(resources)) 1.91 + return false; 1.92 + InitExtensionBehavior(resources, extensionBehavior); 1.93 + fragmentPrecisionHigh = resources.FragmentPrecisionHigh == 1; 1.94 + 1.95 + // ArrayIndexClampingStrategy's enum starts at 1, so 0 is 'default'. 1.96 + if (resources.ArrayIndexClampingStrategy) { 1.97 + clampingStrategy = resources.ArrayIndexClampingStrategy; 1.98 + } 1.99 + arrayBoundsClamper.SetClampingStrategy(clampingStrategy); 1.100 + 1.101 + hashFunction = resources.HashFunction; 1.102 + 1.103 + return true; 1.104 +} 1.105 + 1.106 +bool TCompiler::compile(const char* const shaderStrings[], 1.107 + size_t numStrings, 1.108 + int compileOptions) 1.109 +{ 1.110 + TScopedPoolAllocator scopedAlloc(&allocator, true); 1.111 + clearResults(); 1.112 + 1.113 + if (numStrings == 0) 1.114 + return true; 1.115 + 1.116 + // If compiling for WebGL, validate loop and indexing as well. 1.117 + if (isWebGLBasedSpec(shaderSpec)) 1.118 + compileOptions |= SH_VALIDATE_LOOP_INDEXING; 1.119 + 1.120 + // First string is path of source file if flag is set. The actual source follows. 1.121 + const char* sourcePath = NULL; 1.122 + size_t firstSource = 0; 1.123 + if (compileOptions & SH_SOURCE_PATH) 1.124 + { 1.125 + sourcePath = shaderStrings[0]; 1.126 + ++firstSource; 1.127 + } 1.128 + 1.129 + TIntermediate intermediate(infoSink); 1.130 + TParseContext parseContext(symbolTable, extensionBehavior, intermediate, 1.131 + shaderType, shaderSpec, compileOptions, true, 1.132 + sourcePath, infoSink); 1.133 + parseContext.fragmentPrecisionHigh = fragmentPrecisionHigh; 1.134 + SetGlobalParseContext(&parseContext); 1.135 + 1.136 + // We preserve symbols at the built-in level from compile-to-compile. 1.137 + // Start pushing the user-defined symbols at global level. 1.138 + symbolTable.push(); 1.139 + if (!symbolTable.atGlobalLevel()) { 1.140 + infoSink.info.prefix(EPrefixInternalError); 1.141 + infoSink.info << "Wrong symbol table level"; 1.142 + } 1.143 + 1.144 + // Parse shader. 1.145 + bool success = 1.146 + (PaParseStrings(numStrings - firstSource, &shaderStrings[firstSource], NULL, &parseContext) == 0) && 1.147 + (parseContext.treeRoot != NULL); 1.148 + if (success) { 1.149 + TIntermNode* root = parseContext.treeRoot; 1.150 + success = intermediate.postProcess(root); 1.151 + 1.152 + if (success) 1.153 + success = detectCallDepth(root, infoSink, (compileOptions & SH_LIMIT_CALL_STACK_DEPTH) != 0); 1.154 + 1.155 + if (success && (compileOptions & SH_VALIDATE_LOOP_INDEXING)) 1.156 + success = validateLimitations(root); 1.157 + 1.158 + if (success && (compileOptions & SH_TIMING_RESTRICTIONS)) 1.159 + success = enforceTimingRestrictions(root, (compileOptions & SH_DEPENDENCY_GRAPH) != 0); 1.160 + 1.161 + if (success && shaderSpec == SH_CSS_SHADERS_SPEC) 1.162 + rewriteCSSShader(root); 1.163 + 1.164 + // Unroll for-loop markup needs to happen after validateLimitations pass. 1.165 + if (success && (compileOptions & SH_UNROLL_FOR_LOOP_WITH_INTEGER_INDEX)) 1.166 + ForLoopUnroll::MarkForLoopsWithIntegerIndicesForUnrolling(root); 1.167 + 1.168 + // Built-in function emulation needs to happen after validateLimitations pass. 1.169 + if (success && (compileOptions & SH_EMULATE_BUILT_IN_FUNCTIONS)) 1.170 + builtInFunctionEmulator.MarkBuiltInFunctionsForEmulation(root); 1.171 + 1.172 + // Clamping uniform array bounds needs to happen after validateLimitations pass. 1.173 + if (success && (compileOptions & SH_CLAMP_INDIRECT_ARRAY_BOUNDS)) 1.174 + arrayBoundsClamper.MarkIndirectArrayBoundsForClamping(root); 1.175 + 1.176 + // Disallow expressions deemed too complex. 1.177 + if (success && (compileOptions & SH_LIMIT_EXPRESSION_COMPLEXITY)) 1.178 + success = limitExpressionComplexity(root); 1.179 + 1.180 + // Call mapLongVariableNames() before collectAttribsUniforms() so in 1.181 + // collectAttribsUniforms() we already have the mapped symbol names and 1.182 + // we could composite mapped and original variable names. 1.183 + // Also, if we hash all the names, then no need to do this for long names. 1.184 + if (success && (compileOptions & SH_MAP_LONG_VARIABLE_NAMES) && hashFunction == NULL) 1.185 + mapLongVariableNames(root); 1.186 + 1.187 + if (success && (compileOptions & SH_ATTRIBUTES_UNIFORMS)) { 1.188 + collectAttribsUniforms(root); 1.189 + if (compileOptions & SH_ENFORCE_PACKING_RESTRICTIONS) { 1.190 + success = enforcePackingRestrictions(); 1.191 + if (!success) { 1.192 + infoSink.info.prefix(EPrefixError); 1.193 + infoSink.info << "too many uniforms"; 1.194 + } 1.195 + } 1.196 + } 1.197 + 1.198 + if (success && (compileOptions & SH_INTERMEDIATE_TREE)) 1.199 + intermediate.outputTree(root); 1.200 + 1.201 + if (success && (compileOptions & SH_OBJECT_CODE)) 1.202 + translate(root); 1.203 + } 1.204 + 1.205 + // Cleanup memory. 1.206 + intermediate.remove(parseContext.treeRoot); 1.207 + // Ensure symbol table is returned to the built-in level, 1.208 + // throwing away all but the built-ins. 1.209 + while (!symbolTable.atBuiltInLevel()) 1.210 + symbolTable.pop(); 1.211 + 1.212 + return success; 1.213 +} 1.214 + 1.215 +bool TCompiler::InitBuiltInSymbolTable(const ShBuiltInResources &resources) 1.216 +{ 1.217 + compileResources = resources; 1.218 + 1.219 + assert(symbolTable.isEmpty()); 1.220 + symbolTable.push(); 1.221 + 1.222 + TPublicType integer; 1.223 + integer.type = EbtInt; 1.224 + integer.size = 1; 1.225 + integer.matrix = false; 1.226 + integer.array = false; 1.227 + 1.228 + TPublicType floatingPoint; 1.229 + floatingPoint.type = EbtFloat; 1.230 + floatingPoint.size = 1; 1.231 + floatingPoint.matrix = false; 1.232 + floatingPoint.array = false; 1.233 + 1.234 + switch(shaderType) 1.235 + { 1.236 + case SH_FRAGMENT_SHADER: 1.237 + symbolTable.setDefaultPrecision(integer, EbpMedium); 1.238 + break; 1.239 + case SH_VERTEX_SHADER: 1.240 + symbolTable.setDefaultPrecision(integer, EbpHigh); 1.241 + symbolTable.setDefaultPrecision(floatingPoint, EbpHigh); 1.242 + break; 1.243 + default: assert(false && "Language not supported"); 1.244 + } 1.245 + 1.246 + InsertBuiltInFunctions(shaderType, shaderSpec, resources, symbolTable); 1.247 + 1.248 + IdentifyBuiltIns(shaderType, shaderSpec, resources, symbolTable); 1.249 + 1.250 + return true; 1.251 +} 1.252 + 1.253 +void TCompiler::clearResults() 1.254 +{ 1.255 + arrayBoundsClamper.Cleanup(); 1.256 + infoSink.info.erase(); 1.257 + infoSink.obj.erase(); 1.258 + infoSink.debug.erase(); 1.259 + 1.260 + attribs.clear(); 1.261 + uniforms.clear(); 1.262 + 1.263 + builtInFunctionEmulator.Cleanup(); 1.264 + 1.265 + nameMap.clear(); 1.266 +} 1.267 + 1.268 +bool TCompiler::detectCallDepth(TIntermNode* root, TInfoSink& infoSink, bool limitCallStackDepth) 1.269 +{ 1.270 + DetectCallDepth detect(infoSink, limitCallStackDepth, maxCallStackDepth); 1.271 + root->traverse(&detect); 1.272 + switch (detect.detectCallDepth()) { 1.273 + case DetectCallDepth::kErrorNone: 1.274 + return true; 1.275 + case DetectCallDepth::kErrorMissingMain: 1.276 + infoSink.info.prefix(EPrefixError); 1.277 + infoSink.info << "Missing main()"; 1.278 + return false; 1.279 + case DetectCallDepth::kErrorRecursion: 1.280 + infoSink.info.prefix(EPrefixError); 1.281 + infoSink.info << "Function recursion detected"; 1.282 + return false; 1.283 + case DetectCallDepth::kErrorMaxDepthExceeded: 1.284 + infoSink.info.prefix(EPrefixError); 1.285 + infoSink.info << "Function call stack too deep"; 1.286 + return false; 1.287 + default: 1.288 + UNREACHABLE(); 1.289 + return false; 1.290 + } 1.291 +} 1.292 + 1.293 +void TCompiler::rewriteCSSShader(TIntermNode* root) 1.294 +{ 1.295 + RenameFunction renamer("main(", "css_main("); 1.296 + root->traverse(&renamer); 1.297 +} 1.298 + 1.299 +bool TCompiler::validateLimitations(TIntermNode* root) { 1.300 + ValidateLimitations validate(shaderType, infoSink.info); 1.301 + root->traverse(&validate); 1.302 + return validate.numErrors() == 0; 1.303 +} 1.304 + 1.305 +bool TCompiler::enforceTimingRestrictions(TIntermNode* root, bool outputGraph) 1.306 +{ 1.307 + if (shaderSpec != SH_WEBGL_SPEC) { 1.308 + infoSink.info << "Timing restrictions must be enforced under the WebGL spec."; 1.309 + return false; 1.310 + } 1.311 + 1.312 + if (shaderType == SH_FRAGMENT_SHADER) { 1.313 + TDependencyGraph graph(root); 1.314 + 1.315 + // Output any errors first. 1.316 + bool success = enforceFragmentShaderTimingRestrictions(graph); 1.317 + 1.318 + // Then, output the dependency graph. 1.319 + if (outputGraph) { 1.320 + TDependencyGraphOutput output(infoSink.info); 1.321 + output.outputAllSpanningTrees(graph); 1.322 + } 1.323 + 1.324 + return success; 1.325 + } 1.326 + else { 1.327 + return enforceVertexShaderTimingRestrictions(root); 1.328 + } 1.329 +} 1.330 + 1.331 +bool TCompiler::limitExpressionComplexity(TIntermNode* root) 1.332 +{ 1.333 + TIntermTraverser traverser; 1.334 + root->traverse(&traverser); 1.335 + TDependencyGraph graph(root); 1.336 + 1.337 + for (TFunctionCallVector::const_iterator iter = graph.beginUserDefinedFunctionCalls(); 1.338 + iter != graph.endUserDefinedFunctionCalls(); 1.339 + ++iter) 1.340 + { 1.341 + TGraphFunctionCall* samplerSymbol = *iter; 1.342 + TDependencyGraphTraverser graphTraverser; 1.343 + samplerSymbol->traverse(&graphTraverser); 1.344 + } 1.345 + 1.346 + if (traverser.getMaxDepth() > maxExpressionComplexity) { 1.347 + infoSink.info << "Expression too complex."; 1.348 + return false; 1.349 + } 1.350 + return true; 1.351 +} 1.352 + 1.353 +bool TCompiler::enforceFragmentShaderTimingRestrictions(const TDependencyGraph& graph) 1.354 +{ 1.355 + RestrictFragmentShaderTiming restrictor(infoSink.info); 1.356 + restrictor.enforceRestrictions(graph); 1.357 + return restrictor.numErrors() == 0; 1.358 +} 1.359 + 1.360 +bool TCompiler::enforceVertexShaderTimingRestrictions(TIntermNode* root) 1.361 +{ 1.362 + RestrictVertexShaderTiming restrictor(infoSink.info); 1.363 + restrictor.enforceRestrictions(root); 1.364 + return restrictor.numErrors() == 0; 1.365 +} 1.366 + 1.367 +void TCompiler::collectAttribsUniforms(TIntermNode* root) 1.368 +{ 1.369 + CollectAttribsUniforms collect(attribs, uniforms, hashFunction); 1.370 + root->traverse(&collect); 1.371 +} 1.372 + 1.373 +bool TCompiler::enforcePackingRestrictions() 1.374 +{ 1.375 + VariablePacker packer; 1.376 + return packer.CheckVariablesWithinPackingLimits(maxUniformVectors, uniforms); 1.377 +} 1.378 + 1.379 +void TCompiler::mapLongVariableNames(TIntermNode* root) 1.380 +{ 1.381 + ASSERT(longNameMap); 1.382 + MapLongVariableNames map(longNameMap); 1.383 + root->traverse(&map); 1.384 +} 1.385 + 1.386 +int TCompiler::getMappedNameMaxLength() const 1.387 +{ 1.388 + return MAX_SHORTENED_IDENTIFIER_SIZE + 1; 1.389 +} 1.390 + 1.391 +const TExtensionBehavior& TCompiler::getExtensionBehavior() const 1.392 +{ 1.393 + return extensionBehavior; 1.394 +} 1.395 + 1.396 +const ShBuiltInResources& TCompiler::getResources() const 1.397 +{ 1.398 + return compileResources; 1.399 +} 1.400 + 1.401 +const ArrayBoundsClamper& TCompiler::getArrayBoundsClamper() const 1.402 +{ 1.403 + return arrayBoundsClamper; 1.404 +} 1.405 + 1.406 +ShArrayIndexClampingStrategy TCompiler::getArrayIndexClampingStrategy() const 1.407 +{ 1.408 + return clampingStrategy; 1.409 +} 1.410 + 1.411 +const BuiltInFunctionEmulator& TCompiler::getBuiltInFunctionEmulator() const 1.412 +{ 1.413 + return builtInFunctionEmulator; 1.414 +}