LCOV - code coverage report
Current view: directory - gfx/angle/src/compiler - ValidateLimitations.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 229 0 0.0 %
Date: 2012-06-02 Functions: 24 0 0.0 %

       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                 : 

Generated by: LCOV version 1.7