1*8975f5c5SAndroid Build Coastguard Worker //
2*8975f5c5SAndroid Build Coastguard Worker // Copyright 2002 The ANGLE Project Authors. All rights reserved.
3*8975f5c5SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
4*8975f5c5SAndroid Build Coastguard Worker // found in the LICENSE file.
5*8975f5c5SAndroid Build Coastguard Worker //
6*8975f5c5SAndroid Build Coastguard Worker
7*8975f5c5SAndroid Build Coastguard Worker #include "compiler/translator/ValidateLimitations.h"
8*8975f5c5SAndroid Build Coastguard Worker
9*8975f5c5SAndroid Build Coastguard Worker #include "angle_gl.h"
10*8975f5c5SAndroid Build Coastguard Worker #include "compiler/translator/Diagnostics.h"
11*8975f5c5SAndroid Build Coastguard Worker #include "compiler/translator/ParseContext.h"
12*8975f5c5SAndroid Build Coastguard Worker #include "compiler/translator/tree_util/IntermTraverse.h"
13*8975f5c5SAndroid Build Coastguard Worker
14*8975f5c5SAndroid Build Coastguard Worker namespace sh
15*8975f5c5SAndroid Build Coastguard Worker {
16*8975f5c5SAndroid Build Coastguard Worker
17*8975f5c5SAndroid Build Coastguard Worker namespace
18*8975f5c5SAndroid Build Coastguard Worker {
19*8975f5c5SAndroid Build Coastguard Worker
GetLoopSymbolId(TIntermLoop * loop)20*8975f5c5SAndroid Build Coastguard Worker int GetLoopSymbolId(TIntermLoop *loop)
21*8975f5c5SAndroid Build Coastguard Worker {
22*8975f5c5SAndroid Build Coastguard Worker // Here we assume all the operations are valid, because the loop node is
23*8975f5c5SAndroid Build Coastguard Worker // already validated before this call.
24*8975f5c5SAndroid Build Coastguard Worker TIntermSequence *declSeq = loop->getInit()->getAsDeclarationNode()->getSequence();
25*8975f5c5SAndroid Build Coastguard Worker TIntermBinary *declInit = (*declSeq)[0]->getAsBinaryNode();
26*8975f5c5SAndroid Build Coastguard Worker TIntermSymbol *symbol = declInit->getLeft()->getAsSymbolNode();
27*8975f5c5SAndroid Build Coastguard Worker
28*8975f5c5SAndroid Build Coastguard Worker return symbol->uniqueId().get();
29*8975f5c5SAndroid Build Coastguard Worker }
30*8975f5c5SAndroid Build Coastguard Worker
31*8975f5c5SAndroid Build Coastguard Worker // Traverses a node to check if it represents a constant index expression.
32*8975f5c5SAndroid Build Coastguard Worker // Definition:
33*8975f5c5SAndroid Build Coastguard Worker // constant-index-expressions are a superset of constant-expressions.
34*8975f5c5SAndroid Build Coastguard Worker // Constant-index-expressions can include loop indices as defined in
35*8975f5c5SAndroid Build Coastguard Worker // GLSL ES 1.0 spec, Appendix A, section 4.
36*8975f5c5SAndroid Build Coastguard Worker // The following are constant-index-expressions:
37*8975f5c5SAndroid Build Coastguard Worker // - Constant expressions
38*8975f5c5SAndroid Build Coastguard Worker // - Loop indices as defined in section 4
39*8975f5c5SAndroid Build Coastguard Worker // - Expressions composed of both of the above
40*8975f5c5SAndroid Build Coastguard Worker class ValidateConstIndexExpr : public TIntermTraverser
41*8975f5c5SAndroid Build Coastguard Worker {
42*8975f5c5SAndroid Build Coastguard Worker public:
ValidateConstIndexExpr(const std::vector<int> & loopSymbols)43*8975f5c5SAndroid Build Coastguard Worker ValidateConstIndexExpr(const std::vector<int> &loopSymbols)
44*8975f5c5SAndroid Build Coastguard Worker : TIntermTraverser(true, false, false), mValid(true), mLoopSymbolIds(loopSymbols)
45*8975f5c5SAndroid Build Coastguard Worker {}
46*8975f5c5SAndroid Build Coastguard Worker
47*8975f5c5SAndroid Build Coastguard Worker // Returns true if the parsed node represents a constant index expression.
isValid() const48*8975f5c5SAndroid Build Coastguard Worker bool isValid() const { return mValid; }
49*8975f5c5SAndroid Build Coastguard Worker
visitSymbol(TIntermSymbol * symbol)50*8975f5c5SAndroid Build Coastguard Worker void visitSymbol(TIntermSymbol *symbol) override
51*8975f5c5SAndroid Build Coastguard Worker {
52*8975f5c5SAndroid Build Coastguard Worker // Only constants and loop indices are allowed in a
53*8975f5c5SAndroid Build Coastguard Worker // constant index expression.
54*8975f5c5SAndroid Build Coastguard Worker if (mValid)
55*8975f5c5SAndroid Build Coastguard Worker {
56*8975f5c5SAndroid Build Coastguard Worker bool isLoopSymbol = std::find(mLoopSymbolIds.begin(), mLoopSymbolIds.end(),
57*8975f5c5SAndroid Build Coastguard Worker symbol->uniqueId().get()) != mLoopSymbolIds.end();
58*8975f5c5SAndroid Build Coastguard Worker mValid = (symbol->getQualifier() == EvqConst) || isLoopSymbol;
59*8975f5c5SAndroid Build Coastguard Worker }
60*8975f5c5SAndroid Build Coastguard Worker }
61*8975f5c5SAndroid Build Coastguard Worker
62*8975f5c5SAndroid Build Coastguard Worker private:
63*8975f5c5SAndroid Build Coastguard Worker bool mValid;
64*8975f5c5SAndroid Build Coastguard Worker const std::vector<int> mLoopSymbolIds;
65*8975f5c5SAndroid Build Coastguard Worker };
66*8975f5c5SAndroid Build Coastguard Worker
67*8975f5c5SAndroid Build Coastguard Worker // Traverses intermediate tree to ensure that the shader does not exceed the
68*8975f5c5SAndroid Build Coastguard Worker // minimum functionality mandated in GLSL 1.0 spec, Appendix A.
69*8975f5c5SAndroid Build Coastguard Worker class ValidateLimitationsTraverser : public TLValueTrackingTraverser
70*8975f5c5SAndroid Build Coastguard Worker {
71*8975f5c5SAndroid Build Coastguard Worker public:
72*8975f5c5SAndroid Build Coastguard Worker ValidateLimitationsTraverser(sh::GLenum shaderType,
73*8975f5c5SAndroid Build Coastguard Worker TSymbolTable *symbolTable,
74*8975f5c5SAndroid Build Coastguard Worker TDiagnostics *diagnostics);
75*8975f5c5SAndroid Build Coastguard Worker
76*8975f5c5SAndroid Build Coastguard Worker void visitSymbol(TIntermSymbol *node) override;
77*8975f5c5SAndroid Build Coastguard Worker bool visitBinary(Visit, TIntermBinary *) override;
78*8975f5c5SAndroid Build Coastguard Worker bool visitLoop(Visit, TIntermLoop *) override;
79*8975f5c5SAndroid Build Coastguard Worker
80*8975f5c5SAndroid Build Coastguard Worker private:
81*8975f5c5SAndroid Build Coastguard Worker void error(TSourceLoc loc, const char *reason, const char *token);
82*8975f5c5SAndroid Build Coastguard Worker void error(TSourceLoc loc, const char *reason, const ImmutableString &token);
83*8975f5c5SAndroid Build Coastguard Worker
84*8975f5c5SAndroid Build Coastguard Worker bool isLoopIndex(TIntermSymbol *symbol);
85*8975f5c5SAndroid Build Coastguard Worker bool validateLoopType(TIntermLoop *node);
86*8975f5c5SAndroid Build Coastguard Worker
87*8975f5c5SAndroid Build Coastguard Worker bool validateForLoopHeader(TIntermLoop *node);
88*8975f5c5SAndroid Build Coastguard Worker // If valid, return the index symbol id; Otherwise, return -1.
89*8975f5c5SAndroid Build Coastguard Worker int validateForLoopInit(TIntermLoop *node);
90*8975f5c5SAndroid Build Coastguard Worker bool validateForLoopCond(TIntermLoop *node, int indexSymbolId);
91*8975f5c5SAndroid Build Coastguard Worker bool validateForLoopExpr(TIntermLoop *node, int indexSymbolId);
92*8975f5c5SAndroid Build Coastguard Worker
93*8975f5c5SAndroid Build Coastguard Worker // Returns true if indexing does not exceed the minimum functionality
94*8975f5c5SAndroid Build Coastguard Worker // mandated in GLSL 1.0 spec, Appendix A, Section 5.
95*8975f5c5SAndroid Build Coastguard Worker bool isConstExpr(TIntermNode *node);
96*8975f5c5SAndroid Build Coastguard Worker bool isConstIndexExpr(TIntermNode *node);
97*8975f5c5SAndroid Build Coastguard Worker bool validateIndexing(TIntermBinary *node);
98*8975f5c5SAndroid Build Coastguard Worker
99*8975f5c5SAndroid Build Coastguard Worker sh::GLenum mShaderType;
100*8975f5c5SAndroid Build Coastguard Worker TDiagnostics *mDiagnostics;
101*8975f5c5SAndroid Build Coastguard Worker std::vector<int> mLoopSymbolIds;
102*8975f5c5SAndroid Build Coastguard Worker };
103*8975f5c5SAndroid Build Coastguard Worker
ValidateLimitationsTraverser(sh::GLenum shaderType,TSymbolTable * symbolTable,TDiagnostics * diagnostics)104*8975f5c5SAndroid Build Coastguard Worker ValidateLimitationsTraverser::ValidateLimitationsTraverser(sh::GLenum shaderType,
105*8975f5c5SAndroid Build Coastguard Worker TSymbolTable *symbolTable,
106*8975f5c5SAndroid Build Coastguard Worker TDiagnostics *diagnostics)
107*8975f5c5SAndroid Build Coastguard Worker : TLValueTrackingTraverser(true, false, false, symbolTable),
108*8975f5c5SAndroid Build Coastguard Worker mShaderType(shaderType),
109*8975f5c5SAndroid Build Coastguard Worker mDiagnostics(diagnostics)
110*8975f5c5SAndroid Build Coastguard Worker {
111*8975f5c5SAndroid Build Coastguard Worker ASSERT(diagnostics);
112*8975f5c5SAndroid Build Coastguard Worker }
113*8975f5c5SAndroid Build Coastguard Worker
visitSymbol(TIntermSymbol * node)114*8975f5c5SAndroid Build Coastguard Worker void ValidateLimitationsTraverser::visitSymbol(TIntermSymbol *node)
115*8975f5c5SAndroid Build Coastguard Worker {
116*8975f5c5SAndroid Build Coastguard Worker if (isLoopIndex(node) && isLValueRequiredHere())
117*8975f5c5SAndroid Build Coastguard Worker {
118*8975f5c5SAndroid Build Coastguard Worker error(node->getLine(),
119*8975f5c5SAndroid Build Coastguard Worker "Loop index cannot be statically assigned to within the body of the loop",
120*8975f5c5SAndroid Build Coastguard Worker node->getName());
121*8975f5c5SAndroid Build Coastguard Worker }
122*8975f5c5SAndroid Build Coastguard Worker }
123*8975f5c5SAndroid Build Coastguard Worker
visitBinary(Visit,TIntermBinary * node)124*8975f5c5SAndroid Build Coastguard Worker bool ValidateLimitationsTraverser::visitBinary(Visit, TIntermBinary *node)
125*8975f5c5SAndroid Build Coastguard Worker {
126*8975f5c5SAndroid Build Coastguard Worker // Check indexing.
127*8975f5c5SAndroid Build Coastguard Worker switch (node->getOp())
128*8975f5c5SAndroid Build Coastguard Worker {
129*8975f5c5SAndroid Build Coastguard Worker case EOpIndexDirect:
130*8975f5c5SAndroid Build Coastguard Worker case EOpIndexIndirect:
131*8975f5c5SAndroid Build Coastguard Worker validateIndexing(node);
132*8975f5c5SAndroid Build Coastguard Worker break;
133*8975f5c5SAndroid Build Coastguard Worker default:
134*8975f5c5SAndroid Build Coastguard Worker break;
135*8975f5c5SAndroid Build Coastguard Worker }
136*8975f5c5SAndroid Build Coastguard Worker return true;
137*8975f5c5SAndroid Build Coastguard Worker }
138*8975f5c5SAndroid Build Coastguard Worker
visitLoop(Visit,TIntermLoop * node)139*8975f5c5SAndroid Build Coastguard Worker bool ValidateLimitationsTraverser::visitLoop(Visit, TIntermLoop *node)
140*8975f5c5SAndroid Build Coastguard Worker {
141*8975f5c5SAndroid Build Coastguard Worker if (!validateLoopType(node))
142*8975f5c5SAndroid Build Coastguard Worker return false;
143*8975f5c5SAndroid Build Coastguard Worker
144*8975f5c5SAndroid Build Coastguard Worker if (!validateForLoopHeader(node))
145*8975f5c5SAndroid Build Coastguard Worker return false;
146*8975f5c5SAndroid Build Coastguard Worker
147*8975f5c5SAndroid Build Coastguard Worker mLoopSymbolIds.push_back(GetLoopSymbolId(node));
148*8975f5c5SAndroid Build Coastguard Worker node->getBody()->traverse(this);
149*8975f5c5SAndroid Build Coastguard Worker mLoopSymbolIds.pop_back();
150*8975f5c5SAndroid Build Coastguard Worker
151*8975f5c5SAndroid Build Coastguard Worker // The loop is fully processed - no need to visit children.
152*8975f5c5SAndroid Build Coastguard Worker return false;
153*8975f5c5SAndroid Build Coastguard Worker }
154*8975f5c5SAndroid Build Coastguard Worker
error(TSourceLoc loc,const char * reason,const char * token)155*8975f5c5SAndroid Build Coastguard Worker void ValidateLimitationsTraverser::error(TSourceLoc loc, const char *reason, const char *token)
156*8975f5c5SAndroid Build Coastguard Worker {
157*8975f5c5SAndroid Build Coastguard Worker mDiagnostics->error(loc, reason, token);
158*8975f5c5SAndroid Build Coastguard Worker }
159*8975f5c5SAndroid Build Coastguard Worker
error(TSourceLoc loc,const char * reason,const ImmutableString & token)160*8975f5c5SAndroid Build Coastguard Worker void ValidateLimitationsTraverser::error(TSourceLoc loc,
161*8975f5c5SAndroid Build Coastguard Worker const char *reason,
162*8975f5c5SAndroid Build Coastguard Worker const ImmutableString &token)
163*8975f5c5SAndroid Build Coastguard Worker {
164*8975f5c5SAndroid Build Coastguard Worker error(loc, reason, token.data());
165*8975f5c5SAndroid Build Coastguard Worker }
166*8975f5c5SAndroid Build Coastguard Worker
isLoopIndex(TIntermSymbol * symbol)167*8975f5c5SAndroid Build Coastguard Worker bool ValidateLimitationsTraverser::isLoopIndex(TIntermSymbol *symbol)
168*8975f5c5SAndroid Build Coastguard Worker {
169*8975f5c5SAndroid Build Coastguard Worker return std::find(mLoopSymbolIds.begin(), mLoopSymbolIds.end(), symbol->uniqueId().get()) !=
170*8975f5c5SAndroid Build Coastguard Worker mLoopSymbolIds.end();
171*8975f5c5SAndroid Build Coastguard Worker }
172*8975f5c5SAndroid Build Coastguard Worker
validateLoopType(TIntermLoop * node)173*8975f5c5SAndroid Build Coastguard Worker bool ValidateLimitationsTraverser::validateLoopType(TIntermLoop *node)
174*8975f5c5SAndroid Build Coastguard Worker {
175*8975f5c5SAndroid Build Coastguard Worker TLoopType type = node->getType();
176*8975f5c5SAndroid Build Coastguard Worker if (type == ELoopFor)
177*8975f5c5SAndroid Build Coastguard Worker return true;
178*8975f5c5SAndroid Build Coastguard Worker
179*8975f5c5SAndroid Build Coastguard Worker // Reject while and do-while loops.
180*8975f5c5SAndroid Build Coastguard Worker error(node->getLine(), "This type of loop is not allowed", type == ELoopWhile ? "while" : "do");
181*8975f5c5SAndroid Build Coastguard Worker return false;
182*8975f5c5SAndroid Build Coastguard Worker }
183*8975f5c5SAndroid Build Coastguard Worker
validateForLoopHeader(TIntermLoop * node)184*8975f5c5SAndroid Build Coastguard Worker bool ValidateLimitationsTraverser::validateForLoopHeader(TIntermLoop *node)
185*8975f5c5SAndroid Build Coastguard Worker {
186*8975f5c5SAndroid Build Coastguard Worker ASSERT(node->getType() == ELoopFor);
187*8975f5c5SAndroid Build Coastguard Worker
188*8975f5c5SAndroid Build Coastguard Worker //
189*8975f5c5SAndroid Build Coastguard Worker // The for statement has the form:
190*8975f5c5SAndroid Build Coastguard Worker // for ( init-declaration ; condition ; expression ) statement
191*8975f5c5SAndroid Build Coastguard Worker //
192*8975f5c5SAndroid Build Coastguard Worker int indexSymbolId = validateForLoopInit(node);
193*8975f5c5SAndroid Build Coastguard Worker if (indexSymbolId < 0)
194*8975f5c5SAndroid Build Coastguard Worker return false;
195*8975f5c5SAndroid Build Coastguard Worker if (!validateForLoopCond(node, indexSymbolId))
196*8975f5c5SAndroid Build Coastguard Worker return false;
197*8975f5c5SAndroid Build Coastguard Worker if (!validateForLoopExpr(node, indexSymbolId))
198*8975f5c5SAndroid Build Coastguard Worker return false;
199*8975f5c5SAndroid Build Coastguard Worker
200*8975f5c5SAndroid Build Coastguard Worker return true;
201*8975f5c5SAndroid Build Coastguard Worker }
202*8975f5c5SAndroid Build Coastguard Worker
validateForLoopInit(TIntermLoop * node)203*8975f5c5SAndroid Build Coastguard Worker int ValidateLimitationsTraverser::validateForLoopInit(TIntermLoop *node)
204*8975f5c5SAndroid Build Coastguard Worker {
205*8975f5c5SAndroid Build Coastguard Worker TIntermNode *init = node->getInit();
206*8975f5c5SAndroid Build Coastguard Worker if (init == nullptr)
207*8975f5c5SAndroid Build Coastguard Worker {
208*8975f5c5SAndroid Build Coastguard Worker error(node->getLine(), "Missing init declaration", "for");
209*8975f5c5SAndroid Build Coastguard Worker return -1;
210*8975f5c5SAndroid Build Coastguard Worker }
211*8975f5c5SAndroid Build Coastguard Worker
212*8975f5c5SAndroid Build Coastguard Worker //
213*8975f5c5SAndroid Build Coastguard Worker // init-declaration has the form:
214*8975f5c5SAndroid Build Coastguard Worker // type-specifier identifier = constant-expression
215*8975f5c5SAndroid Build Coastguard Worker //
216*8975f5c5SAndroid Build Coastguard Worker TIntermDeclaration *decl = init->getAsDeclarationNode();
217*8975f5c5SAndroid Build Coastguard Worker if (decl == nullptr)
218*8975f5c5SAndroid Build Coastguard Worker {
219*8975f5c5SAndroid Build Coastguard Worker error(init->getLine(), "Invalid init declaration", "for");
220*8975f5c5SAndroid Build Coastguard Worker return -1;
221*8975f5c5SAndroid Build Coastguard Worker }
222*8975f5c5SAndroid Build Coastguard Worker // To keep things simple do not allow declaration list.
223*8975f5c5SAndroid Build Coastguard Worker TIntermSequence *declSeq = decl->getSequence();
224*8975f5c5SAndroid Build Coastguard Worker if (declSeq->size() != 1)
225*8975f5c5SAndroid Build Coastguard Worker {
226*8975f5c5SAndroid Build Coastguard Worker error(decl->getLine(), "Invalid init declaration", "for");
227*8975f5c5SAndroid Build Coastguard Worker return -1;
228*8975f5c5SAndroid Build Coastguard Worker }
229*8975f5c5SAndroid Build Coastguard Worker TIntermBinary *declInit = (*declSeq)[0]->getAsBinaryNode();
230*8975f5c5SAndroid Build Coastguard Worker if ((declInit == nullptr) || (declInit->getOp() != EOpInitialize))
231*8975f5c5SAndroid Build Coastguard Worker {
232*8975f5c5SAndroid Build Coastguard Worker error(decl->getLine(), "Invalid init declaration", "for");
233*8975f5c5SAndroid Build Coastguard Worker return -1;
234*8975f5c5SAndroid Build Coastguard Worker }
235*8975f5c5SAndroid Build Coastguard Worker TIntermSymbol *symbol = declInit->getLeft()->getAsSymbolNode();
236*8975f5c5SAndroid Build Coastguard Worker if (symbol == nullptr)
237*8975f5c5SAndroid Build Coastguard Worker {
238*8975f5c5SAndroid Build Coastguard Worker error(declInit->getLine(), "Invalid init declaration", "for");
239*8975f5c5SAndroid Build Coastguard Worker return -1;
240*8975f5c5SAndroid Build Coastguard Worker }
241*8975f5c5SAndroid Build Coastguard Worker // The loop index has type int or float.
242*8975f5c5SAndroid Build Coastguard Worker TBasicType type = symbol->getBasicType();
243*8975f5c5SAndroid Build Coastguard Worker if ((type != EbtInt) && (type != EbtUInt) && (type != EbtFloat))
244*8975f5c5SAndroid Build Coastguard Worker {
245*8975f5c5SAndroid Build Coastguard Worker error(symbol->getLine(), "Invalid type for loop index", getBasicString(type));
246*8975f5c5SAndroid Build Coastguard Worker return -1;
247*8975f5c5SAndroid Build Coastguard Worker }
248*8975f5c5SAndroid Build Coastguard Worker // The loop index is initialized with constant expression.
249*8975f5c5SAndroid Build Coastguard Worker if (!isConstExpr(declInit->getRight()))
250*8975f5c5SAndroid Build Coastguard Worker {
251*8975f5c5SAndroid Build Coastguard Worker error(declInit->getLine(), "Loop index cannot be initialized with non-constant expression",
252*8975f5c5SAndroid Build Coastguard Worker symbol->getName());
253*8975f5c5SAndroid Build Coastguard Worker return -1;
254*8975f5c5SAndroid Build Coastguard Worker }
255*8975f5c5SAndroid Build Coastguard Worker
256*8975f5c5SAndroid Build Coastguard Worker return symbol->uniqueId().get();
257*8975f5c5SAndroid Build Coastguard Worker }
258*8975f5c5SAndroid Build Coastguard Worker
validateForLoopCond(TIntermLoop * node,int indexSymbolId)259*8975f5c5SAndroid Build Coastguard Worker bool ValidateLimitationsTraverser::validateForLoopCond(TIntermLoop *node, int indexSymbolId)
260*8975f5c5SAndroid Build Coastguard Worker {
261*8975f5c5SAndroid Build Coastguard Worker TIntermNode *cond = node->getCondition();
262*8975f5c5SAndroid Build Coastguard Worker if (cond == nullptr)
263*8975f5c5SAndroid Build Coastguard Worker {
264*8975f5c5SAndroid Build Coastguard Worker error(node->getLine(), "Missing condition", "for");
265*8975f5c5SAndroid Build Coastguard Worker return false;
266*8975f5c5SAndroid Build Coastguard Worker }
267*8975f5c5SAndroid Build Coastguard Worker //
268*8975f5c5SAndroid Build Coastguard Worker // condition has the form:
269*8975f5c5SAndroid Build Coastguard Worker // loop_index relational_operator constant_expression
270*8975f5c5SAndroid Build Coastguard Worker //
271*8975f5c5SAndroid Build Coastguard Worker TIntermBinary *binOp = cond->getAsBinaryNode();
272*8975f5c5SAndroid Build Coastguard Worker if (binOp == nullptr)
273*8975f5c5SAndroid Build Coastguard Worker {
274*8975f5c5SAndroid Build Coastguard Worker error(node->getLine(), "Invalid condition", "for");
275*8975f5c5SAndroid Build Coastguard Worker return false;
276*8975f5c5SAndroid Build Coastguard Worker }
277*8975f5c5SAndroid Build Coastguard Worker // Loop index should be to the left of relational operator.
278*8975f5c5SAndroid Build Coastguard Worker TIntermSymbol *symbol = binOp->getLeft()->getAsSymbolNode();
279*8975f5c5SAndroid Build Coastguard Worker if (symbol == nullptr)
280*8975f5c5SAndroid Build Coastguard Worker {
281*8975f5c5SAndroid Build Coastguard Worker error(binOp->getLine(), "Invalid condition", "for");
282*8975f5c5SAndroid Build Coastguard Worker return false;
283*8975f5c5SAndroid Build Coastguard Worker }
284*8975f5c5SAndroid Build Coastguard Worker if (symbol->uniqueId().get() != indexSymbolId)
285*8975f5c5SAndroid Build Coastguard Worker {
286*8975f5c5SAndroid Build Coastguard Worker error(symbol->getLine(), "Expected loop index", symbol->getName());
287*8975f5c5SAndroid Build Coastguard Worker return false;
288*8975f5c5SAndroid Build Coastguard Worker }
289*8975f5c5SAndroid Build Coastguard Worker // Relational operator is one of: > >= < <= == or !=.
290*8975f5c5SAndroid Build Coastguard Worker switch (binOp->getOp())
291*8975f5c5SAndroid Build Coastguard Worker {
292*8975f5c5SAndroid Build Coastguard Worker case EOpEqual:
293*8975f5c5SAndroid Build Coastguard Worker case EOpNotEqual:
294*8975f5c5SAndroid Build Coastguard Worker case EOpLessThan:
295*8975f5c5SAndroid Build Coastguard Worker case EOpGreaterThan:
296*8975f5c5SAndroid Build Coastguard Worker case EOpLessThanEqual:
297*8975f5c5SAndroid Build Coastguard Worker case EOpGreaterThanEqual:
298*8975f5c5SAndroid Build Coastguard Worker break;
299*8975f5c5SAndroid Build Coastguard Worker default:
300*8975f5c5SAndroid Build Coastguard Worker error(binOp->getLine(), "Invalid relational operator",
301*8975f5c5SAndroid Build Coastguard Worker GetOperatorString(binOp->getOp()));
302*8975f5c5SAndroid Build Coastguard Worker break;
303*8975f5c5SAndroid Build Coastguard Worker }
304*8975f5c5SAndroid Build Coastguard Worker // Loop index must be compared with a constant.
305*8975f5c5SAndroid Build Coastguard Worker if (!isConstExpr(binOp->getRight()))
306*8975f5c5SAndroid Build Coastguard Worker {
307*8975f5c5SAndroid Build Coastguard Worker error(binOp->getLine(), "Loop index cannot be compared with non-constant expression",
308*8975f5c5SAndroid Build Coastguard Worker symbol->getName());
309*8975f5c5SAndroid Build Coastguard Worker return false;
310*8975f5c5SAndroid Build Coastguard Worker }
311*8975f5c5SAndroid Build Coastguard Worker
312*8975f5c5SAndroid Build Coastguard Worker return true;
313*8975f5c5SAndroid Build Coastguard Worker }
314*8975f5c5SAndroid Build Coastguard Worker
validateForLoopExpr(TIntermLoop * node,int indexSymbolId)315*8975f5c5SAndroid Build Coastguard Worker bool ValidateLimitationsTraverser::validateForLoopExpr(TIntermLoop *node, int indexSymbolId)
316*8975f5c5SAndroid Build Coastguard Worker {
317*8975f5c5SAndroid Build Coastguard Worker TIntermNode *expr = node->getExpression();
318*8975f5c5SAndroid Build Coastguard Worker if (expr == nullptr)
319*8975f5c5SAndroid Build Coastguard Worker {
320*8975f5c5SAndroid Build Coastguard Worker error(node->getLine(), "Missing expression", "for");
321*8975f5c5SAndroid Build Coastguard Worker return false;
322*8975f5c5SAndroid Build Coastguard Worker }
323*8975f5c5SAndroid Build Coastguard Worker
324*8975f5c5SAndroid Build Coastguard Worker // for expression has one of the following forms:
325*8975f5c5SAndroid Build Coastguard Worker // loop_index++
326*8975f5c5SAndroid Build Coastguard Worker // loop_index--
327*8975f5c5SAndroid Build Coastguard Worker // loop_index += constant_expression
328*8975f5c5SAndroid Build Coastguard Worker // loop_index -= constant_expression
329*8975f5c5SAndroid Build Coastguard Worker // ++loop_index
330*8975f5c5SAndroid Build Coastguard Worker // --loop_index
331*8975f5c5SAndroid Build Coastguard Worker // The last two forms are not specified in the spec, but I am assuming
332*8975f5c5SAndroid Build Coastguard Worker // its an oversight.
333*8975f5c5SAndroid Build Coastguard Worker TIntermUnary *unOp = expr->getAsUnaryNode();
334*8975f5c5SAndroid Build Coastguard Worker TIntermBinary *binOp = unOp ? nullptr : expr->getAsBinaryNode();
335*8975f5c5SAndroid Build Coastguard Worker
336*8975f5c5SAndroid Build Coastguard Worker TOperator op = EOpNull;
337*8975f5c5SAndroid Build Coastguard Worker const TFunction *opFunc = nullptr;
338*8975f5c5SAndroid Build Coastguard Worker TIntermSymbol *symbol = nullptr;
339*8975f5c5SAndroid Build Coastguard Worker if (unOp != nullptr)
340*8975f5c5SAndroid Build Coastguard Worker {
341*8975f5c5SAndroid Build Coastguard Worker op = unOp->getOp();
342*8975f5c5SAndroid Build Coastguard Worker opFunc = unOp->getFunction();
343*8975f5c5SAndroid Build Coastguard Worker symbol = unOp->getOperand()->getAsSymbolNode();
344*8975f5c5SAndroid Build Coastguard Worker }
345*8975f5c5SAndroid Build Coastguard Worker else if (binOp != nullptr)
346*8975f5c5SAndroid Build Coastguard Worker {
347*8975f5c5SAndroid Build Coastguard Worker op = binOp->getOp();
348*8975f5c5SAndroid Build Coastguard Worker symbol = binOp->getLeft()->getAsSymbolNode();
349*8975f5c5SAndroid Build Coastguard Worker }
350*8975f5c5SAndroid Build Coastguard Worker
351*8975f5c5SAndroid Build Coastguard Worker // The operand must be loop index.
352*8975f5c5SAndroid Build Coastguard Worker if (symbol == nullptr)
353*8975f5c5SAndroid Build Coastguard Worker {
354*8975f5c5SAndroid Build Coastguard Worker error(expr->getLine(), "Invalid expression", "for");
355*8975f5c5SAndroid Build Coastguard Worker return false;
356*8975f5c5SAndroid Build Coastguard Worker }
357*8975f5c5SAndroid Build Coastguard Worker if (symbol->uniqueId().get() != indexSymbolId)
358*8975f5c5SAndroid Build Coastguard Worker {
359*8975f5c5SAndroid Build Coastguard Worker error(symbol->getLine(), "Expected loop index", symbol->getName());
360*8975f5c5SAndroid Build Coastguard Worker return false;
361*8975f5c5SAndroid Build Coastguard Worker }
362*8975f5c5SAndroid Build Coastguard Worker
363*8975f5c5SAndroid Build Coastguard Worker // The operator is one of: ++ -- += -=.
364*8975f5c5SAndroid Build Coastguard Worker switch (op)
365*8975f5c5SAndroid Build Coastguard Worker {
366*8975f5c5SAndroid Build Coastguard Worker case EOpPostIncrement:
367*8975f5c5SAndroid Build Coastguard Worker case EOpPostDecrement:
368*8975f5c5SAndroid Build Coastguard Worker case EOpPreIncrement:
369*8975f5c5SAndroid Build Coastguard Worker case EOpPreDecrement:
370*8975f5c5SAndroid Build Coastguard Worker ASSERT((unOp != nullptr) && (binOp == nullptr));
371*8975f5c5SAndroid Build Coastguard Worker break;
372*8975f5c5SAndroid Build Coastguard Worker case EOpAddAssign:
373*8975f5c5SAndroid Build Coastguard Worker case EOpSubAssign:
374*8975f5c5SAndroid Build Coastguard Worker ASSERT((unOp == nullptr) && (binOp != nullptr));
375*8975f5c5SAndroid Build Coastguard Worker break;
376*8975f5c5SAndroid Build Coastguard Worker default:
377*8975f5c5SAndroid Build Coastguard Worker if (BuiltInGroup::IsBuiltIn(op))
378*8975f5c5SAndroid Build Coastguard Worker {
379*8975f5c5SAndroid Build Coastguard Worker ASSERT(opFunc != nullptr);
380*8975f5c5SAndroid Build Coastguard Worker error(expr->getLine(), "Invalid built-in call", opFunc->name().data());
381*8975f5c5SAndroid Build Coastguard Worker }
382*8975f5c5SAndroid Build Coastguard Worker else
383*8975f5c5SAndroid Build Coastguard Worker {
384*8975f5c5SAndroid Build Coastguard Worker error(expr->getLine(), "Invalid operator", GetOperatorString(op));
385*8975f5c5SAndroid Build Coastguard Worker }
386*8975f5c5SAndroid Build Coastguard Worker return false;
387*8975f5c5SAndroid Build Coastguard Worker }
388*8975f5c5SAndroid Build Coastguard Worker
389*8975f5c5SAndroid Build Coastguard Worker // Loop index must be incremented/decremented with a constant.
390*8975f5c5SAndroid Build Coastguard Worker if (binOp != nullptr)
391*8975f5c5SAndroid Build Coastguard Worker {
392*8975f5c5SAndroid Build Coastguard Worker if (!isConstExpr(binOp->getRight()))
393*8975f5c5SAndroid Build Coastguard Worker {
394*8975f5c5SAndroid Build Coastguard Worker error(binOp->getLine(), "Loop index cannot be modified by non-constant expression",
395*8975f5c5SAndroid Build Coastguard Worker symbol->getName());
396*8975f5c5SAndroid Build Coastguard Worker return false;
397*8975f5c5SAndroid Build Coastguard Worker }
398*8975f5c5SAndroid Build Coastguard Worker }
399*8975f5c5SAndroid Build Coastguard Worker
400*8975f5c5SAndroid Build Coastguard Worker return true;
401*8975f5c5SAndroid Build Coastguard Worker }
402*8975f5c5SAndroid Build Coastguard Worker
isConstExpr(TIntermNode * node)403*8975f5c5SAndroid Build Coastguard Worker bool ValidateLimitationsTraverser::isConstExpr(TIntermNode *node)
404*8975f5c5SAndroid Build Coastguard Worker {
405*8975f5c5SAndroid Build Coastguard Worker ASSERT(node != nullptr);
406*8975f5c5SAndroid Build Coastguard Worker return node->getAsConstantUnion() != nullptr && node->getAsTyped()->getQualifier() == EvqConst;
407*8975f5c5SAndroid Build Coastguard Worker }
408*8975f5c5SAndroid Build Coastguard Worker
isConstIndexExpr(TIntermNode * node)409*8975f5c5SAndroid Build Coastguard Worker bool ValidateLimitationsTraverser::isConstIndexExpr(TIntermNode *node)
410*8975f5c5SAndroid Build Coastguard Worker {
411*8975f5c5SAndroid Build Coastguard Worker ASSERT(node != nullptr);
412*8975f5c5SAndroid Build Coastguard Worker
413*8975f5c5SAndroid Build Coastguard Worker ValidateConstIndexExpr validate(mLoopSymbolIds);
414*8975f5c5SAndroid Build Coastguard Worker node->traverse(&validate);
415*8975f5c5SAndroid Build Coastguard Worker return validate.isValid();
416*8975f5c5SAndroid Build Coastguard Worker }
417*8975f5c5SAndroid Build Coastguard Worker
validateIndexing(TIntermBinary * node)418*8975f5c5SAndroid Build Coastguard Worker bool ValidateLimitationsTraverser::validateIndexing(TIntermBinary *node)
419*8975f5c5SAndroid Build Coastguard Worker {
420*8975f5c5SAndroid Build Coastguard Worker ASSERT((node->getOp() == EOpIndexDirect) || (node->getOp() == EOpIndexIndirect));
421*8975f5c5SAndroid Build Coastguard Worker
422*8975f5c5SAndroid Build Coastguard Worker bool valid = true;
423*8975f5c5SAndroid Build Coastguard Worker TIntermTyped *index = node->getRight();
424*8975f5c5SAndroid Build Coastguard Worker // The index expession must be a constant-index-expression unless
425*8975f5c5SAndroid Build Coastguard Worker // the operand is a uniform in a vertex shader.
426*8975f5c5SAndroid Build Coastguard Worker TIntermTyped *operand = node->getLeft();
427*8975f5c5SAndroid Build Coastguard Worker bool skip = (mShaderType == GL_VERTEX_SHADER) && (operand->getQualifier() == EvqUniform);
428*8975f5c5SAndroid Build Coastguard Worker if (!skip && !isConstIndexExpr(index))
429*8975f5c5SAndroid Build Coastguard Worker {
430*8975f5c5SAndroid Build Coastguard Worker error(index->getLine(), "Index expression must be constant", "[]");
431*8975f5c5SAndroid Build Coastguard Worker valid = false;
432*8975f5c5SAndroid Build Coastguard Worker }
433*8975f5c5SAndroid Build Coastguard Worker return valid;
434*8975f5c5SAndroid Build Coastguard Worker }
435*8975f5c5SAndroid Build Coastguard Worker
436*8975f5c5SAndroid Build Coastguard Worker } // namespace
437*8975f5c5SAndroid Build Coastguard Worker
ValidateLimitations(TIntermNode * root,GLenum shaderType,TSymbolTable * symbolTable,TDiagnostics * diagnostics)438*8975f5c5SAndroid Build Coastguard Worker bool ValidateLimitations(TIntermNode *root,
439*8975f5c5SAndroid Build Coastguard Worker GLenum shaderType,
440*8975f5c5SAndroid Build Coastguard Worker TSymbolTable *symbolTable,
441*8975f5c5SAndroid Build Coastguard Worker TDiagnostics *diagnostics)
442*8975f5c5SAndroid Build Coastguard Worker {
443*8975f5c5SAndroid Build Coastguard Worker ValidateLimitationsTraverser validate(shaderType, symbolTable, diagnostics);
444*8975f5c5SAndroid Build Coastguard Worker root->traverse(&validate);
445*8975f5c5SAndroid Build Coastguard Worker return diagnostics->numErrors() == 0;
446*8975f5c5SAndroid Build Coastguard Worker }
447*8975f5c5SAndroid Build Coastguard Worker
448*8975f5c5SAndroid Build Coastguard Worker } // namespace sh
449