// // 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]); } }
// 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()); }
// 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 << "@@"; } }
// // 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; }
// // 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"; }