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