blob: b46e4b91b27cd9ec85deacec789839ff87fa1c08 [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 }
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; }
63
64private:
65 bool mValid;
66 const TLoopStack& mLoopStack;
67};
zmo@google.com0b8d4eb2011-04-04 19:17:11 +000068
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.
72class ValidateLoopIndexExpr : public TIntermTraverser {
73public:
74 ValidateLoopIndexExpr(TLoopStack& stack)
75 : mUsesFloatLoopIndex(false),
76 mUsesIntLoopIndex(false),
77 mLoopStack(stack) {}
78
79 bool usesFloatLoopIndex() const { return mUsesFloatLoopIndex; }
80 bool usesIntLoopIndex() const { return mUsesIntLoopIndex; }
81
82 virtual void visitSymbol(TIntermSymbol* symbol) {
83 if (IsLoopIndex(symbol, mLoopStack)) {
84 switch (symbol->getBasicType()) {
85 case EbtFloat:
86 mUsesFloatLoopIndex = true;
87 break;
88 case EbtInt:
89 mUsesIntLoopIndex = true;
90 MarkLoopForUnroll(symbol, mLoopStack);
91 break;
92 default:
93 UNREACHABLE();
94 }
95 }
96 }
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; }
104
105private:
106 bool mUsesFloatLoopIndex;
107 bool mUsesIntLoopIndex;
108 TLoopStack& mLoopStack;
109};
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000110} // namespace
111
112ValidateLimitations::ValidateLimitations(ShShaderType shaderType,
113 TInfoSinkBase& sink)
114 : mShaderType(shaderType),
115 mSink(sink),
116 mNumErrors(0)
117{
118}
119
120void ValidateLimitations::visitSymbol(TIntermSymbol*)
121{
122}
123
124void ValidateLimitations::visitConstantUnion(TIntermConstantUnion*)
125{
126}
127
128bool ValidateLimitations::visitBinary(Visit, TIntermBinary* node)
129{
130 // Check if loop index is modified in the loop body.
131 validateOperation(node, node->getLeft());
132
133 // Check indexing.
134 switch (node->getOp()) {
135 case EOpIndexDirect:
zmo@google.com0b8d4eb2011-04-04 19:17:11 +0000136 validateIndexing(node);
137 break;
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000138 case EOpIndexIndirect:
zmo@google.com0b8d4eb2011-04-04 19:17:11 +0000139#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",
153 "for");
154 }
155 }
156 }
157#endif
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000158 validateIndexing(node);
159 break;
160 default: break;
161 }
162 return true;
163}
164
165bool ValidateLimitations::visitUnary(Visit, TIntermUnary* node)
166{
167 // Check if loop index is modified in the loop body.
168 validateOperation(node, node->getOperand());
169
170 return true;
171}
172
173bool ValidateLimitations::visitSelection(Visit, TIntermSelection*)
174{
175 return true;
176}
177
178bool ValidateLimitations::visitAggregate(Visit, TIntermAggregate* node)
179{
180 switch (node->getOp()) {
181 case EOpFunctionCall:
182 validateFunctionCall(node);
183 break;
184 default:
185 break;
186 }
187 return true;
188}
189
190bool ValidateLimitations::visitLoop(Visit, TIntermLoop* node)
191{
192 if (!validateLoopType(node))
193 return false;
194
195 TLoopInfo info;
196 memset(&info, 0, sizeof(TLoopInfo));
zmo@google.com0b8d4eb2011-04-04 19:17:11 +0000197 info.loop = node;
alokp@chromium.orgb59a7782010-11-24 18:38:33 +0000198 if (!validateForLoopHeader(node, &info))
199 return false;
200
201 TIntermNode* body = node->getBody();
202 if (body != NULL) {
203 mLoopStack.push_back(info);
204 body->traverse(this);
205 mLoopStack.pop_back();
206 }
207
208 // The loop is fully processed - no need to visit children.
209 return false;
210}
211
212bool ValidateLimitations::visitBranch(Visit, TIntermBranch*)
213{
214 return true;
215}
216
217void ValidateLimitations::error(TSourceLoc loc,
218 const char *reason, const char* token)
219{
220 mSink.prefix(EPrefixError);
221 mSink.location(loc);
222 mSink << "'" << token << "' : " << reason << "\n";
223 ++mNumErrors;
224}
225
226bool ValidateLimitations::withinLoopBody() const
227{
228 return !mLoopStack.empty();
229}
230
231bool ValidateLimitations::isLoopIndex(const TIntermSymbol* symbol) const
232{
233 return IsLoopIndex(symbol, mLoopStack);
234}
235
236bool ValidateLimitations::validateLoopType(TIntermLoop* node) {
237 TLoopType type = node->getType();
238 if (type == ELoopFor)
239 return true;
240
241 // Reject while and do-while loops.
242 error(node->getLine(),
243 "This type of loop is not allowed",
244 type == ELoopWhile ? "while" : "do");
245 return false;
246}
247
248bool ValidateLimitations::validateForLoopHeader(TIntermLoop* node,
249 TLoopInfo* info)
250{
251 ASSERT(node->getType() == ELoopFor);
252
253 //
254 // The for statement has the form:
255 // for ( init-declaration ; condition ; expression ) statement
256 //
257 if (!validateForLoopInit(node, info))
258 return false;
259 if (!validateForLoopCond(node, info))
260 return false;
261 if (!validateForLoopExpr(node, info))
262 return false;
263
264 return true;
265}
266
267bool ValidateLimitations::validateForLoopInit(TIntermLoop* node,
268 TLoopInfo* info)
269{
270 TIntermNode* init = node->getInit();
271 if (init == NULL) {
272 error(node->getLine(), "Missing init declaration", "for");
273 return false;
274 }
275
276 //
277 // init-declaration has the form:
278 // type-specifier identifier = constant-expression
279 //
280 TIntermAggregate* decl = init->getAsAggregate();
281 if ((decl == NULL) || (decl->getOp() != EOpDeclaration)) {
282 error(init->getLine(), "Invalid init declaration", "for");
283 return false;
284 }
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");
289 return false;
290 }
291 TIntermBinary* declInit = declSeq[0]->getAsBinaryNode();
292 if ((declInit == NULL) || (declInit->getOp() != EOpInitialize)) {
293 error(decl->getLine(), "Invalid init declaration", "for");
294 return false;
295 }
296 TIntermSymbol* symbol = declInit->getLeft()->getAsSymbolNode();
297 if (symbol == NULL) {
298 error(declInit->getLine(), "Invalid init declaration", "for");
299 return false;
300 }
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));
306 return false;
307 }
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());
313 return false;
314 }
315
316 info->index.id = symbol->getId();
317 return true;
318}
319
320bool ValidateLimitations::validateForLoopCond(TIntermLoop* node,
321 TLoopInfo* info)
322{
323 TIntermNode* cond = node->getCondition();
324 if (cond == NULL) {
325 error(node->getLine(), "Missing condition", "for");
326 return false;
327 }
328 //
329 // condition has the form:
330 // loop_index relational_operator constant_expression
331 //
332 TIntermBinary* binOp = cond->getAsBinaryNode();
333 if (binOp == NULL) {
334 error(node->getLine(), "Invalid condition", "for");
335 return false;
336 }
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");
341 return false;
342 }
343 if (symbol->getId() != info->index.id) {
344 error(symbol->getLine(),
345 "Expected loop index", symbol->getSymbol().c_str());
346 return false;
347 }
348 // Relational operator is one of: > >= < <= == or !=.
349 switch (binOp->getOp()) {
350 case EOpEqual:
351 case EOpNotEqual:
352 case EOpLessThan:
353 case EOpGreaterThan:
354 case EOpLessThanEqual:
355 case EOpGreaterThanEqual:
356 break;
357 default:
358 error(binOp->getLine(),
359 "Invalid relational operator",
360 getOperatorString(binOp->getOp()));
361 break;
362 }
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());
368 return false;
369 }
370
371 return true;
372}
373
374bool ValidateLimitations::validateForLoopExpr(TIntermLoop* node,
375 TLoopInfo* info)
376{
377 TIntermNode* expr = node->getExpression();
378 if (expr == NULL) {
379 error(node->getLine(), "Missing expression", "for");
380 return false;
381 }
382
383 // for expression has one of the following forms:
384 // loop_index++
385 // loop_index--
386 // loop_index += constant_expression
387 // loop_index -= constant_expression
388 // ++loop_index
389 // --loop_index
390 // The last two forms are not specified in the spec, but I am assuming
391 // its an oversight.
392 TIntermUnary* unOp = expr->getAsUnaryNode();
393 TIntermBinary* binOp = unOp ? NULL : expr->getAsBinaryNode();
394
395 TOperator op = EOpNull;
396 TIntermSymbol* symbol = NULL;
397 if (unOp != NULL) {
398 op = unOp->getOp();
399 symbol = unOp->getOperand()->getAsSymbolNode();
400 } else if (binOp != NULL) {
401 op = binOp->getOp();
402 symbol = binOp->getLeft()->getAsSymbolNode();
403 }
404
405 // The operand must be loop index.
406 if (symbol == NULL) {
407 error(expr->getLine(), "Invalid expression", "for");
408 return false;
409 }
410 if (symbol->getId() != info->index.id) {
411 error(symbol->getLine(),
412 "Expected loop index", symbol->getSymbol().c_str());
413 return false;
414 }
415
416 // The operator is one of: ++ -- += -=.
417 switch (op) {
418 case EOpPostIncrement:
419 case EOpPostDecrement:
420 case EOpPreIncrement:
421 case EOpPreDecrement:
422 ASSERT((unOp != NULL) && (binOp == NULL));
423 break;
424 case EOpAddAssign:
425 case EOpSubAssign:
426 ASSERT((unOp == NULL) && (binOp != NULL));
427 break;
428 default:
429 error(expr->getLine(), "Invalid operator", getOperatorString(op));
430 return false;
431 }
432
433 // Loop index must be incremented/decremented with a constant.
434 if (binOp != NULL) {
435 if (!isConstExpr(binOp->getRight())) {
436 error(binOp->getLine(),
437 "Loop index cannot be modified by non-constant expression",
438 symbol->getSymbol().c_str());
439 return false;
440 }
441 }
442
443 return true;
444}
445
446bool ValidateLimitations::validateFunctionCall(TIntermAggregate* node)
447{
448 ASSERT(node->getOp() == EOpFunctionCall);
449
450 // If not within loop body, there is nothing to check.
451 if (!withinLoopBody())
452 return true;
453
454 // List of param indices for which loop indices are used as argument.
455 typedef std::vector<int> ParamIndex;
456 ParamIndex pIndex;
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))
461 pIndex.push_back(i);
462 }
463 // If none of the loop indices are used as arguments,
464 // there is nothing to check.
465 if (pIndex.empty())
466 return true;
467
468 bool valid = true;
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());
481 valid = false;
482 }
483 }
484
485 return valid;
486}
487
488bool ValidateLimitations::validateOperation(TIntermOperator* node,
489 TIntermNode* operand) {
490 // Check if loop index is modified in the loop body.
491 if (!withinLoopBody() || !node->modifiesState())
492 return true;
493
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());
499 }
500 return true;
501}
502
503bool ValidateLimitations::isConstExpr(TIntermNode* node)
504{
505 ASSERT(node != NULL);
506 return node->getAsConstantUnion() != NULL;
507}
508
509bool ValidateLimitations::isConstIndexExpr(TIntermNode* node)
510{
511 ASSERT(node != NULL);
512
513 ValidateConstIndexExpr validate(mLoopStack);
514 node->traverse(&validate);
515 return validate.isValid();
516}
517
518bool ValidateLimitations::validateIndexing(TIntermBinary* node)
519{
520 ASSERT((node->getOp() == EOpIndexDirect) ||
521 (node->getOp() == EOpIndexIndirect));
522
523 bool valid = true;
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());
530 valid = false;
531 }
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", "[]");
539 valid = false;
540 }
541 return valid;
542}
543