Edit

kc3-lang/angle/src/compiler/translator/Compiler.cpp

Branch :

  • Show log

    Commit

  • Author : Olli Etuaho
    Date : 2016-12-16 12:01:18
    Hash : 77ba408a
    Message : Unify Diagnostics interface Use the same kind of interface for reporting preprocessor errors as for reporting regular compiler errors, and make global errors like having too many uniforms also go through Diagnostics. Also don't create std::string objects unnecessarily. Includes cleanups of some dead code related to reporting errors. BUG=angleproject:1670 TEST=angle_unittests Change-Id: I3ee794d32ddeec1826bdf1b76b558f35259f82c0 Reviewed-on: https://chromium-review.googlesource.com/421527 Reviewed-by: Corentin Wallez <cwallez@chromium.org> Reviewed-by: Jamie Madill <jmadill@chromium.org> Commit-Queue: Olli Etuaho <oetuaho@nvidia.com>

  • src/compiler/translator/Compiler.cpp
  • //
    // Copyright (c) 2002-2014 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/AddAndTrueToLoopCondition.h"
    #include "compiler/translator/Cache.h"
    #include "compiler/translator/CallDAG.h"
    #include "compiler/translator/DeferGlobalInitializers.h"
    #include "compiler/translator/EmulateGLFragColorBroadcast.h"
    #include "compiler/translator/EmulatePrecision.h"
    #include "compiler/translator/Initialize.h"
    #include "compiler/translator/InitializeParseContext.h"
    #include "compiler/translator/InitializeVariables.h"
    #include "compiler/translator/ParseContext.h"
    #include "compiler/translator/PruneEmptyDeclarations.h"
    #include "compiler/translator/RegenerateStructNames.h"
    #include "compiler/translator/RemoveInvariantDeclaration.h"
    #include "compiler/translator/RemovePow.h"
    #include "compiler/translator/RewriteDoWhile.h"
    #include "compiler/translator/ScalarizeVecAndMatConstructorArgs.h"
    #include "compiler/translator/UnfoldShortCircuitAST.h"
    #include "compiler/translator/UseInterfaceBlockFields.h"
    #include "compiler/translator/ValidateLimitations.h"
    #include "compiler/translator/ValidateMaxParameters.h"
    #include "compiler/translator/ValidateOutputs.h"
    #include "compiler/translator/VariablePacker.h"
    #include "third_party/compiler/ArrayBoundsClamper.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;
        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 IsWebGLBasedSpec(ShShaderSpec spec)
    {
        return (spec == SH_WEBGL_SPEC || spec == SH_WEBGL2_SPEC || spec == SH_WEBGL3_SPEC);
    }
    
    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 ((compileOptions & SH_DONT_REMOVE_INVARIANT_FOR_FRAGMENT_INPUT) == 0 &&
            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;
        }
    }
    
    namespace
    {
    
    class TScopedPoolAllocator
    {
      public:
        TScopedPoolAllocator(TPoolAllocator *allocator) : mAllocator(allocator)
        {
            mAllocator->push();
            SetGlobalPoolAllocator(mAllocator);
        }
        ~TScopedPoolAllocator()
        {
            SetGlobalPoolAllocator(NULL);
            mAllocator->pop();
        }
    
      private:
        TPoolAllocator *mAllocator;
    };
    
    class TScopedSymbolTableLevel
    {
      public:
        TScopedSymbolTableLevel(TSymbolTable *table) : mTable(table)
        {
            ASSERT(mTable->atBuiltInLevel());
            mTable->push();
        }
        ~TScopedSymbolTableLevel()
        {
            while (!mTable->atBuiltInLevel())
                mTable->pop();
        }
    
      private:
        TSymbolTable *mTable;
    };
    
    int MapSpecToShaderVersion(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;
            default:
                UNREACHABLE();
                return 0;
        }
    }
    
    }  // namespace
    
    TShHandleBase::TShHandleBase()
    {
        allocator.push();
        SetGlobalPoolAllocator(&allocator);
    }
    
    TShHandleBase::~TShHandleBase()
    {
        SetGlobalPoolAllocator(NULL);
        allocator.popAll();
    }
    
    TCompiler::TCompiler(sh::GLenum type, ShShaderSpec spec, ShShaderOutput output)
        : variablesCollected(false),
          shaderType(type),
          shaderSpec(spec),
          outputType(output),
          maxUniformVectors(0),
          maxExpressionComplexity(0),
          maxCallStackDepth(0),
          maxFunctionParameters(0),
          fragmentPrecisionHigh(false),
          clampingStrategy(SH_CLAMP_WITH_CLAMP_INTRINSIC),
          builtInFunctionEmulator(),
          mDiagnostics(infoSink.info),
          mSourcePath(NULL),
          mComputeShaderLocalSizeDeclared(false),
          mTemporaryIndex(0)
    {
        mComputeShaderLocalSize.fill(1);
    }
    
    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(shaderSpec) && shaderVersion == 100) ||
               (compileOptions & SH_VALIDATE_LOOP_INDEXING);
    }
    
    bool TCompiler::Init(const ShBuiltInResources &resources)
    {
        shaderVersion     = 100;
        maxUniformVectors = (shaderType == GL_VERTEX_SHADER) ? resources.MaxVertexUniformVectors
                                                             : resources.MaxFragmentUniformVectors;
        maxExpressionComplexity = resources.MaxExpressionComplexity;
        maxCallStackDepth       = resources.MaxCallStackDepth;
        maxFunctionParameters   = resources.MaxFunctionParameters;
    
        SetGlobalPoolAllocator(&allocator);
    
        // Generate built-in symbol table.
        if (!InitBuiltInSymbolTable(resources))
            return false;
        InitExtensionBehavior(resources, extensionBehavior);
        fragmentPrecisionHigh = resources.FragmentPrecisionHigh == 1;
    
        arrayBoundsClamper.SetClampingStrategy(resources.ArrayIndexClampingStrategy);
        clampingStrategy = resources.ArrayIndexClampingStrategy;
    
        hashFunction = resources.HashFunction;
    
        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)
    {
        clearResults();
    
        ASSERT(numStrings > 0);
        ASSERT(GetGlobalPoolAllocator());
    
        // Reset the extension behavior for each compilation unit.
        ResetExtensionBehavior(extensionBehavior);
    
        // First string is path of source file if flag is set. The actual source follows.
        size_t firstSource = 0;
        if (compileOptions & SH_SOURCE_PATH)
        {
            mSourcePath = shaderStrings[0];
            ++firstSource;
        }
    
        TParseContext parseContext(symbolTable, extensionBehavior, shaderType, shaderSpec,
                                   compileOptions, true, &mDiagnostics, getResources());
    
        parseContext.setFragmentPrecisionHighOnESSL1(fragmentPrecisionHigh);
        SetGlobalParseContext(&parseContext);
    
        // We preserve symbols at the built-in level from compile-to-compile.
        // Start pushing the user-defined symbols at global level.
        TScopedSymbolTableLevel scopedSymbolLevel(&symbolTable);
    
        // Parse shader.
        bool success = (PaParseStrings(numStrings - firstSource, &shaderStrings[firstSource], nullptr,
                                       &parseContext) == 0) &&
                       (parseContext.getTreeRoot() != nullptr);
    
        shaderVersion = parseContext.getShaderVersion();
        if (success && MapSpecToShaderVersion(shaderSpec) < shaderVersion)
        {
            mDiagnostics.globalError("unsupported shader version");
            success = false;
        }
    
        TIntermBlock *root = nullptr;
    
        if (success)
        {
            mPragma = parseContext.pragma();
            symbolTable.setGlobalInvariant(mPragma.stdgl.invariantAll);
    
            mComputeShaderLocalSizeDeclared = parseContext.isComputeShaderLocalSizeDeclared();
            mComputeShaderLocalSize         = parseContext.getComputeShaderLocalSize();
    
            root = parseContext.getTreeRoot();
    
            // Highp might have been auto-enabled based on shader version
            fragmentPrecisionHigh = parseContext.getFragmentPrecisionHigh();
    
            // Disallow expressions deemed too complex.
            if (success && (compileOptions & SH_LIMIT_EXPRESSION_COMPLEXITY))
                success = limitExpressionComplexity(root);
    
            // Create the function DAG and check there is no recursion
            if (success)
                success = initCallDag(root);
    
            if (success && (compileOptions & SH_LIMIT_CALL_STACK_DEPTH))
                success = checkCallDepth();
    
            // Checks which functions are used and if "main" exists
            if (success)
            {
                functionMetadata.clear();
                functionMetadata.resize(mCallDag.size());
                success = tagUsedFunctions();
            }
    
            if (success && !(compileOptions & SH_DONT_PRUNE_UNUSED_FUNCTIONS))
                success = pruneUnusedFunctions(root);
    
            // Prune empty declarations to work around driver bugs and to keep declaration output
            // simple.
            if (success)
                PruneEmptyDeclarations(root);
    
            if (success && shaderVersion == 300 && shaderType == GL_FRAGMENT_SHADER)
                success = validateOutputs(root);
    
            if (success && shouldRunLoopAndIndexingValidation(compileOptions))
                success = validateLimitations(root);
    
            // Fail compilation if precision emulation not supported.
            if (success && getResources().WEBGL_debug_shader_precision &&
                getPragma().debugShaderPrecision)
            {
                if (!EmulatePrecision::SupportedInLanguage(outputType))
                {
                    mDiagnostics.globalError("Precision emulation not supported for this output type.");
                    success = false;
                }
            }
    
            // Built-in function emulation needs to happen after validateLimitations pass.
            if (success)
            {
                // TODO(jmadill): Remove global pool allocator.
                GetGlobalPoolAllocator()->lock();
                initBuiltInFunctionEmulator(&builtInFunctionEmulator, compileOptions);
                GetGlobalPoolAllocator()->unlock();
                builtInFunctionEmulator.MarkBuiltInFunctionsForEmulation(root);
            }
    
            // Clamping uniform array bounds needs to happen after validateLimitations pass.
            if (success && (compileOptions & SH_CLAMP_INDIRECT_ARRAY_BOUNDS))
                arrayBoundsClamper.MarkIndirectArrayBoundsForClamping(root);
    
            // gl_Position is always written in compatibility output mode
            if (success && shaderType == GL_VERTEX_SHADER &&
                ((compileOptions & SH_INIT_GL_POSITION) ||
                 (outputType == SH_GLSL_COMPATIBILITY_OUTPUT)))
                initializeGLPosition(root);
    
            if (success && RemoveInvariant(shaderType, shaderVersion, outputType, compileOptions))
                sh::RemoveInvariantDeclaration(root);
    
            // This pass might emit short circuits so keep it before the short circuit unfolding
            if (success && (compileOptions & SH_REWRITE_DO_WHILE_LOOPS))
                RewriteDoWhile(root, getTemporaryIndex());
    
            if (success && (compileOptions & SH_ADD_AND_TRUE_TO_LOOP_CONDITION))
                sh::AddAndTrueToLoopCondition(root);
    
            if (success && (compileOptions & SH_UNFOLD_SHORT_CIRCUIT))
            {
                UnfoldShortCircuitAST unfoldShortCircuit;
                root->traverse(&unfoldShortCircuit);
                unfoldShortCircuit.updateTree();
            }
    
            if (success && (compileOptions & SH_REMOVE_POW_WITH_CONSTANT_EXPONENT))
            {
                RemovePow(root);
            }
    
            if (success && shouldCollectVariables(compileOptions))
            {
                collectVariables(root);
                if (compileOptions & SH_USE_UNUSED_STANDARD_SHARED_BLOCKS)
                {
                    useAllMembersInUnusedStandardAndSharedBlocks(root);
                }
                if (compileOptions & SH_ENFORCE_PACKING_RESTRICTIONS)
                {
                    success = enforcePackingRestrictions();
                    if (!success)
                    {
                        mDiagnostics.globalError("too many uniforms");
                    }
                }
                if (success && (compileOptions & SH_INIT_OUTPUT_VARIABLES))
                {
                    initializeOutputVariables(root);
                }
            }
    
            if (success && (compileOptions & SH_SCALARIZE_VEC_AND_MAT_CONSTRUCTOR_ARGS))
            {
                ScalarizeVecAndMatConstructorArgs(root, shaderType, fragmentPrecisionHigh,
                                                  &mTemporaryIndex);
            }
    
            if (success && (compileOptions & SH_REGENERATE_STRUCT_NAMES))
            {
                RegenerateStructNames gen(symbolTable, shaderVersion);
                root->traverse(&gen);
            }
    
            if (success && shaderType == GL_FRAGMENT_SHADER && shaderVersion == 100 &&
                compileResources.EXT_draw_buffers && compileResources.MaxDrawBuffers > 1 &&
                IsExtensionEnabled(extensionBehavior, "GL_EXT_draw_buffers"))
            {
                EmulateGLFragColorBroadcast(root, compileResources.MaxDrawBuffers, &outputVariables);
            }
    
            if (success)
            {
                DeferGlobalInitializers(root);
            }
        }
    
        SetGlobalParseContext(NULL);
        if (success)
            return root;
    
        return NULL;
    }
    
    bool TCompiler::compile(const char *const shaderStrings[],
                            size_t numStrings,
                            ShCompileOptions compileOptionsIn)
    {
    #if defined(ANGLE_ENABLE_FUZZER_CORPUS_OUTPUT)
        DumpFuzzerCase(shaderStrings, numStrings, shaderType, shaderSpec, outputType, 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)
                TIntermediate::outputTree(root, infoSink.info);
    
            if (compileOptions & SH_OBJECT_CODE)
                translate(root, compileOptions);
    
            // 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;
        }
    
        compileResources = resources;
        setResourceString();
    
        assert(symbolTable.isEmpty());
        symbolTable.push();  // COMMON_BUILTINS
        symbolTable.push();  // ESSL1_BUILTINS
        symbolTable.push();  // ESSL3_BUILTINS
        symbolTable.push();  // ESSL3_1_BUILTINS
    
        TPublicType integer;
        integer.initializeBasicType(EbtInt);
    
        TPublicType floatingPoint;
        floatingPoint.initializeBasicType(EbtFloat);
    
        switch (shaderType)
        {
            case GL_FRAGMENT_SHADER:
                symbolTable.setDefaultPrecision(integer, EbpMedium);
                break;
            case GL_VERTEX_SHADER:
                symbolTable.setDefaultPrecision(integer, EbpHigh);
                symbolTable.setDefaultPrecision(floatingPoint, EbpHigh);
                break;
            case GL_COMPUTE_SHADER:
                symbolTable.setDefaultPrecision(integer, EbpHigh);
                symbolTable.setDefaultPrecision(floatingPoint, EbpHigh);
                break;
            default:
                assert(false && "Language not supported");
        }
        // Set defaults for sampler types that have default precision, even those that are
        // only available if an extension exists.
        // New sampler types in ESSL3 don't have default precision. ESSL1 types do.
        initSamplerDefaultPrecision(EbtSampler2D);
        initSamplerDefaultPrecision(EbtSamplerCube);
        // SamplerExternalOES is specified in the extension to have default precision.
        initSamplerDefaultPrecision(EbtSamplerExternalOES);
        // It isn't specified whether Sampler2DRect has default precision.
        initSamplerDefaultPrecision(EbtSampler2DRect);
    
        InsertBuiltInFunctions(shaderType, shaderSpec, resources, symbolTable);
    
        IdentifyBuiltIns(shaderType, shaderSpec, resources, symbolTable);
    
        return true;
    }
    
    void TCompiler::initSamplerDefaultPrecision(TBasicType samplerType)
    {
        ASSERT(samplerType > EbtGuardSamplerBegin && samplerType < EbtGuardSamplerEnd);
        TPublicType sampler;
        sampler.initializeBasicType(samplerType);
        symbolTable.setDefaultPrecision(sampler, EbpLow);
    }
    
    void TCompiler::setResourceString()
    {
        std::ostringstream strstream;
    
        // clang-format off
        strstream << ":MaxVertexAttribs:" << compileResources.MaxVertexAttribs
            << ":MaxVertexUniformVectors:" << compileResources.MaxVertexUniformVectors
            << ":MaxVaryingVectors:" << compileResources.MaxVaryingVectors
            << ":MaxVertexTextureImageUnits:" << compileResources.MaxVertexTextureImageUnits
            << ":MaxCombinedTextureImageUnits:" << compileResources.MaxCombinedTextureImageUnits
            << ":MaxTextureImageUnits:" << compileResources.MaxTextureImageUnits
            << ":MaxFragmentUniformVectors:" << compileResources.MaxFragmentUniformVectors
            << ":MaxDrawBuffers:" << compileResources.MaxDrawBuffers
            << ":OES_standard_derivatives:" << compileResources.OES_standard_derivatives
            << ":OES_EGL_image_external:" << compileResources.OES_EGL_image_external
            << ":OES_EGL_image_external_essl3:" << compileResources.OES_EGL_image_external_essl3
            << ":NV_EGL_stream_consumer_external:" << compileResources.NV_EGL_stream_consumer_external
            << ":ARB_texture_rectangle:" << compileResources.ARB_texture_rectangle
            << ":EXT_draw_buffers:" << compileResources.EXT_draw_buffers
            << ":FragmentPrecisionHigh:" << compileResources.FragmentPrecisionHigh
            << ":MaxExpressionComplexity:" << compileResources.MaxExpressionComplexity
            << ":MaxCallStackDepth:" << compileResources.MaxCallStackDepth
            << ":MaxFunctionParameters:" << compileResources.MaxFunctionParameters
            << ":EXT_blend_func_extended:" << compileResources.EXT_blend_func_extended
            << ":EXT_frag_depth:" << compileResources.EXT_frag_depth
            << ":EXT_shader_texture_lod:" << compileResources.EXT_shader_texture_lod
            << ":EXT_shader_framebuffer_fetch:" << compileResources.EXT_shader_framebuffer_fetch
            << ":NV_shader_framebuffer_fetch:" << compileResources.NV_shader_framebuffer_fetch
            << ":ARM_shader_framebuffer_fetch:" << compileResources.ARM_shader_framebuffer_fetch
            << ":MaxVertexOutputVectors:" << compileResources.MaxVertexOutputVectors
            << ":MaxFragmentInputVectors:" << compileResources.MaxFragmentInputVectors
            << ":MinProgramTexelOffset:" << compileResources.MinProgramTexelOffset
            << ":MaxProgramTexelOffset:" << compileResources.MaxProgramTexelOffset
            << ":MaxDualSourceDrawBuffers:" << compileResources.MaxDualSourceDrawBuffers
            << ":NV_draw_buffers:" << compileResources.NV_draw_buffers
            << ":WEBGL_debug_shader_precision:" << compileResources.WEBGL_debug_shader_precision
            << ":MaxImageUnits:" << compileResources.MaxImageUnits
            << ":MaxVertexImageUniforms:" << compileResources.MaxVertexImageUniforms
            << ":MaxFragmentImageUniforms:" << compileResources.MaxFragmentImageUniforms
            << ":MaxComputeImageUniforms:" << compileResources.MaxComputeImageUniforms
            << ":MaxCombinedImageUniforms:" << compileResources.MaxCombinedImageUniforms
            << ":MaxCombinedShaderOutputResources:" << compileResources.MaxCombinedShaderOutputResources
            << ":MaxComputeWorkGroupCountX:" << compileResources.MaxComputeWorkGroupCount[0]
            << ":MaxComputeWorkGroupCountY:" << compileResources.MaxComputeWorkGroupCount[1]
            << ":MaxComputeWorkGroupCountZ:" << compileResources.MaxComputeWorkGroupCount[2]
            << ":MaxComputeWorkGroupSizeX:" << compileResources.MaxComputeWorkGroupSize[0]
            << ":MaxComputeWorkGroupSizeY:" << compileResources.MaxComputeWorkGroupSize[1]
            << ":MaxComputeWorkGroupSizeZ:" << compileResources.MaxComputeWorkGroupSize[2]
            << ":MaxComputeUniformComponents:" << compileResources.MaxComputeUniformComponents
            << ":MaxComputeTextureImageUnits:" << compileResources.MaxComputeTextureImageUnits
            << ":MaxComputeAtomicCounters:" << compileResources.MaxComputeAtomicCounters
            << ":MaxComputeAtomicCounterBuffers:" << compileResources.MaxComputeAtomicCounterBuffers
            << ":MaxVertexAtomicCounters:" << compileResources.MaxVertexAtomicCounters
            << ":MaxFragmentAtomicCounters:" << compileResources.MaxFragmentAtomicCounters
            << ":MaxCombinedAtomicCounters:" << compileResources.MaxCombinedAtomicCounters
            << ":MaxAtomicCounterBindings:" << compileResources.MaxAtomicCounterBindings
            << ":MaxVertexAtomicCounterBuffers:" << compileResources.MaxVertexAtomicCounterBuffers
            << ":MaxFragmentAtomicCounterBuffers:" << compileResources.MaxFragmentAtomicCounterBuffers
            << ":MaxCombinedAtomicCounterBuffers:" << compileResources.MaxCombinedAtomicCounterBuffers
            << ":MaxAtomicCounterBufferSize:" << compileResources.MaxAtomicCounterBufferSize;
        // clang-format on
    
        builtInResourcesString = strstream.str();
    }
    
    void TCompiler::clearResults()
    {
        arrayBoundsClamper.Cleanup();
        infoSink.info.erase();
        infoSink.obj.erase();
        infoSink.debug.erase();
        mDiagnostics.resetErrorCount();
    
        attributes.clear();
        outputVariables.clear();
        uniforms.clear();
        expandedUniforms.clear();
        varyings.clear();
        interfaceBlocks.clear();
        variablesCollected = false;
    
        builtInFunctionEmulator.Cleanup();
    
        nameMap.clear();
    
        mSourcePath     = NULL;
        mTemporaryIndex = 0;
    }
    
    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;
            auto &record = mCallDag.getRecordFromIndex(i);
    
            for (auto &calleeIndex : record.callees)
            {
                depth = std::max(depth, depths[calleeIndex] + 1);
            }
    
            depths[i] = depth;
    
            if (depth >= maxCallStackDepth)
            {
                // Trace back the function chain to have a meaningful info log.
                std::stringstream errorStream;
                errorStream << "Call stack too deep (larger than " << maxCallStackDepth
                            << ") with the following call chain: " << record.name;
    
                int currentFunction = static_cast<int>(i);
                int currentDepth    = depth;
    
                while (currentFunction != -1)
                {
                    errorStream << " -> " << mCallDag.getRecordFromIndex(currentFunction).name;
    
                    int nextFunction = -1;
                    for (auto &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).name == "main(")
            {
                internalTagUsedFunction(i);
                return true;
            }
        }
    
        mDiagnostics.globalError("Missing main()");
        return false;
    }
    
    void TCompiler::internalTagUsedFunction(size_t index)
    {
        if (functionMetadata[index].used)
        {
            return;
        }
    
        functionMetadata[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<FunctionMetadata> *metadatas)
            : mCallDag(callDag), mMetadatas(metadatas)
        {
        }
    
        bool operator()(TIntermNode *node)
        {
            const TIntermAggregate *asAggregate         = node->getAsAggregate();
            const TIntermFunctionDefinition *asFunction = node->getAsFunctionDefinition();
    
            const TFunctionSymbolInfo *functionInfo = nullptr;
    
            if (asFunction)
            {
                functionInfo = asFunction->getFunctionSymbolInfo();
            }
            else if (asAggregate)
            {
                if (asAggregate->getOp() == EOpPrototype)
                {
                    functionInfo = asAggregate->getFunctionSymbolInfo();
                }
            }
            if (functionInfo == nullptr)
            {
                return false;
            }
    
            size_t callDagIndex = mCallDag->findIndex(functionInfo);
            if (callDagIndex == CallDAG::InvalidIndex)
            {
                // This happens only for unimplemented prototypes which are thus unused
                ASSERT(asAggregate && asAggregate->getOp() == EOpPrototype);
                return true;
            }
    
            ASSERT(callDagIndex < mMetadatas->size());
            return !(*mMetadatas)[callDagIndex].used;
        }
    
      private:
        const CallDAG *mCallDag;
        const std::vector<FunctionMetadata> *mMetadatas;
    };
    
    bool TCompiler::pruneUnusedFunctions(TIntermBlock *root)
    {
        UnusedPredicate isUnused(&mCallDag, &functionMetadata);
        TIntermSequence *sequence = root->getSequence();
    
        if (!sequence->empty())
        {
            sequence->erase(std::remove_if(sequence->begin(), sequence->end(), isUnused),
                            sequence->end());
        }
    
        return true;
    }
    
    bool TCompiler::validateOutputs(TIntermNode *root)
    {
        ValidateOutputs validateOutputs(getExtensionBehavior(), compileResources.MaxDrawBuffers);
        root->traverse(&validateOutputs);
        validateOutputs.validate(&mDiagnostics);
        return (mDiagnostics.numErrors() == 0);
    }
    
    bool TCompiler::validateLimitations(TIntermNode *root)
    {
        ValidateLimitations validate(shaderType, &mDiagnostics);
        root->traverse(&validate);
        return mDiagnostics.numErrors() == 0;
    }
    
    bool TCompiler::limitExpressionComplexity(TIntermNode *root)
    {
        TMaxDepthTraverser traverser(maxExpressionComplexity + 1);
        root->traverse(&traverser);
    
        if (traverser.getMaxDepth() > maxExpressionComplexity)
        {
            mDiagnostics.globalError("Expression too complex.");
            return false;
        }
    
        if (!ValidateMaxParameters::validate(root, maxFunctionParameters))
        {
            mDiagnostics.globalError("Function has too many parameters.");
            return false;
        }
    
        return true;
    }
    
    void TCompiler::collectVariables(TIntermNode *root)
    {
        if (!variablesCollected)
        {
            sh::CollectVariables collect(&attributes, &outputVariables, &uniforms, &varyings,
                                         &interfaceBlocks, hashFunction, symbolTable,
                                         extensionBehavior);
            root->traverse(&collect);
    
            // This is for enforcePackingRestriction().
            sh::ExpandUniforms(uniforms, &expandedUniforms);
            variablesCollected = true;
        }
    }
    
    bool TCompiler::shouldCollectVariables(ShCompileOptions compileOptions)
    {
        return (compileOptions & SH_VARIABLES) != 0;
    }
    
    bool TCompiler::wereVariablesCollected() const
    {
        return variablesCollected;
    }
    
    bool TCompiler::enforcePackingRestrictions()
    {
        VariablePacker packer;
        return packer.CheckVariablesWithinPackingLimits(maxUniformVectors, expandedUniforms);
    }
    
    void TCompiler::initializeGLPosition(TIntermNode *root)
    {
        InitVariableList list;
        sh::ShaderVariable var(GL_FLOAT_VEC4, 0);
        var.name = "gl_Position";
        list.push_back(var);
        InitializeVariables(root, list, symbolTable);
    }
    
    void TCompiler::useAllMembersInUnusedStandardAndSharedBlocks(TIntermNode *root)
    {
        sh::InterfaceBlockList list;
    
        for (auto block : interfaceBlocks)
        {
            if (!block.staticUse &&
                (block.layout == sh::BLOCKLAYOUT_STANDARD || block.layout == sh::BLOCKLAYOUT_SHARED))
            {
                list.push_back(block);
            }
        }
    
        sh::UseInterfaceBlockFields(root, list, symbolTable);
    }
    
    void TCompiler::initializeOutputVariables(TIntermNode *root)
    {
        InitVariableList list;
        if (shaderType == GL_VERTEX_SHADER)
        {
            for (auto var : varyings)
            {
                list.push_back(var);
            }
        }
        else
        {
            ASSERT(shaderType == GL_FRAGMENT_SHADER);
            for (auto var : outputVariables)
            {
                list.push_back(var);
            }
        }
        InitializeVariables(root, list, symbolTable);
    }
    
    const TExtensionBehavior &TCompiler::getExtensionBehavior() const
    {
        return extensionBehavior;
    }
    
    const char *TCompiler::getSourcePath() const
    {
        return mSourcePath;
    }
    
    const ShBuiltInResources &TCompiler::getResources() const
    {
        return compileResources;
    }
    
    const ArrayBoundsClamper &TCompiler::getArrayBoundsClamper() const
    {
        return arrayBoundsClamper;
    }
    
    ShArrayIndexClampingStrategy TCompiler::getArrayIndexClampingStrategy() const
    {
        return clampingStrategy;
    }
    
    const BuiltInFunctionEmulator &TCompiler::getBuiltInFunctionEmulator() const
    {
        return builtInFunctionEmulator;
    }
    
    void TCompiler::writePragma(ShCompileOptions compileOptions)
    {
        if (!(compileOptions & SH_FLATTEN_PRAGMA_STDGL_INVARIANT_ALL))
        {
            TInfoSinkBase &sink = infoSink.obj;
            if (mPragma.stdgl.invariantAll)
                sink << "#pragma STDGL invariant(all)\n";
        }
    }
    
    bool TCompiler::isVaryingDefined(const char *varyingName)
    {
        ASSERT(variablesCollected);
        for (size_t ii = 0; ii < varyings.size(); ++ii)
        {
            if (varyings[ii].name == varyingName)
            {
                return true;
            }
        }
    
        return false;
    }
    
    }  // namespace sh