// // Copyright 2002 The ANGLE Project Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // #include "compiler/translator/Compiler.h" #include #include "angle_gl.h" #include "common/utilities.h" #include "compiler/translator/CallDAG.h" #include "compiler/translator/CollectVariables.h" #include "compiler/translator/Initialize.h" #include "compiler/translator/IsASTDepthBelowLimit.h" #include "compiler/translator/OutputTree.h" #include "compiler/translator/ParseContext.h" #include "compiler/translator/ValidateBarrierFunctionCall.h" #include "compiler/translator/ValidateClipCullDistance.h" #include "compiler/translator/ValidateLimitations.h" #include "compiler/translator/ValidateMaxParameters.h" #include "compiler/translator/ValidateOutputs.h" #include "compiler/translator/ValidateTypeSizeLimitations.h" #include "compiler/translator/ValidateVaryingLocations.h" #include "compiler/translator/VariablePacker.h" #include "compiler/translator/tree_ops/ClampIndirectIndices.h" #include "compiler/translator/tree_ops/ClampPointSize.h" #include "compiler/translator/tree_ops/DeclareAndInitBuiltinsForInstancedMultiview.h" #include "compiler/translator/tree_ops/DeferGlobalInitializers.h" #include "compiler/translator/tree_ops/EmulateGLFragColorBroadcast.h" #include "compiler/translator/tree_ops/EmulateMultiDrawShaderBuiltins.h" #include "compiler/translator/tree_ops/EmulatePrecision.h" #include "compiler/translator/tree_ops/FoldExpressions.h" #include "compiler/translator/tree_ops/ForcePrecisionQualifier.h" #include "compiler/translator/tree_ops/InitializeVariables.h" #include "compiler/translator/tree_ops/PruneEmptyCases.h" #include "compiler/translator/tree_ops/PruneNoOps.h" #include "compiler/translator/tree_ops/RemoveArrayLengthMethod.h" #include "compiler/translator/tree_ops/RemoveDynamicIndexing.h" #include "compiler/translator/tree_ops/RemoveInvariantDeclaration.h" #include "compiler/translator/tree_ops/RemoveUnreferencedVariables.h" #include "compiler/translator/tree_ops/ScalarizeVecAndMatConstructorArgs.h" #include "compiler/translator/tree_ops/SeparateDeclarations.h" #include "compiler/translator/tree_ops/SimplifyLoopConditions.h" #include "compiler/translator/tree_ops/SplitSequenceOperator.h" #include "compiler/translator/tree_ops/gl/ClampFragDepth.h" #include "compiler/translator/tree_ops/gl/RegenerateStructNames.h" #include "compiler/translator/tree_ops/gl/RewriteRepeatedAssignToSwizzled.h" #include "compiler/translator/tree_ops/gl/UseInterfaceBlockFields.h" #include "compiler/translator/tree_ops/gl/VectorizeVectorScalarArithmetic.h" #include "compiler/translator/tree_ops/gl/mac/AddAndTrueToLoopCondition.h" #include "compiler/translator/tree_ops/gl/mac/RewriteDoWhile.h" #include "compiler/translator/tree_ops/gl/mac/UnfoldShortCircuitAST.h" #include "compiler/translator/tree_ops/vulkan/EarlyFragmentTestsOptimization.h" #include "compiler/translator/tree_util/BuiltIn.h" #include "compiler/translator/tree_util/IntermNodePatternMatcher.h" #include "compiler/translator/tree_util/ReplaceShadowingVariables.h" #include "compiler/translator/util.h" namespace sh { namespace { #if defined(ANGLE_ENABLE_FUZZER_CORPUS_OUTPUT) void DumpFuzzerCase(char const *const *shaderStrings, size_t numStrings, uint32_t type, uint32_t spec, uint32_t output, uint64_t options) { static int fileIndex = 0; std::ostringstream o = sh::InitializeStream(); o << "corpus/" << fileIndex++ << ".sample"; std::string s = o.str(); // Must match the input format of the fuzzer FILE *f = fopen(s.c_str(), "w"); fwrite(&type, sizeof(type), 1, f); fwrite(&spec, sizeof(spec), 1, f); fwrite(&output, sizeof(output), 1, f); fwrite(&options, sizeof(options), 1, f); char zero[128 - 20] = {0}; fwrite(&zero, 128 - 20, 1, f); for (size_t i = 0; i < numStrings; i++) { fwrite(shaderStrings[i], sizeof(char), strlen(shaderStrings[i]), f); } fwrite(&zero, 1, 1, f); fclose(f); } #endif // defined(ANGLE_ENABLE_FUZZER_CORPUS_OUTPUT) } // anonymous namespace bool IsGLSL130OrNewer(ShShaderOutput output) { return (output == SH_GLSL_130_OUTPUT || output == SH_GLSL_140_OUTPUT || output == SH_GLSL_150_CORE_OUTPUT || output == SH_GLSL_330_CORE_OUTPUT || output == SH_GLSL_400_CORE_OUTPUT || output == SH_GLSL_410_CORE_OUTPUT || output == SH_GLSL_420_CORE_OUTPUT || output == SH_GLSL_430_CORE_OUTPUT || output == SH_GLSL_440_CORE_OUTPUT || output == SH_GLSL_450_CORE_OUTPUT); } bool IsGLSL420OrNewer(ShShaderOutput output) { return (output == SH_GLSL_420_CORE_OUTPUT || output == SH_GLSL_430_CORE_OUTPUT || output == SH_GLSL_440_CORE_OUTPUT || output == SH_GLSL_450_CORE_OUTPUT); } bool IsGLSL410OrOlder(ShShaderOutput output) { return (output == SH_GLSL_130_OUTPUT || output == SH_GLSL_140_OUTPUT || output == SH_GLSL_150_CORE_OUTPUT || output == SH_GLSL_330_CORE_OUTPUT || output == SH_GLSL_400_CORE_OUTPUT || output == SH_GLSL_410_CORE_OUTPUT); } bool RemoveInvariant(sh::GLenum shaderType, int shaderVersion, ShShaderOutput outputType, ShCompileOptions compileOptions) { if (shaderType == GL_FRAGMENT_SHADER && IsGLSL420OrNewer(outputType)) return true; if ((compileOptions & SH_REMOVE_INVARIANT_AND_CENTROID_FOR_ESSL3) != 0 && shaderVersion >= 300 && shaderType == GL_VERTEX_SHADER) return true; return false; } size_t GetGlobalMaxTokenSize(ShShaderSpec spec) { // WebGL defines a max token length of 256, while ES2 leaves max token // size undefined. ES3 defines a max size of 1024 characters. switch (spec) { case SH_WEBGL_SPEC: return 256; default: return 1024; } } int GetMaxUniformVectorsForShaderType(GLenum shaderType, const ShBuiltInResources &resources) { switch (shaderType) { case GL_VERTEX_SHADER: return resources.MaxVertexUniformVectors; case GL_FRAGMENT_SHADER: return resources.MaxFragmentUniformVectors; // TODO (jiawei.shao@intel.com): check if we need finer-grained component counting case GL_COMPUTE_SHADER: return resources.MaxComputeUniformComponents / 4; case GL_GEOMETRY_SHADER_EXT: return resources.MaxGeometryUniformComponents / 4; default: UNREACHABLE(); return -1; } } namespace { class TScopedPoolAllocator { public: TScopedPoolAllocator(angle::PoolAllocator *allocator) : mAllocator(allocator) { mAllocator->push(); SetGlobalPoolAllocator(mAllocator); } ~TScopedPoolAllocator() { SetGlobalPoolAllocator(nullptr); mAllocator->pop(); } private: angle::PoolAllocator *mAllocator; }; class TScopedSymbolTableLevel { public: TScopedSymbolTableLevel(TSymbolTable *table) : mTable(table) { ASSERT(mTable->isEmpty()); mTable->push(); } ~TScopedSymbolTableLevel() { while (!mTable->isEmpty()) mTable->pop(); } private: TSymbolTable *mTable; }; int GetMaxShaderVersionForSpec(ShShaderSpec spec) { switch (spec) { case SH_GLES2_SPEC: case SH_WEBGL_SPEC: return 100; case SH_GLES3_SPEC: case SH_WEBGL2_SPEC: return 300; case SH_GLES3_1_SPEC: case SH_WEBGL3_SPEC: return 310; case SH_GLES3_2_SPEC: return 320; case SH_GL_CORE_SPEC: case SH_GL_COMPATIBILITY_SPEC: return 460; default: UNREACHABLE(); return 0; } } bool ValidateFragColorAndFragData(GLenum shaderType, int shaderVersion, const TSymbolTable &symbolTable, TDiagnostics *diagnostics) { if (shaderVersion > 100 || shaderType != GL_FRAGMENT_SHADER) { return true; } bool usesFragColor = false; bool usesFragData = false; // This validation is a bit stricter than the spec - it's only an error to write to // both FragData and FragColor. But because it's better not to have reads from undefined // variables, we always return an error if they are both referenced, rather than only if they // are written. if (symbolTable.isStaticallyUsed(*BuiltInVariable::gl_FragColor()) || symbolTable.isStaticallyUsed(*BuiltInVariable::gl_SecondaryFragColorEXT())) { usesFragColor = true; } // Extension variables may not always be initialized (saves some time at symbol table init). bool secondaryFragDataUsed = symbolTable.gl_SecondaryFragDataEXT() != nullptr && symbolTable.isStaticallyUsed(*symbolTable.gl_SecondaryFragDataEXT()); if (symbolTable.isStaticallyUsed(*symbolTable.gl_FragData()) || secondaryFragDataUsed) { usesFragData = true; } if (usesFragColor && usesFragData) { const char *errorMessage = "cannot use both gl_FragData and gl_FragColor"; if (symbolTable.isStaticallyUsed(*BuiltInVariable::gl_SecondaryFragColorEXT()) || secondaryFragDataUsed) { errorMessage = "cannot use both output variable sets (gl_FragData, gl_SecondaryFragDataEXT)" " and (gl_FragColor, gl_SecondaryFragColorEXT)"; } diagnostics->globalError(errorMessage); return false; } return true; } } // namespace TShHandleBase::TShHandleBase() { allocator.push(); SetGlobalPoolAllocator(&allocator); } TShHandleBase::~TShHandleBase() { SetGlobalPoolAllocator(nullptr); allocator.popAll(); } TCompiler::TCompiler(sh::GLenum type, ShShaderSpec spec, ShShaderOutput output) : mVariablesCollected(false), mGLPositionInitialized(false), mShaderType(type), mShaderSpec(spec), mOutputType(output), mBuiltInFunctionEmulator(), mDiagnostics(mInfoSink.info), mSourcePath(nullptr), mComputeShaderLocalSizeDeclared(false), mComputeShaderLocalSize(1), mGeometryShaderMaxVertices(-1), mGeometryShaderInvocations(0), mGeometryShaderInputPrimitiveType(EptUndefined), mGeometryShaderOutputPrimitiveType(EptUndefined), mTessControlShaderOutputVertices(0), mTessEvaluationShaderInputPrimitiveType(EtetUndefined), mTessEvaluationShaderInputVertexSpacingType(EtetUndefined), mTessEvaluationShaderInputOrderingType(EtetUndefined), mTessEvaluationShaderInputPointType(EtetUndefined), mCompileOptions(0) {} TCompiler::~TCompiler() {} bool TCompiler::shouldRunLoopAndIndexingValidation(ShCompileOptions compileOptions) const { // If compiling an ESSL 1.00 shader for WebGL, or if its been requested through the API, // validate loop and indexing as well (to verify that the shader only uses minimal functionality // of ESSL 1.00 as in Appendix A of the spec). return (IsWebGLBasedSpec(mShaderSpec) && mShaderVersion == 100) || (compileOptions & SH_VALIDATE_LOOP_INDEXING) != 0; } bool TCompiler::shouldLimitTypeSizes() const { // WebGL shaders limit the size of variables' types in shaders, // including arrays, structs and interface blocks. return IsWebGLBasedSpec(mShaderSpec); } bool TCompiler::Init(const ShBuiltInResources &resources) { SetGlobalPoolAllocator(&allocator); // Generate built-in symbol table. if (!initBuiltInSymbolTable(resources)) return false; mResources = resources; setResourceString(); InitExtensionBehavior(resources, mExtensionBehavior); return true; } TIntermBlock *TCompiler::compileTreeForTesting(const char *const shaderStrings[], size_t numStrings, ShCompileOptions compileOptions) { return compileTreeImpl(shaderStrings, numStrings, compileOptions); } TIntermBlock *TCompiler::compileTreeImpl(const char *const shaderStrings[], size_t numStrings, const ShCompileOptions compileOptions) { // Remember the compile options for helper functions such as validateAST. mCompileOptions = compileOptions; clearResults(); ASSERT(numStrings > 0); ASSERT(GetGlobalPoolAllocator()); // Reset the extension behavior for each compilation unit. ResetExtensionBehavior(mResources, mExtensionBehavior, compileOptions); // If gl_DrawID is not supported, remove it from the available extensions // Currently we only allow emulation of gl_DrawID const bool glDrawIDSupported = (compileOptions & SH_EMULATE_GL_DRAW_ID) != 0; if (!glDrawIDSupported) { auto it = mExtensionBehavior.find(TExtension::ANGLE_multi_draw); if (it != mExtensionBehavior.end()) { mExtensionBehavior.erase(it); } } const bool glBaseVertexBaseInstanceSupported = (compileOptions & SH_EMULATE_GL_BASE_VERTEX_BASE_INSTANCE) != 0; if (!glBaseVertexBaseInstanceSupported) { auto it = mExtensionBehavior.find(TExtension::ANGLE_base_vertex_base_instance); if (it != mExtensionBehavior.end()) { mExtensionBehavior.erase(it); } } // First string is path of source file if flag is set. The actual source follows. size_t firstSource = 0; if ((compileOptions & SH_SOURCE_PATH) != 0) { mSourcePath = shaderStrings[0]; ++firstSource; } TParseContext parseContext(mSymbolTable, mExtensionBehavior, mShaderType, mShaderSpec, compileOptions, !IsDesktopGLSpec(mShaderSpec), &mDiagnostics, getResources(), getOutputType()); parseContext.setFragmentPrecisionHighOnESSL1(mResources.FragmentPrecisionHigh == 1); // We preserve symbols at the built-in level from compile-to-compile. // Start pushing the user-defined symbols at global level. TScopedSymbolTableLevel globalLevel(&mSymbolTable); ASSERT(mSymbolTable.atGlobalLevel()); // Parse shader. if (PaParseStrings(numStrings - firstSource, &shaderStrings[firstSource], nullptr, &parseContext) != 0) { return nullptr; } if (parseContext.getTreeRoot() == nullptr) { return nullptr; } setASTMetadata(parseContext); if (!checkShaderVersion(&parseContext)) { return nullptr; } TIntermBlock *root = parseContext.getTreeRoot(); if (!checkAndSimplifyAST(root, parseContext, compileOptions)) { return nullptr; } return root; } bool TCompiler::checkShaderVersion(TParseContext *parseContext) { if (GetMaxShaderVersionForSpec(mShaderSpec) < mShaderVersion) { mDiagnostics.globalError("unsupported shader version"); return false; } ASSERT(parseContext); switch (mShaderType) { case GL_COMPUTE_SHADER: if (mShaderVersion < 310) { mDiagnostics.globalError("Compute shader is not supported in this shader version."); return false; } break; case GL_GEOMETRY_SHADER_EXT: if (mShaderVersion < 310) { mDiagnostics.globalError( "Geometry shader is not supported in this shader version."); return false; } else if (mShaderVersion == 310) { if (!parseContext->checkCanUseOneOfExtensions( sh::TSourceLoc(), std::array{ {TExtension::EXT_geometry_shader, TExtension::OES_geometry_shader}})) { return false; } } break; case GL_TESS_CONTROL_SHADER_EXT: case GL_TESS_EVALUATION_SHADER_EXT: if (mShaderVersion < 310) { mDiagnostics.globalError( "Tessellation shaders are not supported in this shader version."); return false; } else if (mShaderVersion == 310) { if (!parseContext->checkCanUseExtension(sh::TSourceLoc(), TExtension::EXT_tessellation_shader)) { return false; } } break; default: break; } return true; } void TCompiler::setASTMetadata(const TParseContext &parseContext) { mShaderVersion = parseContext.getShaderVersion(); mPragma = parseContext.pragma(); mSymbolTable.setGlobalInvariant(mPragma.stdgl.invariantAll); mEarlyFragmentTestsSpecified = parseContext.isEarlyFragmentTestsSpecified(); mComputeShaderLocalSizeDeclared = parseContext.isComputeShaderLocalSizeDeclared(); mComputeShaderLocalSize = parseContext.getComputeShaderLocalSize(); mNumViews = parseContext.getNumViews(); if (mShaderType == GL_GEOMETRY_SHADER_EXT) { mGeometryShaderInputPrimitiveType = parseContext.getGeometryShaderInputPrimitiveType(); mGeometryShaderOutputPrimitiveType = parseContext.getGeometryShaderOutputPrimitiveType(); mGeometryShaderMaxVertices = parseContext.getGeometryShaderMaxVertices(); mGeometryShaderInvocations = parseContext.getGeometryShaderInvocations(); } if (mShaderType == GL_TESS_CONTROL_SHADER_EXT) { mTessControlShaderOutputVertices = parseContext.getTessControlShaderOutputVertices(); } if (mShaderType == GL_TESS_EVALUATION_SHADER_EXT) { mTessEvaluationShaderInputPrimitiveType = parseContext.getTessEvaluationShaderInputPrimitiveType(); mTessEvaluationShaderInputVertexSpacingType = parseContext.getTessEvaluationShaderInputVertexSpacingType(); mTessEvaluationShaderInputOrderingType = parseContext.getTessEvaluationShaderInputOrderingType(); mTessEvaluationShaderInputPointType = parseContext.getTessEvaluationShaderInputPointType(); } } unsigned int TCompiler::getSharedMemorySize() const { unsigned int sharedMemSize = 0; for (const sh::ShaderVariable &var : mSharedVariables) { sharedMemSize += var.getExternalSize(); } return sharedMemSize; } bool TCompiler::validateAST(TIntermNode *root) { if ((mCompileOptions & SH_VALIDATE_AST) != 0) { bool valid = ValidateAST(root, &mDiagnostics, mValidateASTOptions); #if defined(ANGLE_ENABLE_ASSERTS) if (!valid) { fprintf(stderr, "AST validation error(s):\n%s\n", mInfoSink.info.c_str()); } #endif // In debug, assert validation. In release, validation errors will be returned back to the // application as internal ANGLE errors. ASSERT(valid); return valid; } return true; } bool TCompiler::checkAndSimplifyAST(TIntermBlock *root, const TParseContext &parseContext, ShCompileOptions compileOptions) { mValidateASTOptions = {}; if (!validateAST(root)) { return false; } // Disallow expressions deemed too complex. if ((compileOptions & SH_LIMIT_EXPRESSION_COMPLEXITY) != 0 && !limitExpressionComplexity(root)) { return false; } if (shouldRunLoopAndIndexingValidation(compileOptions) && !ValidateLimitations(root, mShaderType, &mSymbolTable, &mDiagnostics)) { return false; } if (shouldLimitTypeSizes() && !ValidateTypeSizeLimitations(root, &mSymbolTable, &mDiagnostics)) { return false; } if (!ValidateFragColorAndFragData(mShaderType, mShaderVersion, mSymbolTable, &mDiagnostics)) { return false; } // Fold expressions that could not be folded before validation that was done as a part of // parsing. if (!FoldExpressions(this, root, &mDiagnostics)) { return false; } // Folding should only be able to generate warnings. ASSERT(mDiagnostics.numErrors() == 0); // Validate no barrier() after return before prunning it in |PruneNoOps()| below. if (mShaderType == GL_TESS_CONTROL_SHADER && !ValidateBarrierFunctionCall(root, &mDiagnostics)) { return false; } // We prune no-ops to work around driver bugs and to keep AST processing and output simple. // The following kinds of no-ops are pruned: // 1. Empty declarations "int;". // 2. Literal statements: "1.0;". The ESSL output doesn't define a default precision // for float, so float literal statements would end up with no precision which is // invalid ESSL. // 3. Any unreachable statement after a discard, return, break or continue. // After this empty declarations are not allowed in the AST. if (!PruneNoOps(this, root, &mSymbolTable)) { return false; } // We need to generate globals early if we have non constant initializers enabled bool initializeLocalsAndGlobals = (compileOptions & SH_INITIALIZE_UNINITIALIZED_LOCALS) != 0 && !IsOutputHLSL(getOutputType()); bool canUseLoopsToInitialize = (compileOptions & SH_DONT_USE_LOOPS_TO_INITIALIZE_VARIABLES) == 0; bool highPrecisionSupported = mShaderVersion > 100 || mShaderType != GL_FRAGMENT_SHADER || mResources.FragmentPrecisionHigh == 1; bool enableNonConstantInitializers = IsExtensionEnabled( mExtensionBehavior, TExtension::EXT_shader_non_constant_global_initializers); if (enableNonConstantInitializers && !DeferGlobalInitializers(this, root, initializeLocalsAndGlobals, canUseLoopsToInitialize, highPrecisionSupported, &mSymbolTable)) { return false; } // Create the function DAG and check there is no recursion if (!initCallDag(root)) { return false; } if ((compileOptions & SH_LIMIT_CALL_STACK_DEPTH) != 0 && !checkCallDepth()) { return false; } // Checks which functions are used and if "main" exists mFunctionMetadata.clear(); mFunctionMetadata.resize(mCallDag.size()); if (!tagUsedFunctions()) { return false; } pruneUnusedFunctions(root); if (IsSpecWithFunctionBodyNewScope(mShaderSpec, mShaderVersion)) { if (!ReplaceShadowingVariables(this, root, &mSymbolTable)) { return false; } } if (mShaderVersion >= 310 && !ValidateVaryingLocations(root, &mDiagnostics, mShaderType)) { return false; } if (mShaderVersion >= 300 && mShaderType == GL_FRAGMENT_SHADER && !ValidateOutputs(root, getExtensionBehavior(), mResources.MaxDrawBuffers, &mDiagnostics)) { return false; } // Fail compilation if precision emulation not supported. if (getResources().WEBGL_debug_shader_precision && getPragma().debugShaderPrecision && !EmulatePrecision::SupportedInLanguage(mOutputType)) { mDiagnostics.globalError("Precision emulation not supported for this output type."); return false; } if (parseContext.isExtensionEnabled(TExtension::EXT_clip_cull_distance)) { if (!ValidateClipCullDistance(root, &mDiagnostics, mResources.MaxCombinedClipAndCullDistances)) { return false; } } // Clamping uniform array bounds needs to happen after validateLimitations pass. if ((compileOptions & SH_CLAMP_INDIRECT_ARRAY_BOUNDS) != 0) { if (!ClampIndirectIndices(this, root, &mSymbolTable)) { return false; } } if ((compileOptions & SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW) != 0 && (parseContext.isExtensionEnabled(TExtension::OVR_multiview2) || parseContext.isExtensionEnabled(TExtension::OVR_multiview)) && getShaderType() != GL_COMPUTE_SHADER) { if (!DeclareAndInitBuiltinsForInstancedMultiview( this, root, mNumViews, mShaderType, compileOptions, mOutputType, &mSymbolTable)) { return false; } } // This pass might emit short circuits so keep it before the short circuit unfolding if ((compileOptions & SH_REWRITE_DO_WHILE_LOOPS) != 0) { if (!RewriteDoWhile(this, root, &mSymbolTable)) { return false; } } if ((compileOptions & SH_ADD_AND_TRUE_TO_LOOP_CONDITION) != 0) { if (!AddAndTrueToLoopCondition(this, root)) { return false; } } if ((compileOptions & SH_UNFOLD_SHORT_CIRCUIT) != 0) { if (!UnfoldShortCircuitAST(this, root)) { return false; } } if ((compileOptions & SH_REGENERATE_STRUCT_NAMES) != 0) { if (!RegenerateStructNames(this, root, &mSymbolTable)) { return false; } } if (mShaderType == GL_VERTEX_SHADER && IsExtensionEnabled(mExtensionBehavior, TExtension::ANGLE_multi_draw)) { if ((compileOptions & SH_EMULATE_GL_DRAW_ID) != 0) { if (!EmulateGLDrawID(this, root, &mSymbolTable, &mUniforms, shouldCollectVariables(compileOptions))) { return false; } } } if (mShaderType == GL_VERTEX_SHADER && IsExtensionEnabled(mExtensionBehavior, TExtension::ANGLE_base_vertex_base_instance)) { if ((compileOptions & SH_EMULATE_GL_BASE_VERTEX_BASE_INSTANCE) != 0) { if (!EmulateGLBaseVertexBaseInstance( this, root, &mSymbolTable, &mUniforms, shouldCollectVariables(compileOptions), (compileOptions & SH_ADD_BASE_VERTEX_TO_VERTEX_ID) != 0)) { return false; } } } if (mShaderType == GL_FRAGMENT_SHADER && mShaderVersion == 100 && mResources.EXT_draw_buffers && mResources.MaxDrawBuffers > 1 && IsExtensionEnabled(mExtensionBehavior, TExtension::EXT_draw_buffers)) { if (!EmulateGLFragColorBroadcast(this, root, mResources.MaxDrawBuffers, &mOutputVariables, &mSymbolTable, mShaderVersion)) { return false; } } int simplifyScalarized = (compileOptions & SH_SCALARIZE_VEC_AND_MAT_CONSTRUCTOR_ARGS) != 0 ? IntermNodePatternMatcher::kScalarizedVecOrMatConstructor : 0; // Split multi declarations and remove calls to array length(). // Note that SimplifyLoopConditions needs to be run before any other AST transformations // that may need to generate new statements from loop conditions or loop expressions. if (!SimplifyLoopConditions(this, root, IntermNodePatternMatcher::kMultiDeclaration | IntermNodePatternMatcher::kArrayLengthMethod | simplifyScalarized, &getSymbolTable())) { return false; } // Note that separate declarations need to be run before other AST transformations that // generate new statements from expressions. if (!SeparateDeclarations(this, root)) { return false; } mValidateASTOptions.validateMultiDeclarations = true; if (!SplitSequenceOperator(this, root, IntermNodePatternMatcher::kArrayLengthMethod | simplifyScalarized, &getSymbolTable())) { return false; } if (!RemoveArrayLengthMethod(this, root)) { return false; } if (!RemoveUnreferencedVariables(this, root, &mSymbolTable)) { return false; } // In case the last case inside a switch statement is a certain type of no-op, GLSL compilers in // drivers may not accept it. In this case we clean up the dead code from the end of switch // statements. This is also required because PruneNoOps or RemoveUnreferencedVariables may have // left switch statements that only contained an empty declaration inside the final case in an // invalid state. Relies on that PruneNoOps and RemoveUnreferencedVariables have already been // run. if (!PruneEmptyCases(this, root)) { return false; } // Built-in function emulation needs to happen after validateLimitations pass. // TODO(jmadill): Remove global pool allocator. GetGlobalPoolAllocator()->lock(); initBuiltInFunctionEmulator(&mBuiltInFunctionEmulator, compileOptions); GetGlobalPoolAllocator()->unlock(); mBuiltInFunctionEmulator.markBuiltInFunctionsForEmulation(root); if ((compileOptions & SH_SCALARIZE_VEC_AND_MAT_CONSTRUCTOR_ARGS) != 0) { if (!ScalarizeVecAndMatConstructorArgs(this, root, mShaderType, highPrecisionSupported, &mSymbolTable)) { return false; } } if ((compileOptions & SH_FORCE_SHADER_PRECISION_HIGHP_TO_MEDIUMP) != 0) { if (!ForceShaderPrecisionToMediump(root, &mSymbolTable, mShaderType)) { return false; } } if (shouldCollectVariables(compileOptions)) { ASSERT(!mVariablesCollected); CollectVariables(root, &mAttributes, &mOutputVariables, &mUniforms, &mInputVaryings, &mOutputVaryings, &mSharedVariables, &mUniformBlocks, &mShaderStorageBlocks, mResources.HashFunction, &mSymbolTable, mShaderType, mExtensionBehavior, mResources, mTessControlShaderOutputVertices); collectInterfaceBlocks(); mVariablesCollected = true; if ((compileOptions & SH_USE_UNUSED_STANDARD_SHARED_BLOCKS) != 0) { if (!useAllMembersInUnusedStandardAndSharedBlocks(root)) { return false; } } if ((compileOptions & SH_ENFORCE_PACKING_RESTRICTIONS) != 0) { int maxUniformVectors = GetMaxUniformVectorsForShaderType(mShaderType, mResources); // Returns true if, after applying the packing rules in the GLSL ES 1.00.17 spec // Appendix A, section 7, the shader does not use too many uniforms. if (!CheckVariablesInPackingLimits(maxUniformVectors, mUniforms)) { mDiagnostics.globalError("too many uniforms"); return false; } } bool needInitializeOutputVariables = (compileOptions & SH_INIT_OUTPUT_VARIABLES) != 0 && mShaderType != GL_COMPUTE_SHADER; needInitializeOutputVariables |= (compileOptions & SH_INIT_FRAGMENT_OUTPUT_VARIABLES) != 0 && mShaderType == GL_FRAGMENT_SHADER; if (needInitializeOutputVariables) { if (!initializeOutputVariables(root)) { return false; } } } // Removing invariant declarations must be done after collecting variables. // Otherwise, built-in invariant declarations don't apply. if (RemoveInvariant(mShaderType, mShaderVersion, mOutputType, compileOptions)) { if (!RemoveInvariantDeclaration(this, root)) { return false; } } // gl_Position is always written in compatibility output mode. // It may have been already initialized among other output variables, in that case we don't // need to initialize it twice. if (mShaderType == GL_VERTEX_SHADER && !mGLPositionInitialized && ((compileOptions & SH_INIT_GL_POSITION) != 0 || mOutputType == SH_GLSL_COMPATIBILITY_OUTPUT)) { if (!initializeGLPosition(root)) { return false; } mGLPositionInitialized = true; } // DeferGlobalInitializers needs to be run before other AST transformations that generate new // statements from expressions. But it's fine to run DeferGlobalInitializers after the above // SplitSequenceOperator and RemoveArrayLengthMethod since they only have an effect on the AST // on ESSL >= 3.00, and the initializers that need to be deferred can only exist in ESSL < 3.00. // Exception: if EXT_shader_non_constant_global_initializers is enabled, we must generate global // initializers before we generate the DAG, since initializers may call functions which must not // be optimized out if (!enableNonConstantInitializers && !DeferGlobalInitializers(this, root, initializeLocalsAndGlobals, canUseLoopsToInitialize, highPrecisionSupported, &mSymbolTable)) { return false; } if (initializeLocalsAndGlobals) { // Initialize uninitialized local variables. // In some cases initializing can generate extra statements in the parent block, such as // when initializing nameless structs or initializing arrays in ESSL 1.00. In that case // we need to first simplify loop conditions. We've already separated declarations // earlier, which is also required. If we don't follow the Appendix A limitations, loop // init statements can declare arrays or nameless structs and have multiple // declarations. if (!shouldRunLoopAndIndexingValidation(compileOptions)) { if (!SimplifyLoopConditions(this, root, IntermNodePatternMatcher::kArrayDeclaration | IntermNodePatternMatcher::kNamelessStructDeclaration, &getSymbolTable())) { return false; } } if (!InitializeUninitializedLocals(this, root, getShaderVersion(), canUseLoopsToInitialize, highPrecisionSupported, &getSymbolTable())) { return false; } } if (getShaderType() == GL_VERTEX_SHADER && (compileOptions & SH_CLAMP_POINT_SIZE) != 0) { if (!ClampPointSize(this, root, mResources.MaxPointSize, &getSymbolTable())) { return false; } } if (getShaderType() == GL_FRAGMENT_SHADER && (compileOptions & SH_CLAMP_FRAG_DEPTH) != 0) { if (!ClampFragDepth(this, root, &getSymbolTable())) { return false; } } if ((compileOptions & SH_REWRITE_REPEATED_ASSIGN_TO_SWIZZLED) != 0) { if (!sh::RewriteRepeatedAssignToSwizzled(this, root)) { return false; } } if ((compileOptions & SH_REWRITE_VECTOR_SCALAR_ARITHMETIC) != 0) { if (!VectorizeVectorScalarArithmetic(this, root, &getSymbolTable())) { return false; } } if ((compileOptions & SH_REMOVE_DYNAMIC_INDEXING_OF_SWIZZLED_VECTOR) != 0) { if (!sh::RemoveDynamicIndexingOfSwizzledVector(this, root, &getSymbolTable(), nullptr)) { return false; } } mEarlyFragmentTestsOptimized = false; if ((compileOptions & SH_EARLY_FRAGMENT_TESTS_OPTIMIZATION) != 0) { if (mShaderVersion <= 300 && mShaderType == GL_FRAGMENT_SHADER && !isEarlyFragmentTestsSpecified()) { mEarlyFragmentTestsOptimized = CheckEarlyFragmentTestsFeasible(this, root); } } return true; } bool TCompiler::compile(const char *const shaderStrings[], size_t numStrings, ShCompileOptions compileOptionsIn) { #if defined(ANGLE_ENABLE_FUZZER_CORPUS_OUTPUT) DumpFuzzerCase(shaderStrings, numStrings, mShaderType, mShaderSpec, mOutputType, compileOptionsIn); #endif // defined(ANGLE_ENABLE_FUZZER_CORPUS_OUTPUT) if (numStrings == 0) return true; ShCompileOptions compileOptions = compileOptionsIn; // Apply key workarounds. if (shouldFlattenPragmaStdglInvariantAll()) { // This should be harmless to do in all cases, but for the moment, do it only conditionally. compileOptions |= SH_FLATTEN_PRAGMA_STDGL_INVARIANT_ALL; } TScopedPoolAllocator scopedAlloc(&allocator); TIntermBlock *root = compileTreeImpl(shaderStrings, numStrings, compileOptions); if (root) { if ((compileOptions & SH_INTERMEDIATE_TREE) != 0) { OutputTree(root, mInfoSink.info); } if ((compileOptions & SH_OBJECT_CODE) != 0) { PerformanceDiagnostics perfDiagnostics(&mDiagnostics); if (!translate(root, compileOptions, &perfDiagnostics)) { return false; } } if (mShaderType == GL_VERTEX_SHADER) { bool lookForDrawID = IsExtensionEnabled(mExtensionBehavior, TExtension::ANGLE_multi_draw) && (compileOptions & SH_EMULATE_GL_DRAW_ID) != 0; bool lookForBaseVertexBaseInstance = IsExtensionEnabled(mExtensionBehavior, TExtension::ANGLE_base_vertex_base_instance) && (compileOptions & SH_EMULATE_GL_BASE_VERTEX_BASE_INSTANCE) != 0; if (lookForDrawID || lookForBaseVertexBaseInstance) { for (auto &uniform : mUniforms) { if (lookForDrawID && uniform.name == "angle_DrawID" && uniform.mappedName == "angle_DrawID") { uniform.name = "gl_DrawID"; } else if (lookForBaseVertexBaseInstance && uniform.name == "angle_BaseVertex" && uniform.mappedName == "angle_BaseVertex") { uniform.name = "gl_BaseVertex"; } else if (lookForBaseVertexBaseInstance && uniform.name == "angle_BaseInstance" && uniform.mappedName == "angle_BaseInstance") { uniform.name = "gl_BaseInstance"; } } } } // The IntermNode tree doesn't need to be deleted here, since the // memory will be freed in a big chunk by the PoolAllocator. return true; } return false; } bool TCompiler::initBuiltInSymbolTable(const ShBuiltInResources &resources) { if (resources.MaxDrawBuffers < 1) { return false; } if (resources.EXT_blend_func_extended && resources.MaxDualSourceDrawBuffers < 1) { return false; } mSymbolTable.initializeBuiltIns(mShaderType, mShaderSpec, resources); return true; } void TCompiler::setResourceString() { std::ostringstream strstream = sh::InitializeStream(); // clang-format off strstream << ":MaxVertexAttribs:" << mResources.MaxVertexAttribs << ":MaxVertexUniformVectors:" << mResources.MaxVertexUniformVectors << ":MaxVaryingVectors:" << mResources.MaxVaryingVectors << ":MaxVertexTextureImageUnits:" << mResources.MaxVertexTextureImageUnits << ":MaxCombinedTextureImageUnits:" << mResources.MaxCombinedTextureImageUnits << ":MaxTextureImageUnits:" << mResources.MaxTextureImageUnits << ":MaxFragmentUniformVectors:" << mResources.MaxFragmentUniformVectors << ":MaxDrawBuffers:" << mResources.MaxDrawBuffers << ":OES_standard_derivatives:" << mResources.OES_standard_derivatives << ":OES_EGL_image_external:" << mResources.OES_EGL_image_external << ":OES_EGL_image_external_essl3:" << mResources.OES_EGL_image_external_essl3 << ":NV_EGL_stream_consumer_external:" << mResources.NV_EGL_stream_consumer_external << ":ARB_texture_rectangle:" << mResources.ARB_texture_rectangle << ":EXT_draw_buffers:" << mResources.EXT_draw_buffers << ":FragmentPrecisionHigh:" << mResources.FragmentPrecisionHigh << ":MaxExpressionComplexity:" << mResources.MaxExpressionComplexity << ":MaxCallStackDepth:" << mResources.MaxCallStackDepth << ":MaxFunctionParameters:" << mResources.MaxFunctionParameters << ":EXT_blend_func_extended:" << mResources.EXT_blend_func_extended << ":EXT_frag_depth:" << mResources.EXT_frag_depth << ":EXT_shader_texture_lod:" << mResources.EXT_shader_texture_lod << ":EXT_shader_framebuffer_fetch:" << mResources.EXT_shader_framebuffer_fetch << ":EXT_shader_framebuffer_fetch_non_coherent:" << mResources.EXT_shader_framebuffer_fetch_non_coherent << ":NV_shader_framebuffer_fetch:" << mResources.NV_shader_framebuffer_fetch << ":ARM_shader_framebuffer_fetch:" << mResources.ARM_shader_framebuffer_fetch << ":OVR_multiview2:" << mResources.OVR_multiview2 << ":OVR_multiview:" << mResources.OVR_multiview << ":EXT_YUV_target:" << mResources.EXT_YUV_target << ":EXT_geometry_shader:" << mResources.EXT_geometry_shader << ":OES_geometry_shader:" << mResources.OES_geometry_shader << ":OES_shader_io_blocks:" << mResources.OES_shader_io_blocks << ":EXT_shader_io_blocks:" << mResources.EXT_shader_io_blocks << ":EXT_gpu_shader5:" << mResources.EXT_gpu_shader5 << ":OES_texture_3D:" << mResources.OES_texture_3D << ":MaxVertexOutputVectors:" << mResources.MaxVertexOutputVectors << ":MaxFragmentInputVectors:" << mResources.MaxFragmentInputVectors << ":MinProgramTexelOffset:" << mResources.MinProgramTexelOffset << ":MaxProgramTexelOffset:" << mResources.MaxProgramTexelOffset << ":MaxDualSourceDrawBuffers:" << mResources.MaxDualSourceDrawBuffers << ":MaxViewsOVR:" << mResources.MaxViewsOVR << ":NV_draw_buffers:" << mResources.NV_draw_buffers << ":WEBGL_debug_shader_precision:" << mResources.WEBGL_debug_shader_precision << ":ANGLE_multi_draw:" << mResources.ANGLE_multi_draw << ":ANGLE_base_vertex_base_instance:" << mResources.ANGLE_base_vertex_base_instance << ":APPLE_clip_distance:" << mResources.APPLE_clip_distance << ":OES_texture_cube_map_array:" << mResources.OES_texture_cube_map_array << ":EXT_texture_cube_map_array:" << mResources.EXT_texture_cube_map_array << ":EXT_shadow_samplers:" << mResources.EXT_shadow_samplers << ":OES_shader_multisample_interpolation:" << mResources.OES_shader_multisample_interpolation << ":OES_shader_image_atomic:" << mResources.OES_shader_image_atomic << ":EXT_tessellation_shader:" << mResources.EXT_tessellation_shader << ":OES_texture_buffer:" << mResources.OES_texture_buffer << ":EXT_texture_buffer:" << mResources.EXT_texture_buffer << ":OES_sample_variables:" << mResources.OES_sample_variables << ":EXT_clip_cull_distance:" << mResources.EXT_clip_cull_distance << ":MinProgramTextureGatherOffset:" << mResources.MinProgramTextureGatherOffset << ":MaxProgramTextureGatherOffset:" << mResources.MaxProgramTextureGatherOffset << ":MaxImageUnits:" << mResources.MaxImageUnits << ":MaxSamples:" << mResources.MaxSamples << ":MaxVertexImageUniforms:" << mResources.MaxVertexImageUniforms << ":MaxFragmentImageUniforms:" << mResources.MaxFragmentImageUniforms << ":MaxComputeImageUniforms:" << mResources.MaxComputeImageUniforms << ":MaxCombinedImageUniforms:" << mResources.MaxCombinedImageUniforms << ":MaxCombinedShaderOutputResources:" << mResources.MaxCombinedShaderOutputResources << ":MaxComputeWorkGroupCountX:" << mResources.MaxComputeWorkGroupCount[0] << ":MaxComputeWorkGroupCountY:" << mResources.MaxComputeWorkGroupCount[1] << ":MaxComputeWorkGroupCountZ:" << mResources.MaxComputeWorkGroupCount[2] << ":MaxComputeWorkGroupSizeX:" << mResources.MaxComputeWorkGroupSize[0] << ":MaxComputeWorkGroupSizeY:" << mResources.MaxComputeWorkGroupSize[1] << ":MaxComputeWorkGroupSizeZ:" << mResources.MaxComputeWorkGroupSize[2] << ":MaxComputeUniformComponents:" << mResources.MaxComputeUniformComponents << ":MaxComputeTextureImageUnits:" << mResources.MaxComputeTextureImageUnits << ":MaxComputeAtomicCounters:" << mResources.MaxComputeAtomicCounters << ":MaxComputeAtomicCounterBuffers:" << mResources.MaxComputeAtomicCounterBuffers << ":MaxVertexAtomicCounters:" << mResources.MaxVertexAtomicCounters << ":MaxFragmentAtomicCounters:" << mResources.MaxFragmentAtomicCounters << ":MaxCombinedAtomicCounters:" << mResources.MaxCombinedAtomicCounters << ":MaxAtomicCounterBindings:" << mResources.MaxAtomicCounterBindings << ":MaxVertexAtomicCounterBuffers:" << mResources.MaxVertexAtomicCounterBuffers << ":MaxFragmentAtomicCounterBuffers:" << mResources.MaxFragmentAtomicCounterBuffers << ":MaxCombinedAtomicCounterBuffers:" << mResources.MaxCombinedAtomicCounterBuffers << ":MaxAtomicCounterBufferSize:" << mResources.MaxAtomicCounterBufferSize << ":MaxGeometryUniformComponents:" << mResources.MaxGeometryUniformComponents << ":MaxGeometryUniformBlocks:" << mResources.MaxGeometryUniformBlocks << ":MaxGeometryInputComponents:" << mResources.MaxGeometryInputComponents << ":MaxGeometryOutputComponents:" << mResources.MaxGeometryOutputComponents << ":MaxGeometryOutputVertices:" << mResources.MaxGeometryOutputVertices << ":MaxGeometryTotalOutputComponents:" << mResources.MaxGeometryTotalOutputComponents << ":MaxGeometryTextureImageUnits:" << mResources.MaxGeometryTextureImageUnits << ":MaxGeometryAtomicCounterBuffers:" << mResources.MaxGeometryAtomicCounterBuffers << ":MaxGeometryAtomicCounters:" << mResources.MaxGeometryAtomicCounters << ":MaxGeometryShaderStorageBlocks:" << mResources.MaxGeometryShaderStorageBlocks << ":MaxGeometryShaderInvocations:" << mResources.MaxGeometryShaderInvocations << ":MaxGeometryImageUniforms:" << mResources.MaxGeometryImageUniforms << ":MaxClipDistances" << mResources.MaxClipDistances << ":MaxCullDistances" << mResources.MaxCullDistances << ":MaxCombinedClipAndCullDistances" << mResources.MaxCombinedClipAndCullDistances << ":MaxTessControlInputComponents:" << mResources.MaxTessControlInputComponents << ":MaxTessControlOutputComponents:" << mResources.MaxTessControlOutputComponents << ":MaxTessControlTextureImageUnits:" << mResources.MaxTessControlTextureImageUnits << ":MaxTessControlUniformComponents:" << mResources.MaxTessControlUniformComponents << ":MaxTessControlTotalOutputComponents:" << mResources.MaxTessControlTotalOutputComponents << ":MaxTessControlImageUniforms:" << mResources.MaxTessControlImageUniforms << ":MaxTessControlAtomicCounters:" << mResources.MaxTessControlAtomicCounters << ":MaxTessControlAtomicCounterBuffers:" << mResources.MaxTessControlAtomicCounterBuffers << ":MaxTessPatchComponents:" << mResources.MaxTessPatchComponents << ":MaxPatchVertices:" << mResources.MaxPatchVertices << ":MaxTessGenLevel:" << mResources.MaxTessGenLevel << ":MaxTessEvaluationInputComponents:" << mResources.MaxTessEvaluationInputComponents << ":MaxTessEvaluationOutputComponents:" << mResources.MaxTessEvaluationOutputComponents << ":MaxTessEvaluationTextureImageUnits:" << mResources.MaxTessEvaluationTextureImageUnits << ":MaxTessEvaluationUniformComponents:" << mResources.MaxTessEvaluationUniformComponents << ":MaxTessEvaluationImageUniforms:" << mResources.MaxTessEvaluationImageUniforms << ":MaxTessEvaluationAtomicCounters:" << mResources.MaxTessEvaluationAtomicCounters << ":MaxTessEvaluationAtomicCounterBuffers:" << mResources.MaxTessEvaluationAtomicCounterBuffers; // clang-format on mBuiltInResourcesString = strstream.str(); } void TCompiler::collectInterfaceBlocks() { ASSERT(mInterfaceBlocks.empty()); mInterfaceBlocks.reserve(mUniformBlocks.size() + mShaderStorageBlocks.size()); mInterfaceBlocks.insert(mInterfaceBlocks.end(), mUniformBlocks.begin(), mUniformBlocks.end()); mInterfaceBlocks.insert(mInterfaceBlocks.end(), mShaderStorageBlocks.begin(), mShaderStorageBlocks.end()); } bool TCompiler::emulatePrecisionIfNeeded(TIntermBlock *root, TInfoSinkBase &sink, bool *isNeeded, const ShShaderOutput outputLanguage) { *isNeeded = getResources().WEBGL_debug_shader_precision && getPragma().debugShaderPrecision; if (*isNeeded) { EmulatePrecision emulatePrecision(&getSymbolTable()); root->traverse(&emulatePrecision); if (!emulatePrecision.updateTree(this, root)) { return false; } emulatePrecision.writeEmulationHelpers(sink, getShaderVersion(), outputLanguage); } return true; } void TCompiler::clearResults() { mInfoSink.info.erase(); mInfoSink.obj.erase(); mInfoSink.debug.erase(); mDiagnostics.resetErrorCount(); mAttributes.clear(); mOutputVariables.clear(); mUniforms.clear(); mInputVaryings.clear(); mOutputVaryings.clear(); mSharedVariables.clear(); mInterfaceBlocks.clear(); mUniformBlocks.clear(); mShaderStorageBlocks.clear(); mVariablesCollected = false; mGLPositionInitialized = false; mNumViews = -1; mGeometryShaderInputPrimitiveType = EptUndefined; mGeometryShaderOutputPrimitiveType = EptUndefined; mGeometryShaderInvocations = 0; mGeometryShaderMaxVertices = -1; mTessControlShaderOutputVertices = 0; mTessEvaluationShaderInputPrimitiveType = EtetUndefined; mTessEvaluationShaderInputVertexSpacingType = EtetUndefined; mTessEvaluationShaderInputOrderingType = EtetUndefined; mTessEvaluationShaderInputPointType = EtetUndefined; mBuiltInFunctionEmulator.cleanup(); mNameMap.clear(); mSourcePath = nullptr; mSymbolTable.clearCompilationResults(); } bool TCompiler::initCallDag(TIntermNode *root) { mCallDag.clear(); switch (mCallDag.init(root, &mDiagnostics)) { case CallDAG::INITDAG_SUCCESS: return true; case CallDAG::INITDAG_RECURSION: case CallDAG::INITDAG_UNDEFINED: // Error message has already been written out. ASSERT(mDiagnostics.numErrors() > 0); return false; } UNREACHABLE(); return true; } bool TCompiler::checkCallDepth() { std::vector depths(mCallDag.size()); for (size_t i = 0; i < mCallDag.size(); i++) { int depth = 0; const CallDAG::Record &record = mCallDag.getRecordFromIndex(i); for (int calleeIndex : record.callees) { depth = std::max(depth, depths[calleeIndex] + 1); } depths[i] = depth; if (depth >= mResources.MaxCallStackDepth) { // Trace back the function chain to have a meaningful info log. std::stringstream errorStream = sh::InitializeStream(); errorStream << "Call stack too deep (larger than " << mResources.MaxCallStackDepth << ") with the following call chain: " << record.node->getFunction()->name(); int currentFunction = static_cast(i); int currentDepth = depth; while (currentFunction != -1) { errorStream << " -> " << mCallDag.getRecordFromIndex(currentFunction).node->getFunction()->name(); int nextFunction = -1; for (const int &calleeIndex : mCallDag.getRecordFromIndex(currentFunction).callees) { if (depths[calleeIndex] == currentDepth - 1) { currentDepth--; nextFunction = calleeIndex; } } currentFunction = nextFunction; } std::string errorStr = errorStream.str(); mDiagnostics.globalError(errorStr.c_str()); return false; } } return true; } bool TCompiler::tagUsedFunctions() { // Search from main, starting from the end of the DAG as it usually is the root. for (size_t i = mCallDag.size(); i-- > 0;) { if (mCallDag.getRecordFromIndex(i).node->getFunction()->isMain()) { internalTagUsedFunction(i); return true; } } mDiagnostics.globalError("Missing main()"); return false; } void TCompiler::internalTagUsedFunction(size_t index) { if (mFunctionMetadata[index].used) { return; } mFunctionMetadata[index].used = true; for (int calleeIndex : mCallDag.getRecordFromIndex(index).callees) { internalTagUsedFunction(calleeIndex); } } // A predicate for the stl that returns if a top-level node is unused class TCompiler::UnusedPredicate { public: UnusedPredicate(const CallDAG *callDag, const std::vector *metadatas) : mCallDag(callDag), mMetadatas(metadatas) {} bool operator()(TIntermNode *node) { const TIntermFunctionPrototype *asFunctionPrototype = node->getAsFunctionPrototypeNode(); const TIntermFunctionDefinition *asFunctionDefinition = node->getAsFunctionDefinition(); const TFunction *func = nullptr; if (asFunctionDefinition) { func = asFunctionDefinition->getFunction(); } else if (asFunctionPrototype) { func = asFunctionPrototype->getFunction(); } if (func == nullptr) { return false; } size_t callDagIndex = mCallDag->findIndex(func->uniqueId()); if (callDagIndex == CallDAG::InvalidIndex) { // This happens only for unimplemented prototypes which are thus unused ASSERT(asFunctionPrototype); return true; } ASSERT(callDagIndex < mMetadatas->size()); return !(*mMetadatas)[callDagIndex].used; } private: const CallDAG *mCallDag; const std::vector *mMetadatas; }; void TCompiler::pruneUnusedFunctions(TIntermBlock *root) { UnusedPredicate isUnused(&mCallDag, &mFunctionMetadata); TIntermSequence *sequence = root->getSequence(); if (!sequence->empty()) { sequence->erase(std::remove_if(sequence->begin(), sequence->end(), isUnused), sequence->end()); } } bool TCompiler::limitExpressionComplexity(TIntermBlock *root) { if (!IsASTDepthBelowLimit(root, mResources.MaxExpressionComplexity)) { mDiagnostics.globalError("Expression too complex."); return false; } if (!ValidateMaxParameters(root, mResources.MaxFunctionParameters)) { mDiagnostics.globalError("Function has too many parameters."); return false; } return true; } bool TCompiler::shouldCollectVariables(ShCompileOptions compileOptions) { return (compileOptions & SH_VARIABLES) != 0; } bool TCompiler::wereVariablesCollected() const { return mVariablesCollected; } bool TCompiler::initializeGLPosition(TIntermBlock *root) { sh::ShaderVariable var(GL_FLOAT_VEC4); var.name = "gl_Position"; return InitializeVariables(this, root, {var}, &mSymbolTable, mShaderVersion, mExtensionBehavior, false, false); } bool TCompiler::useAllMembersInUnusedStandardAndSharedBlocks(TIntermBlock *root) { sh::InterfaceBlockList list; for (const sh::InterfaceBlock &block : mUniformBlocks) { if (!block.staticUse && (block.layout == sh::BLOCKLAYOUT_STD140 || block.layout == sh::BLOCKLAYOUT_SHARED)) { list.push_back(block); } } return sh::UseInterfaceBlockFields(this, root, list, mSymbolTable); } bool TCompiler::initializeOutputVariables(TIntermBlock *root) { InitVariableList list; list.reserve(mOutputVaryings.size()); if (mShaderType == GL_VERTEX_SHADER || mShaderType == GL_GEOMETRY_SHADER_EXT) { for (const sh::ShaderVariable &var : mOutputVaryings) { list.push_back(var); if (var.name == "gl_Position") { ASSERT(!mGLPositionInitialized); mGLPositionInitialized = true; } } } else { ASSERT(mShaderType == GL_FRAGMENT_SHADER); for (const sh::ShaderVariable &var : mOutputVariables) { list.push_back(var); } } return InitializeVariables(this, root, list, &mSymbolTable, mShaderVersion, mExtensionBehavior, false, false); } const TExtensionBehavior &TCompiler::getExtensionBehavior() const { return mExtensionBehavior; } const char *TCompiler::getSourcePath() const { return mSourcePath; } const ShBuiltInResources &TCompiler::getResources() const { return mResources; } const BuiltInFunctionEmulator &TCompiler::getBuiltInFunctionEmulator() const { return mBuiltInFunctionEmulator; } bool TCompiler::isVaryingDefined(const char *varyingName) { ASSERT(mVariablesCollected); for (size_t ii = 0; ii < mInputVaryings.size(); ++ii) { if (mInputVaryings[ii].name == varyingName) { return true; } } for (size_t ii = 0; ii < mOutputVaryings.size(); ++ii) { if (mOutputVaryings[ii].name == varyingName) { return true; } } return false; } } // namespace sh