gfx/angle/src/compiler/ValidateLimitations.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

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

mercurial