michael@0: // michael@0: // Copyright (c) 2002-2013 The ANGLE Project Authors. All rights reserved. michael@0: // Use of this source code is governed by a BSD-style license that can be michael@0: // found in the LICENSE file. michael@0: // michael@0: michael@0: #include "compiler/BuiltInFunctionEmulator.h" michael@0: #include "compiler/DetectCallDepth.h" michael@0: #include "compiler/ForLoopUnroll.h" michael@0: #include "compiler/Initialize.h" michael@0: #include "compiler/InitializeParseContext.h" michael@0: #include "compiler/MapLongVariableNames.h" michael@0: #include "compiler/ParseHelper.h" michael@0: #include "compiler/RenameFunction.h" michael@0: #include "compiler/ShHandle.h" michael@0: #include "compiler/ValidateLimitations.h" michael@0: #include "compiler/VariablePacker.h" michael@0: #include "compiler/depgraph/DependencyGraph.h" michael@0: #include "compiler/depgraph/DependencyGraphOutput.h" michael@0: #include "compiler/timing/RestrictFragmentShaderTiming.h" michael@0: #include "compiler/timing/RestrictVertexShaderTiming.h" michael@0: #include "third_party/compiler/ArrayBoundsClamper.h" michael@0: michael@0: bool isWebGLBasedSpec(ShShaderSpec spec) michael@0: { michael@0: return spec == SH_WEBGL_SPEC || spec == SH_CSS_SHADERS_SPEC; michael@0: } michael@0: michael@0: namespace { michael@0: class TScopedPoolAllocator { michael@0: public: michael@0: TScopedPoolAllocator(TPoolAllocator* allocator, bool pushPop) michael@0: : mAllocator(allocator), mPushPopAllocator(pushPop) { michael@0: if (mPushPopAllocator) mAllocator->push(); michael@0: SetGlobalPoolAllocator(mAllocator); michael@0: } michael@0: ~TScopedPoolAllocator() { michael@0: SetGlobalPoolAllocator(NULL); michael@0: if (mPushPopAllocator) mAllocator->pop(); michael@0: } michael@0: michael@0: private: michael@0: TPoolAllocator* mAllocator; michael@0: bool mPushPopAllocator; michael@0: }; michael@0: } // namespace michael@0: michael@0: TShHandleBase::TShHandleBase() { michael@0: allocator.push(); michael@0: SetGlobalPoolAllocator(&allocator); michael@0: } michael@0: michael@0: TShHandleBase::~TShHandleBase() { michael@0: SetGlobalPoolAllocator(NULL); michael@0: allocator.popAll(); michael@0: } michael@0: michael@0: TCompiler::TCompiler(ShShaderType type, ShShaderSpec spec) michael@0: : shaderType(type), michael@0: shaderSpec(spec), michael@0: maxUniformVectors(0), michael@0: maxExpressionComplexity(0), michael@0: maxCallStackDepth(0), michael@0: fragmentPrecisionHigh(false), michael@0: clampingStrategy(SH_CLAMP_WITH_CLAMP_INTRINSIC), michael@0: builtInFunctionEmulator(type) michael@0: { michael@0: longNameMap = LongNameMap::GetInstance(); michael@0: } michael@0: michael@0: TCompiler::~TCompiler() michael@0: { michael@0: ASSERT(longNameMap); michael@0: longNameMap->Release(); michael@0: } michael@0: michael@0: bool TCompiler::Init(const ShBuiltInResources& resources) michael@0: { michael@0: maxUniformVectors = (shaderType == SH_VERTEX_SHADER) ? michael@0: resources.MaxVertexUniformVectors : michael@0: resources.MaxFragmentUniformVectors; michael@0: maxExpressionComplexity = resources.MaxExpressionComplexity; michael@0: maxCallStackDepth = resources.MaxCallStackDepth; michael@0: TScopedPoolAllocator scopedAlloc(&allocator, false); michael@0: michael@0: // Generate built-in symbol table. michael@0: if (!InitBuiltInSymbolTable(resources)) michael@0: return false; michael@0: InitExtensionBehavior(resources, extensionBehavior); michael@0: fragmentPrecisionHigh = resources.FragmentPrecisionHigh == 1; michael@0: michael@0: // ArrayIndexClampingStrategy's enum starts at 1, so 0 is 'default'. michael@0: if (resources.ArrayIndexClampingStrategy) { michael@0: clampingStrategy = resources.ArrayIndexClampingStrategy; michael@0: } michael@0: arrayBoundsClamper.SetClampingStrategy(clampingStrategy); michael@0: michael@0: hashFunction = resources.HashFunction; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool TCompiler::compile(const char* const shaderStrings[], michael@0: size_t numStrings, michael@0: int compileOptions) michael@0: { michael@0: TScopedPoolAllocator scopedAlloc(&allocator, true); michael@0: clearResults(); michael@0: michael@0: if (numStrings == 0) michael@0: return true; michael@0: michael@0: // If compiling for WebGL, validate loop and indexing as well. michael@0: if (isWebGLBasedSpec(shaderSpec)) michael@0: compileOptions |= SH_VALIDATE_LOOP_INDEXING; michael@0: michael@0: // First string is path of source file if flag is set. The actual source follows. michael@0: const char* sourcePath = NULL; michael@0: size_t firstSource = 0; michael@0: if (compileOptions & SH_SOURCE_PATH) michael@0: { michael@0: sourcePath = shaderStrings[0]; michael@0: ++firstSource; michael@0: } michael@0: michael@0: TIntermediate intermediate(infoSink); michael@0: TParseContext parseContext(symbolTable, extensionBehavior, intermediate, michael@0: shaderType, shaderSpec, compileOptions, true, michael@0: sourcePath, infoSink); michael@0: parseContext.fragmentPrecisionHigh = fragmentPrecisionHigh; michael@0: SetGlobalParseContext(&parseContext); michael@0: michael@0: // We preserve symbols at the built-in level from compile-to-compile. michael@0: // Start pushing the user-defined symbols at global level. michael@0: symbolTable.push(); michael@0: if (!symbolTable.atGlobalLevel()) { michael@0: infoSink.info.prefix(EPrefixInternalError); michael@0: infoSink.info << "Wrong symbol table level"; michael@0: } michael@0: michael@0: // Parse shader. michael@0: bool success = michael@0: (PaParseStrings(numStrings - firstSource, &shaderStrings[firstSource], NULL, &parseContext) == 0) && michael@0: (parseContext.treeRoot != NULL); michael@0: if (success) { michael@0: TIntermNode* root = parseContext.treeRoot; michael@0: success = intermediate.postProcess(root); michael@0: michael@0: if (success) michael@0: success = detectCallDepth(root, infoSink, (compileOptions & SH_LIMIT_CALL_STACK_DEPTH) != 0); michael@0: michael@0: if (success && (compileOptions & SH_VALIDATE_LOOP_INDEXING)) michael@0: success = validateLimitations(root); michael@0: michael@0: if (success && (compileOptions & SH_TIMING_RESTRICTIONS)) michael@0: success = enforceTimingRestrictions(root, (compileOptions & SH_DEPENDENCY_GRAPH) != 0); michael@0: michael@0: if (success && shaderSpec == SH_CSS_SHADERS_SPEC) michael@0: rewriteCSSShader(root); michael@0: michael@0: // Unroll for-loop markup needs to happen after validateLimitations pass. michael@0: if (success && (compileOptions & SH_UNROLL_FOR_LOOP_WITH_INTEGER_INDEX)) michael@0: ForLoopUnroll::MarkForLoopsWithIntegerIndicesForUnrolling(root); michael@0: michael@0: // Built-in function emulation needs to happen after validateLimitations pass. michael@0: if (success && (compileOptions & SH_EMULATE_BUILT_IN_FUNCTIONS)) michael@0: builtInFunctionEmulator.MarkBuiltInFunctionsForEmulation(root); michael@0: michael@0: // Clamping uniform array bounds needs to happen after validateLimitations pass. michael@0: if (success && (compileOptions & SH_CLAMP_INDIRECT_ARRAY_BOUNDS)) michael@0: arrayBoundsClamper.MarkIndirectArrayBoundsForClamping(root); michael@0: michael@0: // Disallow expressions deemed too complex. michael@0: if (success && (compileOptions & SH_LIMIT_EXPRESSION_COMPLEXITY)) michael@0: success = limitExpressionComplexity(root); michael@0: michael@0: // Call mapLongVariableNames() before collectAttribsUniforms() so in michael@0: // collectAttribsUniforms() we already have the mapped symbol names and michael@0: // we could composite mapped and original variable names. michael@0: // Also, if we hash all the names, then no need to do this for long names. michael@0: if (success && (compileOptions & SH_MAP_LONG_VARIABLE_NAMES) && hashFunction == NULL) michael@0: mapLongVariableNames(root); michael@0: michael@0: if (success && (compileOptions & SH_ATTRIBUTES_UNIFORMS)) { michael@0: collectAttribsUniforms(root); michael@0: if (compileOptions & SH_ENFORCE_PACKING_RESTRICTIONS) { michael@0: success = enforcePackingRestrictions(); michael@0: if (!success) { michael@0: infoSink.info.prefix(EPrefixError); michael@0: infoSink.info << "too many uniforms"; michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (success && (compileOptions & SH_INTERMEDIATE_TREE)) michael@0: intermediate.outputTree(root); michael@0: michael@0: if (success && (compileOptions & SH_OBJECT_CODE)) michael@0: translate(root); michael@0: } michael@0: michael@0: // Cleanup memory. michael@0: intermediate.remove(parseContext.treeRoot); michael@0: // Ensure symbol table is returned to the built-in level, michael@0: // throwing away all but the built-ins. michael@0: while (!symbolTable.atBuiltInLevel()) michael@0: symbolTable.pop(); michael@0: michael@0: return success; michael@0: } michael@0: michael@0: bool TCompiler::InitBuiltInSymbolTable(const ShBuiltInResources &resources) michael@0: { michael@0: compileResources = resources; michael@0: michael@0: assert(symbolTable.isEmpty()); michael@0: symbolTable.push(); michael@0: michael@0: TPublicType integer; michael@0: integer.type = EbtInt; michael@0: integer.size = 1; michael@0: integer.matrix = false; michael@0: integer.array = false; michael@0: michael@0: TPublicType floatingPoint; michael@0: floatingPoint.type = EbtFloat; michael@0: floatingPoint.size = 1; michael@0: floatingPoint.matrix = false; michael@0: floatingPoint.array = false; michael@0: michael@0: switch(shaderType) michael@0: { michael@0: case SH_FRAGMENT_SHADER: michael@0: symbolTable.setDefaultPrecision(integer, EbpMedium); michael@0: break; michael@0: case SH_VERTEX_SHADER: michael@0: symbolTable.setDefaultPrecision(integer, EbpHigh); michael@0: symbolTable.setDefaultPrecision(floatingPoint, EbpHigh); michael@0: break; michael@0: default: assert(false && "Language not supported"); michael@0: } michael@0: michael@0: InsertBuiltInFunctions(shaderType, shaderSpec, resources, symbolTable); michael@0: michael@0: IdentifyBuiltIns(shaderType, shaderSpec, resources, symbolTable); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: void TCompiler::clearResults() michael@0: { michael@0: arrayBoundsClamper.Cleanup(); michael@0: infoSink.info.erase(); michael@0: infoSink.obj.erase(); michael@0: infoSink.debug.erase(); michael@0: michael@0: attribs.clear(); michael@0: uniforms.clear(); michael@0: michael@0: builtInFunctionEmulator.Cleanup(); michael@0: michael@0: nameMap.clear(); michael@0: } michael@0: michael@0: bool TCompiler::detectCallDepth(TIntermNode* root, TInfoSink& infoSink, bool limitCallStackDepth) michael@0: { michael@0: DetectCallDepth detect(infoSink, limitCallStackDepth, maxCallStackDepth); michael@0: root->traverse(&detect); michael@0: switch (detect.detectCallDepth()) { michael@0: case DetectCallDepth::kErrorNone: michael@0: return true; michael@0: case DetectCallDepth::kErrorMissingMain: michael@0: infoSink.info.prefix(EPrefixError); michael@0: infoSink.info << "Missing main()"; michael@0: return false; michael@0: case DetectCallDepth::kErrorRecursion: michael@0: infoSink.info.prefix(EPrefixError); michael@0: infoSink.info << "Function recursion detected"; michael@0: return false; michael@0: case DetectCallDepth::kErrorMaxDepthExceeded: michael@0: infoSink.info.prefix(EPrefixError); michael@0: infoSink.info << "Function call stack too deep"; michael@0: return false; michael@0: default: michael@0: UNREACHABLE(); michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: void TCompiler::rewriteCSSShader(TIntermNode* root) michael@0: { michael@0: RenameFunction renamer("main(", "css_main("); michael@0: root->traverse(&renamer); michael@0: } michael@0: michael@0: bool TCompiler::validateLimitations(TIntermNode* root) { michael@0: ValidateLimitations validate(shaderType, infoSink.info); michael@0: root->traverse(&validate); michael@0: return validate.numErrors() == 0; michael@0: } michael@0: michael@0: bool TCompiler::enforceTimingRestrictions(TIntermNode* root, bool outputGraph) michael@0: { michael@0: if (shaderSpec != SH_WEBGL_SPEC) { michael@0: infoSink.info << "Timing restrictions must be enforced under the WebGL spec."; michael@0: return false; michael@0: } michael@0: michael@0: if (shaderType == SH_FRAGMENT_SHADER) { michael@0: TDependencyGraph graph(root); michael@0: michael@0: // Output any errors first. michael@0: bool success = enforceFragmentShaderTimingRestrictions(graph); michael@0: michael@0: // Then, output the dependency graph. michael@0: if (outputGraph) { michael@0: TDependencyGraphOutput output(infoSink.info); michael@0: output.outputAllSpanningTrees(graph); michael@0: } michael@0: michael@0: return success; michael@0: } michael@0: else { michael@0: return enforceVertexShaderTimingRestrictions(root); michael@0: } michael@0: } michael@0: michael@0: bool TCompiler::limitExpressionComplexity(TIntermNode* root) michael@0: { michael@0: TIntermTraverser traverser; michael@0: root->traverse(&traverser); michael@0: TDependencyGraph graph(root); michael@0: michael@0: for (TFunctionCallVector::const_iterator iter = graph.beginUserDefinedFunctionCalls(); michael@0: iter != graph.endUserDefinedFunctionCalls(); michael@0: ++iter) michael@0: { michael@0: TGraphFunctionCall* samplerSymbol = *iter; michael@0: TDependencyGraphTraverser graphTraverser; michael@0: samplerSymbol->traverse(&graphTraverser); michael@0: } michael@0: michael@0: if (traverser.getMaxDepth() > maxExpressionComplexity) { michael@0: infoSink.info << "Expression too complex."; michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: bool TCompiler::enforceFragmentShaderTimingRestrictions(const TDependencyGraph& graph) michael@0: { michael@0: RestrictFragmentShaderTiming restrictor(infoSink.info); michael@0: restrictor.enforceRestrictions(graph); michael@0: return restrictor.numErrors() == 0; michael@0: } michael@0: michael@0: bool TCompiler::enforceVertexShaderTimingRestrictions(TIntermNode* root) michael@0: { michael@0: RestrictVertexShaderTiming restrictor(infoSink.info); michael@0: restrictor.enforceRestrictions(root); michael@0: return restrictor.numErrors() == 0; michael@0: } michael@0: michael@0: void TCompiler::collectAttribsUniforms(TIntermNode* root) michael@0: { michael@0: CollectAttribsUniforms collect(attribs, uniforms, hashFunction); michael@0: root->traverse(&collect); michael@0: } michael@0: michael@0: bool TCompiler::enforcePackingRestrictions() michael@0: { michael@0: VariablePacker packer; michael@0: return packer.CheckVariablesWithinPackingLimits(maxUniformVectors, uniforms); michael@0: } michael@0: michael@0: void TCompiler::mapLongVariableNames(TIntermNode* root) michael@0: { michael@0: ASSERT(longNameMap); michael@0: MapLongVariableNames map(longNameMap); michael@0: root->traverse(&map); michael@0: } michael@0: michael@0: int TCompiler::getMappedNameMaxLength() const michael@0: { michael@0: return MAX_SHORTENED_IDENTIFIER_SIZE + 1; michael@0: } michael@0: michael@0: const TExtensionBehavior& TCompiler::getExtensionBehavior() const michael@0: { michael@0: return extensionBehavior; michael@0: } michael@0: michael@0: const ShBuiltInResources& TCompiler::getResources() const michael@0: { michael@0: return compileResources; michael@0: } michael@0: michael@0: const ArrayBoundsClamper& TCompiler::getArrayBoundsClamper() const michael@0: { michael@0: return arrayBoundsClamper; michael@0: } michael@0: michael@0: ShArrayIndexClampingStrategy TCompiler::getArrayIndexClampingStrategy() const michael@0: { michael@0: return clampingStrategy; michael@0: } michael@0: michael@0: const BuiltInFunctionEmulator& TCompiler::getBuiltInFunctionEmulator() const michael@0: { michael@0: return builtInFunctionEmulator; michael@0: }