// 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()); }
// Returns name to be used in generated assembly for a given node string ARBVar::GetNodeVarName(TIntermTyped* node) { TIntermSymbol* symbol = node->getAsSymbolNode(); if (symbol) { return string("symbol_") + symbol->getSymbol().c_str(); } TIntermConstantUnion* cu = node->getAsConstantUnion(); if (cu) { string ret = ""; if (cu->getBasicType() == EbtFloat) { if (cu->getNominalSize() == 1) { ret = "const_" + floattostr(cu->getUnionArrayPointer()[0].getFConst()); } else if (cu->isVector() && cu->getNominalSize() > 1 && cu->getNominalSize() <= 4) { ret = "const_vec" + inttostr(cu->getNominalSize()); for (int i = 0; i < cu->getNominalSize(); ++i) { ret += "_" + floattostr(cu->getUnionArrayPointer()[i].getFConst()); } } } if (ret != "") { for (unsigned int i = 0; i < ret.size(); ++i) { if (ret[i] == '.') { ret[i] = 'x'; } } return ret; } } failmsg() << "Unknown node in GetNodeVarName [" << node->getCompleteString() << "]\n"; return "{unknown node}"; }
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(); } }
// // Add a terminal node for an identifier in an expression. // // Returns the added node. // TIntermSymbol* TIntermediate::addSymbol(int id, const TString& name, const TType& type, TSourceLoc line) { TIntermSymbol* node = new TIntermSymbol(id, name, type); node->setLine(line); return node; }
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; }
TIntermDeclaration* ir_grow_declaration(TIntermDeclaration* declaration, TSymbol* symbol, TIntermTyped* initializer, TInfoSink& infoSink) { TVariable* var = static_cast<TVariable*>(symbol); TIntermSymbol* sym = ir_add_symbol(var->getUniqueId(), var->getName(), var->getType(), var->getType().getLine()); sym->setGlobal(symbol->isGlobal()); return ir_grow_declaration(declaration, sym, initializer, infoSink); }
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(); }
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; }
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; }
TIntermSymbol *TIntermTraverser::createTempSymbol(const TType &type, TQualifier qualifier) { // Each traversal uses at most one temporary variable, so the index stays the same within a single traversal. TInfoSinkBase symbolNameOut; ASSERT(mTemporaryIndex != nullptr); symbolNameOut << "s" << (*mTemporaryIndex); TString symbolName = symbolNameOut.c_str(); TIntermSymbol *node = new TIntermSymbol(0, symbolName, type); node->setInternal(true); node->getTypePointer()->setQualifier(qualifier); return node; }
bool TGlslOutputTraverser::parseInitializer( TIntermBinary *node ) { TIntermTyped *left, *right; left = node->getLeft(); right = node->getRight(); if (!left->getAsSymbolNode()) return false; //Something is likely seriously wrong TIntermSymbol *symNode = left->getAsSymbolNode(); if (symNode->getBasicType() == EbtStruct) return false; GlslSymbol * sym = NULL; if ( !current->hasSymbol( symNode->getId() ) ) { int array = symNode->getTypePointer()->isArray() ? symNode->getTypePointer()->getArraySize() : 0; const char* semantic = ""; if (symNode->getInfo()) semantic = symNode->getInfo()->getSemantic().c_str(); sym = new GlslSymbol( symNode->getSymbol().c_str(), semantic, symNode->getId(), translateType(symNode->getTypePointer()), m_UsePrecision?node->getPrecision():EbpUndefined, translateQualifier(symNode->getQualifier()), array); current->addSymbol(sym); } else return false; //can't init already declared variable if (right->getAsTyped()) { std::stringstream ss; std::stringstream* oldOut = ¤t->getActiveOutput(); current->pushDepth(0); current->setActiveOutput(&ss); right->getAsTyped()->traverse(this); current->setActiveOutput(oldOut); current->popDepth(); sym->setInitializer(ss.str()); } return true; }
bool ValidateLimitations::validateOperation(TIntermOperator *node, TIntermNode* operand) { // Check if loop index is modified in the loop body. if (!withinLoopBody() || !node->isAssignment()) return true; TIntermSymbol *symbol = operand->getAsSymbolNode(); if (symbol && isLoopIndex(symbol)) { error(node->getLine(), "Loop index cannot be statically assigned to within the body of the loop", symbol->getSymbol().c_str()); } return true; }
TIntermSymbol *TIntermTraverser::createTempSymbol(const TType &type, TQualifier qualifier) { // Each traversal uses at most one temporary variable, so the index stays the same within a single traversal. TInfoSinkBase symbolNameOut; ASSERT(mTemporaryIndex != nullptr); symbolNameOut << "s" << (*mTemporaryIndex); TString symbolName = symbolNameOut.c_str(); TIntermSymbol *node = new TIntermSymbol(0, symbolName, type); node->setInternal(true); ASSERT(qualifier == EvqTemporary || qualifier == EvqConst || qualifier == EvqGlobal); node->getTypePointer()->setQualifier(qualifier); // TODO(oetuaho): Might be useful to sanitize layout qualifier etc. on the type of the created // symbol. This might need to be done in other places as well. return node; }
ParsedCode Parser::buildShader(TIntermAggregate* f) { cur.pos = f->getLine().first_line; cur.args.clear(); cur.exprs.clear(); Position pos = f->getLine().first_line; TIntermSequence& sequence = f->getSequence(); TIntermSequence& arguments = sequence[0]->getAsAggregate()->getSequence(); for (auto it = arguments.begin(); it != arguments.end(); ++it) { TIntermSymbol* symbol = (*it)->getAsSymbolNode(); //if (symbol->getType().getStruct()) { // addConstructor(symbol->getType(), scopedStruct(symbol->getType().getTypeName()), NULL); //} cur.args.push_back(allocVar(symbol->getQualifierString(), symbol->getTypePointer(), VUnknown, pos)); } parseExpr(sequence[1]->getAsAggregate()->getSequence()); return cur; }
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(); }
bool ValidateLimitations::visitBinary(Visit, TIntermBinary* node) { // Check if loop index is modified in the loop body. validateOperation(node, node->getLeft()); // Check indexing. switch (node->getOp()) { case EOpIndexDirect: validateIndexing(node); break; case EOpIndexIndirect: #if defined(__APPLE__) // Loop unrolling is a work-around for a Mac Cg compiler bug where it // crashes when a sampler array's index is also the loop index. // Once Apple fixes this bug, we should remove the code in this CL. // See http://codereview.appspot.com/4331048/. if ((node->getLeft() != NULL) && (node->getRight() != NULL) && (node->getLeft()->getAsSymbolNode())) { TIntermSymbol* symbol = node->getLeft()->getAsSymbolNode(); if (IsSampler(symbol->getBasicType()) && symbol->isArray()) { ValidateLoopIndexExpr validate(mLoopStack); node->getRight()->traverse(&validate); if (validate.usesFloatLoopIndex()) { error(node->getLine(), "sampler array index is float loop index", "for"); } } } #endif validateIndexing(node); break; default: break; } return true; }
bool TOutputGLSLBase::visitLoop(Visit visit, TIntermLoop *node) { TInfoSinkBase &out = objSink(); incrementDepth(node); // Loop header. TLoopType loopType = node->getType(); if (loopType == ELoopFor) // for loop { if (!node->getUnrollFlag()) { out << "for ("; if (node->getInit()) node->getInit()->traverse(this); out << "; "; if (node->getCondition()) node->getCondition()->traverse(this); out << "; "; if (node->getExpression()) node->getExpression()->traverse(this); out << ")\n"; } else { // Need to put a one-iteration loop here to handle break. TIntermSequence *declSeq = node->getInit()->getAsAggregate()->getSequence(); TIntermSymbol *indexSymbol = (*declSeq)[0]->getAsBinaryNode()->getLeft()->getAsSymbolNode(); TString name = hashVariableName(indexSymbol->getSymbol()); out << "for (int " << name << " = 0; " << name << " < 1; " << "++" << name << ")\n"; } } else if (loopType == ELoopWhile) // while loop { out << "while ("; ASSERT(node->getCondition() != NULL); node->getCondition()->traverse(this); out << ")\n"; } else // do-while loop { ASSERT(loopType == ELoopDoWhile); out << "do\n"; } // Loop body. if (node->getUnrollFlag()) { out << "{\n"; mLoopUnrollStack.push(node); while (mLoopUnrollStack.satisfiesLoopCondition()) { visitCodeBlock(node->getBody()); mLoopUnrollStack.step(); } mLoopUnrollStack.pop(); out << "}\n"; } else { visitCodeBlock(node->getBody()); } // Loop footer. if (loopType == ELoopDoWhile) // do-while loop { out << "while ("; ASSERT(node->getCondition() != NULL); node->getCondition()->traverse(this); out << ");\n"; } decrementDepth(); // No need to visit children. They have been already processed in // this function. return false; }
TIntermSymbol* ir_add_symbol_internal(int id, const TString& name, const TTypeInfo *info, const TType& type, TSourceLoc line) { TIntermSymbol* node = new TIntermSymbol(id, name, info, type); node->setLine(line); return node; }
// Add a terminal node for an identifier in an expression. TIntermSymbol* ir_add_symbol(const TVariable* var, TSourceLoc line) { TIntermSymbol* node = ir_add_symbol_internal(var->getUniqueId(), var->getName(), var->getInfo(), var->getType(), line); node->setGlobal(var->isGlobal()); return node; }
// // Compare two global objects from two compilation units and see if they match // well enough. Rules can be different for intra- vs. cross-stage matching. // // This function only does one of intra- or cross-stage matching per call. // void TIntermediate::mergeErrorCheck(TInfoSink& infoSink, const TIntermSymbol& symbol, const TIntermSymbol& unitSymbol, bool crossStage) { bool writeTypeComparison = false; // Types have to match if (symbol.getType() != unitSymbol.getType()) { error(infoSink, "Types must match:"); writeTypeComparison = true; } // Qualifiers have to (almost) match // Storage... if (symbol.getQualifier().storage != unitSymbol.getQualifier().storage) { error(infoSink, "Storage qualifiers must match:"); writeTypeComparison = true; } // Precision... if (symbol.getQualifier().precision != unitSymbol.getQualifier().precision) { error(infoSink, "Precision qualifiers must match:"); writeTypeComparison = true; } // Invariance... if (! crossStage && symbol.getQualifier().invariant != unitSymbol.getQualifier().invariant) { error(infoSink, "Presence of invariant qualifier must match:"); writeTypeComparison = true; } // Precise... if (! crossStage && symbol.getQualifier().noContraction != unitSymbol.getQualifier().noContraction) { error(infoSink, "Presence of precise qualifier must match:"); writeTypeComparison = true; } // Auxiliary and interpolation... if (symbol.getQualifier().centroid != unitSymbol.getQualifier().centroid || symbol.getQualifier().smooth != unitSymbol.getQualifier().smooth || symbol.getQualifier().flat != unitSymbol.getQualifier().flat || symbol.getQualifier().sample != unitSymbol.getQualifier().sample || symbol.getQualifier().patch != unitSymbol.getQualifier().patch || symbol.getQualifier().nopersp != unitSymbol.getQualifier().nopersp) { error(infoSink, "Interpolation and auxiliary storage qualifiers must match:"); writeTypeComparison = true; } // Memory... if (symbol.getQualifier().coherent != unitSymbol.getQualifier().coherent || symbol.getQualifier().volatil != unitSymbol.getQualifier().volatil || symbol.getQualifier().restrict != unitSymbol.getQualifier().restrict || symbol.getQualifier().readonly != unitSymbol.getQualifier().readonly || symbol.getQualifier().writeonly != unitSymbol.getQualifier().writeonly) { error(infoSink, "Memory qualifiers must match:"); writeTypeComparison = true; } // Layouts... // TODO: 4.4 enhanced layouts: Generalize to include offset/align: current spec // requires separate user-supplied offset from actual computed offset, but // current implementation only has one offset. if (symbol.getQualifier().layoutMatrix != unitSymbol.getQualifier().layoutMatrix || symbol.getQualifier().layoutPacking != unitSymbol.getQualifier().layoutPacking || symbol.getQualifier().layoutLocation != unitSymbol.getQualifier().layoutLocation || symbol.getQualifier().layoutComponent != unitSymbol.getQualifier().layoutComponent || symbol.getQualifier().layoutIndex != unitSymbol.getQualifier().layoutIndex || symbol.getQualifier().layoutBinding != unitSymbol.getQualifier().layoutBinding || (symbol.getQualifier().hasBinding() && (symbol.getQualifier().layoutOffset != unitSymbol.getQualifier().layoutOffset))) { error(infoSink, "Layout qualification must match:"); writeTypeComparison = true; } // Initializers have to match, if both are present, and if we don't already know the types don't match if (! writeTypeComparison) { if (! symbol.getConstArray().empty() && ! unitSymbol.getConstArray().empty()) { if (symbol.getConstArray() != unitSymbol.getConstArray()) { error(infoSink, "Initializers must match:"); infoSink.info << " " << symbol.getName() << "\n"; } } } if (writeTypeComparison) infoSink.info << " " << symbol.getName() << ": \"" << symbol.getType().getCompleteString() << "\" versus \"" << unitSymbol.getType().getCompleteString() << "\"\n"; }
// // Initializers show up in several places in the grammar. Have one set of // code to handle them here. // bool TParseContext::executeInitializer(TSourceLoc line, TString& identifier, TPublicType& pType, TIntermTyped* initializer, TIntermNode*& intermNode, TVariable* variable) { TType type = TType(pType); if (variable == 0) { if (reservedErrorCheck(line, identifier)) return true; if (voidErrorCheck(line, identifier, pType)) return true; // // add variable to symbol table // variable = new TVariable(&identifier, type); if (! symbolTable.insert(*variable)) { error(line, "redefinition", variable->getName().c_str(), ""); return true; // don't delete variable, it's used by error recovery, and the pool // pop will take care of the memory } } // // identifier must be of type constant, a global, or a temporary // TQualifier qualifier = variable->getType().getQualifier(); if ((qualifier != EvqTemporary) && (qualifier != EvqGlobal) && (qualifier != EvqConst)) { error(line, " cannot initialize this type of qualifier ", variable->getType().getQualifierString(), ""); return true; } // // test for and propagate constant // if (qualifier == EvqConst) { if (qualifier != initializer->getType().getQualifier()) { error(line, " assigning non-constant to", "=", "'%s'", variable->getType().getCompleteString().c_str()); variable->getType().setQualifier(EvqTemporary); return true; } if (type != initializer->getType()) { error(line, " non-matching types for const initializer ", variable->getType().getQualifierString(), ""); variable->getType().setQualifier(EvqTemporary); return true; } if (initializer->getAsConstantUnion()) { ConstantUnion* unionArray = variable->getConstPointer(); if (type.getObjectSize() == 1 && type.getBasicType() != EbtStruct) { *unionArray = (initializer->getAsConstantUnion()->getUnionArrayPointer())[0]; } else { variable->shareConstPointer(initializer->getAsConstantUnion()->getUnionArrayPointer()); } } else if (initializer->getAsSymbolNode()) { const TSymbol* symbol = symbolTable.find(initializer->getAsSymbolNode()->getSymbol()); const TVariable* tVar = static_cast<const TVariable*>(symbol); ConstantUnion* constArray = tVar->getConstPointer(); variable->shareConstPointer(constArray); } else { error(line, " cannot assign to", "=", "'%s'", variable->getType().getCompleteString().c_str()); variable->getType().setQualifier(EvqTemporary); return true; } } if (qualifier != EvqConst) { TIntermSymbol* intermSymbol = intermediate.addSymbol(variable->getUniqueId(), variable->getName(), variable->getType(), line); intermNode = intermediate.addAssign(EOpInitialize, intermSymbol, initializer, line); if (intermNode == 0) { assignError(line, "=", intermSymbol->getCompleteString(), initializer->getCompleteString()); return true; } } else intermNode = 0; return false; }
// // 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; }
bool TSamplerTraverser::traverseAggregate( bool preVisit, TIntermAggregate *node, TIntermTraverser *it) { TSamplerTraverser* sit = static_cast<TSamplerTraverser*>(it); TInfoSink &infoSink = sit->infoSink; if (sit->abort) return false; if (! (sit->typing) ) { switch (node->getOp()) { case EOpFunction: // Store the current function name to use to setup the parameters sit->currentFunction = node->getName().c_str(); break; case EOpParameters: // Store the parameters to the function in the map sit->functionMap[sit->currentFunction.c_str()] = &(node->getSequence()); break; case EOpFunctionCall: { // This is a bit tricky. Find the function in the map. Loop over the parameters // and see if the parameters have been marked as a typed sampler. If so, propagate // the sampler type to the caller if ( sit->functionMap.find ( node->getName().c_str() ) != sit->functionMap.end() ) { // Get the sequence of function parameters TIntermSequence *funcSequence = sit->functionMap[node->getName().c_str()]; // Get the sequence of parameters being passed to function TIntermSequence &sequence = node->getSequence(); // Grab iterators to both sequences TIntermSequence::iterator it = sequence.begin(); TIntermSequence::iterator funcIt = funcSequence->begin(); assert ( sequence.size() == funcSequence->size() ); if ( sequence.size() == funcSequence->size() ) { while ( it != sequence.end() ) { TIntermSymbol *sym = (*it)->getAsSymbolNode(); TIntermSymbol *funcSym = (*funcIt)->getAsSymbolNode(); if ( sym != NULL && funcSym != NULL) { // If the parameter is generic, and the sampler to which // it is being passed has been marked, propogate its sampler // type to the caller. if ( sym->getBasicType() == EbtSamplerGeneric && funcSym->getBasicType() != EbtSamplerGeneric ) { sit->typeSampler ( sym, funcSym->getBasicType() ); } } it++; funcIt++; } } } } break; //HLSL texture functions case EOpTex1D: case EOpTex1DProj: case EOpTex1DLod: case EOpTex1DBias: case EOpTex1DGrad: { TIntermSequence &sequence = node->getSequence(); assert( sequence.size()); TIntermTyped *sampArg = sequence[0]->getAsTyped(); if ( sampArg) { if (sampArg->getBasicType() == EbtSamplerGeneric) { //type the sampler sit->typeSampler( sampArg, EbtSampler1D); } else if (sampArg->getBasicType() != EbtSampler1D) { //We have a sampler mismatch error infoSink.info << "Error: " << node->getLine() << ": Sampler type mismatch, likely using a generic sampler as two types\n"; } } else { assert(0); } } // We need to continue the traverse here, because the calls could be nested break; case EOpTex2D: case EOpTex2DProj: case EOpTex2DLod: case EOpTex2DBias: case EOpTex2DGrad: { TIntermSequence &sequence = node->getSequence(); assert( sequence.size()); TIntermTyped *sampArg = sequence[0]->getAsTyped(); if ( sampArg) { if (sampArg->getBasicType() == EbtSamplerGeneric) { //type the sampler sit->typeSampler( sampArg, EbtSampler2D); } else if (sampArg->getBasicType() != EbtSampler2D) { //We have a sampler mismatch error infoSink.info << "Error: " << node->getLine() << ": Sampler type mismatch, likely using a generic sampler as two types\n"; } } else { assert(0); } } // We need to continue the traverse here, because the calls could be nested break; case EOpTexRect: case EOpTexRectProj: { TIntermSequence &sequence = node->getSequence(); assert( sequence.size()); TIntermTyped *sampArg = sequence[0]->getAsTyped(); if ( sampArg) { if (sampArg->getBasicType() == EbtSamplerGeneric) { //type the sampler sit->typeSampler( sampArg, EbtSamplerRect); } else if (sampArg->getBasicType() != EbtSamplerRect) { //We have a sampler mismatch error infoSink.info << "Error: " << node->getLine() << ": Sampler type mismatch, likely using a generic sampler as two types\n"; } } else { assert(0); } } // We need to continue the traverse here, because the calls could be nested break; case EOpTex3D: case EOpTex3DProj: case EOpTex3DLod: case EOpTex3DBias: case EOpTex3DGrad: { TIntermSequence &sequence = node->getSequence(); assert( sequence.size()); TIntermTyped *sampArg = sequence[0]->getAsTyped(); if ( sampArg) { if (sampArg->getBasicType() == EbtSamplerGeneric) { //type the sampler sit->typeSampler( sampArg, EbtSampler3D); } else if (sampArg->getBasicType() != EbtSampler3D) { //We have a sampler mismatch error infoSink.info << "Error: " << node->getLine() << ": Sampler type mismatch, likely using a generic sampler as two types\n"; } } else { assert(0); } } // We need to continue the traverse here, because the calls could be nested break; case EOpTexCube: case EOpTexCubeProj: case EOpTexCubeLod: case EOpTexCubeBias: case EOpTexCubeGrad: { TIntermSequence &sequence = node->getSequence(); assert( sequence.size()); TIntermTyped *sampArg = sequence[0]->getAsTyped(); if ( sampArg) { if (sampArg->getBasicType() == EbtSamplerGeneric) { //type the sampler sit->typeSampler( sampArg, EbtSamplerCube); } else if (sampArg->getBasicType() != EbtSamplerCube) { //We have a sampler mismatch error infoSink.info << "Error: " << node->getLine() << ": Sampler type mismatch, likely using a generic sampler as two types\n"; } } else { assert(0); } } // We need to continue the traverse here, because the calls could be nested break; default: break; } } return !(sit->abort); }
bool ValidateLimitations::validateForLoopExpr(TIntermLoop *node, int indexSymbolId) { TIntermNode *expr = node->getExpression(); if (expr == NULL) { error(node->getLine(), "Missing expression", "for"); return false; } // 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; TIntermSymbol *symbol = NULL; if (unOp != NULL) { op = unOp->getOp(); symbol = unOp->getOperand()->getAsSymbolNode(); } else if (binOp != NULL) { op = binOp->getOp(); symbol = binOp->getLeft()->getAsSymbolNode(); } // The operand must be loop index. if (symbol == NULL) { error(expr->getLine(), "Invalid expression", "for"); return false; } if (symbol->getId() != indexSymbolId) { error(symbol->getLine(), "Expected loop index", symbol->getSymbol().c_str()); return false; } // The operator is one of: ++ -- += -=. switch (op) { case EOpPostIncrement: case EOpPostDecrement: case EOpPreIncrement: case EOpPreDecrement: ASSERT((unOp != NULL) && (binOp == NULL)); break; case EOpAddAssign: case EOpSubAssign: ASSERT((unOp == NULL) && (binOp != NULL)); break; default: error(expr->getLine(), "Invalid operator", GetOperatorString(op)); return false; } // Loop index must be incremented/decremented with a constant. if (binOp != NULL) { if (!isConstExpr(binOp->getRight())) { error(binOp->getLine(), "Loop index cannot be modified by non-constant expression", symbol->getSymbol().c_str()); return false; } } return true; }
// // 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 there was an error. // bool TParseContextBase::lValueErrorCheck(const TSourceLoc& loc, const char* op, TIntermTyped* node) { TIntermBinary* binaryNode = node->getAsBinaryNode(); if (binaryNode) { switch(binaryNode->getOp()) { case EOpIndexDirect: case EOpIndexIndirect: // fall through case EOpIndexDirectStruct: // fall through case EOpVectorSwizzle: case EOpMatrixSwizzle: return lValueErrorCheck(loc, op, binaryNode->getLeft()); default: break; } error(loc, " l-value required", op, "", ""); return true; } const char* symbol = nullptr; TIntermSymbol* symNode = node->getAsSymbolNode(); if (symNode != nullptr) symbol = symNode->getName().c_str(); const char* message = nullptr; switch (node->getQualifier().storage) { case EvqConst: message = "can't modify a const"; break; case EvqConstReadOnly: message = "can't modify a const"; break; case EvqUniform: message = "can't modify a uniform"; break; case EvqBuffer: if (node->getQualifier().readonly) message = "can't modify a readonly buffer"; break; default: // // Type that can't be written to? // switch (node->getBasicType()) { case EbtSampler: message = "can't modify a sampler"; break; case EbtAtomicUint: message = "can't modify an atomic_uint"; break; case EbtVoid: message = "can't modify void"; break; default: break; } } if (message == nullptr && binaryNode == nullptr && symNode == nullptr) { error(loc, " l-value required", op, "", ""); return true; } // // Everything else is okay, no error. // if (message == nullptr) return false; // // If we get here, we have an error and a message. // if (symNode) error(loc, " l-value required", op, "\"%s\" (%s)", symbol, message); else error(loc, " l-value required", op, "(%s)", message); return true; }
// // Merge the linker objects from unitLinkerObjects into linkerObjects. // Duplication is expected and filtered out, but contradictions are an error. // void TIntermediate::mergeLinkerObjects(TInfoSink& infoSink, TIntermSequence& linkerObjects, const TIntermSequence& unitLinkerObjects) { // Error check and merge the linker objects (duplicates should not be created) std::size_t initialNumLinkerObjects = linkerObjects.size(); for (unsigned int unitLinkObj = 0; unitLinkObj < unitLinkerObjects.size(); ++unitLinkObj) { bool merge = true; for (std::size_t linkObj = 0; linkObj < initialNumLinkerObjects; ++linkObj) { TIntermSymbol* symbol = linkerObjects[linkObj]->getAsSymbolNode(); TIntermSymbol* unitSymbol = unitLinkerObjects[unitLinkObj]->getAsSymbolNode(); assert(symbol && unitSymbol); if (symbol->getName() == unitSymbol->getName()) { // filter out copy merge = false; // but if one has an initializer and the other does not, update // the initializer if (symbol->getConstArray().empty() && ! unitSymbol->getConstArray().empty()) symbol->setConstArray(unitSymbol->getConstArray()); // Similarly for binding if (! symbol->getQualifier().hasBinding() && unitSymbol->getQualifier().hasBinding()) symbol->getQualifier().layoutBinding = unitSymbol->getQualifier().layoutBinding; // Update implicit array sizes mergeImplicitArraySizes(symbol->getWritableType(), unitSymbol->getType()); // Check for consistent types/qualification/initializers etc. mergeErrorCheck(infoSink, *symbol, *unitSymbol, false); } } if (merge) linkerObjects.push_back(unitLinkerObjects[unitLinkObj]); } }
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; }
// TODO(jmadill): This is not complete. void TOutputVulkanGLSL::writeLayoutQualifier(TIntermTyped *variable) { const TType &type = variable->getType(); bool needsCustomLayout = (type.getQualifier() == EvqAttribute || type.getQualifier() == EvqFragmentOut || type.getQualifier() == EvqVertexIn || IsVarying(type.getQualifier()) || IsSampler(type.getBasicType()) || type.isInterfaceBlock()); if (!NeedsToWriteLayoutQualifier(type) && !needsCustomLayout) { return; } TInfoSinkBase &out = objSink(); const TLayoutQualifier &layoutQualifier = type.getLayoutQualifier(); // This isn't super clean, but it gets the job done. // See corresponding code in GlslangWrapper.cpp. TIntermSymbol *symbol = variable->getAsSymbolNode(); ASSERT(symbol); ImmutableString name = symbol->getName(); const char *blockStorage = nullptr; const char *matrixPacking = nullptr; // For interface blocks, use the block name instead. When the layout qualifier is being // replaced in the backend, that would be the name that's available. if (type.isInterfaceBlock()) { const TInterfaceBlock *interfaceBlock = type.getInterfaceBlock(); name = interfaceBlock->name(); TLayoutBlockStorage storage = interfaceBlock->blockStorage(); // Make sure block storage format is specified. if (storage != EbsStd430) { // Change interface block layout qualifiers to std140 for any layout that is not // explicitly set to std430. This is to comply with GL_KHR_vulkan_glsl where shared and // packed are not allowed (and std140 could be used instead) and unspecified layouts can // assume either std140 or std430 (and we choose std140 as std430 is not yet universally // supported). storage = EbsStd140; } blockStorage = getBlockStorageString(storage); } // Specify matrix packing if necessary. if (layoutQualifier.matrixPacking != EmpUnspecified) { matrixPacking = getMatrixPackingString(layoutQualifier.matrixPacking); } if (needsCustomLayout) { out << "@@ LAYOUT-" << name << "("; } else { out << "layout("; } // Output the list of qualifiers already known at this stage, i.e. everything other than // `location` and `set`/`binding`. std::string otherQualifiers = getCommonLayoutQualifiers(variable); const char *separator = ""; if (blockStorage) { out << separator << blockStorage; separator = ", "; } if (matrixPacking) { out << separator << matrixPacking; separator = ", "; } if (!otherQualifiers.empty()) { out << separator << otherQualifiers; } out << ") "; if (needsCustomLayout) { out << "@@"; } }