Implement ES 2.0 (version 100) limitations for non-inductive loop detection and array indexes needing "constant-index-expressions" (inductive variables and constant expressions).

git-svn-id: https://cvs.khronos.org/svn/repos/ogl/trunk/ecosystem/public/sdk/tools/glslang@23478 e7fa87d3-cd2b-0410-9028-fcbf551c1848
This commit is contained in:
John Kessenich 2013-10-14 22:42:16 +00:00
parent a4ea1313c3
commit 27b72e42c3
15 changed files with 856 additions and 240 deletions

View file

@ -316,6 +316,7 @@ enum TOperator {
};
class TIntermTraverser;
class TIntermOperator;
class TIntermAggregate;
class TIntermUnary;
class TIntermBinary;
@ -342,16 +343,17 @@ public:
virtual glslang::TSourceLoc getLoc() const { return loc; }
virtual void setLoc(glslang::TSourceLoc l) { loc = l; }
virtual void traverse(glslang::TIntermTraverser*) = 0;
virtual glslang::TIntermTyped* getAsTyped() { return 0; }
virtual glslang::TIntermConstantUnion* getAsConstantUnion() { return 0; }
virtual glslang::TIntermAggregate* getAsAggregate() { return 0; }
virtual glslang::TIntermUnary* getAsUnaryNode() { return 0; }
virtual glslang::TIntermBinary* getAsBinaryNode() { return 0; }
virtual glslang::TIntermSelection* getAsSelectionNode() { return 0; }
virtual glslang::TIntermSwitch* getAsSwitchNode() { return 0; }
virtual glslang::TIntermMethod* getAsMethodNode() { return 0; }
virtual glslang::TIntermSymbol* getAsSymbolNode() { return 0; }
virtual glslang::TIntermBranch* getAsBranchNode() { return 0; }
virtual glslang::TIntermTyped* getAsTyped() { return 0; }
virtual glslang::TIntermOperator* getAsOperator() { return 0; }
virtual glslang::TIntermConstantUnion* getAsConstantUnion() { return 0; }
virtual glslang::TIntermAggregate* getAsAggregate() { return 0; }
virtual glslang::TIntermUnary* getAsUnaryNode() { return 0; }
virtual glslang::TIntermBinary* getAsBinaryNode() { return 0; }
virtual glslang::TIntermSelection* getAsSelectionNode() { return 0; }
virtual glslang::TIntermSwitch* getAsSwitchNode() { return 0; }
virtual glslang::TIntermMethod* getAsMethodNode() { return 0; }
virtual glslang::TIntermSymbol* getAsSymbolNode() { return 0; }
virtual glslang::TIntermBranch* getAsBranchNode() { return 0; }
virtual ~TIntermNode() { }
protected:
glslang::TSourceLoc loc;
@ -478,7 +480,7 @@ public:
TIntermConstantUnion(const TConstUnionArray& ua, const TType& t) : TIntermTyped(t), unionArray(ua) { }
const TConstUnionArray& getConstArray() const { return unionArray; }
virtual TIntermConstantUnion* getAsConstantUnion() { return this; }
virtual void traverse(TIntermTraverser* );
virtual void traverse(TIntermTraverser*);
virtual TIntermTyped* fold(TOperator, TIntermTyped*);
virtual TIntermTyped* fold(TOperator, const TType&);
protected:
@ -490,6 +492,7 @@ protected:
//
class TIntermOperator : public TIntermTyped {
public:
TIntermOperator* getAsOperator() { return this; }
TOperator getOp() { return op; }
bool modifiesState() const;
bool isConstructor() const;

View file

@ -713,9 +713,9 @@ TIntermTyped* TIntermediate::foldConstructor(TIntermAggregate* aggrNode)
TConstUnionArray unionArray(aggrNode->getType().getObjectSize());
if (aggrNode->getSequence().size() == 1)
error = parseConstTree(aggrNode->getLoc(), aggrNode, unionArray, aggrNode->getOp(), aggrNode->getType(), true);
error = parseConstTree(aggrNode, unionArray, aggrNode->getOp(), aggrNode->getType(), true);
else
error = parseConstTree(aggrNode->getLoc(), aggrNode, unionArray, aggrNode->getOp(), aggrNode->getType());
error = parseConstTree(aggrNode, unionArray, aggrNode->getOp(), aggrNode->getType());
if (error)
return aggrNode;

View file

@ -809,11 +809,11 @@ TIntermTyped* TIntermediate::addSwizzle(TVectorFields& fields, TSourceLoc loc)
//
// Create loop nodes.
//
TIntermNode* TIntermediate::addLoop(TIntermNode* body, TIntermTyped* test, TIntermTyped* terminal, bool testFirst, TSourceLoc loc)
TIntermLoop* TIntermediate::addLoop(TIntermNode* body, TIntermTyped* test, TIntermTyped* terminal, bool testFirst, TSourceLoc loc)
{
TIntermNode* node = new TIntermLoop(body, test, terminal, testFirst);
TIntermLoop* node = new TIntermLoop(body, test, terminal, testFirst);
node->setLoc(loc);
return node;
}

View file

@ -11,7 +11,7 @@ LIBCODEGEN=./../GenericCodeGen/libCodeGen.a
OBJECTS= Initialize.o IntermTraverse.o \
Intermediate.o ParseHelper.o PoolAlloc.o QualifierAlive.o \
RemoveTree.o ShaderLang.o intermOut.o parseConst.o SymbolTable.o \
InfoSink.o Versions.o Constant.o Scan.o
InfoSink.o Versions.o Constant.o Scan.o limits.o
SRCS= gen_glslang_tab.cpp Initialize.cpp IntermTraverse.cpp \
Intermediate.cpp ParseHelper.cpp PoolAlloc.cp QualifierAlive.cpp \
@ -146,3 +146,4 @@ parseConst.o: ../Public/ShaderLang.h
InfoSink.o: ../Include/InfoSink.h
Versions.o: ParseHelper.h Versions.h ../Include/ShHandle.h SymbolTable.h localintermediate.h
Constant.o: localintermediate.h ../Include/intermediate.h ../Public/ShaderLang.h SymbolTable.h Versions.h
limits.o: ParseHelper.h

View file

@ -154,8 +154,17 @@ bool TParseContext::parseShaderStrings(TPpContext& ppContext, char* strings[], s
return true;
}
anyIndexLimits = ! limits.generalAttributeMatrixVectorIndexing ||
! limits.generalConstantMatrixVectorIndexing ||
! limits.generalSamplerIndexing ||
! limits.generalUniformIndexing ||
! limits.generalVariableIndexing ||
! limits.generalVaryingIndexing;
yyparse((void*)this);
finalize();
return numErrors == 0;
}
@ -510,6 +519,23 @@ TIntermTyped* TParseContext::handleBracketDereference(TSourceLoc loc, TIntermTyp
newType.getQualifier().storage = EvqConst;
newType.dereference();
result->setType(newType);
if (anyIndexLimits) {
// for ES 2.0 (version 100) limitations for almost all index operations except vertex-shader uniforms
if ((! limits.generalSamplerIndexing && base->getBasicType() == EbtSampler) ||
(! limits.generalUniformIndexing && base->getQualifier().isUniform() && language != EShLangVertex) ||
(! limits.generalAttributeMatrixVectorIndexing && base->getQualifier().isPipeInput() && language == EShLangVertex && (base->getType().isMatrix() || base->getType().isVector())) ||
(! limits.generalConstantMatrixVectorIndexing && base->getAsConstantUnion()) ||
(! limits.generalVariableIndexing && ! base->getType().getQualifier().isUniform() &&
! base->getType().getQualifier().isPipeInput() &&
! base->getType().getQualifier().isPipeOutput() &&
base->getType().getQualifier().storage != EvqConst) ||
(! limits.generalVaryingIndexing && (base->getType().getQualifier().isPipeInput() ||
base->getType().getQualifier().isPipeOutput()))) {
// it's too early to know what the inductive variables are, save it for post processing
needsIndexLimitationChecking.push_back(index);
}
}
}
return result;
@ -1914,6 +1940,133 @@ void TParseContext::arrayObjectCheck(TSourceLoc loc, const TType& type, const ch
}
}
//
// See if this loop satisfies the limitations for ES 2.0 (version 100) for loops in Appendex A:
//
// "The loop index has type int or float.
//
// "The for statement has the form:
// for ( init-declaration ; condition ; expression )
// init-declaration has the form: type-specifier identifier = constant-expression
// condition has the form: loop-index relational_operator constant-expression
// where relational_operator is one of: > >= < <= == or !=
// expression [sic] has one of the following forms:
// loop-index++
// loop-index--
// loop-index += constant-expression
// loop-index -= constant-expression
//
// The body is handled in an AST traversal.
//
void TParseContext::inductiveLoopCheck(TSourceLoc loc, TIntermNode* init, TIntermLoop* loop)
{
// loop index init must exist and be a declaration, which shows up in the AST as an aggregate of size 1 of the declaration
bool badInit = false;
if (! init || ! init->getAsAggregate() || ! init->getAsAggregate()->getSequence().size() == 1)
badInit = true;
TIntermBinary* binaryInit;
if (! badInit) {
// get the declaration assignment
binaryInit = init->getAsAggregate()->getSequence()[0]->getAsBinaryNode();
if (! binaryInit)
badInit = true;
}
if (badInit) {
error(loc, "inductive-loop init-declaration requires the form \"type-specifier loop-index = constant-expression\"", "limitations", "");
return;
}
// loop index must be type int or float
if (! binaryInit->getType().isScalar() || (binaryInit->getBasicType() != EbtInt && binaryInit->getBasicType() != EbtFloat)) {
error(loc, "inductive loop requires a scalar 'int' or 'float' loop index", "limitations", "");
return;
}
// init is the form "loop-index = constant"
if (binaryInit->getOp() != EOpAssign || ! binaryInit->getLeft()->getAsSymbolNode() || ! binaryInit->getRight()->getAsConstantUnion()) {
error(loc, "inductive-loop init-declaration requires the form \"type-specifier loop-index = constant-expression\"", "limitations", "");
return;
}
// get the unique id of the loop index
int loopIndex = binaryInit->getLeft()->getAsSymbolNode()->getId();
inductiveLoopIds.insert(loopIndex);
// condition's form must be "loop-index relational-operator constant-expression"
bool badCond = ! loop->getTest();
if (! badCond) {
TIntermBinary* binaryCond = loop->getTest()->getAsBinaryNode();
badCond = ! binaryCond;
if (! badCond) {
switch (binaryCond->getOp()) {
case EOpGreaterThan:
case EOpGreaterThanEqual:
case EOpLessThan:
case EOpLessThanEqual:
case EOpEqual:
case EOpNotEqual:
break;
default:
badCond = true;
}
}
if (binaryCond && (! binaryCond->getLeft()->getAsSymbolNode() ||
binaryCond->getLeft()->getAsSymbolNode()->getId() != loopIndex ||
! binaryCond->getRight()->getAsConstantUnion()))
badCond = true;
}
if (badCond) {
error(loc, "inductive-loop condition requires the form \"loop-index <comparison-op> constant-expression\"", "limitations", "");
return;
}
// loop-index++
// loop-index--
// loop-index += constant-expression
// loop-index -= constant-expression
bool badTerminal = ! loop->getTerminal();
if (! badTerminal) {
TIntermUnary* unaryTerminal = loop->getTerminal()->getAsUnaryNode();
TIntermBinary* binaryTerminal = loop->getTerminal()->getAsBinaryNode();
if (unaryTerminal || binaryTerminal) {
switch(loop->getTerminal()->getAsOperator()->getOp()) {
case EOpPostDecrement:
case EOpPostIncrement:
case EOpAddAssign:
case EOpSubAssign:
break;
default:
badTerminal = true;
}
} else
badTerminal = true;
if (binaryTerminal && (! binaryTerminal->getLeft()->getAsSymbolNode() ||
binaryTerminal->getLeft()->getAsSymbolNode()->getId() != loopIndex ||
! binaryTerminal->getRight()->getAsConstantUnion()))
badTerminal = true;
if (unaryTerminal && (! unaryTerminal->getOperand()->getAsSymbolNode() ||
unaryTerminal->getOperand()->getAsSymbolNode()->getId() != loopIndex))
badTerminal = true;
}
if (badTerminal) {
error(loc, "inductive-loop termination requires the form \"loop-index++, loop-index--, loop-index += constant-expression, or loop-index -= constant-expression\"", "limitations", "");
return;
}
// the body
inductiveLoopBodyCheck(loop->getBody(), loopIndex, symbolTable);
}
//
// Do any additional error checking, etc., once we know the parsing is done.
//
void TParseContext::finalize()
{
// Check on array indexes for ES 2.0 (version 100) limitations.
for (size_t i = 0; i < needsIndexLimitationChecking.size(); ++i)
constantIndexExpressionCheck(needsIndexLimitationChecking[i]);
}
//
// Layout qualifier stuff.
//

View file

@ -53,6 +53,8 @@ struct TPragma {
class TScanContext;
class TPpContext;
typedef std::set<int> TIdSetType;
//
// The following are extra variables needed during parsing, grouped together so
// they can be passed to the parser without needing a global.
@ -116,6 +118,9 @@ public:
void nestedBlockCheck(TSourceLoc);
void nestedStructCheck(TSourceLoc);
void arrayObjectCheck(TSourceLoc, const TType&, const char* op);
void inductiveLoopCheck(TSourceLoc, TIntermNode* init, TIntermLoop* loop);
void inductiveLoopBodyCheck(TIntermNode*, int loopIndexId, TSymbolTable&);
void constantIndexExpressionCheck(TIntermNode*);
void setLayoutQualifier(TSourceLoc, TPublicType&, TString&);
void setLayoutQualifier(TSourceLoc, TPublicType&, TString&, int);
@ -167,6 +172,7 @@ protected:
void declareArray(TSourceLoc, TString& identifier, const TType&, TSymbol*&, bool& newDeclaration);
TIntermNode* executeInitializer(TSourceLoc, TString& identifier, TIntermTyped* initializer, TVariable* variable);
TOperator mapTypeToConstructorOp(const TType&);
void finalize();
public:
//
@ -213,6 +219,9 @@ protected:
TQualifier globalInputDefaults;
TQualifier globalOutputDefaults;
TString currentCaller;
TIdSetType inductiveLoopIds;
bool anyIndexLimits;
TVector<TIntermTyped*> needsIndexLimitationChecking;
// TODO: desktop functionality: track use of gl_FragDepth before redeclaration
};

View file

@ -2278,10 +2278,10 @@ iteration_statement
for_init_statement for_rest_statement RIGHT_PAREN statement_no_new_scope {
parseContext.symbolTable.pop(&parseContext.defaultPrecision[0]);
$$ = parseContext.intermediate.makeAggregate($4, $2.loc);
$$ = parseContext.intermediate.growAggregate(
$$,
parseContext.intermediate.addLoop($7, reinterpret_cast<TIntermTyped*>($5.node1), reinterpret_cast<TIntermTyped*>($5.node2), true, $1.loc),
$1.loc);
TIntermLoop* forLoop = parseContext.intermediate.addLoop($7, reinterpret_cast<TIntermTyped*>($5.node1), reinterpret_cast<TIntermTyped*>($5.node2), true, $1.loc);
if (! parseContext.limits.nonInductiveForLoops)
parseContext.inductiveLoopCheck($1.loc, $4, forLoop);
$$ = parseContext.intermediate.growAggregate($$, forLoop, $1.loc);
$$->getAsAggregate()->setOperator(EOpSequence);
--parseContext.loopNestingLevel;
}

View file

@ -0,0 +1,199 @@
//
//Copyright (C) 2013 LunarG, Inc.
//
//All rights reserved.
//
//Redistribution and use in source and binary forms, with or without
//modification, are permitted provided that the following conditions
//are met:
//
// Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
//
// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
//THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
//"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
//LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
//FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
//COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
//INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
//BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
//LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
//CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
//LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
//ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
//POSSIBILITY OF SUCH DAMAGE.
//
//
// Do sub tree walks for
// 1) inductive loop bodies to see if the inductive variable is modified
// 2) array-index expressions to see if they are "constant-index-expression"
//
// These are per Appendix A of ES 2.0:
//
// "Within the body of the loop, the loop index is not statically assigned to nor is it used as the
// argument to a function out or inout parameter."
//
// "The following are constant-index-expressions:
// - Constant expressions
// - Loop indices as defined in section 4
// - Expressions composed of both of the above"
//
// N.B.: assuming the last rule excludes function calls
//
#include "ParseHelper.h"
namespace glslang {
//
// The inductive loop-body traverser.
//
// Just look at things that might modify the loop index.
//
class TInductiveTraverser : public TIntermTraverser {
public:
TInductiveTraverser(int id, TSymbolTable& st) : loopId(id), symbolTable(st), bad(false) { }
int loopId; // unique ID of the symbol that's the loop inductive variable
TSymbolTable& symbolTable;
bool bad;
TSourceLoc badLoc;
};
// check binary operations for those modifying the loop index
bool InductiveBinary(bool /* preVisit */, TIntermBinary* node, TIntermTraverser* it)
{
TInductiveTraverser* oit = static_cast<TInductiveTraverser*>(it);
if (node->modifiesState() && node->getLeft()->getAsSymbolNode() &&
node->getLeft()->getAsSymbolNode()->getId() == oit->loopId) {
oit->bad = true;
oit->badLoc = node->getLoc();
}
return true;
}
// check unary operations for those modifying the loop index
bool InductiveUnary(bool /* preVisit */, TIntermUnary* node, TIntermTraverser* it)
{
TInductiveTraverser* oit = static_cast<TInductiveTraverser*>(it);
if (node->modifiesState() && node->getOperand()->getAsSymbolNode() &&
node->getOperand()->getAsSymbolNode()->getId() == oit->loopId) {
oit->bad = true;
oit->badLoc = node->getLoc();
}
return true;
}
// check function calls for arguments modifying the loop index
bool InductiveAggregate(bool /* preVisit */, TIntermAggregate* node, TIntermTraverser* it)
{
TInductiveTraverser* oit = static_cast<TInductiveTraverser*>(it);
if (node->getOp() == EOpFunctionCall) {
// see if an out or inout argument is the loop index
const TIntermSequence& args = node->getSequence();
for (size_t i = 0; i < args.size(); ++i) {
if (args[i]->getAsSymbolNode() && args[i]->getAsSymbolNode()->getId() == oit->loopId) {
TSymbol* function = oit->symbolTable.find(node->getName());
const TType* type = (*function->getAsFunction())[i].type;
if (type->getQualifier().storage == EvqOut ||
type->getQualifier().storage == EvqInOut) {
oit->bad = true;
oit->badLoc = node->getLoc();
}
}
}
}
return true;
}
//
// External function to call for loop check.
//
void TParseContext::inductiveLoopBodyCheck(TIntermNode* body, int loopId, TSymbolTable& symbolTable)
{
TInductiveTraverser it(loopId, symbolTable);
if (! body)
return;
it.visitAggregate = InductiveAggregate;
it.visitBinary = InductiveBinary;
it.visitUnary = InductiveUnary;
body->traverse(&it);
if (it.bad)
error(it.badLoc, "inductive loop index modified", "limitations", "");
}
//
// The "constant-index-expression" tranverser.
//
// Just look at things that can form an index.
//
class TIndexTraverser : public TIntermTraverser {
public:
TIndexTraverser(const TIdSetType& ids) : inductiveLoopIds(ids), bad(false) { }
const TIdSetType& inductiveLoopIds;
bool bad;
TSourceLoc badLoc;
};
// make sure symbols are inductive-loop indexes
void IndexSymbol(TIntermSymbol* symbol, TIntermTraverser* it)
{
TIndexTraverser* oit = static_cast<TIndexTraverser*>(it);
if (oit->inductiveLoopIds.find(symbol->getId()) == oit->inductiveLoopIds.end()) {
oit->bad = true;
oit->badLoc = symbol->getLoc();
}
}
// check for function calls, assuming they are bad; spec. doesn't really say
bool IndexAggregate(bool /* preVisit */, TIntermAggregate* node, TIntermTraverser* it)
{
TIndexTraverser* oit = static_cast<TIndexTraverser*>(it);
if (node->getOp() == EOpFunctionCall) {
oit->bad = true;
oit->badLoc = node->getLoc();
}
return true;
}
//
// External function to call for loop check.
//
void TParseContext::constantIndexExpressionCheck(TIntermNode* index)
{
TIndexTraverser it(inductiveLoopIds);
it.visitSymbol = IndexSymbol;
it.visitAggregate = IndexAggregate;
index->traverse(&it);
if (it.bad)
error(it.badLoc, "Non-constant-index-expression", "limitations", "");
}
} // end namespace glslang

View file

@ -89,8 +89,8 @@ public:
TIntermTyped* addMethod(TIntermTyped*, const TType&, const TString*, TSourceLoc);
TIntermConstantUnion* addConstantUnion(const TConstUnionArray&, const TType&, TSourceLoc);
TIntermTyped* promoteConstantUnion(TBasicType, TIntermConstantUnion*) ;
bool parseConstTree(TSourceLoc, TIntermNode*, TConstUnionArray, TOperator, const TType&, bool singleConstantParam = false);
TIntermNode* addLoop(TIntermNode*, TIntermTyped*, TIntermTyped*, bool testFirst, TSourceLoc);
bool parseConstTree(TIntermNode*, TConstUnionArray, TOperator, const TType&, bool singleConstantParam = false);
TIntermLoop* addLoop(TIntermNode*, TIntermTyped*, TIntermTyped*, bool testFirst, TSourceLoc);
TIntermBranch* addBranch(TOperator, TSourceLoc);
TIntermBranch* addBranch(TOperator, TIntermTyped*, TSourceLoc);
TIntermTyped* addSwizzle(TVectorFields&, TSourceLoc);

View file

@ -45,7 +45,7 @@ class TConstTraverser : public TIntermTraverser {
public:
TConstTraverser(const TConstUnionArray& cUnion, bool singleConstParam, TOperator constructType, const TType& t) : unionArray(cUnion), type(t),
constructorType(constructType), singleConstantParam(singleConstParam), error(false), isMatrix(false),
matrixCols(0), matrixRows(0) { index = 0; tOp = EOpNull;}
matrixCols(0), matrixRows(0) { index = 0; tOp = EOpNull; }
int index;
TConstUnionArray unionArray;
TOperator tOp;
@ -170,7 +170,7 @@ void ParseConstantUnion(TIntermConstantUnion* node, TIntermTraverser* it)
}
}
bool TIntermediate::parseConstTree(TSourceLoc line, TIntermNode* root, TConstUnionArray unionArray, TOperator constructorType, const TType& t, bool singleConstantParam)
bool TIntermediate::parseConstTree(TIntermNode* root, TConstUnionArray unionArray, TOperator constructorType, const TType& t, bool singleConstantParam)
{
if (root == 0)
return false;