Ensure the shared symbol table levels are read-only to make multi-threading safe...
authorJohn Kessenich <cepheus@frii.com>
Fri, 6 Sep 2013 19:52:57 +0000 (19:52 +0000)
committerJohn Kessenich <cepheus@frii.com>
Fri, 6 Sep 2013 19:52:57 +0000 (19:52 +0000)
git-svn-id: https://cvs.khronos.org/svn/repos/ogl/trunk/ecosystem/public/sdk/tools/glslang@22939 e7fa87d3-cd2b-0410-9028-fcbf551c1848

14 files changed:
Install/Windows/glslangValidator.exe
StandAlone/StandAlone.cpp
StandAlone/Worklist.h
Test/runtests
Todo.txt
glslang/Include/Types.h
glslang/MachineIndependent/Initialize.cpp
glslang/MachineIndependent/ParseHelper.cpp
glslang/MachineIndependent/Scan.cpp
glslang/MachineIndependent/ShaderLang.cpp
glslang/MachineIndependent/SymbolTable.cpp
glslang/MachineIndependent/SymbolTable.h
glslang/MachineIndependent/glslang.y
glslang/Public/ShaderLang.h

index 52f8104..b31e3fc 100644 (file)
Binary files a/Install/Windows/glslangValidator.exe and b/Install/Windows/glslangValidator.exe differ
index 3b26488..8203ad5 100644 (file)
@@ -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
     //
-
-    // 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 & EOptionsLinkProgram)
+        CompileAndLinkShaders();
+    else {
+        bool printShaderNames = Worklist.size() > 1;
+
+        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
index e5c35bd..1bcc7e1 100644 (file)
 
 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<std::string> worklist;
+        std::list<TWorkItem*> worklist;
     };
 
 } // end namespace glslang
index dca678f..3126fca 100644 (file)
@@ -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
index d7c4cbd..09f9e53 100644 (file)
--- 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
index 6ff2ba9..e4f6055 100644 (file)
@@ -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";
index 6e65161..7739d74 100644 (file)
@@ -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;
     }
 }
index 0fe7908..b03063e 100644 (file)
@@ -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, &currentScope);
+    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;
-        }
+        variable = new TVariable(&identifier, TType(type));
+        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)
index b37c50c..e6232fb 100644 (file)
@@ -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;
 
index fa2117a..1b1b98d 100644 (file)
@@ -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)
 {
@@ -112,9 +115,6 @@ bool InitializeSymbolTable(const TString& builtIns, int version, EProfile profil
     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
     // are preserved, and the test for an empty table fails.
@@ -137,44 +137,48 @@ bool InitializeSymbolTable(const TString& builtIns, int version, EProfile profil
     return true;
 }
 
+int CommonIndex(EProfile profile, EShLanguage language)
+{
+    return (profile == EEsProfile && language == EShLangFragment) ? EPcFragment : EPcGeneral;
+}
+
 //
-// To call for per-stage initialization, with the common table already complete.
+// To initialize per-stage shared tables, with the common table already complete.
 //
-void InitializeSymbolTable(TBuiltIns& builtIns, int version, EProfile profile, EShLanguage language, TInfoSink& infoSink, TSymbolTable** commonTable, TSymbolTable** symbolTables)
+void InitializeStageSymbolTable(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]);
+    (*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();
         }    
     }
 
index b6e4ced..8fd892b 100644 (file)
@@ -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));
 }
 
index b885b8e..2a0352c 100644 (file)
@@ -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<int>(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&);
index 609b864..3de7e72 100644 (file)
@@ -807,7 +807,7 @@ function_prototype
         TSymbol* symbol = parseContext.symbolTable.find($1->getMangledName(), &builtIn);\r
         if (symbol && symbol->getAsFunction() && builtIn)\r
             parseContext.requireProfile($2.loc, static_cast<EProfileMask>(~EEsProfileMask), "redeclaration of built-in function");\r
-        TFunction* prevDec = symbol ? symbol->getAsFunction() : 0;\r
+        const TFunction* prevDec = symbol ? symbol->getAsFunction() : 0;\r
         if (prevDec) {\r
             if (prevDec->getReturnType() != $1->getReturnType()) {\r
                 parseContext.error($2.loc, "overloaded functions must have the same return type", $1->getReturnType().getCompleteTypeString().c_str(), "");\r
@@ -2041,7 +2041,7 @@ type_specifier_nonarray
         // This is for user defined type names.  The lexical phase looked up the\r
         // type.\r
         //\r
-        if (TVariable* variable = ($1.symbol)->getAsVariable()) {\r
+        if (const TVariable* variable = ($1.symbol)->getAsVariable()) {\r
             const TType& structure = variable->getType();\r
             $$.init($1.loc, parseContext.symbolTable.atGlobalLevel());\r
             $$.basicType = EbtStruct;\r
index 7b261d4..0e41f1e 100644 (file)
@@ -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