gfx/angle/src/compiler/ValidateLimitations.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/gfx/angle/src/compiler/ValidateLimitations.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,512 @@
     1.4 +//
     1.5 +// Copyright (c) 2002-2010 The ANGLE Project Authors. All rights reserved.
     1.6 +// Use of this source code is governed by a BSD-style license that can be
     1.7 +// found in the LICENSE file.
     1.8 +//
     1.9 +
    1.10 +#include "compiler/ValidateLimitations.h"
    1.11 +#include "compiler/InfoSink.h"
    1.12 +#include "compiler/InitializeParseContext.h"
    1.13 +#include "compiler/ParseHelper.h"
    1.14 +
    1.15 +namespace {
    1.16 +bool IsLoopIndex(const TIntermSymbol* symbol, const TLoopStack& stack) {
    1.17 +    for (TLoopStack::const_iterator i = stack.begin(); i != stack.end(); ++i) {
    1.18 +        if (i->index.id == symbol->getId())
    1.19 +            return true;
    1.20 +    }
    1.21 +    return false;
    1.22 +}
    1.23 +
    1.24 +void MarkLoopForUnroll(const TIntermSymbol* symbol, TLoopStack& stack) {
    1.25 +    for (TLoopStack::iterator i = stack.begin(); i != stack.end(); ++i) {
    1.26 +        if (i->index.id == symbol->getId()) {
    1.27 +            ASSERT(i->loop != NULL);
    1.28 +            i->loop->setUnrollFlag(true);
    1.29 +            return;
    1.30 +        }
    1.31 +    }
    1.32 +    UNREACHABLE();
    1.33 +}
    1.34 +
    1.35 +// Traverses a node to check if it represents a constant index expression.
    1.36 +// Definition:
    1.37 +// constant-index-expressions are a superset of constant-expressions.
    1.38 +// Constant-index-expressions can include loop indices as defined in
    1.39 +// GLSL ES 1.0 spec, Appendix A, section 4.
    1.40 +// The following are constant-index-expressions:
    1.41 +// - Constant expressions
    1.42 +// - Loop indices as defined in section 4
    1.43 +// - Expressions composed of both of the above
    1.44 +class ValidateConstIndexExpr : public TIntermTraverser {
    1.45 +public:
    1.46 +    ValidateConstIndexExpr(const TLoopStack& stack)
    1.47 +        : mValid(true), mLoopStack(stack) {}
    1.48 +
    1.49 +    // Returns true if the parsed node represents a constant index expression.
    1.50 +    bool isValid() const { return mValid; }
    1.51 +
    1.52 +    virtual void visitSymbol(TIntermSymbol* symbol) {
    1.53 +        // Only constants and loop indices are allowed in a
    1.54 +        // constant index expression.
    1.55 +        if (mValid) {
    1.56 +            mValid = (symbol->getQualifier() == EvqConst) ||
    1.57 +                     IsLoopIndex(symbol, mLoopStack);
    1.58 +        }
    1.59 +    }
    1.60 +
    1.61 +private:
    1.62 +    bool mValid;
    1.63 +    const TLoopStack& mLoopStack;
    1.64 +};
    1.65 +
    1.66 +// Traverses a node to check if it uses a loop index.
    1.67 +// If an int loop index is used in its body as a sampler array index,
    1.68 +// mark the loop for unroll.
    1.69 +class ValidateLoopIndexExpr : public TIntermTraverser {
    1.70 +public:
    1.71 +    ValidateLoopIndexExpr(TLoopStack& stack)
    1.72 +        : mUsesFloatLoopIndex(false),
    1.73 +          mUsesIntLoopIndex(false),
    1.74 +          mLoopStack(stack) {}
    1.75 +
    1.76 +    bool usesFloatLoopIndex() const { return mUsesFloatLoopIndex; }
    1.77 +    bool usesIntLoopIndex() const { return mUsesIntLoopIndex; }
    1.78 +
    1.79 +    virtual void visitSymbol(TIntermSymbol* symbol) {
    1.80 +        if (IsLoopIndex(symbol, mLoopStack)) {
    1.81 +            switch (symbol->getBasicType()) {
    1.82 +              case EbtFloat:
    1.83 +                mUsesFloatLoopIndex = true;
    1.84 +                break;
    1.85 +              case EbtInt:
    1.86 +                mUsesIntLoopIndex = true;
    1.87 +                MarkLoopForUnroll(symbol, mLoopStack);
    1.88 +                break;
    1.89 +              default:
    1.90 +                UNREACHABLE();
    1.91 +            }
    1.92 +        }
    1.93 +    }
    1.94 +
    1.95 +private:
    1.96 +    bool mUsesFloatLoopIndex;
    1.97 +    bool mUsesIntLoopIndex;
    1.98 +    TLoopStack& mLoopStack;
    1.99 +};
   1.100 +}  // namespace
   1.101 +
   1.102 +ValidateLimitations::ValidateLimitations(ShShaderType shaderType,
   1.103 +                                         TInfoSinkBase& sink)
   1.104 +    : mShaderType(shaderType),
   1.105 +      mSink(sink),
   1.106 +      mNumErrors(0)
   1.107 +{
   1.108 +}
   1.109 +
   1.110 +bool ValidateLimitations::visitBinary(Visit, TIntermBinary* node)
   1.111 +{
   1.112 +    // Check if loop index is modified in the loop body.
   1.113 +    validateOperation(node, node->getLeft());
   1.114 +
   1.115 +    // Check indexing.
   1.116 +    switch (node->getOp()) {
   1.117 +      case EOpIndexDirect:
   1.118 +        validateIndexing(node);
   1.119 +        break;
   1.120 +      case EOpIndexIndirect:
   1.121 +#if defined(__APPLE__)
   1.122 +        // Loop unrolling is a work-around for a Mac Cg compiler bug where it
   1.123 +        // crashes when a sampler array's index is also the loop index.
   1.124 +        // Once Apple fixes this bug, we should remove the code in this CL.
   1.125 +        // See http://codereview.appspot.com/4331048/.
   1.126 +        if ((node->getLeft() != NULL) && (node->getRight() != NULL) &&
   1.127 +            (node->getLeft()->getAsSymbolNode())) {
   1.128 +            TIntermSymbol* symbol = node->getLeft()->getAsSymbolNode();
   1.129 +            if (IsSampler(symbol->getBasicType()) && symbol->isArray()) {
   1.130 +                ValidateLoopIndexExpr validate(mLoopStack);
   1.131 +                node->getRight()->traverse(&validate);
   1.132 +                if (validate.usesFloatLoopIndex()) {
   1.133 +                    error(node->getLine(),
   1.134 +                          "sampler array index is float loop index",
   1.135 +                          "for");
   1.136 +                }
   1.137 +            }
   1.138 +        }
   1.139 +#endif
   1.140 +        validateIndexing(node);
   1.141 +        break;
   1.142 +      default: break;
   1.143 +    }
   1.144 +    return true;
   1.145 +}
   1.146 +
   1.147 +bool ValidateLimitations::visitUnary(Visit, TIntermUnary* node)
   1.148 +{
   1.149 +    // Check if loop index is modified in the loop body.
   1.150 +    validateOperation(node, node->getOperand());
   1.151 +
   1.152 +    return true;
   1.153 +}
   1.154 +
   1.155 +bool ValidateLimitations::visitAggregate(Visit, TIntermAggregate* node)
   1.156 +{
   1.157 +    switch (node->getOp()) {
   1.158 +      case EOpFunctionCall:
   1.159 +        validateFunctionCall(node);
   1.160 +        break;
   1.161 +      default:
   1.162 +        break;
   1.163 +    }
   1.164 +    return true;
   1.165 +}
   1.166 +
   1.167 +bool ValidateLimitations::visitLoop(Visit, TIntermLoop* node)
   1.168 +{
   1.169 +    if (!validateLoopType(node))
   1.170 +        return false;
   1.171 +
   1.172 +    TLoopInfo info;
   1.173 +    memset(&info, 0, sizeof(TLoopInfo));
   1.174 +    info.loop = node;
   1.175 +    if (!validateForLoopHeader(node, &info))
   1.176 +        return false;
   1.177 +
   1.178 +    TIntermNode* body = node->getBody();
   1.179 +    if (body != NULL) {
   1.180 +        mLoopStack.push_back(info);
   1.181 +        body->traverse(this);
   1.182 +        mLoopStack.pop_back();
   1.183 +    }
   1.184 +
   1.185 +    // The loop is fully processed - no need to visit children.
   1.186 +    return false;
   1.187 +}
   1.188 +
   1.189 +void ValidateLimitations::error(TSourceLoc loc,
   1.190 +                                const char *reason, const char* token)
   1.191 +{
   1.192 +    mSink.prefix(EPrefixError);
   1.193 +    mSink.location(loc);
   1.194 +    mSink << "'" << token << "' : " << reason << "\n";
   1.195 +    ++mNumErrors;
   1.196 +}
   1.197 +
   1.198 +bool ValidateLimitations::withinLoopBody() const
   1.199 +{
   1.200 +    return !mLoopStack.empty();
   1.201 +}
   1.202 +
   1.203 +bool ValidateLimitations::isLoopIndex(const TIntermSymbol* symbol) const
   1.204 +{
   1.205 +    return IsLoopIndex(symbol, mLoopStack);
   1.206 +}
   1.207 +
   1.208 +bool ValidateLimitations::validateLoopType(TIntermLoop* node) {
   1.209 +    TLoopType type = node->getType();
   1.210 +    if (type == ELoopFor)
   1.211 +        return true;
   1.212 +
   1.213 +    // Reject while and do-while loops.
   1.214 +    error(node->getLine(),
   1.215 +          "This type of loop is not allowed",
   1.216 +          type == ELoopWhile ? "while" : "do");
   1.217 +    return false;
   1.218 +}
   1.219 +
   1.220 +bool ValidateLimitations::validateForLoopHeader(TIntermLoop* node,
   1.221 +                                                TLoopInfo* info)
   1.222 +{
   1.223 +    ASSERT(node->getType() == ELoopFor);
   1.224 +
   1.225 +    //
   1.226 +    // The for statement has the form:
   1.227 +    //    for ( init-declaration ; condition ; expression ) statement
   1.228 +    //
   1.229 +    if (!validateForLoopInit(node, info))
   1.230 +        return false;
   1.231 +    if (!validateForLoopCond(node, info))
   1.232 +        return false;
   1.233 +    if (!validateForLoopExpr(node, info))
   1.234 +        return false;
   1.235 +
   1.236 +    return true;
   1.237 +}
   1.238 +
   1.239 +bool ValidateLimitations::validateForLoopInit(TIntermLoop* node,
   1.240 +                                              TLoopInfo* info)
   1.241 +{
   1.242 +    TIntermNode* init = node->getInit();
   1.243 +    if (init == NULL) {
   1.244 +        error(node->getLine(), "Missing init declaration", "for");
   1.245 +        return false;
   1.246 +    }
   1.247 +
   1.248 +    //
   1.249 +    // init-declaration has the form:
   1.250 +    //     type-specifier identifier = constant-expression
   1.251 +    //
   1.252 +    TIntermAggregate* decl = init->getAsAggregate();
   1.253 +    if ((decl == NULL) || (decl->getOp() != EOpDeclaration)) {
   1.254 +        error(init->getLine(), "Invalid init declaration", "for");
   1.255 +        return false;
   1.256 +    }
   1.257 +    // To keep things simple do not allow declaration list.
   1.258 +    TIntermSequence& declSeq = decl->getSequence();
   1.259 +    if (declSeq.size() != 1) {
   1.260 +        error(decl->getLine(), "Invalid init declaration", "for");
   1.261 +        return false;
   1.262 +    }
   1.263 +    TIntermBinary* declInit = declSeq[0]->getAsBinaryNode();
   1.264 +    if ((declInit == NULL) || (declInit->getOp() != EOpInitialize)) {
   1.265 +        error(decl->getLine(), "Invalid init declaration", "for");
   1.266 +        return false;
   1.267 +    }
   1.268 +    TIntermSymbol* symbol = declInit->getLeft()->getAsSymbolNode();
   1.269 +    if (symbol == NULL) {
   1.270 +        error(declInit->getLine(), "Invalid init declaration", "for");
   1.271 +        return false;
   1.272 +    }
   1.273 +    // The loop index has type int or float.
   1.274 +    TBasicType type = symbol->getBasicType();
   1.275 +    if ((type != EbtInt) && (type != EbtFloat)) {
   1.276 +        error(symbol->getLine(),
   1.277 +              "Invalid type for loop index", getBasicString(type));
   1.278 +        return false;
   1.279 +    }
   1.280 +    // The loop index is initialized with constant expression.
   1.281 +    if (!isConstExpr(declInit->getRight())) {
   1.282 +        error(declInit->getLine(),
   1.283 +              "Loop index cannot be initialized with non-constant expression",
   1.284 +              symbol->getSymbol().c_str());
   1.285 +        return false;
   1.286 +    }
   1.287 +
   1.288 +    info->index.id = symbol->getId();
   1.289 +    return true;
   1.290 +}
   1.291 +
   1.292 +bool ValidateLimitations::validateForLoopCond(TIntermLoop* node,
   1.293 +                                              TLoopInfo* info)
   1.294 +{
   1.295 +    TIntermNode* cond = node->getCondition();
   1.296 +    if (cond == NULL) {
   1.297 +        error(node->getLine(), "Missing condition", "for");
   1.298 +        return false;
   1.299 +    }
   1.300 +    //
   1.301 +    // condition has the form:
   1.302 +    //     loop_index relational_operator constant_expression
   1.303 +    //
   1.304 +    TIntermBinary* binOp = cond->getAsBinaryNode();
   1.305 +    if (binOp == NULL) {
   1.306 +        error(node->getLine(), "Invalid condition", "for");
   1.307 +        return false;
   1.308 +    }
   1.309 +    // Loop index should be to the left of relational operator.
   1.310 +    TIntermSymbol* symbol = binOp->getLeft()->getAsSymbolNode();
   1.311 +    if (symbol == NULL) {
   1.312 +        error(binOp->getLine(), "Invalid condition", "for");
   1.313 +        return false;
   1.314 +    }
   1.315 +    if (symbol->getId() != info->index.id) {
   1.316 +        error(symbol->getLine(),
   1.317 +              "Expected loop index", symbol->getSymbol().c_str());
   1.318 +        return false;
   1.319 +    }
   1.320 +    // Relational operator is one of: > >= < <= == or !=.
   1.321 +    switch (binOp->getOp()) {
   1.322 +      case EOpEqual:
   1.323 +      case EOpNotEqual:
   1.324 +      case EOpLessThan:
   1.325 +      case EOpGreaterThan:
   1.326 +      case EOpLessThanEqual:
   1.327 +      case EOpGreaterThanEqual:
   1.328 +        break;
   1.329 +      default:
   1.330 +        error(binOp->getLine(),
   1.331 +              "Invalid relational operator",
   1.332 +              getOperatorString(binOp->getOp()));
   1.333 +        break;
   1.334 +    }
   1.335 +    // Loop index must be compared with a constant.
   1.336 +    if (!isConstExpr(binOp->getRight())) {
   1.337 +        error(binOp->getLine(),
   1.338 +              "Loop index cannot be compared with non-constant expression",
   1.339 +              symbol->getSymbol().c_str());
   1.340 +        return false;
   1.341 +    }
   1.342 +
   1.343 +    return true;
   1.344 +}
   1.345 +
   1.346 +bool ValidateLimitations::validateForLoopExpr(TIntermLoop* node,
   1.347 +                                              TLoopInfo* info)
   1.348 +{
   1.349 +    TIntermNode* expr = node->getExpression();
   1.350 +    if (expr == NULL) {
   1.351 +        error(node->getLine(), "Missing expression", "for");
   1.352 +        return false;
   1.353 +    }
   1.354 +
   1.355 +    // for expression has one of the following forms:
   1.356 +    //     loop_index++
   1.357 +    //     loop_index--
   1.358 +    //     loop_index += constant_expression
   1.359 +    //     loop_index -= constant_expression
   1.360 +    //     ++loop_index
   1.361 +    //     --loop_index
   1.362 +    // The last two forms are not specified in the spec, but I am assuming
   1.363 +    // its an oversight.
   1.364 +    TIntermUnary* unOp = expr->getAsUnaryNode();
   1.365 +    TIntermBinary* binOp = unOp ? NULL : expr->getAsBinaryNode();
   1.366 +
   1.367 +    TOperator op = EOpNull;
   1.368 +    TIntermSymbol* symbol = NULL;
   1.369 +    if (unOp != NULL) {
   1.370 +        op = unOp->getOp();
   1.371 +        symbol = unOp->getOperand()->getAsSymbolNode();
   1.372 +    } else if (binOp != NULL) {
   1.373 +        op = binOp->getOp();
   1.374 +        symbol = binOp->getLeft()->getAsSymbolNode();
   1.375 +    }
   1.376 +
   1.377 +    // The operand must be loop index.
   1.378 +    if (symbol == NULL) {
   1.379 +        error(expr->getLine(), "Invalid expression", "for");
   1.380 +        return false;
   1.381 +    }
   1.382 +    if (symbol->getId() != info->index.id) {
   1.383 +        error(symbol->getLine(),
   1.384 +              "Expected loop index", symbol->getSymbol().c_str());
   1.385 +        return false;
   1.386 +    }
   1.387 +
   1.388 +    // The operator is one of: ++ -- += -=.
   1.389 +    switch (op) {
   1.390 +        case EOpPostIncrement:
   1.391 +        case EOpPostDecrement:
   1.392 +        case EOpPreIncrement:
   1.393 +        case EOpPreDecrement:
   1.394 +            ASSERT((unOp != NULL) && (binOp == NULL));
   1.395 +            break;
   1.396 +        case EOpAddAssign:
   1.397 +        case EOpSubAssign:
   1.398 +            ASSERT((unOp == NULL) && (binOp != NULL));
   1.399 +            break;
   1.400 +        default:
   1.401 +            error(expr->getLine(), "Invalid operator", getOperatorString(op));
   1.402 +            return false;
   1.403 +    }
   1.404 +
   1.405 +    // Loop index must be incremented/decremented with a constant.
   1.406 +    if (binOp != NULL) {
   1.407 +        if (!isConstExpr(binOp->getRight())) {
   1.408 +            error(binOp->getLine(),
   1.409 +                  "Loop index cannot be modified by non-constant expression",
   1.410 +                  symbol->getSymbol().c_str());
   1.411 +            return false;
   1.412 +        }
   1.413 +    }
   1.414 +
   1.415 +    return true;
   1.416 +}
   1.417 +
   1.418 +bool ValidateLimitations::validateFunctionCall(TIntermAggregate* node)
   1.419 +{
   1.420 +    ASSERT(node->getOp() == EOpFunctionCall);
   1.421 +
   1.422 +    // If not within loop body, there is nothing to check.
   1.423 +    if (!withinLoopBody())
   1.424 +        return true;
   1.425 +
   1.426 +    // List of param indices for which loop indices are used as argument.
   1.427 +    typedef std::vector<size_t> ParamIndex;
   1.428 +    ParamIndex pIndex;
   1.429 +    TIntermSequence& params = node->getSequence();
   1.430 +    for (TIntermSequence::size_type i = 0; i < params.size(); ++i) {
   1.431 +        TIntermSymbol* symbol = params[i]->getAsSymbolNode();
   1.432 +        if (symbol && isLoopIndex(symbol))
   1.433 +            pIndex.push_back(i);
   1.434 +    }
   1.435 +    // If none of the loop indices are used as arguments,
   1.436 +    // there is nothing to check.
   1.437 +    if (pIndex.empty())
   1.438 +        return true;
   1.439 +
   1.440 +    bool valid = true;
   1.441 +    TSymbolTable& symbolTable = GetGlobalParseContext()->symbolTable;
   1.442 +    TSymbol* symbol = symbolTable.find(node->getName());
   1.443 +    ASSERT(symbol && symbol->isFunction());
   1.444 +    TFunction* function = static_cast<TFunction*>(symbol);
   1.445 +    for (ParamIndex::const_iterator i = pIndex.begin();
   1.446 +         i != pIndex.end(); ++i) {
   1.447 +        const TParameter& param = function->getParam(*i);
   1.448 +        TQualifier qual = param.type->getQualifier();
   1.449 +        if ((qual == EvqOut) || (qual == EvqInOut)) {
   1.450 +            error(params[*i]->getLine(),
   1.451 +                  "Loop index cannot be used as argument to a function out or inout parameter",
   1.452 +                  params[*i]->getAsSymbolNode()->getSymbol().c_str());
   1.453 +            valid = false;
   1.454 +        }
   1.455 +    }
   1.456 +
   1.457 +    return valid;
   1.458 +}
   1.459 +
   1.460 +bool ValidateLimitations::validateOperation(TIntermOperator* node,
   1.461 +                                            TIntermNode* operand) {
   1.462 +    // Check if loop index is modified in the loop body.
   1.463 +    if (!withinLoopBody() || !node->modifiesState())
   1.464 +        return true;
   1.465 +
   1.466 +    const TIntermSymbol* symbol = operand->getAsSymbolNode();
   1.467 +    if (symbol && isLoopIndex(symbol)) {
   1.468 +        error(node->getLine(),
   1.469 +              "Loop index cannot be statically assigned to within the body of the loop",
   1.470 +              symbol->getSymbol().c_str());
   1.471 +    }
   1.472 +    return true;
   1.473 +}
   1.474 +
   1.475 +bool ValidateLimitations::isConstExpr(TIntermNode* node)
   1.476 +{
   1.477 +    ASSERT(node != NULL);
   1.478 +    return node->getAsConstantUnion() != NULL;
   1.479 +}
   1.480 +
   1.481 +bool ValidateLimitations::isConstIndexExpr(TIntermNode* node)
   1.482 +{
   1.483 +    ASSERT(node != NULL);
   1.484 +
   1.485 +    ValidateConstIndexExpr validate(mLoopStack);
   1.486 +    node->traverse(&validate);
   1.487 +    return validate.isValid();
   1.488 +}
   1.489 +
   1.490 +bool ValidateLimitations::validateIndexing(TIntermBinary* node)
   1.491 +{
   1.492 +    ASSERT((node->getOp() == EOpIndexDirect) ||
   1.493 +           (node->getOp() == EOpIndexIndirect));
   1.494 +
   1.495 +    bool valid = true;
   1.496 +    TIntermTyped* index = node->getRight();
   1.497 +    // The index expression must have integral type.
   1.498 +    if (!index->isScalar() || (index->getBasicType() != EbtInt)) {
   1.499 +        error(index->getLine(),
   1.500 +              "Index expression must have integral type",
   1.501 +              index->getCompleteString().c_str());
   1.502 +        valid = false;
   1.503 +    }
   1.504 +    // The index expession must be a constant-index-expression unless
   1.505 +    // the operand is a uniform in a vertex shader.
   1.506 +    TIntermTyped* operand = node->getLeft();
   1.507 +    bool skip = (mShaderType == SH_VERTEX_SHADER) &&
   1.508 +                (operand->getQualifier() == EvqUniform);
   1.509 +    if (!skip && !isConstIndexExpr(index)) {
   1.510 +        error(index->getLine(), "Index expression must be constant", "[]");
   1.511 +        valid = false;
   1.512 +    }
   1.513 +    return valid;
   1.514 +}
   1.515 +

mercurial