blob: 941f79ae513cdb291ffa8db2534992bf7e4a8d65 [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
28 return symbol->getId();
29}
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(),
58 symbol->getId()) != mLoopSymbolIds.end();
59 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
85 bool withinLoopBody() const;
86 bool isLoopIndex(TIntermSymbol *symbol);
87 bool validateLoopType(TIntermLoop *node);
88
89 bool validateForLoopHeader(TIntermLoop *node);
90 // If valid, return the index symbol id; Otherwise, return -1.
91 int validateForLoopInit(TIntermLoop *node);
92 bool validateForLoopCond(TIntermLoop *node, int indexSymbolId);
93 bool validateForLoopExpr(TIntermLoop *node, int indexSymbolId);
94
95 // Returns true if indexing does not exceed the minimum functionality
96 // mandated in GLSL 1.0 spec, Appendix A, Section 5.
97 bool isConstExpr(TIntermNode *node);
98 bool isConstIndexExpr(TIntermNode *node);
99 bool validateIndexing(TIntermBinary *node);
100
101 sh::GLenum mShaderType;
102 TDiagnostics *mDiagnostics;
103 std::vector<int> mLoopSymbolIds;
104};
105
106ValidateLimitationsTraverser::ValidateLimitationsTraverser(sh::GLenum shaderType,
Olli Etuahoa5e693a2017-07-13 16:07:26 +0300107 TSymbolTable *symbolTable,
Olli Etuaho9ec79392017-03-31 23:04:23 +0300108 int shaderVersion,
109 TDiagnostics *diagnostics)
110 : TLValueTrackingTraverser(true, false, false, symbolTable, shaderVersion),
Olli Etuaho3d0d9a42015-06-01 12:16:36 +0300111 mShaderType(shaderType),
Olli Etuaho9ec79392017-03-31 23:04:23 +0300112 mDiagnostics(diagnostics)
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000113{
Olli Etuaho77ba4082016-12-16 12:01:18 +0000114 ASSERT(diagnostics);
Olli Etuaho8a76dcc2015-12-10 20:25:12 +0200115}
116
Olli Etuaho9ec79392017-03-31 23:04:23 +0300117void ValidateLimitationsTraverser::visitSymbol(TIntermSymbol *node)
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000118{
Olli Etuaho9ec79392017-03-31 23:04:23 +0300119 if (isLoopIndex(node) && isLValueRequiredHere())
120 {
121 error(node->getLine(),
122 "Loop index cannot be statically assigned to within the body of the loop",
123 node->getSymbol().c_str());
124 }
125}
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000126
Olli Etuaho9ec79392017-03-31 23:04:23 +0300127bool ValidateLimitationsTraverser::visitBinary(Visit, TIntermBinary *node)
128{
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000129 // Check indexing.
Zhenyao Mo550c6002014-02-26 15:40:48 -0800130 switch (node->getOp())
131 {
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500132 case EOpIndexDirect:
133 case EOpIndexIndirect:
Olli Etuaho9ec79392017-03-31 23:04:23 +0300134 validateIndexing(node);
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500135 break;
136 default:
137 break;
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000138 }
139 return true;
140}
141
Olli Etuaho9ec79392017-03-31 23:04:23 +0300142bool ValidateLimitationsTraverser::visitLoop(Visit, TIntermLoop *node)
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000143{
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000144 if (!validateLoopType(node))
145 return false;
146
Zhenyao Mo550c6002014-02-26 15:40:48 -0800147 if (!validateForLoopHeader(node))
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000148 return false;
149
Zhenyao Mo550c6002014-02-26 15:40:48 -0800150 TIntermNode *body = node->getBody();
Yunchao He4f285442017-04-21 12:15:49 +0800151 if (body != nullptr)
Zhenyao Mo550c6002014-02-26 15:40:48 -0800152 {
Corentin Wallez1b896c62016-11-16 13:10:44 -0500153 mLoopSymbolIds.push_back(GetLoopSymbolId(node));
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000154 body->traverse(this);
Corentin Wallez1b896c62016-11-16 13:10:44 -0500155 mLoopSymbolIds.pop_back();
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000156 }
157
158 // The loop is fully processed - no need to visit children.
159 return false;
160}
161
Olli Etuaho9ec79392017-03-31 23:04:23 +0300162void ValidateLimitationsTraverser::error(TSourceLoc loc, const char *reason, const char *token)
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000163{
Olli Etuaho77ba4082016-12-16 12:01:18 +0000164 mDiagnostics->error(loc, reason, token);
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000165}
166
Olli Etuaho9ec79392017-03-31 23:04:23 +0300167bool ValidateLimitationsTraverser::withinLoopBody() const
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000168{
Corentin Wallez1b896c62016-11-16 13:10:44 -0500169 return !mLoopSymbolIds.empty();
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000170}
171
Olli Etuaho9ec79392017-03-31 23:04:23 +0300172bool ValidateLimitationsTraverser::isLoopIndex(TIntermSymbol *symbol)
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000173{
Corentin Wallez1b896c62016-11-16 13:10:44 -0500174 return std::find(mLoopSymbolIds.begin(), mLoopSymbolIds.end(), symbol->getId()) !=
175 mLoopSymbolIds.end();
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000176}
177
Olli Etuaho9ec79392017-03-31 23:04:23 +0300178bool ValidateLimitationsTraverser::validateLoopType(TIntermLoop *node)
Zhenyao Mo550c6002014-02-26 15:40:48 -0800179{
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000180 TLoopType type = node->getType();
181 if (type == ELoopFor)
182 return true;
183
184 // Reject while and do-while loops.
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500185 error(node->getLine(), "This type of loop is not allowed", type == ELoopWhile ? "while" : "do");
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000186 return false;
187}
188
Olli Etuaho9ec79392017-03-31 23:04:23 +0300189bool ValidateLimitationsTraverser::validateForLoopHeader(TIntermLoop *node)
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000190{
191 ASSERT(node->getType() == ELoopFor);
192
193 //
194 // The for statement has the form:
195 // for ( init-declaration ; condition ; expression ) statement
196 //
Zhenyao Mo550c6002014-02-26 15:40:48 -0800197 int indexSymbolId = validateForLoopInit(node);
198 if (indexSymbolId < 0)
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000199 return false;
Zhenyao Mo550c6002014-02-26 15:40:48 -0800200 if (!validateForLoopCond(node, indexSymbolId))
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000201 return false;
Zhenyao Mo550c6002014-02-26 15:40:48 -0800202 if (!validateForLoopExpr(node, indexSymbolId))
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000203 return false;
204
205 return true;
206}
207
Olli Etuaho9ec79392017-03-31 23:04:23 +0300208int ValidateLimitationsTraverser::validateForLoopInit(TIntermLoop *node)
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000209{
Zhenyao Mo550c6002014-02-26 15:40:48 -0800210 TIntermNode *init = node->getInit();
Yunchao He4f285442017-04-21 12:15:49 +0800211 if (init == nullptr)
Zhenyao Mo550c6002014-02-26 15:40:48 -0800212 {
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000213 error(node->getLine(), "Missing init declaration", "for");
Zhenyao Mo550c6002014-02-26 15:40:48 -0800214 return -1;
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000215 }
216
217 //
218 // init-declaration has the form:
219 // type-specifier identifier = constant-expression
220 //
Olli Etuaho13389b62016-10-16 11:48:18 +0100221 TIntermDeclaration *decl = init->getAsDeclarationNode();
222 if (decl == nullptr)
Zhenyao Mo550c6002014-02-26 15:40:48 -0800223 {
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000224 error(init->getLine(), "Invalid init declaration", "for");
Zhenyao Mo550c6002014-02-26 15:40:48 -0800225 return -1;
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000226 }
227 // To keep things simple do not allow declaration list.
Zhenyao Moe40d1e92014-07-16 17:40:36 -0700228 TIntermSequence *declSeq = decl->getSequence();
229 if (declSeq->size() != 1)
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 Moe40d1e92014-07-16 17:40:36 -0700234 TIntermBinary *declInit = (*declSeq)[0]->getAsBinaryNode();
Yunchao He4f285442017-04-21 12:15:49 +0800235 if ((declInit == nullptr) || (declInit->getOp() != EOpInitialize))
Zhenyao Mo550c6002014-02-26 15:40:48 -0800236 {
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000237 error(decl->getLine(), "Invalid init declaration", "for");
Zhenyao Mo550c6002014-02-26 15:40:48 -0800238 return -1;
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000239 }
Zhenyao Mo550c6002014-02-26 15:40:48 -0800240 TIntermSymbol *symbol = declInit->getLeft()->getAsSymbolNode();
Yunchao He4f285442017-04-21 12:15:49 +0800241 if (symbol == nullptr)
Zhenyao Mo550c6002014-02-26 15:40:48 -0800242 {
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000243 error(declInit->getLine(), "Invalid init declaration", "for");
Zhenyao Mo550c6002014-02-26 15:40:48 -0800244 return -1;
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000245 }
246 // The loop index has type int or float.
247 TBasicType type = symbol->getBasicType();
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500248 if ((type != EbtInt) && (type != EbtUInt) && (type != EbtFloat))
249 {
250 error(symbol->getLine(), "Invalid type for loop index", getBasicString(type));
Zhenyao Mo550c6002014-02-26 15:40:48 -0800251 return -1;
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000252 }
253 // The loop index is initialized with constant expression.
Zhenyao Mo550c6002014-02-26 15:40:48 -0800254 if (!isConstExpr(declInit->getRight()))
255 {
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500256 error(declInit->getLine(), "Loop index cannot be initialized with non-constant expression",
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000257 symbol->getSymbol().c_str());
Zhenyao Mo550c6002014-02-26 15:40:48 -0800258 return -1;
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000259 }
260
Zhenyao Mo550c6002014-02-26 15:40:48 -0800261 return symbol->getId();
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000262}
263
Olli Etuaho9ec79392017-03-31 23:04:23 +0300264bool ValidateLimitationsTraverser::validateForLoopCond(TIntermLoop *node, int indexSymbolId)
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000265{
Zhenyao Mo550c6002014-02-26 15:40:48 -0800266 TIntermNode *cond = node->getCondition();
Yunchao He4f285442017-04-21 12:15:49 +0800267 if (cond == nullptr)
Zhenyao Mo550c6002014-02-26 15:40:48 -0800268 {
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000269 error(node->getLine(), "Missing condition", "for");
270 return false;
271 }
272 //
273 // condition has the form:
274 // loop_index relational_operator constant_expression
275 //
Zhenyao Mo550c6002014-02-26 15:40:48 -0800276 TIntermBinary *binOp = cond->getAsBinaryNode();
Yunchao He4f285442017-04-21 12:15:49 +0800277 if (binOp == nullptr)
Zhenyao Mo550c6002014-02-26 15:40:48 -0800278 {
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000279 error(node->getLine(), "Invalid condition", "for");
280 return false;
281 }
282 // Loop index should be to the left of relational operator.
Zhenyao Mo550c6002014-02-26 15:40:48 -0800283 TIntermSymbol *symbol = binOp->getLeft()->getAsSymbolNode();
Yunchao He4f285442017-04-21 12:15:49 +0800284 if (symbol == nullptr)
Zhenyao Mo550c6002014-02-26 15:40:48 -0800285 {
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000286 error(binOp->getLine(), "Invalid condition", "for");
287 return false;
288 }
Zhenyao Mo550c6002014-02-26 15:40:48 -0800289 if (symbol->getId() != indexSymbolId)
290 {
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500291 error(symbol->getLine(), "Expected loop index", symbol->getSymbol().c_str());
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000292 return false;
293 }
294 // Relational operator is one of: > >= < <= == or !=.
Zhenyao Mo550c6002014-02-26 15:40:48 -0800295 switch (binOp->getOp())
296 {
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500297 case EOpEqual:
298 case EOpNotEqual:
299 case EOpLessThan:
300 case EOpGreaterThan:
301 case EOpLessThanEqual:
302 case EOpGreaterThanEqual:
303 break;
304 default:
305 error(binOp->getLine(), "Invalid relational operator",
306 GetOperatorString(binOp->getOp()));
307 break;
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000308 }
309 // Loop index must be compared with a constant.
Zhenyao Mo550c6002014-02-26 15:40:48 -0800310 if (!isConstExpr(binOp->getRight()))
311 {
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500312 error(binOp->getLine(), "Loop index cannot be compared with non-constant expression",
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000313 symbol->getSymbol().c_str());
314 return false;
315 }
316
317 return true;
318}
319
Olli Etuaho9ec79392017-03-31 23:04:23 +0300320bool ValidateLimitationsTraverser::validateForLoopExpr(TIntermLoop *node, int indexSymbolId)
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000321{
Zhenyao Mo550c6002014-02-26 15:40:48 -0800322 TIntermNode *expr = node->getExpression();
Yunchao He4f285442017-04-21 12:15:49 +0800323 if (expr == nullptr)
Zhenyao Mo550c6002014-02-26 15:40:48 -0800324 {
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000325 error(node->getLine(), "Missing expression", "for");
326 return false;
327 }
328
329 // for expression has one of the following forms:
330 // loop_index++
331 // loop_index--
332 // loop_index += constant_expression
333 // loop_index -= constant_expression
334 // ++loop_index
335 // --loop_index
336 // The last two forms are not specified in the spec, but I am assuming
337 // its an oversight.
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500338 TIntermUnary *unOp = expr->getAsUnaryNode();
Yunchao Hef81ce4a2017-04-24 10:49:17 +0800339 TIntermBinary *binOp = unOp ? nullptr : expr->getAsBinaryNode();
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000340
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500341 TOperator op = EOpNull;
Yunchao Hed7297bf2017-04-19 15:27:10 +0800342 TIntermSymbol *symbol = nullptr;
Yunchao He4f285442017-04-21 12:15:49 +0800343 if (unOp != nullptr)
Zhenyao Mo550c6002014-02-26 15:40:48 -0800344 {
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500345 op = unOp->getOp();
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000346 symbol = unOp->getOperand()->getAsSymbolNode();
Zhenyao Mo550c6002014-02-26 15:40:48 -0800347 }
Yunchao He4f285442017-04-21 12:15:49 +0800348 else if (binOp != nullptr)
Zhenyao Mo550c6002014-02-26 15:40:48 -0800349 {
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500350 op = binOp->getOp();
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000351 symbol = binOp->getLeft()->getAsSymbolNode();
352 }
353
354 // The operand must be loop index.
Yunchao He4f285442017-04-21 12:15:49 +0800355 if (symbol == nullptr)
Zhenyao Mo550c6002014-02-26 15:40:48 -0800356 {
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000357 error(expr->getLine(), "Invalid expression", "for");
358 return false;
359 }
Zhenyao Mo550c6002014-02-26 15:40:48 -0800360 if (symbol->getId() != indexSymbolId)
361 {
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500362 error(symbol->getLine(), "Expected loop index", symbol->getSymbol().c_str());
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000363 return false;
364 }
365
366 // The operator is one of: ++ -- += -=.
Zhenyao Mo550c6002014-02-26 15:40:48 -0800367 switch (op)
368 {
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500369 case EOpPostIncrement:
370 case EOpPostDecrement:
371 case EOpPreIncrement:
372 case EOpPreDecrement:
Yunchao He4f285442017-04-21 12:15:49 +0800373 ASSERT((unOp != nullptr) && (binOp == nullptr));
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500374 break;
375 case EOpAddAssign:
376 case EOpSubAssign:
Yunchao He4f285442017-04-21 12:15:49 +0800377 ASSERT((unOp == nullptr) && (binOp != nullptr));
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500378 break;
379 default:
380 error(expr->getLine(), "Invalid operator", GetOperatorString(op));
381 return false;
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000382 }
383
384 // Loop index must be incremented/decremented with a constant.
Yunchao He4f285442017-04-21 12:15:49 +0800385 if (binOp != nullptr)
Zhenyao Mo550c6002014-02-26 15:40:48 -0800386 {
387 if (!isConstExpr(binOp->getRight()))
388 {
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500389 error(binOp->getLine(), "Loop index cannot be modified by non-constant expression",
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000390 symbol->getSymbol().c_str());
391 return false;
392 }
393 }
394
395 return true;
396}
397
Olli Etuaho9ec79392017-03-31 23:04:23 +0300398bool ValidateLimitationsTraverser::isConstExpr(TIntermNode *node)
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000399{
Olli Etuahod5610572015-12-10 19:42:09 +0200400 ASSERT(node != nullptr);
401 return node->getAsConstantUnion() != nullptr && node->getAsTyped()->getQualifier() == EvqConst;
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000402}
403
Olli Etuaho9ec79392017-03-31 23:04:23 +0300404bool ValidateLimitationsTraverser::isConstIndexExpr(TIntermNode *node)
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000405{
Yunchao He4f285442017-04-21 12:15:49 +0800406 ASSERT(node != nullptr);
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000407
Corentin Wallez1b896c62016-11-16 13:10:44 -0500408 ValidateConstIndexExpr validate(mLoopSymbolIds);
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000409 node->traverse(&validate);
410 return validate.isValid();
411}
412
Olli Etuaho9ec79392017-03-31 23:04:23 +0300413bool ValidateLimitationsTraverser::validateIndexing(TIntermBinary *node)
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000414{
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500415 ASSERT((node->getOp() == EOpIndexDirect) || (node->getOp() == EOpIndexIndirect));
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000416
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500417 bool valid = true;
Zhenyao Mo550c6002014-02-26 15:40:48 -0800418 TIntermTyped *index = node->getRight();
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000419 // The index expession must be a constant-index-expression unless
420 // the operand is a uniform in a vertex shader.
Zhenyao Mo550c6002014-02-26 15:40:48 -0800421 TIntermTyped *operand = node->getLeft();
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500422 bool skip = (mShaderType == GL_VERTEX_SHADER) && (operand->getQualifier() == EvqUniform);
Zhenyao Mo550c6002014-02-26 15:40:48 -0800423 if (!skip && !isConstIndexExpr(index))
424 {
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000425 error(index->getLine(), "Index expression must be constant", "[]");
426 valid = false;
427 }
428 return valid;
429}
430
Olli Etuaho9ec79392017-03-31 23:04:23 +0300431} // namespace anonymous
432
433bool ValidateLimitations(TIntermNode *root,
434 GLenum shaderType,
Olli Etuahoa5e693a2017-07-13 16:07:26 +0300435 TSymbolTable *symbolTable,
Olli Etuaho9ec79392017-03-31 23:04:23 +0300436 int shaderVersion,
437 TDiagnostics *diagnostics)
438{
439 ValidateLimitationsTraverser validate(shaderType, symbolTable, shaderVersion, diagnostics);
440 root->traverse(&validate);
441 return diagnostics->numErrors() == 0;
442}
443
Jamie Madill45bcc782016-11-07 13:58:48 -0500444} // namespace sh