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