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>