blob: 23f99897a5120cb9d87b3f96a676bfbaab4f5e30 [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"
Olli Etuaho77ba4082016-12-16 12:01:18 +00008
Jamie Madill183bde52014-07-02 15:31:19 -04009#include "angle_gl.h"
Olli Etuaho9ec79392017-03-31 23:04:23 +030010#include "compiler/translator/Diagnostics.h"
Olli Etuahocccf2b02017-07-05 14:50:54 +030011#include "compiler/translator/IntermTraverse.h"
Olli Etuaho9ec79392017-03-31 23:04:23 +030012#include "compiler/translator/ParseContext.h"
alokp@chromium.orgb59a7782010-11-24 18:38:33 +000013
Jamie Madill45bcc782016-11-07 13:58:48 -050014namespace sh
15{
16
Zhenyao Mo550c6002014-02-26 15:40:48 -080017namespace
18{
zmo@google.com0b8d4eb2011-04-04 19:17:11 +000019
Corentin Wallez1b896c62016-11-16 13:10:44 -050020int GetLoopSymbolId(TIntermLoop *loop)
21{
22 // Here we assume all the operations are valid, because the loop node is
23 // already validated before this call.
24 TIntermSequence *declSeq = loop->getInit()->getAsDeclarationNode()->getSequence();
25 TIntermBinary *declInit = (*declSeq)[0]->getAsBinaryNode();
26 TIntermSymbol *symbol = declInit->getLeft()->getAsSymbolNode();
27
Olli Etuahob6af22b2017-12-15 14:05:44 +020028 return symbol->uniqueId().get();
Corentin Wallez1b896c62016-11-16 13:10:44 -050029}
30
alokp@chromium.orgb59a7782010-11-24 18:38:33 +000031// Traverses a node to check if it represents a constant index expression.
32// Definition:
33// constant-index-expressions are a superset of constant-expressions.
34// Constant-index-expressions can include loop indices as defined in
35// GLSL ES 1.0 spec, Appendix A, section 4.
36// The following are constant-index-expressions:
37// - Constant expressions
38// - Loop indices as defined in section 4
39// - Expressions composed of both of the above
Zhenyao Mo550c6002014-02-26 15:40:48 -080040class ValidateConstIndexExpr : public TIntermTraverser
41{
42 public:
Corentin Wallez1b896c62016-11-16 13:10:44 -050043 ValidateConstIndexExpr(const std::vector<int> &loopSymbols)
44 : TIntermTraverser(true, false, false), mValid(true), mLoopSymbolIds(loopSymbols)
Olli Etuaho3d0d9a42015-06-01 12:16:36 +030045 {
46 }
alokp@chromium.orgb59a7782010-11-24 18:38:33 +000047
48 // Returns true if the parsed node represents a constant index expression.
49 bool isValid() const { return mValid; }
50
Corentin Walleze5a1f272015-08-21 02:58:25 +020051 void visitSymbol(TIntermSymbol *symbol) override
Zhenyao Mo550c6002014-02-26 15:40:48 -080052 {
alokp@chromium.orgb59a7782010-11-24 18:38:33 +000053 // Only constants and loop indices are allowed in a
54 // constant index expression.
Zhenyao Mo550c6002014-02-26 15:40:48 -080055 if (mValid)
56 {
Corentin Wallez1b896c62016-11-16 13:10:44 -050057 bool isLoopSymbol = std::find(mLoopSymbolIds.begin(), mLoopSymbolIds.end(),
Olli Etuahob6af22b2017-12-15 14:05:44 +020058 symbol->uniqueId().get()) != mLoopSymbolIds.end();
Corentin Wallez1b896c62016-11-16 13:10:44 -050059 mValid = (symbol->getQualifier() == EvqConst) || isLoopSymbol;
alokp@chromium.orgb59a7782010-11-24 18:38:33 +000060 }
61 }
alokp@chromium.orgb59a7782010-11-24 18:38:33 +000062
Zhenyao Mo550c6002014-02-26 15:40:48 -080063 private:
alokp@chromium.orgb59a7782010-11-24 18:38:33 +000064 bool mValid;
Corentin Wallez1b896c62016-11-16 13:10:44 -050065 const std::vector<int> mLoopSymbolIds;
zmo@google.com0b8d4eb2011-04-04 19:17:11 +000066};
Zhenyao Mo550c6002014-02-26 15:40:48 -080067
Olli Etuaho9ec79392017-03-31 23:04:23 +030068// Traverses intermediate tree to ensure that the shader does not exceed the
69// minimum functionality mandated in GLSL 1.0 spec, Appendix A.
70class ValidateLimitationsTraverser : public TLValueTrackingTraverser
71{
72 public:
73 ValidateLimitationsTraverser(sh::GLenum shaderType,
Olli Etuahoa5e693a2017-07-13 16:07:26 +030074 TSymbolTable *symbolTable,
Olli Etuaho9ec79392017-03-31 23:04:23 +030075 int shaderVersion,
76 TDiagnostics *diagnostics);
alokp@chromium.orgb59a7782010-11-24 18:38:33 +000077
Olli Etuaho9ec79392017-03-31 23:04:23 +030078 void visitSymbol(TIntermSymbol *node) override;
79 bool visitBinary(Visit, TIntermBinary *) override;
80 bool visitLoop(Visit, TIntermLoop *) override;
81
82 private:
83 void error(TSourceLoc loc, const char *reason, const char *token);
84
Olli Etuaho9ec79392017-03-31 23:04:23 +030085 bool isLoopIndex(TIntermSymbol *symbol);
86 bool validateLoopType(TIntermLoop *node);
87
88 bool validateForLoopHeader(TIntermLoop *node);
89 // If valid, return the index symbol id; Otherwise, return -1.
90 int validateForLoopInit(TIntermLoop *node);
91 bool validateForLoopCond(TIntermLoop *node, int indexSymbolId);
92 bool validateForLoopExpr(TIntermLoop *node, int indexSymbolId);
93
94 // Returns true if indexing does not exceed the minimum functionality
95 // mandated in GLSL 1.0 spec, Appendix A, Section 5.
96 bool isConstExpr(TIntermNode *node);
97 bool isConstIndexExpr(TIntermNode *node);
98 bool validateIndexing(TIntermBinary *node);
99
100 sh::GLenum mShaderType;
101 TDiagnostics *mDiagnostics;
102 std::vector<int> mLoopSymbolIds;
103};
104
105ValidateLimitationsTraverser::ValidateLimitationsTraverser(sh::GLenum shaderType,
Olli Etuahoa5e693a2017-07-13 16:07:26 +0300106 TSymbolTable *symbolTable,
Olli Etuaho9ec79392017-03-31 23:04:23 +0300107 int shaderVersion,
108 TDiagnostics *diagnostics)
109 : TLValueTrackingTraverser(true, false, false, symbolTable, shaderVersion),
Olli Etuaho3d0d9a42015-06-01 12:16:36 +0300110 mShaderType(shaderType),
Olli Etuaho9ec79392017-03-31 23:04:23 +0300111 mDiagnostics(diagnostics)
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000112{
Olli Etuaho77ba4082016-12-16 12:01:18 +0000113 ASSERT(diagnostics);
Olli Etuaho8a76dcc2015-12-10 20:25:12 +0200114}
115
Olli Etuaho9ec79392017-03-31 23:04:23 +0300116void ValidateLimitationsTraverser::visitSymbol(TIntermSymbol *node)
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000117{
Olli Etuaho9ec79392017-03-31 23:04:23 +0300118 if (isLoopIndex(node) && isLValueRequiredHere())
119 {
120 error(node->getLine(),
121 "Loop index cannot be statically assigned to within the body of the loop",
Olli Etuaho8b5e8fd2017-12-15 14:59:15 +0200122 node->getName().c_str());
Olli Etuaho9ec79392017-03-31 23:04:23 +0300123 }
124}
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000125
Olli Etuaho9ec79392017-03-31 23:04:23 +0300126bool ValidateLimitationsTraverser::visitBinary(Visit, TIntermBinary *node)
127{
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000128 // Check indexing.
Zhenyao Mo550c6002014-02-26 15:40:48 -0800129 switch (node->getOp())
130 {
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500131 case EOpIndexDirect:
132 case EOpIndexIndirect:
Olli Etuaho9ec79392017-03-31 23:04:23 +0300133 validateIndexing(node);
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500134 break;
135 default:
136 break;
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000137 }
138 return true;
139}
140
Olli Etuaho9ec79392017-03-31 23:04:23 +0300141bool ValidateLimitationsTraverser::visitLoop(Visit, TIntermLoop *node)
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000142{
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000143 if (!validateLoopType(node))
144 return false;
145
Zhenyao Mo550c6002014-02-26 15:40:48 -0800146 if (!validateForLoopHeader(node))
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000147 return false;
148
Zhenyao Mo550c6002014-02-26 15:40:48 -0800149 TIntermNode *body = node->getBody();
Yunchao He4f285442017-04-21 12:15:49 +0800150 if (body != nullptr)
Zhenyao Mo550c6002014-02-26 15:40:48 -0800151 {
Corentin Wallez1b896c62016-11-16 13:10:44 -0500152 mLoopSymbolIds.push_back(GetLoopSymbolId(node));
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000153 body->traverse(this);
Corentin Wallez1b896c62016-11-16 13:10:44 -0500154 mLoopSymbolIds.pop_back();
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000155 }
156
157 // The loop is fully processed - no need to visit children.
158 return false;
159}
160
Olli Etuaho9ec79392017-03-31 23:04:23 +0300161void ValidateLimitationsTraverser::error(TSourceLoc loc, const char *reason, const char *token)
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000162{
Olli Etuaho77ba4082016-12-16 12:01:18 +0000163 mDiagnostics->error(loc, reason, token);
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000164}
165
Olli Etuaho9ec79392017-03-31 23:04:23 +0300166bool ValidateLimitationsTraverser::isLoopIndex(TIntermSymbol *symbol)
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000167{
Olli Etuahob6af22b2017-12-15 14:05:44 +0200168 return std::find(mLoopSymbolIds.begin(), mLoopSymbolIds.end(), symbol->uniqueId().get()) !=
Corentin Wallez1b896c62016-11-16 13:10:44 -0500169 mLoopSymbolIds.end();
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000170}
171
Olli Etuaho9ec79392017-03-31 23:04:23 +0300172bool ValidateLimitationsTraverser::validateLoopType(TIntermLoop *node)
Zhenyao Mo550c6002014-02-26 15:40:48 -0800173{
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000174 TLoopType type = node->getType();
175 if (type == ELoopFor)
176 return true;
177
178 // Reject while and do-while loops.
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500179 error(node->getLine(), "This type of loop is not allowed", type == ELoopWhile ? "while" : "do");
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000180 return false;
181}
182
Olli Etuaho9ec79392017-03-31 23:04:23 +0300183bool ValidateLimitationsTraverser::validateForLoopHeader(TIntermLoop *node)
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000184{
185 ASSERT(node->getType() == ELoopFor);
186
187 //
188 // The for statement has the form:
189 // for ( init-declaration ; condition ; expression ) statement
190 //
Zhenyao Mo550c6002014-02-26 15:40:48 -0800191 int indexSymbolId = validateForLoopInit(node);
192 if (indexSymbolId < 0)
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000193 return false;
Zhenyao Mo550c6002014-02-26 15:40:48 -0800194 if (!validateForLoopCond(node, indexSymbolId))
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000195 return false;
Zhenyao Mo550c6002014-02-26 15:40:48 -0800196 if (!validateForLoopExpr(node, indexSymbolId))
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000197 return false;
198
199 return true;
200}
201
Olli Etuaho9ec79392017-03-31 23:04:23 +0300202int ValidateLimitationsTraverser::validateForLoopInit(TIntermLoop *node)
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000203{
Zhenyao Mo550c6002014-02-26 15:40:48 -0800204 TIntermNode *init = node->getInit();
Yunchao He4f285442017-04-21 12:15:49 +0800205 if (init == nullptr)
Zhenyao Mo550c6002014-02-26 15:40:48 -0800206 {
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000207 error(node->getLine(), "Missing init declaration", "for");
Zhenyao Mo550c6002014-02-26 15:40:48 -0800208 return -1;
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000209 }
210
211 //
212 // init-declaration has the form:
213 // type-specifier identifier = constant-expression
214 //
Olli Etuaho13389b62016-10-16 11:48:18 +0100215 TIntermDeclaration *decl = init->getAsDeclarationNode();
216 if (decl == nullptr)
Zhenyao Mo550c6002014-02-26 15:40:48 -0800217 {
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000218 error(init->getLine(), "Invalid init declaration", "for");
Zhenyao Mo550c6002014-02-26 15:40:48 -0800219 return -1;
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000220 }
221 // To keep things simple do not allow declaration list.
Zhenyao Moe40d1e92014-07-16 17:40:36 -0700222 TIntermSequence *declSeq = decl->getSequence();
223 if (declSeq->size() != 1)
Zhenyao Mo550c6002014-02-26 15:40:48 -0800224 {
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000225 error(decl->getLine(), "Invalid init declaration", "for");
Zhenyao Mo550c6002014-02-26 15:40:48 -0800226 return -1;
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000227 }
Zhenyao Moe40d1e92014-07-16 17:40:36 -0700228 TIntermBinary *declInit = (*declSeq)[0]->getAsBinaryNode();
Yunchao He4f285442017-04-21 12:15:49 +0800229 if ((declInit == nullptr) || (declInit->getOp() != EOpInitialize))
Zhenyao Mo550c6002014-02-26 15:40:48 -0800230 {
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000231 error(decl->getLine(), "Invalid init declaration", "for");
Zhenyao Mo550c6002014-02-26 15:40:48 -0800232 return -1;
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000233 }
Zhenyao Mo550c6002014-02-26 15:40:48 -0800234 TIntermSymbol *symbol = declInit->getLeft()->getAsSymbolNode();
Yunchao He4f285442017-04-21 12:15:49 +0800235 if (symbol == nullptr)
Zhenyao Mo550c6002014-02-26 15:40:48 -0800236 {
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000237 error(declInit->getLine(), "Invalid init declaration", "for");
Zhenyao Mo550c6002014-02-26 15:40:48 -0800238 return -1;
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000239 }
240 // The loop index has type int or float.
241 TBasicType type = symbol->getBasicType();
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500242 if ((type != EbtInt) && (type != EbtUInt) && (type != EbtFloat))
243 {
244 error(symbol->getLine(), "Invalid type for loop index", getBasicString(type));
Zhenyao Mo550c6002014-02-26 15:40:48 -0800245 return -1;
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000246 }
247 // The loop index is initialized with constant expression.
Zhenyao Mo550c6002014-02-26 15:40:48 -0800248 if (!isConstExpr(declInit->getRight()))
249 {
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500250 error(declInit->getLine(), "Loop index cannot be initialized with non-constant expression",
Olli Etuaho8b5e8fd2017-12-15 14:59:15 +0200251 symbol->getName().c_str());
Zhenyao Mo550c6002014-02-26 15:40:48 -0800252 return -1;
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000253 }
254
Olli Etuahob6af22b2017-12-15 14:05:44 +0200255 return symbol->uniqueId().get();
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000256}
257
Olli Etuaho9ec79392017-03-31 23:04:23 +0300258bool ValidateLimitationsTraverser::validateForLoopCond(TIntermLoop *node, int indexSymbolId)
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000259{
Zhenyao Mo550c6002014-02-26 15:40:48 -0800260 TIntermNode *cond = node->getCondition();
Yunchao He4f285442017-04-21 12:15:49 +0800261 if (cond == nullptr)
Zhenyao Mo550c6002014-02-26 15:40:48 -0800262 {
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000263 error(node->getLine(), "Missing condition", "for");
264 return false;
265 }
266 //
267 // condition has the form:
268 // loop_index relational_operator constant_expression
269 //
Zhenyao Mo550c6002014-02-26 15:40:48 -0800270 TIntermBinary *binOp = cond->getAsBinaryNode();
Yunchao He4f285442017-04-21 12:15:49 +0800271 if (binOp == nullptr)
Zhenyao Mo550c6002014-02-26 15:40:48 -0800272 {
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000273 error(node->getLine(), "Invalid condition", "for");
274 return false;
275 }
276 // Loop index should be to the left of relational operator.
Zhenyao Mo550c6002014-02-26 15:40:48 -0800277 TIntermSymbol *symbol = binOp->getLeft()->getAsSymbolNode();
Yunchao He4f285442017-04-21 12:15:49 +0800278 if (symbol == nullptr)
Zhenyao Mo550c6002014-02-26 15:40:48 -0800279 {
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000280 error(binOp->getLine(), "Invalid condition", "for");
281 return false;
282 }
Olli Etuahob6af22b2017-12-15 14:05:44 +0200283 if (symbol->uniqueId().get() != indexSymbolId)
Zhenyao Mo550c6002014-02-26 15:40:48 -0800284 {
Olli Etuaho8b5e8fd2017-12-15 14:59:15 +0200285 error(symbol->getLine(), "Expected loop index", symbol->getName().c_str());
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000286 return false;
287 }
288 // Relational operator is one of: > >= < <= == or !=.
Zhenyao Mo550c6002014-02-26 15:40:48 -0800289 switch (binOp->getOp())
290 {
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500291 case EOpEqual:
292 case EOpNotEqual:
293 case EOpLessThan:
294 case EOpGreaterThan:
295 case EOpLessThanEqual:
296 case EOpGreaterThanEqual:
297 break;
298 default:
299 error(binOp->getLine(), "Invalid relational operator",
300 GetOperatorString(binOp->getOp()));
301 break;
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000302 }
303 // Loop index must be compared with a constant.
Zhenyao Mo550c6002014-02-26 15:40:48 -0800304 if (!isConstExpr(binOp->getRight()))
305 {
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500306 error(binOp->getLine(), "Loop index cannot be compared with non-constant expression",
Olli Etuaho8b5e8fd2017-12-15 14:59:15 +0200307 symbol->getName().c_str());
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000308 return false;
309 }
310
311 return true;
312}
313
Olli Etuaho9ec79392017-03-31 23:04:23 +0300314bool ValidateLimitationsTraverser::validateForLoopExpr(TIntermLoop *node, int indexSymbolId)
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000315{
Zhenyao Mo550c6002014-02-26 15:40:48 -0800316 TIntermNode *expr = node->getExpression();
Yunchao He4f285442017-04-21 12:15:49 +0800317 if (expr == nullptr)
Zhenyao Mo550c6002014-02-26 15:40:48 -0800318 {
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000319 error(node->getLine(), "Missing expression", "for");
320 return false;
321 }
322
323 // for expression has one of the following forms:
324 // loop_index++
325 // loop_index--
326 // loop_index += constant_expression
327 // loop_index -= constant_expression
328 // ++loop_index
329 // --loop_index
330 // The last two forms are not specified in the spec, but I am assuming
331 // its an oversight.
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500332 TIntermUnary *unOp = expr->getAsUnaryNode();
Yunchao Hef81ce4a2017-04-24 10:49:17 +0800333 TIntermBinary *binOp = unOp ? nullptr : expr->getAsBinaryNode();
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000334
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500335 TOperator op = EOpNull;
Yunchao Hed7297bf2017-04-19 15:27:10 +0800336 TIntermSymbol *symbol = nullptr;
Yunchao He4f285442017-04-21 12:15:49 +0800337 if (unOp != nullptr)
Zhenyao Mo550c6002014-02-26 15:40:48 -0800338 {
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500339 op = unOp->getOp();
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000340 symbol = unOp->getOperand()->getAsSymbolNode();
Zhenyao Mo550c6002014-02-26 15:40:48 -0800341 }
Yunchao He4f285442017-04-21 12:15:49 +0800342 else if (binOp != nullptr)
Zhenyao Mo550c6002014-02-26 15:40:48 -0800343 {
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500344 op = binOp->getOp();
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000345 symbol = binOp->getLeft()->getAsSymbolNode();
346 }
347
348 // The operand must be loop index.
Yunchao He4f285442017-04-21 12:15:49 +0800349 if (symbol == nullptr)
Zhenyao Mo550c6002014-02-26 15:40:48 -0800350 {
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000351 error(expr->getLine(), "Invalid expression", "for");
352 return false;
353 }
Olli Etuahob6af22b2017-12-15 14:05:44 +0200354 if (symbol->uniqueId().get() != indexSymbolId)
Zhenyao Mo550c6002014-02-26 15:40:48 -0800355 {
Olli Etuaho8b5e8fd2017-12-15 14:59:15 +0200356 error(symbol->getLine(), "Expected loop index", symbol->getName().c_str());
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000357 return false;
358 }
359
360 // The operator is one of: ++ -- += -=.
Zhenyao Mo550c6002014-02-26 15:40:48 -0800361 switch (op)
362 {
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500363 case EOpPostIncrement:
364 case EOpPostDecrement:
365 case EOpPreIncrement:
366 case EOpPreDecrement:
Yunchao He4f285442017-04-21 12:15:49 +0800367 ASSERT((unOp != nullptr) && (binOp == nullptr));
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500368 break;
369 case EOpAddAssign:
370 case EOpSubAssign:
Yunchao He4f285442017-04-21 12:15:49 +0800371 ASSERT((unOp == nullptr) && (binOp != nullptr));
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500372 break;
373 default:
374 error(expr->getLine(), "Invalid operator", GetOperatorString(op));
375 return false;
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000376 }
377
378 // Loop index must be incremented/decremented with a constant.
Yunchao He4f285442017-04-21 12:15:49 +0800379 if (binOp != nullptr)
Zhenyao Mo550c6002014-02-26 15:40:48 -0800380 {
381 if (!isConstExpr(binOp->getRight()))
382 {
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500383 error(binOp->getLine(), "Loop index cannot be modified by non-constant expression",
Olli Etuaho8b5e8fd2017-12-15 14:59:15 +0200384 symbol->getName().c_str());
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000385 return false;
386 }
387 }
388
389 return true;
390}
391
Olli Etuaho9ec79392017-03-31 23:04:23 +0300392bool ValidateLimitationsTraverser::isConstExpr(TIntermNode *node)
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000393{
Olli Etuahod5610572015-12-10 19:42:09 +0200394 ASSERT(node != nullptr);
395 return node->getAsConstantUnion() != nullptr && node->getAsTyped()->getQualifier() == EvqConst;
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000396}
397
Olli Etuaho9ec79392017-03-31 23:04:23 +0300398bool ValidateLimitationsTraverser::isConstIndexExpr(TIntermNode *node)
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000399{
Yunchao He4f285442017-04-21 12:15:49 +0800400 ASSERT(node != nullptr);
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000401
Corentin Wallez1b896c62016-11-16 13:10:44 -0500402 ValidateConstIndexExpr validate(mLoopSymbolIds);
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000403 node->traverse(&validate);
404 return validate.isValid();
405}
406
Olli Etuaho9ec79392017-03-31 23:04:23 +0300407bool ValidateLimitationsTraverser::validateIndexing(TIntermBinary *node)
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000408{
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500409 ASSERT((node->getOp() == EOpIndexDirect) || (node->getOp() == EOpIndexIndirect));
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000410
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500411 bool valid = true;
Zhenyao Mo550c6002014-02-26 15:40:48 -0800412 TIntermTyped *index = node->getRight();
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000413 // The index expession must be a constant-index-expression unless
414 // the operand is a uniform in a vertex shader.
Zhenyao Mo550c6002014-02-26 15:40:48 -0800415 TIntermTyped *operand = node->getLeft();
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500416 bool skip = (mShaderType == GL_VERTEX_SHADER) && (operand->getQualifier() == EvqUniform);
Zhenyao Mo550c6002014-02-26 15:40:48 -0800417 if (!skip && !isConstIndexExpr(index))
418 {
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000419 error(index->getLine(), "Index expression must be constant", "[]");
420 valid = false;
421 }
422 return valid;
423}
424
Olli Etuaho9ec79392017-03-31 23:04:23 +0300425} // namespace anonymous
426
427bool ValidateLimitations(TIntermNode *root,
428 GLenum shaderType,
Olli Etuahoa5e693a2017-07-13 16:07:26 +0300429 TSymbolTable *symbolTable,
Olli Etuaho9ec79392017-03-31 23:04:23 +0300430 int shaderVersion,
431 TDiagnostics *diagnostics)
432{
433 ValidateLimitationsTraverser validate(shaderType, symbolTable, shaderVersion, diagnostics);
434 root->traverse(&validate);
435 return diagnostics->numErrors() == 0;
436}
437
Jamie Madill45bcc782016-11-07 13:58:48 -0500438} // namespace sh