// // Copyright (C) 2017-2018 Google, Inc. // Copyright (C) 2017 LunarG, 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. // #include "hlslParseHelper.h" #include "hlslScanContext.h" #include "hlslGrammar.h" #include "hlslAttributes.h" #include "../Include/Common.h" #include "../MachineIndependent/Scan.h" #include "../MachineIndependent/preprocessor/PpContext.h" #include <algorithm> #include <functional> #include <cctype> #include <array> #include <set> namespace glslang { HlslParseContext::HlslParseContext(TSymbolTable& symbolTable, TIntermediate& interm, bool parsingBuiltins, int version, EProfile profile, const SpvVersion& spvVersion, EShLanguage language, TInfoSink& infoSink, const TString sourceEntryPointName, bool forwardCompatible, EShMessages messages) : … { … } HlslParseContext::~HlslParseContext() { … } void HlslParseContext::initializeExtensionBehavior() { … } void HlslParseContext::setLimits(const TBuiltInResource& r) { … } // // Parse an array of strings using the parser in HlslRules. // // Returns true for successful acceptance of the shader, false if any errors. // bool HlslParseContext::parseShaderStrings(TPpContext& ppContext, TInputScanner& input, bool versionWillBeError) { … } // // Return true if this l-value node should be converted in some manner. // For instance: turning a load aggregate into a store in an l-value. // bool HlslParseContext::shouldConvertLValue(const TIntermNode* node) const { … } void HlslParseContext::growGlobalUniformBlock(const TSourceLoc& loc, TType& memberType, const TString& memberName, TTypeList* newTypeList) { … } // // Return a TLayoutFormat corresponding to the given texture type. // TLayoutFormat HlslParseContext::getLayoutFromTxType(const TSourceLoc& loc, const TType& txType) { … } // // 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 HlslParseContext::lValueErrorCheck(const TSourceLoc& loc, const char* op, TIntermTyped* node) { … } // // This function handles l-value conversions and verifications. It uses, but is not synonymous // with lValueErrorCheck. That function accepts an l-value directly, while this one must be // given the surrounding tree - e.g, with an assignment, so we can convert the assign into a // series of other image operations. // // Most things are passed through unmodified, except for error checking. // TIntermTyped* HlslParseContext::handleLvalue(const TSourceLoc& loc, const char* op, TIntermTyped*& node) { … } void HlslParseContext::handlePragma(const TSourceLoc& loc, const TVector<TString>& tokens) { … } // // Look at a '.' matrix selector string and change it into components // for a matrix. There are two types: // // _21 second row, first column (one based) // _m21 third row, second column (zero based) // // Returns true if there is no error. // bool HlslParseContext::parseMatrixSwizzleSelector(const TSourceLoc& loc, const TString& fields, int cols, int rows, TSwizzleSelectors<TMatrixSelector>& components) { … } // If the 'comps' express a column of a matrix, // return the column. Column means the first coords all match. // // Otherwise, return -1. // int HlslParseContext::getMatrixComponentsColumn(int rows, const TSwizzleSelectors<TMatrixSelector>& selector) { … } // // Handle seeing a variable identifier in the grammar. // TIntermTyped* HlslParseContext::handleVariable(const TSourceLoc& loc, const TString* string) { … } // // Handle operator[] on any objects it applies to. Currently: // Textures // Buffers // TIntermTyped* HlslParseContext::handleBracketOperator(const TSourceLoc& loc, TIntermTyped* base, TIntermTyped* index) { … } // // Cast index value to a uint if it isn't already (for operator[], load indexes, etc) TIntermTyped* HlslParseContext::makeIntegerIndex(TIntermTyped* index) { … } // // Handle seeing a base[index] dereference in the grammar. // TIntermTyped* HlslParseContext::handleBracketDereference(const TSourceLoc& loc, TIntermTyped* base, TIntermTyped* index) { … } // Handle seeing a binary node with a math operation. TIntermTyped* HlslParseContext::handleBinaryMath(const TSourceLoc& loc, const char* str, TOperator op, TIntermTyped* left, TIntermTyped* right) { … } // Handle seeing a unary node with a math operation. TIntermTyped* HlslParseContext::handleUnaryMath(const TSourceLoc& loc, const char* str, TOperator op, TIntermTyped* childNode) { … } // // Return true if the name is a struct buffer method // bool HlslParseContext::isStructBufferMethod(const TString& name) const { … } // // Handle seeing a base.field dereference in the grammar, where 'field' is a // swizzle or member variable. // TIntermTyped* HlslParseContext::handleDotDereference(const TSourceLoc& loc, TIntermTyped* base, const TString& field) { … } // // Return true if the field should be treated as a built-in method. // Return false otherwise. // bool HlslParseContext::isBuiltInMethod(const TSourceLoc&, TIntermTyped* base, const TString& field) { … } // Independently establish a built-in that is a member of a structure. // 'arraySizes' are what's desired for the independent built-in, whatever // the higher-level source/expression of them was. void HlslParseContext::splitBuiltIn(const TString& baseName, const TType& memberType, const TArraySizes* arraySizes, const TQualifier& outerQualifier) { … } // Split a type into // 1. a struct of non-I/O members // 2. a collection of independent I/O variables void HlslParseContext::split(const TVariable& variable) { … } // Recursive implementation of split(). // Returns reference to the modified type. const TType& HlslParseContext::split(const TType& type, const TString& name, const TQualifier& outerQualifier) { … } // Is this an aggregate that should be flattened? // Can be applied to intermediate levels of type in a hierarchy. // Some things like flattening uniform arrays are only about the top level // of the aggregate, triggered on 'topLevel'. bool HlslParseContext::shouldFlatten(const TType& type, TStorageQualifier qualifier, bool topLevel) const { … } // Top level variable flattening: construct data void HlslParseContext::flatten(const TVariable& variable, bool linkage, bool arrayed) { … } // Recursively flatten the given variable at the provided type, building the flattenData as we go. // // This is mutually recursive with flattenStruct and flattenArray. // We are going to flatten an arbitrarily nested composite structure into a linear sequence of // members, and later on, we want to turn a path through the tree structure into a final // location in this linear sequence. // // If the tree was N-ary, that can be directly calculated. However, we are dealing with // arbitrary numbers - perhaps a struct of 7 members containing an array of 3. Thus, we must // build a data structure to allow the sequence of bracket and dot operators on arrays and // structs to arrive at the proper member. // // To avoid storing a tree with pointers, we are going to flatten the tree into a vector of integers. // The leaves are the indexes into the flattened member array. // Each level will have the next location for the Nth item stored sequentially, so for instance: // // struct { float2 a[2]; int b; float4 c[3] }; // // This will produce the following flattened tree: // Pos: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 // (3, 7, 8, 5, 6, 0, 1, 2, 11, 12, 13, 3, 4, 5} // // Given a reference to mystruct.c[1], the access chain is (2,1), so we traverse: // (0+2) = 8 --> (8+1) = 12 --> 12 = 4 // // so the 4th flattened member in traversal order is ours. // int HlslParseContext::flatten(const TVariable& variable, const TType& type, TFlattenData& flattenData, TString name, bool linkage, const TQualifier& outerQualifier, const TArraySizes* builtInArraySizes) { … } // Add a single flattened member to the flattened data being tracked for the composite // Returns true for the final flattening level. int HlslParseContext::addFlattenedMember(const TVariable& variable, const TType& type, TFlattenData& flattenData, const TString& memberName, bool linkage, const TQualifier& outerQualifier, const TArraySizes* builtInArraySizes) { … } // Figure out the mapping between an aggregate's top members and an // equivalent set of individual variables. // // Assumes shouldFlatten() or equivalent was called first. int HlslParseContext::flattenStruct(const TVariable& variable, const TType& type, TFlattenData& flattenData, TString name, bool linkage, const TQualifier& outerQualifier, const TArraySizes* builtInArraySizes) { … } // Figure out mapping between an array's members and an // equivalent set of individual variables. // // Assumes shouldFlatten() or equivalent was called first. int HlslParseContext::flattenArray(const TVariable& variable, const TType& type, TFlattenData& flattenData, TString name, bool linkage, const TQualifier& outerQualifier) { … } // Return true if we have flattened this node. bool HlslParseContext::wasFlattened(const TIntermTyped* node) const { … } // Return true if we have split this structure bool HlslParseContext::wasSplit(const TIntermTyped* node) const { … } // Turn an access into an aggregate that was flattened to instead be // an access to the individual variable the member was flattened to. // Assumes wasFlattened() or equivalent was called first. TIntermTyped* HlslParseContext::flattenAccess(TIntermTyped* base, int member) { … } TIntermTyped* HlslParseContext::flattenAccess(long long uniqueId, int member, TStorageQualifier outerStorage, const TType& dereferencedType, int subset) { … } // For finding where the first leaf is in a subtree of a multi-level aggregate // that is just getting a subset assigned. Follows the same logic as flattenAccess, // but logically going down the "left-most" tree branch each step of the way. // // Returns the offset into the first leaf of the subset. int HlslParseContext::findSubtreeOffset(const TIntermNode& node) const { … } // Recursively do the desent int HlslParseContext::findSubtreeOffset(const TType& type, int subset, const TVector<int>& offsets) const { if (!type.isArray() && !type.isStruct()) return offsets[subset]; TType derefType(type, 0); return findSubtreeOffset(derefType, offsets[subset], offsets); }; // Find and return the split IO TVariable for id, or nullptr if none. TVariable* HlslParseContext::getSplitNonIoVar(long long id) const { … } // Pass through to base class after remembering built-in mappings. void HlslParseContext::trackLinkage(TSymbol& symbol) { … } // Returns true if the built-in is a clip or cull distance variable. bool HlslParseContext::isClipOrCullDistance(TBuiltInVariable builtIn) { … } // Some types require fixed array sizes in SPIR-V, but can be scalars or // arrays of sizes SPIR-V doesn't allow. For example, tessellation factors. // This creates the right size. A conversion is performed when the internal // type is copied to or from the external type. This corrects the externally // facing input or output type to abide downstream semantics. void HlslParseContext::fixBuiltInIoType(TType& type) { … } // Variables that correspond to the user-interface in and out of a stage // (not the built-in interface) are // - assigned locations // - registered as a linkage node (part of the stage's external interface). // Assumes it is called in the order in which locations should be assigned. void HlslParseContext::assignToInterface(TVariable& variable) { … } // // Handle seeing a function declarator in the grammar. This is the precursor // to recognizing a function prototype or function definition. // void HlslParseContext::handleFunctionDeclarator(const TSourceLoc& loc, TFunction& function, bool prototype) { … } // For struct buffers with counters, we must pass the counter buffer as hidden parameter. // This adds the hidden parameter to the parameter list in 'paramNodes' if needed. // Otherwise, it's a no-op void HlslParseContext::addStructBufferHiddenCounterParam(const TSourceLoc& loc, TParameter& param, TIntermAggregate*& paramNodes) { … } // // Handle seeing the function prototype in front of a function definition in the grammar. // The body is handled after this function returns. // // Returns an aggregate of parameter-symbol nodes. // TIntermAggregate* HlslParseContext::handleFunctionDefinition(const TSourceLoc& loc, TFunction& function, const TAttributes& attributes, TIntermNode*& entryPointTree) { … } // Handle all [attrib] attribute for the shader entry point void HlslParseContext::handleEntryPointAttributes(const TSourceLoc& loc, const TAttributes& attributes) { … } // Update the given type with any type-like attribute information in the // attributes. void HlslParseContext::transferTypeAttributes(const TSourceLoc& loc, const TAttributes& attributes, TType& type, bool allowEntry) { … } // // Do all special handling for the entry point, including wrapping // the shader's entry point with the official entry point that will call it. // // The following: // // retType shaderEntryPoint(args...) // shader declared entry point // { body } // // Becomes // // out retType ret; // in iargs<that are input>...; // out oargs<that are output> ...; // // void shaderEntryPoint() // synthesized, but official, entry point // { // args<that are input> = iargs...; // ret = @shaderEntryPoint(args...); // oargs = args<that are output>...; // } // retType @shaderEntryPoint(args...) // { body } // // The symbol table will still map the original entry point name to the // the modified function and its new name: // // symbol table: shaderEntryPoint -> @shaderEntryPoint // // Returns nullptr if no entry-point tree was built, otherwise, returns // a subtree that creates the entry point. // TIntermNode* HlslParseContext::transformEntryPoint(const TSourceLoc& loc, TFunction& userFunction, const TAttributes& attributes) { … } void HlslParseContext::handleFunctionBody(const TSourceLoc& loc, TFunction& function, TIntermNode* functionBody, TIntermNode*& node) { … } // AST I/O is done through shader globals declared in the 'in' or 'out' // storage class. An HLSL entry point has a return value, input parameters // and output parameters. These need to get remapped to the AST I/O. void HlslParseContext::remapEntryPointIO(TFunction& function, TVariable*& returnValue, TVector<TVariable*>& inputs, TVector<TVariable*>& outputs) { … } // An HLSL function that looks like an entry point, but is not, // declares entry point IO built-ins, but these have to be undone. void HlslParseContext::remapNonEntryPointIO(TFunction& function) { … } TIntermNode* HlslParseContext::handleDeclare(const TSourceLoc& loc, TIntermTyped* var) { … } // Handle function returns, including type conversions to the function return type // if necessary. TIntermNode* HlslParseContext::handleReturnValue(const TSourceLoc& loc, TIntermTyped* value) { … } void HlslParseContext::handleFunctionArgument(TFunction* function, TIntermTyped*& arguments, TIntermTyped* newArg) { … } // FragCoord may require special loading: we can optionally reciprocate W. TIntermTyped* HlslParseContext::assignFromFragCoord(const TSourceLoc& loc, TOperator op, TIntermTyped* left, TIntermTyped* right) { … } // Position may require special handling: we can optionally invert Y. // See: https://github.com/KhronosGroup/glslang/issues/1173 // https://github.com/KhronosGroup/glslang/issues/494 TIntermTyped* HlslParseContext::assignPosition(const TSourceLoc& loc, TOperator op, TIntermTyped* left, TIntermTyped* right) { … } // Clip and cull distance require special handling due to a semantic mismatch. In HLSL, // these can be float scalar, float vector, or arrays of float scalar or float vector. // In SPIR-V, they are arrays of scalar floats in all cases. We must copy individual components // (e.g, both x and y components of a float2) out into the destination float array. // // The values are assigned to sequential members of the output array. The inner dimension // is vector components. The outer dimension is array elements. TIntermAggregate* HlslParseContext::assignClipCullDistance(const TSourceLoc& loc, TOperator op, int semanticId, TIntermTyped* left, TIntermTyped* right) { … } // Some simple source assignments need to be flattened to a sequence // of AST assignments. Catch these and flatten, otherwise, pass through // to intermediate.addAssign(). // // Also, assignment to matrix swizzles requires multiple component assignments, // intercept those as well. TIntermTyped* HlslParseContext::handleAssign(const TSourceLoc& loc, TOperator op, TIntermTyped* left, TIntermTyped* right) { … } // An assignment to matrix swizzle must be decomposed into individual assignments. // These must be selected component-wise from the RHS and stored component-wise // into the LHS. TIntermTyped* HlslParseContext::handleAssignToMatrixSwizzle(const TSourceLoc& loc, TOperator op, TIntermTyped* left, TIntermTyped* right) { … } // // HLSL atomic operations have slightly different arguments than // GLSL/AST/SPIRV. The semantics are converted below in decomposeIntrinsic. // This provides the post-decomposition equivalent opcode. // TOperator HlslParseContext::mapAtomicOp(const TSourceLoc& loc, TOperator op, bool isImage) { … } // // Create a combined sampler/texture from separate sampler and texture. // TIntermAggregate* HlslParseContext::handleSamplerTextureCombine(const TSourceLoc& loc, TIntermTyped* argTex, TIntermTyped* argSampler) { … } // Return true if this a buffer type that has an associated counter buffer. bool HlslParseContext::hasStructBuffCounter(const TType& type) const { … } void HlslParseContext::counterBufferType(const TSourceLoc& loc, TType& type) { … } // declare counter for a structured buffer type void HlslParseContext::declareStructBufferCounter(const TSourceLoc& loc, const TType& bufferType, const TString& name) { … } // return the counter that goes with a given structuredbuffer TIntermTyped* HlslParseContext::getStructBufferCounter(const TSourceLoc& loc, TIntermTyped* buffer) { … } // // Decompose structure buffer methods into AST // void HlslParseContext::decomposeStructBufferMethods(const TSourceLoc& loc, TIntermTyped*& node, TIntermNode* arguments) { … } // Create array of standard sample positions for given sample count. // TODO: remove when a real method to query sample pos exists in SPIR-V. TIntermConstantUnion* HlslParseContext::getSamplePosArray(int count) { … } // // Decompose DX9 and DX10 sample intrinsics & object methods into AST // void HlslParseContext::decomposeSampleMethods(const TSourceLoc& loc, TIntermTyped*& node, TIntermNode* arguments) { … } // // Decompose geometry shader methods // void HlslParseContext::decomposeGeometryMethods(const TSourceLoc& loc, TIntermTyped*& node, TIntermNode* arguments) { … } // // Optionally decompose intrinsics to AST opcodes. // void HlslParseContext::decomposeIntrinsic(const TSourceLoc& loc, TIntermTyped*& node, TIntermNode* arguments) { … } // // Handle seeing function call syntax in the grammar, which could be any of // - .length() method // - constructor // - a call to a built-in function mapped to an operator // - a call to a built-in function that will remain a function call (e.g., texturing) // - user function // - subroutine call (not implemented yet) // TIntermTyped* HlslParseContext::handleFunctionCall(const TSourceLoc& loc, TFunction* function, TIntermTyped* arguments) { … } // An initial argument list is difficult: it can be null, or a single node, // or an aggregate if more than one argument. Add one to the front, maintaining // this lack of uniformity. void HlslParseContext::pushFrontArguments(TIntermTyped* front, TIntermTyped*& arguments) { … } // // HLSL allows mismatched dimensions on vec*mat, mat*vec, vec*vec, and mat*mat. This is a // situation not well suited to resolution in intrinsic selection, but we can do so here, since we // can look at both arguments insert explicit shape changes if required. // void HlslParseContext::addGenMulArgumentConversion(const TSourceLoc& loc, TFunction& call, TIntermTyped*& args) { … } // // Add any needed implicit conversions for function-call arguments to input parameters. // void HlslParseContext::addInputArgumentConversions(const TFunction& function, TIntermTyped*& arguments) { … } // // Add any needed implicit expansion of calling arguments from what the shader listed to what's // internally needed for the AST (given the constraints downstream). // void HlslParseContext::expandArguments(const TSourceLoc& loc, const TFunction& function, TIntermTyped*& arguments) { … } // // Add any needed implicit output conversions for function-call arguments. This // can require a new tree topology, complicated further by whether the function // has a return value. // // Returns a node of a subtree that evaluates to the return value of the function. // TIntermTyped* HlslParseContext::addOutputArgumentConversions(const TFunction& function, TIntermOperator& intermNode) { … } // // Add any needed "hidden" counter buffer arguments for function calls. // // Modifies the 'aggregate' argument if needed. Otherwise, is no-op. // void HlslParseContext::addStructBuffArguments(const TSourceLoc& loc, TIntermAggregate*& aggregate) { … } // // Do additional checking of built-in function calls that is not caught // by normal semantic checks on argument type, extension tagging, etc. // // Assumes there has been a semantically correct match to a built-in function prototype. // void HlslParseContext::builtInOpCheck(const TSourceLoc& loc, const TFunction& fnCandidate, TIntermOperator& callNode) { … } // // Handle seeing something in a grammar production that can be done by calling // a constructor. // // The constructor still must be "handled" by handleFunctionCall(), which will // then call handleConstructor(). // TFunction* HlslParseContext::makeConstructorCall(const TSourceLoc& loc, const TType& type) { … } // // Handle seeing a "COLON semantic" at the end of a type declaration, // by updating the type according to the semantic. // void HlslParseContext::handleSemantic(TSourceLoc loc, TQualifier& qualifier, TBuiltInVariable builtIn, const TString& upperCase) { … } // // Handle seeing something like "PACKOFFSET LEFT_PAREN c[Subcomponent][.component] RIGHT_PAREN" // // 'location' has the "c[Subcomponent]" part. // 'component' points to the "component" part, or nullptr if not present. // void HlslParseContext::handlePackOffset(const TSourceLoc& loc, TQualifier& qualifier, const glslang::TString& location, const glslang::TString* component) { … } // // Handle seeing something like "REGISTER LEFT_PAREN [shader_profile,] Type# RIGHT_PAREN" // // 'profile' points to the shader_profile part, or nullptr if not present. // 'desc' is the type# part. // void HlslParseContext::handleRegister(const TSourceLoc& loc, TQualifier& qualifier, const glslang::TString* profile, const glslang::TString& desc, int subComponent, const glslang::TString* spaceDesc) { … } // Convert to a scalar boolean, or if not allowed by HLSL semantics, // report an error and return nullptr. TIntermTyped* HlslParseContext::convertConditionalExpression(const TSourceLoc& loc, TIntermTyped* condition, bool mustBeScalar) { … } // // Same error message for all places assignments don't work. // void HlslParseContext::assignError(const TSourceLoc& loc, const char* op, TString left, TString right) { … } // // Same error message for all places unary operations don't work. // void HlslParseContext::unaryOpError(const TSourceLoc& loc, const char* op, TString operand) { … } // // Same error message for all binary operations don't work. // void HlslParseContext::binaryOpError(const TSourceLoc& loc, const char* op, TString left, TString right) { … } // // A basic type of EbtVoid is a key that the name string was seen in the source, but // it was not found as a variable in the symbol table. If so, give the error // message and insert a dummy variable in the symbol table to prevent future errors. // void HlslParseContext::variableCheck(TIntermTyped*& nodePtr) { … } // // Both test, and if necessary spit out an error, to see if the node is really // a constant. // void HlslParseContext::constantValueCheck(TIntermTyped* node, const char* token) { … } // // Both test, and if necessary spit out an error, to see if the node is really // an integer. // void HlslParseContext::integerCheck(const TIntermTyped* node, const char* token) { … } // // Both test, and if necessary spit out an error, to see if we are currently // globally scoped. // void HlslParseContext::globalCheck(const TSourceLoc& loc, const char* token) { … } bool HlslParseContext::builtInName(const TString& /*identifier*/) { … } // // Make sure there is enough data and not too many arguments provided to the // constructor to build something of the type of the constructor. Also returns // the type of the constructor. // // Returns true if there was an error in construction. // bool HlslParseContext::constructorError(const TSourceLoc& loc, TIntermNode* node, TFunction& function, TOperator op, TType& type) { … } // See if 'node', in the context of constructing aggregates, is a scalar argument // to a constructor. // bool HlslParseContext::isScalarConstructor(const TIntermNode* node) { … } // Checks to see if a void variable has been declared and raise an error message for such a case // // returns true in case of an error // bool HlslParseContext::voidErrorCheck(const TSourceLoc& loc, const TString& identifier, const TBasicType basicType) { … } // // Fix just a full qualifier (no variables or types yet, but qualifier is complete) at global level. // void HlslParseContext::globalQualifierFix(const TSourceLoc&, TQualifier& qualifier) { … } // // Merge characteristics of the 'src' qualifier into the 'dst'. // void HlslParseContext::mergeQualifiers(TQualifier& dst, const TQualifier& src) { … } // used to flatten the sampler type space into a single dimension // correlates with the declaration of defaultSamplerPrecision[] int HlslParseContext::computeSamplerTypeIndex(TSampler& sampler) { … } // // Do size checking for an array type's size. // void HlslParseContext::arraySizeCheck(const TSourceLoc& loc, TIntermTyped* expr, TArraySize& sizePair) { … } // // Require array to be completely sized // void HlslParseContext::arraySizeRequiredCheck(const TSourceLoc& loc, const TArraySizes& arraySizes) { … } void HlslParseContext::structArrayCheck(const TSourceLoc& /*loc*/, const TType& type) { … } // // 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. // void HlslParseContext::declareArray(const TSourceLoc& loc, const TString& identifier, const TType& type, TSymbol*& symbol, bool track) { … } // // Enforce non-initializer type/qualifier rules. // void HlslParseContext::fixConstInit(const TSourceLoc& loc, const TString& identifier, TType& type, TIntermTyped*& initializer) { … } // // See if the identifier is a built-in symbol that can be redeclared, and if so, // copy the symbol table's read-only built-in variable to the current // global level, where it can be modified based on the passed in type. // // Returns nullptr if no redeclaration took place; meaning a normal declaration still // needs to occur for it, not necessarily an error. // // Returns a redeclared and type-modified variable if a redeclared occurred. // TSymbol* HlslParseContext::redeclareBuiltinVariable(const TSourceLoc& /*loc*/, const TString& identifier, const TQualifier& /*qualifier*/, const TShaderQualifiers& /*publicType*/) { … } // // Generate index to the array element in a structure buffer (SSBO) // TIntermTyped* HlslParseContext::indexStructBufferContent(const TSourceLoc& loc, TIntermTyped* buffer) const { … } // // IFF type is a structuredbuffer/byteaddressbuffer type, return the content // (template) type. E.g, StructuredBuffer<MyType> -> MyType. Else return nullptr. // TType* HlslParseContext::getStructBufferContentType(const TType& type) const { … } // // If an existing struct buffer has a sharable type, then share it. // void HlslParseContext::shareStructBufferType(TType& type) { … } void HlslParseContext::paramFix(TType& type) { … } void HlslParseContext::specializationCheck(const TSourceLoc& loc, const TType& type, const char* op) { … } // // Layout qualifier stuff. // // Put the id's layout qualification into the public type, for qualifiers not having a number set. // This is before we know any type information for error checking. void HlslParseContext::setLayoutQualifier(const TSourceLoc& loc, TQualifier& qualifier, TString& id) { … } // Put the id's layout qualifier value into the public type, for qualifiers having a number set. // This is before we know any type information for error checking. void HlslParseContext::setLayoutQualifier(const TSourceLoc& loc, TQualifier& qualifier, TString& id, const TIntermTyped* node) { … } void HlslParseContext::setSpecConstantId(const TSourceLoc& loc, TQualifier& qualifier, int value) { … } // Merge any layout qualifier information from src into dst, leaving everything else in dst alone // // "More than one layout qualifier may appear in a single declaration. // Additionally, the same layout-qualifier-name can occur multiple times // within a layout qualifier or across multiple layout qualifiers in the // same declaration. When the same layout-qualifier-name occurs // multiple times, in a single declaration, the last occurrence overrides // the former occurrence(s). Further, if such a layout-qualifier-name // will effect subsequent declarations or other observable behavior, it // is only the last occurrence that will have any effect, behaving as if // the earlier occurrence(s) within the declaration are not present. // This is also true for overriding layout-qualifier-names, where one // overrides the other (e.g., row_major vs. column_major); only the last // occurrence has any effect." // void HlslParseContext::mergeObjectLayoutQualifiers(TQualifier& dst, const TQualifier& src, bool inheritOnly) { … } // // Look up a function name in the symbol table, and make sure it is a function. // // First, look for an exact match. If there is none, use the generic selector // TParseContextBase::selectFunction() to find one, parameterized by the // convertible() and better() predicates defined below. // // Return the function symbol if found, otherwise nullptr. // const TFunction* HlslParseContext::findFunction(const TSourceLoc& loc, TFunction& call, bool& builtIn, int& thisDepth, TIntermTyped*& args) { … } // // Do everything necessary to handle a typedef declaration, for a single symbol. // // 'parseType' is the type part of the declaration (to the left) // 'arraySizes' is the arrayness tagged on the identifier (to the right) // void HlslParseContext::declareTypedef(const TSourceLoc& loc, const TString& identifier, const TType& parseType) { … } // Do everything necessary to handle a struct declaration, including // making IO aliases because HLSL allows mixed IO in a struct that specializes // based on the usage (input, output, uniform, none). void HlslParseContext::declareStruct(const TSourceLoc& loc, TString& structName, TType& type) { … } // Lookup a user-type by name. // If found, fill in the type and return the defining symbol. // If not found, return nullptr. TSymbol* HlslParseContext::lookupUserType(const TString& typeName, TType& type) { … } // // Do everything necessary to handle a variable (non-block) declaration. // Either redeclaring a variable, or making a new one, updating the symbol // table, and all error checking. // // Returns a subtree node that computes an initializer, if needed. // Returns nullptr if there is no code to execute for initialization. // // 'parseType' is the type part of the declaration (to the left) // 'arraySizes' is the arrayness tagged on the identifier (to the right) // TIntermNode* HlslParseContext::declareVariable(const TSourceLoc& loc, const TString& identifier, TType& type, TIntermTyped* initializer) { … } // Pick up global defaults from the provide global defaults into dst. void HlslParseContext::inheritGlobalDefaults(TQualifier& dst) const { … } // // Make an internal-only variable whose name is for debug purposes only // and won't be searched for. Callers will only use the return value to use // the variable, not the name to look it up. It is okay if the name // is the same as other names; there won't be any conflict. // TVariable* HlslParseContext::makeInternalVariable(const char* name, const TType& type) const { … } // Make a symbol node holding a new internal temporary variable. TIntermSymbol* HlslParseContext::makeInternalVariableNode(const TSourceLoc& loc, const char* name, const TType& type) const { … } // // Declare a non-array variable, the main point being there is no redeclaration // for resizing allowed. // // Return the successfully declared variable. // TVariable* HlslParseContext::declareNonArray(const TSourceLoc& loc, const TString& identifier, const TType& type, bool track) { … } // Return a declaration of a temporary variable // // This is used to force a variable to be declared in the correct scope // when debug information is being generated. TIntermNode* HlslParseContext::executeDeclaration(const TSourceLoc& loc, TVariable* variable) { … } // // Handle all types of initializers from the grammar. // // Returning nullptr just means there is no code to execute to handle the // initializer, which will, for example, be the case for constant initializers. // // Returns a subtree that accomplished the initialization. // TIntermNode* HlslParseContext::executeInitializer(const TSourceLoc& loc, TIntermTyped* initializer, TVariable* variable) { … } // // Reprocess any initializer-list { ... } parts of the initializer. // Need to hierarchically assign correct types and implicit // conversions. Will do this mimicking the same process used for // creating a constructor-style initializer, ensuring we get the // same form. // // Returns a node representing an expression for the initializer list expressed // as the correct type. // // Returns nullptr if there is an error. // TIntermTyped* HlslParseContext::convertInitializerList(const TSourceLoc& loc, const TType& type, TIntermTyped* initializer, TIntermTyped* scalarInit) { … } // Lengthen list to be long enough to cover any gap from the current list size // to 'size'. If the list is longer, do nothing. // The value to lengthen with is the default for short lists. // // By default, lists that are too short due to lack of initializers initialize to zero. // Alternatively, it could be a scalar initializer for a structure. Both cases are handled, // based on whether something is passed in as 'scalarInit'. // // 'scalarInit' must be safe to use each time this is called (no side effects replication). // void HlslParseContext::lengthenList(const TSourceLoc& loc, TIntermSequence& list, int size, TIntermTyped* scalarInit) { … } // // Test for the correctness of the parameters passed to various constructor functions // and also convert them to the right data type, if allowed and required. // // Returns nullptr for an error or the constructed node (aggregate or typed) for no error. // TIntermTyped* HlslParseContext::handleConstructor(const TSourceLoc& loc, TIntermTyped* node, const TType& type) { … } // Add a constructor, either from the grammar, or other programmatic reasons. // // 'node' is what to construct from. // 'type' is what type to construct. // // Returns the constructed object. // Return nullptr if it can't be done. // TIntermTyped* HlslParseContext::addConstructor(const TSourceLoc& loc, TIntermTyped* node, const TType& type) { … } // Function for constructor implementation. Calls addUnaryMath with appropriate EOp value // for the parameter to the constructor (passed to this function). Essentially, it converts // the parameter types correctly. If a constructor expects an int (like ivec2) and is passed a // float, then float is converted to int. // // Returns nullptr for an error or the constructed node. // TIntermTyped* HlslParseContext::constructBuiltIn(const TType& type, TOperator op, TIntermTyped* node, const TSourceLoc& loc, bool subset) { … } // Convert the array in node to the requested type, which is also an array. // Returns nullptr on failure, otherwise returns aggregate holding the list of // elements needed to construct the array. TIntermTyped* HlslParseContext::convertArray(TIntermTyped* node, const TType& type) { … } // This function tests for the type of the parameters to the structure or array constructor. Raises // an error message if the expected type does not match the parameter passed to the constructor. // // Returns nullptr for an error or the input node itself if the expected and the given parameter types match. // TIntermTyped* HlslParseContext::constructAggregate(TIntermNode* node, const TType& type, int paramCount, const TSourceLoc& loc) { … } // // Do everything needed to add an interface block. // void HlslParseContext::declareBlock(const TSourceLoc& loc, TType& type, const TString* instanceName) { … } // // "For a block, this process applies to the entire block, or until the first member // is reached that has a location layout qualifier. When a block member is declared with a location // qualifier, its location comes from that qualifier: The member's location qualifier overrides the block-level // declaration. Subsequent members are again assigned consecutive locations, based on the newest location, // until the next member declared with a location qualifier. The values used for locations do not have to be // declared in increasing order." void HlslParseContext::fixBlockLocations(const TSourceLoc& loc, TQualifier& qualifier, TTypeList& typeList, bool memberWithLocation, bool memberWithoutLocation) { … } void HlslParseContext::fixXfbOffsets(TQualifier& qualifier, TTypeList& typeList) { … } // Calculate and save the offset of each block member, using the recursively // defined block offset rules and the user-provided offset and align. // // Also, compute and save the total size of the block. For the block's size, arrayness // is not taken into account, as each element is backed by a separate buffer. // void HlslParseContext::fixBlockUniformOffsets(const TQualifier& qualifier, TTypeList& typeList) { … } // For an identifier that is already declared, add more qualification to it. void HlslParseContext::addQualifierToExisting(const TSourceLoc& loc, TQualifier qualifier, const TString& identifier) { … } void HlslParseContext::addQualifierToExisting(const TSourceLoc& loc, TQualifier qualifier, TIdentifierList& identifiers) { … } // // Update the intermediate for the given input geometry // bool HlslParseContext::handleInputGeometry(const TSourceLoc& loc, const TLayoutGeometry& geometry) { … } // // Update the intermediate for the given output geometry // bool HlslParseContext::handleOutputGeometry(const TSourceLoc& loc, const TLayoutGeometry& geometry) { … } // // Selection attributes // void HlslParseContext::handleSelectionAttributes(const TSourceLoc& loc, TIntermSelection* selection, const TAttributes& attributes) { … } // // Switch attributes // void HlslParseContext::handleSwitchAttributes(const TSourceLoc& loc, TIntermSwitch* selection, const TAttributes& attributes) { … } // // Loop attributes // void HlslParseContext::handleLoopAttributes(const TSourceLoc& loc, TIntermLoop* loop, const TAttributes& attributes) { … } // // Updating default qualifier for the case of a declaration with just a qualifier, // no type, block, or identifier. // void HlslParseContext::updateStandaloneQualifierDefaults(const TSourceLoc& loc, const TPublicType& publicType) { … } // // Take the sequence of statements that has been built up since the last case/default, // put it on the list of top-level nodes for the current (inner-most) switch statement, // and follow that by the case/default we are on now. (See switch topology comment on // TIntermSwitch.) // void HlslParseContext::wrapupSwitchSubsequence(TIntermAggregate* statements, TIntermNode* branchNode) { … } // // Turn the top-level node sequence built up of wrapupSwitchSubsequence // into a switch node. // TIntermNode* HlslParseContext::addSwitch(const TSourceLoc& loc, TIntermTyped* expression, TIntermAggregate* lastStatements, const TAttributes& attributes) { … } // Make a new symbol-table level that is made out of the members of a structure. // This should be done as an anonymous struct (name is "") so that the symbol table // finds the members with no explicit reference to a 'this' variable. void HlslParseContext::pushThisScope(const TType& thisStruct, const TVector<TFunctionDeclarator>& functionDeclarators) { … } // Track levels of class/struct/namespace nesting with a prefix string using // the type names separated by the scoping operator. E.g., two levels // would look like: // // outer::inner // // The string is empty when at normal global level. // void HlslParseContext::pushNamespace(const TString& typeName) { … } // Opposite of pushNamespace(), see above void HlslParseContext::popNamespace() { … } // Use the class/struct nesting string to create a global name for // a member of a class/struct. void HlslParseContext::getFullNamespaceName(TString*& name) const { … } // Helper function to add the namespace scope mangling syntax to a string. void HlslParseContext::addScopeMangler(TString& name) { … } // Return true if this has uniform-interface like decorations. bool HlslParseContext::hasUniform(const TQualifier& qualifier) const { … } // Potentially not the opposite of hasUniform(), as if some characteristic is // ever used for more than one thing (e.g., uniform or input), hasUniform() should // say it exists, but clearUniform() should leave it in place. void HlslParseContext::clearUniform(TQualifier& qualifier) { … } // Return false if builtIn by itself doesn't force this qualifier to be an input qualifier. bool HlslParseContext::isInputBuiltIn(const TQualifier& qualifier) const { … } // Return true if there are decorations to preserve for input-like storage. bool HlslParseContext::hasInput(const TQualifier& qualifier) const { … } // Return false if builtIn by itself doesn't force this qualifier to be an output qualifier. bool HlslParseContext::isOutputBuiltIn(const TQualifier& qualifier) const { … } // Return true if there are decorations to preserve for output-like storage. bool HlslParseContext::hasOutput(const TQualifier& qualifier) const { … } // Make the IO decorations etc. be appropriate only for an input interface. void HlslParseContext::correctInput(TQualifier& qualifier) { … } // Make the IO decorations etc. be appropriate only for an output interface. void HlslParseContext::correctOutput(TQualifier& qualifier) { … } // Make the IO decorations etc. be appropriate only for uniform type interfaces. void HlslParseContext::correctUniform(TQualifier& qualifier) { … } // Clear out all IO/Uniform stuff, so this has nothing to do with being an IO interface. void HlslParseContext::clearUniformInputOutput(TQualifier& qualifier) { … } // Set texture return type. Returns success (not all types are valid). bool HlslParseContext::setTextureReturnType(TSampler& sampler, const TType& retType, const TSourceLoc& loc) { … } // Return the sampler return type in retType. void HlslParseContext::getTextureReturnType(const TSampler& sampler, TType& retType) const { … } // Return a symbol for the tessellation linkage variable of the given TBuiltInVariable type TIntermSymbol* HlslParseContext::findTessLinkageSymbol(TBuiltInVariable biType) const { … } // Find the patch constant function (issues error, returns nullptr if not found) const TFunction* HlslParseContext::findPatchConstantFunction(const TSourceLoc& loc) { … } // Finalization step: Add patch constant function invocation void HlslParseContext::addPatchConstantInvocation() { … } // Finalization step: remove unused buffer blocks from linkage (we don't know until the // shader is entirely compiled). // Preserve order of remaining symbols. void HlslParseContext::removeUnusedStructBufferCounters() { … } // Finalization step: patch texture shadow modes to match samplers they were combined with void HlslParseContext::fixTextureShadowModes() { … } // Finalization step: patch append methods to use proper stream output, which isn't known until // main is parsed, which could happen after the append method is parsed. void HlslParseContext::finalizeAppendMethods() { … } // post-processing void HlslParseContext::finish() { … } } // end namespace glslang