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