Edit

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

Branch :

  • Show log

    Commit

  • Author : Shahbaz Youssefi
    Date : 2021-07-29 11:19:33
    Hash : ebe943e2
    Message : Vulkan: SPIR-V Gen: Fix gl_Clip/CullDistance These built-ins can be redeclared in the shader. The translator took these redeclarations (and gl_LastFragData) as UserDefined symbols. There were a number of hacks such as in name generation and CollectVariables, to special-case these redeclarations. This change instead makes sure that these variables continue to be considered built-ins with the appropriate qualifiers. A number of fixes are made accordingly: - Hacks are removed. - In fragment shaders, ANGLEClipDistance was initialized with gl_ClipDistance for further use by the shader. The code generation however mistakenly produced `gl_ClipDistance[0] = ANGLEClipDistance[0];`, which caused compilation failures by glslang, but passed the tests accidentally because they expected link failures (see next item). - CollectVariables is fixed to correctly collect gl_Clip/CullDistance in fragment shaders; previously they were collected as output varyings, and therefore the aforementioned link error was not produced in the tests after the compilation error was fixed. Additional fixes: - The transformation of gl_Clip/CullDistance was always ever done on one of them due to misplaced breaks in the loop that detected their presence. - The transformation of gl_CullDistance was skipped when it was not redeclared. Validation is added to make sure these built-ins always have the correct qualifier even when redeclared. SPIR-V gen support for these variables have been fixed as well. Bug: angleproject:4889 Change-Id: Ic8bf2c508357035cb937ef41a28ae22ffc533ebe Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/3059921 Reviewed-by: Tim Van Patten <timvp@google.com> Reviewed-by: Jamie Madill <jmadill@chromium.org> Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org>

  • src/compiler/translator/SymbolTable.cpp
  • //
    // 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.
    //
    // Symbol table for parsing. The design principles and most of the functionality are documented in
    // the header file.
    //
    
    #if defined(_MSC_VER)
    #    pragma warning(disable : 4718)
    #endif
    
    #include "compiler/translator/SymbolTable.h"
    
    #include "angle_gl.h"
    #include "compiler/translator/ImmutableString.h"
    #include "compiler/translator/IntermNode.h"
    #include "compiler/translator/StaticType.h"
    #include "compiler/translator/util.h"
    
    namespace sh
    {
    namespace
    {
    bool CheckShaderType(Shader expected, GLenum actual)
    {
        switch (expected)
        {
            case Shader::ALL:
                return true;
            case Shader::FRAGMENT:
                return actual == GL_FRAGMENT_SHADER;
            case Shader::VERTEX:
                return actual == GL_VERTEX_SHADER;
            case Shader::COMPUTE:
                return actual == GL_COMPUTE_SHADER;
            case Shader::GEOMETRY:
                return actual == GL_GEOMETRY_SHADER;
            case Shader::GEOMETRY_EXT:
                return actual == GL_GEOMETRY_SHADER_EXT;
            case Shader::TESS_CONTROL_EXT:
                return actual == GL_TESS_CONTROL_SHADER_EXT;
            case Shader::TESS_EVALUATION_EXT:
                return actual == GL_TESS_EVALUATION_SHADER_EXT;
            case Shader::NOT_COMPUTE:
                return actual != GL_COMPUTE_SHADER;
            default:
                UNREACHABLE();
                return false;
        }
    }
    
    bool CheckExtension(uint32_t extensionIndex, const ShBuiltInResources &resources)
    {
        const int *resourcePtr = reinterpret_cast<const int *>(&resources);
        return resourcePtr[extensionIndex] > 0;
    }
    }  // namespace
    
    class TSymbolTable::TSymbolTableLevel
    {
      public:
        TSymbolTableLevel() = default;
    
        bool insert(TSymbol *symbol);
    
        // Insert a function using its unmangled name as the key.
        void insertUnmangled(TFunction *function);
    
        TSymbol *find(const ImmutableString &name) const;
    
      private:
        using tLevel        = TUnorderedMap<ImmutableString,
                                     TSymbol *,
                                     ImmutableString::FowlerNollVoHash<sizeof(size_t)>>;
        using tLevelPair    = const tLevel::value_type;
        using tInsertResult = std::pair<tLevel::iterator, bool>;
    
        tLevel level;
    };
    
    bool TSymbolTable::TSymbolTableLevel::insert(TSymbol *symbol)
    {
        // returning true means symbol was added to the table
        tInsertResult result = level.insert(tLevelPair(symbol->getMangledName(), symbol));
        return result.second;
    }
    
    void TSymbolTable::TSymbolTableLevel::insertUnmangled(TFunction *function)
    {
        level.insert(tLevelPair(function->name(), function));
    }
    
    TSymbol *TSymbolTable::TSymbolTableLevel::find(const ImmutableString &name) const
    {
        tLevel::const_iterator it = level.find(name);
        if (it == level.end())
            return nullptr;
        else
            return (*it).second;
    }
    
    TSymbolTable::TSymbolTable()
        : mGlobalInvariant(false),
          mUniqueIdCounter(0),
          mShaderType(GL_FRAGMENT_SHADER),
          mShaderSpec(SH_GLES2_SPEC),
          mGlInVariableWithArraySize(nullptr)
    {}
    
    TSymbolTable::~TSymbolTable() = default;
    
    bool TSymbolTable::isEmpty() const
    {
        return mTable.empty();
    }
    
    bool TSymbolTable::atGlobalLevel() const
    {
        return mTable.size() == 1u;
    }
    
    void TSymbolTable::push()
    {
        mTable.emplace_back(new TSymbolTableLevel);
        mPrecisionStack.emplace_back(new PrecisionStackLevel);
    }
    
    void TSymbolTable::pop()
    {
        mTable.pop_back();
        mPrecisionStack.pop_back();
    }
    
    const TFunction *TSymbolTable::markFunctionHasPrototypeDeclaration(
        const ImmutableString &mangledName,
        bool *hadPrototypeDeclarationOut) const
    {
        TFunction *function         = findUserDefinedFunction(mangledName);
        *hadPrototypeDeclarationOut = function->hasPrototypeDeclaration();
        function->setHasPrototypeDeclaration();
        return function;
    }
    
    const TFunction *TSymbolTable::setFunctionParameterNamesFromDefinition(const TFunction *function,
                                                                           bool *wasDefinedOut) const
    {
        TFunction *firstDeclaration = findUserDefinedFunction(function->getMangledName());
        ASSERT(firstDeclaration);
        // Note: 'firstDeclaration' could be 'function' if this is the first time we've seen function as
        // it would have just been put in the symbol table. Otherwise, we're looking up an earlier
        // occurance.
        if (function != firstDeclaration)
        {
            // The previous declaration should have the same parameters as the function definition
            // (parameter names may differ).
            firstDeclaration->shareParameters(*function);
        }
    
        *wasDefinedOut = firstDeclaration->isDefined();
        firstDeclaration->setDefined();
        return firstDeclaration;
    }
    
    bool TSymbolTable::setGlInArraySize(unsigned int inputArraySize)
    {
        if (mGlInVariableWithArraySize)
        {
            return mGlInVariableWithArraySize->getType().getOutermostArraySize() == inputArraySize;
        }
        const TInterfaceBlock *glPerVertex = static_cast<const TInterfaceBlock *>(m_gl_PerVertex);
        TType *glInType = new TType(glPerVertex, EvqPerVertexIn, TLayoutQualifier::Create());
        glInType->makeArray(inputArraySize);
        mGlInVariableWithArraySize =
            new TVariable(this, ImmutableString("gl_in"), glInType, SymbolType::BuiltIn,
                          TExtension::EXT_geometry_shader);
        return true;
    }
    
    TVariable *TSymbolTable::getGlInVariableWithArraySize() const
    {
        return mGlInVariableWithArraySize;
    }
    
    const TVariable *TSymbolTable::gl_FragData() const
    {
        return static_cast<const TVariable *>(m_gl_FragData);
    }
    
    const TVariable *TSymbolTable::gl_SecondaryFragDataEXT() const
    {
        return static_cast<const TVariable *>(m_gl_SecondaryFragDataEXT);
    }
    
    TSymbolTable::VariableMetadata *TSymbolTable::getOrCreateVariableMetadata(const TVariable &variable)
    {
        int id    = variable.uniqueId().get();
        auto iter = mVariableMetadata.find(id);
        if (iter == mVariableMetadata.end())
        {
            iter = mVariableMetadata.insert(std::make_pair(id, VariableMetadata())).first;
        }
        return &iter->second;
    }
    
    void TSymbolTable::markStaticWrite(const TVariable &variable)
    {
        auto metadata         = getOrCreateVariableMetadata(variable);
        metadata->staticWrite = true;
    }
    
    void TSymbolTable::markStaticRead(const TVariable &variable)
    {
        auto metadata        = getOrCreateVariableMetadata(variable);
        metadata->staticRead = true;
    }
    
    bool TSymbolTable::isStaticallyUsed(const TVariable &variable) const
    {
        ASSERT(!variable.getConstPointer());
        int id    = variable.uniqueId().get();
        auto iter = mVariableMetadata.find(id);
        return iter != mVariableMetadata.end() && (iter->second.staticRead || iter->second.staticWrite);
    }
    
    void TSymbolTable::addInvariantVarying(const TVariable &variable)
    {
        ASSERT(atGlobalLevel());
        auto metadata       = getOrCreateVariableMetadata(variable);
        metadata->invariant = true;
    }
    
    bool TSymbolTable::isVaryingInvariant(const TVariable &variable) const
    {
        ASSERT(atGlobalLevel());
        if (mGlobalInvariant && (IsShaderOutput(variable.getType().getQualifier())))
        {
            return true;
        }
        int id    = variable.uniqueId().get();
        auto iter = mVariableMetadata.find(id);
        return iter != mVariableMetadata.end() && iter->second.invariant;
    }
    
    void TSymbolTable::setGlobalInvariant(bool invariant)
    {
        ASSERT(atGlobalLevel());
        mGlobalInvariant = invariant;
    }
    
    const TSymbol *TSymbolTable::find(const ImmutableString &name, int shaderVersion) const
    {
        const TSymbol *userSymbol = findUserDefined(name);
        if (userSymbol)
        {
            return userSymbol;
        }
    
        return findBuiltIn(name, shaderVersion);
    }
    
    const TSymbol *TSymbolTable::findUserDefined(const ImmutableString &name) const
    {
        int userDefinedLevel = static_cast<int>(mTable.size()) - 1;
        while (userDefinedLevel >= 0)
        {
            const TSymbol *symbol = mTable[userDefinedLevel]->find(name);
            if (symbol)
            {
                return symbol;
            }
            userDefinedLevel--;
        }
    
        return nullptr;
    }
    
    TFunction *TSymbolTable::findUserDefinedFunction(const ImmutableString &name) const
    {
        // User-defined functions are always declared at the global level.
        ASSERT(!mTable.empty());
        return static_cast<TFunction *>(mTable[0]->find(name));
    }
    
    const TSymbol *TSymbolTable::findGlobal(const ImmutableString &name) const
    {
        ASSERT(!mTable.empty());
        return mTable[0]->find(name);
    }
    
    const TSymbol *TSymbolTable::findGlobalWithConversion(
        const std::vector<ImmutableString> &names) const
    {
        for (const ImmutableString &name : names)
        {
            const TSymbol *target = findGlobal(name);
            if (target != nullptr)
                return target;
        }
        return nullptr;
    }
    
    const TSymbol *TSymbolTable::findBuiltInWithConversion(const std::vector<ImmutableString> &names,
                                                           int shaderVersion) const
    {
        for (const ImmutableString &name : names)
        {
            const TSymbol *target = findBuiltIn(name, shaderVersion);
            if (target != nullptr)
                return target;
        }
        return nullptr;
    }
    
    bool TSymbolTable::declare(TSymbol *symbol)
    {
        ASSERT(!mTable.empty());
        // The following built-ins may be redeclared by the shader: gl_ClipDistance, gl_CullDistance and
        // gl_LastFragData.
        ASSERT(symbol->symbolType() == SymbolType::UserDefined ||
               (symbol->symbolType() == SymbolType::BuiltIn && IsRedeclarableBuiltIn(symbol->name())));
        ASSERT(!symbol->isFunction());
        return mTable.back()->insert(symbol);
    }
    
    bool TSymbolTable::declareInternal(TSymbol *symbol)
    {
        ASSERT(!mTable.empty());
        ASSERT(symbol->symbolType() == SymbolType::AngleInternal);
        ASSERT(!symbol->isFunction());
        return mTable.back()->insert(symbol);
    }
    
    void TSymbolTable::declareUserDefinedFunction(TFunction *function, bool insertUnmangledName)
    {
        ASSERT(!mTable.empty());
        if (insertUnmangledName)
        {
            // Insert the unmangled name to detect potential future redefinition as a variable.
            mTable[0]->insertUnmangled(function);
        }
        mTable[0]->insert(function);
    }
    
    void TSymbolTable::setDefaultPrecision(TBasicType type, TPrecision prec)
    {
        int indexOfLastElement = static_cast<int>(mPrecisionStack.size()) - 1;
        // Uses map operator [], overwrites the current value
        (*mPrecisionStack[indexOfLastElement])[type] = prec;
    }
    
    TPrecision TSymbolTable::getDefaultPrecision(TBasicType type) const
    {
        if (!SupportsPrecision(type))
            return EbpUndefined;
    
        // unsigned integers use the same precision as signed
        TBasicType baseType = (type == EbtUInt) ? EbtInt : type;
    
        int level = static_cast<int>(mPrecisionStack.size()) - 1;
        ASSERT(level >= 0);  // Just to be safe. Should not happen.
        // If we dont find anything we return this. Some types don't have predefined default precision.
        TPrecision prec = EbpUndefined;
        while (level >= 0)
        {
            PrecisionStackLevel::iterator it = mPrecisionStack[level]->find(baseType);
            if (it != mPrecisionStack[level]->end())
            {
                prec = (*it).second;
                break;
            }
            level--;
        }
        return prec;
    }
    
    void TSymbolTable::clearCompilationResults()
    {
        mGlobalInvariant = false;
        mUniqueIdCounter = kLastBuiltInId + 1;
        mVariableMetadata.clear();
        mGlInVariableWithArraySize = nullptr;
    
        // User-defined scopes should have already been cleared when the compilation finished.
        ASSERT(mTable.empty());
    }
    
    int TSymbolTable::nextUniqueIdValue()
    {
        ASSERT(mUniqueIdCounter < std::numeric_limits<int>::max());
        return ++mUniqueIdCounter;
    }
    
    void TSymbolTable::initializeBuiltIns(sh::GLenum type,
                                          ShShaderSpec spec,
                                          const ShBuiltInResources &resources)
    {
        mShaderType = type;
        mShaderSpec = spec;
        mResources  = resources;
    
        // We need just one precision stack level for predefined precisions.
        mPrecisionStack.emplace_back(new PrecisionStackLevel);
    
        if (IsDesktopGLSpec(spec))
        {
            setDefaultPrecision(EbtInt, EbpUndefined);
            setDefaultPrecision(EbtFloat, EbpUndefined);
        }
        else
        {
            switch (type)
            {
                case GL_FRAGMENT_SHADER:
                    setDefaultPrecision(EbtInt, EbpMedium);
                    break;
                case GL_VERTEX_SHADER:
                case GL_COMPUTE_SHADER:
                case GL_GEOMETRY_SHADER_EXT:
                case GL_TESS_CONTROL_SHADER_EXT:
                case GL_TESS_EVALUATION_SHADER_EXT:
                    setDefaultPrecision(EbtInt, EbpHigh);
                    setDefaultPrecision(EbtFloat, EbpHigh);
                    break;
                default:
                    UNREACHABLE();
            }
        }
    
        // 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);
        // SamplerExternal2DY2YEXT is specified in the extension to have default precision.
        initSamplerDefaultPrecision(EbtSamplerExternal2DY2YEXT);
        // It isn't specified whether Sampler2DRect has default precision.
        initSamplerDefaultPrecision(EbtSampler2DRect);
    
        if (spec < SH_GLES3_SPEC)
        {
            // Only set the default precision of shadow samplers in ESLL1. They become core in ESSL3
            // where they do not have a defalut precision.
            initSamplerDefaultPrecision(EbtSampler2DShadow);
        }
    
        setDefaultPrecision(EbtAtomicCounter, EbpHigh);
    
        initializeBuiltInVariables(type, spec, resources);
        mUniqueIdCounter = kLastBuiltInId + 1;
    }
    
    void TSymbolTable::initSamplerDefaultPrecision(TBasicType samplerType)
    {
        ASSERT(samplerType >= EbtGuardSamplerBegin && samplerType <= EbtGuardSamplerEnd);
        setDefaultPrecision(samplerType, EbpLow);
    }
    
    TSymbolTable::VariableMetadata::VariableMetadata()
        : staticRead(false), staticWrite(false), invariant(false)
    {}
    
    const TSymbol *SymbolRule::get(ShShaderSpec shaderSpec,
                                   int shaderVersion,
                                   sh::GLenum shaderType,
                                   const ShBuiltInResources &resources,
                                   const TSymbolTableBase &symbolTable) const
    {
        if (IsDesktopGLSpec(shaderSpec) != (mIsDesktop == 1))
            return nullptr;
    
        if (mVersion == kESSL1Only && shaderVersion != static_cast<int>(kESSL1Only))
            return nullptr;
    
        if (mVersion > shaderVersion)
            return nullptr;
    
        if (!CheckShaderType(static_cast<Shader>(mShaders), shaderType))
            return nullptr;
    
        if (mExtensionIndex != 0 && !CheckExtension(mExtensionIndex, resources))
            return nullptr;
    
        return mIsVar > 0 ? symbolTable.*(mSymbolOrVar.var) : mSymbolOrVar.symbol;
    }
    
    const TSymbol *FindMangledBuiltIn(ShShaderSpec shaderSpec,
                                      int shaderVersion,
                                      sh::GLenum shaderType,
                                      const ShBuiltInResources &resources,
                                      const TSymbolTableBase &symbolTable,
                                      const SymbolRule *rules,
                                      uint16_t startIndex,
                                      uint16_t endIndex)
    {
        for (uint32_t ruleIndex = startIndex; ruleIndex < endIndex; ++ruleIndex)
        {
            const TSymbol *symbol =
                rules[ruleIndex].get(shaderSpec, shaderVersion, shaderType, resources, symbolTable);
            if (symbol)
            {
                return symbol;
            }
        }
    
        return nullptr;
    }
    
    bool UnmangledEntry::matches(const ImmutableString &name,
                                 ShShaderSpec shaderSpec,
                                 int shaderVersion,
                                 sh::GLenum shaderType,
                                 const TExtensionBehavior &extensions) const
    {
        if (name != mName)
            return false;
    
        if (!CheckShaderType(static_cast<Shader>(mShaderType), shaderType))
            return false;
    
        if (IsDesktopGLSpec(shaderSpec))
        {
            if (mGLSLVersion > shaderVersion)
                return false;
    
            if (mGLSLExtension == TExtension::UNDEFINED)
                return true;
    
            return IsExtensionEnabled(extensions, mGLSLExtension);
        }
        else
        {
            if (mESSLVersion == kESSL1Only && shaderVersion != static_cast<int>(kESSL1Only))
                return false;
    
            if (mESSLVersion > shaderVersion)
                return false;
    
            bool anyExtension        = false;
            bool anyExtensionEnabled = false;
            for (TExtension ext : mESSLExtensions)
            {
                if (ext != TExtension::UNDEFINED)
                {
                    anyExtension        = true;
                    anyExtensionEnabled = anyExtensionEnabled || IsExtensionEnabled(extensions, ext);
                }
            }
    
            if (!anyExtension)
                return true;
    
            return anyExtensionEnabled;
        }
    }
    }  // namespace sh