// Copyright (c) 2017 Google Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef SOURCE_OPT_FOLD_H_ #define SOURCE_OPT_FOLD_H_ #include <cstdint> #include <vector> #include "source/opt/const_folding_rules.h" #include "source/opt/constants.h" #include "source/opt/def_use_manager.h" #include "source/opt/folding_rules.h" namespace spvtools { namespace opt { class InstructionFolder { public: explicit InstructionFolder(IRContext* context) : context_(context) {} // Returns the result of folding a scalar instruction with the given |opcode| // and |operands|. Each entry in |operands| is a pointer to an // analysis::Constant instance, which should've been created with the constant // manager (See IRContext::get_constant_mgr). // // It is an error to call this function with an opcode that does not pass the // IsFoldableOpcode test. If any error occurs during folding, the folder will // fail with a call to assert. uint32_t FoldScalars( SpvOp opcode, const std::vector<const analysis::Constant*>& operands) const; // Returns the result of performing an operation with the given |opcode| over // constant vectors with |num_dims| dimensions. Each entry in |operands| is a // pointer to an analysis::Constant instance, which should've been created // with the constant manager (See IRContext::get_constant_mgr). // // This function iterates through the given vector type constant operands and // calculates the result for each element of the result vector to return. // Vectors with longer than 32-bit scalar components are not accepted in this // function. // // It is an error to call this function with an opcode that does not pass the // IsFoldableOpcode test. If any error occurs during folding, the folder will // fail with a call to assert. std::vector<uint32_t> FoldVectors( SpvOp opcode, uint32_t num_dims, const std::vector<const analysis::Constant*>& operands) const; // Returns true if |opcode| represents an operation handled by FoldScalars or // FoldVectors. bool IsFoldableOpcode(SpvOp opcode) const; // Returns true if |cst| is supported by FoldScalars and FoldVectors. bool IsFoldableConstant(const analysis::Constant* cst) const; // Returns true if |FoldInstructionToConstant| could fold an instruction whose // result type is |type_inst|. bool IsFoldableType(Instruction* type_inst) const; // Tries to fold |inst| to a single constant, when the input ids to |inst| // have been substituted using |id_map|. Returns a pointer to the OpConstant* // instruction if successful. If necessary, a new constant instruction is // created and placed in the global values section. // // |id_map| is a function that takes one result id and returns another. It // can be used for things like CCP where it is known that some ids contain a // constant, but the instruction itself has not been updated yet. This can // map those ids to the appropriate constants. Instruction* FoldInstructionToConstant( Instruction* inst, std::function<uint32_t(uint32_t)> id_map) const; // Returns true if |inst| can be folded into a simpler instruction. // If |inst| can be simplified, |inst| is overwritten with the simplified // instruction reusing the same result id. // // If |inst| is simplified, it is possible that the resulting code in invalid // because the instruction is in a bad location. Callers of this function // have to handle the following cases: // // 1) An OpPhi becomes and OpCopyObject - If there are OpPhi instruction after // |inst| in a basic block then this is invalid. The caller must fix this // up. bool FoldInstruction(Instruction* inst) const; // Return true if this opcode has a const folding rule associtated with it. bool HasConstFoldingRule(SpvOp opcode) const { return GetConstantFoldingRules().HasFoldingRule(opcode); } private: // Returns a reference to the ConstnatFoldingRules instance. const ConstantFoldingRules& GetConstantFoldingRules() const { return const_folding_rules; } // Returns a reference to the FoldingRules instance. const FoldingRules& GetFoldingRules() const { return folding_rules; } // Returns the single-word result from performing the given unary operation on // the operand value which is passed in as a 32-bit word. uint32_t UnaryOperate(SpvOp opcode, uint32_t operand) const; // Returns the single-word result from performing the given binary operation // on the operand values which are passed in as two 32-bit word. uint32_t BinaryOperate(SpvOp opcode, uint32_t a, uint32_t b) const; // Returns the single-word result from performing the given ternary operation // on the operand values which are passed in as three 32-bit word. uint32_t TernaryOperate(SpvOp opcode, uint32_t a, uint32_t b, uint32_t c) const; // Returns the single-word result from performing the given operation on the // operand words. This only works with 32-bit operations and uses boolean // convention that 0u is false, and anything else is boolean true. // TODO(qining): Support operands other than 32-bit wide. uint32_t OperateWords(SpvOp opcode, const std::vector<uint32_t>& operand_words) const; bool FoldInstructionInternal(Instruction* inst) const; // Returns true if |inst| is a binary operation that takes two integers as // parameters and folds to a constant that can be represented as an unsigned // 32-bit value when the ids have been replaced by |id_map|. If |inst| can be // folded, the resulting value is returned in |*result|. Valid result types // for the instruction are any integer (signed or unsigned) with 32-bits or // less, or a boolean value. bool FoldBinaryIntegerOpToConstant( Instruction* inst, const std::function<uint32_t(uint32_t)>& id_map, uint32_t* result) const; // Returns true if |inst| is a binary operation on two boolean values, and // folds // to a constant boolean value when the ids have been replaced using |id_map|. // If |inst| can be folded, the result value is returned in |*result|. bool FoldBinaryBooleanOpToConstant( Instruction* inst, const std::function<uint32_t(uint32_t)>& id_map, uint32_t* result) const; // Returns true if |inst| can be folded to an constant when the ids have been // substituted using id_map. If it can, the value is returned in |result|. If // not, |result| is unchanged. It is assumed that not all operands are // constant. Those cases are handled by |FoldScalar|. bool FoldIntegerOpToConstant(Instruction* inst, const std::function<uint32_t(uint32_t)>& id_map, uint32_t* result) const; IRContext* context_; // Folding rules used by |FoldInstructionToConstant| and |FoldInstruction|. ConstantFoldingRules const_folding_rules; // Folding rules used by |FoldInstruction|. FoldingRules folding_rules; }; } // namespace opt } // namespace spvtools #endif // SOURCE_OPT_FOLD_H_