blob: 767d0bf38487a178048902e6fff8187636a27db4 [file] [log] [blame]
alokp@chromium.orgb59a7782010-11-24 18:38:33 +00001//
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
11namespace {
12bool 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
zmo@google.com0b8d4eb2011-04-04 19:17:11 +000020void MarkLoopForUnroll(const TIntermSymbol* symbol, TLoopStack& stack) {
21 for (TLoopStack::iterator i = stack.begin(); i != stack.end(); ++i) {
22 if (i->index.id == symbol->getId()) {
23 ASSERT(i->loop != NULL);
24 i->loop->setUnrollFlag(true);
25 return;
26 }
27 }
28 UNREACHABLE();
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
40class ValidateConstIndexExpr : public TIntermTraverser {
41public:
42 ValidateConstIndexExpr(const TLoopStack& stack)
43 : mValid(true), mLoopStack(stack) {}
44
45 // Returns true if the parsed node represents a constant index expression.
46 bool isValid() const { return mValid; }
47
48 virtual void visitSymbol(TIntermSymbol* symbol) {
49 // Only constants and loop indices are allowed in a
50 // constant index expression.
51 if (mValid) {
52 mValid = (symbol->getQualifier() == EvqConst) ||
53 IsLoopIndex(symbol, mLoopStack);
54 }
55 }
alokp@chromium.orgb59a7782010-11-24 18:38:33 +000056
57private:
58 bool mValid;
59 const TLoopStack& mLoopStack;
60};
zmo@google.com0b8d4eb2011-04-04 19:17:11 +000061
62// Traverses a node to check if it uses a loop index.
63// If an int loop index is used in its body as a sampler array index,
64// mark the loop for unroll.
65class ValidateLoopIndexExpr : public TIntermTraverser {
66public:
67 ValidateLoopIndexExpr(TLoopStack& stack)
68 : mUsesFloatLoopIndex(false),
69 mUsesIntLoopIndex(false),
70 mLoopStack(stack) {}
71
72 bool usesFloatLoopIndex() const { return mUsesFloatLoopIndex; }
73 bool usesIntLoopIndex() const { return mUsesIntLoopIndex; }
74
75 virtual void visitSymbol(TIntermSymbol* symbol) {
76 if (IsLoopIndex(symbol, mLoopStack)) {
77 switch (symbol->getBasicType()) {
78 case EbtFloat:
79 mUsesFloatLoopIndex = true;
80 break;
81 case EbtInt:
82 mUsesIntLoopIndex = true;
83 MarkLoopForUnroll(symbol, mLoopStack);
84 break;
85 default:
86 UNREACHABLE();
87 }
88 }
89 }
zmo@google.com0b8d4eb2011-04-04 19:17:11 +000090
91private:
92 bool mUsesFloatLoopIndex;
93 bool mUsesIntLoopIndex;
94 TLoopStack& mLoopStack;
95};
alokp@chromium.orgb59a7782010-11-24 18:38:33 +000096} // namespace
97
98ValidateLimitations::ValidateLimitations(ShShaderType shaderType,
99 TInfoSinkBase& sink)
100 : mShaderType(shaderType),
101 mSink(sink),
102 mNumErrors(0)
103{
104}
105
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000106bool ValidateLimitations::visitBinary(Visit, TIntermBinary* node)
107{
108 // Check if loop index is modified in the loop body.
109 validateOperation(node, node->getLeft());
110
111 // Check indexing.
112 switch (node->getOp()) {
113 case EOpIndexDirect:
zmo@google.com0b8d4eb2011-04-04 19:17:11 +0000114 validateIndexing(node);
115 break;
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000116 case EOpIndexIndirect:
zmo@google.com0b8d4eb2011-04-04 19:17:11 +0000117#if defined(__APPLE__)
118 // Loop unrolling is a work-around for a Mac Cg compiler bug where it
119 // crashes when a sampler array's index is also the loop index.
120 // Once Apple fixes this bug, we should remove the code in this CL.
121 // See http://codereview.appspot.com/4331048/.
122 if ((node->getLeft() != NULL) && (node->getRight() != NULL) &&
123 (node->getLeft()->getAsSymbolNode())) {
124 TIntermSymbol* symbol = node->getLeft()->getAsSymbolNode();
125 if (IsSampler(symbol->getBasicType()) && symbol->isArray()) {
126 ValidateLoopIndexExpr validate(mLoopStack);
127 node->getRight()->traverse(&validate);
128 if (validate.usesFloatLoopIndex()) {
129 error(node->getLine(),
130 "sampler array index is float loop index",
131 "for");
132 }
133 }
134 }
135#endif
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000136 validateIndexing(node);
137 break;
138 default: break;
139 }
140 return true;
141}
142
143bool ValidateLimitations::visitUnary(Visit, TIntermUnary* node)
144{
145 // Check if loop index is modified in the loop body.
146 validateOperation(node, node->getOperand());
147
148 return true;
149}
150
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000151bool ValidateLimitations::visitAggregate(Visit, TIntermAggregate* node)
152{
153 switch (node->getOp()) {
154 case EOpFunctionCall:
155 validateFunctionCall(node);
156 break;
157 default:
158 break;
159 }
160 return true;
161}
162
163bool ValidateLimitations::visitLoop(Visit, TIntermLoop* node)
164{
165 if (!validateLoopType(node))
166 return false;
167
168 TLoopInfo info;
169 memset(&info, 0, sizeof(TLoopInfo));
zmo@google.com0b8d4eb2011-04-04 19:17:11 +0000170 info.loop = node;
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000171 if (!validateForLoopHeader(node, &info))
172 return false;
173
174 TIntermNode* body = node->getBody();
175 if (body != NULL) {
176 mLoopStack.push_back(info);
177 body->traverse(this);
178 mLoopStack.pop_back();
179 }
180
181 // The loop is fully processed - no need to visit children.
182 return false;
183}
184
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000185void ValidateLimitations::error(TSourceLoc loc,
186 const char *reason, const char* token)
187{
188 mSink.prefix(EPrefixError);
189 mSink.location(loc);
190 mSink << "'" << token << "' : " << reason << "\n";
191 ++mNumErrors;
192}
193
194bool ValidateLimitations::withinLoopBody() const
195{
196 return !mLoopStack.empty();
197}
198
199bool ValidateLimitations::isLoopIndex(const TIntermSymbol* symbol) const
200{
201 return IsLoopIndex(symbol, mLoopStack);
202}
203
204bool ValidateLimitations::validateLoopType(TIntermLoop* node) {
205 TLoopType type = node->getType();
206 if (type == ELoopFor)
207 return true;
208
209 // Reject while and do-while loops.
210 error(node->getLine(),
211 "This type of loop is not allowed",
212 type == ELoopWhile ? "while" : "do");
213 return false;
214}
215
216bool ValidateLimitations::validateForLoopHeader(TIntermLoop* node,
217 TLoopInfo* info)
218{
219 ASSERT(node->getType() == ELoopFor);
220
221 //
222 // The for statement has the form:
223 // for ( init-declaration ; condition ; expression ) statement
224 //
225 if (!validateForLoopInit(node, info))
226 return false;
227 if (!validateForLoopCond(node, info))
228 return false;
229 if (!validateForLoopExpr(node, info))
230 return false;
231
232 return true;
233}
234
235bool ValidateLimitations::validateForLoopInit(TIntermLoop* node,
236 TLoopInfo* info)
237{
238 TIntermNode* init = node->getInit();
239 if (init == NULL) {
240 error(node->getLine(), "Missing init declaration", "for");
241 return false;
242 }
243
244 //
245 // init-declaration has the form:
246 // type-specifier identifier = constant-expression
247 //
248 TIntermAggregate* decl = init->getAsAggregate();
249 if ((decl == NULL) || (decl->getOp() != EOpDeclaration)) {
250 error(init->getLine(), "Invalid init declaration", "for");
251 return false;
252 }
253 // To keep things simple do not allow declaration list.
254 TIntermSequence& declSeq = decl->getSequence();
255 if (declSeq.size() != 1) {
256 error(decl->getLine(), "Invalid init declaration", "for");
257 return false;
258 }
259 TIntermBinary* declInit = declSeq[0]->getAsBinaryNode();
260 if ((declInit == NULL) || (declInit->getOp() != EOpInitialize)) {
261 error(decl->getLine(), "Invalid init declaration", "for");
262 return false;
263 }
264 TIntermSymbol* symbol = declInit->getLeft()->getAsSymbolNode();
265 if (symbol == NULL) {
266 error(declInit->getLine(), "Invalid init declaration", "for");
267 return false;
268 }
269 // The loop index has type int or float.
270 TBasicType type = symbol->getBasicType();
271 if ((type != EbtInt) && (type != EbtFloat)) {
272 error(symbol->getLine(),
273 "Invalid type for loop index", getBasicString(type));
274 return false;
275 }
276 // The loop index is initialized with constant expression.
277 if (!isConstExpr(declInit->getRight())) {
278 error(declInit->getLine(),
279 "Loop index cannot be initialized with non-constant expression",
280 symbol->getSymbol().c_str());
281 return false;
282 }
283
284 info->index.id = symbol->getId();
285 return true;
286}
287
288bool ValidateLimitations::validateForLoopCond(TIntermLoop* node,
289 TLoopInfo* info)
290{
291 TIntermNode* cond = node->getCondition();
292 if (cond == NULL) {
293 error(node->getLine(), "Missing condition", "for");
294 return false;
295 }
296 //
297 // condition has the form:
298 // loop_index relational_operator constant_expression
299 //
300 TIntermBinary* binOp = cond->getAsBinaryNode();
301 if (binOp == NULL) {
302 error(node->getLine(), "Invalid condition", "for");
303 return false;
304 }
305 // Loop index should be to the left of relational operator.
306 TIntermSymbol* symbol = binOp->getLeft()->getAsSymbolNode();
307 if (symbol == NULL) {
308 error(binOp->getLine(), "Invalid condition", "for");
309 return false;
310 }
311 if (symbol->getId() != info->index.id) {
312 error(symbol->getLine(),
313 "Expected loop index", symbol->getSymbol().c_str());
314 return false;
315 }
316 // Relational operator is one of: > >= < <= == or !=.
317 switch (binOp->getOp()) {
318 case EOpEqual:
319 case EOpNotEqual:
320 case EOpLessThan:
321 case EOpGreaterThan:
322 case EOpLessThanEqual:
323 case EOpGreaterThanEqual:
324 break;
325 default:
326 error(binOp->getLine(),
327 "Invalid relational operator",
328 getOperatorString(binOp->getOp()));
329 break;
330 }
331 // Loop index must be compared with a constant.
332 if (!isConstExpr(binOp->getRight())) {
333 error(binOp->getLine(),
334 "Loop index cannot be compared with non-constant expression",
335 symbol->getSymbol().c_str());
336 return false;
337 }
338
339 return true;
340}
341
342bool ValidateLimitations::validateForLoopExpr(TIntermLoop* node,
343 TLoopInfo* info)
344{
345 TIntermNode* expr = node->getExpression();
346 if (expr == NULL) {
347 error(node->getLine(), "Missing expression", "for");
348 return false;
349 }
350
351 // for expression has one of the following forms:
352 // loop_index++
353 // loop_index--
354 // loop_index += constant_expression
355 // loop_index -= constant_expression
356 // ++loop_index
357 // --loop_index
358 // The last two forms are not specified in the spec, but I am assuming
359 // its an oversight.
360 TIntermUnary* unOp = expr->getAsUnaryNode();
361 TIntermBinary* binOp = unOp ? NULL : expr->getAsBinaryNode();
362
363 TOperator op = EOpNull;
364 TIntermSymbol* symbol = NULL;
365 if (unOp != NULL) {
366 op = unOp->getOp();
367 symbol = unOp->getOperand()->getAsSymbolNode();
368 } else if (binOp != NULL) {
369 op = binOp->getOp();
370 symbol = binOp->getLeft()->getAsSymbolNode();
371 }
372
373 // The operand must be loop index.
374 if (symbol == NULL) {
375 error(expr->getLine(), "Invalid expression", "for");
376 return false;
377 }
378 if (symbol->getId() != info->index.id) {
379 error(symbol->getLine(),
380 "Expected loop index", symbol->getSymbol().c_str());
381 return false;
382 }
383
384 // The operator is one of: ++ -- += -=.
385 switch (op) {
386 case EOpPostIncrement:
387 case EOpPostDecrement:
388 case EOpPreIncrement:
389 case EOpPreDecrement:
390 ASSERT((unOp != NULL) && (binOp == NULL));
391 break;
392 case EOpAddAssign:
393 case EOpSubAssign:
394 ASSERT((unOp == NULL) && (binOp != NULL));
395 break;
396 default:
397 error(expr->getLine(), "Invalid operator", getOperatorString(op));
398 return false;
399 }
400
401 // Loop index must be incremented/decremented with a constant.
402 if (binOp != NULL) {
403 if (!isConstExpr(binOp->getRight())) {
404 error(binOp->getLine(),
405 "Loop index cannot be modified by non-constant expression",
406 symbol->getSymbol().c_str());
407 return false;
408 }
409 }
410
411 return true;
412}
413
414bool ValidateLimitations::validateFunctionCall(TIntermAggregate* node)
415{
416 ASSERT(node->getOp() == EOpFunctionCall);
417
418 // If not within loop body, there is nothing to check.
419 if (!withinLoopBody())
420 return true;
421
422 // List of param indices for which loop indices are used as argument.
423 typedef std::vector<int> ParamIndex;
424 ParamIndex pIndex;
425 TIntermSequence& params = node->getSequence();
426 for (TIntermSequence::size_type i = 0; i < params.size(); ++i) {
427 TIntermSymbol* symbol = params[i]->getAsSymbolNode();
428 if (symbol && isLoopIndex(symbol))
429 pIndex.push_back(i);
430 }
431 // If none of the loop indices are used as arguments,
432 // there is nothing to check.
433 if (pIndex.empty())
434 return true;
435
436 bool valid = true;
437 TSymbolTable& symbolTable = GlobalParseContext->symbolTable;
438 TSymbol* symbol = symbolTable.find(node->getName());
439 ASSERT(symbol && symbol->isFunction());
440 TFunction* function = static_cast<TFunction*>(symbol);
441 for (ParamIndex::const_iterator i = pIndex.begin();
442 i != pIndex.end(); ++i) {
443 const TParameter& param = function->getParam(*i);
444 TQualifier qual = param.type->getQualifier();
445 if ((qual == EvqOut) || (qual == EvqInOut)) {
446 error(params[*i]->getLine(),
447 "Loop index cannot be used as argument to a function out or inout parameter",
448 params[*i]->getAsSymbolNode()->getSymbol().c_str());
449 valid = false;
450 }
451 }
452
453 return valid;
454}
455
456bool ValidateLimitations::validateOperation(TIntermOperator* node,
457 TIntermNode* operand) {
458 // Check if loop index is modified in the loop body.
459 if (!withinLoopBody() || !node->modifiesState())
460 return true;
461
462 const TIntermSymbol* symbol = operand->getAsSymbolNode();
463 if (symbol && isLoopIndex(symbol)) {
464 error(node->getLine(),
465 "Loop index cannot be statically assigned to within the body of the loop",
466 symbol->getSymbol().c_str());
467 }
468 return true;
469}
470
471bool ValidateLimitations::isConstExpr(TIntermNode* node)
472{
473 ASSERT(node != NULL);
474 return node->getAsConstantUnion() != NULL;
475}
476
477bool ValidateLimitations::isConstIndexExpr(TIntermNode* node)
478{
479 ASSERT(node != NULL);
480
481 ValidateConstIndexExpr validate(mLoopStack);
482 node->traverse(&validate);
483 return validate.isValid();
484}
485
486bool ValidateLimitations::validateIndexing(TIntermBinary* node)
487{
488 ASSERT((node->getOp() == EOpIndexDirect) ||
489 (node->getOp() == EOpIndexIndirect));
490
491 bool valid = true;
492 TIntermTyped* index = node->getRight();
493 // The index expression must have integral type.
494 if (!index->isScalar() || (index->getBasicType() != EbtInt)) {
495 error(index->getLine(),
496 "Index expression must have integral type",
497 index->getCompleteString().c_str());
498 valid = false;
499 }
500 // The index expession must be a constant-index-expression unless
501 // the operand is a uniform in a vertex shader.
502 TIntermTyped* operand = node->getLeft();
503 bool skip = (mShaderType == SH_VERTEX_SHADER) &&
504 (operand->getQualifier() == EvqUniform);
505 if (!skip && !isConstIndexExpr(index)) {
506 error(index->getLine(), "Index expression must be constant", "[]");
507 valid = false;
508 }
509 return valid;
510}
511