diff --git a/Install/Windows/glslangValidator.exe b/Install/Windows/glslangValidator.exe index 52f8104d..b31e3fce 100644 Binary files a/Install/Windows/glslangValidator.exe and b/Install/Windows/glslangValidator.exe differ diff --git a/StandAlone/StandAlone.cpp b/StandAlone/StandAlone.cpp index 3b264886..8203ad50 100644 --- a/StandAlone/StandAlone.cpp +++ b/StandAlone/StandAlone.cpp @@ -55,6 +55,7 @@ enum TOptions { EOptionRelaxedErrors = 0x008, EOptionGiveWarnings = 0x010, EOptionsLinkProgram = 0x020, + EOptionMultiThreaded = 0x040, }; // @@ -119,16 +120,29 @@ void GenerateResources(TBuiltInResource& resources) resources.maxProgramTexelOffset = 7; } +// thread-safe list of shaders to asynchronously grab and compile glslang::TWorklist Worklist; + +// array of unique places to leave the shader names and infologs for the asynchronous compiles +glslang::TWorkItem **Work = 0; +int NumWorkItems = 0; + int Options = 0; bool Delay = false; +const char* ExecutableName; bool ProcessArguments(int argc, char* argv[]) { + ExecutableName = argv[0]; + NumWorkItems = argc; // will include some empties where the '-' options were, but it doesn't matter, they'll be 0 + Work = new glslang::TWorkItem*[NumWorkItems]; + Work[0] = 0; + argc--; argv++; for (; argc >= 1; argc--, argv++) { if (argv[0][0] == '-') { + Work[argc] = 0; switch (argv[0][1]) { case 'd': Delay = true; @@ -148,11 +162,18 @@ bool ProcessArguments(int argc, char* argv[]) case 's': Options |= EOptionSuppressInfolog; break; + case 't': + #ifdef _WIN32 + Options |= EOptionMultiThreaded; + #endif + break; default: return false; } - } else - Worklist.add(std::string(argv[0])); + } else { + Work[argc] = new glslang::TWorkItem(std::string(argv[0])); + Worklist.add(Work[argc]); + } } if (Worklist.empty()) @@ -168,18 +189,18 @@ unsigned int #endif CompileShaders(void*) { - std::string shaderName; - while (Worklist.remove(shaderName)) { - ShHandle compiler = ShConstructCompiler(FindLanguage(shaderName), Options); + glslang::TWorkItem* workItem; + while (Worklist.remove(workItem)) { + ShHandle compiler = ShConstructCompiler(FindLanguage(workItem->name), Options); if (compiler == 0) return false; TBuiltInResource resources; GenerateResources(resources); - CompileFile(shaderName.c_str(), compiler, Options, &resources); + CompileFile(workItem->name.c_str(), compiler, Options, &resources); if (! (Options & EOptionSuppressInfolog)) - puts(ShGetInfoLog(compiler)); + workItem->results = ShGetInfoLog(compiler); ShDestruct(compiler); } @@ -212,13 +233,13 @@ void CompileAndLinkShaders() // glslang::TProgram program; - std::string shaderName; - while (Worklist.remove(shaderName)) { - EShLanguage stage = FindLanguage(shaderName); + glslang::TWorkItem* workItem; + while (Worklist.remove(workItem)) { + EShLanguage stage = FindLanguage(workItem->name); glslang::TShader* shader = new glslang::TShader(stage); shaders.push_back(shader); - char** shaderStrings = ReadFileData(shaderName.c_str()); + char** shaderStrings = ReadFileData(workItem->name.c_str()); if (! shaderStrings) { usage(); return; @@ -231,7 +252,7 @@ void CompileAndLinkShaders() program.addShader(shader); if (! (Options & EOptionSuppressInfolog)) { - puts(shaderName.c_str()); + puts(workItem->name.c_str()); puts(shader->getInfoLog()); puts(shader->getInfoDebugLog()); } @@ -266,7 +287,7 @@ int C_DECL main(int argc, char* argv[]) // Init for front-end proper ShInitialize(); - // Init for for standalone + // Init for standalone glslang::InitGlobalLock(); if (! ProcessArguments(argc, argv)) { @@ -276,29 +297,39 @@ int C_DECL main(int argc, char* argv[]) // // Two modes: - // 1) linking all arguments together, single-threaded - // 2) independent arguments, can be tackled by multiple asynchronous threads, for testing thread safety + // 1) linking all arguments together, single-threaded, new C++ interface + // 2) independent arguments, can be tackled by multiple asynchronous threads, for testing thread safety, using the old handle interface // + if (Options & EOptionsLinkProgram) + CompileAndLinkShaders(); + else { + bool printShaderNames = Worklist.size() > 1; - // TODO: finish threading, allow external control over number of threads - const int NumThreads = 1; - if (NumThreads > 1) { - void* threads[NumThreads]; - for (int t = 0; t < NumThreads; ++t) { - threads[t] = glslang::OS_CreateThread(&CompileShaders); - if (! threads[t]) { - printf("Failed to create thread\n"); - return EFailThreadCreate; + if (Options & EOptionMultiThreaded) { + const int NumThreads = 16; + void* threads[NumThreads]; + for (int t = 0; t < NumThreads; ++t) { + threads[t] = glslang::OS_CreateThread(&CompileShaders); + if (! threads[t]) { + printf("Failed to create thread\n"); + return EFailThreadCreate; + } } - } - glslang::OS_WaitForAllThreads(threads, NumThreads); - } else { - if (Options & EOptionsLinkProgram) { - CompileAndLinkShaders(); + glslang::OS_WaitForAllThreads(threads, NumThreads); } else { if (! CompileShaders(0)) compileFailed = true; } + + // Print out all the resulting infologs + for (int w = 0; w < NumWorkItems; ++w) { + if (Work[w]) { + if (printShaderNames) + puts(Work[w]->name.c_str()); + puts(Work[w]->results.c_str()); + delete Work[w]; + } + } } if (Delay) @@ -401,7 +432,7 @@ bool CompileFile(const char *fileName, ShHandle compiler, int Options, const TBu // void usage() { - printf("Usage: standalone [ options ] filename\n" + printf("Usage: glslangValidator [ options ] filename\n" "Where: filename is a name ending in\n" " .vert for a vertex shader\n" " .tesc for a tessellation control shader\n" @@ -415,8 +446,9 @@ void usage() "-d: delay exit\n" "-l: link validation of all input files\n" "-m: memory leak mode\n" + "-r: relaxed semantic error-checking mode\n" "-s: silent mode\n" - "-r: relaxed semantic error-checking mode\n"); + "-t: multi-threaded mode\n"); } #ifndef _WIN32 diff --git a/StandAlone/Worklist.h b/StandAlone/Worklist.h index e5c35bd6..1bcc7e1e 100644 --- a/StandAlone/Worklist.h +++ b/StandAlone/Worklist.h @@ -41,27 +41,36 @@ namespace glslang { + class TWorkItem { + public: + TWorkItem() { } + explicit TWorkItem(const std::string& s) : + name(s) { } + std::string name; + std::string results; + }; + class TWorklist { public: TWorklist() { } virtual ~TWorklist() { } - void add(const std::string& s) + void add(TWorkItem* item) { GetGlobalLock(); - worklist.push_back(s); + worklist.push_back(item); ReleaseGlobalLock(); } - bool remove(std::string& s) + bool remove(TWorkItem*& item) { GetGlobalLock(); if (worklist.empty()) return false; - s = worklist.front(); + item = worklist.front(); worklist.pop_front(); ReleaseGlobalLock(); @@ -69,13 +78,18 @@ namespace glslang { return true; } + int size() + { + return worklist.size(); + } + bool empty() { return worklist.empty(); } protected: - std::list worklist; + std::list worklist; }; } // end namespace glslang diff --git a/Test/runtests b/Test/runtests index dca678f6..3126fca1 100644 --- a/Test/runtests +++ b/Test/runtests @@ -27,3 +27,12 @@ function runLinkTest { runLinkTest mains1.frag mains2.frag noMain1.geom noMain2.geom runLinkTest noMain.vert mains.frag + +# +# multi-threaded test +# + +echo Comparing single thread to multithread for all tests in current directory... +$EXE -i *.vert *.geom *.frag *.tes* *.comp > singleThread.out +$EXE -i *.vert *.geom *.frag *.tes* *.comp -t > multiThread.out +diff singleThread.out multiThread.out diff --git a/Todo.txt b/Todo.txt index d7c4cbd8..09f9e53f 100644 --- a/Todo.txt +++ b/Todo.txt @@ -1,10 +1,5 @@ Current functionality level: ESSL 3.0 -Performance - -Testing - - thread safety - Link Validation - ensure no static references thrown away Cross-stage linking @@ -21,6 +16,8 @@ Link Validation Intra-stage linking - exactly one main - type consistency check of uniforms, globals, ins, and outs, both variables and blocks + - value checking of global const initializers + - value checking of uniform initializers - location/component/binding/index/offset match check - location/component aliasing (except desktop vertex shader inputs) - location layout range/overlap semantics diff --git a/glslang/Include/Types.h b/glslang/Include/Types.h index 6ff2ba97..e4f60555 100644 --- a/glslang/Include/Types.h +++ b/glslang/Include/Types.h @@ -577,12 +577,14 @@ public: virtual bool isMatrix() const { return matrixCols ? true : false; } virtual bool isArray() const { return arraySizes != 0; } int getArraySize() const { return arraySizes->front(); } - void setArraySizes(TArraySizes s) { + void setArraySizes(TArraySizes s) + { // copy; we don't want distinct types sharing the same descriptor if (! arraySizes) arraySizes = NewPoolTArraySizes(); *arraySizes = *s; } + void changeArraySize(int s) { arraySizes->front() = s; } void setMaxArraySize (int s) { maxArraySize = s; } int getMaxArraySize () const { return maxArraySize; } @@ -590,10 +592,13 @@ public: TType* getArrayInformationType() { return arrayInformationType; } virtual bool isVector() const { return vectorSize > 1; } virtual bool isScalar() const { return vectorSize == 1; } - const char* getBasicString() const { + const char* getBasicString() const + { return TType::getBasicString(basicType); } - static const char* getBasicString(TBasicType t) { + + static const char* getBasicString(TBasicType t) + { switch (t) { case EbtVoid: return "void"; case EbtFloat: return "float"; diff --git a/glslang/MachineIndependent/Initialize.cpp b/glslang/MachineIndependent/Initialize.cpp index 6e65161e..7739d74a 100644 --- a/glslang/MachineIndependent/Initialize.cpp +++ b/glslang/MachineIndependent/Initialize.cpp @@ -1657,11 +1657,11 @@ void IdentifyBuiltIns(int version, EProfile profile, EShLanguage language, TSymb TArraySizes arraySizes = NewPoolTArraySizes(); arraySizes->push_back(resources.maxDrawBuffers); fragData.setArraySizes(arraySizes); - symbolTable.insert(*new TVariable(NewPoolTString("gl_FragData"), fragData)); + symbolTable.insert(*new TVariable(NewPoolTString("gl_FragData"), fragData)); } break; - default: + default: break; } } diff --git a/glslang/MachineIndependent/ParseHelper.cpp b/glslang/MachineIndependent/ParseHelper.cpp index 0fe79081..b03063e2 100644 --- a/glslang/MachineIndependent/ParseHelper.cpp +++ b/glslang/MachineIndependent/ParseHelper.cpp @@ -456,10 +456,10 @@ TIntermTyped* TParseContext::handleVariable(TSourceLoc loc, TSymbol* symbol, TSt { TIntermTyped* node = 0; - TAnonMember* anon = symbol ? symbol->getAsAnonMember() : 0; + const TAnonMember* anon = symbol ? symbol->getAsAnonMember() : 0; if (anon) { // it was a member of an anonymous container, have to insert its dereference - TVariable* variable = anon->getAnonContainer().getAsVariable(); + const TVariable* variable = anon->getAnonContainer().getAsVariable(); TIntermTyped* container = intermediate.addSymbol(variable->getUniqueId(), variable->getName(), variable->getType(), loc); TConstUnion* unionArray = new TConstUnion[1]; unionArray->setUConst(anon->getMemberNumber()); @@ -1084,7 +1084,7 @@ bool TParseContext::lValueErrorCheck(TSourceLoc loc, const char* op, TIntermType } return errorReturn; - default: + default: break; } error(loc, " l-value required", op, "", ""); @@ -1120,7 +1120,7 @@ bool TParseContext::lValueErrorCheck(TSourceLoc loc, const char* op, TIntermType case EbtVoid: message = "can't modify void"; break; - default: + default: break; } } @@ -1237,7 +1237,7 @@ bool TParseContext::constructorError(TSourceLoc loc, TIntermNode* node, TFunctio case EOpConstructDMat4x4: constructingMatrix = true; break; - default: + default: break; } @@ -1626,7 +1626,7 @@ bool TParseContext::insertBuiltInArrayAtGlobalLevel() return false; } - TVariable* variable = symbol->getAsVariable(); + const TVariable* variable = symbol->getAsVariable(); if (! variable) { infoSink.info.message(EPrefixInternalError, "variable expected"); @@ -1730,21 +1730,18 @@ void TParseContext::arrayCheck(TSourceLoc loc, TString& identifier, const TPubli // Don't check for reserved word use until after we know it's not in the symbol table, // because reserved arrays can be redeclared. // + // However, reserved arrays cannot be modified in a shared symbol table, so add a new + // one at a non-shared level in the table. + // - bool sameScope = false; - TSymbol* symbol = symbolTable.find(identifier, 0, &sameScope); - if (symbol == 0 || !sameScope) { + bool currentScope; + TSymbol* symbol = symbolTable.find(identifier, 0, ¤tScope); + if (symbol == 0 || ! currentScope) { if (reservedErrorCheck(loc, identifier)) return; - + variable = new TVariable(&identifier, TType(type)); - - if (! symbolTable.insert(*variable)) { - delete variable; - error(loc, "INTERNAL ERROR inserting new symbol", identifier.c_str(), ""); - - return; - } + symbolTable.insert(*variable); } else { variable = symbol->getAsVariable(); @@ -1777,7 +1774,7 @@ void TParseContext::arrayCheck(TSourceLoc loc, TString& identifier, const TPubli t = t->getArrayInformationType(); } - variable->getType().setArraySizes(type.arraySizes); + variable->getWritableType().setArraySizes(type.arraySizes); } voidErrorCheck(loc, identifier, type); @@ -1797,6 +1794,8 @@ bool TParseContext::arraySetMaxSize(TIntermSymbol *node, TType* type, int size, return true; } + // There are multiple copies of the array type tagging results of operations. + // Chain these together, so they can all reflect the final size. type->setArrayInformationType(variable->getArrayInformationType()); variable->updateArrayInformationType(type); @@ -1816,13 +1815,13 @@ bool TParseContext::arraySetMaxSize(TIntermSymbol *node, TType* type, int size, } } - // we dont want to update the maxArraySize when this flag is not set, we just want to include this - // node type in the chain of node types so that its updated when a higher maxArraySize comes in. - if (!updateFlag) + // We don't want to update the maxArraySize when this flag is not set, we just want to include this + // node type in the chain of node types so that it's updated when a higher maxArraySize comes in. + if (! updateFlag) return false; size++; - variable->getType().setMaxArraySize(size); + variable->getWritableType().setMaxArraySize(size); type->setMaxArraySize(size); TType* tt = type; @@ -1997,8 +1996,7 @@ const TFunction* TParseContext::findFunction(TSourceLoc loc, TFunction* call, bo } // -// Initializers show up in several places in the grammar. Have one set of -// code to handle them here. +// Handle all types of initializers from the grammar. // bool TParseContext::executeInitializerError(TSourceLoc loc, TString& identifier, TPublicType& pType, TIntermTyped* initializer, TIntermNode*& intermNode, TVariable* variable) @@ -2044,13 +2042,13 @@ bool TParseContext::executeInitializerError(TSourceLoc loc, TString& identifier, if (qualifier == EvqConst) { if (qualifier != initializer->getType().getQualifier().storage) { error(loc, " assigning non-constant to", "=", "'%s'", variable->getType().getCompleteString().c_str()); - variable->getType().getQualifier().storage = EvqTemporary; + variable->getWritableType().getQualifier().storage = EvqTemporary; return true; } if (type != initializer->getType()) { error(loc, " non-matching types for const initializer ", variable->getType().getStorageQualifierString(), ""); - variable->getType().getQualifier().storage = EvqTemporary; + variable->getWritableType().getQualifier().storage = EvqTemporary; return true; } if (initializer->getAsConstantUnion()) { @@ -2063,7 +2061,7 @@ bool TParseContext::executeInitializerError(TSourceLoc loc, TString& identifier, } } else if (initializer->getAsSymbolNode()) { TSymbol* symbol = symbolTable.find(initializer->getAsSymbolNode()->getName()); - if (TVariable* tVar = symbol->getAsVariable()) { + if (const TVariable* tVar = symbol->getAsVariable()) { TConstUnion* constArray = tVar->getConstUnionPointer(); variable->shareConstPointer(constArray); } else { @@ -2072,7 +2070,7 @@ bool TParseContext::executeInitializerError(TSourceLoc loc, TString& identifier, } } else { error(loc, " cannot assign to", "=", "'%s'", variable->getType().getCompleteString().c_str()); - variable->getType().getQualifier().storage = EvqTemporary; + variable->getWritableType().getQualifier().storage = EvqTemporary; return true; } } @@ -2372,7 +2370,8 @@ void TParseContext::addBlock(TSourceLoc loc, TTypeList& typeList, const TString* // For an identifier that is already declared, add more qualification to it. void TParseContext::addQualifierToExisting(TSourceLoc loc, TQualifier qualifier, const TString& identifier) { - TSymbol* existing = symbolTable.find(identifier); + bool sharedLevel; + TSymbol* existing = symbolTable.find(identifier, 0, 0, &sharedLevel); TVariable* variable = existing ? existing->getAsVariable() : 0; if (! variable) { error(loc, "identifier not previously declared", identifier.c_str(), ""); @@ -2390,8 +2389,16 @@ void TParseContext::addQualifierToExisting(TSourceLoc loc, TQualifier qualifier, return; } + // + // Don't change a shared variable; rather add a new one at the current scope. + // + if (sharedLevel) { + variable = new TVariable(&variable->getName(), variable->getType()); + symbolTable.insert(*variable); + } + if (qualifier.invariant) - variable->getType().getQualifier().invariant = true; + variable->getWritableType().getQualifier().invariant = true; } void TParseContext::addQualifierToExisting(TSourceLoc loc, TQualifier qualifier, TIdentifierList& identifiers) diff --git a/glslang/MachineIndependent/Scan.cpp b/glslang/MachineIndependent/Scan.cpp index b37c50cb..e6232fbe 100644 --- a/glslang/MachineIndependent/Scan.cpp +++ b/glslang/MachineIndependent/Scan.cpp @@ -851,7 +851,7 @@ int TScanContext::tokenizeIdentifier() return identifierOrReserved(reserved); } - default: + default: parseContext.infoSink.info.message(EPrefixInternalError, "Unknown glslang keyword", loc); return 0; } @@ -868,7 +868,7 @@ int TScanContext::identifierOrType() parserToken->sType.lex.symbol = parseContext.symbolTable.find(*parserToken->sType.lex.string); if (afterType == false && parserToken->sType.lex.symbol) { - if (TVariable* variable = parserToken->sType.lex.symbol->getAsVariable()) { + if (const TVariable* variable = parserToken->sType.lex.symbol->getAsVariable()) { if (variable->isUserType()) { afterType = true; diff --git a/glslang/MachineIndependent/ShaderLang.cpp b/glslang/MachineIndependent/ShaderLang.cpp index fa2117ac..1b1b98d2 100644 --- a/glslang/MachineIndependent/ShaderLang.cpp +++ b/glslang/MachineIndependent/ShaderLang.cpp @@ -100,6 +100,9 @@ TSymbolTable* SharedSymbolTables[VersionCount][EProfileCount][EShLangCount] = {} TPoolAllocator* PerProcessGPA = 0; +// +// Parse and add to the given symbol table the content of the given shader string. +// bool InitializeSymbolTable(const TString& builtIns, int version, EProfile profile, EShLanguage language, TInfoSink& infoSink, TSymbolTable& symbolTable) { @@ -111,9 +114,6 @@ bool InitializeSymbolTable(const TString& builtIns, int version, EProfile profil parseContext.setScanContext(&scanContext); parseContext.setPpContext(&ppContext); - // - // Parse the built-ins. This should only happen once per - // language symbol table when no 'resources' are passed in. // // Push the symbol table to give it an initial scope. This // push should not have a corresponding pop, so that built-ins @@ -137,44 +137,48 @@ bool InitializeSymbolTable(const TString& builtIns, int version, EProfile profil return true; } -// -// 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(EProfile profile, EShLanguage language) { - int commonIndex = EPcGeneral; - if (profile == EEsProfile && language == EShLangFragment) - commonIndex = EPcFragment; + return (profile == EEsProfile && language == EShLangFragment) ? EPcFragment : EPcGeneral; +} - (*symbolTables[language]).adoptLevels(*commonTable[commonIndex]); +// +// To initialize per-stage shared tables, with the common table already complete. +// +void InitializeStageSymbolTable(TBuiltIns& builtIns, int version, EProfile profile, EShLanguage language, TInfoSink& infoSink, TSymbolTable** commonTable, TSymbolTable** symbolTables) +{ + (*symbolTables[language]).adoptLevels(*commonTable[CommonIndex(profile, language)]); 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) +// +// Initialize the full set of shareable symbol tables; +// The common (cross-stage) and those sharable per-stage. +// +bool InitializeSymbolTables(TInfoSink& infoSink, TSymbolTable** commonTable, TSymbolTable** symbolTables, int version, EProfile profile) { TBuiltIns builtIns; - builtIns.initialize(version, profile); - // do the common table + // do the common tables 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); + InitializeStageSymbolTable(builtIns, version, profile, EShLangVertex, infoSink, commonTable, symbolTables); + InitializeStageSymbolTable(builtIns, version, profile, EShLangFragment, infoSink, commonTable, symbolTables); if (profile != EEsProfile && version >= 400) { - InitializeSymbolTable(builtIns, version, profile, EShLangTessControl, infoSink, commonTable, symbolTables); - InitializeSymbolTable(builtIns, version, profile, EShLangTessEvaluation, infoSink, commonTable, symbolTables); + InitializeStageSymbolTable(builtIns, version, profile, EShLangTessControl, infoSink, commonTable, symbolTables); + InitializeStageSymbolTable(builtIns, version, profile, EShLangTessEvaluation, infoSink, commonTable, symbolTables); } if (profile != EEsProfile && version >= 150) - InitializeSymbolTable(builtIns, version, profile, EShLangGeometry, infoSink, commonTable, symbolTables); + InitializeStageSymbolTable(builtIns, version, profile, EShLangGeometry, infoSink, commonTable, symbolTables); if (profile != EEsProfile && version >= 430) - InitializeSymbolTable(builtIns, version, profile, EShLangCompute, infoSink, commonTable, symbolTables); + InitializeStageSymbolTable(builtIns, version, profile, EShLangCompute, infoSink, commonTable, symbolTables); return true; } @@ -222,7 +226,7 @@ void SetupBuiltinSymbolTable(int version, EProfile profile) TPoolAllocator* builtInPoolAllocator = new TPoolAllocator(); SetThreadPoolAllocator(*builtInPoolAllocator); - // Dynamically allocate the symbol tables so we can control when they are deallocated WRT the pool. + // Dynamically allocate the local symbol tables so we can control when they are deallocated WRT when the pool is popped. TSymbolTable* commonTable[EPcCount]; TSymbolTable* stageTables[EShLangCount]; for (int precClass = 0; precClass < EPcCount; ++precClass) @@ -231,7 +235,7 @@ void SetupBuiltinSymbolTable(int version, EProfile profile) stageTables[stage] = new TSymbolTable; // Generate the local symbol tables using the new pool - GenerateBuiltInSymbolTable(infoSink, commonTable, stageTables, version, profile); + InitializeSymbolTables(infoSink, commonTable, stageTables, version, profile); // Switch to the process-global pool SetThreadPoolAllocator(*PerProcessGPA); @@ -241,12 +245,15 @@ void SetupBuiltinSymbolTable(int version, EProfile profile) if (! commonTable[precClass]->isEmpty()) { CommonSymbolTable[versionIndex][profile][precClass] = new TSymbolTable; CommonSymbolTable[versionIndex][profile][precClass]->copyTable(*commonTable[precClass]); + CommonSymbolTable[versionIndex][profile][precClass]->readOnly(); } } for (int stage = 0; stage < EShLangCount; ++stage) { if (! stageTables[stage]->isEmpty()) { SharedSymbolTables[versionIndex][profile][stage] = new TSymbolTable; + SharedSymbolTables[versionIndex][profile][stage]->adoptLevels(*CommonSymbolTable[versionIndex][profile][CommonIndex(profile, (EShLanguage)stage)]); SharedSymbolTables[versionIndex][profile][stage]->copyTable(*stageTables[stage]); + SharedSymbolTables[versionIndex][profile][stage]->readOnly(); } } diff --git a/glslang/MachineIndependent/SymbolTable.cpp b/glslang/MachineIndependent/SymbolTable.cpp index b6e4ced6..8fd892b1 100644 --- a/glslang/MachineIndependent/SymbolTable.cpp +++ b/glslang/MachineIndependent/SymbolTable.cpp @@ -207,6 +207,15 @@ void TSymbolTableLevel::relateToOperator(const char* name, TOperator op) } } +// +// Make all symbols in this table level read only. +// +void TSymbolTableLevel::readOnly() +{ + for (tLevel::iterator it = level.begin(); it != level.end(); ++it) + (*it).second->readOnly(); +} + TSymbol::TSymbol(const TSymbol& copyOf) { name = NewPoolTString(copyOf.name->c_str()); @@ -279,10 +288,12 @@ TSymbolTableLevel* TSymbolTableLevel::clone(TStructureMap& remapper) void TSymbolTable::copyTable(const TSymbolTable& copyOf) { + assert(adoptedLevels == copyOf.adoptedLevels); + TStructureMap remapper; uniqueId = copyOf.uniqueId; noBuiltInRedeclarations = copyOf.noBuiltInRedeclarations; - for (unsigned int i = 0; i < copyOf.table.size(); ++i) + for (unsigned int i = copyOf.adoptedLevels; i < copyOf.table.size(); ++i) table.push_back(copyOf.table[i]->clone(remapper)); } diff --git a/glslang/MachineIndependent/SymbolTable.h b/glslang/MachineIndependent/SymbolTable.h index b885b8eb..2a0352c8 100644 --- a/glslang/MachineIndependent/SymbolTable.h +++ b/glslang/MachineIndependent/SymbolTable.h @@ -80,7 +80,7 @@ class TAnonMember; class TSymbol { public: POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator()) - explicit TSymbol(const TString *n) : name(n) { } + explicit TSymbol(const TString *n) : name(n), writable(true) { } virtual TSymbol* clone(TStructureMap& remapper) = 0; virtual ~TSymbol() { } @@ -88,18 +88,28 @@ public: void changeName(const char* buf) { name = new TString(buf); } virtual const TString& getMangledName() const { return getName(); } virtual TFunction* getAsFunction() { return 0; } + virtual const TFunction* getAsFunction() const { return 0; } virtual TVariable* getAsVariable() { return 0; } - virtual TAnonMember* getAsAnonMember() { return 0; } + virtual const TVariable* getAsVariable() const { return 0; } + virtual const TAnonMember* getAsAnonMember() const { return 0; } void setUniqueId(int id) { uniqueId = id; } int getUniqueId() const { return uniqueId; } virtual void dump(TInfoSink &infoSink) const = 0; + void readOnly() { writable = false; } + protected: explicit TSymbol(const TSymbol&); TSymbol& operator=(const TSymbol&); const TString *name; unsigned int uniqueId; // For cross-scope comparing during code generation + + // + // N.B.: Non-const functions that will be generally used should assert an this, + // to avoid overwriting shared symbol-table information. + // + bool writable; }; // @@ -119,12 +129,12 @@ public: virtual ~TVariable() { } virtual TVariable* getAsVariable() { return this; } - TType& getType() { return type; } + virtual const TVariable* getAsVariable() const { return this; } + TType& getWritableType() { assert(writable); return type; } const TType& getType() const { return type; } bool isUserType() const { return userType; } - void setStorageQualifier(TStorageQualifier qualifier) { type.getQualifier().storage = qualifier; } - void updateArrayInformationType(TType *t) { arrayInformationType = t; } - TType* getArrayInformationType() { return arrayInformationType; } + void updateArrayInformationType(TType *t) { assert(writable); arrayInformationType = t; } + TType* getArrayInformationType() { assert(writable); return arrayInformationType; } virtual void dump(TInfoSink &infoSink) const; @@ -193,22 +203,24 @@ public: virtual ~TFunction(); virtual TFunction* getAsFunction() { return this; } + virtual const TFunction* getAsFunction() const { return this; } void addParameter(TParameter& p) { + assert(writable); parameters.push_back(p); p.type->appendMangledName(mangledName); } const TString& getMangledName() const { return mangledName; } const TType& getReturnType() const { return returnType; } - void relateToOperator(TOperator o) { op = o; } + void relateToOperator(TOperator o) { assert(writable); op = o; } TOperator getBuiltInOp() const { return op; } - void setDefined() { defined = true; } - bool isDefined() { return defined; } + void setDefined() { assert(writable); defined = true; } + bool isDefined() const { return defined; } int getParamCount() const { return static_cast(parameters.size()); } - TParameter& operator [](int i) { return parameters[i]; } + TParameter& operator [](int i) { assert(writable); return parameters[i]; } const TParameter& operator [](int i) const { return parameters[i]; } virtual void dump(TInfoSink &infoSink) const; @@ -232,7 +244,7 @@ public: virtual TAnonMember* clone(TStructureMap& remapper); virtual ~TAnonMember() { } - TAnonMember* getAsAnonMember() { return this; } + const TAnonMember* getAsAnonMember() const { return this; } TSymbol& getAnonContainer() const { return anonContainer; } unsigned int getMemberNumber() const { return memberNumber; } virtual void dump(TInfoSink &infoSink) const; @@ -266,7 +278,7 @@ public: symbol.changeName(buf); bool isOkay = true; - TTypeList& types = *symbol.getAsVariable()->getType().getStruct(); + const TTypeList& types = *symbol.getAsVariable()->getType().getStruct(); for (unsigned int m = 0; m < types.size(); ++m) { TAnonMember* member = new TAnonMember(&types[m].type->getFieldName(), m, symbol); result = level.insert(tLevelPair(member->getMangledName(), member)); @@ -351,6 +363,7 @@ public: void relateToOperator(const char* name, TOperator op); void dump(TInfoSink &infoSink) const; TSymbolTableLevel* clone(TStructureMap& remapper); + void readOnly(); protected: explicit TSymbolTableLevel(TSymbolTableLevel&); @@ -397,15 +410,17 @@ public: // 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 + // 2: built-ins specific to a compile, like resources that are context-dependent, or redeclared built-ins // 3: user-shader globals // protected: - static const int globalLevel = 3; + bool isSharedLevel(int level) { return level <= 1; } // exclude all per-compile levels + bool isBuiltInLevel(int level) { return level <= 2; } // exclude user globals + bool isGlobalLevel(int level) { return level <= 3; } // include user globals public: bool isEmpty() { return table.size() == 0; } - bool atBuiltInLevel() { return table.size() <= globalLevel; } // exclude user globals - bool atGlobalLevel() { return table.size() <= globalLevel + 1; } // include user globals + bool atBuiltInLevel() { return isBuiltInLevel(currentLevel()); } + bool atGlobalLevel() { return isGlobalLevel(currentLevel()); } void setNoBuiltInRedeclarations() { noBuiltInRedeclarations = true; } @@ -440,7 +455,7 @@ public: return table[currentLevel()]->insert(symbol); } - TSymbol* find(const TString& name, bool* builtIn = 0, bool *sameScope = 0) + TSymbol* find(const TString& name, bool* builtIn = 0, bool *currentScope = 0, bool *sharedLevel = 0) { int level = currentLevel(); TSymbol* symbol; @@ -450,9 +465,11 @@ public: } while (symbol == 0 && level >= 0); level++; if (builtIn) - *builtIn = level < 2; - if (sameScope) - *sameScope = level == currentLevel(); + *builtIn = isBuiltInLevel(level); + if (currentScope) + *currentScope = level == currentLevel(); + if (sharedLevel) + *sharedLevel = isSharedLevel(level); return symbol; } @@ -469,6 +486,12 @@ public: void setPreviousDefaultPrecisions(TPrecisionQualifier *p) { table[currentLevel()]->setPreviousDefaultPrecisions(p); } + void readOnly() + { + for (unsigned int level = 0; level < table.size(); ++level) + table[level]->readOnly(); + } + protected: TSymbolTable(TSymbolTable&); TSymbolTable& operator=(TSymbolTableLevel&); diff --git a/glslang/MachineIndependent/glslang.y b/glslang/MachineIndependent/glslang.y index 609b864f..3de7e724 100644 --- a/glslang/MachineIndependent/glslang.y +++ b/glslang/MachineIndependent/glslang.y @@ -807,7 +807,7 @@ function_prototype TSymbol* symbol = parseContext.symbolTable.find($1->getMangledName(), &builtIn); if (symbol && symbol->getAsFunction() && builtIn) parseContext.requireProfile($2.loc, static_cast(~EEsProfileMask), "redeclaration of built-in function"); - TFunction* prevDec = symbol ? symbol->getAsFunction() : 0; + const TFunction* prevDec = symbol ? symbol->getAsFunction() : 0; if (prevDec) { if (prevDec->getReturnType() != $1->getReturnType()) { parseContext.error($2.loc, "overloaded functions must have the same return type", $1->getReturnType().getCompleteTypeString().c_str(), ""); @@ -2041,7 +2041,7 @@ type_specifier_nonarray // This is for user defined type names. The lexical phase looked up the // type. // - if (TVariable* variable = ($1.symbol)->getAsVariable()) { + if (const TVariable* variable = ($1.symbol)->getAsVariable()) { const TType& structure = variable->getType(); $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); $$.basicType = EbtStruct; diff --git a/glslang/Public/ShaderLang.h b/glslang/Public/ShaderLang.h index 7b261d42..0e41f1ee 100644 --- a/glslang/Public/ShaderLang.h +++ b/glslang/Public/ShaderLang.h @@ -286,7 +286,7 @@ protected: friend class TProgram; private: - void operator=(TShader&); + TShader& operator=(TShader&); }; class TProgram { @@ -306,7 +306,7 @@ protected: TInfoSink* infoSink; private: - void operator=(TProgram&); + TProgram& operator=(TProgram&); }; } // end namespace glslang