diff --git a/glslang/Include/Types.h b/glslang/Include/Types.h index 0b3917cb..e4c84dc4 100644 --- a/glslang/Include/Types.h +++ b/glslang/Include/Types.h @@ -44,6 +44,12 @@ namespace glslang { const int GlslangMaxTypeLength = 200; // TODO: need to print block/struct one member per line, so this can stay bounded +const char* const AnonymousPrefix = "anon@"; // for something like a block whose members can be directly accessed +inline bool IsAnonymous(const TString& name) +{ + return name.compare(0, 5, AnonymousPrefix) == 0; +} + // // Details within a sampler type // @@ -174,7 +180,7 @@ typedef TVector TIdentifierList; struct TArraySizes { POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator()) - TArraySizes() : implicitArraySize(0) { } + TArraySizes() : implicitArraySize(1) { } int getSize() { return sizes.front(); } // TArraySizes only exists if there is at least one dimension void setSize(int s) { sizes.push_back(s); } bool isArrayOfArrays() { return sizes.size() > 1; } @@ -701,14 +707,14 @@ public: sampler.clear(); qualifier = p.qualifier; if (p.userDef) { - structure = p.userDef->getStruct(); + structure = p.userDef->getWritableStruct(); // public type is short-lived; there are no sharing issues typeName = NewPoolTString(p.userDef->getTypeName().c_str()); } } // to efficiently make a dereferenced type // without ever duplicating the outer structure that will be thrown away // and using only shallow copy - TType(const TType& type, int derefIndex) + TType(const TType& type, int derefIndex, bool rowMajor = false) { if (! type.isArray() && (type.basicType == EbtStruct || type.basicType == EbtBlock)) { // do a structure dereference @@ -718,7 +724,7 @@ public: } else { // do an array/vector/matrix dereference shallowCopy(type); - dereference(); + dereference(rowMajor); } } // for making structures, ... @@ -761,12 +767,12 @@ public: { shallowCopy(copyOf); - if (arraySizes) { + if (copyOf.arraySizes) { arraySizes = new TArraySizes; *arraySizes = *copyOf.arraySizes; } - if (structure) { + if (copyOf.structure) { structure = new TTypeList; TStructureMapIterator iter; for (unsigned int i = 0; i < copyOf.structure->size(); ++i) { @@ -778,9 +784,9 @@ public: } } - if (fieldName) + if (copyOf.fieldName) fieldName = NewPoolTString(copyOf.fieldName->c_str()); - if (typeName) + if (copyOf.typeName) typeName = NewPoolTString(copyOf.typeName->c_str()); } @@ -793,47 +799,48 @@ public: } // Merge type from parent, where a parentType is at the beginning of a declaration, - // establishing some charastics for all subsequent names, while this type + // establishing some characteristics for all subsequent names, while this type // is on the individual names. void mergeType(const TPublicType& parentType) { // arrayness is currently the only child aspect that has to be preserved - setElementType(parentType.basicType, parentType.vectorSize, parentType.matrixCols, parentType.matrixRows, parentType.userDef); + basicType = parentType.basicType; + vectorSize = parentType.vectorSize; + matrixCols = parentType.matrixCols; + matrixRows = parentType.matrixRows; qualifier = parentType.qualifier; sampler = parentType.sampler; if (parentType.arraySizes) setArraySizes(parentType.arraySizes); - if (parentType.userDef) + if (parentType.userDef) { + structure = parentType.userDef->getWritableStruct(); setTypeName(parentType.userDef->getTypeName()); + } } - virtual void dereference() + virtual void dereference(bool rowMajor = false) { if (arraySizes) arraySizes = 0; else if (matrixCols > 0) { - vectorSize = matrixRows; + if (rowMajor) + vectorSize = matrixCols; + else + vectorSize = matrixRows; matrixCols = 0; matrixRows = 0; } else if (vectorSize > 1) vectorSize = 1; } - virtual void setElementType(TBasicType t, int s, int mc, int mr, const TType* userDef) - { - basicType = t; - vectorSize = s; - matrixCols = mc; - matrixRows = mr; - if (userDef) - structure = userDef->getStruct(); - // leave array information intact. - } + virtual void hideType() { basicType = EbtVoid; vectorSize = 1; } + virtual bool wasTypeHidden() const { return basicType == EbtVoid; } + virtual void setTypeName(const TString& n) { typeName = NewPoolTString(n.c_str()); } virtual void setFieldName(const TString& n) { fieldName = NewPoolTString(n.c_str()); } virtual const TString& getTypeName() const { - assert(typeName); + assert(typeName); return *typeName; } @@ -845,17 +852,22 @@ public: virtual TBasicType getBasicType() const { return basicType; } virtual const TSampler& getSampler() const { return sampler; } - virtual TQualifier& getQualifier() { return qualifier; } + + virtual TQualifier& getQualifier() { return qualifier; } virtual const TQualifier& getQualifier() const { return qualifier; } virtual int getVectorSize() const { return vectorSize; } virtual int getMatrixCols() const { return matrixCols; } virtual int getMatrixRows() const { return matrixRows; } + virtual int getArraySize() const { return arraySizes->sizes.front(); } + virtual int getImplicitArraySize () const { return arraySizes->implicitArraySize; } virtual bool isScalar() const { return vectorSize == 1 && ! isStruct() && ! isArray(); } virtual bool isVector() const { return vectorSize > 1; } virtual bool isMatrix() const { return matrixCols ? true : false; } virtual bool isArray() const { return arraySizes != 0; } + virtual bool isImplicitlySizedArray() const { return isArray() && ! getArraySize(); } + virtual bool isExplicitlySizedArray() const { return ! isImplicitlySizedArray(); } virtual bool isStruct() const { return structure != 0; } // Recursively checks if the type contains the given basic type @@ -885,28 +897,58 @@ public: } return false; } - int getArraySize() const { return arraySizes->sizes.front(); } - void shareArraySizes(const TType& type) + + // Recursively check the structure for any implicitly-sized arrays, needed for triggering a copyUp(). + virtual bool containsImplicitlySizedArray() const { - // For when we are sharing existing array descriptors. - // This allows all references to the same array - // to be updated at once, by having all of them share the - // array description. + if (isImplicitlySizedArray()) + return true; + if (! structure) + return false; + for (unsigned int i = 0; i < structure->size(); ++i) { + if ((*structure)[i].type->containsImplicitlySizedArray()) + return true; + } + return false; + } + + // Array editing methods. Array descriptors can be shared across + // type instances. This allows all uses of the same array + // to be updated at once. E.g., all nodes can be explicitly sized + // by tracking and correcting one implicit size. Or, all nodes + // can get the explicit size on a redeclaration that gives size. + // + // N.B.: Don't share with the shared symbol tables (symbols are + // marked as isReadOnly(). Such symbols with arrays that will be + // edited need to copyUp() on first use, so that + // A) the edits don't effect the shared symbol table, and + // B) the edits are shared across all users. + void updateArraySizes(const TType& type) + { + // For when we may already be sharing existing array descriptors, + // keeping the pointers the same, just updating the contents. *arraySizes = *type.arraySizes; } void setArraySizes(TArraySizes* s) { - // For when we don't want distinct types sharing the same descriptor. + // For setting a fresh new set of array sizes, not yet worrying about sharing. arraySizes = new TArraySizes; *arraySizes = *s; } void setArraySizes(const TType& type) { setArraySizes(type.arraySizes); } - void changeArraySize(int s) { arraySizes->sizes.front() = s; } - bool isImplicitlySizedArray() const { return isArray() && ! getArraySize(); } - bool isExplicitlySizedArray() const { return ! isImplicitlySizedArray(); } void setImplicitArraySize (int s) { arraySizes->implicitArraySize = s; } - int getImplicitArraySize () const { return arraySizes->implicitArraySize; } + + // Recursively make the implicit array size the explicit array size, through the type tree. + void adoptImplicitArraySizes() + { + if (isImplicitlySizedArray()) + changeArraySize(getImplicitArraySize()); + if (isStruct()) { + for (int i = 0; i < (int)structure->size(); ++i) + (*structure)[i].type->adoptImplicitArraySizes(); + } + } const char* getBasicString() const { @@ -1044,15 +1086,15 @@ public: const char* getStorageQualifierString() const { return GetStorageQualifierString(qualifier.storage); } const char* getPrecisionQualifierString() const { return GetPrecisionQualifierString(qualifier.precision); } - TTypeList* getStruct() { return structure; } - TTypeList* getStruct() const { return structure; } + const TTypeList* getStruct() const { return structure; } + TTypeList* getWritableStruct() const { return structure; } // This should only be used when known to not be sharing with other threads int computeNumComponents() const { int components = 0; if (getBasicType() == EbtStruct || getBasicType() == EbtBlock) { - for (TTypeList::iterator tl = getStruct()->begin(); tl != getStruct()->end(); tl++) + for (TTypeList::const_iterator tl = getStruct()->begin(); tl != getStruct()->end(); tl++) components += ((*tl).type)->computeNumComponents(); } else if (matrixCols) components = matrixCols * matrixRows; @@ -1158,9 +1200,8 @@ protected: TSampler sampler; TQualifier qualifier; - TArraySizes* arraySizes; - - TTypeList* structure; // 0 unless this is a struct + TArraySizes* arraySizes; // 0 unless this is an array; can be shared across types + TTypeList* structure; // 0 unless this is a struct; can be shared across types TString *fieldName; // for structure field names TString *typeName; // for structure type name }; diff --git a/glslang/MachineIndependent/Intermediate.cpp b/glslang/MachineIndependent/Intermediate.cpp index 5195a0b7..62ff0c5c 100644 --- a/glslang/MachineIndependent/Intermediate.cpp +++ b/glslang/MachineIndependent/Intermediate.cpp @@ -866,10 +866,10 @@ void TIntermediate::addSymbolLinkageNodes(TIntermAggregate*& linkage, EShLanguag // by the AST. // // Almost entirely, translation of symbols is driven by what's present - // in the AST traversal, not by translating the symbol table. + // in the AST traversal, not by translating the symbol table. // // However, there are some special cases: - // - From the specification: "Special built-in inputs gl_VertexID and + // - From the specification: "Special built-in inputs gl_VertexID and // gl_InstanceID are also considered active vertex attributes." // - Linker-based type mismatch error reporting needs to see all // uniforms/ins/outs variables and blocks. diff --git a/glslang/MachineIndependent/ParseHelper.cpp b/glslang/MachineIndependent/ParseHelper.cpp index 8b2be3c4..3fcdbaf7 100644 --- a/glslang/MachineIndependent/ParseHelper.cpp +++ b/glslang/MachineIndependent/ParseHelper.cpp @@ -371,59 +371,56 @@ void C_DECL TParseContext::warn(TSourceLoc loc, const char* szReason, const char TIntermTyped* TParseContext::handleVariable(TSourceLoc loc, TSymbol* symbol, TString* string) { TIntermTyped* node = 0; - bool noteAccess = false; // Error check for function requiring specific extensions present. if (symbol && symbol->getNumExtensions()) requireExtensions(loc, symbol->getNumExtensions(), symbol->getExtensions(), symbol->getName().c_str()); + if (symbol && symbol->isReadOnly()) { + // All shared things containing an implicitly sized array must be copied up + // on first use, so that all future references will share its array structure, + // so that editing the implicit size will effect all nodes consuming it, + // and so that editing the implicit size won't change the shared one. + if (symbol->getType().containsImplicitlySizedArray()) + makeEditable(symbol); + } + + const TVariable* variable; const TAnonMember* anon = symbol ? symbol->getAsAnonMember() : 0; if (anon) { - // it was a member of an anonymous container - - // create a subtree for its dereference - const TVariable* variable = anon->getAnonContainer().getAsVariable(); + // It was a member of an anonymous container. + + // Create a subtree for its dereference. + variable = anon->getAnonContainer().getAsVariable(); TIntermTyped* container = intermediate.addSymbol(*variable, loc); TConstUnionArray unionArray(1); unionArray[0].setUConst(anon->getMemberNumber()); TIntermTyped* constNode = intermediate.addConstantUnion(unionArray, TType(EbtUint, EvqConst), loc); - node = intermediate.addIndex(EOpIndexDirectStruct, container, constNode, loc); - node->setType(*(*variable->getType().getStruct())[anon->getMemberNumber()].type); - if (node->getBasicType() == EbtVoid) - error(loc, "member of nameless block was not redeclared", string->c_str(), ""); - if (variable->getType().getQualifier().isIo()) - noteAccess = true; - // TODO: does this create any accidental type sharing with the built-in level? + node->setType(*(*variable->getType().getStruct())[anon->getMemberNumber()].type); + if (node->getType().wasTypeHidden()) + error(loc, "member of nameless block was not redeclared", string->c_str(), ""); } else { - // The symbol table search was done in the lexical phase, but - // if this is a new symbol, it wouldn't have found it. - TVariable* variable = symbol ? symbol->getAsVariable() : 0; + // Not a member of an anonymous container. + + // The symbol table search was done in the lexical phase. + // See if it was a variable. + variable = symbol ? symbol->getAsVariable() : 0; if (symbol && ! variable) error(loc, "variable name expected", string->c_str(), ""); + // Recovery, if it wasn't found or was not a variable. if (! variable) variable = new TVariable(string, TType(EbtVoid)); if (variable->getType().getQualifier().storage == EvqConst) node = intermediate.addConstantUnion(variable->getConstArray(), variable->getType(), loc); - else { - TType* type; - if (variable->isReadOnly()) { - type = new TType; - // break type sharing with built-ins; only costs if there are arrays or structures - type->deepCopy(variable->getType()); - } else - type = &variable->getWritableType(); - // addSymbol will do a shallow copy of the type to the node, thus sharing array and struct information - node = intermediate.addSymbol(variable->getUniqueId(), variable->getName(), *type, loc); - if (type->getQualifier().isIo()) - noteAccess = true; - } + else + node = intermediate.addSymbol(*variable, loc); } - if (noteAccess) + if (variable->getType().getQualifier().isIo()) intermediate.addIoAccessed(*string); return node; @@ -543,6 +540,22 @@ void TParseContext::handleIndexLimits(TSourceLoc loc, TIntermTyped* base, TInter } } +// Make a shared symbol have a non-shared version that can be edited by the current +// compile, such that editing its type will not change the shared version and will +// effect all nodes sharing it. +void TParseContext::makeEditable(TSymbol*& symbol) +{ + // copyUp() does a deep copy of the type. + symbol = symbolTable.copyUp(symbol); + + // Also, see if it's tied to IO resizing + if (isIoResizeArray(symbol->getType())) + ioArraySymbolResizeList.push_back(symbol); + + // Also, save it in the AST for linker use. + intermediate.addSymbolLinkageNode(linkage, *symbol); +} + // Return true if this is a geometry shader input array or tessellation control output array. bool TParseContext::isIoResizeArray(const TType& type) const { @@ -585,33 +598,20 @@ void TParseContext::ioArrayCheck(TSourceLoc loc, const TType& type, const TStrin } // Handle a dereference of a geometry shader input array or tessellation control output array. -// See ioArrayNodeResizeList comment in ParseHelper.h. +// See ioArraySymbolResizeList comment in ParseHelper.h. // void TParseContext::handleIoResizeArrayAccess(TSourceLoc loc, TIntermTyped* base) { - TIntermSymbol* symbol = base->getAsSymbolNode(); - assert(symbol); - ioArrayNodeResizeList.push_back(symbol); - if (symbol && builtInName(symbol->getName())) { - // make sure we have a user-modifiable copy of this built-in input array - TSymbol* arry = symbolTable.find(symbol->getName()); - if (arry->isReadOnly()) { - arry = symbolTable.copyUp(arry); + TIntermSymbol* symbolNode = base->getAsSymbolNode(); + assert(symbolNode); + if (! symbolNode) + return; - // fix array size, if already implicitly size - if (arry->getType().isImplicitlySizedArray()) { - int newSize = getIoArrayImplicitSize(); - if (newSize) { - arry->getWritableType().changeArraySize(newSize); - symbol->getWritableType().changeArraySize(newSize); - } - } - - ioArraySymbolResizeList.push_back(arry); - - // Save it in the AST for linker use. - intermediate.addSymbolLinkageNode(linkage, *arry); - } + // fix array size, if it can be fixed and needs to be fixed (will allow variable indexing) + if (symbolNode->getType().isImplicitlySizedArray()) { + int newSize = getIoArrayImplicitSize(); + if (newSize) + symbolNode->getWritableType().changeArraySize(newSize); } } @@ -640,9 +640,6 @@ void TParseContext::checkIoArraysConsistency(TSourceLoc loc, bool tailOnly) return; } - for (size_t i = 0; i < ioArrayNodeResizeList.size(); ++i) - checkIoArrayConsistency(loc, requiredSize, feature, ioArrayNodeResizeList[i]->getWritableType(), ioArrayNodeResizeList[i]->getName()); - for (size_t i = 0; i < ioArraySymbolResizeList.size(); ++i) checkIoArrayConsistency(loc, requiredSize, feature, ioArraySymbolResizeList[i]->getWritableType(), ioArraySymbolResizeList[i]->getName()); } @@ -732,7 +729,7 @@ TIntermTyped* TParseContext::handleDotDereference(TSourceLoc loc, TIntermTyped* } else if (base->isMatrix()) error(loc, "field selection not allowed on matrix", ".", ""); else if (base->getBasicType() == EbtStruct || base->getBasicType() == EbtBlock) { - TTypeList* fields = base->getType().getStruct(); + const TTypeList* fields = base->getType().getStruct(); bool fieldFound = false; unsigned int member; for (member = 0; member < fields->size(); ++member) { @@ -2118,7 +2115,7 @@ bool TParseContext::containsSampler(const TType& type) return true; if (type.getBasicType() == EbtStruct) { - TTypeList& structure = *type.getStruct(); + const TTypeList& structure = *type.getStruct(); for (unsigned int i = 0; i < structure.size(); ++i) { if (containsSampler(*structure[i].type)) return true; @@ -2204,7 +2201,7 @@ void TParseContext::arrayDimCheck(TSourceLoc loc, const TType* type, TArraySizes } // -// Do all the semantic checking for declaring an array, with and +// Do all the semantic checking for declaring or redeclaring an array, with and // without a size, and make the right changes to the symbol table. // // size == 0 means no specified size. @@ -2249,27 +2246,28 @@ void TParseContext::declareArray(TSourceLoc loc, TString& identifier, const TTyp return; } - TType& newType = symbol->getWritableType(); + // redeclareBuiltinVariable() should have already done the copyUp() + TType& existingType = symbol->getWritableType(); - if (! newType.isArray()) { + if (! existingType.isArray()) { error(loc, "redeclaring non-array as array", identifier.c_str(), ""); return; } - if (newType.isExplicitlySizedArray()) { + if (existingType.isExplicitlySizedArray()) { // be more leniant for input arrays to geometry shaders and tessellation control outputs, where the redeclaration is the same size - if (! (isIoResizeArray(type) && newType.getArraySize() == type.getArraySize())) + if (! (isIoResizeArray(type) && existingType.getArraySize() == type.getArraySize())) error(loc, "redeclaration of array with size", identifier.c_str(), ""); return; } - if (! newType.sameElementType(type)) { + if (! existingType.sameElementType(type)) { error(loc, "redeclaration of array with a different type", identifier.c_str(), ""); return; } arrayLimitCheck(loc, identifier, type.getArraySize()); - newType.shareArraySizes(type); + existingType.updateArraySizes(type); if (isIoResizeArray(type)) checkIoArraysConsistency(loc); @@ -2284,13 +2282,29 @@ void TParseContext::updateImplicitArraySize(TSourceLoc loc, TIntermNode *node, i // something to do... - // TODO: 1.50 linker: unsized block member array: 'node' could be an expression for a dereference - TIntermSymbol* symbolNode = node->getAsSymbolNode(); - if (! symbolNode) - return; + // Figure out what symbol to lookup, as we will use its type to edit for the size change, + // as that type will be shared through shallow copies for future references. + TSymbol* symbol = 0; + int blockIndex = -1; + const TString* lookupName; + if (node->getAsSymbolNode()) + lookupName = &node->getAsSymbolNode()->getName(); + else if (node->getAsBinaryNode()) { + const TIntermBinary* deref = node->getAsBinaryNode(); + // This has to be the result of a block dereference, unless it's bad shader code + if (! deref->getLeft()->getAsSymbolNode() || deref->getLeft()->getBasicType() != EbtBlock || + deref->getRight()->getAsConstantUnion() == 0) + return; - TSymbol* symbol = symbolTable.find(symbolNode->getName()); - assert(symbol); + blockIndex = deref->getRight()->getAsConstantUnion()->getConstArray()[0].getIConst(); + + lookupName = &deref->getLeft()->getAsSymbolNode()->getName(); + if (IsAnonymous(*lookupName)) + lookupName = &(*deref->getLeft()->getType().getStruct())[blockIndex].type->getFieldName(); + } + + // Lookup the symbol, should only fail if shader code is incorrect + symbol = symbolTable.find(*lookupName); if (symbol == 0) return; @@ -2299,18 +2313,6 @@ void TParseContext::updateImplicitArraySize(TSourceLoc loc, TIntermNode *node, i return; } - // For read-only built-ins, add a new variable for holding the maximum array size of an implicitly-sized shared array. - // TODO: desktop linker: unsized arrays: is this new array type shared with the node? - if (symbol->isReadOnly()) { - symbol = symbolTable.copyUp(symbol); - - if (isIoResizeArray(symbol->getType())) - ioArraySymbolResizeList.push_back(symbol); - - // Save it in the AST for linker use. - intermediate.addSymbolLinkageNode(linkage, *symbol); - } - symbol->getWritableType().setImplicitArraySize(index + 1); } @@ -2371,14 +2373,8 @@ TSymbol* TParseContext::redeclareBuiltinVariable(TSourceLoc loc, const TString& // redeclaration. Otherwise, make the new one. if (builtIn) { // Copy the symbol up to make a writable version + makeEditable(symbol); newDeclaration = true; - symbol = symbolTable.copyUp(symbol); - - if (isIoResizeArray(symbol->getType())) - ioArraySymbolResizeList.push_back(symbol); - - // Save it in the AST for linker use. - intermediate.addSymbolLinkageNode(linkage, *symbol); } // Now, modify the type of the copy, as per the type of the current redeclaration. @@ -2490,12 +2486,12 @@ void TParseContext::redeclareBuiltinBlock(TSourceLoc loc, TTypeList& newTypeList // - remove unused members // - ensure remaining qualifiers/types match TType& type = block->getWritableType(); - TTypeList::iterator member = type.getStruct()->begin(); + TTypeList::iterator member = type.getWritableStruct()->begin(); size_t numOriginalMembersFound = 0; while (member != type.getStruct()->end()) { // look for match bool found = false; - TTypeList::iterator newMember; + TTypeList::const_iterator newMember; TSourceLoc memberLoc; for (newMember = newTypeList.begin(); newMember != newTypeList.end(); ++newMember) { if (member->type->getFieldName() == newMember->type->getFieldName()) { @@ -2536,13 +2532,13 @@ void TParseContext::redeclareBuiltinBlock(TSourceLoc loc, TTypeList& newTypeList // go to next member ++member; } else { - // Use EbtVoid to tag missing members of anonymous blocks that have been redeclared, - // to hide the original (shared) declaration. - // (Instance-named blocks can just have the member removed.) + // For missing members of anonymous blocks that have been redeclared, + // hide the original (shared) declaration. + // Instance-named blocks can just have the member removed. if (instanceName) - member = type.getStruct()->erase(member); + member = type.getWritableStruct()->erase(member); else { - member->type->setElementType(EbtVoid, 1, 0, 0, 0); + member->type->hideType(); ++member; } } @@ -2643,7 +2639,7 @@ void TParseContext::opaqueCheck(TSourceLoc loc, const TType& type, const char* o void TParseContext::structTypeCheck(TSourceLoc loc, TPublicType& publicType) { - TTypeList& typeList = *publicType.userDef->getStruct(); + const TTypeList& typeList = *publicType.userDef->getStruct(); // fix and check for member storage qualifiers and types that don't belong within a structure for (unsigned int member = 0; member < typeList.size(); ++member) { @@ -3797,7 +3793,7 @@ TIntermTyped* TParseContext::addConstructor(TSourceLoc loc, TIntermNode* node, c TIntermAggregate* aggrNode = node->getAsAggregate(); - TTypeList::iterator memberTypes; + TTypeList::const_iterator memberTypes; if (op == EOpConstructStruct) memberTypes = type.getStruct()->begin(); diff --git a/glslang/MachineIndependent/ShaderLang.cpp b/glslang/MachineIndependent/ShaderLang.cpp index d7810832..fc200a6d 100644 --- a/glslang/MachineIndependent/ShaderLang.cpp +++ b/glslang/MachineIndependent/ShaderLang.cpp @@ -1064,7 +1064,7 @@ bool TProgram::linkStage(EShLanguage stage, EShMessages messages) intermediate[stage]->finalCheck(*infoSink); if (messages & EShMsgAST) - intermediate[stage]->output(*infoSink, stages[stage].size() > 1); + intermediate[stage]->output(*infoSink, true); return intermediate[stage]->getNumErrors() == 0; } diff --git a/glslang/MachineIndependent/linkValidate.cpp b/glslang/MachineIndependent/linkValidate.cpp index 4a7d0bcc..5c262bdd 100644 --- a/glslang/MachineIndependent/linkValidate.cpp +++ b/glslang/MachineIndependent/linkValidate.cpp @@ -206,7 +206,10 @@ void TIntermediate::mergeLinkerObjects(TInfoSink& infoSink, TIntermSequence& lin // 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); } @@ -216,6 +219,25 @@ void TIntermediate::mergeLinkerObjects(TInfoSink& infoSink, TIntermSequence& lin } } +// Recursively merge the implicit array sizes through the objects' respective type trees. +void TIntermediate::mergeImplicitArraySizes(TType& type, const TType& unitType) +{ + if (type.isImplicitlySizedArray() && unitType.isArray()) { + int newImplicitArraySize = unitType.getArraySize(); + if (newImplicitArraySize == 0) + newImplicitArraySize = unitType.getImplicitArraySize(); + if (newImplicitArraySize > type.getImplicitArraySize ()) + type.setImplicitArraySize(newImplicitArraySize); + } + + // Type mismatches are caught and reported after this, just be careful for now. + if (! type.isStruct() || ! unitType.isStruct() || type.getStruct()->size() != unitType.getStruct()->size()) + return; + + for (int i = 0; i < (int)type.getStruct()->size(); ++i) + mergeImplicitArraySizes(*(*type.getStruct())[i].type, *(*unitType.getStruct())[i].type); +} + // // 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. @@ -305,7 +327,7 @@ void TIntermediate::mergeErrorCheck(TInfoSink& infoSink, const TIntermSymbol& sy // Do final link-time error checking of a complete (merged) intermediate representation. // (Much error checking was done during merging). // -// Also, lock in defaults of things not set. +// Also, lock in defaults of things not set, including array sizes. // void TIntermediate::finalCheck(TInfoSink& infoSink) { @@ -392,6 +414,21 @@ void TIntermediate::finalCheck(TInfoSink& infoSink) case EShLangCompute: break; } + + // Process the tree for any node-specific work. + class TFinalLinkTraverser : public TIntermTraverser { + public: + TFinalLinkTraverser() { } + virtual ~TFinalLinkTraverser() { } + + virtual void visitSymbol(TIntermSymbol* symbol) + { + // Implicitly size arrays. + symbol->getWritableType().adoptImplicitArraySizes(); + } + } finalLinkTraverser; + + treeRoot->traverse(&finalLinkTraverser); } // @@ -877,11 +914,8 @@ int TIntermediate::getBaseAlignment(const TType& type, int& size, bool std140) c // rules 5 and 7 if (type.isMatrix()) { - TType derefType(type, 0); - // rule 5: deref to row, not to column, meaning the size of vector is num columns instead of num rows - if (type.getQualifier().layoutMatrix == ElmRowMajor) - derefType.setElementType(derefType.getBasicType(), type.getMatrixCols(), 0, 0, 0); + TType derefType(type, 0, type.getQualifier().layoutMatrix == ElmRowMajor); alignment = getBaseAlignment(derefType, size, std140); if (std140) diff --git a/glslang/MachineIndependent/localintermediate.h b/glslang/MachineIndependent/localintermediate.h index 621772f9..4ebbf2c0 100644 --- a/glslang/MachineIndependent/localintermediate.h +++ b/glslang/MachineIndependent/localintermediate.h @@ -251,6 +251,7 @@ protected: void error(TInfoSink& infoSink, const char*); void mergeBodies(TInfoSink&, TIntermSequence& globals, const TIntermSequence& unitGlobals); void mergeLinkerObjects(TInfoSink&, TIntermSequence& linkerObjects, const TIntermSequence& unitLinkerObjects); + void mergeImplicitArraySizes(TType&, const TType&); void mergeErrorCheck(TInfoSink&, const TIntermSymbol&, const TIntermSymbol&, bool crossStage); void checkCallGraphCycles(TInfoSink&); void inOutLocationCheck(TInfoSink&); diff --git a/glslang/MachineIndependent/reflection.cpp b/glslang/MachineIndependent/reflection.cpp index 07bba319..ac9f3b6c 100644 --- a/glslang/MachineIndependent/reflection.cpp +++ b/glslang/MachineIndependent/reflection.cpp @@ -283,7 +283,7 @@ public: bool block = base->getBasicType() == EbtBlock; if (block) { offset = 0; - anonymous = base->getName().compare(0, 6, "__anon") == 0; + anonymous = IsAnonymous(base->getName()); if (base->getType().isArray()) { assert(! anonymous); for (int e = 0; e < base->getType().getArraySize(); ++e)