blob: 404464a85ef3b835e6c379b4a2d88e25e481e735 [file] [log] [blame]
alokp@chromium.orgb59a7782010-11-24 18:38:33 +00001//
shannonwoods@chromium.org96e7ba12013-05-30 00:02:41 +00002// Copyright (c) 2002-2013 The ANGLE Project Authors. All rights reserved.
alokp@chromium.orgb59a7782010-11-24 18:38:33 +00003// Use of this source code is governed by a BSD-style license that can be
4// found in the LICENSE file.
5//
6
Geoff Lang17732822013-08-29 13:46:49 -04007#include "compiler/translator/ValidateLimitations.h"
8#include "compiler/translator/InfoSink.h"
9#include "compiler/translator/InitializeParseContext.h"
Jamie Madill6b9cb252013-10-17 10:45:47 -040010#include "compiler/translator/ParseContext.h"
Jamie Madill183bde52014-07-02 15:31:19 -040011#include "angle_gl.h"
alokp@chromium.orgb59a7782010-11-24 18:38:33 +000012
Jamie Madill45bcc782016-11-07 13:58:48 -050013namespace sh
14{
15
Zhenyao Mo550c6002014-02-26 15:40:48 -080016namespace
17{
zmo@google.com0b8d4eb2011-04-04 19:17:11 +000018
Corentin Wallez1b896c62016-11-16 13:10:44 -050019int GetLoopSymbolId(TIntermLoop *loop)
20{
21 // Here we assume all the operations are valid, because the loop node is
22 // already validated before this call.
23 TIntermSequence *declSeq = loop->getInit()->getAsDeclarationNode()->getSequence();
24 TIntermBinary *declInit = (*declSeq)[0]->getAsBinaryNode();
25 TIntermSymbol *symbol = declInit->getLeft()->getAsSymbolNode();
26
27 return symbol->getId();
28}
29
alokp@chromium.orgb59a7782010-11-24 18:38:33 +000030// Traverses a node to check if it represents a constant index expression.
31// Definition:
32// constant-index-expressions are a superset of constant-expressions.
33// Constant-index-expressions can include loop indices as defined in
34// GLSL ES 1.0 spec, Appendix A, section 4.
35// The following are constant-index-expressions:
36// - Constant expressions
37// - Loop indices as defined in section 4
38// - Expressions composed of both of the above
Zhenyao Mo550c6002014-02-26 15:40:48 -080039class ValidateConstIndexExpr : public TIntermTraverser
40{
41 public:
Corentin Wallez1b896c62016-11-16 13:10:44 -050042 ValidateConstIndexExpr(const std::vector<int> &loopSymbols)
43 : TIntermTraverser(true, false, false), mValid(true), mLoopSymbolIds(loopSymbols)
Olli Etuaho3d0d9a42015-06-01 12:16:36 +030044 {
45 }
alokp@chromium.orgb59a7782010-11-24 18:38:33 +000046
47 // Returns true if the parsed node represents a constant index expression.
48 bool isValid() const { return mValid; }
49
Corentin Walleze5a1f272015-08-21 02:58:25 +020050 void visitSymbol(TIntermSymbol *symbol) override
Zhenyao Mo550c6002014-02-26 15:40:48 -080051 {
alokp@chromium.orgb59a7782010-11-24 18:38:33 +000052 // Only constants and loop indices are allowed in a
53 // constant index expression.
Zhenyao Mo550c6002014-02-26 15:40:48 -080054 if (mValid)
55 {
Corentin Wallez1b896c62016-11-16 13:10:44 -050056 bool isLoopSymbol = std::find(mLoopSymbolIds.begin(), mLoopSymbolIds.end(),
57 symbol->getId()) != mLoopSymbolIds.end();
58 mValid = (symbol->getQualifier() == EvqConst) || isLoopSymbol;
alokp@chromium.orgb59a7782010-11-24 18:38:33 +000059 }
60 }
alokp@chromium.orgb59a7782010-11-24 18:38:33 +000061
Zhenyao Mo550c6002014-02-26 15:40:48 -080062 private:
alokp@chromium.orgb59a7782010-11-24 18:38:33 +000063 bool mValid;
Corentin Wallez1b896c62016-11-16 13:10:44 -050064 const std::vector<int> mLoopSymbolIds;
zmo@google.com0b8d4eb2011-04-04 19:17:11 +000065};
Zhenyao Mo550c6002014-02-26 15:40:48 -080066
67} // namespace anonymous
alokp@chromium.orgb59a7782010-11-24 18:38:33 +000068
Olli Etuaho8a76dcc2015-12-10 20:25:12 +020069ValidateLimitations::ValidateLimitations(sh::GLenum shaderType, TInfoSinkBase *sink)
Olli Etuaho3d0d9a42015-06-01 12:16:36 +030070 : TIntermTraverser(true, false, false),
71 mShaderType(shaderType),
alokp@chromium.orgb59a7782010-11-24 18:38:33 +000072 mSink(sink),
Olli Etuaho8a76dcc2015-12-10 20:25:12 +020073 mNumErrors(0),
74 mValidateIndexing(true),
75 mValidateInnerLoops(true)
alokp@chromium.orgb59a7782010-11-24 18:38:33 +000076{
77}
78
Olli Etuaho8a76dcc2015-12-10 20:25:12 +020079// static
80bool ValidateLimitations::IsLimitedForLoop(TIntermLoop *loop)
81{
82 // The shader type doesn't matter in this case.
83 ValidateLimitations validate(GL_FRAGMENT_SHADER, nullptr);
84 validate.mValidateIndexing = false;
85 validate.mValidateInnerLoops = false;
86 if (!validate.validateLoopType(loop))
87 return false;
88 if (!validate.validateForLoopHeader(loop))
89 return false;
90 TIntermNode *body = loop->getBody();
91 if (body != nullptr)
92 {
Corentin Wallez1b896c62016-11-16 13:10:44 -050093 validate.mLoopSymbolIds.push_back(GetLoopSymbolId(loop));
Olli Etuaho8a76dcc2015-12-10 20:25:12 +020094 body->traverse(&validate);
Corentin Wallez1b896c62016-11-16 13:10:44 -050095 validate.mLoopSymbolIds.pop_back();
Olli Etuaho8a76dcc2015-12-10 20:25:12 +020096 }
97 return (validate.mNumErrors == 0);
98}
99
Zhenyao Mo550c6002014-02-26 15:40:48 -0800100bool ValidateLimitations::visitBinary(Visit, TIntermBinary *node)
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000101{
102 // Check if loop index is modified in the loop body.
103 validateOperation(node, node->getLeft());
104
105 // Check indexing.
Zhenyao Mo550c6002014-02-26 15:40:48 -0800106 switch (node->getOp())
107 {
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500108 case EOpIndexDirect:
109 case EOpIndexIndirect:
110 if (mValidateIndexing)
111 validateIndexing(node);
112 break;
113 default:
114 break;
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000115 }
116 return true;
117}
118
Zhenyao Mo550c6002014-02-26 15:40:48 -0800119bool ValidateLimitations::visitUnary(Visit, TIntermUnary *node)
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000120{
121 // Check if loop index is modified in the loop body.
122 validateOperation(node, node->getOperand());
123
124 return true;
125}
126
Zhenyao Mo550c6002014-02-26 15:40:48 -0800127bool ValidateLimitations::visitAggregate(Visit, TIntermAggregate *node)
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000128{
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500129 switch (node->getOp())
130 {
131 case EOpFunctionCall:
132 validateFunctionCall(node);
133 break;
134 default:
135 break;
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000136 }
137 return true;
138}
139
Zhenyao Mo550c6002014-02-26 15:40:48 -0800140bool ValidateLimitations::visitLoop(Visit, TIntermLoop *node)
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000141{
Olli Etuaho8a76dcc2015-12-10 20:25:12 +0200142 if (!mValidateInnerLoops)
143 return true;
144
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000145 if (!validateLoopType(node))
146 return false;
147
Zhenyao Mo550c6002014-02-26 15:40:48 -0800148 if (!validateForLoopHeader(node))
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000149 return false;
150
Zhenyao Mo550c6002014-02-26 15:40:48 -0800151 TIntermNode *body = node->getBody();
152 if (body != NULL)
153 {
Corentin Wallez1b896c62016-11-16 13:10:44 -0500154 mLoopSymbolIds.push_back(GetLoopSymbolId(node));
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000155 body->traverse(this);
Corentin Wallez1b896c62016-11-16 13:10:44 -0500156 mLoopSymbolIds.pop_back();
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000157 }
158
159 // The loop is fully processed - no need to visit children.
160 return false;
161}
162
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500163void ValidateLimitations::error(TSourceLoc loc, const char *reason, const char *token)
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000164{
Olli Etuaho8a76dcc2015-12-10 20:25:12 +0200165 if (mSink)
166 {
167 mSink->prefix(EPrefixError);
168 mSink->location(loc);
169 (*mSink) << "'" << token << "' : " << reason << "\n";
170 }
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000171 ++mNumErrors;
172}
173
174bool ValidateLimitations::withinLoopBody() const
175{
Corentin Wallez1b896c62016-11-16 13:10:44 -0500176 return !mLoopSymbolIds.empty();
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000177}
178
Zhenyao Mo550c6002014-02-26 15:40:48 -0800179bool ValidateLimitations::isLoopIndex(TIntermSymbol *symbol)
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000180{
Corentin Wallez1b896c62016-11-16 13:10:44 -0500181 return std::find(mLoopSymbolIds.begin(), mLoopSymbolIds.end(), symbol->getId()) !=
182 mLoopSymbolIds.end();
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000183}
184
Zhenyao Mo550c6002014-02-26 15:40:48 -0800185bool ValidateLimitations::validateLoopType(TIntermLoop *node)
186{
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000187 TLoopType type = node->getType();
188 if (type == ELoopFor)
189 return true;
190
191 // Reject while and do-while loops.
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500192 error(node->getLine(), "This type of loop is not allowed", type == ELoopWhile ? "while" : "do");
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000193 return false;
194}
195
Zhenyao Mo550c6002014-02-26 15:40:48 -0800196bool ValidateLimitations::validateForLoopHeader(TIntermLoop *node)
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000197{
198 ASSERT(node->getType() == ELoopFor);
199
200 //
201 // The for statement has the form:
202 // for ( init-declaration ; condition ; expression ) statement
203 //
Zhenyao Mo550c6002014-02-26 15:40:48 -0800204 int indexSymbolId = validateForLoopInit(node);
205 if (indexSymbolId < 0)
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000206 return false;
Zhenyao Mo550c6002014-02-26 15:40:48 -0800207 if (!validateForLoopCond(node, indexSymbolId))
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000208 return false;
Zhenyao Mo550c6002014-02-26 15:40:48 -0800209 if (!validateForLoopExpr(node, indexSymbolId))
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000210 return false;
211
212 return true;
213}
214
Zhenyao Mo550c6002014-02-26 15:40:48 -0800215int ValidateLimitations::validateForLoopInit(TIntermLoop *node)
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000216{
Zhenyao Mo550c6002014-02-26 15:40:48 -0800217 TIntermNode *init = node->getInit();
218 if (init == NULL)
219 {
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000220 error(node->getLine(), "Missing init declaration", "for");
Zhenyao Mo550c6002014-02-26 15:40:48 -0800221 return -1;
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000222 }
223
224 //
225 // init-declaration has the form:
226 // type-specifier identifier = constant-expression
227 //
Olli Etuaho13389b62016-10-16 11:48:18 +0100228 TIntermDeclaration *decl = init->getAsDeclarationNode();
229 if (decl == nullptr)
Zhenyao Mo550c6002014-02-26 15:40:48 -0800230 {
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000231 error(init->getLine(), "Invalid init declaration", "for");
Zhenyao Mo550c6002014-02-26 15:40:48 -0800232 return -1;
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000233 }
234 // To keep things simple do not allow declaration list.
Zhenyao Moe40d1e92014-07-16 17:40:36 -0700235 TIntermSequence *declSeq = decl->getSequence();
236 if (declSeq->size() != 1)
Zhenyao Mo550c6002014-02-26 15:40:48 -0800237 {
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000238 error(decl->getLine(), "Invalid init declaration", "for");
Zhenyao Mo550c6002014-02-26 15:40:48 -0800239 return -1;
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000240 }
Zhenyao Moe40d1e92014-07-16 17:40:36 -0700241 TIntermBinary *declInit = (*declSeq)[0]->getAsBinaryNode();
Zhenyao Mo550c6002014-02-26 15:40:48 -0800242 if ((declInit == NULL) || (declInit->getOp() != EOpInitialize))
243 {
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000244 error(decl->getLine(), "Invalid init declaration", "for");
Zhenyao Mo550c6002014-02-26 15:40:48 -0800245 return -1;
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000246 }
Zhenyao Mo550c6002014-02-26 15:40:48 -0800247 TIntermSymbol *symbol = declInit->getLeft()->getAsSymbolNode();
248 if (symbol == NULL)
249 {
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000250 error(declInit->getLine(), "Invalid init declaration", "for");
Zhenyao Mo550c6002014-02-26 15:40:48 -0800251 return -1;
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000252 }
253 // The loop index has type int or float.
254 TBasicType type = symbol->getBasicType();
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500255 if ((type != EbtInt) && (type != EbtUInt) && (type != EbtFloat))
256 {
257 error(symbol->getLine(), "Invalid type for loop index", getBasicString(type));
Zhenyao Mo550c6002014-02-26 15:40:48 -0800258 return -1;
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000259 }
260 // The loop index is initialized with constant expression.
Zhenyao Mo550c6002014-02-26 15:40:48 -0800261 if (!isConstExpr(declInit->getRight()))
262 {
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500263 error(declInit->getLine(), "Loop index cannot be initialized with non-constant expression",
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000264 symbol->getSymbol().c_str());
Zhenyao Mo550c6002014-02-26 15:40:48 -0800265 return -1;
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000266 }
267
Zhenyao Mo550c6002014-02-26 15:40:48 -0800268 return symbol->getId();
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000269}
270
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500271bool ValidateLimitations::validateForLoopCond(TIntermLoop *node, int indexSymbolId)
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000272{
Zhenyao Mo550c6002014-02-26 15:40:48 -0800273 TIntermNode *cond = node->getCondition();
274 if (cond == NULL)
275 {
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000276 error(node->getLine(), "Missing condition", "for");
277 return false;
278 }
279 //
280 // condition has the form:
281 // loop_index relational_operator constant_expression
282 //
Zhenyao Mo550c6002014-02-26 15:40:48 -0800283 TIntermBinary *binOp = cond->getAsBinaryNode();
284 if (binOp == NULL)
285 {
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000286 error(node->getLine(), "Invalid condition", "for");
287 return false;
288 }
289 // Loop index should be to the left of relational operator.
Zhenyao Mo550c6002014-02-26 15:40:48 -0800290 TIntermSymbol *symbol = binOp->getLeft()->getAsSymbolNode();
291 if (symbol == NULL)
292 {
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000293 error(binOp->getLine(), "Invalid condition", "for");
294 return false;
295 }
Zhenyao Mo550c6002014-02-26 15:40:48 -0800296 if (symbol->getId() != indexSymbolId)
297 {
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500298 error(symbol->getLine(), "Expected loop index", symbol->getSymbol().c_str());
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000299 return false;
300 }
301 // Relational operator is one of: > >= < <= == or !=.
Zhenyao Mo550c6002014-02-26 15:40:48 -0800302 switch (binOp->getOp())
303 {
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500304 case EOpEqual:
305 case EOpNotEqual:
306 case EOpLessThan:
307 case EOpGreaterThan:
308 case EOpLessThanEqual:
309 case EOpGreaterThanEqual:
310 break;
311 default:
312 error(binOp->getLine(), "Invalid relational operator",
313 GetOperatorString(binOp->getOp()));
314 break;
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000315 }
316 // Loop index must be compared with a constant.
Zhenyao Mo550c6002014-02-26 15:40:48 -0800317 if (!isConstExpr(binOp->getRight()))
318 {
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500319 error(binOp->getLine(), "Loop index cannot be compared with non-constant expression",
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000320 symbol->getSymbol().c_str());
321 return false;
322 }
323
324 return true;
325}
326
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500327bool ValidateLimitations::validateForLoopExpr(TIntermLoop *node, int indexSymbolId)
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000328{
Zhenyao Mo550c6002014-02-26 15:40:48 -0800329 TIntermNode *expr = node->getExpression();
330 if (expr == NULL)
331 {
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000332 error(node->getLine(), "Missing expression", "for");
333 return false;
334 }
335
336 // for expression has one of the following forms:
337 // loop_index++
338 // loop_index--
339 // loop_index += constant_expression
340 // loop_index -= constant_expression
341 // ++loop_index
342 // --loop_index
343 // The last two forms are not specified in the spec, but I am assuming
344 // its an oversight.
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500345 TIntermUnary *unOp = expr->getAsUnaryNode();
Zhenyao Mo550c6002014-02-26 15:40:48 -0800346 TIntermBinary *binOp = unOp ? NULL : expr->getAsBinaryNode();
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000347
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500348 TOperator op = EOpNull;
Zhenyao Mo550c6002014-02-26 15:40:48 -0800349 TIntermSymbol *symbol = NULL;
350 if (unOp != NULL)
351 {
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500352 op = unOp->getOp();
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000353 symbol = unOp->getOperand()->getAsSymbolNode();
Zhenyao Mo550c6002014-02-26 15:40:48 -0800354 }
355 else if (binOp != NULL)
356 {
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500357 op = binOp->getOp();
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000358 symbol = binOp->getLeft()->getAsSymbolNode();
359 }
360
361 // The operand must be loop index.
Zhenyao Mo550c6002014-02-26 15:40:48 -0800362 if (symbol == NULL)
363 {
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000364 error(expr->getLine(), "Invalid expression", "for");
365 return false;
366 }
Zhenyao Mo550c6002014-02-26 15:40:48 -0800367 if (symbol->getId() != indexSymbolId)
368 {
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500369 error(symbol->getLine(), "Expected loop index", symbol->getSymbol().c_str());
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000370 return false;
371 }
372
373 // The operator is one of: ++ -- += -=.
Zhenyao Mo550c6002014-02-26 15:40:48 -0800374 switch (op)
375 {
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500376 case EOpPostIncrement:
377 case EOpPostDecrement:
378 case EOpPreIncrement:
379 case EOpPreDecrement:
380 ASSERT((unOp != NULL) && (binOp == NULL));
381 break;
382 case EOpAddAssign:
383 case EOpSubAssign:
384 ASSERT((unOp == NULL) && (binOp != NULL));
385 break;
386 default:
387 error(expr->getLine(), "Invalid operator", GetOperatorString(op));
388 return false;
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000389 }
390
391 // Loop index must be incremented/decremented with a constant.
Zhenyao Mo550c6002014-02-26 15:40:48 -0800392 if (binOp != NULL)
393 {
394 if (!isConstExpr(binOp->getRight()))
395 {
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500396 error(binOp->getLine(), "Loop index cannot be modified by non-constant expression",
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000397 symbol->getSymbol().c_str());
398 return false;
399 }
400 }
401
402 return true;
403}
404
Zhenyao Mo550c6002014-02-26 15:40:48 -0800405bool ValidateLimitations::validateFunctionCall(TIntermAggregate *node)
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000406{
407 ASSERT(node->getOp() == EOpFunctionCall);
408
409 // If not within loop body, there is nothing to check.
410 if (!withinLoopBody())
411 return true;
412
413 // List of param indices for which loop indices are used as argument.
shannon.woods@transgaming.comd64b3da2013-02-28 23:19:26 +0000414 typedef std::vector<size_t> ParamIndex;
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000415 ParamIndex pIndex;
Zhenyao Moe40d1e92014-07-16 17:40:36 -0700416 TIntermSequence *params = node->getSequence();
417 for (TIntermSequence::size_type i = 0; i < params->size(); ++i)
Zhenyao Mo550c6002014-02-26 15:40:48 -0800418 {
Zhenyao Moe40d1e92014-07-16 17:40:36 -0700419 TIntermSymbol *symbol = (*params)[i]->getAsSymbolNode();
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000420 if (symbol && isLoopIndex(symbol))
421 pIndex.push_back(i);
422 }
423 // If none of the loop indices are used as arguments,
424 // there is nothing to check.
425 if (pIndex.empty())
426 return true;
427
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500428 bool valid = true;
429 TSymbolTable &symbolTable = GetGlobalParseContext()->symbolTable;
Olli Etuahobd674552016-10-06 13:28:42 +0100430 TSymbol *symbol = symbolTable.find(node->getFunctionSymbolInfo()->getName(),
431 GetGlobalParseContext()->getShaderVersion());
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000432 ASSERT(symbol && symbol->isFunction());
Zhenyao Mo550c6002014-02-26 15:40:48 -0800433 TFunction *function = static_cast<TFunction *>(symbol);
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500434 for (ParamIndex::const_iterator i = pIndex.begin(); i != pIndex.end(); ++i)
Zhenyao Mo550c6002014-02-26 15:40:48 -0800435 {
Dmitry Skibaefa3d8e2015-06-22 14:52:10 -0700436 const TConstParameter &param = function->getParam(*i);
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500437 TQualifier qual = param.type->getQualifier();
Zhenyao Mo550c6002014-02-26 15:40:48 -0800438 if ((qual == EvqOut) || (qual == EvqInOut))
439 {
Zhenyao Moe40d1e92014-07-16 17:40:36 -0700440 error((*params)[*i]->getLine(),
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000441 "Loop index cannot be used as argument to a function out or inout parameter",
Zhenyao Moe40d1e92014-07-16 17:40:36 -0700442 (*params)[*i]->getAsSymbolNode()->getSymbol().c_str());
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000443 valid = false;
444 }
445 }
446
447 return valid;
448}
449
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500450bool ValidateLimitations::validateOperation(TIntermOperator *node, TIntermNode *operand)
Zhenyao Mo550c6002014-02-26 15:40:48 -0800451{
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000452 // Check if loop index is modified in the loop body.
Jamie Madillf4b79ba2013-11-26 10:38:18 -0500453 if (!withinLoopBody() || !node->isAssignment())
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000454 return true;
455
Zhenyao Mo550c6002014-02-26 15:40:48 -0800456 TIntermSymbol *symbol = operand->getAsSymbolNode();
457 if (symbol && isLoopIndex(symbol))
458 {
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000459 error(node->getLine(),
460 "Loop index cannot be statically assigned to within the body of the loop",
461 symbol->getSymbol().c_str());
462 }
463 return true;
464}
465
Zhenyao Mo550c6002014-02-26 15:40:48 -0800466bool ValidateLimitations::isConstExpr(TIntermNode *node)
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000467{
Olli Etuahod5610572015-12-10 19:42:09 +0200468 ASSERT(node != nullptr);
469 return node->getAsConstantUnion() != nullptr && node->getAsTyped()->getQualifier() == EvqConst;
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000470}
471
Zhenyao Mo550c6002014-02-26 15:40:48 -0800472bool ValidateLimitations::isConstIndexExpr(TIntermNode *node)
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000473{
474 ASSERT(node != NULL);
475
Corentin Wallez1b896c62016-11-16 13:10:44 -0500476 ValidateConstIndexExpr validate(mLoopSymbolIds);
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000477 node->traverse(&validate);
478 return validate.isValid();
479}
480
Zhenyao Mo550c6002014-02-26 15:40:48 -0800481bool ValidateLimitations::validateIndexing(TIntermBinary *node)
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000482{
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500483 ASSERT((node->getOp() == EOpIndexDirect) || (node->getOp() == EOpIndexIndirect));
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000484
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500485 bool valid = true;
Zhenyao Mo550c6002014-02-26 15:40:48 -0800486 TIntermTyped *index = node->getRight();
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000487 // The index expession must be a constant-index-expression unless
488 // the operand is a uniform in a vertex shader.
Zhenyao Mo550c6002014-02-26 15:40:48 -0800489 TIntermTyped *operand = node->getLeft();
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500490 bool skip = (mShaderType == GL_VERTEX_SHADER) && (operand->getQualifier() == EvqUniform);
Zhenyao Mo550c6002014-02-26 15:40:48 -0800491 if (!skip && !isConstIndexExpr(index))
492 {
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000493 error(index->getLine(), "Index expression must be constant", "[]");
494 valid = false;
495 }
496 return valid;
497}
498
Jamie Madill45bcc782016-11-07 13:58:48 -0500499} // namespace sh