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