// // Copyright (C) 2002-2005 3Dlabs Inc. Ltd. // Copyright (C) 2012-2015 LunarG, Inc. // Copyright (C) 2015-2020 Google, Inc. // Copyright (C) 2017 ARM Limited. // // 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. // // // Build the intermediate representation. // #include "localintermediate.h" #include "RemoveTree.h" #include "SymbolTable.h" #include "propagateNoContraction.h" #include <cfloat> #include <utility> #include <tuple> namespace glslang { //////////////////////////////////////////////////////////////////////////// // // First set of functions are to help build the intermediate representation. // These functions are not member functions of the nodes. // They are called from parser productions. // ///////////////////////////////////////////////////////////////////////////// // // Add a terminal node for an identifier in an expression. // // Returns the added node. // TIntermSymbol* TIntermediate::addSymbol(long long id, const TString& name, const TType& type, const TConstUnionArray& constArray, TIntermTyped* constSubtree, const TSourceLoc& loc) { … } TIntermSymbol* TIntermediate::addSymbol(const TIntermSymbol& intermSymbol) { … } TIntermSymbol* TIntermediate::addSymbol(const TVariable& variable) { … } TIntermSymbol* TIntermediate::addSymbol(const TVariable& variable, const TSourceLoc& loc) { … } TIntermSymbol* TIntermediate::addSymbol(const TType& type, const TSourceLoc& loc) { … } // // Connect two nodes with a new parent that does a binary operation on the nodes. // // Returns the added node. // // Returns nullptr if the working conversions and promotions could not be found. // TIntermTyped* TIntermediate::addBinaryMath(TOperator op, TIntermTyped* left, TIntermTyped* right, const TSourceLoc& loc) { … } // // Low level: add binary node (no promotions or other argument modifications) // TIntermBinary* TIntermediate::addBinaryNode(TOperator op, TIntermTyped* left, TIntermTyped* right, const TSourceLoc& loc) const { … } // // like non-type form, but sets node's type. // TIntermBinary* TIntermediate::addBinaryNode(TOperator op, TIntermTyped* left, TIntermTyped* right, const TSourceLoc& loc, const TType& type) const { … } // // Low level: add unary node (no promotions or other argument modifications) // TIntermUnary* TIntermediate::addUnaryNode(TOperator op, TIntermTyped* child, const TSourceLoc& loc) const { … } // // like non-type form, but sets node's type. // TIntermUnary* TIntermediate::addUnaryNode(TOperator op, TIntermTyped* child, const TSourceLoc& loc, const TType& type) const { … } // // Connect two nodes through an assignment. // // Returns the added node. // // Returns nullptr if the 'right' type could not be converted to match the 'left' type, // or the resulting operation cannot be properly promoted. // TIntermTyped* TIntermediate::addAssign(TOperator op, TIntermTyped* left, TIntermTyped* right, const TSourceLoc& loc) { … } // // Connect two nodes through an index operator, where the left node is the base // of an array or struct, and the right node is a direct or indirect offset. // // Returns the added node. // The caller should set the type of the returned node. // TIntermTyped* TIntermediate::addIndex(TOperator op, TIntermTyped* base, TIntermTyped* index, const TSourceLoc& loc) { … } // // Add one node as the parent of another that it operates on. // // Returns the added node. // TIntermTyped* TIntermediate::addUnaryMath(TOperator op, TIntermTyped* child, const TSourceLoc& loc) { … } TIntermTyped* TIntermediate::addBuiltInFunctionCall(const TSourceLoc& loc, TOperator op, bool unary, TIntermNode* childNode, const TType& returnType) { … } // // This is the safe way to change the operator on an aggregate, as it // does lots of error checking and fixing. Especially for establishing // a function call's operation on its set of parameters. Sequences // of instructions are also aggregates, but they just directly set // their operator to EOpSequence. // // Returns an aggregate node, which could be the one passed in if // it was already an aggregate. // TIntermTyped* TIntermediate::setAggregateOperator(TIntermNode* node, TOperator op, const TType& type, const TSourceLoc& loc) { … } bool TIntermediate::isConversionAllowed(TOperator op, TIntermTyped* node) const { … } bool TIntermediate::buildConvertOp(TBasicType dst, TBasicType src, TOperator& newOp) const { … } // This is 'mechanism' here, it does any conversion told. // It is about basic type, not about shape. // The policy comes from the shader or the calling code. TIntermTyped* TIntermediate::createConversion(TBasicType convertTo, TIntermTyped* node) const { … } TIntermTyped* TIntermediate::addConversion(TBasicType convertTo, TIntermTyped* node) const { … } // For converting a pair of operands to a binary operation to compatible // types with each other, relative to the operation in 'op'. // This does not cover assignment operations, which is asymmetric in that the // left type is not changeable. // See addConversion(op, type, node) for assignments and unary operation // conversions. // // Generally, this is focused on basic type conversion, not shape conversion. // See addShapeConversion() for shape conversions. // // Returns the converted pair of nodes. // Returns <nullptr, nullptr> when there is no conversion. std::tuple<TIntermTyped*, TIntermTyped*> TIntermediate::addPairConversion(TOperator op, TIntermTyped* node0, TIntermTyped* node1) { … } // // Convert the node's type to the given type, as allowed by the operation involved: 'op'. // For implicit conversions, 'op' is not the requested conversion, it is the explicit // operation requiring the implicit conversion. // // Binary operation conversions should be handled by addConversion(op, node, node), not here. // // Returns a node representing the conversion, which could be the same // node passed in if no conversion was needed. // // Generally, this is focused on basic type conversion, not shape conversion. // See addShapeConversion() for shape conversions. // // Return nullptr if a conversion can't be done. // TIntermTyped* TIntermediate::addConversion(TOperator op, const TType& type, TIntermTyped* node) { … } // Convert the node's shape of type for the given type, as allowed by the // operation involved: 'op'. This is for situations where there is only one // direction to consider doing the shape conversion. // // This implements policy, it call addShapeConversion() for the mechanism. // // Generally, the AST represents allowed GLSL shapes, so this isn't needed // for GLSL. Bad shapes are caught in conversion or promotion. // // Return 'node' if no conversion was done. Promotion handles final shape // checking. // TIntermTyped* TIntermediate::addUniShapeConversion(TOperator op, const TType& type, TIntermTyped* node) { … } // Convert the nodes' shapes to be compatible for the operation 'op'. // // This implements policy, it call addShapeConversion() for the mechanism. // // Generally, the AST represents allowed GLSL shapes, so this isn't needed // for GLSL. Bad shapes are caught in conversion or promotion. // void TIntermediate::addBiShapeConversion(TOperator op, TIntermTyped*& lhsNode, TIntermTyped*& rhsNode) { … } // Convert the node's shape of type for the given type, as allowed by the // operation involved: 'op'. // // Generally, the AST represents allowed GLSL shapes, so this isn't needed // for GLSL. Bad shapes are caught in conversion or promotion. // // Return 'node' if no conversion was done. Promotion handles final shape // checking. // TIntermTyped* TIntermediate::addShapeConversion(const TType& type, TIntermTyped* node) { … } bool TIntermediate::isIntegralPromotion(TBasicType from, TBasicType to) const { … } bool TIntermediate::isFPPromotion(TBasicType from, TBasicType to) const { … } bool TIntermediate::isIntegralConversion(TBasicType from, TBasicType to) const { … } bool TIntermediate::isFPConversion(TBasicType from, TBasicType to) const { … } bool TIntermediate::isFPIntegralConversion(TBasicType from, TBasicType to) const { … } // // See if the 'from' type is allowed to be implicitly converted to the // 'to' type. This is not about vector/array/struct, only about basic type. // bool TIntermediate::canImplicitlyPromote(TBasicType from, TBasicType to, TOperator op) const { … } static bool canSignedIntTypeRepresentAllUnsignedValues(TBasicType sintType, TBasicType uintType) { … } static TBasicType getCorrespondingUnsignedType(TBasicType type) { … } // Implements the following rules // - If either operand has type float64_t or derived from float64_t, // the other shall be converted to float64_t or derived type. // - Otherwise, if either operand has type float32_t or derived from // float32_t, the other shall be converted to float32_t or derived type. // - Otherwise, if either operand has type float16_t or derived from // float16_t, the other shall be converted to float16_t or derived type. // - Otherwise, if both operands have integer types the following rules // shall be applied to the operands: // - If both operands have the same type, no further conversion // is needed. // - Otherwise, if both operands have signed integer types or both // have unsigned integer types, the operand with the type of lesser // integer conversion rank shall be converted to the type of the // operand with greater rank. // - Otherwise, if the operand that has unsigned integer type has rank // greater than or equal to the rank of the type of the other // operand, the operand with signed integer type shall be converted // to the type of the operand with unsigned integer type. // - Otherwise, if the type of the operand with signed integer type can // represent all of the values of the type of the operand with // unsigned integer type, the operand with unsigned integer type // shall be converted to the type of the operand with signed // integer type. // - Otherwise, both operands shall be converted to the unsigned // integer type corresponding to the type of the operand with signed // integer type. std::tuple<TBasicType, TBasicType> TIntermediate::getConversionDestinationType(TBasicType type0, TBasicType type1, TOperator op) const { … } // // Given a type, find what operation would fully construct it. // TOperator TIntermediate::mapTypeToConstructorOp(const TType& type) const { … } // // Safe way to combine two nodes into an aggregate. Works with null pointers, // a node that's not a aggregate yet, etc. // // Returns the resulting aggregate, unless nullptr was passed in for // both existing nodes. // TIntermAggregate* TIntermediate::growAggregate(TIntermNode* left, TIntermNode* right) { … } TIntermAggregate* TIntermediate::growAggregate(TIntermNode* left, TIntermNode* right, const TSourceLoc& loc) { … } TIntermAggregate* TIntermediate::mergeAggregate(TIntermNode* left, TIntermNode* right) { … } TIntermAggregate* TIntermediate::mergeAggregate(TIntermNode* left, TIntermNode* right, const TSourceLoc& loc) { … } // // Turn an existing node into an aggregate. // // Returns an aggregate, unless nullptr was passed in for the existing node. // TIntermAggregate* TIntermediate::makeAggregate(TIntermNode* node) { … } TIntermAggregate* TIntermediate::makeAggregate(TIntermNode* node, const TSourceLoc& loc) { … } // // Make an aggregate with an empty sequence. // TIntermAggregate* TIntermediate::makeAggregate(const TSourceLoc& loc) { … } // // For "if" test nodes. There are three children; a condition, // a true path, and a false path. The two paths are in the // nodePair. // // Returns the selection node created. // TIntermSelection* TIntermediate::addSelection(TIntermTyped* cond, TIntermNodePair nodePair, const TSourceLoc& loc) { … } TIntermTyped* TIntermediate::addComma(TIntermTyped* left, TIntermTyped* right, const TSourceLoc& loc) { … } TIntermTyped* TIntermediate::addMethod(TIntermTyped* object, const TType& type, const TString* name, const TSourceLoc& loc) { … } // // For "?:" test nodes. There are three children; a condition, // a true path, and a false path. The two paths are specified // as separate parameters. For vector 'cond', the true and false // are not paths, but vectors to mix. // // Specialization constant operations include // - The ternary operator ( ? : ) // // Returns the selection node created, or nullptr if one could not be. // TIntermTyped* TIntermediate::addSelection(TIntermTyped* cond, TIntermTyped* trueBlock, TIntermTyped* falseBlock, const TSourceLoc& loc) { … } // // Constant terminal nodes. Has a union that contains bool, float or int constants // // Returns the constant union node created. // TIntermConstantUnion* TIntermediate::addConstantUnion(const TConstUnionArray& unionArray, const TType& t, const TSourceLoc& loc, bool literal) const { … } TIntermConstantUnion* TIntermediate::addConstantUnion(signed char i8, const TSourceLoc& loc, bool literal) const { … } TIntermConstantUnion* TIntermediate::addConstantUnion(unsigned char u8, const TSourceLoc& loc, bool literal) const { … } TIntermConstantUnion* TIntermediate::addConstantUnion(signed short i16, const TSourceLoc& loc, bool literal) const { … } TIntermConstantUnion* TIntermediate::addConstantUnion(unsigned short u16, const TSourceLoc& loc, bool literal) const { … } TIntermConstantUnion* TIntermediate::addConstantUnion(int i, const TSourceLoc& loc, bool literal) const { … } TIntermConstantUnion* TIntermediate::addConstantUnion(unsigned int u, const TSourceLoc& loc, bool literal) const { … } TIntermConstantUnion* TIntermediate::addConstantUnion(long long i64, const TSourceLoc& loc, bool literal) const { … } TIntermConstantUnion* TIntermediate::addConstantUnion(unsigned long long u64, const TSourceLoc& loc, bool literal) const { … } TIntermConstantUnion* TIntermediate::addConstantUnion(bool b, const TSourceLoc& loc, bool literal) const { … } TIntermConstantUnion* TIntermediate::addConstantUnion(double d, TBasicType baseType, const TSourceLoc& loc, bool literal) const { … } TIntermConstantUnion* TIntermediate::addConstantUnion(const TString* s, const TSourceLoc& loc, bool literal) const { … } // Put vector swizzle selectors onto the given sequence void TIntermediate::pushSelector(TIntermSequence& sequence, const TVectorSelector& selector, const TSourceLoc& loc) { … } // Put matrix swizzle selectors onto the given sequence void TIntermediate::pushSelector(TIntermSequence& sequence, const TMatrixSelector& selector, const TSourceLoc& loc) { … } // Make an aggregate node that has a sequence of all selectors. template TIntermTyped* TIntermediate::addSwizzle<TVectorSelector>(TSwizzleSelectors<TVectorSelector>& selector, const TSourceLoc& loc); template TIntermTyped* TIntermediate::addSwizzle<TMatrixSelector>(TSwizzleSelectors<TMatrixSelector>& selector, const TSourceLoc& loc); template<typename selectorType> TIntermTyped* TIntermediate::addSwizzle(TSwizzleSelectors<selectorType>& selector, const TSourceLoc& loc) { … } // // Follow the left branches down to the root of an l-value // expression (just "." and []). // // Return the base of the l-value (where following indexing quits working). // Return nullptr if a chain following dereferences cannot be followed. // // 'swizzleOkay' says whether or not it is okay to consider a swizzle // a valid part of the dereference chain. // // 'bufferReferenceOk' says if type is buffer_reference, the routine will stop to find the most left node. // // 'proc' is an optional function to run on each node that is processed during the traversal. 'proc' must // return true to continue the traversal, or false to end the traversal early. // const TIntermTyped* TIntermediate::traverseLValueBase(const TIntermTyped* node, bool swizzleOkay, bool bufferReferenceOk, std::function<bool(const TIntermNode&)> proc) { … } // // Create while and do-while loop nodes. // TIntermLoop* TIntermediate::addLoop(TIntermNode* body, TIntermTyped* test, TIntermTyped* terminal, bool testFirst, const TSourceLoc& loc) { … } // // Create a for-loop sequence. // TIntermAggregate* TIntermediate::addForLoop(TIntermNode* body, TIntermNode* initializer, TIntermTyped* test, TIntermTyped* terminal, bool testFirst, const TSourceLoc& loc, TIntermLoop*& node) { … } // // Add branches. // TIntermBranch* TIntermediate::addBranch(TOperator branchOp, const TSourceLoc& loc) { … } TIntermBranch* TIntermediate::addBranch(TOperator branchOp, TIntermTyped* expression, const TSourceLoc& loc) { … } // Propagate precision from formal function return type to actual return type, // and on to its subtree. void TIntermBranch::updatePrecision(TPrecisionQualifier parentPrecision) { … } // // This is to be executed after the final root is put on top by the parsing // process. // bool TIntermediate::postProcess(TIntermNode* root, EShLanguage /*language*/) { … } void TIntermediate::addSymbolLinkageNodes(TIntermAggregate*& linkage, EShLanguage language, TSymbolTable& symbolTable) { … } // // Add the given name or symbol to the list of nodes at the end of the tree used // for link-time checking and external linkage. // void TIntermediate::addSymbolLinkageNode(TIntermAggregate*& linkage, TSymbolTable& symbolTable, const TString& name) { … } void TIntermediate::addSymbolLinkageNode(TIntermAggregate*& linkage, const TSymbol& symbol) { … } // // Add a caller->callee relationship to the call graph. // Assumes the strings are unique per signature. // void TIntermediate::addToCallGraph(TInfoSink& /*infoSink*/, const TString& caller, const TString& callee) { … } // // This deletes the tree. // void TIntermediate::removeTree() { … } // // Implement the part of KHR_vulkan_glsl that lists the set of operations // that can result in a specialization constant operation. // // "5.x Specialization Constant Operations" // // Only some operations discussed in this section may be applied to a // specialization constant and still yield a result that is as // specialization constant. The operations allowed are listed below. // When a specialization constant is operated on with one of these // operators and with another constant or specialization constant, the // result is implicitly a specialization constant. // // - int(), uint(), and bool() constructors for type conversions // from any of the following types to any of the following types: // * int // * uint // * bool // - vector versions of the above conversion constructors // - allowed implicit conversions of the above // - swizzles (e.g., foo.yx) // - The following when applied to integer or unsigned integer types: // * unary negative ( - ) // * binary operations ( + , - , * , / , % ) // * shift ( <<, >> ) // * bitwise operations ( & , | , ^ ) // - The following when applied to integer or unsigned integer scalar types: // * comparison ( == , != , > , >= , < , <= ) // - The following when applied to the Boolean scalar type: // * not ( ! ) // * logical operations ( && , || , ^^ ) // * comparison ( == , != )" // // This function just handles binary and unary nodes. Construction // rules are handled in construction paths that are not covered by the unary // and binary paths, while required conversions will still show up here // as unary converters in the from a construction operator. // bool TIntermediate::isSpecializationOperation(const TIntermOperator& node) const { … } // Is the operation one that must propagate nonuniform? bool TIntermediate::isNonuniformPropagating(TOperator op) const { … } //////////////////////////////////////////////////////////////// // // Member functions of the nodes used for building the tree. // //////////////////////////////////////////////////////////////// // // Say whether or not an operation node changes the value of a variable. // // Returns true if state is modified. // bool TIntermOperator::modifiesState() const { … } // // returns true if the operator is for one of the constructors // bool TIntermOperator::isConstructor() const { … } // // Make sure the type of an operator is appropriate for its // combination of operation and operand type. This will invoke // promoteUnary, promoteBinary, etc as needed. // // Returns false if nothing makes sense. // bool TIntermediate::promote(TIntermOperator* node) { … } // // See TIntermediate::promote // bool TIntermediate::promoteUnary(TIntermUnary& node) { … } // Propagate precision qualifiers *up* from children to parent. void TIntermUnary::updatePrecision() { … } // // See TIntermediate::promote // bool TIntermediate::promoteBinary(TIntermBinary& node) { … } // // See TIntermediate::promote // bool TIntermediate::promoteAggregate(TIntermAggregate& node) { … } // Propagate precision qualifiers *up* from children to parent, and then // back *down* again to the children's subtrees. void TIntermAggregate::updatePrecision() { … } // Propagate precision qualifiers *up* from children to parent, and then // back *down* again to the children's subtrees. void TIntermBinary::updatePrecision() { … } // Recursively propagate precision qualifiers *down* the subtree of the current node, // until reaching a node that already has a precision qualifier or otherwise does // not participate in precision propagation. void TIntermTyped::propagatePrecision(TPrecisionQualifier newPrecision) { … } TIntermTyped* TIntermediate::promoteConstantUnion(TBasicType promoteTo, TIntermConstantUnion* node) const { … } void TIntermAggregate::setPragmaTable(const TPragmaTable& pTable) { … } // If either node is a specialization constant, while the other is // a constant (or specialization constant), the result is still // a specialization constant. bool TIntermediate::specConstantPropagates(const TIntermTyped& node1, const TIntermTyped& node2) { … } struct TextureUpgradeAndSamplerRemovalTransform : public TIntermTraverser { … }; void TIntermediate::performTextureUpgradeAndSamplerRemovalTransformation(TIntermNode* root) { … } const char* TIntermediate::getResourceName(TResourceType res) { … } } // end namespace glslang