summaryrefslogtreecommitdiffstats
path: root/gfx/angle/checkout/src/compiler/translator/Compiler.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /gfx/angle/checkout/src/compiler/translator/Compiler.cpp
parentInitial commit. (diff)
downloadfirefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz
firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'gfx/angle/checkout/src/compiler/translator/Compiler.cpp')
-rw-r--r--gfx/angle/checkout/src/compiler/translator/Compiler.cpp1746
1 files changed, 1746 insertions, 0 deletions
diff --git a/gfx/angle/checkout/src/compiler/translator/Compiler.cpp b/gfx/angle/checkout/src/compiler/translator/Compiler.cpp
new file mode 100644
index 0000000000..891d1ddf61
--- /dev/null
+++ b/gfx/angle/checkout/src/compiler/translator/Compiler.cpp
@@ -0,0 +1,1746 @@
+//
+// 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 <sstream>
+
+#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/FoldExpressions.h"
+#include "compiler/translator/tree_ops/ForcePrecisionQualifier.h"
+#include "compiler/translator/tree_ops/InitializeVariables.h"
+#include "compiler/translator/tree_ops/MonomorphizeUnsupportedFunctions.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/RewritePixelLocalStorage.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/apple/AddAndTrueToLoopCondition.h"
+#include "compiler/translator/tree_ops/apple/RewriteDoWhile.h"
+#include "compiler/translator/tree_ops/apple/UnfoldShortCircuitAST.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_util/BuiltIn.h"
+#include "compiler/translator/tree_util/IntermNodePatternMatcher.h"
+#include "compiler/translator/tree_util/ReplaceShadowingVariables.h"
+#include "compiler/translator/util.h"
+
+// #define ANGLE_FUZZER_CORPUS_OUTPUT_DIR "corpus/"
+
+#if defined(ANGLE_FUZZER_CORPUS_OUTPUT_DIR)
+# include "common/hash_utils.h"
+# include "common/mathutil.h"
+#endif
+
+namespace sh
+{
+
+namespace
+{
+// Helper that returns if a top-level node is unused. If it's a function, the function prototype is
+// returned as well.
+bool IsTopLevelNodeUnusedFunction(const CallDAG &callDag,
+ const std::vector<TFunctionMetadata> &metadata,
+ TIntermNode *node,
+ const TFunction **functionOut)
+{
+ const TIntermFunctionPrototype *asFunctionPrototype = node->getAsFunctionPrototypeNode();
+ const TIntermFunctionDefinition *asFunctionDefinition = node->getAsFunctionDefinition();
+
+ *functionOut = nullptr;
+
+ if (asFunctionDefinition)
+ {
+ *functionOut = asFunctionDefinition->getFunction();
+ }
+ else if (asFunctionPrototype)
+ {
+ *functionOut = asFunctionPrototype->getFunction();
+ }
+ if (*functionOut == nullptr)
+ {
+ return false;
+ }
+
+ size_t callDagIndex = callDag.findIndex((*functionOut)->uniqueId());
+ if (callDagIndex == CallDAG::InvalidIndex)
+ {
+ // This happens only for unimplemented prototypes which are thus unused
+ ASSERT(asFunctionPrototype);
+ return true;
+ }
+
+ ASSERT(callDagIndex < metadata.size());
+ return !metadata[callDagIndex].used;
+}
+
+#if defined(ANGLE_FUZZER_CORPUS_OUTPUT_DIR)
+void DumpFuzzerCase(char const *const *shaderStrings,
+ size_t numStrings,
+ uint32_t type,
+ uint32_t spec,
+ uint32_t output,
+ const ShCompileOptions &options)
+{
+ ShaderDumpHeader header{};
+ header.type = type;
+ header.spec = spec;
+ header.output = output;
+ memcpy(&header.basicCompileOptions, &options, offsetof(ShCompileOptions, metal));
+ static_assert(offsetof(ShCompileOptions, metal) <= sizeof(header.basicCompileOptions));
+ memcpy(&header.metalCompileOptions, &options.metal, sizeof(options.metal));
+ static_assert(sizeof(options.metal) <= sizeof(header.metalCompileOptions));
+ memcpy(&header.plsCompileOptions, &options.pls, sizeof(options.pls));
+ static_assert(sizeof(options.pls) <= sizeof(header.plsCompileOptions));
+ size_t contentsLength = sizeof(header) + 1; // Extra: header + nul terminator.
+ for (size_t i = 0; i < numStrings; i++)
+ {
+ contentsLength += strlen(shaderStrings[i]);
+ }
+ std::vector<uint8_t> contents(rx::roundUp<size_t>(contentsLength, 4), 0);
+ memcpy(&contents[0], &header, sizeof(header));
+ uint8_t *data = &contents[sizeof(header)];
+ for (size_t i = 0; i < numStrings; i++)
+ {
+ auto length = strlen(shaderStrings[i]);
+ memcpy(data, shaderStrings[i], length);
+ data += length;
+ }
+ auto hash = angle::ComputeGenericHash(contents.data(), contents.size());
+
+ std::ostringstream o = sh::InitializeStream<std::ostringstream>();
+ o << ANGLE_FUZZER_CORPUS_OUTPUT_DIR << std::hex << std::setw(16) << std::setfill('0') << hash
+ << ".sample";
+ std::string s = o.str();
+
+ // Must match the input format of the fuzzer
+ FILE *f = fopen(s.c_str(), "w");
+ fwrite(contents.data(), sizeof(char), contentsLength, f);
+ fclose(f);
+}
+#endif // defined(ANGLE_FUZZER_CORPUS_OUTPUT_DIR)
+} // 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,
+ const ShCompileOptions &compileOptions)
+{
+ if (shaderType == GL_FRAGMENT_SHADER && IsGLSL420OrNewer(outputType))
+ return true;
+
+ if (compileOptions.removeInvariantAndCentroidForESSL3 && 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 [[nodiscard]] TScopedPoolAllocator
+{
+ public:
+ TScopedPoolAllocator(angle::PoolAllocator *allocator) : mAllocator(allocator)
+ {
+ mAllocator->push();
+ SetGlobalPoolAllocator(mAllocator);
+ }
+ ~TScopedPoolAllocator()
+ {
+ SetGlobalPoolAllocator(nullptr);
+ mAllocator->pop();
+ }
+
+ private:
+ angle::PoolAllocator *mAllocator;
+};
+
+class [[nodiscard]] 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),
+ mHasAnyPreciseType(false),
+ mAdvancedBlendEquations(0),
+ mHasPixelLocalStorageUniforms(false),
+ mCompileOptions{}
+{}
+
+TCompiler::~TCompiler() {}
+
+bool TCompiler::isHighPrecisionSupported() const
+{
+ return mShaderVersion > 100 || mShaderType != GL_FRAGMENT_SHADER ||
+ mResources.FragmentPrecisionHigh == 1;
+}
+
+bool TCompiler::shouldRunLoopAndIndexingValidation(const 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.validateLoopIndexing;
+}
+
+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,
+ const 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.emulateGLDrawID;
+ if (!glDrawIDSupported)
+ {
+ auto it = mExtensionBehavior.find(TExtension::ANGLE_multi_draw);
+ if (it != mExtensionBehavior.end())
+ {
+ mExtensionBehavior.erase(it);
+ }
+ }
+
+ const bool glBaseVertexBaseInstanceSupported = compileOptions.emulateGLBaseVertexBaseInstance;
+ if (!glBaseVertexBaseInstanceSupported)
+ {
+ auto it =
+ mExtensionBehavior.find(TExtension::ANGLE_base_vertex_base_instance_shader_builtin);
+ 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.sourcePath)
+ {
+ 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 (!postParseChecks(parseContext))
+ {
+ 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, 2u>{
+ {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();
+
+ mHasDiscard = parseContext.hasDiscard();
+
+ mEnablesPerSampleShading = parseContext.isSampleQualifierSpecified();
+
+ mComputeShaderLocalSizeDeclared = parseContext.isComputeShaderLocalSizeDeclared();
+ mComputeShaderLocalSize = parseContext.getComputeShaderLocalSize();
+
+ mNumViews = parseContext.getNumViews();
+
+ mHasAnyPreciseType = parseContext.hasAnyPreciseType();
+
+ if (mShaderType == GL_FRAGMENT_SHADER)
+ {
+ mAdvancedBlendEquations = parseContext.getAdvancedBlendEquations();
+ mHasPixelLocalStorageUniforms = !parseContext.pixelLocalStorageBindings().empty();
+ }
+ 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.validateAST)
+ {
+ bool valid = ValidateAST(root, &mDiagnostics, mValidateASTOptions);
+
+#if defined(ANGLE_ENABLE_ASSERTS)
+ if (!valid)
+ {
+ OutputTree(root, mInfoSink.info);
+ 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::disableValidateFunctionCall()
+{
+ bool wasEnabled = mValidateASTOptions.validateFunctionCall;
+ mValidateASTOptions.validateFunctionCall = false;
+ return wasEnabled;
+}
+
+void TCompiler::restoreValidateFunctionCall(bool enable)
+{
+ ASSERT(!mValidateASTOptions.validateFunctionCall);
+ mValidateASTOptions.validateFunctionCall = enable;
+}
+
+bool TCompiler::disableValidateVariableReferences()
+{
+ bool wasEnabled = mValidateASTOptions.validateVariableReferences;
+ mValidateASTOptions.validateVariableReferences = false;
+ return wasEnabled;
+}
+
+void TCompiler::restoreValidateVariableReferences(bool enable)
+{
+ ASSERT(!mValidateASTOptions.validateVariableReferences);
+ mValidateASTOptions.validateVariableReferences = enable;
+}
+
+void TCompiler::enableValidateNoMoreTransformations()
+{
+ mValidateASTOptions.validateNoMoreTransformations = true;
+}
+
+bool TCompiler::checkAndSimplifyAST(TIntermBlock *root,
+ const TParseContext &parseContext,
+ const ShCompileOptions &compileOptions)
+{
+ mValidateASTOptions = {};
+
+ // Desktop GLSL shaders don't have precision, so don't expect them to be specified.
+ mValidateASTOptions.validatePrecision = !IsDesktopGLSpec(mShaderSpec);
+
+ if (!validateAST(root))
+ {
+ return false;
+ }
+
+ // For now, rewrite pixel local storage before collecting variables or any operations on images.
+ //
+ // TODO(anglebug.com/7279):
+ // Should this actually run after collecting variables?
+ // Do we need more introspection?
+ // Do we want to hide rewritten shader image uniforms from glGetActiveUniform?
+ if (hasPixelLocalStorageUniforms())
+ {
+ ASSERT(
+ IsExtensionEnabled(mExtensionBehavior, TExtension::ANGLE_shader_pixel_local_storage));
+ if (!RewritePixelLocalStorage(this, root, getSymbolTable(), compileOptions,
+ getShaderVersion()))
+ {
+ mDiagnostics.globalError("internal compiler error translating pixel local storage");
+ return false;
+ }
+ }
+
+ // Disallow expressions deemed too complex.
+ if (compileOptions.limitExpressionComplexity && !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;
+ }
+ mValidateASTOptions.validateNoStatementsAfterBranch = true;
+
+ // We need to generate globals early if we have non constant initializers enabled
+ bool initializeLocalsAndGlobals =
+ compileOptions.initializeUninitializedLocals && !IsOutputHLSL(getOutputType());
+ bool canUseLoopsToInitialize = !compileOptions.dontUseLoopsToInitializeVariables;
+ bool highPrecisionSupported = isHighPrecisionSupported();
+ bool enableNonConstantInitializers = IsExtensionEnabled(
+ mExtensionBehavior, TExtension::EXT_shader_non_constant_global_initializers);
+ // forceDeferGlobalInitializers is needed for MSL
+ // to convert a non-const global. For example:
+ //
+ // int someGlobal = 123;
+ //
+ // to
+ //
+ // int someGlobal;
+ // void main() {
+ // someGlobal = 123;
+ //
+ // This is because MSL doesn't allow statically initialized globals.
+ bool forceDeferGlobalInitializers = getOutputType() == SH_MSL_METAL_OUTPUT;
+
+ if (enableNonConstantInitializers &&
+ !DeferGlobalInitializers(this, root, initializeLocalsAndGlobals, canUseLoopsToInitialize,
+ highPrecisionSupported, forceDeferGlobalInitializers,
+ &mSymbolTable))
+ {
+ return false;
+ }
+
+ // Create the function DAG and check there is no recursion
+ if (!initCallDag(root))
+ {
+ return false;
+ }
+
+ if (compileOptions.limitCallStackDepth && !checkCallDepth())
+ {
+ return false;
+ }
+
+ // Checks which functions are used and if "main" exists
+ mFunctionMetadata.clear();
+ mFunctionMetadata.resize(mCallDag.size());
+ if (!tagUsedFunctions())
+ {
+ return false;
+ }
+
+ if (!pruneUnusedFunctions(root))
+ {
+ return false;
+ }
+
+ if (IsSpecWithFunctionBodyNewScope(mShaderSpec, mShaderVersion))
+ {
+ if (!ReplaceShadowingVariables(this, root, &mSymbolTable))
+ {
+ return false;
+ }
+ }
+
+ if (mShaderVersion >= 310 && !ValidateVaryingLocations(root, &mDiagnostics, mShaderType))
+ {
+ return false;
+ }
+
+ // anglebug.com/7484: The ESSL spec has a bug with images as function arguments. The recommended
+ // workaround is to inline functions that accept image arguments.
+ if (mShaderVersion >= 310 && !MonomorphizeUnsupportedFunctions(
+ this, root, &mSymbolTable, compileOptions,
+ UnsupportedFunctionArgsBitSet{UnsupportedFunctionArgs::Image}))
+ {
+ return false;
+ }
+
+ if (mShaderVersion >= 300 && mShaderType == GL_FRAGMENT_SHADER &&
+ !ValidateOutputs(root, getExtensionBehavior(), mResources.MaxDrawBuffers, &mDiagnostics))
+ {
+ 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.clampIndirectArrayBounds)
+ {
+ if (!ClampIndirectIndices(this, root, &mSymbolTable))
+ {
+ return false;
+ }
+ }
+
+ if (compileOptions.initializeBuiltinsForInstancedMultiview &&
+ (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.rewriteDoWhileLoops)
+ {
+ if (!RewriteDoWhile(this, root, &mSymbolTable))
+ {
+ return false;
+ }
+ }
+
+ if (compileOptions.addAndTrueToLoopCondition)
+ {
+ if (!AddAndTrueToLoopCondition(this, root))
+ {
+ return false;
+ }
+ }
+
+ if (compileOptions.unfoldShortCircuit)
+ {
+ if (!UnfoldShortCircuitAST(this, root))
+ {
+ return false;
+ }
+ }
+
+ if (compileOptions.regenerateStructNames)
+ {
+ if (!RegenerateStructNames(this, root, &mSymbolTable))
+ {
+ return false;
+ }
+ }
+
+ if (mShaderType == GL_VERTEX_SHADER &&
+ IsExtensionEnabled(mExtensionBehavior, TExtension::ANGLE_multi_draw))
+ {
+ if (compileOptions.emulateGLDrawID)
+ {
+ if (!EmulateGLDrawID(this, root, &mSymbolTable, &mUniforms,
+ shouldCollectVariables(compileOptions)))
+ {
+ return false;
+ }
+ }
+ }
+
+ if (mShaderType == GL_VERTEX_SHADER &&
+ IsExtensionEnabled(mExtensionBehavior,
+ TExtension::ANGLE_base_vertex_base_instance_shader_builtin))
+ {
+ if (compileOptions.emulateGLBaseVertexBaseInstance)
+ {
+ if (!EmulateGLBaseVertexBaseInstance(this, root, &mSymbolTable, &mUniforms,
+ shouldCollectVariables(compileOptions),
+ compileOptions.addBaseVertexToVertexID))
+ {
+ 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.scalarizeVecAndMatConstructorArgs
+ ? 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, &getSymbolTable()))
+ {
+ 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.
+ GetGlobalPoolAllocator()->lock();
+ initBuiltInFunctionEmulator(&mBuiltInFunctionEmulator, compileOptions);
+ GetGlobalPoolAllocator()->unlock();
+ mBuiltInFunctionEmulator.markBuiltInFunctionsForEmulation(root);
+
+ if (compileOptions.scalarizeVecAndMatConstructorArgs)
+ {
+ if (!ScalarizeVecAndMatConstructorArgs(this, root, &mSymbolTable))
+ {
+ return false;
+ }
+ }
+
+ if (compileOptions.forceShaderPrecisionHighpToMediump)
+ {
+ 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.useUnusedStandardSharedBlocks)
+ {
+ if (!useAllMembersInUnusedStandardAndSharedBlocks(root))
+ {
+ return false;
+ }
+ }
+ if (compileOptions.enforcePackingRestrictions)
+ {
+ 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.initOutputVariables && mShaderType != GL_COMPUTE_SHADER;
+ needInitializeOutputVariables |=
+ compileOptions.initFragmentOutputVariables && 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.initGLPosition || mOutputType == SH_GLSL_COMPATIBILITY_OUTPUT))
+ {
+ if (!initializeGLPosition(root))
+ {
+ return false;
+ }
+ mGLPositionInitialized = true;
+ }
+
+ if (mShaderType == GL_VERTEX_SHADER && compileOptions.initGLPointSize)
+ {
+ sh::ShaderVariable var(GL_FLOAT);
+ var.name = "gl_PointSize";
+ if (!InitializeVariables(this, root, {var}, &mSymbolTable, mShaderVersion,
+ mExtensionBehavior, false, false))
+ {
+ return false;
+ }
+ }
+
+ // 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, forceDeferGlobalInitializers,
+ &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.clampPointSize)
+ {
+ if (!ClampPointSize(this, root, mResources.MaxPointSize, &getSymbolTable()))
+ {
+ return false;
+ }
+ }
+
+ if (getShaderType() == GL_FRAGMENT_SHADER && compileOptions.clampFragDepth)
+ {
+ if (!ClampFragDepth(this, root, &getSymbolTable()))
+ {
+ return false;
+ }
+ }
+
+ if (compileOptions.rewriteRepeatedAssignToSwizzled)
+ {
+ if (!sh::RewriteRepeatedAssignToSwizzled(this, root))
+ {
+ return false;
+ }
+ }
+
+ if (compileOptions.removeDynamicIndexingOfSwizzledVector)
+ {
+ if (!sh::RemoveDynamicIndexingOfSwizzledVector(this, root, &getSymbolTable(), nullptr))
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool TCompiler::postParseChecks(const TParseContext &parseContext)
+{
+ std::stringstream errorMessage;
+
+ if (parseContext.getTreeRoot() == nullptr)
+ {
+ errorMessage << "Shader parsing failed (mTreeRoot == nullptr)";
+ }
+
+ for (TType *type : parseContext.getDeferredArrayTypesToSize())
+ {
+ errorMessage << "Unsized global array type: " << type->getBasicString();
+ }
+
+ if (!errorMessage.str().empty())
+ {
+ mDiagnostics.globalError(errorMessage.str().c_str());
+ return false;
+ }
+
+ return true;
+}
+
+bool TCompiler::compile(const char *const shaderStrings[],
+ size_t numStrings,
+ const ShCompileOptions &compileOptionsIn)
+{
+#if defined(ANGLE_FUZZER_CORPUS_OUTPUT_DIR)
+ DumpFuzzerCase(shaderStrings, numStrings, mShaderType, mShaderSpec, mOutputType,
+ compileOptionsIn);
+#endif // defined(ANGLE_FUZZER_CORPUS_OUTPUT_DIR)
+
+ 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.flattenPragmaSTDGLInvariantAll = true;
+ }
+
+ TScopedPoolAllocator scopedAlloc(&allocator);
+ TIntermBlock *root = compileTreeImpl(shaderStrings, numStrings, compileOptions);
+
+ if (root)
+ {
+ if (compileOptions.intermediateTree)
+ {
+ OutputTree(root, mInfoSink.info);
+ }
+
+ if (compileOptions.objectCode)
+ {
+ PerformanceDiagnostics perfDiagnostics(&mDiagnostics);
+ if (!translate(root, compileOptions, &perfDiagnostics))
+ {
+ return false;
+ }
+ }
+
+ if (mShaderType == GL_VERTEX_SHADER)
+ {
+ bool lookForDrawID =
+ IsExtensionEnabled(mExtensionBehavior, TExtension::ANGLE_multi_draw) &&
+ compileOptions.emulateGLDrawID;
+ bool lookForBaseVertexBaseInstance =
+ IsExtensionEnabled(mExtensionBehavior,
+ TExtension::ANGLE_base_vertex_base_instance_shader_builtin) &&
+ compileOptions.emulateGLBaseVertexBaseInstance;
+
+ 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<std::ostringstream>();
+
+ // 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_primitive_bounding_box:" << mResources.EXT_primitive_bounding_box
+ << ":OES_primitive_bounding_box:" << mResources.OES_primitive_bounding_box
+ << ":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
+ << ":ANGLE_multi_draw:" << mResources.ANGLE_multi_draw
+ << ":ANGLE_base_vertex_base_instance_shader_builtin:" << mResources.ANGLE_base_vertex_base_instance_shader_builtin
+ << ":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());
+}
+
+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<int> 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<std::stringstream>();
+ errorStream << "Call stack too deep (larger than " << mResources.MaxCallStackDepth
+ << ") with the following call chain: "
+ << record.node->getFunction()->name();
+
+ int currentFunction = static_cast<int>(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);
+ }
+}
+
+bool TCompiler::pruneUnusedFunctions(TIntermBlock *root)
+{
+ TIntermSequence *sequence = root->getSequence();
+
+ size_t writeIndex = 0;
+ for (size_t readIndex = 0; readIndex < sequence->size(); ++readIndex)
+ {
+ TIntermNode *node = sequence->at(readIndex);
+
+ // Keep anything that's not unused.
+ const TFunction *function = nullptr;
+ const bool shouldPrune =
+ IsTopLevelNodeUnusedFunction(mCallDag, mFunctionMetadata, node, &function);
+ if (!shouldPrune)
+ {
+ (*sequence)[writeIndex++] = node;
+ continue;
+ }
+
+ // If a function is unused, it may have a struct declaration in its return value which
+ // shouldn't be pruned. In that case, replace the function definition with the struct
+ // definition.
+ ASSERT(function != nullptr);
+ const TType &returnType = function->getReturnType();
+ if (!returnType.isStructSpecifier())
+ {
+ continue;
+ }
+
+ TVariable *structVariable =
+ new TVariable(&mSymbolTable, kEmptyImmutableString, &returnType, SymbolType::Empty);
+ TIntermSymbol *structSymbol = new TIntermSymbol(structVariable);
+ TIntermDeclaration *structDeclaration = new TIntermDeclaration;
+ structDeclaration->appendDeclarator(structSymbol);
+
+ structSymbol->setLine(node->getLine());
+ structDeclaration->setLine(node->getLine());
+
+ (*sequence)[writeIndex++] = structDeclaration;
+ }
+
+ sequence->resize(writeIndex);
+
+ return validateAST(root);
+}
+
+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(const ShCompileOptions &compileOptions)
+{
+ return compileOptions.variables;
+}
+
+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)
+ {
+ // in-out variables represent the context of the framebuffer
+ // when the draw call starts, so they have to be considered
+ // as already initialized.
+ if (!var.isFragmentInOut)
+ {
+ 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