Add the SH_TIMING_RESTRICTIONS compile flag and dependency graph implementation.

Description of the algorithm:
http://code.google.com/p/mvujovic/wiki/ShaderControlFlowAnalysis

This flag is one potential solution to timing attacks on textures containing cross-domain content
or user agent data.
This kind of analysis could be useful for both WebGL and CSS Shaders.

The SH_TIMING_RESTRICTIONS flag will reject a shader if it uses texture dependent data to affect
control flow.

Other ways of affecting shader timing such as using NaNs in basic arithmetic operations or using
built-in functions (e.g. atan) with different inputs are still under investigation.

Issue=329
Review URL: http://codereview.appspot.com/6195062/



git-svn-id: https://angleproject.googlecode.com/svn/trunk@1101 736b8ea6-26fd-11df-bfd4-992fa37f6226
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index b376845..651ba51 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -39,6 +39,10 @@
 Apple Inc.

  David Kilzer

 

+Adobe Systems Inc.

+ Alexandru Chiculita

+ Max Vujovic

+

 Aitor Moreno <aitormoreno at gmail.com>

 Jim Hauxwell <james at dattrax.co.uk>

 ddefrostt

diff --git a/include/GLSLANG/ShaderLang.h b/include/GLSLANG/ShaderLang.h
index a3fc935..16c3e09 100644
--- a/include/GLSLANG/ShaderLang.h
+++ b/include/GLSLANG/ShaderLang.h
@@ -34,7 +34,7 @@
 
 // Version number for shader translation API.
 // It is incremented everytime the API changes.
-#define SH_VERSION 105
+#define SH_VERSION 106
 
 //
 // The names of the following enums have been derived by replacing GL prefix
@@ -104,7 +104,23 @@
   SH_UNROLL_FOR_LOOP_WITH_INTEGER_INDEX = 0x0080,
 
   // This is needed only as a workaround for certain OpenGL driver bugs.
-  SH_EMULATE_BUILT_IN_FUNCTIONS = 0x0100
+  SH_EMULATE_BUILT_IN_FUNCTIONS = 0x0100,
+
+  // This is an experimental flag to enforce restrictions that aim to prevent 
+  // timing attacks.
+  // It generates compilation errors for shaders that could expose sensitive
+  // texture information via the timing channel.
+  // To use this flag, you must compile the shader under the WebGL spec
+  // (using the SH_WEBGL_SPEC flag).
+  SH_TIMING_RESTRICTIONS = 0x0200,
+    
+  // This flag prints the dependency graph that is used to enforce timing
+  // restrictions on fragment shaders.
+  // This flag only has an effect if all of the following are true:
+  // - The shader spec is SH_WEBGL_SPEC.
+  // - The compile options contain the SH_TIMING_RESTRICTIONS flag.
+  // - The shader type is SH_FRAGMENT_SHADER.
+  SH_DEPENDENCY_GRAPH = 0x0400
 } ShCompileOptions;
 
 //
diff --git a/samples/translator/translator.cpp b/samples/translator/translator.cpp
index 8f45f25..35e187f 100644
--- a/samples/translator/translator.cpp
+++ b/samples/translator/translator.cpp
@@ -67,6 +67,7 @@
     char* buffer = 0;
     int bufferLen = 0;
     int numAttribs = 0, numUniforms = 0;
+    ShShaderSpec spec = SH_GLES2_SPEC;
     ShShaderOutput output = SH_ESSL_OUTPUT;
 
     ShInitialize();
@@ -85,6 +86,17 @@
             case 'u': compileOptions |= SH_ATTRIBUTES_UNIFORMS; break;
             case 'l': compileOptions |= SH_UNROLL_FOR_LOOP_WITH_INTEGER_INDEX; break;
             case 'e': compileOptions |= SH_EMULATE_BUILT_IN_FUNCTIONS; break;
+            case 'd': compileOptions |= SH_DEPENDENCY_GRAPH; break;
+            case 't': compileOptions |= SH_TIMING_RESTRICTIONS; break;
+            case 's':
+                if (argv[0][2] == '=') {
+                    switch (argv[0][3]) {
+                        case 'e': spec = SH_GLES2_SPEC; break;
+                        case 'w': spec = SH_WEBGL_SPEC; break;
+                        default: failCode = EFailUsage;
+                    }                    
+                }
+                break;
             case 'b':
                 if (argv[0][2] == '=') {
                     switch (argv[0][3]) {
@@ -116,13 +128,13 @@
             case SH_VERTEX_SHADER:
                 if (vertexCompiler == 0)
                     vertexCompiler = ShConstructCompiler(
-                        SH_VERTEX_SHADER, SH_GLES2_SPEC, output, &resources);
+                        SH_VERTEX_SHADER, spec, output, &resources);
                 compiler = vertexCompiler;
                 break;
             case SH_FRAGMENT_SHADER:
                 if (fragmentCompiler == 0)
                     fragmentCompiler = ShConstructCompiler(
-                        SH_FRAGMENT_SHADER, SH_GLES2_SPEC, output, &resources);
+                        SH_FRAGMENT_SHADER, spec, output, &resources);
                 compiler = fragmentCompiler;
                 break;
             default: break;
@@ -196,6 +208,10 @@
         "       -u       : print active attribs and uniforms\n"
         "       -l       : unroll for-loops with integer indices\n"
         "       -e       : emulate certain built-in functions (workaround for driver bugs)\n"
+        "       -t       : enforce experimental timing restrictions\n"
+        "       -d       : print dependency graph used to enforce timing restrictions\n"
+        "       -s=e     : use GLES2 spec (this is by default)\n"
+        "       -s=w     : use WebGL spec\n"
         "       -b=e     : output GLSL ES code (this is by default)\n"
         "       -b=g     : output GLSL code\n"
         "       -b=h     : output HLSL code\n"
diff --git a/src/build_angle.gyp b/src/build_angle.gyp
index 359a29e..59d6efe 100644
--- a/src/build_angle.gyp
+++ b/src/build_angle.gyp
@@ -126,6 +126,19 @@
         'compiler/preprocessor/symbols.h',
         'compiler/preprocessor/tokens.c',
         'compiler/preprocessor/tokens.h',
+        # Dependency graph
+        'compiler/depgraph/DependencyGraph.cpp',
+        'compiler/depgraph/DependencyGraph.h',
+        'compiler/depgraph/DependencyGraphBuilder.cpp',
+        'compiler/depgraph/DependencyGraphBuilder.h',
+        'compiler/depgraph/DependencyGraphOutput.cpp',
+        'compiler/depgraph/DependencyGraphOutput.h',
+        'compiler/depgraph/DependencyGraphTraverse.cpp',
+        # Timing restrictions
+        'compiler/timing/RestrictFragmentShaderTiming.cpp',
+        'compiler/timing/RestrictFragmentShaderTiming.h',
+        'compiler/timing/RestrictVertexShaderTiming.cpp',
+        'compiler/timing/RestrictVertexShaderTiming.h',
       ],
       'conditions': [
         ['OS=="win"', {
diff --git a/src/compiler/Compiler.cpp b/src/compiler/Compiler.cpp
index f27cb75..c6fcb41 100644
--- a/src/compiler/Compiler.cpp
+++ b/src/compiler/Compiler.cpp
@@ -12,6 +12,10 @@
 #include "compiler/ParseHelper.h"
 #include "compiler/ShHandle.h"
 #include "compiler/ValidateLimitations.h"
+#include "compiler/depgraph/DependencyGraph.h"
+#include "compiler/depgraph/DependencyGraphOutput.h"
+#include "compiler/timing/RestrictFragmentShaderTiming.h"
+#include "compiler/timing/RestrictVertexShaderTiming.h"
 
 namespace {
 bool InitializeSymbolTable(
@@ -161,6 +165,13 @@
         if (success && (compileOptions & SH_VALIDATE_LOOP_INDEXING))
             success = validateLimitations(root);
 
+        // FIXME(mvujovic): For now, we only consider "u_texture" to be a potentially unsafe symbol.
+        // If we end up using timing restrictions in WebGL and CSS Shaders, we should expose an API
+        // to pass in the names of other potentially unsafe symbols (e.g. uniforms referencing 
+        // cross-domain textures).
+        if (success && (compileOptions & SH_TIMING_RESTRICTIONS))
+            success = enforceTimingRestrictions(root, "u_texture", (compileOptions & SH_DEPENDENCY_GRAPH) != 0);
+
         // Unroll for-loop markup needs to happen after validateLimitations pass.
         if (success && (compileOptions & SH_UNROLL_FOR_LOOP_WITH_INTEGER_INDEX))
             ForLoopUnroll::MarkForLoopsWithIntegerIndicesForUnrolling(root);
@@ -241,6 +252,50 @@
     return validate.numErrors() == 0;
 }
 
+bool TCompiler::enforceTimingRestrictions(TIntermNode* root,
+                                          const TString& restrictedSymbol,
+                                          bool outputGraph)
+{
+    if (shaderSpec != SH_WEBGL_SPEC) {
+        infoSink.info << "Timing restrictions must be enforced under the WebGL spec.";
+        return false;
+    }
+
+    if (shaderType == SH_FRAGMENT_SHADER) {
+        TDependencyGraph graph(root);
+
+        // Output any errors first.
+        bool success = enforceFragmentShaderTimingRestrictions(graph, restrictedSymbol);
+        
+        // Then, output the dependency graph.
+        if (outputGraph) {
+            TDependencyGraphOutput output(infoSink.info);
+            output.outputAllSpanningTrees(graph);
+        }
+        
+        return success;
+    }
+    else {
+        return enforceVertexShaderTimingRestrictions(root, restrictedSymbol);
+    }
+}
+
+bool TCompiler::enforceFragmentShaderTimingRestrictions(const TDependencyGraph& graph,
+                                                        const TString& restrictedSymbol)
+{
+    RestrictFragmentShaderTiming restrictor(infoSink.info, restrictedSymbol);
+    restrictor.enforceRestrictions(graph);
+    return restrictor.numErrors() == 0;
+}
+
+bool TCompiler::enforceVertexShaderTimingRestrictions(TIntermNode* root,
+                                                      const TString& restrictedSymbol)
+{
+    RestrictVertexShaderTiming restrictor(infoSink.info, restrictedSymbol);
+    restrictor.enforceRestrictions(root);
+    return restrictor.numErrors() == 0;
+}
+
 void TCompiler::collectAttribsUniforms(TIntermNode* root)
 {
     CollectAttribsUniforms collect(attribs, uniforms);
diff --git a/src/compiler/ShHandle.h b/src/compiler/ShHandle.h
index 91c47e7..0faaeb1 100644
--- a/src/compiler/ShHandle.h
+++ b/src/compiler/ShHandle.h
@@ -24,6 +24,7 @@
 
 class LongNameMap;
 class TCompiler;
+class TDependencyGraph;
 
 //
 // The base class used to back handles returned to the driver.
@@ -79,6 +80,17 @@
     void mapLongVariableNames(TIntermNode* root);
     // Translate to object code.
     virtual void translate(TIntermNode* root) = 0;
+    // Returns true if the shader passes the restrictions that aim to prevent timing attacks.
+    bool enforceTimingRestrictions(TIntermNode* root,
+                                   const TString& restrictedSymbol,
+                                   bool outputGraph);
+    // Returns true if the shader does not define the restricted symbol.
+    bool enforceVertexShaderTimingRestrictions(TIntermNode* root,
+                                               const TString& restrictedSymbol);
+    // Returns true if the shader does not use the restricted symbol to affect control flow or in
+    // operations whose time can depend on the input values.
+    bool enforceFragmentShaderTimingRestrictions(const TDependencyGraph& graph,
+                                                 const TString& restrictedSymbol);
     // Get built-in extensions with default behavior.
     const TExtensionBehavior& getExtensionBehavior() const;
 
diff --git a/src/compiler/depgraph/DependencyGraph.cpp b/src/compiler/depgraph/DependencyGraph.cpp
new file mode 100644
index 0000000..0f1ae79
--- /dev/null
+++ b/src/compiler/depgraph/DependencyGraph.cpp
@@ -0,0 +1,109 @@
+//
+// Copyright (c) 2012 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "compiler/depgraph/DependencyGraph.h"
+#include "compiler/depgraph/DependencyGraphBuilder.h"
+
+TDependencyGraph::TDependencyGraph(TIntermNode* intermNode)
+{
+    TDependencyGraphBuilder::build(intermNode, this);
+}
+
+TDependencyGraph::~TDependencyGraph()
+{
+    for (TGraphNodeVector::const_iterator iter = mAllNodes.begin(); iter != mAllNodes.end(); ++iter)
+    {
+        TGraphNode* node = *iter;
+        delete node;
+    }
+}
+
+TGraphSymbol* TDependencyGraph::getGlobalSymbolByName(const TString& name) const
+{
+    TSymbolNameMap::const_iterator iter = mGlobalSymbolMap.find(name);
+    if (iter == mGlobalSymbolMap.end())
+        return NULL;
+
+    TSymbolNamePair pair = *iter;
+    TGraphSymbol* symbol = pair.second;
+    return symbol;
+}
+
+TGraphArgument* TDependencyGraph::createArgument(TIntermAggregate* intermFunctionCall,
+                                                 int argumentNumber)
+{
+    TGraphArgument* argument = new TGraphArgument(intermFunctionCall, argumentNumber);
+    mAllNodes.push_back(argument);
+    return argument;
+}
+
+TGraphFunctionCall* TDependencyGraph::createFunctionCall(TIntermAggregate* intermFunctionCall)
+{
+    TGraphFunctionCall* functionCall = new TGraphFunctionCall(intermFunctionCall);
+    mAllNodes.push_back(functionCall);
+    if (functionCall->getIntermFunctionCall()->isUserDefined())
+        mUserDefinedFunctionCalls.push_back(functionCall);
+    return functionCall;
+}
+
+TGraphSymbol* TDependencyGraph::getOrCreateSymbol(TIntermSymbol* intermSymbol, bool isGlobalSymbol)
+{
+    TSymbolIdMap::const_iterator iter = mSymbolIdMap.find(intermSymbol->getId());
+
+    TGraphSymbol* symbol = NULL;
+
+    if (iter != mSymbolIdMap.end()) {
+        TSymbolIdPair pair = *iter;
+        symbol = pair.second;
+    } else {
+        symbol = new TGraphSymbol(intermSymbol);
+        mAllNodes.push_back(symbol);
+
+        TSymbolIdPair pair(intermSymbol->getId(), symbol);
+        mSymbolIdMap.insert(pair);
+
+        if (isGlobalSymbol) {
+            // We map all symbols in the global scope by name, so traversers of the graph can
+            // quickly start searches at global symbols with specific names.
+            TSymbolNamePair pair(intermSymbol->getSymbol(), symbol);
+            mGlobalSymbolMap.insert(pair);
+        }
+    }
+
+    return symbol;
+}
+
+TGraphSelection* TDependencyGraph::createSelection(TIntermSelection* intermSelection)
+{
+    TGraphSelection* selection = new TGraphSelection(intermSelection);
+    mAllNodes.push_back(selection);
+    return selection;
+}
+
+TGraphLoop* TDependencyGraph::createLoop(TIntermLoop* intermLoop)
+{
+    TGraphLoop* loop = new TGraphLoop(intermLoop);
+    mAllNodes.push_back(loop);
+    return loop;
+}
+
+TGraphLogicalOp* TDependencyGraph::createLogicalOp(TIntermBinary* intermLogicalOp)
+{
+    TGraphLogicalOp* logicalOp = new TGraphLogicalOp(intermLogicalOp);
+    mAllNodes.push_back(logicalOp);
+    return logicalOp;
+}
+
+const char* TGraphLogicalOp::getOpString() const
+{
+    const char* opString = NULL;
+    switch (getIntermLogicalOp()->getOp()) {
+        case EOpLogicalAnd: opString = "and"; break;
+        case EOpLogicalOr: opString = "or"; break;
+        default: opString = "unknown"; break;
+    }
+    return opString;
+}
diff --git a/src/compiler/depgraph/DependencyGraph.h b/src/compiler/depgraph/DependencyGraph.h
new file mode 100644
index 0000000..14aefb9
--- /dev/null
+++ b/src/compiler/depgraph/DependencyGraph.h
@@ -0,0 +1,207 @@
+//
+// Copyright (c) 2012 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef COMPILER_DEPGRAPH_DEPENDENCY_GRAPH_H
+#define COMPILER_DEPGRAPH_DEPENDENCY_GRAPH_H
+
+#include "compiler/intermediate.h"
+
+#include <set>
+#include <stack>
+
+class TGraphNode;
+class TGraphParentNode;
+class TGraphArgument;
+class TGraphFunctionCall;
+class TGraphSymbol;
+class TGraphSelection;
+class TGraphLoop;
+class TGraphLogicalOp;
+class TDependencyGraphTraverser;
+class TDependencyGraphOutput;
+
+typedef std::set<TGraphNode*> TGraphNodeSet;
+typedef std::vector<TGraphNode*> TGraphNodeVector;
+typedef std::vector<TGraphFunctionCall*> TFunctionCallVector;
+
+//
+// Base class for all dependency graph nodes.
+//
+class TGraphNode {
+public:
+    TGraphNode(TIntermNode* node) : intermNode(node) {}
+    virtual ~TGraphNode() {}
+    virtual void traverse(TDependencyGraphTraverser* graphTraverser);
+protected:
+    TIntermNode* intermNode;
+};
+
+//
+// Base class for dependency graph nodes that may have children.
+//
+class TGraphParentNode : public TGraphNode {
+public:
+    TGraphParentNode(TIntermNode* node) : TGraphNode(node) {}
+    virtual ~TGraphParentNode() {}
+    void addDependentNode(TGraphNode* node) { if (node != this) mDependentNodes.insert(node); }
+    virtual void traverse(TDependencyGraphTraverser* graphTraverser);
+private:
+    TGraphNodeSet mDependentNodes;
+};
+
+//
+// Handle function call arguments.
+//
+class TGraphArgument : public TGraphParentNode {
+public:
+    TGraphArgument(TIntermAggregate* intermFunctionCall, int argumentNumber)
+        : TGraphParentNode(intermFunctionCall)
+        , mArgumentNumber(argumentNumber) {}
+    virtual ~TGraphArgument() {}
+    const TIntermAggregate* getIntermFunctionCall() const { return intermNode->getAsAggregate(); }
+    int getArgumentNumber() const { return mArgumentNumber; }
+    virtual void traverse(TDependencyGraphTraverser* graphTraverser);
+private:
+    int mArgumentNumber;
+};
+
+//
+// Handle function calls.
+//
+class TGraphFunctionCall : public TGraphParentNode {
+public:
+    TGraphFunctionCall(TIntermAggregate* intermFunctionCall)
+        : TGraphParentNode(intermFunctionCall) {}
+    virtual ~TGraphFunctionCall() {}
+    const TIntermAggregate* getIntermFunctionCall() const { return intermNode->getAsAggregate(); }
+    virtual void traverse(TDependencyGraphTraverser* graphTraverser);
+};
+
+//
+// Handle symbols.
+//
+class TGraphSymbol : public TGraphParentNode {
+public:
+    TGraphSymbol(TIntermSymbol* intermSymbol) : TGraphParentNode(intermSymbol) {}
+    virtual ~TGraphSymbol() {}
+    const TIntermSymbol* getIntermSymbol() const { return intermNode->getAsSymbolNode(); }
+    virtual void traverse(TDependencyGraphTraverser* graphTraverser);
+};
+
+//
+// Handle if statements and ternary operators.
+//
+class TGraphSelection : public TGraphNode {
+public:
+    TGraphSelection(TIntermSelection* intermSelection) : TGraphNode(intermSelection) {}
+    virtual ~TGraphSelection() {}
+    const TIntermSelection* getIntermSelection() const { return intermNode->getAsSelectionNode(); }
+    virtual void traverse(TDependencyGraphTraverser* graphTraverser);
+};
+
+//
+// Handle for, do-while, and while loops.
+//
+class TGraphLoop : public TGraphNode {
+public:
+    TGraphLoop(TIntermLoop* intermLoop) : TGraphNode(intermLoop) {}
+    virtual ~TGraphLoop() {}
+    const TIntermLoop* getIntermLoop() const { return intermNode->getAsLoopNode(); }
+    virtual void traverse(TDependencyGraphTraverser* graphTraverser);
+};
+
+//
+// Handle logical and, or.
+//
+class TGraphLogicalOp : public TGraphNode {
+public:
+    TGraphLogicalOp(TIntermBinary* intermLogicalOp) : TGraphNode(intermLogicalOp) {}
+    virtual ~TGraphLogicalOp() {}
+    const TIntermBinary* getIntermLogicalOp() const { return intermNode->getAsBinaryNode(); }
+    const char* getOpString() const;
+    virtual void traverse(TDependencyGraphTraverser* graphTraverser);
+};
+
+//
+// A dependency graph of symbols, function calls, conditions etc.
+//
+// This class provides an interface to the entry points of the dependency graph.
+//
+// Dependency graph nodes should be created by using one of the provided "create..." methods.
+// This class (and nobody else) manages the memory of the created nodes.
+// Nodes may not be removed after being added, so all created nodes will exist while the
+// TDependencyGraph instance exists.
+//
+class TDependencyGraph {
+public:
+    TDependencyGraph(TIntermNode* intermNode);
+    ~TDependencyGraph();
+    TGraphNodeVector::const_iterator begin() const { return mAllNodes.begin(); }
+    TGraphNodeVector::const_iterator end() const { return mAllNodes.end(); }
+
+    TFunctionCallVector::const_iterator beginUserDefinedFunctionCalls() const
+    {
+        return mUserDefinedFunctionCalls.begin();
+    }
+
+    TFunctionCallVector::const_iterator endUserDefinedFunctionCalls() const
+    {
+        return mUserDefinedFunctionCalls.end();
+    }
+
+    // Returns NULL if the symbol is not found.
+    TGraphSymbol* getGlobalSymbolByName(const TString& name) const;
+
+    TGraphArgument* createArgument(TIntermAggregate* intermFunctionCall, int argumentNumber);
+    TGraphFunctionCall* createFunctionCall(TIntermAggregate* intermFunctionCall);
+    TGraphSymbol* getOrCreateSymbol(TIntermSymbol* intermSymbol, bool isGlobalSymbol);
+    TGraphSelection* createSelection(TIntermSelection* intermSelection);
+    TGraphLoop* createLoop(TIntermLoop* intermLoop);
+    TGraphLogicalOp* createLogicalOp(TIntermBinary* intermLogicalOp);
+private:
+    typedef TMap<int, TGraphSymbol*> TSymbolIdMap;
+    typedef std::pair<int, TGraphSymbol*> TSymbolIdPair;
+
+    typedef TMap<TString, TGraphSymbol*> TSymbolNameMap;
+    typedef std::pair<TString, TGraphSymbol*> TSymbolNamePair;
+
+    TSymbolIdMap mSymbolIdMap;
+    TSymbolNameMap mGlobalSymbolMap;
+    TFunctionCallVector mUserDefinedFunctionCalls;
+    TGraphNodeVector mAllNodes;
+};
+
+//
+// For traversing the dependency graph. Users should derive from this,
+// put their traversal specific data in it, and then pass it to a
+// traverse method.
+//
+// When using this, just fill in the methods for nodes you want visited.
+//
+class TDependencyGraphTraverser {
+public:
+    TDependencyGraphTraverser() : mDepth(0) {}
+
+    virtual void visitSymbol(TGraphSymbol* symbol) {};
+    virtual void visitArgument(TGraphArgument* selection) {};
+    virtual void visitFunctionCall(TGraphFunctionCall* functionCall) {};
+    virtual void visitSelection(TGraphSelection* selection) {};
+    virtual void visitLoop(TGraphLoop* loop) {};
+    virtual void visitLogicalOp(TGraphLogicalOp* logicalOp) {};
+
+    int getDepth() const { return mDepth; }
+    void incrementDepth() { ++mDepth; }
+    void decrementDepth() { --mDepth; }
+
+    void clearVisited() { mVisited.clear(); }
+    void markVisited(TGraphNode* node) { mVisited.insert(node); }
+    bool isVisited(TGraphNode* node) const { return mVisited.find(node) != mVisited.end(); }
+private:
+    int mDepth;
+    TGraphNodeSet mVisited;
+};
+
+#endif
diff --git a/src/compiler/depgraph/DependencyGraphBuilder.cpp b/src/compiler/depgraph/DependencyGraphBuilder.cpp
new file mode 100644
index 0000000..8e45c37
--- /dev/null
+++ b/src/compiler/depgraph/DependencyGraphBuilder.cpp
@@ -0,0 +1,241 @@
+//
+// Copyright (c) 2012 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "compiler/depgraph/DependencyGraphBuilder.h"
+
+TDependencyGraphBuilder::TLeftmostSymbolMaintainer::TSubtreePlaceholder
+    TDependencyGraphBuilder::TLeftmostSymbolMaintainer::kLeftSubtree;
+
+TDependencyGraphBuilder::TLeftmostSymbolMaintainer::TSubtreePlaceholder
+    TDependencyGraphBuilder::TLeftmostSymbolMaintainer::kRightSubtree;
+
+void TDependencyGraphBuilder::build(TIntermNode* node, TDependencyGraph* graph)
+{
+    TDependencyGraphBuilder builder(graph);
+    builder.build(node);
+}
+
+bool TDependencyGraphBuilder::visitAggregate(Visit visit, TIntermAggregate* intermAggregate)
+{
+    switch (intermAggregate->getOp()) {
+        case EOpFunction: visitFunctionDefinition(intermAggregate); break;
+        case EOpFunctionCall: visitFunctionCall(intermAggregate); break;
+        default: visitAggregateChildren(intermAggregate); break;
+    }
+
+    return false;
+}
+
+void TDependencyGraphBuilder::visitFunctionDefinition(TIntermAggregate* intermAggregate)
+{
+    // Function defintions should only exist in the global scope.
+    ASSERT(mIsGlobalScope);
+
+    // Currently, we do not support user defined functions.
+    if (intermAggregate->getName() != "main(")
+        return;
+
+    mIsGlobalScope = false;
+
+    visitAggregateChildren(intermAggregate);
+
+    mIsGlobalScope = true;
+}
+
+// Takes an expression like "f(x)" and creates a dependency graph like
+// "x -> argument 0 -> function call".
+void TDependencyGraphBuilder::visitFunctionCall(TIntermAggregate* intermFunctionCall)
+{
+    TGraphFunctionCall* functionCall = mGraph->createFunctionCall(intermFunctionCall);
+
+    // Run through the function call arguments.
+    int argumentNumber = 0;
+    TIntermSequence& intermArguments = intermFunctionCall->getSequence();
+    for (TIntermSequence::const_iterator iter = intermArguments.begin();
+         iter != intermArguments.end();
+         ++iter, ++argumentNumber)
+    {
+        TNodeSetMaintainer nodeSetMaintainer(this);
+
+        TIntermNode* intermArgument = *iter;
+        intermArgument->traverse(this);
+
+        if (TParentNodeSet* argumentNodes = mNodeSets.getTopSet()) {
+            TGraphArgument* argument = mGraph->createArgument(intermFunctionCall, argumentNumber);
+            connectMultipleNodesToSingleNode(argumentNodes, argument);
+            argument->addDependentNode(functionCall);
+        }
+    }
+
+    // Push the leftmost symbol of this function call into the current set of dependent symbols to
+    // represent the result of this function call.
+    // Thus, an expression like "y = f(x)" will yield a dependency graph like
+    // "x -> argument 0 -> function call -> y".
+    // This line essentially passes the function call node back up to an earlier visitAssignment
+    // call, which will create the connection "function call -> y".
+    mNodeSets.insertIntoTopSet(functionCall);
+}
+
+void TDependencyGraphBuilder::visitAggregateChildren(TIntermAggregate* intermAggregate)
+{
+    TIntermSequence& sequence = intermAggregate->getSequence();
+    for(TIntermSequence::const_iterator iter = sequence.begin(); iter != sequence.end(); ++iter)
+    {
+        TIntermNode* intermChild = *iter;
+        intermChild->traverse(this);
+    }
+}
+
+void TDependencyGraphBuilder::visitSymbol(TIntermSymbol* intermSymbol)
+{
+    // Push this symbol into the set of dependent symbols for the current assignment or condition
+    // that we are traversing.
+    TGraphSymbol* symbol = mGraph->getOrCreateSymbol(intermSymbol, mIsGlobalScope);
+    mNodeSets.insertIntoTopSet(symbol);
+
+    // If this symbol is the current leftmost symbol under an assignment, replace the previous
+    // leftmost symbol with this symbol.
+    if (!mLeftmostSymbols.empty() && mLeftmostSymbols.top() !=
+        &TLeftmostSymbolMaintainer::kRightSubtree) {
+        mLeftmostSymbols.pop();
+        mLeftmostSymbols.push(symbol);
+    }
+}
+
+bool TDependencyGraphBuilder::visitBinary(Visit visit, TIntermBinary* intermBinary)
+{
+    TOperator op = intermBinary->getOp();
+    if (op == EOpInitialize || intermBinary->modifiesState())
+        visitAssignment(intermBinary);
+    else if (op == EOpLogicalAnd || op == EOpLogicalOr)
+        visitLogicalOp(intermBinary);
+    else
+        visitBinaryChildren(intermBinary);
+
+    return false;
+}
+
+void TDependencyGraphBuilder::visitAssignment(TIntermBinary* intermAssignment)
+{
+    TIntermTyped* intermLeft = intermAssignment->getLeft();
+    if (!intermLeft)
+        return;
+
+    TGraphSymbol* leftmostSymbol = NULL;
+
+    {
+        TNodeSetMaintainer nodeSetMaintainer(this);
+
+        {
+            TLeftmostSymbolMaintainer leftmostSymbolMaintainer(this, TLeftmostSymbolMaintainer::kLeftSubtree);
+            intermLeft->traverse(this);
+            leftmostSymbol = mLeftmostSymbols.top();
+
+            // After traversing the left subtree of this assignment, we should have found a real
+            // leftmost symbol, and the leftmost symbol should not be a placeholder.
+            ASSERT(leftmostSymbol != &TLeftmostSymbolMaintainer::kLeftSubtree);
+            ASSERT(leftmostSymbol != &TLeftmostSymbolMaintainer::kRightSubtree);
+        }
+
+        if (TIntermTyped* intermRight = intermAssignment->getRight()) {
+            TLeftmostSymbolMaintainer leftmostSymbolMaintainer(this, TLeftmostSymbolMaintainer::kRightSubtree);
+            intermRight->traverse(this);
+        }
+
+        if (TParentNodeSet* assignmentNodes = mNodeSets.getTopSet())
+            connectMultipleNodesToSingleNode(assignmentNodes, leftmostSymbol);
+    }
+
+    // Push the leftmost symbol of this assignment into the current set of dependent symbols to
+    // represent the result of this assignment.
+    // An expression like "a = (b = c)" will yield a dependency graph like "c -> b -> a".
+    // This line essentially passes the leftmost symbol of the nested assignment ("b" in this
+    // example) back up to the earlier visitAssignment call for the outer assignment, which will
+    // create the connection "b -> a".
+    mNodeSets.insertIntoTopSet(leftmostSymbol);
+}
+
+void TDependencyGraphBuilder::visitLogicalOp(TIntermBinary* intermLogicalOp)
+{
+    if (TIntermTyped* intermLeft = intermLogicalOp->getLeft()) {
+        TNodeSetPropagatingMaintainer nodeSetMaintainer(this);
+
+        intermLeft->traverse(this);
+        if (TParentNodeSet* leftNodes = mNodeSets.getTopSet()) {
+            TGraphLogicalOp* logicalOp = mGraph->createLogicalOp(intermLogicalOp);
+            connectMultipleNodesToSingleNode(leftNodes, logicalOp);
+        }
+    }
+
+    if (TIntermTyped* intermRight = intermLogicalOp->getRight()) {
+        TLeftmostSymbolMaintainer leftmostSymbolMaintainer(this, TLeftmostSymbolMaintainer::kRightSubtree);
+        intermRight->traverse(this);
+    }
+}
+
+void TDependencyGraphBuilder::visitBinaryChildren(TIntermBinary* intermBinary)
+{
+    if (TIntermTyped* intermLeft = intermBinary->getLeft())
+        intermLeft->traverse(this);
+
+    if (TIntermTyped* intermRight = intermBinary->getRight()) {
+        TLeftmostSymbolMaintainer leftmostSymbolMaintainer(this, TLeftmostSymbolMaintainer::kRightSubtree);
+        intermRight->traverse(this);
+    }
+}
+
+bool TDependencyGraphBuilder::visitSelection(Visit visit, TIntermSelection* intermSelection)
+{
+    if (TIntermNode* intermCondition = intermSelection->getCondition()) {
+        TNodeSetMaintainer nodeSetMaintainer(this);
+
+        intermCondition->traverse(this);
+        if (TParentNodeSet* conditionNodes = mNodeSets.getTopSet()) {
+            TGraphSelection* selection = mGraph->createSelection(intermSelection);
+            connectMultipleNodesToSingleNode(conditionNodes, selection);
+        }
+    }
+
+    if (TIntermNode* intermTrueBlock = intermSelection->getTrueBlock())
+        intermTrueBlock->traverse(this);
+
+    if (TIntermNode* intermFalseBlock = intermSelection->getFalseBlock())
+        intermFalseBlock->traverse(this);
+
+    return false;
+}
+
+bool TDependencyGraphBuilder::visitLoop(Visit visit, TIntermLoop* intermLoop)
+{
+    if (TIntermTyped* intermCondition = intermLoop->getCondition()) {
+        TNodeSetMaintainer nodeSetMaintainer(this);
+
+        intermCondition->traverse(this);
+        if (TParentNodeSet* conditionNodes = mNodeSets.getTopSet()) {
+            TGraphLoop* loop = mGraph->createLoop(intermLoop);
+            connectMultipleNodesToSingleNode(conditionNodes, loop);
+        }
+    }
+
+    if (TIntermNode* intermBody = intermLoop->getBody())
+        intermBody->traverse(this);
+
+    if (TIntermTyped* intermExpression = intermLoop->getExpression())
+        intermExpression->traverse(this);
+
+    return false;
+}
+
+
+void TDependencyGraphBuilder::connectMultipleNodesToSingleNode(TParentNodeSet* nodes,
+                                                               TGraphNode* node) const
+{
+    for (TParentNodeSet::const_iterator iter = nodes->begin(); iter != nodes->end(); ++iter)
+    {
+        TGraphParentNode* currentNode = *iter;
+        currentNode->addDependentNode(node);
+    }
+}
diff --git a/src/compiler/depgraph/DependencyGraphBuilder.h b/src/compiler/depgraph/DependencyGraphBuilder.h
new file mode 100644
index 0000000..91bc490
--- /dev/null
+++ b/src/compiler/depgraph/DependencyGraphBuilder.h
@@ -0,0 +1,186 @@
+//
+// Copyright (c) 2012 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef COMPILER_DEPGRAPH_DEPENDENCY_GRAPH_BUILDER_H
+#define COMPILER_DEPGRAPH_DEPENDENCY_GRAPH_BUILDER_H
+
+#include "compiler/depgraph/DependencyGraph.h"
+
+//
+// Creates a dependency graph of symbols, function calls, conditions etc. by traversing a
+// intermediate tree.
+//
+class TDependencyGraphBuilder : public TIntermTraverser {
+public:
+    static void build(TIntermNode* node, TDependencyGraph* graph);
+
+    virtual void visitSymbol(TIntermSymbol*);
+    virtual bool visitBinary(Visit visit, TIntermBinary*);
+    virtual bool visitSelection(Visit visit, TIntermSelection*);
+    virtual bool visitAggregate(Visit visit, TIntermAggregate*);
+    virtual bool visitLoop(Visit visit, TIntermLoop*);
+
+private:
+    typedef std::stack<TGraphSymbol*> TSymbolStack;
+    typedef std::set<TGraphParentNode*> TParentNodeSet;
+
+    //
+    // For collecting the dependent nodes of assignments, conditions, etc.
+    // while traversing the intermediate tree.
+    //
+    // This data structure is stack of sets. Each set contains dependency graph parent nodes.
+    //
+    class TNodeSetStack {
+    public:
+        TNodeSetStack() {};
+        ~TNodeSetStack() { clear(); }
+
+        // This should only be called after a pushSet.
+        // Returns NULL if the top set is empty.
+        TParentNodeSet* getTopSet() const
+        {
+            ASSERT(!nodeSets.empty());
+            TParentNodeSet* topSet = nodeSets.top();
+            return !topSet->empty() ? topSet : NULL;
+        }
+
+        void pushSet() { nodeSets.push(new TParentNodeSet()); }
+        void popSet()
+        {
+            ASSERT(!nodeSets.empty());
+            delete nodeSets.top();
+            nodeSets.pop();
+        }
+
+        // Pops the top set and adds its contents to the new top set.
+        // This should only be called after a pushSet.
+        // If there is no set below the top set, the top set is just deleted.
+        void popSetIntoNext()
+        {
+            ASSERT(!nodeSets.empty());
+            TParentNodeSet* oldTopSet = nodeSets.top();
+            nodeSets.pop();
+
+            if (!nodeSets.empty()) {
+                TParentNodeSet* newTopSet = nodeSets.top();
+                newTopSet->insert(oldTopSet->begin(), oldTopSet->end());
+            }
+
+            delete oldTopSet;
+        }
+
+        // Does nothing if there is no top set.
+        // This can be called when there is no top set if we are visiting
+        // symbols that are not under an assignment or condition.
+        // We don't need to track those symbols.
+        void insertIntoTopSet(TGraphParentNode* node)
+        {
+            if (nodeSets.empty())
+                return;
+
+            nodeSets.top()->insert(node);
+        }
+
+        void clear()
+        {
+            while (!nodeSets.empty())
+                popSet();
+        }
+
+    private:
+        typedef std::stack<TParentNodeSet*> TParentNodeSetStack;
+
+        TParentNodeSetStack nodeSets;
+    };
+
+    //
+    // An instance of this class pushes a new node set when instantiated.
+    // When the instance goes out of scope, it and pops the node set.
+    //
+    class TNodeSetMaintainer {
+    public:
+        TNodeSetMaintainer(TDependencyGraphBuilder* factory)
+            : sets(factory->mNodeSets) { sets.pushSet(); }
+        ~TNodeSetMaintainer() { sets.popSet(); }
+    protected:
+        TNodeSetStack& sets;
+    };
+
+    //
+    // An instance of this class pushes a new node set when instantiated.
+    // When the instance goes out of scope, it and pops the top node set and adds its contents to
+    // the new top node set.
+    //
+    class TNodeSetPropagatingMaintainer {
+    public:
+        TNodeSetPropagatingMaintainer(TDependencyGraphBuilder* factory)
+            : sets(factory->mNodeSets) { sets.pushSet(); }
+        ~TNodeSetPropagatingMaintainer() { sets.popSetIntoNext(); }
+    protected:
+        TNodeSetStack& sets;
+    };
+
+    //
+    // An instance of this class keeps track of the leftmost symbol while we're exploring an
+    // assignment.
+    // It will push the placeholder symbol kLeftSubtree when instantiated under a left subtree,
+    // and kRightSubtree under a right subtree.
+    // When it goes out of scope, it will pop the leftmost symbol at the top of the scope.
+    // During traversal, the TDependencyGraphBuilder will replace kLeftSubtree with a real symbol.
+    // kRightSubtree will never be replaced by a real symbol because we are tracking the leftmost
+    // symbol.
+    //
+    class TLeftmostSymbolMaintainer {
+    public:
+        class TSubtreePlaceholder : public TGraphSymbol {
+        public:
+            TSubtreePlaceholder() : TGraphSymbol(NULL) {}
+        };
+
+        static TSubtreePlaceholder kLeftSubtree;
+        static TSubtreePlaceholder kRightSubtree;
+
+        TLeftmostSymbolMaintainer(TDependencyGraphBuilder* factory, TSubtreePlaceholder& subtree)
+            : leftmostSymbols(factory->mLeftmostSymbols)
+        {
+            needsPlaceholderSymbol = leftmostSymbols.empty() || leftmostSymbols.top() != &subtree;
+            if (needsPlaceholderSymbol)
+                leftmostSymbols.push(&subtree);
+        }
+
+        ~TLeftmostSymbolMaintainer()
+        {
+            if (needsPlaceholderSymbol)
+                leftmostSymbols.pop();
+        }
+
+    protected:
+        TSymbolStack& leftmostSymbols;
+        bool needsPlaceholderSymbol;
+    };
+
+    TDependencyGraphBuilder(TDependencyGraph* graph)
+        : TIntermTraverser(true, false, false)
+        , mGraph(graph)
+        , mIsGlobalScope(true) {}
+    void build(TIntermNode* intermNode) { intermNode->traverse(this); }
+
+    void connectMultipleNodesToSingleNode(TParentNodeSet* nodes, TGraphNode* node) const;
+
+    void visitAssignment(TIntermBinary*);
+    void visitLogicalOp(TIntermBinary*);
+    void visitBinaryChildren(TIntermBinary*);
+    void visitFunctionDefinition(TIntermAggregate*);
+    void visitFunctionCall(TIntermAggregate* intermFunctionCall);
+    void visitAggregateChildren(TIntermAggregate*);
+
+    TDependencyGraph* mGraph;
+    TNodeSetStack mNodeSets;
+    TSymbolStack mLeftmostSymbols;
+    bool mIsGlobalScope;
+};
+
+#endif  // COMPILER_DEPGRAPH_DEPENDENCY_GRAPH_BUILDER_H
diff --git a/src/compiler/depgraph/DependencyGraphOutput.cpp b/src/compiler/depgraph/DependencyGraphOutput.cpp
new file mode 100644
index 0000000..6fc489e
--- /dev/null
+++ b/src/compiler/depgraph/DependencyGraphOutput.cpp
@@ -0,0 +1,65 @@
+//
+// Copyright (c) 2012 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "compiler/depgraph/DependencyGraphOutput.h"
+
+void TDependencyGraphOutput::outputIndentation()
+{
+    for (int i = 0; i < getDepth(); ++i)
+        mSink << "  ";
+}
+
+void TDependencyGraphOutput::visitArgument(TGraphArgument* parameter)
+{
+    outputIndentation();
+    mSink << "argument " << parameter->getArgumentNumber() << " of call to "
+          << parameter->getIntermFunctionCall()->getName() << "\n";
+}
+
+void TDependencyGraphOutput::visitFunctionCall(TGraphFunctionCall* functionCall)
+{
+    outputIndentation();
+    mSink << "function call " <<  functionCall->getIntermFunctionCall()->getName() << "\n";
+}
+
+void TDependencyGraphOutput::visitSymbol(TGraphSymbol* symbol)
+{
+    outputIndentation();
+    mSink << symbol->getIntermSymbol()->getSymbol() << " (symbol id: "
+          << symbol->getIntermSymbol()->getId() << ")\n";
+}
+
+void TDependencyGraphOutput::visitSelection(TGraphSelection* selection)
+{
+    outputIndentation();
+    mSink << "selection\n";
+}
+
+void TDependencyGraphOutput::visitLoop(TGraphLoop* loop)
+{
+    outputIndentation();
+    mSink << "loop condition\n";
+}
+
+void TDependencyGraphOutput::visitLogicalOp(TGraphLogicalOp* logicalOp)
+{
+    outputIndentation();
+    mSink << "logical " << logicalOp->getOpString() << "\n";
+}
+
+void TDependencyGraphOutput::outputAllSpanningTrees(TDependencyGraph& graph)
+{
+    mSink << "\n";
+
+    for (TGraphNodeVector::const_iterator iter = graph.begin(); iter != graph.end(); ++iter)
+    {
+        TGraphNode* symbol = *iter;
+        mSink << "--- Dependency graph spanning tree ---\n";
+        clearVisited();
+        symbol->traverse(this);
+        mSink << "\n";
+    }
+}
diff --git a/src/compiler/depgraph/DependencyGraphOutput.h b/src/compiler/depgraph/DependencyGraphOutput.h
new file mode 100644
index 0000000..01447da
--- /dev/null
+++ b/src/compiler/depgraph/DependencyGraphOutput.h
@@ -0,0 +1,30 @@
+//
+// Copyright (c) 2012 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef COMPILER_DEPGRAPH_DEPENDENCY_GRAPH_OUTPUT_H
+#define COMPILER_DEPGRAPH_DEPENDENCY_GRAPH_OUTPUT_H
+
+#include "compiler/depgraph/DependencyGraph.h"
+#include "compiler/InfoSink.h"
+
+class TDependencyGraphOutput : public TDependencyGraphTraverser {
+public:
+    TDependencyGraphOutput(TInfoSinkBase& sink) : mSink(sink) {}
+    virtual void visitSymbol(TGraphSymbol* symbol);
+    virtual void visitArgument(TGraphArgument* parameter);
+    virtual void visitFunctionCall(TGraphFunctionCall* functionCall);
+    virtual void visitSelection(TGraphSelection* selection);
+    virtual void visitLoop(TGraphLoop* loop);
+    virtual void visitLogicalOp(TGraphLogicalOp* logicalOp);
+
+    void outputAllSpanningTrees(TDependencyGraph& graph);
+private:
+    void outputIndentation();
+
+    TInfoSinkBase& mSink;
+};
+
+#endif  // COMPILER_DEPGRAPH_DEPENDENCY_GRAPH_OUTPUT_H
diff --git a/src/compiler/depgraph/DependencyGraphTraverse.cpp b/src/compiler/depgraph/DependencyGraphTraverse.cpp
new file mode 100644
index 0000000..b158575
--- /dev/null
+++ b/src/compiler/depgraph/DependencyGraphTraverse.cpp
@@ -0,0 +1,69 @@
+//
+// Copyright (c) 2012 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "compiler/depgraph/DependencyGraph.h"
+
+// These methods do a breadth-first traversal through the graph and mark visited nodes.
+
+void TGraphNode::traverse(TDependencyGraphTraverser* graphTraverser)
+{
+    graphTraverser->markVisited(this);
+}
+
+void TGraphParentNode::traverse(TDependencyGraphTraverser* graphTraverser)
+{
+    TGraphNode::traverse(graphTraverser);
+
+    graphTraverser->incrementDepth();
+
+    // Visit the parent node's children.
+    for (TGraphNodeSet::const_iterator iter = mDependentNodes.begin();
+         iter != mDependentNodes.end();
+         ++iter)
+    {
+        TGraphNode* node = *iter;
+        if (!graphTraverser->isVisited(node))
+            node->traverse(graphTraverser);
+    }
+
+    graphTraverser->decrementDepth();
+}
+
+void TGraphArgument::traverse(TDependencyGraphTraverser* graphTraverser)
+{
+    graphTraverser->visitArgument(this);
+    TGraphParentNode::traverse(graphTraverser);
+}
+
+void TGraphFunctionCall::traverse(TDependencyGraphTraverser* graphTraverser)
+{
+    graphTraverser->visitFunctionCall(this);
+    TGraphParentNode::traverse(graphTraverser);
+}
+
+void TGraphSymbol::traverse(TDependencyGraphTraverser* graphTraverser)
+{
+    graphTraverser->visitSymbol(this);
+    TGraphParentNode::traverse(graphTraverser);
+}
+
+void TGraphSelection::traverse(TDependencyGraphTraverser* graphTraverser)
+{
+    graphTraverser->visitSelection(this);
+    TGraphNode::traverse(graphTraverser);
+}
+
+void TGraphLoop::traverse(TDependencyGraphTraverser* graphTraverser)
+{
+    graphTraverser->visitLoop(this);
+    TGraphNode::traverse(graphTraverser);
+}
+
+void TGraphLogicalOp::traverse(TDependencyGraphTraverser* graphTraverser)
+{
+    graphTraverser->visitLogicalOp(this);
+    TGraphNode::traverse(graphTraverser);
+}
diff --git a/src/compiler/intermediate.h b/src/compiler/intermediate.h
index 5441557..995a9f4 100644
--- a/src/compiler/intermediate.h
+++ b/src/compiler/intermediate.h
@@ -452,7 +452,7 @@
     const TString& getName() const { return name; }
 
     void setUserDefined() { userDefined = true; }
-    bool isUserDefined() { return userDefined; }
+    bool isUserDefined() const { return userDefined; }
 
     void setOptimize(bool o) { optimize = o; }
     bool getOptimize() { return optimize; }
diff --git a/src/compiler/timing/RestrictFragmentShaderTiming.cpp b/src/compiler/timing/RestrictFragmentShaderTiming.cpp
new file mode 100644
index 0000000..3b3bbeb
--- /dev/null
+++ b/src/compiler/timing/RestrictFragmentShaderTiming.cpp
@@ -0,0 +1,84 @@
+//
+// Copyright (c) 2012 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "compiler/InfoSink.h"
+#include "compiler/ParseHelper.h"
+#include "compiler/depgraph/DependencyGraphOutput.h"
+#include "compiler/timing/RestrictFragmentShaderTiming.h"
+
+// FIXME(mvujovic): We do not know if the execution time of built-in operations like sin, pow, etc.
+// can vary based on the value of the input arguments. If so, we should restrict those as well.
+void RestrictFragmentShaderTiming::enforceRestrictions(const TDependencyGraph& graph)
+{
+    mNumErrors = 0;
+
+    // FIXME(mvujovic): The dependency graph does not support user defined function calls right now,
+    // so we generate errors for them.
+    validateUserDefinedFunctionCallUsage(graph);
+
+    // Traverse the dependency graph starting at s_texture and generate an error each time we hit a
+    // condition node.
+    TGraphSymbol* uTextureGraphSymbol = graph.getGlobalSymbolByName(mRestrictedSymbol);
+    if (uTextureGraphSymbol &&
+        uTextureGraphSymbol->getIntermSymbol()->getQualifier() == EvqUniform &&
+        uTextureGraphSymbol->getIntermSymbol()->getBasicType() == EbtSampler2D)
+        uTextureGraphSymbol->traverse(this);
+}
+
+void RestrictFragmentShaderTiming::validateUserDefinedFunctionCallUsage(const TDependencyGraph& graph)
+{
+    for (TFunctionCallVector::const_iterator iter = graph.beginUserDefinedFunctionCalls();
+         iter != graph.endUserDefinedFunctionCalls();
+         ++iter)
+    {
+        TGraphFunctionCall* functionCall = *iter;
+        beginError(functionCall->getIntermFunctionCall());
+        mSink << "A call to a user defined function is not permitted.\n";
+    }
+}
+
+void RestrictFragmentShaderTiming::beginError(const TIntermNode* node)
+{
+    ++mNumErrors;
+    mSink.prefix(EPrefixError);
+    mSink.location(node->getLine());
+}
+
+void RestrictFragmentShaderTiming::visitArgument(TGraphArgument* parameter)
+{
+    // FIXME(mvujovic): We should restrict sampler dependent values from being texture coordinates 
+    // in all available sampling operationsn supported in GLSL ES.
+    // This includes overloaded signatures of texture2D, textureCube, and others.
+    if (parameter->getIntermFunctionCall()->getName() != "texture2D(s21;vf2;" ||
+        parameter->getArgumentNumber() != 1)
+        return;
+
+    beginError(parameter->getIntermFunctionCall());
+    mSink << "An expression dependent on a uniform sampler2D by the name '" << mRestrictedSymbol
+          << "' is not permitted to be the second argument of a texture2D call.\n";
+}
+
+void RestrictFragmentShaderTiming::visitSelection(TGraphSelection* selection)
+{
+    beginError(selection->getIntermSelection());
+    mSink << "An expression dependent on a uniform sampler2D by the name '" << mRestrictedSymbol
+          << "' is not permitted in a conditional statement.\n";
+}
+
+void RestrictFragmentShaderTiming::visitLoop(TGraphLoop* loop)
+{
+    beginError(loop->getIntermLoop());
+    mSink << "An expression dependent on a uniform sampler2D by the name '" << mRestrictedSymbol
+          << "' is not permitted in a loop condition.\n";
+}
+
+void RestrictFragmentShaderTiming::visitLogicalOp(TGraphLogicalOp* logicalOp)
+{
+    beginError(logicalOp->getIntermLogicalOp());
+    mSink << "An expression dependent on a uniform sampler2D by the name '" << mRestrictedSymbol
+          << "' is not permitted on the left hand side of a logical " << logicalOp->getOpString()
+          << " operator.\n";
+}
diff --git a/src/compiler/timing/RestrictFragmentShaderTiming.h b/src/compiler/timing/RestrictFragmentShaderTiming.h
new file mode 100644
index 0000000..57cb4a3
--- /dev/null
+++ b/src/compiler/timing/RestrictFragmentShaderTiming.h
@@ -0,0 +1,41 @@
+//
+// Copyright (c) 2012 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef COMPILER_TIMING_RESTRICT_FRAGMENT_SHADER_TIMING_H_
+#define COMPILER_TIMING_RESTRICT_FRAGMENT_SHADER_TIMING_H_
+
+#include "GLSLANG/ShaderLang.h"
+
+#include "compiler/intermediate.h"
+#include "compiler/depgraph/DependencyGraph.h"
+
+class TInfoSinkBase;
+
+class RestrictFragmentShaderTiming : TDependencyGraphTraverser {
+public:
+    RestrictFragmentShaderTiming(TInfoSinkBase& sink, const TString& restrictedSymbol)
+        : mSink(sink)
+        , mRestrictedSymbol(restrictedSymbol)
+        , mNumErrors(0) {}
+
+    void enforceRestrictions(const TDependencyGraph& graph);
+    int numErrors() const { return mNumErrors; }
+
+    virtual void visitArgument(TGraphArgument* parameter);
+    virtual void visitSelection(TGraphSelection* selection);
+    virtual void visitLoop(TGraphLoop* loop);
+    virtual void visitLogicalOp(TGraphLogicalOp* logicalOp);
+
+private:
+    void beginError(const TIntermNode* node);
+    void validateUserDefinedFunctionCallUsage(const TDependencyGraph& graph);
+
+	TInfoSinkBase& mSink;
+    const TString mRestrictedSymbol;
+    int mNumErrors;
+};
+
+#endif  // COMPILER_TIMING_RESTRICT_FRAGMENT_SHADER_TIMING_H_
diff --git a/src/compiler/timing/RestrictVertexShaderTiming.cpp b/src/compiler/timing/RestrictVertexShaderTiming.cpp
new file mode 100644
index 0000000..220cb49
--- /dev/null
+++ b/src/compiler/timing/RestrictVertexShaderTiming.cpp
@@ -0,0 +1,30 @@
+//
+// Copyright (c) 2012 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#include "compiler/timing/RestrictVertexShaderTiming.h"
+
+void RestrictVertexShaderTiming::visitSymbol(TIntermSymbol* node)
+{
+    if (node->getQualifier() == EvqUniform &&
+        node->getBasicType() == EbtSampler2D &&
+        node->getSymbol() == mRestrictedSymbol) {
+        mFoundRestrictedSymbol = true;
+        mSink.prefix(EPrefixError);
+        mSink.location(node->getLine());
+        mSink << "Definition of a uniform sampler2D by the name '" << mRestrictedSymbol
+              << "' is not permitted in vertex shaders.\n";
+    }
+}
+
+bool RestrictVertexShaderTiming::visitAggregate(Visit visit, TIntermAggregate* node)
+{
+    // Don't keep exploring if we've found the restricted symbol, and don't explore anything besides
+    // the global scope (i.e. don't explore function definitions).
+    if (mFoundRestrictedSymbol || node->getOp() == EOpFunction)
+        return false;
+
+    return true;
+}
diff --git a/src/compiler/timing/RestrictVertexShaderTiming.h b/src/compiler/timing/RestrictVertexShaderTiming.h
new file mode 100644
index 0000000..c5cdae5
--- /dev/null
+++ b/src/compiler/timing/RestrictVertexShaderTiming.h
@@ -0,0 +1,41 @@
+//
+// Copyright (c) 2012 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+#ifndef COMPILER_TIMING_RESTRICT_VERTEX_SHADER_TIMING_H_
+#define COMPILER_TIMING_RESTRICT_VERTEX_SHADER_TIMING_H_
+
+#include "GLSLANG/ShaderLang.h"
+
+#include "compiler/intermediate.h"
+#include "compiler/InfoSink.h"
+
+class TInfoSinkBase;
+
+class RestrictVertexShaderTiming : public TIntermTraverser {
+public:
+    RestrictVertexShaderTiming(TInfoSinkBase& sink, const TString& restrictedSymbol)
+        : TIntermTraverser(true, false, false)
+        , mSink(sink)
+        , mRestrictedSymbol(restrictedSymbol)
+        , mFoundRestrictedSymbol(false) {}
+
+    void enforceRestrictions(TIntermNode* root) { root->traverse(this); }
+    int numErrors() { return mFoundRestrictedSymbol ? 1 : 0; }
+
+    virtual void visitSymbol(TIntermSymbol*);
+    virtual bool visitBinary(Visit visit, TIntermBinary*) { return false; }
+    virtual bool visitUnary(Visit visit, TIntermUnary*) { return false; }
+    virtual bool visitSelection(Visit visit, TIntermSelection*) { return false; }
+    virtual bool visitAggregate(Visit visit, TIntermAggregate*);
+    virtual bool visitLoop(Visit visit, TIntermLoop*) { return false; };
+    virtual bool visitBranch(Visit visit, TIntermBranch*) { return false; };
+private:
+    TInfoSinkBase& mSink;
+    const TString mRestrictedSymbol;
+    bool mFoundRestrictedSymbol;
+};
+
+#endif  // COMPILER_TIMING_RESTRICT_VERTEX_SHADER_TIMING_H_
diff --git a/src/compiler/translator_common.vcproj b/src/compiler/translator_common.vcproj
index fad9ad8..250a684 100644
--- a/src/compiler/translator_common.vcproj
+++ b/src/compiler/translator_common.vcproj
@@ -518,6 +518,38 @@
 					>

 				</File>

 			</Filter>

+			<Filter

+				Name="depgraph"

+				>

+				<File

+					RelativePath=".\depgraph\DependencyGraph.cpp"

+					>

+				</File>

+				<File

+					RelativePath=".\depgraph\DependencyGraphBuilder.cpp"

+					>

+				</File>

+				<File

+					RelativePath=".\depgraph\DependencyGraphOutput.cpp"

+					>

+				</File>

+				<File

+					RelativePath=".\depgraph\DependencyGraphTraverse.cpp"

+					>

+				</File>

+			</Filter>

+			<Filter

+				Name="timing"

+				>

+				<File

+					RelativePath=".\timing\RestrictFragmentShaderTiming.cpp"

+					>

+				</File>

+				<File

+					RelativePath=".\timing\RestrictVertexShaderTiming.cpp"

+					>

+				</File>

+			</Filter>

 		</Filter>

 		<Filter

 			Name="Header Files"

@@ -696,6 +728,34 @@
 					>

 				</File>

 			</Filter>

+			<Filter

+				Name="timing"

+				>

+				<File

+					RelativePath=".\timing\RestrictFragmentShaderTiming.h"

+					>

+				</File>

+				<File

+					RelativePath=".\timing\RestrictVertexShaderTiming.h"

+					>

+				</File>

+			</Filter>

+			<Filter

+				Name="depgraph"

+				>

+				<File

+					RelativePath=".\depgraph\DependencyGraph.h"

+					>

+				</File>

+				<File

+					RelativePath=".\depgraph\DependencyGraphBuilder.h"

+					>

+				</File>

+				<File

+					RelativePath=".\depgraph\DependencyGraphOutput.h"

+					>

+				</File>

+			</Filter>

 		</Filter>

 	</Files>

 	<Globals>