blob: 8b9eef3c76f1fdd957b0d1300b8fb856aa8b56ef [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 TDiagnostics *diagnostics);
alokp@chromium.orgb59a7782010-11-24 18:38:33 +000076
Olli Etuaho9ec79392017-03-31 23:04:23 +030077 void visitSymbol(TIntermSymbol *node) override;
78 bool visitBinary(Visit, TIntermBinary *) override;
79 bool visitLoop(Visit, TIntermLoop *) override;
80
81 private:
82 void error(TSourceLoc loc, const char *reason, const char *token);
83
Olli Etuaho9ec79392017-03-31 23:04:23 +030084 bool isLoopIndex(TIntermSymbol *symbol);
85 bool validateLoopType(TIntermLoop *node);
86
87 bool validateForLoopHeader(TIntermLoop *node);
88 // If valid, return the index symbol id; Otherwise, return -1.
89 int validateForLoopInit(TIntermLoop *node);
90 bool validateForLoopCond(TIntermLoop *node, int indexSymbolId);
91 bool validateForLoopExpr(TIntermLoop *node, int indexSymbolId);
92
93 // Returns true if indexing does not exceed the minimum functionality
94 // mandated in GLSL 1.0 spec, Appendix A, Section 5.
95 bool isConstExpr(TIntermNode *node);
96 bool isConstIndexExpr(TIntermNode *node);
97 bool validateIndexing(TIntermBinary *node);
98
99 sh::GLenum mShaderType;
100 TDiagnostics *mDiagnostics;
101 std::vector<int> mLoopSymbolIds;
102};
103
104ValidateLimitationsTraverser::ValidateLimitationsTraverser(sh::GLenum shaderType,
Olli Etuahoa5e693a2017-07-13 16:07:26 +0300105 TSymbolTable *symbolTable,
Olli Etuaho9ec79392017-03-31 23:04:23 +0300106 TDiagnostics *diagnostics)
Olli Etuaho68981eb2018-01-23 17:46:12 +0200107 : TLValueTrackingTraverser(true, false, false, symbolTable),
Olli Etuaho3d0d9a42015-06-01 12:16:36 +0300108 mShaderType(shaderType),
Olli Etuaho9ec79392017-03-31 23:04:23 +0300109 mDiagnostics(diagnostics)
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000110{
Olli Etuaho77ba4082016-12-16 12:01:18 +0000111 ASSERT(diagnostics);
Olli Etuaho8a76dcc2015-12-10 20:25:12 +0200112}
113
Olli Etuaho9ec79392017-03-31 23:04:23 +0300114void ValidateLimitationsTraverser::visitSymbol(TIntermSymbol *node)
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000115{
Olli Etuaho9ec79392017-03-31 23:04:23 +0300116 if (isLoopIndex(node) && isLValueRequiredHere())
117 {
118 error(node->getLine(),
119 "Loop index cannot be statically assigned to within the body of the loop",
Olli Etuaho8b5e8fd2017-12-15 14:59:15 +0200120 node->getName().c_str());
Olli Etuaho9ec79392017-03-31 23:04:23 +0300121 }
122}
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000123
Olli Etuaho9ec79392017-03-31 23:04:23 +0300124bool ValidateLimitationsTraverser::visitBinary(Visit, TIntermBinary *node)
125{
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000126 // Check indexing.
Zhenyao Mo550c6002014-02-26 15:40:48 -0800127 switch (node->getOp())
128 {
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500129 case EOpIndexDirect:
130 case EOpIndexIndirect:
Olli Etuaho9ec79392017-03-31 23:04:23 +0300131 validateIndexing(node);
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500132 break;
133 default:
134 break;
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000135 }
136 return true;
137}
138
Olli Etuaho9ec79392017-03-31 23:04:23 +0300139bool ValidateLimitationsTraverser::visitLoop(Visit, TIntermLoop *node)
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000140{
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000141 if (!validateLoopType(node))
142 return false;
143
Zhenyao Mo550c6002014-02-26 15:40:48 -0800144 if (!validateForLoopHeader(node))
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000145 return false;
146
Zhenyao Mo550c6002014-02-26 15:40:48 -0800147 TIntermNode *body = node->getBody();
Yunchao He4f285442017-04-21 12:15:49 +0800148 if (body != nullptr)
Zhenyao Mo550c6002014-02-26 15:40:48 -0800149 {
Corentin Wallez1b896c62016-11-16 13:10:44 -0500150 mLoopSymbolIds.push_back(GetLoopSymbolId(node));
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000151 body->traverse(this);
Corentin Wallez1b896c62016-11-16 13:10:44 -0500152 mLoopSymbolIds.pop_back();
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000153 }
154
155 // The loop is fully processed - no need to visit children.
156 return false;
157}
158
Olli Etuaho9ec79392017-03-31 23:04:23 +0300159void ValidateLimitationsTraverser::error(TSourceLoc loc, const char *reason, const char *token)
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000160{
Olli Etuaho77ba4082016-12-16 12:01:18 +0000161 mDiagnostics->error(loc, reason, token);
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000162}
163
Olli Etuaho9ec79392017-03-31 23:04:23 +0300164bool ValidateLimitationsTraverser::isLoopIndex(TIntermSymbol *symbol)
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000165{
Olli Etuahob6af22b2017-12-15 14:05:44 +0200166 return std::find(mLoopSymbolIds.begin(), mLoopSymbolIds.end(), symbol->uniqueId().get()) !=
Corentin Wallez1b896c62016-11-16 13:10:44 -0500167 mLoopSymbolIds.end();
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000168}
169
Olli Etuaho9ec79392017-03-31 23:04:23 +0300170bool ValidateLimitationsTraverser::validateLoopType(TIntermLoop *node)
Zhenyao Mo550c6002014-02-26 15:40:48 -0800171{
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000172 TLoopType type = node->getType();
173 if (type == ELoopFor)
174 return true;
175
176 // Reject while and do-while loops.
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500177 error(node->getLine(), "This type of loop is not allowed", type == ELoopWhile ? "while" : "do");
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000178 return false;
179}
180
Olli Etuaho9ec79392017-03-31 23:04:23 +0300181bool ValidateLimitationsTraverser::validateForLoopHeader(TIntermLoop *node)
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000182{
183 ASSERT(node->getType() == ELoopFor);
184
185 //
186 // The for statement has the form:
187 // for ( init-declaration ; condition ; expression ) statement
188 //
Zhenyao Mo550c6002014-02-26 15:40:48 -0800189 int indexSymbolId = validateForLoopInit(node);
190 if (indexSymbolId < 0)
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000191 return false;
Zhenyao Mo550c6002014-02-26 15:40:48 -0800192 if (!validateForLoopCond(node, indexSymbolId))
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000193 return false;
Zhenyao Mo550c6002014-02-26 15:40:48 -0800194 if (!validateForLoopExpr(node, indexSymbolId))
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000195 return false;
196
197 return true;
198}
199
Olli Etuaho9ec79392017-03-31 23:04:23 +0300200int ValidateLimitationsTraverser::validateForLoopInit(TIntermLoop *node)
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000201{
Zhenyao Mo550c6002014-02-26 15:40:48 -0800202 TIntermNode *init = node->getInit();
Yunchao He4f285442017-04-21 12:15:49 +0800203 if (init == nullptr)
Zhenyao Mo550c6002014-02-26 15:40:48 -0800204 {
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000205 error(node->getLine(), "Missing init declaration", "for");
Zhenyao Mo550c6002014-02-26 15:40:48 -0800206 return -1;
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000207 }
208
209 //
210 // init-declaration has the form:
211 // type-specifier identifier = constant-expression
212 //
Olli Etuaho13389b62016-10-16 11:48:18 +0100213 TIntermDeclaration *decl = init->getAsDeclarationNode();
214 if (decl == nullptr)
Zhenyao Mo550c6002014-02-26 15:40:48 -0800215 {
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000216 error(init->getLine(), "Invalid init declaration", "for");
Zhenyao Mo550c6002014-02-26 15:40:48 -0800217 return -1;
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000218 }
219 // To keep things simple do not allow declaration list.
Zhenyao Moe40d1e92014-07-16 17:40:36 -0700220 TIntermSequence *declSeq = decl->getSequence();
221 if (declSeq->size() != 1)
Zhenyao Mo550c6002014-02-26 15:40:48 -0800222 {
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000223 error(decl->getLine(), "Invalid init declaration", "for");
Zhenyao Mo550c6002014-02-26 15:40:48 -0800224 return -1;
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000225 }
Zhenyao Moe40d1e92014-07-16 17:40:36 -0700226 TIntermBinary *declInit = (*declSeq)[0]->getAsBinaryNode();
Yunchao He4f285442017-04-21 12:15:49 +0800227 if ((declInit == nullptr) || (declInit->getOp() != EOpInitialize))
Zhenyao Mo550c6002014-02-26 15:40:48 -0800228 {
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000229 error(decl->getLine(), "Invalid init declaration", "for");
Zhenyao Mo550c6002014-02-26 15:40:48 -0800230 return -1;
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000231 }
Zhenyao Mo550c6002014-02-26 15:40:48 -0800232 TIntermSymbol *symbol = declInit->getLeft()->getAsSymbolNode();
Yunchao He4f285442017-04-21 12:15:49 +0800233 if (symbol == nullptr)
Zhenyao Mo550c6002014-02-26 15:40:48 -0800234 {
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000235 error(declInit->getLine(), "Invalid init declaration", "for");
Zhenyao Mo550c6002014-02-26 15:40:48 -0800236 return -1;
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000237 }
238 // The loop index has type int or float.
239 TBasicType type = symbol->getBasicType();
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500240 if ((type != EbtInt) && (type != EbtUInt) && (type != EbtFloat))
241 {
242 error(symbol->getLine(), "Invalid type for loop index", getBasicString(type));
Zhenyao Mo550c6002014-02-26 15:40:48 -0800243 return -1;
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000244 }
245 // The loop index is initialized with constant expression.
Zhenyao Mo550c6002014-02-26 15:40:48 -0800246 if (!isConstExpr(declInit->getRight()))
247 {
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500248 error(declInit->getLine(), "Loop index cannot be initialized with non-constant expression",
Olli Etuaho8b5e8fd2017-12-15 14:59:15 +0200249 symbol->getName().c_str());
Zhenyao Mo550c6002014-02-26 15:40:48 -0800250 return -1;
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000251 }
252
Olli Etuahob6af22b2017-12-15 14:05:44 +0200253 return symbol->uniqueId().get();
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000254}
255
Olli Etuaho9ec79392017-03-31 23:04:23 +0300256bool ValidateLimitationsTraverser::validateForLoopCond(TIntermLoop *node, int indexSymbolId)
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000257{
Zhenyao Mo550c6002014-02-26 15:40:48 -0800258 TIntermNode *cond = node->getCondition();
Yunchao He4f285442017-04-21 12:15:49 +0800259 if (cond == nullptr)
Zhenyao Mo550c6002014-02-26 15:40:48 -0800260 {
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000261 error(node->getLine(), "Missing condition", "for");
262 return false;
263 }
264 //
265 // condition has the form:
266 // loop_index relational_operator constant_expression
267 //
Zhenyao Mo550c6002014-02-26 15:40:48 -0800268 TIntermBinary *binOp = cond->getAsBinaryNode();
Yunchao He4f285442017-04-21 12:15:49 +0800269 if (binOp == nullptr)
Zhenyao Mo550c6002014-02-26 15:40:48 -0800270 {
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000271 error(node->getLine(), "Invalid condition", "for");
272 return false;
273 }
274 // Loop index should be to the left of relational operator.
Zhenyao Mo550c6002014-02-26 15:40:48 -0800275 TIntermSymbol *symbol = binOp->getLeft()->getAsSymbolNode();
Yunchao He4f285442017-04-21 12:15:49 +0800276 if (symbol == nullptr)
Zhenyao Mo550c6002014-02-26 15:40:48 -0800277 {
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000278 error(binOp->getLine(), "Invalid condition", "for");
279 return false;
280 }
Olli Etuahob6af22b2017-12-15 14:05:44 +0200281 if (symbol->uniqueId().get() != indexSymbolId)
Zhenyao Mo550c6002014-02-26 15:40:48 -0800282 {
Olli Etuaho8b5e8fd2017-12-15 14:59:15 +0200283 error(symbol->getLine(), "Expected loop index", symbol->getName().c_str());
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000284 return false;
285 }
286 // Relational operator is one of: > >= < <= == or !=.
Zhenyao Mo550c6002014-02-26 15:40:48 -0800287 switch (binOp->getOp())
288 {
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500289 case EOpEqual:
290 case EOpNotEqual:
291 case EOpLessThan:
292 case EOpGreaterThan:
293 case EOpLessThanEqual:
294 case EOpGreaterThanEqual:
295 break;
296 default:
297 error(binOp->getLine(), "Invalid relational operator",
298 GetOperatorString(binOp->getOp()));
299 break;
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000300 }
301 // Loop index must be compared with a constant.
Zhenyao Mo550c6002014-02-26 15:40:48 -0800302 if (!isConstExpr(binOp->getRight()))
303 {
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500304 error(binOp->getLine(), "Loop index cannot be compared with non-constant expression",
Olli Etuaho8b5e8fd2017-12-15 14:59:15 +0200305 symbol->getName().c_str());
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000306 return false;
307 }
308
309 return true;
310}
311
Olli Etuaho9ec79392017-03-31 23:04:23 +0300312bool ValidateLimitationsTraverser::validateForLoopExpr(TIntermLoop *node, int indexSymbolId)
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000313{
Zhenyao Mo550c6002014-02-26 15:40:48 -0800314 TIntermNode *expr = node->getExpression();
Yunchao He4f285442017-04-21 12:15:49 +0800315 if (expr == nullptr)
Zhenyao Mo550c6002014-02-26 15:40:48 -0800316 {
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000317 error(node->getLine(), "Missing expression", "for");
318 return false;
319 }
320
321 // for expression has one of the following forms:
322 // loop_index++
323 // loop_index--
324 // loop_index += constant_expression
325 // loop_index -= constant_expression
326 // ++loop_index
327 // --loop_index
328 // The last two forms are not specified in the spec, but I am assuming
329 // its an oversight.
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500330 TIntermUnary *unOp = expr->getAsUnaryNode();
Yunchao Hef81ce4a2017-04-24 10:49:17 +0800331 TIntermBinary *binOp = unOp ? nullptr : expr->getAsBinaryNode();
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000332
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500333 TOperator op = EOpNull;
Yunchao Hed7297bf2017-04-19 15:27:10 +0800334 TIntermSymbol *symbol = nullptr;
Yunchao He4f285442017-04-21 12:15:49 +0800335 if (unOp != nullptr)
Zhenyao Mo550c6002014-02-26 15:40:48 -0800336 {
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500337 op = unOp->getOp();
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000338 symbol = unOp->getOperand()->getAsSymbolNode();
Zhenyao Mo550c6002014-02-26 15:40:48 -0800339 }
Yunchao He4f285442017-04-21 12:15:49 +0800340 else if (binOp != nullptr)
Zhenyao Mo550c6002014-02-26 15:40:48 -0800341 {
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500342 op = binOp->getOp();
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000343 symbol = binOp->getLeft()->getAsSymbolNode();
344 }
345
346 // The operand must be loop index.
Yunchao He4f285442017-04-21 12:15:49 +0800347 if (symbol == nullptr)
Zhenyao Mo550c6002014-02-26 15:40:48 -0800348 {
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000349 error(expr->getLine(), "Invalid expression", "for");
350 return false;
351 }
Olli Etuahob6af22b2017-12-15 14:05:44 +0200352 if (symbol->uniqueId().get() != indexSymbolId)
Zhenyao Mo550c6002014-02-26 15:40:48 -0800353 {
Olli Etuaho8b5e8fd2017-12-15 14:59:15 +0200354 error(symbol->getLine(), "Expected loop index", symbol->getName().c_str());
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000355 return false;
356 }
357
358 // The operator is one of: ++ -- += -=.
Zhenyao Mo550c6002014-02-26 15:40:48 -0800359 switch (op)
360 {
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500361 case EOpPostIncrement:
362 case EOpPostDecrement:
363 case EOpPreIncrement:
364 case EOpPreDecrement:
Yunchao He4f285442017-04-21 12:15:49 +0800365 ASSERT((unOp != nullptr) && (binOp == nullptr));
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500366 break;
367 case EOpAddAssign:
368 case EOpSubAssign:
Yunchao He4f285442017-04-21 12:15:49 +0800369 ASSERT((unOp == nullptr) && (binOp != nullptr));
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500370 break;
371 default:
372 error(expr->getLine(), "Invalid operator", GetOperatorString(op));
373 return false;
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000374 }
375
376 // Loop index must be incremented/decremented with a constant.
Yunchao He4f285442017-04-21 12:15:49 +0800377 if (binOp != nullptr)
Zhenyao Mo550c6002014-02-26 15:40:48 -0800378 {
379 if (!isConstExpr(binOp->getRight()))
380 {
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500381 error(binOp->getLine(), "Loop index cannot be modified by non-constant expression",
Olli Etuaho8b5e8fd2017-12-15 14:59:15 +0200382 symbol->getName().c_str());
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000383 return false;
384 }
385 }
386
387 return true;
388}
389
Olli Etuaho9ec79392017-03-31 23:04:23 +0300390bool ValidateLimitationsTraverser::isConstExpr(TIntermNode *node)
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000391{
Olli Etuahod5610572015-12-10 19:42:09 +0200392 ASSERT(node != nullptr);
393 return node->getAsConstantUnion() != nullptr && node->getAsTyped()->getQualifier() == EvqConst;
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000394}
395
Olli Etuaho9ec79392017-03-31 23:04:23 +0300396bool ValidateLimitationsTraverser::isConstIndexExpr(TIntermNode *node)
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000397{
Yunchao He4f285442017-04-21 12:15:49 +0800398 ASSERT(node != nullptr);
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000399
Corentin Wallez1b896c62016-11-16 13:10:44 -0500400 ValidateConstIndexExpr validate(mLoopSymbolIds);
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000401 node->traverse(&validate);
402 return validate.isValid();
403}
404
Olli Etuaho9ec79392017-03-31 23:04:23 +0300405bool ValidateLimitationsTraverser::validateIndexing(TIntermBinary *node)
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000406{
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500407 ASSERT((node->getOp() == EOpIndexDirect) || (node->getOp() == EOpIndexIndirect));
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000408
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500409 bool valid = true;
Zhenyao Mo550c6002014-02-26 15:40:48 -0800410 TIntermTyped *index = node->getRight();
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000411 // The index expession must be a constant-index-expression unless
412 // the operand is a uniform in a vertex shader.
Zhenyao Mo550c6002014-02-26 15:40:48 -0800413 TIntermTyped *operand = node->getLeft();
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500414 bool skip = (mShaderType == GL_VERTEX_SHADER) && (operand->getQualifier() == EvqUniform);
Zhenyao Mo550c6002014-02-26 15:40:48 -0800415 if (!skip && !isConstIndexExpr(index))
416 {
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000417 error(index->getLine(), "Index expression must be constant", "[]");
418 valid = false;
419 }
420 return valid;
421}
422
Olli Etuaho9ec79392017-03-31 23:04:23 +0300423} // namespace anonymous
424
425bool ValidateLimitations(TIntermNode *root,
426 GLenum shaderType,
Olli Etuahoa5e693a2017-07-13 16:07:26 +0300427 TSymbolTable *symbolTable,
Olli Etuaho9ec79392017-03-31 23:04:23 +0300428 TDiagnostics *diagnostics)
429{
Olli Etuaho68981eb2018-01-23 17:46:12 +0200430 ValidateLimitationsTraverser validate(shaderType, symbolTable, diagnostics);
Olli Etuaho9ec79392017-03-31 23:04:23 +0300431 root->traverse(&validate);
432 return diagnostics->numErrors() == 0;
433}
434
Jamie Madill45bcc782016-11-07 13:58:48 -0500435} // namespace sh