// // Copyright (C) 2013 LunarG, Inc. // Copyright (C) 2017 ARM Limited. // Copyright (C) 2015-2018 Google, Inc. // // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions // are met: // // Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // // Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following // disclaimer in the documentation and/or other materials provided // with the distribution. // // Neither the name of 3Dlabs Inc. Ltd. nor the names of its // contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE // COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. // // // Do link-time merging and validation of intermediate representations. // // Basic model is that during compilation, each compilation unit (shader) is // compiled into one TIntermediate instance. Then, at link time, multiple // units for the same stage can be merged together, which can generate errors. // Then, after all merging, a single instance of TIntermediate represents // the whole stage. A final error check can be done on the resulting stage, // even if no merging was done (i.e., the stage was only one compilation unit). // #include "localintermediate.h" #include "../Include/InfoSink.h" #include "SymbolTable.h" namespace glslang { // // Link-time error emitter. // void TIntermediate::error(TInfoSink& infoSink, const char* message, EShLanguage unitStage) { … } // Link-time warning. void TIntermediate::warn(TInfoSink& infoSink, const char* message, EShLanguage unitStage) { … } // TODO: 4.4 offset/align: "Two blocks linked together in the same program with the same block // name must have the exact same set of members qualified with offset and their integral-constant // expression values must be the same, or a link-time error results." // // Merge the information from 'unit' into 'this' // void TIntermediate::merge(TInfoSink& infoSink, TIntermediate& unit) { … } // // check that link objects between stages // void TIntermediate::mergeUniformObjects(TInfoSink& infoSink, TIntermediate& unit) { … } static inline bool isSameInterface(TIntermSymbol* symbol, EShLanguage stage, TIntermSymbol* unitSymbol, EShLanguage unitStage) { … } static bool isSameSymbol(TIntermSymbol* symbol1, EShLanguage stage1, TIntermSymbol* symbol2, EShLanguage stage2) { … } // // do error checking on the shader boundary in / out vars // void TIntermediate::checkStageIO(TInfoSink& infoSink, TIntermediate& unit) { … } void TIntermediate::mergeCallGraphs(TInfoSink& infoSink, TIntermediate& unit) { … } #define MERGE_MAX(member) … #define MERGE_TRUE(member) … void TIntermediate::mergeModes(TInfoSink& infoSink, TIntermediate& unit) { … } // // Merge the 'unit' AST into 'this' AST. // That includes rationalizing the unique IDs, which were set up independently, // and might have overlaps that are not the same symbol, or might have different // IDs for what should be the same shared symbol. // void TIntermediate::mergeTrees(TInfoSink& infoSink, TIntermediate& unit) { … } static const TString& getNameForIdMap(TIntermSymbol* symbol) { … } // Traverser that seeds an ID map with all built-ins, and tracks the // maximum ID used, currently using (maximum ID + 1) as new symbol id shift seed. // Level id will keep same after shifting. // (It would be nice to put this in a function, but that causes warnings // on having no bodies for the copy-constructor/operator=.) class TBuiltInIdTraverser : public TIntermTraverser { … }; // Traverser that seeds an ID map with non-builtins. // (It would be nice to put this in a function, but that causes warnings // on having no bodies for the copy-constructor/operator=.) class TUserIdTraverser : public TIntermTraverser { … }; // Initialize the the ID map with what we know of 'this' AST. void TIntermediate::seedIdMap(TIdMaps& idMaps, long long& idShift) { … } // Traverser to map an AST ID to what was known from the seeding AST. // (It would be nice to put this in a function, but that causes warnings // on having no bodies for the copy-constructor/operator=.) class TRemapIdTraverser : public TIntermTraverser { … }; void TIntermediate::remapIds(const TIdMaps& idMaps, long long idShift, TIntermediate& unit) { … } // // Merge the function bodies and global-level initializers from unitGlobals into globals. // Will error check duplication of function bodies for the same signature. // void TIntermediate::mergeBodies(TInfoSink& infoSink, TIntermSequence& globals, const TIntermSequence& unitGlobals) { … } // // Global Unfiform block stores any default uniforms (i.e. uniforms without a block) // If two linked stages declare the same member, they are meant to be the same uniform // and need to be in the same block // merge the members of different stages to allow them to be linked properly // as a single block // void TIntermediate::mergeGlobalUniformBlocks(TInfoSink& infoSink, TIntermediate& unit, bool mergeExistingOnly) { … } void TIntermediate::mergeBlockDefinitions(TInfoSink& infoSink, TIntermSymbol* block, TIntermSymbol* unitBlock, TIntermediate* unit) { … } // // 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, EShLanguage unitStage) { … } // TODO 4.5 link functionality: cull distance array size checking // Recursively merge the implicit array sizes through the objects' respective type trees. void TIntermediate::mergeImplicitArraySizes(TType& type, const TType& unitType) { … } // // 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, EShLanguage unitStage) { … } void TIntermediate::sharedBlockCheck(TInfoSink& infoSink) { … } // // 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, including array sizes. // void TIntermediate::finalCheck(TInfoSink& infoSink, bool keepUncalled) { … } // // See if the call graph contains any static recursion, which is disallowed // by the specification. // void TIntermediate::checkCallGraphCycles(TInfoSink& infoSink) { … } // // See which functions are reachable from the entry point and which have bodies. // Reachable ones with missing bodies are errors. // Unreachable bodies are dead code. // void TIntermediate::checkCallGraphBodies(TInfoSink& infoSink, bool keepUncalled) { … } // // Satisfy rules for location qualifiers on inputs and outputs // void TIntermediate::inOutLocationCheck(TInfoSink& infoSink) { … } TIntermAggregate* TIntermediate::findLinkerObjects() const { … } // See if a variable was both a user-declared output and used. // Note: the spec discusses writing to one, but this looks at read or write, which // is more useful, and perhaps the spec should be changed to reflect that. bool TIntermediate::userOutputUsed() const { … } // Accumulate locations used for inputs, outputs, and uniforms, payload, callable data, and tileImageEXT // and check for collisions as the accumulation is done. // // Returns < 0 if no collision, >= 0 if collision and the value returned is a colliding value. // // typeCollision is set to true if there is no direct collision, but the types in the same location // are different. // int TIntermediate::addUsedLocation(const TQualifier& qualifier, const TType& type, bool& typeCollision) { … } // Check that two types can be stored in different components in the same location. // They must be the same type, except signed/unsigned integers are considered compatible. static bool checkCompatibleTypes(TBasicType t1, TBasicType t2) { … } // Compare a new (the passed in) 'range' against the existing set, and see // if there are any collisions. // // Returns < 0 if no collision, >= 0 if collision and the value returned is a colliding value. // int TIntermediate::checkLocationRange(int set, const TIoRange& range, const TType& type, bool& typeCollision) { … } int TIntermediate::checkLocationRT(int set, int location) { … } // Accumulate bindings and offsets, and check for collisions // as the accumulation is done. // // Returns < 0 if no collision, >= 0 if collision and the value returned is a colliding value. // int TIntermediate::addUsedOffsets(int binding, int offset, int numOffsets) { … } // Accumulate used constant_id values. // // Return false is one was already used. bool TIntermediate::addUsedConstantId(int id) { … } // Recursively figure out how many locations are used up by an input or output type. // Return the size of type, as measured by "locations". int TIntermediate::computeTypeLocationSize(const TType& type, EShLanguage stage) { … } // Same as computeTypeLocationSize but for uniforms int TIntermediate::computeTypeUniformLocationSize(const TType& type) { … } // Accumulate xfb buffer ranges and check for collisions as the accumulation is done. // // Returns < 0 if no collision, >= 0 if collision and the value returned is a colliding value. // int TIntermediate::addXfbBufferOffset(const TType& type) { … } // Recursively figure out how many bytes of xfb buffer are used by the given type. // Return the size of type, in bytes. // Sets contains64BitType to true if the type contains a 64-bit data type. // Sets contains32BitType to true if the type contains a 32-bit data type. // Sets contains16BitType to true if the type contains a 16-bit data type. // N.B. Caller must set contains64BitType, contains32BitType, and contains16BitType to false before calling. unsigned int TIntermediate::computeTypeXfbSize(const TType& type, bool& contains64BitType, bool& contains32BitType, bool& contains16BitType) const { … } const int baseAlignmentVec4Std140 = …; // Return the size and alignment of a component of the given type. // The size is returned in the 'size' parameter // Return value is the alignment.. int TIntermediate::getBaseAlignmentScalar(const TType& type, int& size) { … } // Implement base-alignment and size rules from section 7.6.2.2 Standard Uniform Block Layout // Operates recursively. // // If std140 is true, it does the rounding up to vec4 size required by std140, // otherwise it does not, yielding std430 rules. // // The size is returned in the 'size' parameter // // The stride is only non-0 for arrays or matrices, and is the stride of the // top-level object nested within the type. E.g., for an array of matrices, // it is the distances needed between matrices, despite the rules saying the // stride comes from the flattening down to vectors. // // Return value is the alignment of the type. int TIntermediate::getBaseAlignment(const TType& type, int& size, int& stride, TLayoutPacking layoutPacking, bool rowMajor) { … } // To aid the basic HLSL rule about crossing vec4 boundaries. bool TIntermediate::improperStraddle(const TType& type, int size, int offset, bool vectorLike) { … } int TIntermediate::getScalarAlignment(const TType& type, int& size, int& stride, bool rowMajor) { … } int TIntermediate::getMemberAlignment(const TType& type, int& size, int& stride, TLayoutPacking layoutPacking, bool rowMajor) { … } // shared calculation by getOffset and getOffsets void TIntermediate::updateOffset(const TType& parentType, const TType& memberType, int& offset, int& memberSize) { … } // Lookup or calculate the offset of a block member, using the recursively // defined block offset rules. int TIntermediate::getOffset(const TType& type, int index) { … } // Calculate the block data size. // Block arrayness is not taken into account, each element is backed by a separate buffer. int TIntermediate::getBlockSize(const TType& blockType) { … } int TIntermediate::computeBufferReferenceTypeSize(const TType& type) { … } bool TIntermediate::isIoResizeArray(const TType& type, EShLanguage language) { … } } // end namespace glslang