Merge pull request #2532 from juliusikkala/master

Pass environment through PreprocessDeferred
diff --git a/SPIRV/GlslangToSpv.cpp b/SPIRV/GlslangToSpv.cpp
index 5ad2d33..8528955 100644
--- a/SPIRV/GlslangToSpv.cpp
+++ b/SPIRV/GlslangToSpv.cpp
@@ -257,15 +257,15 @@
     spv::Id nonSemanticDebugPrintf;
     std::unordered_map<const char*, spv::Id> extBuiltinMap;
 
-    std::unordered_map<int, spv::Id> symbolValues;
-    std::unordered_set<int> rValueParameters;  // set of formal function parameters passed as rValues,
+    std::unordered_map<long long, spv::Id> symbolValues;
+    std::unordered_set<long long> rValueParameters;  // set of formal function parameters passed as rValues,
                                                // rather than a pointer
     std::unordered_map<std::string, spv::Function*> functionMap;
     std::unordered_map<const glslang::TTypeList*, spv::Id> structMap[glslang::ElpCount][glslang::ElmCount];
     // for mapping glslang block indices to spv indices (e.g., due to hidden members):
-    std::unordered_map<int, std::vector<int>> memberRemapper;
+    std::unordered_map<long long, std::vector<int>> memberRemapper;
     // for mapping glslang symbol struct to symbol Id
-    std::unordered_map<const glslang::TTypeList*, int> glslangTypeToIdMap;
+    std::unordered_map<const glslang::TTypeList*, long long> glslangTypeToIdMap;
     std::stack<bool> breakForLoop;  // false means break for switch
     std::unordered_map<std::string, const glslang::TIntermSymbol*> counterOriginator;
     // Map pointee types for EbtReference to their forward pointers
@@ -1994,7 +1994,7 @@
                 {
                     // This may be, e.g., an anonymous block-member selection, which generally need
                     // index remapping due to hidden members in anonymous blocks.
-                    int glslangId = glslangTypeToIdMap[node->getLeft()->getType().getStruct()];
+                    long long glslangId = glslangTypeToIdMap[node->getLeft()->getType().getStruct()];
                     if (memberRemapper.find(glslangId) != memberRemapper.end()) {
                         std::vector<int>& remapper = memberRemapper[glslangId];
                         assert(remapper.size() > 0);
diff --git a/Test/baseResults/link.vk.multiBlocksValid.1.0.geom.out b/Test/baseResults/link.vk.multiBlocksValid.1.0.geom.out
index c0b33b7..b0456a0 100644
--- a/Test/baseResults/link.vk.multiBlocksValid.1.0.geom.out
+++ b/Test/baseResults/link.vk.multiBlocksValid.1.0.geom.out
@@ -328,14 +328,14 @@
                               Decorate 59(Vertex) Block
                               Decorate 61(oV) Location 0
                               Decorate 64(Vertex) Block
-                              Decorate 68(iV) Location 1
+                              Decorate 68(iV) Location 0
                               MemberDecorate 95(BufferBlock) 0 ColMajor
                               MemberDecorate 95(BufferBlock) 0 Offset 0
                               MemberDecorate 95(BufferBlock) 0 MatrixStride 16
                               Decorate 95(BufferBlock) BufferBlock
                               Decorate 97(uBuf) DescriptorSet 0
                               Decorate 97(uBuf) Binding 1
-                              Decorate 100(P) Location 0
+                              Decorate 100(P) Location 2
                2:             TypeVoid
                3:             TypeFunction 2
                6:             TypeFloat 32
diff --git a/Test/baseResults/spv.specConstant.vert.out b/Test/baseResults/spv.specConstant.vert.out
index 76f3de6..f7d4381 100644
--- a/Test/baseResults/spv.specConstant.vert.out
+++ b/Test/baseResults/spv.specConstant.vert.out
@@ -11,7 +11,7 @@
                               Source GLSL 400
                               Name 4  "main"
                               Name 9  "arraySize"
-                              Name 14  "foo(vf4[s805310914];"
+                              Name 14  "foo(vf4[s216172782];"
                               Name 13  "p"
                               Name 17  "builtin_spec_constant("
                               Name 20  "color"
@@ -106,10 +106,10 @@
                               Store 20(color) 46
               48:          10 Load 22(ucol)
                               Store 47(param) 48
-              49:           2 FunctionCall 14(foo(vf4[s805310914];) 47(param)
+              49:           2 FunctionCall 14(foo(vf4[s216172782];) 47(param)
                               Return
                               FunctionEnd
-14(foo(vf4[s805310914];):           2 Function None 12
+14(foo(vf4[s216172782];):           2 Function None 12
            13(p):     11(ptr) FunctionParameter
               15:             Label
               54:     24(ptr) AccessChain 53(dupUcol) 23
diff --git a/glslang/HLSL/hlslParseHelper.cpp b/glslang/HLSL/hlslParseHelper.cpp
index fce647a..63dd8e3 100644
--- a/glslang/HLSL/hlslParseHelper.cpp
+++ b/glslang/HLSL/hlslParseHelper.cpp
@@ -1376,7 +1376,7 @@
 
     return flattened ? flattened : base;
 }
-TIntermTyped* HlslParseContext::flattenAccess(int uniqueId, int member, TStorageQualifier outerStorage,
+TIntermTyped* HlslParseContext::flattenAccess(long long uniqueId, int member, TStorageQualifier outerStorage,
     const TType& dereferencedType, int subset)
 {
     const auto flattenData = flattenMap.find(uniqueId);
@@ -1444,7 +1444,7 @@
 };
 
 // Find and return the split IO TVariable for id, or nullptr if none.
-TVariable* HlslParseContext::getSplitNonIoVar(int id) const
+TVariable* HlslParseContext::getSplitNonIoVar(long long id) const
 {
     const auto splitNonIoVar = splitNonIoVars.find(id);
     if (splitNonIoVar == splitNonIoVars.end())
@@ -3256,7 +3256,7 @@
         // shadow state.  This depends on downstream optimization to
         // DCE one variant in [shadow, nonshadow] if both are present,
         // or the SPIR-V module would be invalid.
-        int newId = texSymbol->getId();
+        long long newId = texSymbol->getId();
 
         // Check to see if this texture has been given a shadow mode already.
         // If so, look up the one we already have.
diff --git a/glslang/HLSL/hlslParseHelper.h b/glslang/HLSL/hlslParseHelper.h
index b92856a..2d7165c 100644
--- a/glslang/HLSL/hlslParseHelper.h
+++ b/glslang/HLSL/hlslParseHelper.h
@@ -253,12 +253,12 @@
 
     // Array and struct flattening
     TIntermTyped* flattenAccess(TIntermTyped* base, int member);
-    TIntermTyped* flattenAccess(int uniqueId, int member, TStorageQualifier outerStorage, const TType&, int subset = -1);
+    TIntermTyped* flattenAccess(long long uniqueId, int member, TStorageQualifier outerStorage, const TType&, int subset = -1);
     int findSubtreeOffset(const TIntermNode&) const;
     int findSubtreeOffset(const TType&, int subset, const TVector<int>& offsets) const;
     bool shouldFlatten(const TType&, TStorageQualifier, bool topLevel) const;
     bool wasFlattened(const TIntermTyped* node) const;
-    bool wasFlattened(int id) const { return flattenMap.find(id) != flattenMap.end(); }
+    bool wasFlattened(long long id) const { return flattenMap.find(id) != flattenMap.end(); }
     int  addFlattenedMember(const TVariable&, const TType&, TFlattenData&, const TString& name, bool linkage,
                             const TQualifier& outerQualifier, const TArraySizes* builtInArraySizes);
 
@@ -267,8 +267,8 @@
     void splitBuiltIn(const TString& baseName, const TType& memberType, const TArraySizes*, const TQualifier&);
     const TType& split(const TType& type, const TString& name, const TQualifier&);
     bool wasSplit(const TIntermTyped* node) const;
-    bool wasSplit(int id) const { return splitNonIoVars.find(id) != splitNonIoVars.end(); }
-    TVariable* getSplitNonIoVar(int id) const;
+    bool wasSplit(long long id) const { return splitNonIoVars.find(id) != splitNonIoVars.end(); }
+    TVariable* getSplitNonIoVar(long long id) const;
     void addPatchConstantInvocation();
     void fixTextureShadowModes();
     void finalizeAppendMethods();
@@ -386,7 +386,7 @@
     //
     TVector<TSymbol*> ioArraySymbolResizeList;
 
-    TMap<int, TFlattenData> flattenMap;
+    TMap<long long, TFlattenData> flattenMap;
 
     // IO-type map. Maps a pure symbol-table form of a structure-member list into
     // each of the (up to) three kinds of IO, as each as different allowed decorations,
@@ -399,7 +399,7 @@
     TMap<const TTypeList*, tIoKinds> ioTypeMap;
 
     // Structure splitting data:
-    TMap<int, TVariable*> splitNonIoVars;  // variables with the built-in interstage IO removed, indexed by unique ID.
+    TMap<long long, TVariable*> splitNonIoVars;  // variables with the built-in interstage IO removed, indexed by unique ID.
 
     // Structuredbuffer shared types.  Typically there are only a few.
     TVector<TType*> structBufferTypes;
@@ -488,18 +488,18 @@
     struct tShadowTextureSymbols {
         tShadowTextureSymbols() { symId.fill(-1); }
 
-        void set(bool shadow, int id) { symId[int(shadow)] = id; }
-        int get(bool shadow) const { return symId[int(shadow)]; }
+        void set(bool shadow, long long id) { symId[int(shadow)] = id; }
+        long long get(bool shadow) const { return symId[int(shadow)]; }
 
         // True if this texture has been seen with both shadow and non-shadow modes
         bool overloaded() const { return symId[0] != -1 && symId[1] != -1; }
-        bool isShadowId(int id) const { return symId[1] == id; }
+        bool isShadowId(long long id) const { return symId[1] == id; }
 
     private:
-        std::array<int, 2> symId;
+        std::array<long long, 2> symId;
     };
 
-    TMap<int, tShadowTextureSymbols*> textureShadowVariant;
+    TMap<long long, tShadowTextureSymbols*> textureShadowVariant;
     bool parsingEntrypointParameters;
 };
 
diff --git a/glslang/Include/intermediate.h b/glslang/Include/intermediate.h
index 19cd32e..85edd63 100644
--- a/glslang/Include/intermediate.h
+++ b/glslang/Include/intermediate.h
@@ -1275,15 +1275,15 @@
     // if symbol is initialized as symbol(sym), the memory comes from the pool allocator of sym. If sym comes from
     // per process threadPoolAllocator, then it causes increased memory usage per compile
     // it is essential to use "symbol = sym" to assign to symbol
-    TIntermSymbol(int i, const TString& n, const TType& t)
+    TIntermSymbol(long long i, const TString& n, const TType& t)
         : TIntermTyped(t), id(i),
 #ifndef GLSLANG_WEB
         flattenSubset(-1),
 #endif
         constSubtree(nullptr)
           { name = n; }
-    virtual int getId() const { return id; }
-    virtual void changeId(int i) { id = i; }
+    virtual long long getId() const { return id; }
+    virtual void changeId(long long i) { id = i; }
     virtual const TString& getName() const { return name; }
     virtual void traverse(TIntermTraverser*);
     virtual       TIntermSymbol* getAsSymbolNode()       { return this; }
@@ -1301,10 +1301,10 @@
 
     // This is meant for cases where a node has already been constructed, and
     // later on, it becomes necessary to switch to a different symbol.
-    virtual void switchId(int newId) { id = newId; }
+    virtual void switchId(long long newId) { id = newId; }
 
 protected:
-    int id;                      // the unique id of the symbol this node represents
+    long long id;                // the unique id of the symbol this node represents
 #ifndef GLSLANG_WEB
     int flattenSubset;           // how deeply the flattened object rooted at id has been dereferenced
 #endif
diff --git a/glslang/MachineIndependent/Intermediate.cpp b/glslang/MachineIndependent/Intermediate.cpp
index b7ad87a..cadf6f3 100644
--- a/glslang/MachineIndependent/Intermediate.cpp
+++ b/glslang/MachineIndependent/Intermediate.cpp
@@ -65,7 +65,7 @@
 // Returns the added node.
 //
 
-TIntermSymbol* TIntermediate::addSymbol(int id, const TString& name, const TType& type, const TConstUnionArray& constArray,
+TIntermSymbol* TIntermediate::addSymbol(long long id, const TString& name, const TType& type, const TConstUnionArray& constArray,
                                         TIntermTyped* constSubtree, const TSourceLoc& loc)
 {
     TIntermSymbol* node = new TIntermSymbol(id, name, type);
diff --git a/glslang/MachineIndependent/ParseHelper.cpp b/glslang/MachineIndependent/ParseHelper.cpp
index 8927b82..cba242a 100644
--- a/glslang/MachineIndependent/ParseHelper.cpp
+++ b/glslang/MachineIndependent/ParseHelper.cpp
@@ -4296,8 +4296,10 @@
         // If it wasn't at a built-in level, then it's already been redeclared;
         // that is, this is a redeclaration of a redeclaration; reuse that initial
         // redeclaration.  Otherwise, make the new one.
-        if (builtIn)
+        if (builtIn) {
             makeEditable(symbol);
+            symbolTable.amendSymbolIdLevel(*symbol);
+        }
 
         // Now, modify the type of the copy, as per the type of the current redeclaration.
 
@@ -4826,7 +4828,7 @@
     }
 
     // get the unique id of the loop index
-    int loopIndex = binaryInit->getLeft()->getAsSymbolNode()->getId();
+    long long loopIndex = binaryInit->getLeft()->getAsSymbolNode()->getId();
     inductiveLoopIds.insert(loopIndex);
 
     // condition's form must be "loop-index relational-operator constant-expression"
diff --git a/glslang/MachineIndependent/ParseHelper.h b/glslang/MachineIndependent/ParseHelper.h
index 4093fbf..ad9b8d6 100644
--- a/glslang/MachineIndependent/ParseHelper.h
+++ b/glslang/MachineIndependent/ParseHelper.h
@@ -67,7 +67,7 @@
 class TScanContext;
 class TPpContext;
 
-typedef std::set<int> TIdSetType;
+typedef std::set<long long> TIdSetType;
 typedef std::map<const TTypeList*, std::map<size_t, const TTypeList*>> TStructRecord;
 
 //
@@ -392,7 +392,7 @@
     void arrayLimitCheck(const TSourceLoc&, const TString&, int size);
     void limitCheck(const TSourceLoc&, int value, const char* limit, const char* feature);
 
-    void inductiveLoopBodyCheck(TIntermNode*, int loopIndexId, TSymbolTable&);
+    void inductiveLoopBodyCheck(TIntermNode*, long long loopIndexId, TSymbolTable&);
     void constantIndexExpressionCheck(TIntermNode*);
 
     void setLayoutQualifier(const TSourceLoc&, TPublicType&, TString&);
diff --git a/glslang/MachineIndependent/ShaderLang.cpp b/glslang/MachineIndependent/ShaderLang.cpp
index 659aa43..3f793a7 100644
--- a/glslang/MachineIndependent/ShaderLang.cpp
+++ b/glslang/MachineIndependent/ShaderLang.cpp
@@ -949,6 +949,9 @@
     if (cachedTable)
         symbolTable->adoptLevels(*cachedTable);
 
+    if (intermediate.getUniqueId() != 0)
+        symbolTable->overwriteUniqueId(intermediate.getUniqueId());
+
     // Add built-in symbols that are potentially context dependent;
     // they get popped again further down.
     if (! AddContextSpecificSymbols(resources, compiler->infoSink, *symbolTable, version, profile, spvVersion,
@@ -1011,6 +1014,7 @@
     bool success = processingContext(*parseContext, ppContext, fullInput,
                                      versionWillBeError, *symbolTable,
                                      intermediate, optLevel, messages);
+    intermediate.setUniqueId(symbolTable->getMaxSymbolId());
     return success;
 }
 
@@ -1811,6 +1815,11 @@
     intermediate->addProcesses(p);
 }
 
+void  TShader::setUniqueId(unsigned long long id)
+{
+    intermediate->setUniqueId(id);
+}
+
 void TShader::setInvertY(bool invert)                   { intermediate->setInvertY(invert); }
 void TShader::setNanMinMaxClamp(bool useNonNan)         { intermediate->setNanMinMaxClamp(useNonNan); }
 
diff --git a/glslang/MachineIndependent/SymbolTable.cpp b/glslang/MachineIndependent/SymbolTable.cpp
index f6291c3..0e5ee19 100644
--- a/glslang/MachineIndependent/SymbolTable.cpp
+++ b/glslang/MachineIndependent/SymbolTable.cpp
@@ -170,7 +170,7 @@
         for (int i = 0; i < arraySizes->getNumDims(); ++i) {
             if (arraySizes->getDimNode(i)) {
                 if (arraySizes->getDimNode(i)->getAsSymbolNode())
-                    snprintf(buf, maxSize, "s%d", arraySizes->getDimNode(i)->getAsSymbolNode()->getId());
+                    snprintf(buf, maxSize, "s%lld", arraySizes->getDimNode(i)->getAsSymbolNode()->getId());
                 else
                     snprintf(buf, maxSize, "s%p", arraySizes->getDimNode(i));
             } else
diff --git a/glslang/MachineIndependent/SymbolTable.h b/glslang/MachineIndependent/SymbolTable.h
index db16c19..54c3ca5 100644
--- a/glslang/MachineIndependent/SymbolTable.h
+++ b/glslang/MachineIndependent/SymbolTable.h
@@ -104,8 +104,8 @@
     virtual const TAnonMember* getAsAnonMember() const { return 0; }
     virtual const TType& getType() const = 0;
     virtual TType& getWritableType() = 0;
-    virtual void setUniqueId(int id) { uniqueId = id; }
-    virtual int getUniqueId() const { return uniqueId; }
+    virtual void setUniqueId(long long id) { uniqueId = id; }
+    virtual long long getUniqueId() const { return uniqueId; }
     virtual void setExtensions(int numExts, const char* const exts[])
     {
         assert(extensions == 0);
@@ -130,7 +130,7 @@
     TSymbol& operator=(const TSymbol&);
 
     const TString *name;
-    unsigned int uniqueId;      // For cross-scope comparing during code generation
+    unsigned long long uniqueId;      // For cross-scope comparing during code generation
 
     // For tracking what extensions must be present
     // (don't use if correct version/profile is present).
@@ -612,6 +612,7 @@
     //   3: user-shader globals
     //
 protected:
+    static const uint32_t LevelFlagBitOffset = 56;
     static const int globalLevel = 3;
     static bool isSharedLevel(int level)  { return level <= 1; }            // exclude all per-compile levels
     static bool isBuiltInLevel(int level) { return level <= 2; }            // exclude user globals
@@ -620,10 +621,12 @@
     bool isEmpty() { return table.size() == 0; }
     bool atBuiltInLevel() { return isBuiltInLevel(currentLevel()); }
     bool atGlobalLevel()  { return isGlobalLevel(currentLevel()); }
-    static bool isBuiltInSymbol(int uniqueId) {
-        int level = uniqueId >> LevelFlagBitOffset;
+    static bool isBuiltInSymbol(long long uniqueId) {
+        int level = static_cast<int>(uniqueId >> LevelFlagBitOffset);
         return isBuiltInLevel(level);
     }
+    static constexpr uint64_t uniqueIdMask = (1LL << LevelFlagBitOffset) - 1;
+    static const uint32_t MaxLevelInUniqueID = 127;
     void setNoBuiltInRedeclarations() { noBuiltInRedeclarations = true; }
     void setSeparateNameSpaces() { separateNameSpaces = true; }
 
@@ -691,6 +694,16 @@
         return table[currentLevel()]->amend(symbol, firstNewMember);
     }
 
+    // Update the level info in symbol's unique ID to current level
+    void amendSymbolIdLevel(TSymbol& symbol)
+    {
+        // clamp level to avoid overflow
+        uint64_t level = currentLevel() > MaxLevelInUniqueID ? MaxLevelInUniqueID : currentLevel();
+        uint64_t symbolId = symbol.getUniqueId();
+        symbolId &= uniqueIdMask;
+        symbolId |= (level << LevelFlagBitOffset);
+        symbol.setUniqueId(symbolId);
+    }
     //
     // To allocate an internal temporary, which will need to be uniquely
     // identified by the consumer of the AST, but never need to
@@ -859,7 +872,7 @@
         }
     }
 
-    int getMaxSymbolId() { return uniqueId; }
+    long long getMaxSymbolId() { return uniqueId; }
 #if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE)
     void dump(TInfoSink& infoSink, bool complete = false) const;
 #endif
@@ -876,19 +889,24 @@
     // Add current level in the high-bits of unique id
     void updateUniqueIdLevelFlag() {
         // clamp level to avoid overflow
-        uint32_t level = currentLevel() > 7 ? 7 : currentLevel();
-        uniqueId &= ((1 << LevelFlagBitOffset) - 1);
+        uint64_t level = currentLevel() > MaxLevelInUniqueID ? MaxLevelInUniqueID : currentLevel();
+        uniqueId &= uniqueIdMask;
         uniqueId |= (level << LevelFlagBitOffset);
     }
 
+    void overwriteUniqueId(long long id)
+    {
+        uniqueId = id;
+        updateUniqueIdLevelFlag();
+    }
+
 protected:
     TSymbolTable(TSymbolTable&);
     TSymbolTable& operator=(TSymbolTableLevel&);
 
     int currentLevel() const { return static_cast<int>(table.size()) - 1; }
-    static const uint32_t LevelFlagBitOffset = 28;
     std::vector<TSymbolTableLevel*> table;
-    int uniqueId;     // for unique identification in code generation
+    long long uniqueId;     // for unique identification in code generation
     bool noBuiltInRedeclarations;
     bool separateNameSpaces;
     unsigned int adoptedLevels;
diff --git a/glslang/MachineIndependent/iomapper.h b/glslang/MachineIndependent/iomapper.h
index 7934c4a..1dce8ff 100644
--- a/glslang/MachineIndependent/iomapper.h
+++ b/glslang/MachineIndependent/iomapper.h
@@ -52,7 +52,7 @@
 
 class TIntermediate;
 struct TVarEntryInfo {
-    int id;
+    long long id;
     TIntermSymbol* symbol;
     bool live;
     int newBinding;
diff --git a/glslang/MachineIndependent/limits.cpp b/glslang/MachineIndependent/limits.cpp
index 51d9300..3915705 100644
--- a/glslang/MachineIndependent/limits.cpp
+++ b/glslang/MachineIndependent/limits.cpp
@@ -63,14 +63,14 @@
 
 class TInductiveTraverser : public TIntermTraverser {
 public:
-    TInductiveTraverser(int id, TSymbolTable& st)
+    TInductiveTraverser(long long id, TSymbolTable& st)
     : loopId(id), symbolTable(st), bad(false)  { }
 
     virtual bool visitBinary(TVisit, TIntermBinary* node);
     virtual bool visitUnary(TVisit, TIntermUnary* node);
     virtual bool visitAggregate(TVisit, TIntermAggregate* node);
 
-    int loopId;           // unique ID of the symbol that's the loop inductive variable
+    long long loopId;           // unique ID of the symbol that's the loop inductive variable
     TSymbolTable& symbolTable;
     bool bad;
     TSourceLoc badLoc;
@@ -129,7 +129,7 @@
 //
 // External function to call for loop check.
 //
-void TParseContext::inductiveLoopBodyCheck(TIntermNode* body, int loopId, TSymbolTable& symbolTable)
+void TParseContext::inductiveLoopBodyCheck(TIntermNode* body, long long loopId, TSymbolTable& symbolTable)
 {
     TInductiveTraverser it(loopId, symbolTable);
 
diff --git a/glslang/MachineIndependent/linkValidate.cpp b/glslang/MachineIndependent/linkValidate.cpp
index 2d14031..3f3a312 100644
--- a/glslang/MachineIndependent/linkValidate.cpp
+++ b/glslang/MachineIndependent/linkValidate.cpp
@@ -48,6 +48,7 @@
 
 #include "localintermediate.h"
 #include "../Include/InfoSink.h"
+#include "SymbolTable.h"
 
 namespace glslang {
 
@@ -306,9 +307,9 @@
     // Map by global name to unique ID to rationalize the same object having
     // differing IDs in different trees.
     TIdMaps idMaps;
-    int maxId;
-    seedIdMap(idMaps, maxId);
-    remapIds(idMaps, maxId + 1, unit);
+    long long idShift;
+    seedIdMap(idMaps, idShift);
+    remapIds(idMaps, idShift + 1, unit);
 
     mergeBodies(infoSink, globals, unitGlobals);
     mergeLinkerObjects(infoSink, linkerObjects, unitLinkerObjects);
@@ -329,14 +330,14 @@
 
 
 // Traverser that seeds an ID map with all built-ins, and tracks the
-// maximum ID used.
+// maximum ID used, currently using (maximum ID + 1) as new symbol id shift seed.
+// Level id will keep same after shifting.
 // (It would be nice to put this in a function, but that causes warnings
 // on having no bodies for the copy-constructor/operator=.)
 class TBuiltInIdTraverser : public TIntermTraverser {
 public:
-    TBuiltInIdTraverser(TIdMaps& idMaps) : idMaps(idMaps), maxId(0) { }
+    TBuiltInIdTraverser(TIdMaps& idMaps) : idMaps(idMaps), idShift(0) { }
     // If it's a built in, add it to the map.
-    // Track the max ID.
     virtual void visitSymbol(TIntermSymbol* symbol)
     {
         const TQualifier& qualifier = symbol->getType().getQualifier();
@@ -344,14 +345,16 @@
             TShaderInterface si = symbol->getType().getShaderInterface();
             idMaps[si][getNameForIdMap(symbol)] = symbol->getId();
         }
-        maxId = std::max(maxId, symbol->getId());
+        idShift = (symbol->getId() & ~TSymbolTable::uniqueIdMask) |
+                std::max(idShift & TSymbolTable::uniqueIdMask,
+                         symbol->getId() & TSymbolTable::uniqueIdMask);
     }
-    int getMaxId() const { return maxId; }
+    long long getIdShift() const { return idShift; }
 protected:
     TBuiltInIdTraverser(TBuiltInIdTraverser&);
     TBuiltInIdTraverser& operator=(TBuiltInIdTraverser&);
     TIdMaps& idMaps;
-    int maxId;
+    long long idShift;
 };
 
 // Traverser that seeds an ID map with non-builtins.
@@ -377,12 +380,12 @@
 };
 
 // Initialize the the ID map with what we know of 'this' AST.
-void TIntermediate::seedIdMap(TIdMaps& idMaps, int& maxId)
+void TIntermediate::seedIdMap(TIdMaps& idMaps, long long& idShift)
 {
     // all built-ins everywhere need to align on IDs and contribute to the max ID
     TBuiltInIdTraverser builtInIdTraverser(idMaps);
     treeRoot->traverse(&builtInIdTraverser);
-    maxId = builtInIdTraverser.getMaxId();
+    idShift = builtInIdTraverser.getIdShift() & TSymbolTable::uniqueIdMask;
 
     // user variables in the linker object list need to align on ids
     TUserIdTraverser userIdTraverser(idMaps);
@@ -394,7 +397,7 @@
 // on having no bodies for the copy-constructor/operator=.)
 class TRemapIdTraverser : public TIntermTraverser {
 public:
-    TRemapIdTraverser(const TIdMaps& idMaps, int idShift) : idMaps(idMaps), idShift(idShift) { }
+    TRemapIdTraverser(const TIdMaps& idMaps, long long idShift) : idMaps(idMaps), idShift(idShift) { }
     // Do the mapping:
     //  - if the same symbol, adopt the 'this' ID
     //  - otherwise, ensure a unique ID by shifting to a new space
@@ -406,7 +409,9 @@
             TShaderInterface si = symbol->getType().getShaderInterface();
             auto it = idMaps[si].find(getNameForIdMap(symbol));
             if (it != idMaps[si].end()) {
-                symbol->changeId(it->second);
+                uint64_t id = (symbol->getId() & ~TSymbolTable::uniqueIdMask) |
+                    (it->second & TSymbolTable::uniqueIdMask);
+                symbol->changeId(id);
                 remapped = true;
             }
         }
@@ -417,10 +422,10 @@
     TRemapIdTraverser(TRemapIdTraverser&);
     TRemapIdTraverser& operator=(TRemapIdTraverser&);
     const TIdMaps& idMaps;
-    int idShift;
+    long long idShift;
 };
 
-void TIntermediate::remapIds(const TIdMaps& idMaps, int idShift, TIntermediate& unit)
+void TIntermediate::remapIds(const TIdMaps& idMaps, long long idShift, TIntermediate& unit)
 {
     // Remap all IDs to either share or be unique, as dictated by the idMap and idShift.
     TRemapIdTraverser idTraverser(idMaps, idShift);
diff --git a/glslang/MachineIndependent/localintermediate.h b/glslang/MachineIndependent/localintermediate.h
index 8550ad0..1db3d1c 100644
--- a/glslang/MachineIndependent/localintermediate.h
+++ b/glslang/MachineIndependent/localintermediate.h
@@ -227,10 +227,10 @@
 
 class TIdMaps {
 public:
-    TMap<TString, int>& operator[](int i) { return maps[i]; }
-    const TMap<TString, int>& operator[](int i) const { return maps[i]; }
+    TMap<TString, long long>& operator[](long long i) { return maps[i]; }
+    const TMap<TString, long long>& operator[](long long i) const { return maps[i]; }
 private:
-    TMap<TString, int> maps[EsiCount];
+    TMap<TString, long long> maps[EsiCount];
 };
 
 class TNumericFeatures {
@@ -292,7 +292,8 @@
         invertY(false),
         useStorageBuffer(false),
         nanMinMaxClamp(false),
-        depthReplacing(false)
+        depthReplacing(false),
+        uniqueId(0)
 #ifndef GLSLANG_WEB
         ,
         implicitThisName("@this"), implicitCounterName("@count"),
@@ -911,6 +912,8 @@
     void addProcess(const std::string& process) { processes.addProcess(process); }
     void addProcessArgument(const std::string& arg) { processes.addArgument(arg); }
     const std::vector<std::string>& getProcesses() const { return processes.getProcesses(); }
+    unsigned long long getUniqueId() const { return uniqueId; }
+    void setUniqueId(unsigned long long id) { uniqueId = id; }
 
     // Certain explicit conversions are allowed conditionally
 #ifdef GLSLANG_WEB
@@ -939,14 +942,14 @@
 #endif
 
 protected:
-    TIntermSymbol* addSymbol(int Id, const TString&, const TType&, const TConstUnionArray&, TIntermTyped* subtree, const TSourceLoc&);
+    TIntermSymbol* addSymbol(long long Id, const TString&, const TType&, const TConstUnionArray&, TIntermTyped* subtree, const TSourceLoc&);
     void error(TInfoSink& infoSink, const char*);
     void warn(TInfoSink& infoSink, const char*);
     void mergeCallGraphs(TInfoSink&, TIntermediate&);
     void mergeModes(TInfoSink&, TIntermediate&);
     void mergeTrees(TInfoSink&, TIntermediate&);
-    void seedIdMap(TIdMaps& idMaps, int& maxId);
-    void remapIds(const TIdMaps& idMaps, int idShift, TIntermediate&);
+    void seedIdMap(TIdMaps& idMaps, long long& IdShift);
+    void remapIds(const TIdMaps& idMaps, long long idShift, TIntermediate&);
     void mergeBodies(TInfoSink&, TIntermSequence& globals, const TIntermSequence& unitGlobals);
     void mergeLinkerObjects(TInfoSink&, TIntermSequence& linkerObjects, const TIntermSequence& unitLinkerObjects);
     void mergeImplicitArraySizes(TType&, const TType&);
@@ -999,6 +1002,7 @@
     int localSize[3];
     bool localSizeNotDefault[3];
     int localSizeSpecId[3];
+    unsigned long long uniqueId;
 #ifndef GLSLANG_WEB
 public:
     const char* const implicitThisName;
diff --git a/glslang/Public/ShaderLang.h b/glslang/Public/ShaderLang.h
index 3b47403..e147b0d 100644
--- a/glslang/Public/ShaderLang.h
+++ b/glslang/Public/ShaderLang.h
@@ -458,6 +458,7 @@
     GLSLANG_EXPORT void setEntryPoint(const char* entryPoint);
     GLSLANG_EXPORT void setSourceEntryPoint(const char* sourceEntryPointName);
     GLSLANG_EXPORT void addProcesses(const std::vector<std::string>&);
+    GLSLANG_EXPORT void setUniqueId(unsigned long long id);
 
     // IO resolver binding data: see comments in ShaderLang.cpp
     GLSLANG_EXPORT void setShiftBinding(TResourceType res, unsigned int base);