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.
7 #include "compiler/ValidateLimitations.h"
8 #include "compiler/InfoSink.h"
9 #include "compiler/ParseHelper.h"
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())
20 void 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);
31 // Traverses a node to check if it represents a constant index expression.
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
40 class ValidateConstIndexExpr : public TIntermTraverser {
42 ValidateConstIndexExpr(const TLoopStack& stack)
43 : mValid(true), mLoopStack(stack) {}
45 // Returns true if the parsed node represents a constant index expression.
46 bool isValid() const { return mValid; }
48 virtual void visitSymbol(TIntermSymbol* symbol) {
49 // Only constants and loop indices are allowed in a
50 // constant index expression.
52 mValid = (symbol->getQualifier() == EvqConst) ||
53 IsLoopIndex(symbol, mLoopStack);
56 virtual void visitConstantUnion(TIntermConstantUnion*) {}
57 virtual bool visitBinary(Visit, TIntermBinary*) { return true; }
58 virtual bool visitUnary(Visit, TIntermUnary*) { return true; }
59 virtual bool visitSelection(Visit, TIntermSelection*) { return true; }
60 virtual bool visitAggregate(Visit, TIntermAggregate*) { return true; }
61 virtual bool visitLoop(Visit, TIntermLoop*) { return true; }
62 virtual bool visitBranch(Visit, TIntermBranch*) { return true; }
66 const TLoopStack& mLoopStack;
69 // Traverses a node to check if it uses a loop index.
70 // If an int loop index is used in its body as a sampler array index,
71 // mark the loop for unroll.
72 class ValidateLoopIndexExpr : public TIntermTraverser {
74 ValidateLoopIndexExpr(TLoopStack& stack)
75 : mUsesFloatLoopIndex(false),
76 mUsesIntLoopIndex(false),
79 bool usesFloatLoopIndex() const { return mUsesFloatLoopIndex; }
80 bool usesIntLoopIndex() const { return mUsesIntLoopIndex; }
82 virtual void visitSymbol(TIntermSymbol* symbol) {
83 if (IsLoopIndex(symbol, mLoopStack)) {
84 switch (symbol->getBasicType()) {
86 mUsesFloatLoopIndex = true;
89 mUsesIntLoopIndex = true;
90 MarkLoopForUnroll(symbol, mLoopStack);
97 virtual void visitConstantUnion(TIntermConstantUnion*) {}
98 virtual bool visitBinary(Visit, TIntermBinary*) { return true; }
99 virtual bool visitUnary(Visit, TIntermUnary*) { return true; }
100 virtual bool visitSelection(Visit, TIntermSelection*) { return true; }
101 virtual bool visitAggregate(Visit, TIntermAggregate*) { return true; }
102 virtual bool visitLoop(Visit, TIntermLoop*) { return true; }
103 virtual bool visitBranch(Visit, TIntermBranch*) { return true; }
106 bool mUsesFloatLoopIndex;
107 bool mUsesIntLoopIndex;
108 TLoopStack& mLoopStack;
112 ValidateLimitations::ValidateLimitations(ShShaderType shaderType,
114 : mShaderType(shaderType),
120 void ValidateLimitations::visitSymbol(TIntermSymbol*)
124 void ValidateLimitations::visitConstantUnion(TIntermConstantUnion*)
128 bool ValidateLimitations::visitBinary(Visit, TIntermBinary* node)
130 // Check if loop index is modified in the loop body.
131 validateOperation(node, node->getLeft());
134 switch (node->getOp()) {
136 validateIndexing(node);
138 case EOpIndexIndirect:
139 #if defined(__APPLE__)
140 // Loop unrolling is a work-around for a Mac Cg compiler bug where it
141 // crashes when a sampler array's index is also the loop index.
142 // Once Apple fixes this bug, we should remove the code in this CL.
143 // See http://codereview.appspot.com/4331048/.
144 if ((node->getLeft() != NULL) && (node->getRight() != NULL) &&
145 (node->getLeft()->getAsSymbolNode())) {
146 TIntermSymbol* symbol = node->getLeft()->getAsSymbolNode();
147 if (IsSampler(symbol->getBasicType()) && symbol->isArray()) {
148 ValidateLoopIndexExpr validate(mLoopStack);
149 node->getRight()->traverse(&validate);
150 if (validate.usesFloatLoopIndex()) {
151 error(node->getLine(),
152 "sampler array index is float loop index",
158 validateIndexing(node);
165 bool ValidateLimitations::visitUnary(Visit, TIntermUnary* node)
167 // Check if loop index is modified in the loop body.
168 validateOperation(node, node->getOperand());
173 bool ValidateLimitations::visitSelection(Visit, TIntermSelection*)
178 bool ValidateLimitations::visitAggregate(Visit, TIntermAggregate* node)
180 switch (node->getOp()) {
181 case EOpFunctionCall:
182 validateFunctionCall(node);
190 bool ValidateLimitations::visitLoop(Visit, TIntermLoop* node)
192 if (!validateLoopType(node))
196 memset(&info, 0, sizeof(TLoopInfo));
198 if (!validateForLoopHeader(node, &info))
201 TIntermNode* body = node->getBody();
203 mLoopStack.push_back(info);
204 body->traverse(this);
205 mLoopStack.pop_back();
208 // The loop is fully processed - no need to visit children.
212 bool ValidateLimitations::visitBranch(Visit, TIntermBranch*)
217 void ValidateLimitations::error(TSourceLoc loc,
218 const char *reason, const char* token)
220 mSink.prefix(EPrefixError);
222 mSink << "'" << token << "' : " << reason << "\n";
226 bool ValidateLimitations::withinLoopBody() const
228 return !mLoopStack.empty();
231 bool ValidateLimitations::isLoopIndex(const TIntermSymbol* symbol) const
233 return IsLoopIndex(symbol, mLoopStack);
236 bool ValidateLimitations::validateLoopType(TIntermLoop* node) {
237 TLoopType type = node->getType();
238 if (type == ELoopFor)
241 // Reject while and do-while loops.
242 error(node->getLine(),
243 "This type of loop is not allowed",
244 type == ELoopWhile ? "while" : "do");
248 bool ValidateLimitations::validateForLoopHeader(TIntermLoop* node,
251 ASSERT(node->getType() == ELoopFor);
254 // The for statement has the form:
255 // for ( init-declaration ; condition ; expression ) statement
257 if (!validateForLoopInit(node, info))
259 if (!validateForLoopCond(node, info))
261 if (!validateForLoopExpr(node, info))
267 bool ValidateLimitations::validateForLoopInit(TIntermLoop* node,
270 TIntermNode* init = node->getInit();
272 error(node->getLine(), "Missing init declaration", "for");
277 // init-declaration has the form:
278 // type-specifier identifier = constant-expression
280 TIntermAggregate* decl = init->getAsAggregate();
281 if ((decl == NULL) || (decl->getOp() != EOpDeclaration)) {
282 error(init->getLine(), "Invalid init declaration", "for");
285 // To keep things simple do not allow declaration list.
286 TIntermSequence& declSeq = decl->getSequence();
287 if (declSeq.size() != 1) {
288 error(decl->getLine(), "Invalid init declaration", "for");
291 TIntermBinary* declInit = declSeq[0]->getAsBinaryNode();
292 if ((declInit == NULL) || (declInit->getOp() != EOpInitialize)) {
293 error(decl->getLine(), "Invalid init declaration", "for");
296 TIntermSymbol* symbol = declInit->getLeft()->getAsSymbolNode();
297 if (symbol == NULL) {
298 error(declInit->getLine(), "Invalid init declaration", "for");
301 // The loop index has type int or float.
302 TBasicType type = symbol->getBasicType();
303 if ((type != EbtInt) && (type != EbtFloat)) {
304 error(symbol->getLine(),
305 "Invalid type for loop index", getBasicString(type));
308 // The loop index is initialized with constant expression.
309 if (!isConstExpr(declInit->getRight())) {
310 error(declInit->getLine(),
311 "Loop index cannot be initialized with non-constant expression",
312 symbol->getSymbol().c_str());
316 info->index.id = symbol->getId();
320 bool ValidateLimitations::validateForLoopCond(TIntermLoop* node,
323 TIntermNode* cond = node->getCondition();
325 error(node->getLine(), "Missing condition", "for");
329 // condition has the form:
330 // loop_index relational_operator constant_expression
332 TIntermBinary* binOp = cond->getAsBinaryNode();
334 error(node->getLine(), "Invalid condition", "for");
337 // Loop index should be to the left of relational operator.
338 TIntermSymbol* symbol = binOp->getLeft()->getAsSymbolNode();
339 if (symbol == NULL) {
340 error(binOp->getLine(), "Invalid condition", "for");
343 if (symbol->getId() != info->index.id) {
344 error(symbol->getLine(),
345 "Expected loop index", symbol->getSymbol().c_str());
348 // Relational operator is one of: > >= < <= == or !=.
349 switch (binOp->getOp()) {
354 case EOpLessThanEqual:
355 case EOpGreaterThanEqual:
358 error(binOp->getLine(),
359 "Invalid relational operator",
360 getOperatorString(binOp->getOp()));
363 // Loop index must be compared with a constant.
364 if (!isConstExpr(binOp->getRight())) {
365 error(binOp->getLine(),
366 "Loop index cannot be compared with non-constant expression",
367 symbol->getSymbol().c_str());
374 bool ValidateLimitations::validateForLoopExpr(TIntermLoop* node,
377 TIntermNode* expr = node->getExpression();
379 error(node->getLine(), "Missing expression", "for");
383 // for expression has one of the following forms:
386 // loop_index += constant_expression
387 // loop_index -= constant_expression
390 // The last two forms are not specified in the spec, but I am assuming
392 TIntermUnary* unOp = expr->getAsUnaryNode();
393 TIntermBinary* binOp = unOp ? NULL : expr->getAsBinaryNode();
395 TOperator op = EOpNull;
396 TIntermSymbol* symbol = NULL;
399 symbol = unOp->getOperand()->getAsSymbolNode();
400 } else if (binOp != NULL) {
402 symbol = binOp->getLeft()->getAsSymbolNode();
405 // The operand must be loop index.
406 if (symbol == NULL) {
407 error(expr->getLine(), "Invalid expression", "for");
410 if (symbol->getId() != info->index.id) {
411 error(symbol->getLine(),
412 "Expected loop index", symbol->getSymbol().c_str());
416 // The operator is one of: ++ -- += -=.
418 case EOpPostIncrement:
419 case EOpPostDecrement:
420 case EOpPreIncrement:
421 case EOpPreDecrement:
422 ASSERT((unOp != NULL) && (binOp == NULL));
426 ASSERT((unOp == NULL) && (binOp != NULL));
429 error(expr->getLine(), "Invalid operator", getOperatorString(op));
433 // Loop index must be incremented/decremented with a constant.
435 if (!isConstExpr(binOp->getRight())) {
436 error(binOp->getLine(),
437 "Loop index cannot be modified by non-constant expression",
438 symbol->getSymbol().c_str());
446 bool ValidateLimitations::validateFunctionCall(TIntermAggregate* node)
448 ASSERT(node->getOp() == EOpFunctionCall);
450 // If not within loop body, there is nothing to check.
451 if (!withinLoopBody())
454 // List of param indices for which loop indices are used as argument.
455 typedef std::vector<int> ParamIndex;
457 TIntermSequence& params = node->getSequence();
458 for (TIntermSequence::size_type i = 0; i < params.size(); ++i) {
459 TIntermSymbol* symbol = params[i]->getAsSymbolNode();
460 if (symbol && isLoopIndex(symbol))
463 // If none of the loop indices are used as arguments,
464 // there is nothing to check.
469 TSymbolTable& symbolTable = GlobalParseContext->symbolTable;
470 TSymbol* symbol = symbolTable.find(node->getName());
471 ASSERT(symbol && symbol->isFunction());
472 TFunction* function = static_cast<TFunction*>(symbol);
473 for (ParamIndex::const_iterator i = pIndex.begin();
474 i != pIndex.end(); ++i) {
475 const TParameter& param = function->getParam(*i);
476 TQualifier qual = param.type->getQualifier();
477 if ((qual == EvqOut) || (qual == EvqInOut)) {
478 error(params[*i]->getLine(),
479 "Loop index cannot be used as argument to a function out or inout parameter",
480 params[*i]->getAsSymbolNode()->getSymbol().c_str());
488 bool ValidateLimitations::validateOperation(TIntermOperator* node,
489 TIntermNode* operand) {
490 // Check if loop index is modified in the loop body.
491 if (!withinLoopBody() || !node->modifiesState())
494 const TIntermSymbol* symbol = operand->getAsSymbolNode();
495 if (symbol && isLoopIndex(symbol)) {
496 error(node->getLine(),
497 "Loop index cannot be statically assigned to within the body of the loop",
498 symbol->getSymbol().c_str());
503 bool ValidateLimitations::isConstExpr(TIntermNode* node)
505 ASSERT(node != NULL);
506 return node->getAsConstantUnion() != NULL;
509 bool ValidateLimitations::isConstIndexExpr(TIntermNode* node)
511 ASSERT(node != NULL);
513 ValidateConstIndexExpr validate(mLoopStack);
514 node->traverse(&validate);
515 return validate.isValid();
518 bool ValidateLimitations::validateIndexing(TIntermBinary* node)
520 ASSERT((node->getOp() == EOpIndexDirect) ||
521 (node->getOp() == EOpIndexIndirect));
524 TIntermTyped* index = node->getRight();
525 // The index expression must have integral type.
526 if (!index->isScalar() || (index->getBasicType() != EbtInt)) {
527 error(index->getLine(),
528 "Index expression must have integral type",
529 index->getCompleteString().c_str());
532 // The index expession must be a constant-index-expression unless
533 // the operand is a uniform in a vertex shader.
534 TIntermTyped* operand = node->getLeft();
535 bool skip = (mShaderType == SH_VERTEX_SHADER) &&
536 (operand->getQualifier() == EvqUniform);
537 if (!skip && !isConstIndexExpr(index)) {
538 error(index->getLine(), "Index expression must be constant", "[]");