diff --git a/glslang/MachineIndependent/ShaderLang.cpp b/glslang/MachineIndependent/ShaderLang.cpp index 4a652615..7bdae096 100644 --- a/glslang/MachineIndependent/ShaderLang.cpp +++ b/glslang/MachineIndependent/ShaderLang.cpp @@ -78,35 +78,38 @@ int MapVersionToIndex(int version) } // V const int VersionCount = 12; -// -// A process-global symbol table per version per profile per language. This will be sparsely +// only one of these needed for non-ES; ES needs 2 for different precision defaults of built-ins +enum EPrecisionClass { + EPcGeneral, + EPcFragment, + EPcCount +}; + +// A process-global symbol table per version per profile for built-ins common +// to multiple stages (languages), and a process-global symbol table per version +// per profile per stage for built-ins unique to each stage. They will be sparsely // populated, so they will only only be generated as needed. // // Each has a different set of built-ins, and we want to preserve that from // compile to compile. // +TSymbolTable* CommonSymbolTable[VersionCount][EProfileCount][EPcCount] = {}; TSymbolTable* SharedSymbolTables[VersionCount][EProfileCount][EShLangCount] = {}; TPoolAllocator* PerProcessGPA = 0; -bool InitializeSymbolTable(const TBuiltIns& builtIns, int version, EProfile profile, EShLanguage language, TInfoSink& infoSink, - const TBuiltInResource* resources, TSymbolTable* symbolTables) +bool InitializeSymbolTable(const TString& builtIns, int version, EProfile profile, EShLanguage language, TInfoSink& infoSink, + TSymbolTable& symbolTable) { TIntermediate intermediate(infoSink, version, profile); - TSymbolTable* symbolTable; - if (resources) - symbolTable = symbolTables; - else - symbolTable = &symbolTables[language]; - - TParseContext parseContext(*symbolTable, intermediate, true, version, profile, language, infoSink); + TParseContext parseContext(symbolTable, intermediate, true, version, profile, language, infoSink); TPpContext ppContext(parseContext); glslang::TScanContext scanContext(parseContext); parseContext.scanContext = &scanContext; parseContext.ppContext = &ppContext; - assert(symbolTable->isEmpty() || symbolTable->atSharedBuiltInLevel()); + assert(symbolTable.isEmpty() || symbolTable.atSharedBuiltInLevel()); // // Parse the built-ins. This should only happen once per @@ -117,55 +120,72 @@ bool InitializeSymbolTable(const TBuiltIns& builtIns, int version, EProfile prof // are preserved, and the test for an empty table fails. // - symbolTable->push(); + symbolTable.push(); const char* builtInShaders[2]; int builtInLengths[2]; - builtInShaders[0] = builtIns.getCommonString().c_str(); - builtInLengths[0] = builtIns.getCommonString().size(); - builtInShaders[1] = builtIns.getStageString(language).c_str(); - builtInLengths[1] = builtIns.getStageString(language).size(); + builtInShaders[0] = builtIns.c_str(); + builtInLengths[0] = builtIns.size(); - if (! parseContext.parseShaderStrings(ppContext, const_cast(builtInShaders), builtInLengths, 2) != 0) { + if (! parseContext.parseShaderStrings(ppContext, const_cast(builtInShaders), builtInLengths, 1) != 0) { infoSink.info.message(EPrefixInternalError, "Unable to parse built-ins"); printf("Unable to parse built-ins\n"); return false; } - if (resources) - IdentifyBuiltIns(version, profile, parseContext.language, *symbolTable, *resources); - else - IdentifyBuiltIns(version, profile, parseContext.language, *symbolTable); - return true; } -bool GenerateBuiltInSymbolTable(TInfoSink& infoSink, TSymbolTable* symbolTables, int version, EProfile profile) +// +// To call for per-stage initialization, with the common table already complete. +// +void InitializeSymbolTable(TBuiltIns& builtIns, int version, EProfile profile, EShLanguage language, TInfoSink& infoSink, TSymbolTable* commonTable, TSymbolTable* symbolTables) +{ + int commonIndex = EPcGeneral; + if (profile == EEsProfile && language == EShLangFragment) + commonIndex = EPcFragment; + + symbolTables[language].adoptLevels(commonTable[commonIndex]); + InitializeSymbolTable(builtIns.getStageString(language), version, profile, language, infoSink, symbolTables[language]); + IdentifyBuiltIns(version, profile, language, symbolTables[language]); + if (profile == EEsProfile) + symbolTables[language].setNoBuiltInRedeclarations(); +} + +bool GenerateBuiltInSymbolTable(TInfoSink& infoSink, TSymbolTable* commonTable, TSymbolTable* symbolTables, int version, EProfile profile) { TBuiltIns builtIns; builtIns.initialize(version, profile); - InitializeSymbolTable(builtIns, version, profile, EShLangVertex, infoSink, 0, symbolTables); + + // do the common table + InitializeSymbolTable(builtIns.getCommonString(), version, profile, EShLangVertex, infoSink, commonTable[EPcGeneral]); + if (profile == EEsProfile) + InitializeSymbolTable(builtIns.getCommonString(), version, profile, EShLangFragment, infoSink, commonTable[EPcFragment]); + + // do the per-stage tables + InitializeSymbolTable(builtIns, version, profile, EShLangVertex, infoSink, commonTable, symbolTables); + InitializeSymbolTable(builtIns, version, profile, EShLangFragment, infoSink, commonTable, symbolTables); if (profile != EEsProfile && version >= 400) { - InitializeSymbolTable(builtIns, version, profile, EShLangTessControl, infoSink, 0, symbolTables); - InitializeSymbolTable(builtIns, version, profile, EShLangTessEvaluation, infoSink, 0, symbolTables); + InitializeSymbolTable(builtIns, version, profile, EShLangTessControl, infoSink, commonTable, symbolTables); + InitializeSymbolTable(builtIns, version, profile, EShLangTessEvaluation, infoSink, commonTable, symbolTables); } if (profile != EEsProfile && version >= 150) - InitializeSymbolTable(builtIns, version, profile, EShLangGeometry, infoSink, 0, symbolTables); - InitializeSymbolTable(builtIns, version, profile, EShLangFragment, infoSink, 0, symbolTables); + InitializeSymbolTable(builtIns, version, profile, EShLangGeometry, infoSink, commonTable, symbolTables); if (profile != EEsProfile && version >= 430) - InitializeSymbolTable(builtIns, version, profile, EShLangCompute, infoSink, 0, symbolTables); + InitializeSymbolTable(builtIns, version, profile, EShLangCompute, infoSink, commonTable, symbolTables); return true; } -bool AddContextSpecificSymbols(const TBuiltInResource* resources, TInfoSink& infoSink, TSymbolTable* symbolTables, int version, EProfile profile, EShLanguage language) +bool AddContextSpecificSymbols(const TBuiltInResource* resources, TInfoSink& infoSink, TSymbolTable& symbolTable, int version, EProfile profile, EShLanguage language) { TBuiltIns builtIns; builtIns.initialize(*resources, version, profile, language); - InitializeSymbolTable(builtIns, version, profile, language, infoSink, resources, symbolTables); + InitializeSymbolTable(builtIns.getCommonString(), version, profile, language, infoSink, symbolTable); + IdentifyBuiltIns(version, profile, language, symbolTable, *resources); return true; } @@ -189,9 +209,9 @@ void SetupBuiltinSymbolTable(int version, EProfile profile) // Make sure only one thread tries to do this at a time glslang::GetGlobalLock(); - // See if it's already been done. + // See if it's already been done for this version/profile combination int versionIndex = MapVersionToIndex(version); - if (SharedSymbolTables[versionIndex][profile][EShLangVertex]) { + if (CommonSymbolTable[versionIndex][profile][EPcGeneral]) { glslang::ReleaseGlobalLock(); return; @@ -202,25 +222,34 @@ void SetupBuiltinSymbolTable(int version, EProfile profile) TPoolAllocator* builtInPoolAllocator = new TPoolAllocator(); SetThreadPoolAllocator(*builtInPoolAllocator); - // Generate the symbol table using the new pool - TSymbolTable symTables[EShLangCount]; - if (profile == EEsProfile) { - for (int stage = 0; stage < EShLangCount; ++stage) - symTables[stage].setNoBuiltInRedeclarations(); - } - GenerateBuiltInSymbolTable(infoSink, symTables, version, profile); + // Generate the local symbol tables using the new pool + TSymbolTable commonTable[2]; + TSymbolTable stageTables[EShLangCount]; + GenerateBuiltInSymbolTable(infoSink, commonTable, stageTables, version, profile); // Switch to the process-global pool SetThreadPoolAllocator(*PerProcessGPA); - // Copy the symbol table from the new pool to the process-global pool + // Copy the local symbol tables from the new pool to the global tables using the process-global pool + for (int precClass = 0; precClass < EPcCount; ++precClass) { + if (! commonTable[precClass].isEmpty()) { + CommonSymbolTable[versionIndex][profile][precClass] = new TSymbolTable; + CommonSymbolTable[versionIndex][profile][precClass]->copyTable(commonTable[precClass]); + } + } for (int stage = 0; stage < EShLangCount; ++stage) { - if (! symTables[stage].isEmpty()) { + if (! stageTables[stage].isEmpty()) { SharedSymbolTables[versionIndex][profile][stage] = new TSymbolTable; - SharedSymbolTables[versionIndex][profile][stage]->copyTable(symTables[stage]); + SharedSymbolTables[versionIndex][profile][stage]->copyTable(stageTables[stage]); } } + // Explicitly clean up the local tables before deleting the pool they used and before releasing the lock. + for (int precClass = 0; precClass < EPcCount; ++precClass) + commonTable[precClass].~TSymbolTable(); + for (int stage = 0; stage < EShLangCount; ++stage) + stageTables[stage].~TSymbolTable(); + delete builtInPoolAllocator; SetThreadPoolAllocator(savedGPA); @@ -429,18 +458,13 @@ int ShCompile( TSymbolTable* cachedTable = SharedSymbolTables[MapVersionToIndex(version)] [profile] [compiler->getLanguage()]; - TSymbolTable* errorTable = 0; - if (! cachedTable) { - errorTable = new TSymbolTable; - cachedTable = errorTable; - } - TSymbolTable symbolTable(*cachedTable); - if (errorTable) - delete errorTable; + TSymbolTable symbolTable; + if (cachedTable) + symbolTable.adoptLevels(*cachedTable); // Add built-in symbols that are potentially context dependent; // they get popped again further down. - AddContextSpecificSymbols(resources, compiler->infoSink, &symbolTable, version, profile, compiler->getLanguage()); + AddContextSpecificSymbols(resources, compiler->infoSink, symbolTable, version, profile, compiler->getLanguage()); TParseContext parseContext(symbolTable, intermediate, false, version, profile, compiler->getLanguage(), compiler->infoSink, forwardCompatible, messages); glslang::TScanContext scanContext(parseContext); diff --git a/glslang/MachineIndependent/SymbolTable.h b/glslang/MachineIndependent/SymbolTable.h index 48a38efe..4bfae185 100644 --- a/glslang/MachineIndependent/SymbolTable.h +++ b/glslang/MachineIndependent/SymbolTable.h @@ -246,7 +246,7 @@ protected: class TSymbolTableLevel { public: POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator()) - TSymbolTableLevel() : defaultPrecision (0), anonId(0) { } + TSymbolTableLevel() : defaultPrecision(0), anonId(0) { } ~TSymbolTableLevel(); bool insert(TSymbol& symbol) @@ -365,21 +365,12 @@ protected: class TSymbolTable { public: - TSymbolTable() : uniqueId(0), noBuiltInRedeclarations(false), adoptedLevels(1) // TODO: memory: can we make adoptedLevels be 0 for symbol tables we don't keep? + TSymbolTable() : uniqueId(0), noBuiltInRedeclarations(false), adoptedLevels(0) { // // This symbol table cannot be used until push() is called. // } - explicit TSymbolTable(TSymbolTable& symTable) - { - if (! symTable.isEmpty()) { - table.push_back(symTable.table[0]); - adoptedLevels = 1; - uniqueId = symTable.uniqueId; - noBuiltInRedeclarations = symTable.noBuiltInRedeclarations; - } // else should only be to handle error paths - } ~TSymbolTable() { // don't deallocate levels passed in from elsewhere @@ -388,18 +379,28 @@ public: } // - // When the symbol table is initialized with the built-ins, there should - // 'push' calls, so that built-in shared across all compiles are at level 0 - // built-ins specific to a compile are at level 1 and the shader - // globals are at level 2. - // - // TODO: compile-time memory: have an even earlier level for all built-ins - // common to all stages. Currently, each stage has copy. + // While level adopting is generic, the methods below enact a the following + // convention for levels: + // 0: common built-ins shared across all stages, all compiles, only one copy for all symbol tables + // 1: per-stage built-ins, shared across all compiles, but a different copy per stage + // 2: built-ins specific to a compile, like resources that are context-dependent + // 3: user-shader globals // + void adoptLevels(TSymbolTable& symTable) + { + for (unsigned int level = 0; level < symTable.table.size(); ++level) { + table.push_back(symTable.table[level]); + ++adoptedLevels; + } + uniqueId = symTable.uniqueId; + noBuiltInRedeclarations = symTable.noBuiltInRedeclarations; + } bool isEmpty() { return table.size() == 0; } bool atBuiltInLevel() { return atSharedBuiltInLevel() || atDynamicBuiltInLevel(); } - bool atSharedBuiltInLevel() { return table.size() == 1; } - bool atGlobalLevel() { return table.size() <= 3; } + bool atSharedBuiltInLevel() { return table.size() <= 2; } + bool atDynamicBuiltInLevel() { return table.size() == 3; } + bool atGlobalLevel() { return table.size() <= 4; } + void setNoBuiltInRedeclarations() { noBuiltInRedeclarations = true; } void push() @@ -450,8 +451,16 @@ public: return symbol; } - TSymbolTableLevel* getGlobalLevel() { assert(table.size() >= 3); return table[2]; } - void relateToOperator(const char* name, TOperator op) { table[0]->relateToOperator(name, op); } + void relateToOperator(const char* name, TOperator op) + { + for (unsigned int level = 0; level < table.size(); ++level) { + if (atSharedBuiltInLevel()) + table[level]->relateToOperator(name, op); + else + break; + } + } + int getMaxSymbolId() { return uniqueId; } void dump(TInfoSink &infoSink) const; void copyTable(const TSymbolTable& copyOf); @@ -459,10 +468,10 @@ public: void setPreviousDefaultPrecisions(TPrecisionQualifier *p) { table[currentLevel()]->setPreviousDefaultPrecisions(p); } protected: + TSymbolTable(TSymbolTable&); TSymbolTable& operator=(TSymbolTableLevel&); int currentLevel() const { return static_cast(table.size()) - 1; } - bool atDynamicBuiltInLevel() { return table.size() == 2; } std::vector table; int uniqueId; // for unique identification in code generation diff --git a/glslang/MachineIndependent/glslang.y b/glslang/MachineIndependent/glslang.y index 79d5ab13..c41693fa 100644 --- a/glslang/MachineIndependent/glslang.y +++ b/glslang/MachineIndependent/glslang.y @@ -1098,7 +1098,7 @@ declaration | PRECISION precision_qualifier type_specifier SEMICOLON { parseContext.profileRequires($1.loc, ENoProfile, 130, 0, "precision statement"); - // lazy setting of the previous scope's defaults, only takes on first one in a particular scope + // lazy setting of the previous scope's defaults, has effect only the first time it is called in a particular scope parseContext.symbolTable.setPreviousDefaultPrecisions(&parseContext.defaultPrecision[0]); parseContext.setDefaultPrecision($1.loc, $3, $2.qualifier.precision);