| // | |
| // 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::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()); | |
| ASSERT(symbol->symbolType() == SymbolType::UserDefined); | |
| 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: | |
| 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); | |
| 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 (static_cast<TExtension>(mGLSLExtension) == TExtension::UNDEFINED) | |
| return true; | |
| return IsExtensionEnabled(extensions, static_cast<TExtension>(mGLSLExtension)); | |
| } | |
| else | |
| { | |
| if (mESSLVersion == kESSL1Only && shaderVersion != static_cast<int>(kESSL1Only)) | |
| return false; | |
| if (mESSLVersion > shaderVersion) | |
| return false; | |
| if (static_cast<TExtension>(mESSLExtension) == TExtension::UNDEFINED) | |
| return true; | |
| return IsExtensionEnabled(extensions, static_cast<TExtension>(mESSLExtension)); | |
| } | |
| } | |
| } // namespace sh |