Add and partially implement an interface for doing uniform reflection.  It includes an AST traversal to identify live accesses.  

It does not yet correctly compute block offsets, give correct GL-API-style type values, or handle arrays.

This is tied to the new -q flag.


git-svn-id: https://cvs.khronos.org/svn/repos/ogl/trunk/ecosystem/public/sdk/tools/glslang@23938 e7fa87d3-cd2b-0410-9028-fcbf551c1848
diff --git a/StandAlone/StandAlone.cpp b/StandAlone/StandAlone.cpp
index b3a37dc..a3c1faf 100644
--- a/StandAlone/StandAlone.cpp
+++ b/StandAlone/StandAlone.cpp
@@ -61,6 +61,7 @@
     EOptionsLinkProgram       = 0x020,
     EOptionMultiThreaded      = 0x040,
     EOptionDumpConfig         = 0x080,
+    EOptionDumpReflection     = 0x100,
 };
 
 //
@@ -466,6 +467,9 @@
             case 'm':
                 Options |= EOptionMemoryLeakMode;
                 break;
+            case 'q':
+                Options |= EOptionDumpReflection;
+                break;
             case 'r':
                 Options |= EOptionRelaxedErrors;
                 break;
@@ -575,6 +579,11 @@
         puts(program.getInfoDebugLog());
     }
 
+    if (Options & EOptionDumpReflection) {
+        program.buildReflection();
+        program.dumpReflection();
+    }
+
     // Free everything up, program has to go before the shaders
     // because it might have merged stuff from the shaders, and
     // the stuff from the shaders has to have its destructors called
@@ -771,6 +780,7 @@
            "-d: delay exit\n"
            "-l: link validation of all input files\n"
            "-m: memory leak mode\n"
+           "-q: dump reflection query database\n"
            "-r: relaxed semantic error-checking mode\n"
            "-s: silent mode\n"
            "-t: multi-threaded mode\n");
diff --git a/Test/baseResults/reflection.vert.out b/Test/baseResults/reflection.vert.out
new file mode 100644
index 0000000..dd02aea
--- /dev/null
+++ b/Test/baseResults/reflection.vert.out
@@ -0,0 +1,33 @@
+reflection.vert

+Warning, version 440 is not yet complete; some version-specific features are present, but many are missing.

+

+

+

+Linked vertex stage:

+

+

+

+Uniform reflection:

+0:anonMember3: offset 32, type 35666, arraySize 1, index 0

+1:s.a: offset -1, type 35666, arraySize 1, index -1

+2:anonMember1: offset 0, type 35666, arraySize 1, index 0

+3:uf1: offset -1, type 35666, arraySize 1, index -1

+4:uf2: offset -1, type 35666, arraySize 1, index -1

+5:ablock.member3: offset 32, type 35666, arraySize 1, index 1

+

+Uniform block reflection:

+0: nameless: offset -1, type -1, arraySize 1, index -1

+1: ablock: offset -1, type -1, arraySize 1, index -1

+

+Live names

+ablock: 1

+ablock.member3: 5

+anonMember1: 2

+anonMember3: 0

+liveFunction1(: -1

+liveFunction2(: -1

+nameless: 0

+s.a: 1

+uf1: 3

+uf2: 4

+

diff --git a/Test/reflection.vert b/Test/reflection.vert
new file mode 100644
index 0000000..79a6ef8
--- /dev/null
+++ b/Test/reflection.vert
@@ -0,0 +1,73 @@
+#version 440 core

+

+uniform nameless {

+    vec3 anonMember1;

+    vec4 anonDeadMember2;

+    vec4 anonMember3;

+};

+

+uniform named {

+    vec3 deadMember1;

+    vec4 member2;

+    vec4 member3;

+} ablock;

+

+uniform namelessdead {

+    int a;

+};

+

+uniform namedDead {

+    int b;

+} bblock;

+

+struct TS {

+    int a;

+    int dead;

+};

+

+uniform TS s;

+

+uniform float uf1;

+uniform float uf2;

+uniform float ufDead3;

+uniform float ufDead4;

+

+const bool control = true;

+

+void deadFunction()

+{

+    vec3 v3 = ablock.deadMember1;

+    vec4 v = anonDeadMember2;

+    float f = ufDead4;

+}

+

+void liveFunction2()

+{

+    vec3 v = anonMember1;

+    float f = uf1;

+}

+

+void liveFunction1()

+{

+    liveFunction2();

+    float f = uf2;

+    vec4 v = ablock.member3;

+}

+

+void main()

+{

+    liveFunction1();

+    liveFunction2();

+

+    if (! control)

+        deadFunction();

+

+    float f;

+

+    if (control) {

+        liveFunction2();

+        f = anonMember3.z;

+        f = s.a;

+    } else

+        f = ufDead3;

+}

diff --git a/Test/runtests b/Test/runtests
index 5f0e941..9263304 100644
--- a/Test/runtests
+++ b/Test/runtests
@@ -44,6 +44,13 @@
 runLinkTest empty.frag empty2.frag empty3.frag
 
 #
+# reflection tests
+#
+echo Running reflection...
+$EXE -l -q reflection.vert > $TARGETDIR/reflection.vert.out
+diff -b $BASEDIR/reflection.vert.out $TARGETDIR/reflection.vert.out
+
+#
 # multi-threaded test
 #
 echo Comparing single thread to multithread for all tests in current directory...
diff --git a/Todo.txt b/Todo.txt
index 29ed8c1..7aff9fe 100644
--- a/Todo.txt
+++ b/Todo.txt
@@ -36,6 +36,8 @@
       - Non ES: write to only one of gl_FragColor, gl_FragData, or user-declared
       - 1.50: at least one geometry shader says input primitive and at least one says output primitive...
       - 1.50: at least one geometry shader says max_vertices...
+      - 1.50: geometry shaders: max_vertices must be checked against gl_MaxGeometryOutputVertices (maybe at compile time)
+      + 1.50: origin_upper_left and pixel_center_integer have to match
 	  - 4.4: An interface contains two different blocks, each with no instance name, where the blocks contain a member with the same name.
 	  - 4.4: component aliasing (except desktop vertex shader inputs)
     Intra-stage linking, multiple shader
@@ -90,8 +92,8 @@
       + Add new minimum maximums for gl_MaxVertexOutputComponents, gl_MaxGeometryInputComponents, gl_MaxGeometryOutputComponents, and gl_MaxFragmentInputComponents, 
                 rather than relying on gl_MaxVaryingComponents.  Also, corrected gl_MaxVaryingComponents to be 60 instead of 64.
       + Added gl_PrimitiveID as an input to fragment shaders.
-      - Added gl_FragCoord qualifiers origin_upper_left, and pixel_center_integer to modify the values returned by gl_FragCoord (and have no affect on any other aspect of the pipeline or language).
-        - including redeclaration of gl_FragCoord that adds nothing
+      + Added gl_FragCoord qualifiers origin_upper_left, and pixel_center_integer to modify the values returned by gl_FragCoord (and have no affect on any other aspect of the pipeline or language).
+        + including redeclaration of gl_FragCoord that adds nothing
       - Added support for multi-sample textures through sampler2DMS and sampler2DMSArray support in texelFetch() and textureSize().
       + Broadened interface blocks from just uniforms to in and out interfaces as well.
       + Broaden array usage to include vertex shader inputs (vertex in).
diff --git a/glslang.vcxproj b/glslang.vcxproj
index 47ba6ee..7685882 100644
--- a/glslang.vcxproj
+++ b/glslang.vcxproj
@@ -165,6 +165,7 @@
     <ClCompile Include="glslang\MachineIndependent\preprocessor\PpContext.cpp" />

     <ClCompile Include="glslang\MachineIndependent\preprocessor\PpSymbols.cpp" />

     <ClCompile Include="glslang\MachineIndependent\preprocessor\PpTokens.cpp" />

+    <ClCompile Include="glslang\MachineIndependent\reflection.cpp" />

     <ClCompile Include="glslang\MachineIndependent\Scan.cpp" />

     <ClCompile Include="glslang\MachineIndependent\Versions.cpp" />

     <ClCompile Include="OGLCompilersDLL\InitializeDll.cpp" />

@@ -192,6 +193,7 @@
     <ClInclude Include="glslang\MachineIndependent\ParseHelper.h" />

     <ClInclude Include="glslang\MachineIndependent\preprocessor\PpContext.h" />

     <ClInclude Include="glslang\MachineIndependent\preprocessor\PpTokens.h" />

+    <ClInclude Include="glslang\MachineIndependent\reflection.h" />

     <ClInclude Include="glslang\MachineIndependent\RemoveTree.h" />

     <ClInclude Include="glslang\MachineIndependent\localintermediate.h" />

     <ClInclude Include="glslang\Include\BaseTypes.h" />

diff --git a/glslang.vcxproj.filters b/glslang.vcxproj.filters
index 62fb9ae..2f3b2be 100644
--- a/glslang.vcxproj.filters
+++ b/glslang.vcxproj.filters
@@ -112,6 +112,9 @@
     <ClCompile Include="glslang\MachineIndependent\linkValidate.cpp">

       <Filter>Machine Independent</Filter>

     </ClCompile>

+    <ClCompile Include="glslang\MachineIndependent\reflection.cpp">

+      <Filter>Machine Independent</Filter>

+    </ClCompile>

   </ItemGroup>

   <ItemGroup>

     <ClInclude Include="glslang\MachineIndependent\Initialize.h">

@@ -192,6 +195,9 @@
     <ClInclude Include="glslang\MachineIndependent\preprocessor\PpTokens.h">

       <Filter>Machine Independent\Preprocessor</Filter>

     </ClInclude>

+    <ClInclude Include="glslang\MachineIndependent\reflection.h">

+      <Filter>Machine Independent</Filter>

+    </ClInclude>

   </ItemGroup>

   <ItemGroup>

     <CustomBuild Include="glslang\MachineIndependent\glslang.y">

diff --git a/glslang/Include/intermediate.h b/glslang/Include/intermediate.h
index f1c5487..135ca9f 100644
--- a/glslang/Include/intermediate.h
+++ b/glslang/Include/intermediate.h
@@ -588,7 +588,7 @@
     TIntermSelection(TIntermTyped* cond, TIntermNode* trueB, TIntermNode* falseB, const TType& type) :
         TIntermTyped(type), condition(cond), trueBlock(trueB), falseBlock(falseB) {}
     virtual void traverse(TIntermTraverser*);
-    virtual TIntermNode* getCondition() const { return condition; }
+    virtual TIntermTyped* getCondition() const { return condition; }
     virtual TIntermNode* getTrueBlock() const { return trueBlock; }
     virtual TIntermNode* getFalseBlock() const { return falseBlock; }
     virtual TIntermSelection* getAsSelectionNode() { return this; }
@@ -624,6 +624,12 @@
 // When using this, just fill in the methods for nodes you want visited.
 // Return false from a pre-visit to skip visiting that node's subtree.
 //
+// Explicitly set postVisit to true if you want post visiting, otherwise,
+// filled in methods will only be called at pre-visit time (before processing
+// the subtree).
+//
+// If you only want post-visits, explicitly turn off preVisit and turn on postVisit.
+//
 class TIntermTraverser {
 public:
     POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator())
diff --git a/glslang/MachineIndependent/Makefile b/glslang/MachineIndependent/Makefile
index fe3b8bd..1f62ede 100644
--- a/glslang/MachineIndependent/Makefile
+++ b/glslang/MachineIndependent/Makefile
@@ -11,7 +11,7 @@
 OBJECTS= Initialize.o IntermTraverse.o \
 	Intermediate.o ParseHelper.o PoolAlloc.o \
 	RemoveTree.o ShaderLang.o intermOut.o parseConst.o SymbolTable.o \
-	InfoSink.o Versions.o Constant.o Scan.o limits.o linkValidate.o
+	InfoSink.o Versions.o Constant.o Scan.o limits.o linkValidate.o reflection.o
 
 SRCS= gen_glslang_tab.cpp Initialize.cpp IntermTraverse.cpp \
 	Intermediate.cpp ParseHelper.cpp PoolAlloc.cp \
@@ -147,3 +147,4 @@
 Constant.o: localintermediate.h ../Include/intermediate.h ../Public/ShaderLang.h SymbolTable.h Versions.h
 limits.o:  ParseHelper.h
 linkValidate.o: localintermediate.h
+reflection.o: ../Include/Common.h reflection.h localintermediate.h
diff --git a/glslang/MachineIndependent/ShaderLang.cpp b/glslang/MachineIndependent/ShaderLang.cpp
index ea93c15..2b22a8c 100644
--- a/glslang/MachineIndependent/ShaderLang.cpp
+++ b/glslang/MachineIndependent/ShaderLang.cpp
@@ -53,6 +53,7 @@
 
 #define SH_EXPORTING
 #include "../Public/ShaderLang.h"
+#include "reflection.h"
 #include "Initialize.h"
 
 namespace { // anonymous namespace for file-local functions and symbols
@@ -967,18 +968,23 @@
     return infoSink->debug.c_str();
 }
 
-TProgram::TProgram() : pool(0)
+TProgram::TProgram() : pool(0), reflection(0), linked(false)
 {
     infoSink = new TInfoSink;
-    for (int s = 0; s < EShLangCount; ++s)
+    for (int s = 0; s < EShLangCount; ++s) {
         intermediate[s] = 0;
+        newedIntermediate[s] = false;
+    }
 }
 
 TProgram::~TProgram()
 {
     delete infoSink;
+    delete reflection;
+
     for (int s = 0; s < EShLangCount; ++s)
-        delete intermediate[s];
+        if (newedIntermediate[s])
+            delete intermediate[s];
 
     delete pool;
 }
@@ -989,8 +995,12 @@
 //
 bool TProgram::link(EShMessages messages)
 {
-    bool error = false;
+    if (linked)
+        return false;
+    linked = true;
 
+    bool error = false;
+    
     pool = new TPoolAllocator();
     SetThreadPoolAllocator(*pool);
 
@@ -1013,12 +1023,11 @@
     // Be efficient for the common single compilation unit per stage case,
     // reusing it's TIntermediate instead of merging into a new one.
     //
-    TIntermediate* merged;
     if (stages[stage].size() == 1)
-        merged = stages[stage].front()->intermediate;    
+        intermediate[stage] = stages[stage].front()->intermediate;
     else {
         intermediate[stage] = new TIntermediate(stage);
-        merged = intermediate[stage];
+        newedIntermediate[stage] = true;
     }
 
     infoSink->info << "\nLinked " << StageName(stage) << " stage:\n\n";
@@ -1026,15 +1035,15 @@
     if (stages[stage].size() > 1) {
         std::list<TShader*>::const_iterator it;
         for (it = stages[stage].begin(); it != stages[stage].end(); ++it)
-            merged->merge(*infoSink, *(*it)->intermediate);
+            intermediate[stage]->merge(*infoSink, *(*it)->intermediate);
 
         if (messages & EShMsgAST)
-            merged->outputTree(*infoSink);
+            intermediate[stage]->outputTree(*infoSink);
     }
 
-    merged->errorCheck(*infoSink);
+    intermediate[stage]->errorCheck(*infoSink);
 
-    return merged->getNumErrors() > 0;
+    return intermediate[stage]->getNumErrors() > 0;
 }
 
 const char* TProgram::getInfoLog()
@@ -1047,4 +1056,38 @@
     return infoSink->debug.c_str();
 }
 
+//
+// Reflection implementation.
+//
+
+bool TProgram::buildReflection()
+{    
+    if (! linked || reflection)
+        return false;
+
+    reflection = new TReflection;
+
+    for (int s = 0; s < EShLangCount; ++s) {
+        if (intermediate[s]) {
+            if (! reflection->addStage((EShLanguage)s, *intermediate[s]))
+                return false;
+        }
+    }
+
+    return true;
+}
+
+int TProgram::getNumLiveUniformVariables()           { return reflection->getNumUniforms(); }
+int TProgram::getNumLiveUniformBlocks()              { return reflection->getNumUniformBlocks(); }
+const char* TProgram::getUniformName(int index)      { return reflection->getUniform(index).name.c_str(); }
+const char* TProgram::getUniformBlockName(int index) { return reflection->getUniformBlock(index).name.c_str(); }
+int TProgram::getUniformBlockSize(int index)         { return reflection->getUniformBlock(index).size; }
+int TProgram::getUniformIndex(const char* name)      { return reflection->getIndex(name); }
+int TProgram::getUniformBlockIndex(int index)        { return reflection->getUniform(index).index; }
+int TProgram::getUniformType(int index)              { return reflection->getUniform(index).glDefineType; }
+int TProgram::getUniformBufferOffset(int index)      { return reflection->getUniform(index).offset; }
+int TProgram::getUniformArraySize(int index)         { return reflection->getUniform(index).size; }
+
+void TProgram::dumpReflection()                      { reflection->dump(); }
+
 } // end namespace glslang
diff --git a/glslang/MachineIndependent/linkValidate.cpp b/glslang/MachineIndependent/linkValidate.cpp
index 0c1fb7b..55bfdc6 100644
--- a/glslang/MachineIndependent/linkValidate.cpp
+++ b/glslang/MachineIndependent/linkValidate.cpp
@@ -296,7 +296,7 @@
             TCall* call = stack.back();

 

             // Add to the stack just one callee.

-            // This algorithm always terminates, because only ! visited and ! currentPath causes a push

+            // This algorithm always terminates, because only !visited and !currentPath causes a push

             // and all pushes change currentPath to true, and all pops change visited to true.

             TGraph::iterator child = callGraph.begin();

             for (; child != callGraph.end(); ++child) {

@@ -312,6 +312,7 @@
                             error(infoSink, "Recursion detected:");

                             infoSink.info << "    " << call->callee << " calling " << child->callee << "\n";

                             child->errorGiven = true;

+                            recursive = true;

                         }

                     } else {

                         child->currentPath = true;

diff --git a/glslang/MachineIndependent/localintermediate.h b/glslang/MachineIndependent/localintermediate.h
index 5657fca..0931c50 100644
--- a/glslang/MachineIndependent/localintermediate.h
+++ b/glslang/MachineIndependent/localintermediate.h
@@ -56,7 +56,8 @@
 //
 class TIntermediate {
 public:
-    explicit TIntermediate(EShLanguage l, int v = 0, EProfile p = ENoProfile) : language(l), treeRoot(0), profile(p), version(v), numMains(0), numErrors(0),
+    explicit TIntermediate(EShLanguage l, int v = 0, EProfile p = ENoProfile) : language(l), treeRoot(0), profile(p), version(v), 
+        numMains(0), numErrors(0), recursive(false),
         invocations(0), maxVertices(0), inputPrimitive(ElgNone), outputPrimitive(ElgNone), pixelCenterInteger(false), originUpperLeft(false) { }
 
     void setVersion(int v) { version = v; }
@@ -66,7 +67,9 @@
     void setTreeRoot(TIntermNode* r) { treeRoot = r; }
     TIntermNode* getTreeRoot() const { return treeRoot; }
     void addMainCount() { ++numMains; }
+    int getNumMains() const { return numMains; }
     int getNumErrors() const { return numErrors; }
+    bool isRecursive() const { return recursive; }
     
     TIntermSymbol* addSymbol(int Id, const TString&, const TType&, TSourceLoc);
     TIntermTyped* addConversion(TOperator, const TType&, TIntermTyped*);
@@ -157,6 +160,7 @@
     int version;
     int numMains;
     int numErrors;
+    bool recursive;
     int invocations;
     int maxVertices;
     TLayoutGeometry inputPrimitive;
diff --git a/glslang/MachineIndependent/reflection.cpp b/glslang/MachineIndependent/reflection.cpp
new file mode 100644
index 0000000..44cd22c
--- /dev/null
+++ b/glslang/MachineIndependent/reflection.cpp
@@ -0,0 +1,333 @@
+//

+//Copyright (C) 2013 LunarG, Inc.

+//

+//All rights reserved.

+//

+//Redistribution and use in source and binary forms, with or without

+//modification, are permitted provided that the following conditions

+//are met:

+//

+//    Redistributions of source code must retain the above copyright

+//    notice, this list of conditions and the following disclaimer.

+//

+//    Redistributions in binary form must reproduce the above

+//    copyright notice, this list of conditions and the following

+//    disclaimer in the documentation and/or other materials provided

+//    with the distribution.

+//

+//    Neither the name of 3Dlabs Inc. Ltd. nor the names of its

+//    contributors may be used to endorse or promote products derived

+//    from this software without specific prior written permission.

+//

+//THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS

+//"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT

+//LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS

+//FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE

+//COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,

+//INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,

+//BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;

+//LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER

+//CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT

+//LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN

+//ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE

+//POSSIBILITY OF SUCH DAMAGE.

+//

+

+#include "../Include/Common.h"

+#include "reflection.h"

+#include "localintermediate.h"

+

+//

+// Grow the reflection database through a friend traverser class of TReflection and a 

+// collection of functions to do a liveness traversal that note what uniforms are used

+// in semantically non-dead code.

+//

+// Can be used multiple times, once per stage, to grow a program reflection.

+//

+// High-level algorithm for one stage:

+//

+// 1. Put main() on list of live functions.

+//

+// 2. Traverse any live function, while skipping if-tests with a compile-time constant 

+//    condition of false, and while adding any encountered function calls to the live 

+//    function list.

+//

+//    Repeat until the live function list is empty.

+//

+// 3. Add any encountered uniform variables and blocks to the reflection database.

+//

+// Can be attempted with a failed link, but will return false if recursion had been detected, or 

+// there wasn't exactly one main.

+//

+

+namespace glslang {

+

+//

+// The traverser: mostly pass through, except 

+//  - processing function-call nodes to push live functions onto the stack of functions to process

+//  - processing binary nodes to see if they are dereferences of aggregates to track

+//  - processing symbol nodes to see if they are non-aggregate objects to track

+//  - processing selection nodes to trim semantically dead code

+//

+// This is in the glslang namespace directly so it can be a friend of TReflection.

+//

+

+class TLiveTraverser : public TIntermTraverser {

+public:

+    TLiveTraverser(const TIntermediate& i, TReflection& r) : intermediate(i), reflection(r) { }

+

+    // Track live funtions as well as uniforms, so that we don't visit dead functions

+    // and only visit each function once.

+    void addFunctionCall(TIntermAggregate* call)

+    {

+        // just use the map to ensure we process each function at most once

+        if (reflection.nameToIndex.find(call->getName()) == reflection.nameToIndex.end()) {

+            reflection.nameToIndex[call->getName()] = -1;

+            pushFunction(call->getName());

+        }

+    }

+

+    // Add a simple uniform variable reference to the uniform database, no derefence involved.

+    void addUniform(const TIntermSymbol& symbol)

+    {

+        if (reflection.nameToIndex.find(symbol.getName()) == reflection.nameToIndex.end()) {

+            if (isReflectionGranularity(symbol.getType())) {

+                reflection.nameToIndex[symbol.getName()] = reflection.indexToUniform.size();

+                reflection.indexToUniform.push_back(TObjectReflection(symbol.getName(), -1, MapToGlType(symbol.getType()), MapToGlArraySize(symbol.getType()), -1));

+            }

+        }

+    }

+

+    // Add a complex uniform reference where blocks/struct/arrays are involved in tha access.

+    void addDereferencedUniform(TIntermSymbol* base, TIntermBinary* node)

+    {

+        bool block = base->getBasicType() == EbtBlock;

+        int offset = -1;

+        int blockIndex = -1;

+        bool anonymous = false;

+        if (block) {

+            anonymous = base->getName().compare(0, 6, "__anon") == 0;

+            const TString& blockName = anonymous ? base->getType().getTypeName() : base->getName();

+            TReflection::TNameToIndex::const_iterator it = reflection.nameToIndex.find(blockName);

+            if (it == reflection.nameToIndex.end()) {

+                blockIndex = reflection.indexToUniformBlock.size();

+                reflection.nameToIndex[blockName] = blockIndex;

+                reflection.indexToUniformBlock.push_back(TObjectReflection(blockName, -1,  -1, 1, -1));

+            } else

+                blockIndex = it->second;

+        }

+        TString name;

+

+        switch (node->getOp()) {

+        case EOpIndexDirect:

+        case EOpIndexIndirect:

+            // TODO: reflection: handle array dereferences

+            //name = base->getName();

+            //name.append("[]");

+            break;

+        case EOpIndexDirectStruct:

+        {

+            if (! anonymous) {

+                name = base->getName();

+                name.append(".");

+            }

+            int structIndex = node->getRight()->getAsConstantUnion()->getConstArray()[0].getIConst();

+            if (block)

+                offset = structIndex * 16; // TODO: reflection: compute std140 offsets

+            name.append((*base->getType().getStruct())[structIndex].type->getFieldName().c_str());

+            break;

+        }

+        default:

+            break;

+        }

+

+        // TODO: reflection: handle deeper dereference chains than just one dereference

+

+        if (name.size() > 0) {

+            if (reflection.nameToIndex.find(name) == reflection.nameToIndex.end()) {

+                reflection.nameToIndex[name] = reflection.indexToUniform.size();                        

+                reflection.indexToUniform.push_back(TObjectReflection(name, offset, MapToGlType(node->getType()), MapToGlArraySize(node->getType()), blockIndex));

+            }

+        }

+    }

+

+    //

+    // Given a function name, find its subroot in the tree, and push it onto the stack of 

+    // functions left to process.

+    //

+    void pushFunction(const TString& name)

+    {

+        TIntermSequence& globals = intermediate.getTreeRoot()->getAsAggregate()->getSequence();

+        for (unsigned int f = 0; f < globals.size(); ++f) {

+            TIntermAggregate* candidate = globals[f]->getAsAggregate();

+            if (candidate && candidate->getOp() == EOpFunction && candidate->getName() == name) {

+                functions.push_back(candidate);

+                break;

+            }

+        }

+    }

+

+    // Are we at a level in a dereference chain at which individual active uniform queries are made?

+    bool isReflectionGranularity(const TType& type)

+    {

+        return type.getBasicType() != EbtBlock && type.getBasicType() != EbtStruct;

+    }

+

+    // For a binary operation indexing into an aggregate, chase down the base of the aggregate.

+    // Return 0 if the topology does not fit this situation.

+    TIntermSymbol* findBase(const TIntermBinary* node)

+    {

+        TIntermSymbol *symbol = node->getLeft()->getAsSymbolNode();

+        if (symbol)

+            return symbol;

+        TIntermBinary* left = node->getLeft()->getAsBinaryNode();

+        if (! left)

+            return 0;

+

+        return findBase(left);

+    }

+

+    int MapToGlType(const TType& type)

+    {

+        // TODO: reflection: flesh out all GL types

+        #define GL_FLOAT_VEC4 0x8B52

+

+        return GL_FLOAT_VEC4;

+    }

+

+    int MapToGlArraySize(const TType& type)

+    {

+        return type.isArray() ? type.getArraySize() : 1;

+    }

+    

+    typedef std::list<TIntermAggregate*> TFunctionStack;

+    TFunctionStack functions;

+    const TIntermediate& intermediate;

+    TReflection& reflection;

+};

+

+namespace {

+

+//

+// Implement the traversal functions of interest.

+//

+

+// To catch which function calls are not dead, and hence which functions must be visited.

+bool LiveAggregate(bool /* preVisit */, TIntermAggregate* node, TIntermTraverser* it)

+{

+    TLiveTraverser* oit = static_cast<TLiveTraverser*>(it);

+

+    if (node->getOp() == EOpFunctionCall)

+        oit->addFunctionCall(node);

+

+    return true; // traverse this subtree

+}

+

+// To catch dereferenced aggregates that must be reflected.

+bool LiveBinary(bool /* preVisit */, TIntermBinary* node, TIntermTraverser* it)

+{

+    TLiveTraverser* oit = static_cast<TLiveTraverser*>(it);

+

+    switch (node->getOp()) {

+    case EOpIndexDirect:

+    case EOpIndexIndirect:

+    case EOpIndexDirectStruct:

+        // If the left side is already small enough granularity to report, ignore

+        // this operation, and pick it up when the left side is visited.

+        if (! oit->isReflectionGranularity(node->getLeft()->getType()) &&

+            oit->isReflectionGranularity(node->getType())) {            

+            // right granularity; see if this really is a uniform-based dereference

+            TIntermSymbol* base = oit->findBase(node);

+            if (base && base->getQualifier().storage == EvqUniform)

+                oit->addDereferencedUniform(base, node);

+        }

+    default:

+        break;

+    }

+

+    return true;  // still need to visit everything below

+}

+

+// To catch non-dereferenced objects that must be reflected.

+void LiveSymbol(TIntermSymbol* symbol, TIntermTraverser* it)

+{

+    TLiveTraverser* oit = static_cast<TLiveTraverser*>(it);

+

+    if (symbol->getQualifier().storage == EvqUniform)

+        oit->addUniform(*symbol);

+}

+

+// To prune semantically dead paths.

+bool LiveSelection(bool /* preVisit */,  TIntermSelection* node, TIntermTraverser* it)

+{

+    TLiveTraverser* oit = static_cast<TLiveTraverser*>(it);

+

+    TIntermConstantUnion* constant = node->getCondition()->getAsConstantUnion();

+    if (constant) {

+        // cull the path that is dead

+        if (constant->getConstArray()[0].getBConst() == true && node->getTrueBlock())

+            node->getTrueBlock()->traverse(it);

+        if (constant->getConstArray()[0].getBConst() == false && node->getFalseBlock())

+            node->getFalseBlock()->traverse(it);

+

+        return false; // don't traverse any more, we did it all above

+    } else

+        return true; // traverse the whole subtree

+}

+

+} // end anonymous namespace

+

+//

+// Implement TReflection methods.

+//

+

+// Merge live symbols from 'intermediate' into the existing reflection database.

+//

+// Returns false if the input is too malformed to do this.

+bool TReflection::addStage(EShLanguage, const TIntermediate& intermediate)

+{    

+    if (intermediate.getNumMains() != 1 || intermediate.isRecursive())

+        return false;

+

+    TLiveTraverser it(intermediate, *this);

+    it.visitSymbol = LiveSymbol;

+    it.visitSelection = LiveSelection;

+    it.visitBinary = LiveBinary;

+    it.visitAggregate = LiveAggregate;

+

+    // put main() on functions to process

+    it.pushFunction("main(");

+

+    // process all the functions

+    while (! it.functions.empty()) {

+        TIntermNode* function = it.functions.back();

+        it.functions.pop_back();

+        function->traverse(&it);

+    }

+

+    return true;

+}

+

+void TReflection::dump()

+{

+    printf("Uniform reflection:\n");

+    for (size_t i = 0; i < indexToUniform.size(); ++i) {

+        printf("%d:", i);

+        indexToUniform[i].dump();

+    }

+    printf("\n");

+

+    printf("Uniform block reflection:\n");

+    for (size_t i = 0; i < indexToUniformBlock.size(); ++i) {

+        printf("%d: ", i);

+        indexToUniformBlock[i].dump();

+    }

+    printf("\n");

+

+    printf("Live names\n");

+    for (TNameToIndex::const_iterator it = nameToIndex.begin(); it != nameToIndex.end(); ++it)

+        printf("%s: %d\n", it->first.c_str(), it->second);

+    printf("\n");

+}

+

+} // end namespace glslang

diff --git a/glslang/MachineIndependent/reflection.h b/glslang/MachineIndependent/reflection.h
new file mode 100644
index 0000000..efff377
--- /dev/null
+++ b/glslang/MachineIndependent/reflection.h
@@ -0,0 +1,122 @@
+//

+//Copyright (C) 2013 LunarG, Inc.

+//

+//All rights reserved.

+//

+//Redistribution and use in source and binary forms, with or without

+//modification, are permitted provided that the following conditions

+//are met:

+//

+//    Redistributions of source code must retain the above copyright

+//    notice, this list of conditions and the following disclaimer.

+//

+//    Redistributions in binary form must reproduce the above

+//    copyright notice, this list of conditions and the following

+//    disclaimer in the documentation and/or other materials provided

+//    with the distribution.

+//

+//    Neither the name of 3Dlabs Inc. Ltd. nor the names of its

+//    contributors may be used to endorse or promote products derived

+//    from this software without specific prior written permission.

+//

+//THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS

+//"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT

+//LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS

+//FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE

+//COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,

+//INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,

+//BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;

+//LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER

+//CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT

+//LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN

+//ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE

+//POSSIBILITY OF SUCH DAMAGE.

+//

+

+#ifndef _REFLECTION_INCLUDED

+#define _REFLECTION_INCLUDED

+

+#include "../Public/ShaderLang.h"

+

+#include <list>

+#include <set>

+

+//

+// A reflection database and its interface, consistent with the OpenGL API reflection queries.

+//

+

+namespace glslang {

+

+class TIntermediate;

+class TIntermAggregate;

+class TLiveTraverser;

+

+// Data needed for just a single object at the granularity exchanged by the reflection API

+class TObjectReflection {

+public:

+    TObjectReflection(const TString pName, int pOffset, int pGLDefineType, int pSize, int pIndex) : 

+        name(pName), offset(pOffset), glDefineType(pGLDefineType), size(pSize), index(pIndex) { }

+    void dump() const { printf("%s: offset %d, type %d, arraySize %d, index %d\n", name.c_str(), offset, glDefineType, size, index); }

+    const TString name;

+    int offset;

+    int glDefineType;

+    int size;         // data size in bytes for a block, array size for a (non-block) object that's an array

+    int index;

+};

+

+// The full reflection database

+class TReflection {

+public:

+    TReflection() : badReflection("__bad__", -1, -1, -1, -1) {}

+    virtual ~TReflection() {}

+

+    // grow the reflection stage by stage

+    bool addStage(EShLanguage, const TIntermediate&);

+

+    // for mapping a uniform index to a uniform object's description

+    int getNumUniforms() { return indexToUniform.size(); }

+    const TObjectReflection& getUniform(int i) const

+    {

+        if (i >= 0 && i < (int)indexToUniform.size())

+            return indexToUniform[i];

+        else

+            return badReflection;

+    }

+

+    // for mapping a block index to the block's description

+    int getNumUniformBlocks() const { return indexToUniformBlock.size(); }

+    const TObjectReflection& getUniformBlock(int i) const 

+    {

+        if (i >= 0 && i < (int)indexToUniformBlock.size())

+            return indexToUniformBlock[i];

+        else

+            return badReflection;

+    }

+

+    // for mapping any name to its index (both block names and uniforms names)

+    int getIndex(const char* name) const 

+    {

+        TNameToIndex::const_iterator it = nameToIndex.find(name);

+        if (it == nameToIndex.end())

+            return -1;

+        else

+            return it->second;

+    }

+

+    void dump();

+

+protected:

+    friend glslang::TLiveTraverser;

+

+    typedef std::map<TString, int> TNameToIndex;

+    typedef std::vector<TObjectReflection> TMapIndexToReflection;

+

+    TObjectReflection badReflection; // return for queries of -1 or generally out of range; has expected descriptions with in it for this

+    TNameToIndex nameToIndex;        // maps names to indexes; can hold all types of data: uniform/buffer and which function names have been processed

+    TMapIndexToReflection indexToUniform;

+    TMapIndexToReflection indexToUniformBlock;

+};

+

+} // end namespace glslang

+

+#endif _REFLECTION_INCLUDED

diff --git a/glslang/Public/ShaderLang.h b/glslang/Public/ShaderLang.h
index f9d3336..0ab620f 100644
--- a/glslang/Public/ShaderLang.h
+++ b/glslang/Public/ShaderLang.h
@@ -305,6 +305,8 @@
     TShader& operator=(TShader&);
 };
 
+class TReflection;
+
 // Make one TProgram per set of shaders that will get linked together.  Add all 
 // the shaders that are to be linked together.  After calling shader.parse()
 // for all shaders, call link().
@@ -316,17 +318,36 @@
     TProgram();
     virtual ~TProgram();
     void addShader(TShader* shader) { stages[shader->stage].push_back(shader); }
+
+    // Link Validation interface
     bool link(EShMessages);
     const char* getInfoLog();
     const char* getInfoDebugLog();
+
+    // Reflection Interface
+    bool buildReflection();                     // call first, to do liveness analysis, index mapping, etc.; returns false on failure
+    int getNumLiveUniformVariables();           // can be used for glGetProgramiv(GL_ACTIVE_UNIFORMS)
+    int getNumLiveUniformBlocks();              // can be used for glGetProgramiv(GL_ACTIVE_UNIFORM_BLOCKS)
+    const char* getUniformName(int index);      // can be used for "name" part of glGetActiveUniform()
+    const char* getUniformBlockName(int index); // can be used for glGetActiveUniformBlockName()
+    int getUniformBlockSize(int index);         // can be used for glGetActiveUniformBlockiv(UNIFORM_BLOCK_DATA_SIZE)
+    int getUniformIndex(const char* name);      // can be used for glGetUniformIndices()
+    int getUniformBlockIndex(int index);        // can be used for glGetActiveUniformsiv(GL_UNIFORM_BLOCK_INDEX)
+    int getUniformType(int index);              // can be used for glGetActiveUniformsiv(GL_UNIFORM_TYPE)
+    int getUniformBufferOffset(int index);      // can be used for glGetActiveUniformsiv(GL_UNIFORM_OFFSET)
+    int getUniformArraySize(int index);         // can be used for glGetActiveUniformsiv(GL_UNIFORM_SIZE)
+    void dumpReflection();
+
 protected:
     bool linkStage(EShLanguage, EShMessages);
 
-protected:
     TPoolAllocator* pool;
     std::list<TShader*> stages[EShLangCount];
     TIntermediate* intermediate[EShLangCount];
+    bool newedIntermediate[EShLangCount];      // track which intermediate were "new" versus reusing a singleton unit in a stage
     TInfoSink* infoSink;
+    TReflection* reflection;
+    bool linked;
 
 private:
     TProgram& operator=(TProgram&);