Example #1
0
// Test for and give an error if the node can't be read from.
void TParseContextBase::rValueErrorCheck(const TSourceLoc& loc, const char* op, TIntermTyped* node)
{
    if (! node)
        return;

    TIntermBinary* binaryNode = node->getAsBinaryNode();
    if (binaryNode) {
        switch(binaryNode->getOp()) {
        case EOpIndexDirect:
        case EOpIndexIndirect:
        case EOpIndexDirectStruct:
        case EOpVectorSwizzle:
        case EOpMatrixSwizzle:
            rValueErrorCheck(loc, op, binaryNode->getLeft());
        default:
            break;
        }

        return;
    }

    TIntermSymbol* symNode = node->getAsSymbolNode();
    if (symNode && symNode->getQualifier().writeonly)
        error(loc, "can't read from writeonly object: ", op, symNode->getName().c_str());
}
Example #2
0
TIntermBinary *MakeNewBinary(TOperator op, TIntermTyped *left, TIntermTyped *right, const TType &resultType)
{
    TIntermBinary *binary = new TIntermBinary(op);
    binary->setLeft(left);
    binary->setRight(right);
    binary->setType(resultType);
    return binary;
}
TIntermBinary *TIntermTraverser::createTempAssignment(TIntermTyped *rightNode)
{
    ASSERT(rightNode != nullptr);
    TIntermSymbol *tempSymbol = createTempSymbol(rightNode->getType());
    TIntermBinary *assignment = new TIntermBinary(EOpAssign);
    assignment->setLeft(tempSymbol);
    assignment->setRight(rightNode);
    assignment->setType(tempSymbol->getType());
    return assignment;
}
int ForLoopUnroll::getLoopIncrement(TIntermLoop* node)
{
    TIntermNode* expr = node->getExpression();
    ASSERT(expr != NULL);
    // for expression has one of the following forms:
    //     loop_index++
    //     loop_index--
    //     loop_index += constant_expression
    //     loop_index -= constant_expression
    //     ++loop_index
    //     --loop_index
    // The last two forms are not specified in the spec, but I am assuming
    // its an oversight.
    TIntermUnary* unOp = expr->getAsUnaryNode();
    TIntermBinary* binOp = unOp ? NULL : expr->getAsBinaryNode();

    TOperator op = EOpNull;
    TIntermConstantUnion* incrementNode = NULL;
    if (unOp != NULL) {
        op = unOp->getOp();
    } else if (binOp != NULL) {
        op = binOp->getOp();
        ASSERT(binOp->getRight() != NULL);
        incrementNode = binOp->getRight()->getAsConstantUnion();
        ASSERT(incrementNode != NULL);
    }

    int increment = 0;
    // The operator is one of: ++ -- += -=.
    switch (op) {
        case EOpPostIncrement:
        case EOpPreIncrement:
            ASSERT((unOp != NULL) && (binOp == NULL));
            increment = 1;
            break;
        case EOpPostDecrement:
        case EOpPreDecrement:
            ASSERT((unOp != NULL) && (binOp == NULL));
            increment = -1;
            break;
        case EOpAddAssign:
            ASSERT((unOp == NULL) && (binOp != NULL));
            increment = evaluateIntConstant(incrementNode);
            break;
        case EOpSubAssign:
            ASSERT((unOp == NULL) && (binOp != NULL));
            increment = - evaluateIntConstant(incrementNode);
            break;
        default:
            ASSERT(false);
    }

    return increment;
}
Example #5
0
//
// Connect two nodes through an index operator, where the left node is the base
// of an array or struct, and the right node is a direct or indirect offset.
//
// Returns the added node.
// The caller should set the type of the returned node.
//
TIntermTyped* TIntermediate::addIndex(TOperator op, TIntermTyped* base, TIntermTyped* index, const TSourceLoc& line)
{
    TIntermBinary* node = new TIntermBinary(op);
    node->setLine(line);
    node->setLeft(base);
    node->setRight(index);

    // caller should set the type

    return node;
}
TIntermAggregate *TIntermTraverser::createTempInitDeclaration(TIntermTyped *initializer, TQualifier qualifier)
{
    ASSERT(initializer != nullptr);
    TIntermSymbol *tempSymbol = createTempSymbol(initializer->getType(), qualifier);
    TIntermAggregate *tempDeclaration = new TIntermAggregate(EOpDeclaration);
    TIntermBinary *tempInit = new TIntermBinary(EOpInitialize);
    tempInit->setLeft(tempSymbol);
    tempInit->setRight(initializer);
    tempInit->setType(tempSymbol->getType());
    tempDeclaration->getSequence()->push_back(tempInit);
    return tempDeclaration;
}
// Connect two nodes through an index operator, where the left node is the base
// of an array or struct, and the right node is a direct or indirect offset.
//
// The caller should set the type of the returned node.
TIntermTyped* ir_add_index(TOperator op, TIntermTyped* base, TIntermTyped* index, TSourceLoc line)
{
	TIntermBinary* node = new TIntermBinary(op);
	if (line.line == 0)
		line = index->getLine();
	node->setLine(line);
	node->setLeft(base);
	node->setRight(index);

	// caller should set the type

	return node;
}
Example #8
0
bool ValidateLimitations::validateForLoopCond(TIntermLoop *node,
                                              int indexSymbolId)
{
    TIntermNode *cond = node->getCondition();
    if (cond == NULL)
    {
        error(node->getLine(), "Missing condition", "for");
        return false;
    }
    //
    // condition has the form:
    //     loop_index relational_operator constant_expression
    //
    TIntermBinary *binOp = cond->getAsBinaryNode();
    if (binOp == NULL)
    {
        error(node->getLine(), "Invalid condition", "for");
        return false;
    }
    // Loop index should be to the left of relational operator.
    TIntermSymbol *symbol = binOp->getLeft()->getAsSymbolNode();
    if (symbol == NULL)
    {
        error(binOp->getLine(), "Invalid condition", "for");
        return false;
    }
    if (symbol->getId() != indexSymbolId)
    {
        error(symbol->getLine(),
              "Expected loop index", symbol->getSymbol().c_str());
        return false;
    }
    // Relational operator is one of: > >= < <= == or !=.
    switch (binOp->getOp())
    {
      case EOpEqual:
      case EOpNotEqual:
      case EOpLessThan:
      case EOpGreaterThan:
      case EOpLessThanEqual:
      case EOpGreaterThanEqual:
        break;
      default:
        error(binOp->getLine(),
              "Invalid relational operator",
              GetOperatorString(binOp->getOp()));
        break;
    }
    // Loop index must be compared with a constant.
    if (!isConstExpr(binOp->getRight()))
    {
        error(binOp->getLine(),
              "Loop index cannot be compared with non-constant expression",
              symbol->getSymbol().c_str());
        return false;
    }

    return true;
}
TString ScalarizeVecAndMatConstructorArgs::createTempVariable(TIntermTyped *original)
{
    TString tempVarName = "_webgl_tmp_";
    if (original->isScalar())
    {
        tempVarName += "scalar_";
    }
    else if (original->isVector())
    {
        tempVarName += "vec_";
    }
    else
    {
        ASSERT(original->isMatrix());
        tempVarName += "mat_";
    }
    tempVarName += Str(mTempVarCount).c_str();
    mTempVarCount++;

    ASSERT(original);
    TType type = original->getType();
    type.setQualifier(EvqTemporary);

    if (mShaderType == GL_FRAGMENT_SHADER &&
        type.getBasicType() == EbtFloat &&
        type.getPrecision() == EbpUndefined)
    {
        // We use the highest available precision for the temporary variable
        // to avoid computing the actual precision using the rules defined
        // in GLSL ES 1.0 Section 4.5.2.
        type.setPrecision(mFragmentPrecisionHigh ? EbpHigh : EbpMedium);
    }

    TIntermBinary *init = new TIntermBinary(EOpInitialize);
    TIntermSymbol *symbolNode = new TIntermSymbol(-1, tempVarName, type);
    init->setLeft(symbolNode);
    init->setRight(original);
    init->setType(type);

    TIntermAggregate *decl = new TIntermAggregate(EOpDeclaration);
    decl->getSequence()->push_back(init);

    ASSERT(mSequenceStack.size() > 0);
    TIntermSequence &sequence = mSequenceStack.back();
    sequence.push_back(decl);

    return tempVarName;
}
Example #10
0
//
// Connect two nodes through an index operator, where the left node is the base
// of an array or struct, and the right node is a direct or indirect offset.
//
// Returns the added node.
// The caller should set the type of the returned node.
//
TIntermTyped *TIntermediate::addIndex(TOperator op,
                                      TIntermTyped *base,
                                      TIntermTyped *index,
                                      const TSourceLoc &line,
                                      TDiagnostics *diagnostics)
{
    TIntermBinary *node = new TIntermBinary(op, base, index);
    node->setLine(line);

    TIntermTyped *folded = node->fold(diagnostics);
    if (folded)
    {
        return folded;
    }

    return node;
}
Example #11
0
bool FlagStd140Structs::isInStd140InterfaceBlock(TIntermTyped *node) const
{
    TIntermBinary *binaryNode = node->getAsBinaryNode();

    if (binaryNode)
    {
        return isInStd140InterfaceBlock(binaryNode->getLeft());
    }

    const TType &type = node->getType();

    // determine if we are in the standard layout
    const TInterfaceBlock *interfaceBlock = type.getInterfaceBlock();
    if (interfaceBlock)
    {
        return (interfaceBlock->blockStorage() == EbsStd140);
    }

    return false;
}
Example #12
0
//
// Connect two nodes through an assignment.
//
// Returns the added node.
//
TIntermTyped* TIntermediate::addAssign(TOperator op, TIntermTyped* left, TIntermTyped* right, const TSourceLoc& line)
{
    //
    // Like adding binary math, except the conversion can only go
    // from right to left.
    //
    TIntermBinary* node = new TIntermBinary(op);
    node->setLine(line);

    TIntermTyped* child = addConversion(op, left->getType(), right);
    if (child == 0)
        return 0;

    node->setLeft(left);
    node->setRight(child);
    if (! node->promote(infoSink))
        return 0;

    return node;
}
Example #13
0
bool IntermNodePatternMatcher::match(TIntermAggregate *node, TIntermNode *parentNode)
{
    if ((mMask & kExpressionReturningArray) != 0)
    {
        if (parentNode != nullptr)
        {
            TIntermBinary *parentBinary = parentNode->getAsBinaryNode();
            bool parentIsAssignment =
                (parentBinary != nullptr &&
                 (parentBinary->getOp() == EOpAssign || parentBinary->getOp() == EOpInitialize));

            if (node->getType().isArray() && !parentIsAssignment &&
                (node->isConstructor() || node->isFunctionCall()) && !parentNode->getAsBlock())
            {
                return true;
            }
        }
    }
    return false;
}
Example #14
0
//
// Connect two nodes through an assignment.
//
// Returns the added node.
//
TIntermTyped *TIntermediate::addAssign(
    TOperator op, TIntermTyped *left, TIntermTyped *right, const TSourceLoc &line)
{
    if (left->getType().getStruct() || right->getType().getStruct())
    {
        if (left->getType() != right->getType())
        {
            return NULL;
        }
    }

    TIntermBinary *node = new TIntermBinary(op);
    node->setLine(line);

    node->setLeft(left);
    node->setRight(right);
    if (!node->promote(mInfoSink))
        return NULL;

    return node;
}
Example #15
0
void TLoopIndexInfo::fillInfo(TIntermLoop *node)
{
    if (node == NULL)
        return;

    // Here we assume all the operations are valid, because the loop node is
    // already validated in ValidateLimitations.
    TIntermSequence *declSeq =
        node->getInit()->getAsAggregate()->getSequence();
    TIntermBinary *declInit = (*declSeq)[0]->getAsBinaryNode();
    TIntermSymbol *symbol = declInit->getLeft()->getAsSymbolNode();

    mId = symbol->getId();
    mType = symbol->getBasicType();

    if (mType == EbtInt)
    {
        TIntermConstantUnion* initNode = declInit->getRight()->getAsConstantUnion();
        mInitValue = EvaluateIntConstant(initNode);
        mCurrentValue = mInitValue;
        mIncrementValue = GetLoopIntIncrement(node);

        TIntermBinary* binOp = node->getCondition()->getAsBinaryNode();
        mStopValue = EvaluateIntConstant(
            binOp->getRight()->getAsConstantUnion());
        mOp = binOp->getOp();
    }
}
Example #16
0
//
// Connect two nodes with a new parent that does a binary operation on the nodes.
//
// Returns the added node.
//
TIntermTyped *TIntermediate::addBinaryMath(
    TOperator op, TIntermTyped *left, TIntermTyped *right, const TSourceLoc &line)
{
    //
    // Need a new node holding things together then.  Make
    // one and promote it to the right type.
    //
    TIntermBinary *node = new TIntermBinary(op);
    node->setLine(line);

    node->setLeft(left);
    node->setRight(right);
    if (!node->promote(mInfoSink))
        return NULL;

    //
    // See if we can fold constants.
    //
    TIntermConstantUnion *leftTempConstant = left->getAsConstantUnion();
    TIntermConstantUnion *rightTempConstant = right->getAsConstantUnion();
    if (leftTempConstant && rightTempConstant)
    {
        TIntermTyped *typedReturnNode =
            leftTempConstant->fold(node->getOp(), rightTempConstant, mInfoSink);

        if (typedReturnNode)
            return typedReturnNode;
    }

    return node;
}
Example #17
0
// Check if the tree starting at node corresponds to exp2(y * log2(x))
// If the tree matches, set base to the node corresponding to x.
bool IsPowWorkaround(TIntermNode *node, TIntermNode **base)
{
    TIntermUnary *exp = node->getAsUnaryNode();
    if (exp != nullptr && exp->getOp() == EOpExp2)
    {
        TIntermBinary *mul = exp->getOperand()->getAsBinaryNode();
        if (mul != nullptr && mul->isMultiplication())
        {
            TIntermUnary *log = mul->getRight()->getAsUnaryNode();
            if (mul->getLeft()->getAsConstantUnion() && log != nullptr)
            {
                if (log->getOp() == EOpLog2)
                {
                    if (base)
                        *base = log->getOperand();
                    return true;
                }
            }
        }
    }
    return false;
}
Example #18
0
// Connect two nodes through an assignment.
TIntermTyped* ir_add_assign(TOperator op, TIntermTyped* left, TIntermTyped* right, TSourceLoc line, TParseContext& ctx)
{
   //
   // Like adding binary math, except the conversion can only go
   // from right to left.
   //
   TIntermBinary* node = new TIntermBinary(op);
   if (line.line == 0)
      line = left->getLine();
   node->setLine(line);

   TIntermTyped* child = ir_add_conversion(op, left->getType(), right, ctx.infoSink);
   if (child == 0)
      return 0;

   node->setLeft(left);
   node->setRight(child);
   if (! node->promote(ctx))
      return 0;

   return node;
}
Example #19
0
void TSamplerTraverser::typeSampler( TIntermTyped *node, TBasicType samp )
{
   TIntermSymbol *symNode = node->getAsSymbolNode();

   if ( !symNode)
   {
      //TODO: add logic to handle sampler arrays and samplers as struct members

      //Don't try typing this one, it is a complex expression
      TIntermBinary *biNode = node->getAsBinaryNode();

      if ( biNode )
      {
         switch (biNode->getOp())
         {
         case EOpIndexDirect:
         case EOpIndexIndirect:
            infoSink.info << "Warning: " << node->getLine() <<  ": typing of sampler arrays presently unsupported\n";
            break;

         case EOpIndexDirectStruct:
            infoSink.info << "Warning: " << node->getLine() <<  ": typing of samplers as struct members presently unsupported\n";
            break;
         }
      }
      else
      {
         infoSink.info << "Warning: " << node->getLine() <<  ": unexpected expression type for sampler, cannot type\n";
      }
      abort = false;
   }
   else
   {
      // We really have something to type, abort this traverse and activate typing
      abort = true;
      id = symNode->getId();
      sampType = samp;
   }
}
void ForLoopUnroll::FillLoopIndexInfo(TIntermLoop* node, TLoopIndexInfo& info)
{
    ASSERT(node->getType() == ELoopFor);
    ASSERT(node->getUnrollFlag());

    TIntermNode* init = node->getInit();
    ASSERT(init != NULL);
    TIntermAggregate* decl = init->getAsAggregate();
    ASSERT((decl != NULL) && (decl->getOp() == EOpDeclaration));
    TIntermSequence& declSeq = decl->getSequence();
    ASSERT(declSeq.size() == 1);
    TIntermBinary* declInit = declSeq[0]->getAsBinaryNode();
    ASSERT((declInit != NULL) && (declInit->getOp() == EOpInitialize));
    TIntermSymbol* symbol = declInit->getLeft()->getAsSymbolNode();
    ASSERT(symbol != NULL);
    ASSERT(symbol->getBasicType() == EbtInt);

    info.id = symbol->getId();

    ASSERT(declInit->getRight() != NULL);
    TIntermConstantUnion* initNode = declInit->getRight()->getAsConstantUnion();
    ASSERT(initNode != NULL);

    info.initValue = evaluateIntConstant(initNode);
    info.currentValue = info.initValue;

    TIntermNode* cond = node->getCondition();
    ASSERT(cond != NULL);
    TIntermBinary* binOp = cond->getAsBinaryNode();
    ASSERT(binOp != NULL);
    ASSERT(binOp->getRight() != NULL);
    ASSERT(binOp->getRight()->getAsConstantUnion() != NULL);

    info.incrementValue = getLoopIncrement(node);
    info.stopValue = evaluateIntConstant(
        binOp->getRight()->getAsConstantUnion());
    info.op = binOp->getOp();
}
Example #21
0
// Special case for matrix[idx1][idx2]: output as matrix[idx2][idx1]
static bool Check2DMatrixIndex (TGlslOutputTraverser* goit, std::stringstream& out, TIntermTyped* left, TIntermTyped* right)
{
	if (left->isVector() && !left->isArray())
	{
		TIntermBinary* leftBin = left->getAsBinaryNode();
		if (leftBin && (leftBin->getOp() == EOpIndexDirect || leftBin->getOp() == EOpIndexIndirect))
		{
			TIntermTyped* superLeft = leftBin->getLeft();
			TIntermTyped* superRight = leftBin->getRight();
			if (superLeft->isMatrix() && !superLeft->isArray())
			{
				superLeft->traverse (goit);
				out << "[";
				right->traverse(goit);
				out << "][";
				superRight->traverse(goit);
				out << "]";
				return true;
			}
		}
	}
	return false;
}
Example #22
0
int ValidateLimitations::validateForLoopInit(TIntermLoop *node)
{
    TIntermNode *init = node->getInit();
    if (init == NULL)
    {
        error(node->getLine(), "Missing init declaration", "for");
        return -1;
    }

    //
    // init-declaration has the form:
    //     type-specifier identifier = constant-expression
    //
    TIntermAggregate *decl = init->getAsAggregate();
    if ((decl == NULL) || (decl->getOp() != EOpDeclaration))
    {
        error(init->getLine(), "Invalid init declaration", "for");
        return -1;
    }
    // To keep things simple do not allow declaration list.
    TIntermSequence &declSeq = decl->getSequence();
    if (declSeq.size() != 1)
    {
        error(decl->getLine(), "Invalid init declaration", "for");
        return -1;
    }
    TIntermBinary *declInit = declSeq[0]->getAsBinaryNode();
    if ((declInit == NULL) || (declInit->getOp() != EOpInitialize))
    {
        error(decl->getLine(), "Invalid init declaration", "for");
        return -1;
    }
    TIntermSymbol *symbol = declInit->getLeft()->getAsSymbolNode();
    if (symbol == NULL)
    {
        error(declInit->getLine(), "Invalid init declaration", "for");
        return -1;
    }
    // The loop index has type int or float.
    TBasicType type = symbol->getBasicType();
    if ((type != EbtInt) && (type != EbtFloat))
    {
        error(symbol->getLine(),
              "Invalid type for loop index", getBasicString(type));
        return -1;
    }
    // The loop index is initialized with constant expression.
    if (!isConstExpr(declInit->getRight()))
    {
        error(declInit->getLine(),
              "Loop index cannot be initialized with non-constant expression",
              symbol->getSymbol().c_str());
        return -1;
    }

    return symbol->getId();
}
void InitializeVariables::insertInitCode(TIntermSequence *sequence)
{
    for (size_t ii = 0; ii < mVariables.size(); ++ii)
    {
        const InitVariableInfo &varInfo = mVariables[ii];

        if (varInfo.type.isArray())
        {
            for (int index = varInfo.type.getArraySize() - 1; index >= 0; --index)
            {
                TIntermBinary *assign = new TIntermBinary(EOpAssign);
                sequence->insert(sequence->begin(), assign);

                TIntermBinary *indexDirect = new TIntermBinary(EOpIndexDirect);
                TIntermSymbol *symbol = new TIntermSymbol(0, varInfo.name, varInfo.type);
                indexDirect->setLeft(symbol);
                TIntermConstantUnion *indexNode = constructIndexNode(index);
                indexDirect->setRight(indexNode);

                assign->setLeft(indexDirect);

                TIntermConstantUnion *zeroConst = constructFloatConstUnionNode(varInfo.type);
                assign->setRight(zeroConst);
            }
        }
        else
        {
            TIntermBinary *assign = new TIntermBinary(EOpAssign);
            sequence->insert(sequence->begin(), assign);
            TIntermSymbol *symbol = new TIntermSymbol(0, varInfo.name, varInfo.type);
            assign->setLeft(symbol);
            TIntermConstantUnion *zeroConst = constructFloatConstUnionNode(varInfo.type);
            assign->setRight(zeroConst);
        }

    }
}
Example #24
0
//
// Connect two nodes with a new parent that does a binary operation on the nodes.
//
// Returns the added node.
//
TIntermTyped *TIntermediate::addBinaryMath(
    TOperator op, TIntermTyped *left, TIntermTyped *right, const TSourceLoc &line)
{
    //
    // Need a new node holding things together then.  Make
    // one and promote it to the right type.
    //
    TIntermBinary *node = new TIntermBinary(op);
    node->setLine(line);

    node->setLeft(left);
    node->setRight(right);
    if (!node->promote(mInfoSink))
        return NULL;

    // See if we can fold constants.
    TIntermTyped *foldedNode = node->fold(mInfoSink);
    if (foldedNode)
        return foldedNode;

    return node;
}
Example #25
0
//
// Connect two nodes with a new parent that does a binary operation on the nodes.
//
// Returns the added node.
//
TIntermTyped *TIntermediate::addBinaryMath(
    TOperator op, TIntermTyped *left, TIntermTyped *right, const TSourceLoc &line)
{
    switch (op)
    {
      case EOpEqual:
      case EOpNotEqual:
        if (left->isArray())
            return NULL;
        break;
      case EOpLessThan:
      case EOpGreaterThan:
      case EOpLessThanEqual:
      case EOpGreaterThanEqual:
        if (left->isMatrix() || left->isArray() || left->isVector() ||
            left->getBasicType() == EbtStruct)
        {
            return NULL;
        }
        break;
      case EOpLogicalOr:
      case EOpLogicalXor:
      case EOpLogicalAnd:
        if (left->getBasicType() != EbtBool ||
            left->isMatrix() || left->isArray() || left->isVector())
        {
            return NULL;
        }
        break;
      case EOpAdd:
      case EOpSub:
      case EOpDiv:
      case EOpMul:
        if (left->getBasicType() == EbtStruct || left->getBasicType() == EbtBool)
            return NULL;
      default:
        break;
    }

    if (left->getBasicType() != right->getBasicType())
    {
        return NULL;
    }

    //
    // Need a new node holding things together then.  Make
    // one and promote it to the right type.
    //
    TIntermBinary *node = new TIntermBinary(op);
    node->setLine(line);

    node->setLeft(left);
    node->setRight(right);
    if (!node->promote(mInfoSink))
        return NULL;

    //
    // See if we can fold constants.
    //
    TIntermConstantUnion *leftTempConstant = left->getAsConstantUnion();
    TIntermConstantUnion *rightTempConstant = right->getAsConstantUnion();
    if (leftTempConstant && rightTempConstant)
    {
        TIntermTyped *typedReturnNode =
            leftTempConstant->fold(node->getOp(), rightTempConstant, mInfoSink);

        if (typedReturnNode)
            return typedReturnNode;
    }

    return node;
}
//
// Connect two nodes with a new parent that does a binary operation on the nodes.
//
// Returns the added node.
//
TIntermTyped* TIntermediate::addBinaryMath(TOperator op, TIntermTyped* left, TIntermTyped* right, TSourceLoc line, TSymbolTable& symbolTable)
{
    switch (op) {
        case EOpEqual:
        case EOpNotEqual:
            if (left->isArray())
                return 0;
            break;
        case EOpLessThan:
        case EOpGreaterThan:
        case EOpLessThanEqual:
        case EOpGreaterThanEqual:
            if (left->isMatrix() || left->isArray() || left->isVector() || left->getBasicType() == EbtStruct) {
                return 0;
            }
            break;
        case EOpLogicalOr:
        case EOpLogicalXor:
        case EOpLogicalAnd:
            if (left->getBasicType() != EbtBool || left->isMatrix() || left->isArray() || left->isVector()) {
                return 0;
            }
            break;
        case EOpAdd:
        case EOpSub:
        case EOpDiv:
        case EOpMul:
            if (left->getBasicType() == EbtStruct || left->getBasicType() == EbtBool)
                return 0;
        default: break;
    }

    //
    // First try converting the children to compatible types.
    //
    if (left->getType().getStruct() && right->getType().getStruct()) {
        if (left->getType() != right->getType())
            return 0;
    } else {
        TIntermTyped* child = addConversion(op, left->getType(), right);
        if (child)
            right = child;
        else {
            child = addConversion(op, right->getType(), left);
            if (child)
                left = child;
            else
                return 0;
        }
    }

    //
    // Need a new node holding things together then.  Make
    // one and promote it to the right type.
    //
    TIntermBinary* node = new TIntermBinary(op);
    if (line == 0)
        line = right->getLine();
    node->setLine(line);

    node->setLeft(left);
    node->setRight(right);
    if (!node->promote(infoSink))
        return 0;

    //
    // See if we can fold constants.
    //
    TIntermTyped* typedReturnNode = 0;
    TIntermConstantUnion *leftTempConstant = left->getAsConstantUnion();
    TIntermConstantUnion *rightTempConstant = right->getAsConstantUnion();
    if (leftTempConstant && rightTempConstant) {
        typedReturnNode = leftTempConstant->fold(node->getOp(), rightTempConstant, infoSink);

        if (typedReturnNode)
            return typedReturnNode;
    }

    return node;
}
Example #27
0
bool TGlslOutputTraverser::traverseBinary( bool preVisit, TIntermBinary *node, TIntermTraverser *it )
{
   TString op = "??";
   TGlslOutputTraverser* goit = static_cast<TGlslOutputTraverser*>(it);
   GlslFunction *current = goit->current;
   std::stringstream& out = current->getActiveOutput();
   bool infix = true;
   bool assign = false;
   bool needsParens = true;

   switch (node->getOp())
   {
   case EOpAssign:                   op = "=";   infix = true; needsParens = false; break;
   case EOpAddAssign:                op = "+=";  infix = true; needsParens = false; break;
   case EOpSubAssign:                op = "-=";  infix = true; needsParens = false; break;
   case EOpMulAssign:                op = "*=";  infix = true; needsParens = false; break;
   case EOpVectorTimesMatrixAssign:  op = "*=";  infix = true; needsParens = false; break;
   case EOpVectorTimesScalarAssign:  op = "*=";  infix = true; needsParens = false; break;
   case EOpMatrixTimesScalarAssign:  op = "*=";  infix = true; needsParens = false; break;
   case EOpMatrixTimesMatrixAssign:  op = "*=";  infix = true; needsParens = false; break;
   case EOpDivAssign:                op = "/=";  infix = true; needsParens = false; break;
   case EOpModAssign:                op = "%=";  infix = true; needsParens = false; break;
   case EOpAndAssign:                op = "&=";  infix = true; needsParens = false; break;
   case EOpInclusiveOrAssign:        op = "|=";  infix = true; needsParens = false; break;
   case EOpExclusiveOrAssign:        op = "^=";  infix = true; needsParens = false; break;
   case EOpLeftShiftAssign:          op = "<<="; infix = true; needsParens = false; break;
   case EOpRightShiftAssign:         op = "??="; infix = true; needsParens = false; break;

   case EOpIndexDirect:
      {
         TIntermTyped *left = node->getLeft();
         TIntermTyped *right = node->getRight();
         assert( left && right);

         current->beginStatement();

		 if (Check2DMatrixIndex (goit, out, left, right))
			 return false;

		 if (left->isMatrix() && !left->isArray())
		 {
			 if (right->getAsConstant())
			 {
				 current->addLibFunction (EOpMatrixIndex);
				 out << "xll_matrixindex (";
				 left->traverse(goit);
				 out << ", ";
				 right->traverse(goit);
				 out << ")";
				 return false;
			 }
			 else
			 {
				 current->addLibFunction (EOpTranspose);
				 current->addLibFunction (EOpMatrixIndex);
				 current->addLibFunction (EOpMatrixIndexDynamic);
				 out << "xll_matrixindexdynamic (";
				 left->traverse(goit);
				 out << ", ";
				 right->traverse(goit);
				 out << ")";
				 return false;
			 }
		 }

         left->traverse(goit);

         // Special code for handling a vector component select (this improves readability)
         if (left->isVector() && !left->isArray() && right->getAsConstant())
         {
            char swiz[] = "xyzw";
            goit->visitConstantUnion = TGlslOutputTraverser::traverseImmediateConstant;
            goit->generatingCode = false;
            right->traverse(goit);
            assert( goit->indexList.size() == 1);
            assert( goit->indexList[0] < 4);
            out << "." << swiz[goit->indexList[0]];
            goit->indexList.clear();
            goit->visitConstantUnion = TGlslOutputTraverser::traverseConstantUnion;
            goit->generatingCode = true;
         }
         else
         {
            out << "[";
            right->traverse(goit);
            out << "]";
         }
         return false;
      }
   case EOpIndexIndirect:
      {
      TIntermTyped *left = node->getLeft();
      TIntermTyped *right = node->getRight();
      current->beginStatement();

	  if (Check2DMatrixIndex (goit, out, left, right))
		  return false;

	  if (left && right && left->isMatrix() && !left->isArray())
	  {
		  if (right->getAsConstant())
		  {
			  current->addLibFunction (EOpMatrixIndex);
			  out << "xll_matrixindex (";
			  left->traverse(goit);
			  out << ", ";
			  right->traverse(goit);
			  out << ")";
			  return false;
		  }
		  else
		  {
			  current->addLibFunction (EOpTranspose);
			  current->addLibFunction (EOpMatrixIndex);
			  current->addLibFunction (EOpMatrixIndexDynamic);
			  out << "xll_matrixindexdynamic (";
			  left->traverse(goit);
			  out << ", ";
			  right->traverse(goit);
			  out << ")";
			  return false;
		  }
	  }

      if (left)
         left->traverse(goit);
      out << "[";
      if (right)
         right->traverse(goit);
      out << "]";
      return false;
	  }

   case EOpIndexDirectStruct:
      {
         current->beginStatement();
         GlslStruct *s = goit->createStructFromType(node->getLeft()->getTypePointer());
         if (node->getLeft())
            node->getLeft()->traverse(goit);

         // The right child is always an offset into the struct, switch to get an
         // immediate constant, and put it back afterwords
         goit->visitConstantUnion = TGlslOutputTraverser::traverseImmediateConstant;
         goit->generatingCode = false;

         if (node->getRight())
         {
            node->getRight()->traverse(goit);
            assert( goit->indexList.size() == 1);
            assert( goit->indexList[0] < s->memberCount());
            out << "." << s->getMember(goit->indexList[0]).name;

         }

         goit->indexList.clear();
         goit->visitConstantUnion = TGlslOutputTraverser::traverseConstantUnion;
         goit->generatingCode = true;
      }
      return false;

   case EOpVectorSwizzle:
      current->beginStatement();
      if (node->getLeft())
         node->getLeft()->traverse(goit);
      goit->visitConstantUnion = TGlslOutputTraverser::traverseImmediateConstant;
      goit->generatingCode = false;
      if (node->getRight())
      {
         node->getRight()->traverse(goit);
         assert( goit->indexList.size() <= 4);
         out << '.';
         const char fields[] = "xyzw";
         for (int ii = 0; ii < (int)goit->indexList.size(); ii++)
         {
            int val = goit->indexList[ii];
            assert( val >= 0);
            assert( val < 4);
            out << fields[val];
         }
      }
      goit->indexList.clear();
      goit->visitConstantUnion = TGlslOutputTraverser::traverseConstantUnion;
      goit->generatingCode = true;
      return false;

	case EOpMatrixSwizzle:		   
		// This presently only works for swizzles as rhs operators
		if (node->getRight())
		{
			goit->visitConstantUnion = TGlslOutputTraverser::traverseImmediateConstant;
			goit->generatingCode = false;

			node->getRight()->traverse(goit);

			goit->visitConstantUnion = TGlslOutputTraverser::traverseConstantUnion;
			goit->generatingCode = true;

			std::vector<int> elements = goit->indexList;
			goit->indexList.clear();
			
			if (elements.size() > 4 || elements.size() < 1) {
				goit->infoSink.info << "Matrix swizzle operations can must contain at least 1 and at most 4 element selectors.";
				return true;
			}

			unsigned column[4] = {0}, row[4] = {0};
			for (unsigned i = 0; i != elements.size(); ++i)
			{
				unsigned val = elements[i];
				column[i] = val % 4;
				row[i] = val / 4;
			}

			bool sameColumn = true;
			for (unsigned i = 1; i != elements.size(); ++i)
				sameColumn &= column[i] == column[i-1];

			static const char* fields = "xyzw";
			
			if (sameColumn)
			{				
				//select column, then swizzle row
				if (node->getLeft())
					node->getLeft()->traverse(goit);
				out << "[" << column[0] << "].";
				
				for (unsigned i = 0; i < elements.size(); ++i)
					out << fields[row[i]];
			}
			else
			{
				// Insert constructor, and dereference individually

				// Might need to account for different types here 
				assert( elements.size() != 1); //should have hit same collumn case
				out << "vec" << elements.size() << "(";
				if (node->getLeft())
					node->getLeft()->traverse(goit);
				out << "[" << column[0] << "].";
				out << fields[row[0]];
				
				for (unsigned i = 1; i < elements.size(); ++i)
				{
					out << ", ";
					if (node->getLeft())
						node->getLeft()->traverse(goit);
					out << "[" << column[i] << "].";
					out << fields[row[i]];
				}
				out << ")";
			}
		}
		return false;

   case EOpAdd:    op = "+"; infix = true; break;
   case EOpSub:    op = "-"; infix = true; break;
   case EOpMul:    op = "*"; infix = true; break;
   case EOpDiv:    op = "/"; infix = true; break;
   case EOpMod:    op = "mod"; infix = false; break;
   case EOpRightShift:  op = "<<"; infix = true;
		   break;
   case EOpLeftShift:   op = ">>"; infix = true; break;
   case EOpAnd:         op = "&"; infix = true; break;
   case EOpInclusiveOr: op = "|"; infix = true; break;
   case EOpExclusiveOr: op = "^"; infix = true; break;
   case EOpEqual:       
      writeComparison ( "==", "equal", node, goit );
      return false;        

   case EOpNotEqual:        
      writeComparison ( "!=", "notEqual", node, goit );
      return false;               

   case EOpLessThan: 
      writeComparison ( "<", "lessThan", node, goit );
      return false;               

   case EOpGreaterThan:
      writeComparison ( ">", "greaterThan", node, goit );
      return false;               

   case EOpLessThanEqual:    
      writeComparison ( "<=", "lessThanEqual", node, goit );
      return false;               


   case EOpGreaterThanEqual: 
      writeComparison ( ">=", "greaterThanEqual", node, goit );
      return false;               


   case EOpVectorTimesScalar: op = "*"; infix = true; break;
   case EOpVectorTimesMatrix: op = "*"; infix = true; break;
   case EOpMatrixTimesVector: op = "*"; infix = true; break;
   case EOpMatrixTimesScalar: op = "*"; infix = true; break;
   case EOpMatrixTimesMatrix: op = "*"; infix = true; break;

   case EOpLogicalOr:  op = "||"; infix = true; break;
   case EOpLogicalXor: op = "^^"; infix = true; break;
   case EOpLogicalAnd: op = "&&"; infix = true; break;
   default: assert(0);
   }

   current->beginStatement();

   if (infix)
   {
	   // special case for swizzled matrix assignment
	   if (node->getOp() == EOpAssign && node->getLeft() && node->getRight()) {
		   TIntermBinary* lval = node->getLeft()->getAsBinaryNode();
		   
		   if (lval && lval->getOp() == EOpMatrixSwizzle) {
			   static const char* vec_swizzles = "xyzw";
			   TIntermTyped* rval = node->getRight();
			   TIntermTyped* lexp = lval->getLeft();
			   
			   goit->visitConstantUnion = TGlslOutputTraverser::traverseImmediateConstant;
			   goit->generatingCode = false;
			   
			   lval->getRight()->traverse(goit);
			   
			   goit->visitConstantUnion = TGlslOutputTraverser::traverseConstantUnion;
			   goit->generatingCode = true;
			   
			   std::vector<int> swizzles = goit->indexList;
			   goit->indexList.clear();
			   
			   char temp_rval[128];
			   unsigned n_swizzles = swizzles.size();
			   
			   if (n_swizzles > 1) {
				   snprintf(temp_rval, 128, "xlat_swiztemp%d", goit->swizzleAssignTempCounter++);
				   
				   current->beginStatement();
				   out << "vec" << n_swizzles << " " << temp_rval << " = ";
				   rval->traverse(goit);			   
				   current->endStatement();
			   }
			   
			   for (unsigned i = 0; i != n_swizzles; ++i) {
				   unsigned col = swizzles[i] / 4;
				   unsigned row = swizzles[i] % 4;
				   
				   current->beginStatement();
				   lexp->traverse(goit);
				   out << "[" << row << "][" << col << "] = ";
				   if (n_swizzles > 1)
					   out << temp_rval << "." << vec_swizzles[i];
				   else
					   rval->traverse(goit);
				   
				   current->endStatement();
			   }

			   return false;
		   }
	   }

      if (needsParens)
         out << '(';

      if (node->getLeft())
         node->getLeft()->traverse(goit);
      out << ' ' << op << ' ';
      if (node->getRight())
         node->getRight()->traverse(goit);

      if (needsParens)
         out << ')';
   }
   else
   {
      if (assign)
      {		  
         // Need to traverse the left child twice to allow for the assign and the op
         // This is OK, because we know it is an lvalue
         if (node->getLeft())
            node->getLeft()->traverse(goit);

         out << " = " << op << '(';

         if (node->getLeft())
            node->getLeft()->traverse(goit);
         out << ", ";
         if (node->getRight())
            node->getRight()->traverse(goit);

         out << ')';
      }
      else
      {
         out << op << '(';

         if (node->getLeft())
            node->getLeft()->traverse(goit);
         out << ", ";
         if (node->getRight())
            node->getRight()->traverse(goit);

         out << ')';
      }
   }

   return false;
}
Example #28
0
// Connect two nodes with a new parent that does a binary operation on the nodes.
TIntermTyped* ir_add_binary_math(TOperator op, TIntermTyped* left, TIntermTyped* right, TSourceLoc line, TParseContext& ctx)
{
	if (!left || !right)
		return 0;
	
	switch (op)
	{
	case EOpLessThan:
	case EOpGreaterThan:
	case EOpLessThanEqual:
	case EOpGreaterThanEqual:
		if (left->getType().isMatrix() || left->getType().isArray() || left->getType().getBasicType() == EbtStruct)
		{
			return 0;
		}
		break;
	case EOpLogicalOr:
	case EOpLogicalXor:
	case EOpLogicalAnd:
		if (left->getType().isMatrix() || left->getType().isArray())
			return 0;
		
		if ( left->getBasicType() != EbtBool )
		{
			if ( left->getType().getBasicType() != EbtInt && left->getType().getBasicType() != EbtFloat )
				return 0;
			else
			{
				// If the left is a float or int, convert to a bool.  This is the conversion that HLSL
				// does
				left = ir_add_conversion(EOpConstructBool, 
									 TType ( EbtBool, left->getPrecision(), left->getQualifier(),
											left->getColsCount(), left->getRowsCount(), left->isMatrix(), left->isArray()), 
									 left, ctx.infoSink);
				
				if ( left == 0 )
					return 0;
			}
			
		}
		
		if (right->getType().isMatrix() || right->getType().isArray() || right->getType().isVector())
			return 0;
		
		if ( right->getBasicType() != EbtBool )
		{
			if ( right->getType().getBasicType() != EbtInt && right->getType().getBasicType() != EbtFloat )
				return 0;
			else
			{
				// If the right is a float or int, convert to a bool.  This is the conversion that HLSL
				// does
				right = ir_add_conversion(EOpConstructBool, 
									  TType ( EbtBool, right->getPrecision(), right->getQualifier(),
											 right->getColsCount(), right->getRowsCount(), right->isMatrix(), right->isArray()), 
									  right, ctx.infoSink);
				
				if ( right == 0 )
					return 0;
			}
			
		}
		break;
	case EOpAdd:
	case EOpSub:
	case EOpDiv:
	case EOpMul:
	case EOpMod:
		{
			TBasicType ltype = left->getType().getBasicType();
			TBasicType rtype = right->getType().getBasicType();
			if (ltype == EbtStruct)
				return 0;
			
			// If left or right type is a bool, convert to float.
			bool leftToFloat = (ltype == EbtBool);
			bool rightToFloat = (rtype == EbtBool);
			// For modulus, if either is an integer, convert to float as well.
			if (op == EOpMod)
			{
				leftToFloat |= (ltype == EbtInt);
				rightToFloat |= (rtype == EbtInt);
			}
				
			if (leftToFloat)
			{
				left = ir_add_conversion (EOpConstructFloat, TType (EbtFloat, left->getPrecision(), left->getQualifier(), left->getColsCount(), left->getRowsCount(), left->isMatrix(), left->isArray()), left, ctx.infoSink);
				if (left == 0)
					return 0;
			}
			if (rightToFloat)
			{
				right = ir_add_conversion (EOpConstructFloat, TType (EbtFloat, right->getPrecision(), right->getQualifier(), right->getColsCount(), right->getRowsCount(), right->isMatrix(), right->isArray()), right, ctx.infoSink);
				if (right == 0)
					return 0;
			}
		}
		break;
	default:
		break;
	}
	
	// 
	// First try converting the children to compatible types.
	//
	
	if (!(left->getType().getStruct() && right->getType().getStruct()))
	{
		TIntermTyped* child = 0;
		bool useLeft = true; //default to using the left child as the type to promote to
		
		//need to always convert up
		if ( left->getType().getBasicType() != EbtFloat)
		{
			if ( right->getType().getBasicType() == EbtFloat)
			{
				useLeft = false;
			}
			else
			{
				if ( left->getType().getBasicType() != EbtInt)
				{
					if ( right->getType().getBasicType() == EbtInt)
						useLeft = false;
				}
			}
		}
		
		if (useLeft)
		{
			child = ir_add_conversion(op, left->getType(), right, ctx.infoSink);
			if (child)
				right = child;
			else
			{
				child = ir_add_conversion(op, right->getType(), left, ctx.infoSink);
				if (child)
					left = child;
				else
					return 0;
			}
		}
		else
		{
			child = ir_add_conversion(op, right->getType(), left, ctx.infoSink);
			if (child)
				left = child;
			else
			{
				child = ir_add_conversion(op, left->getType(), right, ctx.infoSink);
				if (child)
					right = child;
				else
					return 0;
			}
		}
		
	}
	else
	{
		if (left->getType() != right->getType())
			return 0;
	}
	
	
	//
	// Need a new node holding things together then.  Make
	// one and promote it to the right type.
	//
	TIntermBinary* node = new TIntermBinary(op);
	if (line.line == 0)
		line = right->getLine();
	node->setLine(line);
	
	node->setLeft(left);
	node->setRight(right);
	if (! node->promote(ctx))
		return 0;
	
	//
	// See if we can fold constants
	
	TIntermConstant* constA = left->getAsConstant();
	TIntermConstant* constB = right->getAsConstant();
	
	if (constA && constB)
	{
		TIntermConstant* FoldBinaryConstantExpression(TOperator op, TIntermConstant* nodeA, TIntermConstant* nodeB);
		TIntermConstant* res = FoldBinaryConstantExpression(node->getOp(), constA, constB);
		if (res)
		{
			delete node;
			return res;
		}
	}
	
	return node;
}
Example #29
0
bool TGlslOutputTraverser::traverseDeclaration(bool preVisit, TIntermDeclaration* decl, TIntermTraverser* it) {
	TGlslOutputTraverser* goit = static_cast<TGlslOutputTraverser*>(it);
	GlslFunction *current = goit->current;
	std::stringstream& out = current->getActiveOutput();
	EGlslSymbolType symbol_type = translateType(decl->getTypePointer());
	TType& type = *decl->getTypePointer();
	
	
	bool emit_osx10_6_arrays = goit->m_EmitSnowLeopardCompatibleArrayInitializers
		&& decl->containsArrayInitialization();
	
	if (emit_osx10_6_arrays) {
		assert(decl->isSingleInitialization() && "Emission of multiple in-line array declarations isn't supported when running in OS X 10.6 compatible mode.");
		
		current->indent(out);
		out << "#if defined(OSX_SNOW_LEOPARD)" << std::endl;
		current->increaseDepth();
		
		TQualifier q = type.getQualifier();
		if (q == EvqConst)
			q = EvqTemporary;
		
		current->beginStatement();
		if (q != EvqTemporary && q != EvqGlobal)
			out << type.getQualifierString() << " ";
		
		TIntermBinary* assign = decl->getDeclaration()->getAsBinaryNode();
		TIntermSymbol* sym = assign->getLeft()->getAsSymbolNode();
		TIntermSequence& init = assign->getRight()->getAsAggregate()->getSequence();
		
		writeType(out, symbol_type, NULL, goit->m_UsePrecision ? decl->getPrecision() : EbpUndefined);
		out << "[" << type.getArraySize() << "] " << sym->getSymbol();
		current->endStatement();
		
		unsigned n_vals = init.size();
		for (unsigned i = 0; i != n_vals; ++i) {
			current->beginStatement();
			sym->traverse(goit);
			out << "[" << i << "]" << " = ";
			init[i]->traverse(goit);
			current->endStatement();
		}
		
		current->decreaseDepth();
		current->indent(out);
		out << "#else" << std::endl;
		current->increaseDepth();
	}
	
	current->beginStatement();
	
	if (type.getQualifier() != EvqTemporary && type.getQualifier() != EvqGlobal)
		out << type.getQualifierString() << " ";
	
	if (type.getBasicType() == EbtStruct)
		out << type.getTypeName();
	else
		writeType(out, symbol_type, NULL, goit->m_UsePrecision ? decl->getPrecision() : EbpUndefined);
	
	if (type.isArray())
		out << "[" << type.getArraySize() << "]";
	
	out << " ";
	
	decl->getDeclaration()->traverse(goit);
	
	current->endStatement();
	
	if (emit_osx10_6_arrays) {
		current->decreaseDepth();
		current->indent(out);
		out << "#endif" << std::endl;
	}
	
	return false;
}
Example #30
0
//
// Both test and if necessary, spit out an error, to see if the node is really
// an l-value that can be operated on this way.
//
// Returns true if the was an error.
//
bool TParseContext::lValueErrorCheck(int line, const char* op, TIntermTyped* node)
{
    TIntermSymbol* symNode = node->getAsSymbolNode();
    TIntermBinary* binaryNode = node->getAsBinaryNode();

    if (binaryNode) {
        bool errorReturn;

        switch(binaryNode->getOp()) {
        case EOpIndexDirect:
        case EOpIndexIndirect:
        case EOpIndexDirectStruct:
            return lValueErrorCheck(line, op, binaryNode->getLeft());
        case EOpVectorSwizzle:
            errorReturn = lValueErrorCheck(line, op, binaryNode->getLeft());
            if (!errorReturn) {
                int offset[4] = {0,0,0,0};

                TIntermTyped* rightNode = binaryNode->getRight();
                TIntermAggregate *aggrNode = rightNode->getAsAggregate();
                
                for (TIntermSequence::iterator p = aggrNode->getSequence().begin(); 
                                               p != aggrNode->getSequence().end(); p++) {
                    int value = (*p)->getAsTyped()->getAsConstantUnion()->getUnionArrayPointer()->getIConst();
                    offset[value]++;     
                    if (offset[value] > 1) {
                        error(line, " l-value of swizzle cannot have duplicate components", op, "", "");

                        return true;
                    }
                }
            } 

            return errorReturn;
        default: 
            break;
        }
        error(line, " l-value required", op, "", "");

        return true;
    }


    const char* symbol = 0;
    if (symNode != 0)
        symbol = symNode->getSymbol().c_str();

    const char* message = 0;
    switch (node->getQualifier()) {
    case EvqConst:          message = "can't modify a const";        break;
    case EvqConstReadOnly:  message = "can't modify a const";        break;
    case EvqAttribute:      message = "can't modify an attribute";   break;
    case EvqUniform:        message = "can't modify a uniform";      break;
    case EvqVaryingIn:      message = "can't modify a varying";      break;
    case EvqInput:          message = "can't modify an input";       break;
    case EvqFragCoord:      message = "can't modify gl_FragCoord";   break;
    case EvqFrontFacing:    message = "can't modify gl_FrontFacing"; break;
    case EvqPointCoord:     message = "can't modify gl_PointCoord";  break;
    default:

        //
        // Type that can't be written to?
        //
        switch (node->getBasicType()) {
        case EbtSampler2D:
        case EbtSamplerCube:
            message = "can't modify a sampler";
            break;
        case EbtVoid:
            message = "can't modify void";
            break;
        default: 
            break;
        }
    }

    if (message == 0 && binaryNode == 0 && symNode == 0) {
        error(line, " l-value required", op, "", "");

        return true;
    }


    //
    // Everything else is okay, no error.
    //
    if (message == 0)
        return false;

    //
    // If we get here, we have an error and a message.
    //
    if (symNode)
        error(line, " l-value required", op, "\"%s\" (%s)", symbol, message);
    else
        error(line, " l-value required", op, "(%s)", message);

    return true;
}