// // Copyright (C) 2002-2005 3Dlabs Inc. Ltd. // Copyright (C) 2016 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. // #ifndef _LOCAL_INTERMEDIATE_INCLUDED_ #define _LOCAL_INTERMEDIATE_INCLUDED_ #include "../Include/intermediate.h" #include "../Public/ShaderLang.h" #include "Versions.h" #include #include #include #include #include class TInfoSink; namespace glslang { struct TMatrixSelector { int coord1; // stay agnostic about column/row; this is parse order int coord2; }; typedef int TVectorSelector; const int MaxSwizzleSelectors = 4; template class TSwizzleSelectors { public: TSwizzleSelectors() : size_(0) { } void push_back(selectorType comp) { if (size_ < MaxSwizzleSelectors) components[size_++] = comp; } void resize(int s) { assert(s <= size_); size_ = s; } int size() const { return size_; } selectorType operator[](int i) const { assert(i < MaxSwizzleSelectors); return components[i]; } private: int size_; selectorType components[MaxSwizzleSelectors]; }; // // Some helper structures for TIntermediate. Their contents are encapsulated // by TIntermediate. // // Used for call-graph algorithms for detecting recursion, missing bodies, and dead bodies. // A "call" is a pair: . // There can be duplicates. General assumption is the list is small. struct TCall { TCall(const TString& pCaller, const TString& pCallee) : caller(pCaller), callee(pCallee) { } TString caller; TString callee; bool visited; bool currentPath; bool errorGiven; int calleeBodyPosition; }; // A generic 1-D range. struct TRange { TRange(int start, int last) : start(start), last(last) { } bool overlap(const TRange& rhs) const { return last >= rhs.start && start <= rhs.last; } int start; int last; }; // An IO range is a 3-D rectangle; the set of (location, component, index) triples all lying // within the same location range, component range, and index value. Locations don't alias unless // all other dimensions of their range overlap. struct TIoRange { TIoRange(TRange location, TRange component, TBasicType basicType, int index) : location(location), component(component), basicType(basicType), index(index) { } bool overlap(const TIoRange& rhs) const { return location.overlap(rhs.location) && component.overlap(rhs.component) && index == rhs.index; } TRange location; TRange component; TBasicType basicType; int index; }; // An offset range is a 2-D rectangle; the set of (binding, offset) pairs all lying // within the same binding and offset range. struct TOffsetRange { TOffsetRange(TRange binding, TRange offset) : binding(binding), offset(offset) { } bool overlap(const TOffsetRange& rhs) const { return binding.overlap(rhs.binding) && offset.overlap(rhs.offset); } TRange binding; TRange offset; }; #ifndef GLSLANG_WEB // Things that need to be tracked per xfb buffer. struct TXfbBuffer { TXfbBuffer() : stride(TQualifier::layoutXfbStrideEnd), implicitStride(0), contains64BitType(false), contains32BitType(false), contains16BitType(false) { } std::vector ranges; // byte offsets that have already been assigned unsigned int stride; unsigned int implicitStride; bool contains64BitType; bool contains32BitType; bool contains16BitType; }; #endif // Track a set of strings describing how the module was processed. // This includes command line options, transforms, etc., ideally inclusive enough // to reproduce the steps used to transform the input source to the output. // E.g., see SPIR-V OpModuleProcessed. // Each "process" or "transform" uses is expressed in the form: // process arg0 arg1 arg2 ... // process arg0 arg1 arg2 ... // where everything is textual, and there can be zero or more arguments class TProcesses { public: TProcesses() {} ~TProcesses() {} void addProcess(const char* process) { processes.push_back(process); } void addProcess(const std::string& process) { processes.push_back(process); } void addArgument(int arg) { processes.back().append(" "); std::string argString = std::to_string(arg); processes.back().append(argString); } void addArgument(const char* arg) { processes.back().append(" "); processes.back().append(arg); } void addArgument(const std::string& arg) { processes.back().append(" "); processes.back().append(arg); } void addIfNonZero(const char* process, int value) { if (value != 0) { addProcess(process); addArgument(value); } } const std::vector& getProcesses() const { return processes; } private: std::vector processes; }; class TSymbolTable; class TSymbol; class TVariable; // // Texture and Sampler transformation mode. // enum ComputeDerivativeMode { LayoutDerivativeNone, // default layout as SPV_NV_compute_shader_derivatives not enabled LayoutDerivativeGroupQuads, // derivative_group_quadsNV LayoutDerivativeGroupLinear, // derivative_group_linearNV }; class TIdMaps { public: TMap& operator[](long long i) { return maps[i]; } const TMap& operator[](long long i) const { return maps[i]; } private: TMap maps[EsiCount]; }; class TNumericFeatures { public: TNumericFeatures() : features(0) { } TNumericFeatures(const TNumericFeatures&) = delete; TNumericFeatures& operator=(const TNumericFeatures&) = delete; typedef enum : unsigned int { shader_explicit_arithmetic_types = 1 << 0, shader_explicit_arithmetic_types_int8 = 1 << 1, shader_explicit_arithmetic_types_int16 = 1 << 2, shader_explicit_arithmetic_types_int32 = 1 << 3, shader_explicit_arithmetic_types_int64 = 1 << 4, shader_explicit_arithmetic_types_float16 = 1 << 5, shader_explicit_arithmetic_types_float32 = 1 << 6, shader_explicit_arithmetic_types_float64 = 1 << 7, shader_implicit_conversions = 1 << 8, gpu_shader_fp64 = 1 << 9, gpu_shader_int16 = 1 << 10, gpu_shader_half_float = 1 << 11, } feature; void insert(feature f) { features |= f; } void erase(feature f) { features &= ~f; } bool contains(feature f) const { return (features & f) != 0; } private: unsigned int features; }; // MustBeAssigned wraps a T, asserting that it has been assigned with // operator =() before attempting to read with operator T() or operator ->(). // Used to catch cases where fields are read before they have been assigned. template class MustBeAssigned { public: MustBeAssigned() = default; MustBeAssigned(const T& v) : value(v) {} operator const T&() const { assert(isSet); return value; } const T* operator ->() const { assert(isSet); return &value; } MustBeAssigned& operator = (const T& v) { value = v; isSet = true; return *this; } private: T value; bool isSet = false; }; // // Set of helper functions to help parse and build the tree. // class TIntermediate { public: explicit TIntermediate(EShLanguage l, int v = 0, EProfile p = ENoProfile) : language(l), #ifndef GLSLANG_ANGLE profile(p), version(v), #endif treeRoot(0), resources(TBuiltInResource{}), numEntryPoints(0), numErrors(0), numPushConstants(0), recursive(false), invertY(false), useStorageBuffer(false), invariantAll(false), nanMinMaxClamp(false), depthReplacing(false), uniqueId(0), globalUniformBlockName(""), atomicCounterBlockName(""), globalUniformBlockSet(TQualifier::layoutSetEnd), globalUniformBlockBinding(TQualifier::layoutBindingEnd), atomicCounterBlockSet(TQualifier::layoutSetEnd) #ifndef GLSLANG_WEB , implicitThisName("@this"), implicitCounterName("@count"), source(EShSourceNone), useVulkanMemoryModel(false), invocations(TQualifier::layoutNotSet), vertices(TQualifier::layoutNotSet), inputPrimitive(ElgNone), outputPrimitive(ElgNone), pixelCenterInteger(false), originUpperLeft(false), vertexSpacing(EvsNone), vertexOrder(EvoNone), interlockOrdering(EioNone), pointMode(false), earlyFragmentTests(false), postDepthCoverage(false), depthLayout(EldNone), hlslFunctionality1(false), blendEquations(0), xfbMode(false), multiStream(false), layoutOverrideCoverage(false), geoPassthroughEXT(false), numShaderRecordBlocks(0), computeDerivativeMode(LayoutDerivativeNone), primitives(TQualifier::layoutNotSet), numTaskNVBlocks(0), layoutPrimitiveCulling(false), autoMapBindings(false), autoMapLocations(false), flattenUniformArrays(false), useUnknownFormat(false), hlslOffsets(false), hlslIoMapping(false), useVariablePointers(false), textureSamplerTransformMode(EShTexSampTransKeep), needToLegalize(false), binaryDoubleOutput(false), subgroupUniformControlFlow(false), usePhysicalStorageBuffer(false), spirvRequirement(nullptr), spirvExecutionMode(nullptr), uniformLocationBase(0) #endif { localSize[0] = 1; localSize[1] = 1; localSize[2] = 1; localSizeNotDefault[0] = false; localSizeNotDefault[1] = false; localSizeNotDefault[2] = false; localSizeSpecId[0] = TQualifier::layoutNotSet; localSizeSpecId[1] = TQualifier::layoutNotSet; localSizeSpecId[2] = TQualifier::layoutNotSet; #ifndef GLSLANG_WEB xfbBuffers.resize(TQualifier::layoutXfbBufferEnd); shiftBinding.fill(0); #endif } void setVersion(int v) { #ifndef GLSLANG_ANGLE version = v; #endif } void setProfile(EProfile p) { #ifndef GLSLANG_ANGLE profile = p; #endif } int getVersion() const { return version; } EProfile getProfile() const { return profile; } void setSpv(const SpvVersion& s) { spvVersion = s; // client processes if (spvVersion.vulkan > 0) processes.addProcess("client vulkan100"); if (spvVersion.openGl > 0) processes.addProcess("client opengl100"); // target SPV switch (spvVersion.spv) { case 0: break; case EShTargetSpv_1_0: break; case EShTargetSpv_1_1: processes.addProcess("target-env spirv1.1"); break; case EShTargetSpv_1_2: processes.addProcess("target-env spirv1.2"); break; case EShTargetSpv_1_3: processes.addProcess("target-env spirv1.3"); break; case EShTargetSpv_1_4: processes.addProcess("target-env spirv1.4"); break; case EShTargetSpv_1_5: processes.addProcess("target-env spirv1.5"); break; default: processes.addProcess("target-env spirvUnknown"); break; } // target-environment processes switch (spvVersion.vulkan) { case 0: break; case EShTargetVulkan_1_0: processes.addProcess("target-env vulkan1.0"); break; case EShTargetVulkan_1_1: processes.addProcess("target-env vulkan1.1"); break; case EShTargetVulkan_1_2: processes.addProcess("target-env vulkan1.2"); break; default: processes.addProcess("target-env vulkanUnknown"); break; } if (spvVersion.openGl > 0) processes.addProcess("target-env opengl"); } const SpvVersion& getSpv() const { return spvVersion; } EShLanguage getStage() const { return language; } void addRequestedExtension(const char* extension) { requestedExtensions.insert(extension); } const std::set& getRequestedExtensions() const { return requestedExtensions; } bool isRayTracingStage() const { return language >= EShLangRayGen && language <= EShLangCallableNV; } void setTreeRoot(TIntermNode* r) { treeRoot = r; } TIntermNode* getTreeRoot() const { return treeRoot; } void incrementEntryPointCount() { ++numEntryPoints; } int getNumEntryPoints() const { return numEntryPoints; } int getNumErrors() const { return numErrors; } void addPushConstantCount() { ++numPushConstants; } void setLimits(const TBuiltInResource& r) { resources = r; } const TBuiltInResource& getLimits() const { return resources; } bool postProcess(TIntermNode*, EShLanguage); void removeTree(); void setEntryPointName(const char* ep) { entryPointName = ep; processes.addProcess("entry-point"); processes.addArgument(entryPointName); } void setEntryPointMangledName(const char* ep) { entryPointMangledName = ep; } const std::string& getEntryPointName() const { return entryPointName; } const std::string& getEntryPointMangledName() const { return entryPointMangledName; } void setInvertY(bool invert) { invertY = invert; if (invertY) processes.addProcess("invert-y"); } bool getInvertY() const { return invertY; } #ifdef ENABLE_HLSL void setSource(EShSource s) { source = s; } EShSource getSource() const { return source; } #else void setSource(EShSource s) { assert(s == EShSourceGlsl); (void)s; } EShSource getSource() const { return EShSourceGlsl; } #endif bool isRecursive() const { return recursive; } TIntermSymbol* addSymbol(const TVariable&); TIntermSymbol* addSymbol(const TVariable&, const TSourceLoc&); TIntermSymbol* addSymbol(const TType&, const TSourceLoc&); TIntermSymbol* addSymbol(const TIntermSymbol&); TIntermTyped* addConversion(TOperator, const TType&, TIntermTyped*); std::tuple addPairConversion(TOperator op, TIntermTyped* node0, TIntermTyped* node1); TIntermTyped* addUniShapeConversion(TOperator, const TType&, TIntermTyped*); TIntermTyped* addConversion(TBasicType convertTo, TIntermTyped* node) const; void addBiShapeConversion(TOperator, TIntermTyped*& lhsNode, TIntermTyped*& rhsNode); TIntermTyped* addShapeConversion(const TType&, TIntermTyped*); TIntermTyped* addBinaryMath(TOperator, TIntermTyped* left, TIntermTyped* right, const TSourceLoc&); TIntermTyped* addAssign(TOperator op, TIntermTyped* left, TIntermTyped* right, const TSourceLoc&); TIntermTyped* addIndex(TOperator op, TIntermTyped* base, TIntermTyped* index, const TSourceLoc&); TIntermTyped* addUnaryMath(TOperator, TIntermTyped* child, const TSourceLoc&); TIntermTyped* addBuiltInFunctionCall(const TSourceLoc& line, TOperator, bool unary, TIntermNode*, const TType& returnType); bool canImplicitlyPromote(TBasicType from, TBasicType to, TOperator op = EOpNull) const; bool isIntegralPromotion(TBasicType from, TBasicType to) const; bool isFPPromotion(TBasicType from, TBasicType to) const; bool isIntegralConversion(TBasicType from, TBasicType to) const; bool isFPConversion(TBasicType from, TBasicType to) const; bool isFPIntegralConversion(TBasicType from, TBasicType to) const; TOperator mapTypeToConstructorOp(const TType&) const; TIntermAggregate* growAggregate(TIntermNode* left, TIntermNode* right); TIntermAggregate* growAggregate(TIntermNode* left, TIntermNode* right, const TSourceLoc&); TIntermAggregate* makeAggregate(TIntermNode* node); TIntermAggregate* makeAggregate(TIntermNode* node, const TSourceLoc&); TIntermAggregate* makeAggregate(const TSourceLoc&); TIntermTyped* setAggregateOperator(TIntermNode*, TOperator, const TType& type, const TSourceLoc&); bool areAllChildConst(TIntermAggregate* aggrNode); TIntermSelection* addSelection(TIntermTyped* cond, TIntermNodePair code, const TSourceLoc&); TIntermTyped* addSelection(TIntermTyped* cond, TIntermTyped* trueBlock, TIntermTyped* falseBlock, const TSourceLoc&); TIntermTyped* addComma(TIntermTyped* left, TIntermTyped* right, const TSourceLoc&); TIntermTyped* addMethod(TIntermTyped*, const TType&, const TString*, const TSourceLoc&); TIntermConstantUnion* addConstantUnion(const TConstUnionArray&, const TType&, const TSourceLoc&, bool literal = false) const; TIntermConstantUnion* addConstantUnion(signed char, const TSourceLoc&, bool literal = false) const; TIntermConstantUnion* addConstantUnion(unsigned char, const TSourceLoc&, bool literal = false) const; TIntermConstantUnion* addConstantUnion(signed short, const TSourceLoc&, bool literal = false) const; TIntermConstantUnion* addConstantUnion(unsigned short, const TSourceLoc&, bool literal = false) const; TIntermConstantUnion* addConstantUnion(int, const TSourceLoc&, bool literal = false) const; TIntermConstantUnion* addConstantUnion(unsigned int, const TSourceLoc&, bool literal = false) const; TIntermConstantUnion* addConstantUnion(long long, const TSourceLoc&, bool literal = false) const; TIntermConstantUnion* addConstantUnion(unsigned long long, const TSourceLoc&, bool literal = false) const; TIntermConstantUnion* addConstantUnion(bool, const TSourceLoc&, bool literal = false) const; TIntermConstantUnion* addConstantUnion(double, TBasicType, const TSourceLoc&, bool literal = false) const; TIntermConstantUnion* addConstantUnion(const TString*, const TSourceLoc&, bool literal = false) const; TIntermTyped* promoteConstantUnion(TBasicType, TIntermConstantUnion*) const; bool parseConstTree(TIntermNode*, TConstUnionArray, TOperator, const TType&, bool singleConstantParam = false); TIntermLoop* addLoop(TIntermNode*, TIntermTyped*, TIntermTyped*, bool testFirst, const TSourceLoc&); TIntermAggregate* addForLoop(TIntermNode*, TIntermNode*, TIntermTyped*, TIntermTyped*, bool testFirst, const TSourceLoc&, TIntermLoop*&); TIntermBranch* addBranch(TOperator, const TSourceLoc&); TIntermBranch* addBranch(TOperator, TIntermTyped*, const TSourceLoc&); template TIntermTyped* addSwizzle(TSwizzleSelectors&, const TSourceLoc&); // Low level functions to add nodes (no conversions or other higher level transformations) // If a type is provided, the node's type will be set to it. TIntermBinary* addBinaryNode(TOperator op, TIntermTyped* left, TIntermTyped* right, const TSourceLoc&) const; TIntermBinary* addBinaryNode(TOperator op, TIntermTyped* left, TIntermTyped* right, const TSourceLoc&, const TType&) const; TIntermUnary* addUnaryNode(TOperator op, TIntermTyped* child, const TSourceLoc&) const; TIntermUnary* addUnaryNode(TOperator op, TIntermTyped* child, const TSourceLoc&, const TType&) const; // Constant folding (in Constant.cpp) TIntermTyped* fold(TIntermAggregate* aggrNode); TIntermTyped* foldConstructor(TIntermAggregate* aggrNode); TIntermTyped* foldDereference(TIntermTyped* node, int index, const TSourceLoc&); TIntermTyped* foldSwizzle(TIntermTyped* node, TSwizzleSelectors& fields, const TSourceLoc&); // Tree ops static const TIntermTyped* findLValueBase(const TIntermTyped*, bool swizzleOkay , bool BufferReferenceOk = false); // Linkage related void addSymbolLinkageNodes(TIntermAggregate*& linkage, EShLanguage, TSymbolTable&); void addSymbolLinkageNode(TIntermAggregate*& linkage, const TSymbol&); TIntermAggregate* findLinkerObjects() const; void setGlobalUniformBlockName(const char* name) { globalUniformBlockName = std::string(name); } const char* getGlobalUniformBlockName() const { return globalUniformBlockName.c_str(); } void setGlobalUniformSet(unsigned int set) { globalUniformBlockSet = set; } unsigned int getGlobalUniformSet() const { return globalUniformBlockSet; } void setGlobalUniformBinding(unsigned int binding) { globalUniformBlockBinding = binding; } unsigned int getGlobalUniformBinding() const { return globalUniformBlockBinding; } void setAtomicCounterBlockName(const char* name) { atomicCounterBlockName = std::string(name); } const char* getAtomicCounterBlockName() const { return atomicCounterBlockName.c_str(); } void setAtomicCounterBlockSet(unsigned int set) { atomicCounterBlockSet = set; } unsigned int getAtomicCounterBlockSet() const { return atomicCounterBlockSet; } void setUseStorageBuffer() { useStorageBuffer = true; } bool usingStorageBuffer() const { return useStorageBuffer; } void setInvariantAll() { invariantAll = true; } bool isInvariantAll() const { return invariantAll; } void setDepthReplacing() { depthReplacing = true; } bool isDepthReplacing() const { return depthReplacing; } bool setLocalSize(int dim, int size) { if (localSizeNotDefault[dim]) return size == localSize[dim]; localSizeNotDefault[dim] = true; localSize[dim] = size; return true; } unsigned int getLocalSize(int dim) const { return localSize[dim]; } bool isLocalSizeSet() const { // Return true if any component has been set (i.e. any component is not default). return localSizeNotDefault[0] || localSizeNotDefault[1] || localSizeNotDefault[2]; } bool setLocalSizeSpecId(int dim, int id) { if (localSizeSpecId[dim] != TQualifier::layoutNotSet) return id == localSizeSpecId[dim]; localSizeSpecId[dim] = id; return true; } int getLocalSizeSpecId(int dim) const { return localSizeSpecId[dim]; } bool isLocalSizeSpecialized() const { // Return true if any component has been specialized. return localSizeSpecId[0] != TQualifier::layoutNotSet || localSizeSpecId[1] != TQualifier::layoutNotSet || localSizeSpecId[2] != TQualifier::layoutNotSet; } #ifdef GLSLANG_WEB void output(TInfoSink&, bool tree) { } bool isEsProfile() const { return false; } bool getXfbMode() const { return false; } bool isMultiStream() const { return false; } TLayoutGeometry getOutputPrimitive() const { return ElgNone; } bool getPostDepthCoverage() const { return false; } bool getEarlyFragmentTests() const { return false; } TLayoutDepth getDepth() const { return EldNone; } bool getPixelCenterInteger() const { return false; } void setOriginUpperLeft() { } bool getOriginUpperLeft() const { return true; } TInterlockOrdering getInterlockOrdering() const { return EioNone; } bool getAutoMapBindings() const { return false; } bool getAutoMapLocations() const { return false; } int getNumPushConstants() const { return 0; } void addShaderRecordCount() { } void addTaskNVCount() { } void setUseVulkanMemoryModel() { } bool usingVulkanMemoryModel() const { return false; } bool usingPhysicalStorageBuffer() const { return false; } bool usingVariablePointers() const { return false; } unsigned getXfbStride(int buffer) const { return 0; } bool hasLayoutDerivativeModeNone() const { return false; } ComputeDerivativeMode getLayoutDerivativeModeNone() const { return LayoutDerivativeNone; } #else void output(TInfoSink&, bool tree); bool isEsProfile() const { return profile == EEsProfile; } void setShiftBinding(TResourceType res, unsigned int shift) { shiftBinding[res] = shift; const char* name = getResourceName(res); if (name != nullptr) processes.addIfNonZero(name, shift); } unsigned int getShiftBinding(TResourceType res) const { return shiftBinding[res]; } void setShiftBindingForSet(TResourceType res, unsigned int shift, unsigned int set) { if (shift == 0) // ignore if there's no shift: it's a no-op. return; shiftBindingForSet[res][set] = shift; const char* name = getResourceName(res); if (name != nullptr) { processes.addProcess(name); processes.addArgument(shift); processes.addArgument(set); } } int getShiftBindingForSet(TResourceType res, unsigned int set) const { const auto shift = shiftBindingForSet[res].find(set); return shift == shiftBindingForSet[res].end() ? -1 : shift->second; } bool hasShiftBindingForSet(TResourceType res) const { return !shiftBindingForSet[res].empty(); } void setResourceSetBinding(const std::vector& shift) { resourceSetBinding = shift; if (shift.size() > 0) { processes.addProcess("resource-set-binding"); for (int s = 0; s < (int)shift.size(); ++s) processes.addArgument(shift[s]); } } const std::vector& getResourceSetBinding() const { return resourceSetBinding; } void setAutoMapBindings(bool map) { autoMapBindings = map; if (autoMapBindings) processes.addProcess("auto-map-bindings"); } bool getAutoMapBindings() const { return autoMapBindings; } void setAutoMapLocations(bool map) { autoMapLocations = map; if (autoMapLocations) processes.addProcess("auto-map-locations"); } bool getAutoMapLocations() const { return autoMapLocations; } #ifdef ENABLE_HLSL void setFlattenUniformArrays(bool flatten) { flattenUniformArrays = flatten; if (flattenUniformArrays) processes.addProcess("flatten-uniform-arrays"); } bool getFlattenUniformArrays() const { return flattenUniformArrays; } #endif void setNoStorageFormat(bool b) { useUnknownFormat = b; if (useUnknownFormat) processes.addProcess("no-storage-format"); } bool getNoStorageFormat() const { return useUnknownFormat; } void setUseVulkanMemoryModel() { useVulkanMemoryModel = true; processes.addProcess("use-vulkan-memory-model"); } bool usingVulkanMemoryModel() const { return useVulkanMemoryModel; } void setUsePhysicalStorageBuffer() { usePhysicalStorageBuffer = true; } bool usingPhysicalStorageBuffer() const { return usePhysicalStorageBuffer; } void setUseVariablePointers() { useVariablePointers = true; processes.addProcess("use-variable-pointers"); } bool usingVariablePointers() const { return useVariablePointers; } #ifdef ENABLE_HLSL template T addCounterBufferName(const T& name) const { return name + implicitCounterName; } bool hasCounterBufferName(const TString& name) const { size_t len = strlen(implicitCounterName); return name.size() > len && name.compare(name.size() - len, len, implicitCounterName) == 0; } #endif void setTextureSamplerTransformMode(EShTextureSamplerTransformMode mode) { textureSamplerTransformMode = mode; } int getNumPushConstants() const { return numPushConstants; } void addShaderRecordCount() { ++numShaderRecordBlocks; } void addTaskNVCount() { ++numTaskNVBlocks; } bool setInvocations(int i) { if (invocations != TQualifier::layoutNotSet) return invocations == i; invocations = i; return true; } int getInvocations() const { return invocations; } bool setVertices(int m) { if (vertices != TQualifier::layoutNotSet) return vertices == m; vertices = m; return true; } int getVertices() const { return vertices; } bool setInputPrimitive(TLayoutGeometry p) { if (inputPrimitive != ElgNone) return inputPrimitive == p; inputPrimitive = p; return true; } TLayoutGeometry getInputPrimitive() const { return inputPrimitive; } bool setVertexSpacing(TVertexSpacing s) { if (vertexSpacing != EvsNone) return vertexSpacing == s; vertexSpacing = s; return true; } TVertexSpacing getVertexSpacing() const { return vertexSpacing; } bool setVertexOrder(TVertexOrder o) { if (vertexOrder != EvoNone) return vertexOrder == o; vertexOrder = o; return true; } TVertexOrder getVertexOrder() const { return vertexOrder; } void setPointMode() { pointMode = true; } bool getPointMode() const { return pointMode; } bool setInterlockOrdering(TInterlockOrdering o) { if (interlockOrdering != EioNone) return interlockOrdering == o; interlockOrdering = o; return true; } TInterlockOrdering getInterlockOrdering() const { return interlockOrdering; } void setXfbMode() { xfbMode = true; } bool getXfbMode() const { return xfbMode; } void setMultiStream() { multiStream = true; } bool isMultiStream() const { return multiStream; } bool setOutputPrimitive(TLayoutGeometry p) { if (outputPrimitive != ElgNone) return outputPrimitive == p; outputPrimitive = p; return true; } TLayoutGeometry getOutputPrimitive() const { return outputPrimitive; } void setPostDepthCoverage() { postDepthCoverage = true; } bool getPostDepthCoverage() const { return postDepthCoverage; } void setEarlyFragmentTests() { earlyFragmentTests = true; } bool getEarlyFragmentTests() const { return earlyFragmentTests; } bool setDepth(TLayoutDepth d) { if (depthLayout != EldNone) return depthLayout == d; depthLayout = d; return true; } TLayoutDepth getDepth() const { return depthLayout; } void setOriginUpperLeft() { originUpperLeft = true; } bool getOriginUpperLeft() const { return originUpperLeft; } void setPixelCenterInteger() { pixelCenterInteger = true; } bool getPixelCenterInteger() const { return pixelCenterInteger; } void addBlendEquation(TBlendEquationShift b) { blendEquations |= (1 << b); } unsigned int getBlendEquations() const { return blendEquations; } bool setXfbBufferStride(int buffer, unsigned stride) { if (xfbBuffers[buffer].stride != TQualifier::layoutXfbStrideEnd) return xfbBuffers[buffer].stride == stride; xfbBuffers[buffer].stride = stride; return true; } unsigned getXfbStride(int buffer) const { return xfbBuffers[buffer].stride; } int addXfbBufferOffset(const TType&); unsigned int computeTypeXfbSize(const TType&, bool& contains64BitType, bool& contains32BitType, bool& contains16BitType) const; unsigned int computeTypeXfbSize(const TType&, bool& contains64BitType) const; void setLayoutOverrideCoverage() { layoutOverrideCoverage = true; } bool getLayoutOverrideCoverage() const { return layoutOverrideCoverage; } void setGeoPassthroughEXT() { geoPassthroughEXT = true; } bool getGeoPassthroughEXT() const { return geoPassthroughEXT; } void setLayoutDerivativeMode(ComputeDerivativeMode mode) { computeDerivativeMode = mode; } bool hasLayoutDerivativeModeNone() const { return computeDerivativeMode != LayoutDerivativeNone; } ComputeDerivativeMode getLayoutDerivativeModeNone() const { return computeDerivativeMode; } void setLayoutPrimitiveCulling() { layoutPrimitiveCulling = true; } bool getLayoutPrimitiveCulling() const { return layoutPrimitiveCulling; } bool setPrimitives(int m) { if (primitives != TQualifier::layoutNotSet) return primitives == m; primitives = m; return true; } int getPrimitives() const { return primitives; } const char* addSemanticName(const TString& name) { return semanticNameSet.insert(name).first->c_str(); } void addUniformLocationOverride(const char* nameStr, int location) { std::string name = nameStr; uniformLocationOverrides[name] = location; } int getUniformLocationOverride(const char* nameStr) const { std::string name = nameStr; auto pos = uniformLocationOverrides.find(name); if (pos == uniformLocationOverrides.end()) return -1; else return pos->second; } void setUniformLocationBase(int base) { uniformLocationBase = base; } int getUniformLocationBase() const { return uniformLocationBase; } void setNeedsLegalization() { needToLegalize = true; } bool needsLegalization() const { return needToLegalize; } void setBinaryDoubleOutput() { binaryDoubleOutput = true; } bool getBinaryDoubleOutput() { return binaryDoubleOutput; } void setSubgroupUniformControlFlow() { subgroupUniformControlFlow = true; } bool getSubgroupUniformControlFlow() const { return subgroupUniformControlFlow; } // GL_EXT_spirv_intrinsics void insertSpirvRequirement(const TSpirvRequirement* spirvReq); bool hasSpirvRequirement() const { return spirvRequirement != nullptr; } const TSpirvRequirement& getSpirvRequirement() const { return *spirvRequirement; } void insertSpirvExecutionMode(int executionMode, const TIntermAggregate* args = nullptr); void insertSpirvExecutionModeId(int executionMode, const TIntermAggregate* args); bool hasSpirvExecutionMode() const { return spirvExecutionMode != nullptr; } const TSpirvExecutionMode& getSpirvExecutionMode() const { return *spirvExecutionMode; } #endif // GLSLANG_WEB void addBlockStorageOverride(const char* nameStr, TBlockStorageClass backing) { std::string name(nameStr); blockBackingOverrides[name] = backing; } TBlockStorageClass getBlockStorageOverride(const char* nameStr) const { std::string name = nameStr; auto pos = blockBackingOverrides.find(name); if (pos == blockBackingOverrides.end()) return EbsNone; else return pos->second; } #ifdef ENABLE_HLSL void setHlslFunctionality1() { hlslFunctionality1 = true; } bool getHlslFunctionality1() const { return hlslFunctionality1; } void setHlslOffsets() { hlslOffsets = true; if (hlslOffsets) processes.addProcess("hlsl-offsets"); } bool usingHlslOffsets() const { return hlslOffsets; } void setHlslIoMapping(bool b) { hlslIoMapping = b; if (hlslIoMapping) processes.addProcess("hlsl-iomap"); } bool usingHlslIoMapping() { return hlslIoMapping; } #else bool getHlslFunctionality1() const { return false; } bool usingHlslOffsets() const { return false; } bool usingHlslIoMapping() { return false; } #endif bool usingScalarBlockLayout() const { for (auto extIt = requestedExtensions.begin(); extIt != requestedExtensions.end(); ++extIt) { if (*extIt == E_GL_EXT_scalar_block_layout) return true; } return false; } bool IsRequestedExtension(const char* extension) const { return (requestedExtensions.find(extension) != requestedExtensions.end()); } void addToCallGraph(TInfoSink&, const TString& caller, const TString& callee); void merge(TInfoSink&, TIntermediate&); void finalCheck(TInfoSink&, bool keepUncalled); void mergeGlobalUniformBlocks(TInfoSink& infoSink, TIntermediate& unit, bool mergeExistingOnly); void mergeUniformObjects(TInfoSink& infoSink, TIntermediate& unit); void checkStageIO(TInfoSink&, TIntermediate&); bool buildConvertOp(TBasicType dst, TBasicType src, TOperator& convertOp) const; TIntermTyped* createConversion(TBasicType convertTo, TIntermTyped* node) const; void addIoAccessed(const TString& name) { ioAccessed.insert(name); } bool inIoAccessed(const TString& name) const { return ioAccessed.find(name) != ioAccessed.end(); } int addUsedLocation(const TQualifier&, const TType&, bool& typeCollision); int checkLocationRange(int set, const TIoRange& range, const TType&, bool& typeCollision); int checkLocationRT(int set, int location); int addUsedOffsets(int binding, int offset, int numOffsets); bool addUsedConstantId(int id); static int computeTypeLocationSize(const TType&, EShLanguage); static int computeTypeUniformLocationSize(const TType&); static int getBaseAlignmentScalar(const TType&, int& size); static int getBaseAlignment(const TType&, int& size, int& stride, TLayoutPacking layoutPacking, bool rowMajor); static int getScalarAlignment(const TType&, int& size, int& stride, bool rowMajor); static int getMemberAlignment(const TType&, int& size, int& stride, TLayoutPacking layoutPacking, bool rowMajor); static bool improperStraddle(const TType& type, int size, int offset); static void updateOffset(const TType& parentType, const TType& memberType, int& offset, int& memberSize); static int getOffset(const TType& type, int index); static int getBlockSize(const TType& blockType); static int computeBufferReferenceTypeSize(const TType&); static bool isIoResizeArray(const TType& type, EShLanguage language); bool promote(TIntermOperator*); void setNanMinMaxClamp(bool setting) { nanMinMaxClamp = setting; } bool getNanMinMaxClamp() const { return nanMinMaxClamp; } void setSourceFile(const char* file) { if (file != nullptr) sourceFile = file; } const std::string& getSourceFile() const { return sourceFile; } void addSourceText(const char* text, size_t len) { sourceText.append(text, len); } const std::string& getSourceText() const { return sourceText; } const std::map& getIncludeText() const { return includeText; } void addIncludeText(const char* name, const char* text, size_t len) { includeText[name].assign(text,len); } void addProcesses(const std::vector& p) { for (int i = 0; i < (int)p.size(); ++i) processes.addProcess(p[i]); } void addProcess(const std::string& process) { processes.addProcess(process); } void addProcessArgument(const std::string& arg) { processes.addArgument(arg); } const std::vector& getProcesses() const { return processes.getProcesses(); } unsigned long long getUniqueId() const { return uniqueId; } void setUniqueId(unsigned long long id) { uniqueId = id; } // Certain explicit conversions are allowed conditionally #ifdef GLSLANG_WEB bool getArithemeticInt8Enabled() const { return false; } bool getArithemeticInt16Enabled() const { return false; } bool getArithemeticFloat16Enabled() const { return false; } void updateNumericFeature(TNumericFeatures::feature f, bool on) { } #else bool getArithemeticInt8Enabled() const { return numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types) || numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types_int8); } bool getArithemeticInt16Enabled() const { return numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types) || numericFeatures.contains(TNumericFeatures::gpu_shader_int16) || numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types_int16); } bool getArithemeticFloat16Enabled() const { return numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types) || numericFeatures.contains(TNumericFeatures::gpu_shader_half_float) || numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types_float16); } void updateNumericFeature(TNumericFeatures::feature f, bool on) { on ? numericFeatures.insert(f) : numericFeatures.erase(f); } #endif protected: TIntermSymbol* addSymbol(long long Id, const TString&, const TType&, const TConstUnionArray&, TIntermTyped* subtree, const TSourceLoc&); void error(TInfoSink& infoSink, const char*); void warn(TInfoSink& infoSink, const char*); void mergeCallGraphs(TInfoSink&, TIntermediate&); void mergeModes(TInfoSink&, TIntermediate&); void mergeTrees(TInfoSink&, TIntermediate&); void seedIdMap(TIdMaps& idMaps, long long& IdShift); void remapIds(const TIdMaps& idMaps, long long idShift, TIntermediate&); void mergeBodies(TInfoSink&, TIntermSequence& globals, const TIntermSequence& unitGlobals); void mergeLinkerObjects(TInfoSink&, TIntermSequence& linkerObjects, const TIntermSequence& unitLinkerObjects, EShLanguage); void mergeBlockDefinitions(TInfoSink&, TIntermSymbol* block, TIntermSymbol* unitBlock, TIntermediate* unitRoot); void mergeImplicitArraySizes(TType&, const TType&); void mergeErrorCheck(TInfoSink&, const TIntermSymbol&, const TIntermSymbol&, EShLanguage); void checkCallGraphCycles(TInfoSink&); void checkCallGraphBodies(TInfoSink&, bool keepUncalled); void inOutLocationCheck(TInfoSink&); void sharedBlockCheck(TInfoSink&); bool userOutputUsed() const; bool isSpecializationOperation(const TIntermOperator&) const; bool isNonuniformPropagating(TOperator) const; bool promoteUnary(TIntermUnary&); bool promoteBinary(TIntermBinary&); void addSymbolLinkageNode(TIntermAggregate*& linkage, TSymbolTable&, const TString&); bool promoteAggregate(TIntermAggregate&); void pushSelector(TIntermSequence&, const TVectorSelector&, const TSourceLoc&); void pushSelector(TIntermSequence&, const TMatrixSelector&, const TSourceLoc&); bool specConstantPropagates(const TIntermTyped&, const TIntermTyped&); void performTextureUpgradeAndSamplerRemovalTransformation(TIntermNode* root); bool isConversionAllowed(TOperator op, TIntermTyped* node) const; std::tuple getConversionDestinationType(TBasicType type0, TBasicType type1, TOperator op) const; static const char* getResourceName(TResourceType); const EShLanguage language; // stage, known at construction time std::string entryPointName; std::string entryPointMangledName; typedef std::list TGraph; TGraph callGraph; #ifdef GLSLANG_ANGLE const EProfile profile = ECoreProfile; const int version = 450; #else EProfile profile; // source profile int version; // source version #endif SpvVersion spvVersion; TIntermNode* treeRoot; std::set requestedExtensions; // cumulation of all enabled or required extensions; not connected to what subset of the shader used them MustBeAssigned resources; int numEntryPoints; int numErrors; int numPushConstants; bool recursive; bool invertY; bool useStorageBuffer; bool invariantAll; bool nanMinMaxClamp; // true if desiring min/max/clamp to favor non-NaN over NaN bool depthReplacing; int localSize[3]; bool localSizeNotDefault[3]; int localSizeSpecId[3]; unsigned long long uniqueId; std::string globalUniformBlockName; std::string atomicCounterBlockName; unsigned int globalUniformBlockSet; unsigned int globalUniformBlockBinding; unsigned int atomicCounterBlockSet; #ifndef GLSLANG_WEB public: const char* const implicitThisName; const char* const implicitCounterName; protected: EShSource source; // source language, known a bit later bool useVulkanMemoryModel; int invocations; int vertices; TLayoutGeometry inputPrimitive; TLayoutGeometry outputPrimitive; bool pixelCenterInteger; bool originUpperLeft; TVertexSpacing vertexSpacing; TVertexOrder vertexOrder; TInterlockOrdering interlockOrdering; bool pointMode; bool earlyFragmentTests; bool postDepthCoverage; TLayoutDepth depthLayout; bool hlslFunctionality1; int blendEquations; // an 'or'ing of masks of shifts of TBlendEquationShift bool xfbMode; std::vector xfbBuffers; // all the data we need to track per xfb buffer bool multiStream; bool layoutOverrideCoverage; bool geoPassthroughEXT; int numShaderRecordBlocks; ComputeDerivativeMode computeDerivativeMode; int primitives; int numTaskNVBlocks; bool layoutPrimitiveCulling; // Base shift values std::array shiftBinding; // Per-descriptor-set shift values std::array, EResCount> shiftBindingForSet; std::vector resourceSetBinding; bool autoMapBindings; bool autoMapLocations; bool flattenUniformArrays; bool useUnknownFormat; bool hlslOffsets; bool hlslIoMapping; bool useVariablePointers; std::set semanticNameSet; EShTextureSamplerTransformMode textureSamplerTransformMode; bool needToLegalize; bool binaryDoubleOutput; bool subgroupUniformControlFlow; bool usePhysicalStorageBuffer; TSpirvRequirement* spirvRequirement; TSpirvExecutionMode* spirvExecutionMode; std::unordered_map uniformLocationOverrides; int uniformLocationBase; TNumericFeatures numericFeatures; #endif std::unordered_map blockBackingOverrides; std::unordered_set usedConstantId; // specialization constant ids used std::vector usedAtomics; // sets of bindings used by atomic counters std::vector usedIo[4]; // sets of used locations, one for each of in, out, uniform, and buffers std::vector usedIoRT[2]; // sets of used location, one for rayPayload/rayPayloadIN and other // for callableData/callableDataIn // set of names of statically read/written I/O that might need extra checking std::set ioAccessed; // source code of shader, useful as part of debug information std::string sourceFile; std::string sourceText; // Included text. First string is a name, second is the included text std::map includeText; // for OpModuleProcessed, or equivalent TProcesses processes; private: void operator=(TIntermediate&); // prevent assignments }; } // end namespace glslang #endif // _LOCAL_INTERMEDIATE_INCLUDED_