// Copyright 2016 The SwiftShader Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "Compiler.h" #include "AnalyzeCallDepth.h" #include "Initialize.h" #include "InitializeParseContext.h" #include "InitializeGlobals.h" #include "ParseHelper.h" #include "ValidateLimitations.h" namespace { class TScopedPoolAllocator { public: TScopedPoolAllocator(TPoolAllocator* allocator, bool pushPop) : mAllocator(allocator), mPushPopAllocator(pushPop) { if (mPushPopAllocator) mAllocator->push(); SetGlobalPoolAllocator(mAllocator); } ~TScopedPoolAllocator() { SetGlobalPoolAllocator(nullptr); if (mPushPopAllocator) mAllocator->pop(); } private: TPoolAllocator* mAllocator; bool mPushPopAllocator; }; } // namespace // // Initialize built-in resources with minimum expected values. // ShBuiltInResources::ShBuiltInResources() { // Constants. MaxVertexAttribs = 8; MaxVertexUniformVectors = 128; MaxVaryingVectors = 8; MaxVertexTextureImageUnits = 0; MaxCombinedTextureImageUnits = 8; MaxTextureImageUnits = 8; MaxFragmentUniformVectors = 16; MaxDrawBuffers = 1; MaxVertexOutputVectors = 16; MaxFragmentInputVectors = 15; MinProgramTexelOffset = -8; MaxProgramTexelOffset = 7; // Extensions. OES_standard_derivatives = 0; OES_fragment_precision_high = 0; OES_EGL_image_external = 0; MaxCallStackDepth = UINT_MAX; } TCompiler::TCompiler(GLenum type) : shaderType(type), maxCallStackDepth(UINT_MAX) { allocator.push(); SetGlobalPoolAllocator(&allocator); } TCompiler::~TCompiler() { SetGlobalPoolAllocator(nullptr); allocator.popAll(); } bool TCompiler::Init(const ShBuiltInResources& resources) { shaderVersion = 100; maxCallStackDepth = resources.MaxCallStackDepth; TScopedPoolAllocator scopedAlloc(&allocator, false); // Generate built-in symbol table. if (!InitBuiltInSymbolTable(resources)) return false; InitExtensionBehavior(resources, extensionBehavior); return true; } bool TCompiler::compile(const char* const shaderStrings[], const int numStrings, int compileOptions) { TScopedPoolAllocator scopedAlloc(&allocator, true); clearResults(); if (numStrings == 0) return true; // First string is path of source file if flag is set. The actual source follows. const char* sourcePath = nullptr; int firstSource = 0; if (compileOptions & SH_SOURCE_PATH) { sourcePath = shaderStrings[0]; ++firstSource; } TIntermediate intermediate(infoSink); TParseContext parseContext(symbolTable, extensionBehavior, intermediate, shaderType, compileOptions, true, sourcePath, infoSink); SetGlobalParseContext(&parseContext); // We preserve symbols at the built-in level from compile-to-compile. // Start pushing the user-defined symbols at global level. symbolTable.push(); if (!symbolTable.atGlobalLevel()) infoSink.info.message(EPrefixInternalError, "Wrong symbol table level"); // Parse shader. bool success = (PaParseStrings(numStrings - firstSource, &shaderStrings[firstSource], nullptr, &parseContext) == 0) && (parseContext.getTreeRoot() != nullptr); shaderVersion = parseContext.getShaderVersion(); if (success) { TIntermNode* root = parseContext.getTreeRoot(); success = intermediate.postProcess(root); if (success) success = validateCallDepth(root, infoSink); if (success && (compileOptions & SH_VALIDATE_LOOP_INDEXING)) success = validateLimitations(root); if (success && (compileOptions & SH_INTERMEDIATE_TREE)) intermediate.outputTree(root); if (success && (compileOptions & SH_OBJECT_CODE)) success = translate(root); } // Ensure symbol table is returned to the built-in level, // throwing away all but the built-ins. while (!symbolTable.atBuiltInLevel()) symbolTable.pop(); return success; } bool TCompiler::InitBuiltInSymbolTable(const ShBuiltInResources &resources) { assert(symbolTable.isEmpty()); symbolTable.push(); // COMMON_BUILTINS symbolTable.push(); // ESSL1_BUILTINS symbolTable.push(); // ESSL3_BUILTINS TPublicType integer; integer.type = EbtInt; integer.primarySize = 1; integer.secondarySize = 1; integer.array = false; TPublicType floatingPoint; floatingPoint.type = EbtFloat; floatingPoint.primarySize = 1; floatingPoint.secondarySize = 1; floatingPoint.array = false; switch(shaderType) { case GL_FRAGMENT_SHADER: symbolTable.setDefaultPrecision(integer, EbpMedium); break; case GL_VERTEX_SHADER: symbolTable.setDefaultPrecision(integer, EbpHigh); symbolTable.setDefaultPrecision(floatingPoint, EbpHigh); break; default: assert(false && "Language not supported"); } InsertBuiltInFunctions(shaderType, resources, symbolTable); IdentifyBuiltIns(shaderType, resources, symbolTable); return true; } void TCompiler::clearResults() { infoSink.info.erase(); infoSink.obj.erase(); infoSink.debug.erase(); } bool TCompiler::validateCallDepth(TIntermNode *root, TInfoSink &infoSink) { AnalyzeCallDepth validator(root); unsigned int depth = validator.analyzeCallDepth(); if(depth == 0) { infoSink.info.prefix(EPrefixError); infoSink.info << "Missing main()"; return false; } else if(depth == UINT_MAX) { infoSink.info.prefix(EPrefixError); infoSink.info << "Function recursion detected"; return false; } else if(depth > maxCallStackDepth) { infoSink.info.prefix(EPrefixError); infoSink.info << "Function call stack too deep (depth was "; infoSink.info << depth; infoSink.info << " while maximum call stack depth is "; infoSink.info << maxCallStackDepth; infoSink.info << ")"; return false; } return true; } bool TCompiler::validateLimitations(TIntermNode* root) { ValidateLimitations validate(shaderType, infoSink.info); root->traverse(&validate); return validate.numErrors() == 0; } const TExtensionBehavior& TCompiler::getExtensionBehavior() const { return extensionBehavior; } bool InitCompilerGlobals() { if(!InitializePoolIndex()) { assert(0 && "InitCompilerGlobals(): Failed to initalize global pool"); return false; } if(!InitializeParseContextIndex()) { assert(0 && "InitCompilerGlobals(): Failed to initalize parse context"); return false; } return true; } void FreeCompilerGlobals() { FreeParseContextIndex(); FreePoolIndex(); }