From a45d4cac61278e26fe9e352962d03d2b15bc08c5 Mon Sep 17 00:00:00 2001 From: Steven Perron Date: Thu, 5 Jul 2018 14:19:50 -0400 Subject: [PATCH] Move folding routines into a class The folding routines are currently global functions. They also rely on data in an std::map that holds the folding rules for each opcode. This causes that map to not have a clear owner, and therefore never gets deleted. There has been a request to delete this map. To implement this, we will create a InstructionFolder class that owns the maps. The IRContext will own the InstructionFolder instance. Then the global functions will become public memeber functions of the InstructionFolder. Fixes https://github.com/KhronosGroup/SPIRV-Tools/issues/1659. --- source/opt/ccp_pass.cpp | 3 +- source/opt/const_folding_rules.cpp | 2 + source/opt/const_folding_rules.h | 5 - source/opt/fold.cpp | 89 +++------ source/opt/fold.h | 187 ++++++++++++------ ...ld_spec_constant_op_and_composite_pass.cpp | 6 +- source/opt/folding_rules.cpp | 111 +++++------ source/opt/folding_rules.h | 5 +- source/opt/instruction.cpp | 7 +- source/opt/ir_context.h | 10 + source/opt/simplification_pass.cpp | 8 +- test/opt/fold_test.cpp | 24 +-- 12 files changed, 257 insertions(+), 200 deletions(-) diff --git a/source/opt/ccp_pass.cpp b/source/opt/ccp_pass.cpp index e9044ad41..b64eadfcf 100644 --- a/source/opt/ccp_pass.cpp +++ b/source/opt/ccp_pass.cpp @@ -134,7 +134,8 @@ SSAPropagator::PropStatus CCPPass::VisitAssignment(ir::Instruction* instr) { return it->second; }; ir::Instruction* folded_inst = - opt::FoldInstructionToConstant(instr, map_func); + context()->get_instruction_folder().FoldInstructionToConstant(instr, + map_func); if (folded_inst != nullptr) { // We do not want to change the body of the function by adding new // instructions. When folding we can only generate new constants. diff --git a/source/opt/const_folding_rules.cpp b/source/opt/const_folding_rules.cpp index 0f9956f83..213ce1106 100644 --- a/source/opt/const_folding_rules.cpp +++ b/source/opt/const_folding_rules.cpp @@ -14,6 +14,8 @@ #include "const_folding_rules.h" +#include "ir_context.h" + namespace spvtools { namespace opt { diff --git a/source/opt/const_folding_rules.h b/source/opt/const_folding_rules.h index 2d9ecbaa3..49251427d 100644 --- a/source/opt/const_folding_rules.h +++ b/source/opt/const_folding_rules.h @@ -18,11 +18,6 @@ #include #include "constants.h" -#include "def_use_manager.h" -#include "folding_rules.h" -#include "ir_builder.h" -#include "ir_context.h" -#include "latest_version_spirv_header.h" namespace spvtools { namespace opt { diff --git a/source/opt/fold.cpp b/source/opt/fold.cpp index 844aafdf8..0e24b0b28 100644 --- a/source/opt/fold.cpp +++ b/source/opt/fold.cpp @@ -41,9 +41,9 @@ namespace { #define UINT32_MAX 0xffffffff /* 4294967295U */ #endif -// 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) { +} // namespace + +uint32_t InstructionFolder::UnaryOperate(SpvOp opcode, uint32_t operand) const { switch (opcode) { // Arthimetics case SpvOp::SpvOpSNegate: @@ -59,9 +59,8 @@ uint32_t UnaryOperate(SpvOp opcode, uint32_t operand) { } } -// 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) { +uint32_t InstructionFolder::BinaryOperate(SpvOp opcode, uint32_t a, + uint32_t b) const { switch (opcode) { // Arthimetics case SpvOp::SpvOpIAdd: @@ -150,9 +149,8 @@ uint32_t BinaryOperate(SpvOp opcode, uint32_t a, uint32_t b) { } } -// 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) { +uint32_t InstructionFolder::TernaryOperate(SpvOp opcode, uint32_t a, uint32_t b, + uint32_t c) const { switch (opcode) { case SpvOp::SpvOpSelect: return (static_cast(a)) ? b : c; @@ -163,12 +161,8 @@ uint32_t TernaryOperate(SpvOp opcode, uint32_t a, uint32_t b, uint32_t c) { } } -// 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& operand_words) { +uint32_t InstructionFolder::OperateWords( + SpvOp opcode, const std::vector& operand_words) const { switch (operand_words.size()) { case 1: return UnaryOperate(opcode, operand_words.front()); @@ -183,7 +177,7 @@ uint32_t OperateWords(SpvOp opcode, } } -bool FoldInstructionInternal(ir::Instruction* inst) { +bool InstructionFolder::FoldInstructionInternal(ir::Instruction* inst) const { ir::IRContext* context = inst->context(); auto identity_map = [](uint32_t id) { return id; }; ir::Instruction* folded_inst = FoldInstructionToConstant(inst, identity_map); @@ -199,8 +193,7 @@ bool FoldInstructionInternal(ir::Instruction* inst) { std::vector constants = const_manager->GetOperandConstants(inst); - static FoldingRules* rules = new FoldingRules(); - for (FoldingRule rule : rules->GetRulesForOpcode(opcode)) { + for (FoldingRule rule : GetFoldingRules().GetRulesForOpcode(opcode)) { if (rule(inst, constants)) { return true; } @@ -208,19 +201,13 @@ bool FoldInstructionInternal(ir::Instruction* inst) { return false; } -} // namespace - -const ConstantFoldingRules& GetConstantFoldingRules() { - static ConstantFoldingRules* rules = new ConstantFoldingRules(); - return *rules; -} - // Returns the result of performing an operation on scalar constant operands. // This function extracts the operand values as 32 bit words and returns the // result in 32 bit word. Scalar constants with longer than 32-bit width are // not accepted in this function. -uint32_t FoldScalars(SpvOp opcode, - const std::vector& operands) { +uint32_t InstructionFolder::FoldScalars( + SpvOp opcode, + const std::vector& operands) const { assert(IsFoldableOpcode(opcode) && "Unhandled instruction opcode in FoldScalars"); std::vector operand_values_in_raw_words; @@ -242,15 +229,9 @@ uint32_t FoldScalars(SpvOp opcode, return OperateWords(opcode, operand_values_in_raw_words); } -// 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(ir::Instruction* inst, - std::function id_map, - uint32_t* result) { +bool InstructionFolder::FoldBinaryIntegerOpToConstant( + ir::Instruction* inst, std::function id_map, + uint32_t* result) const { SpvOp opcode = inst->opcode(); ir::IRContext* context = inst->context(); analysis::ConstantManager* const_manger = context->get_constant_mgr(); @@ -432,12 +413,9 @@ bool FoldBinaryIntegerOpToConstant(ir::Instruction* inst, return false; } -// 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(ir::Instruction* inst, - std::function id_map, - uint32_t* result) { +bool InstructionFolder::FoldBinaryBooleanOpToConstant( + ir::Instruction* inst, std::function id_map, + uint32_t* result) const { SpvOp opcode = inst->opcode(); ir::IRContext* context = inst->context(); analysis::ConstantManager* const_manger = context->get_constant_mgr(); @@ -484,13 +462,9 @@ bool FoldBinaryBooleanOpToConstant(ir::Instruction* inst, return false; } -// 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(ir::Instruction* inst, - std::function id_map, - uint32_t* result) { +bool InstructionFolder::FoldIntegerOpToConstant( + ir::Instruction* inst, std::function id_map, + uint32_t* result) const { assert(IsFoldableOpcode(inst->opcode()) && "Unhandled instruction opcode in FoldScalars"); switch (inst->NumInOperands()) { @@ -502,9 +476,9 @@ bool FoldIntegerOpToConstant(ir::Instruction* inst, } } -std::vector FoldVectors( +std::vector InstructionFolder::FoldVectors( SpvOp opcode, uint32_t num_dims, - const std::vector& operands) { + const std::vector& operands) const { assert(IsFoldableOpcode(opcode) && "Unhandled instruction opcode in FoldVectors"); std::vector result; @@ -547,7 +521,7 @@ std::vector FoldVectors( return result; } -bool IsFoldableOpcode(SpvOp opcode) { +bool InstructionFolder::IsFoldableOpcode(SpvOp opcode) const { // NOTE: Extend to more opcodes as new cases are handled in the folder // functions. switch (opcode) { @@ -589,7 +563,8 @@ bool IsFoldableOpcode(SpvOp opcode) { } } -bool IsFoldableConstant(const analysis::Constant* cst) { +bool InstructionFolder::IsFoldableConstant( + const analysis::Constant* cst) const { // Currently supported constants are 32-bit values or null constants. if (const analysis::ScalarConstant* scalar = cst->AsScalarConstant()) return scalar->words().size() == 1; @@ -597,8 +572,8 @@ bool IsFoldableConstant(const analysis::Constant* cst) { return cst->AsNullConstant() != nullptr; } -ir::Instruction* FoldInstructionToConstant( - ir::Instruction* inst, std::function id_map) { +ir::Instruction* InstructionFolder::FoldInstructionToConstant( + ir::Instruction* inst, std::function id_map) const { ir::IRContext* context = inst->context(); analysis::ConstantManager* const_mgr = context->get_constant_mgr(); @@ -659,7 +634,7 @@ ir::Instruction* FoldInstructionToConstant( return nullptr; } -bool IsFoldableType(ir::Instruction* type_inst) { +bool InstructionFolder::IsFoldableType(ir::Instruction* type_inst) const { // Support 32-bit integers. if (type_inst->opcode() == SpvOpTypeInt) { return type_inst->GetSingleWordInOperand(0) == 32; @@ -672,7 +647,7 @@ bool IsFoldableType(ir::Instruction* type_inst) { return false; } -bool FoldInstruction(ir::Instruction* inst) { +bool InstructionFolder::FoldInstruction(ir::Instruction* inst) const { bool modified = false; ir::Instruction* folded_inst(inst); while (folded_inst->opcode() != SpvOpCopyObject && diff --git a/source/opt/fold.h b/source/opt/fold.h index 9c6028dfb..c00b56e75 100644 --- a/source/opt/fold.h +++ b/source/opt/fold.h @@ -21,76 +21,145 @@ #include "const_folding_rules.h" #include "constants.h" #include "def_use_manager.h" +#include "folding_rules.h" namespace spvtools { namespace opt { -// Returns a reference to the ConstnatFoldingRules instance. -const ConstantFoldingRules& GetConstantFoldingRules(); +class InstructionFolder { + public: + // 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& operands) const; -// 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 -// faill with a call to assert. -uint32_t FoldScalars(SpvOp opcode, - const std::vector& operands); + // 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 FoldVectors( + SpvOp opcode, uint32_t num_dims, + const std::vector& 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 -// faill with a call to assert. -std::vector FoldVectors( - SpvOp opcode, uint32_t num_dims, - const std::vector& operands); + // Returns true if |opcode| represents an operation handled by FoldScalars or + // FoldVectors. + bool IsFoldableOpcode(SpvOp opcode) const; -// Returns true if |opcode| represents an operation handled by FoldScalars or -// FoldVectors. -bool IsFoldableOpcode(SpvOp opcode); + // Returns true if |cst| is supported by FoldScalars and FoldVectors. + bool IsFoldableConstant(const analysis::Constant* cst) const; -// Returns true if |cst| is supported by FoldScalars and FoldVectors. -bool IsFoldableConstant(const analysis::Constant* cst); + // Returns true if |FoldInstructionToConstant| could fold an instruction whose + // result type is |type_inst|. + bool IsFoldableType(ir::Instruction* type_inst) const; -// Returns true if |FoldInstructionToConstant| could fold an instruction whose -// result type is |type_inst|. -bool IsFoldableType(ir::Instruction* type_inst); + // 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. + ir::Instruction* FoldInstructionToConstant( + ir::Instruction* inst, std::function 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(ir::Instruction* 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. -ir::Instruction* FoldInstructionToConstant( - ir::Instruction* inst, std::function id_map); + // Return true if this opcode has a const folding rule associtated with it. + bool HasConstFoldingRule(SpvOp opcode) const { + return GetConstantFoldingRules().HasFoldingRule(opcode); + } -// 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(ir::Instruction* inst); + 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& operand_words) const; + + bool FoldInstructionInternal(ir::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(ir::Instruction* inst, + std::function 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(ir::Instruction* inst, + std::function 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(ir::Instruction* inst, + std::function id_map, + uint32_t* result) const; + + // Folding rules used by |FoldInstructionToConstant| and |FoldInstruction|. + ConstantFoldingRules const_folding_rules; + + // Folding rules used by |FoldInstruction|. + FoldingRules folding_rules; +}; } // namespace opt } // namespace spvtools diff --git a/source/opt/fold_spec_constant_op_and_composite_pass.cpp b/source/opt/fold_spec_constant_op_and_composite_pass.cpp index 79eaeade2..df78c69b9 100644 --- a/source/opt/fold_spec_constant_op_and_composite_pass.cpp +++ b/source/opt/fold_spec_constant_op_and_composite_pass.cpp @@ -351,7 +351,8 @@ ir::Instruction* FoldSpecConstantOpAndCompositePass::DoComponentWiseOperation( if (result_type->AsInteger() || result_type->AsBool()) { // Scalar operation - uint32_t result_val = FoldScalars(spec_opcode, operands); + uint32_t result_val = + context()->get_instruction_folder().FoldScalars(spec_opcode, operands); auto result_const = context()->get_constant_mgr()->GetConstant(result_type, {result_val}); return context()->get_constant_mgr()->BuildInstructionAndAddToModule( @@ -362,7 +363,8 @@ ir::Instruction* FoldSpecConstantOpAndCompositePass::DoComponentWiseOperation( result_type->AsVector()->element_type(); uint32_t num_dims = result_type->AsVector()->element_count(); std::vector result_vec = - FoldVectors(spec_opcode, num_dims, operands); + context()->get_instruction_folder().FoldVectors(spec_opcode, num_dims, + operands); std::vector result_vector_components; for (uint32_t r : result_vec) { if (auto rc = diff --git a/source/opt/folding_rules.cpp b/source/opt/folding_rules.cpp index 9b88cc868..138ee843f 100644 --- a/source/opt/folding_rules.cpp +++ b/source/opt/folding_rules.cpp @@ -16,7 +16,7 @@ #include -#include "fold.h" +#include "ir_context.h" #include "latest_version_glsl_std_450_header.h" namespace spvtools { @@ -1541,72 +1541,73 @@ FoldingRule VectorShuffleFeedingExtract() { // corresponding |a| in the FMix is 0 or 1, we can extract from one of the // operands of the FMix. FoldingRule FMixFeedingExtract() { - return [](ir::Instruction* inst, - const std::vector&) { - assert(inst->opcode() == SpvOpCompositeExtract && - "Wrong opcode. Should be OpCompositeExtract."); - analysis::DefUseManager* def_use_mgr = inst->context()->get_def_use_mgr(); - analysis::ConstantManager* const_mgr = inst->context()->get_constant_mgr(); + return + [](ir::Instruction* inst, const std::vector&) { + assert(inst->opcode() == SpvOpCompositeExtract && + "Wrong opcode. Should be OpCompositeExtract."); + ir::IRContext* context = inst->context(); + analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); - uint32_t composite_id = - inst->GetSingleWordInOperand(kExtractCompositeIdInIdx); - ir::Instruction* composite_inst = def_use_mgr->GetDef(composite_id); + uint32_t composite_id = + inst->GetSingleWordInOperand(kExtractCompositeIdInIdx); + ir::Instruction* composite_inst = def_use_mgr->GetDef(composite_id); - if (composite_inst->opcode() != SpvOpExtInst) { - return false; - } + if (composite_inst->opcode() != SpvOpExtInst) { + return false; + } - uint32_t inst_set_id = - inst->context()->get_feature_mgr()->GetExtInstImportId_GLSLstd450(); + uint32_t inst_set_id = + inst->context()->get_feature_mgr()->GetExtInstImportId_GLSLstd450(); - if (composite_inst->GetSingleWordInOperand(kExtInstSetIdInIdx) != - inst_set_id || - composite_inst->GetSingleWordInOperand(kExtInstInstructionInIdx) != - GLSLstd450FMix) { - return false; - } + if (composite_inst->GetSingleWordInOperand(kExtInstSetIdInIdx) != + inst_set_id || + composite_inst->GetSingleWordInOperand(kExtInstInstructionInIdx) != + GLSLstd450FMix) { + return false; + } - // Get the |a| for the FMix instruction. - uint32_t a_id = composite_inst->GetSingleWordInOperand(kFMixAIdInIdx); - std::unique_ptr a(inst->Clone(inst->context())); - a->SetInOperand(kExtractCompositeIdInIdx, {a_id}); - FoldInstruction(a.get()); + // Get the |a| for the FMix instruction. + uint32_t a_id = composite_inst->GetSingleWordInOperand(kFMixAIdInIdx); + std::unique_ptr a(inst->Clone(inst->context())); + a->SetInOperand(kExtractCompositeIdInIdx, {a_id}); + context->get_instruction_folder().FoldInstruction(a.get()); - if (a->opcode() != SpvOpCopyObject) { - return false; - } + if (a->opcode() != SpvOpCopyObject) { + return false; + } - const analysis::Constant* a_const = - const_mgr->FindDeclaredConstant(a->GetSingleWordInOperand(0)); + const analysis::Constant* a_const = + const_mgr->FindDeclaredConstant(a->GetSingleWordInOperand(0)); - if (!a_const) { - return false; - } + if (!a_const) { + return false; + } - bool use_x = false; + bool use_x = false; - assert(a_const->type()->AsFloat()); - double element_value = a_const->GetValueAsDouble(); - if (element_value == 0.0) { - use_x = true; - } else if (element_value == 1.0) { - use_x = false; - } else { - return false; - } + assert(a_const->type()->AsFloat()); + double element_value = a_const->GetValueAsDouble(); + if (element_value == 0.0) { + use_x = true; + } else if (element_value == 1.0) { + use_x = false; + } else { + return false; + } - // Get the id of the of the vector the element comes from. - uint32_t new_vector = 0; - if (use_x) { - new_vector = composite_inst->GetSingleWordInOperand(kFMixXIdInIdx); - } else { - new_vector = composite_inst->GetSingleWordInOperand(kFMixYIdInIdx); - } + // Get the id of the of the vector the element comes from. + uint32_t new_vector = 0; + if (use_x) { + new_vector = composite_inst->GetSingleWordInOperand(kFMixXIdInIdx); + } else { + new_vector = composite_inst->GetSingleWordInOperand(kFMixYIdInIdx); + } - // Update the extract instruction. - inst->SetInOperand(kExtractCompositeIdInIdx, {new_vector}); - return true; - }; + // Update the extract instruction. + inst->SetInOperand(kExtractCompositeIdInIdx, {new_vector}); + return true; + }; } FoldingRule RedundantPhi() { diff --git a/source/opt/folding_rules.h b/source/opt/folding_rules.h index 78277e82c..ddedf2b25 100644 --- a/source/opt/folding_rules.h +++ b/source/opt/folding_rules.h @@ -19,9 +19,6 @@ #include #include "constants.h" -#include "def_use_manager.h" -#include "ir_builder.h" -#include "ir_context.h" namespace spvtools { namespace opt { @@ -62,7 +59,7 @@ class FoldingRules { public: FoldingRules(); - const std::vector& GetRulesForOpcode(SpvOp opcode) { + const std::vector& GetRulesForOpcode(SpvOp opcode) const { auto it = rules_.find(opcode); if (it != rules_.end()) { return it->second; diff --git a/source/opt/instruction.cpp b/source/opt/instruction.cpp index 2c50a0dcd..1999b7697 100644 --- a/source/opt/instruction.cpp +++ b/source/opt/instruction.cpp @@ -470,15 +470,16 @@ bool Instruction::IsOpaqueType() const { bool Instruction::IsFoldable() const { return IsFoldableByFoldScalar() || - opt::GetConstantFoldingRules().HasFoldingRule(opcode()); + context()->get_instruction_folder().HasConstFoldingRule(opcode()); } bool Instruction::IsFoldableByFoldScalar() const { - if (!opt::IsFoldableOpcode(opcode())) { + const opt::InstructionFolder& folder = context()->get_instruction_folder(); + if (!folder.IsFoldableOpcode(opcode())) { return false; } Instruction* type = context()->get_def_use_mgr()->GetDef(type_id()); - return opt::IsFoldableType(type); + return folder.IsFoldableType(type); } bool Instruction::IsFloatingPointFoldingAllowed() const { diff --git a/source/opt/ir_context.h b/source/opt/ir_context.h index 27f64ba34..9ef2d4ba6 100644 --- a/source/opt/ir_context.h +++ b/source/opt/ir_context.h @@ -22,6 +22,7 @@ #include "def_use_manager.h" #include "dominator_analysis.h" #include "feature_manager.h" +#include "fold.h" #include "loop_descriptor.h" #include "module.h" #include "register_pressure.h" @@ -445,6 +446,13 @@ class IRContext { // its definitions and uses. inline void UpdateDefUse(Instruction* inst); + const opt::InstructionFolder& get_instruction_folder() { + if (!inst_folder_) { + inst_folder_.reset(new opt::InstructionFolder()); + } + return *inst_folder_; + } + private: // Builds the def-use manager from scratch, even if it was already valid. void BuildDefUseManager() { @@ -601,6 +609,8 @@ class IRContext { std::unique_ptr reg_pressure_; std::unique_ptr vn_table_; + + std::unique_ptr inst_folder_; }; inline ir::IRContext::Analysis operator|(ir::IRContext::Analysis lhs, diff --git a/source/opt/simplification_pass.cpp b/source/opt/simplification_pass.cpp index 8b41379e8..984dd1aac 100644 --- a/source/opt/simplification_pass.cpp +++ b/source/opt/simplification_pass.cpp @@ -45,18 +45,20 @@ bool SimplificationPass::SimplifyFunction(ir::Function* function) { std::unordered_set process_phis; std::unordered_set inst_to_kill; std::unordered_set in_work_list; + const opt::InstructionFolder& folder = context()->get_instruction_folder(); cfg()->ForEachBlockInReversePostOrder( function->entry().get(), [&modified, &process_phis, &work_list, &in_work_list, &inst_to_kill, - this](ir::BasicBlock* bb) { + folder, this](ir::BasicBlock* bb) { for (ir::Instruction* inst = &*bb->begin(); inst; inst = inst->NextNode()) { if (inst->opcode() == SpvOpPhi) { process_phis.insert(inst); } - if (inst->opcode() == SpvOpCopyObject || FoldInstruction(inst)) { + if (inst->opcode() == SpvOpCopyObject || + folder.FoldInstruction(inst)) { modified = true; context()->AnalyzeUses(inst); get_def_use_mgr()->ForEachUser(inst, [&work_list, &process_phis, @@ -85,7 +87,7 @@ bool SimplificationPass::SimplifyFunction(ir::Function* function) { for (size_t i = 0; i < work_list.size(); ++i) { ir::Instruction* inst = work_list[i]; in_work_list.erase(inst); - if (inst->opcode() == SpvOpCopyObject || FoldInstruction(inst)) { + if (inst->opcode() == SpvOpCopyObject || folder.FoldInstruction(inst)) { modified = true; context()->AnalyzeUses(inst); get_def_use_mgr()->ForEachUser( diff --git a/test/opt/fold_test.cpp b/test/opt/fold_test.cpp index 281c69cb4..c00e95ae2 100644 --- a/test/opt/fold_test.cpp +++ b/test/opt/fold_test.cpp @@ -86,7 +86,7 @@ TEST_P(IntegerInstructionFoldingTest, Case) { // Fold the instruction to test. opt::analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); ir::Instruction* inst = def_use_mgr->GetDef(tc.id_to_fold); - bool succeeded = opt::FoldInstruction(inst); + bool succeeded = context->get_instruction_folder().FoldInstruction(inst); // Make sure the instruction folded as expected. EXPECT_TRUE(succeeded); @@ -454,7 +454,7 @@ TEST_P(IntVectorInstructionFoldingTest, Case) { // Fold the instruction to test. opt::analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); ir::Instruction* inst = def_use_mgr->GetDef(tc.id_to_fold); - bool succeeded = opt::FoldInstruction(inst); + bool succeeded = context->get_instruction_folder().FoldInstruction(inst); // Make sure the instruction folded as expected. EXPECT_TRUE(succeeded); @@ -518,7 +518,7 @@ TEST_P(BooleanInstructionFoldingTest, Case) { // Fold the instruction to test. opt::analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); ir::Instruction* inst = def_use_mgr->GetDef(tc.id_to_fold); - bool succeeded = opt::FoldInstruction(inst); + bool succeeded = context->get_instruction_folder().FoldInstruction(inst); // Make sure the instruction folded as expected. EXPECT_TRUE(succeeded); @@ -1129,7 +1129,7 @@ TEST_P(FloatInstructionFoldingTest, Case) { // Fold the instruction to test. opt::analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); ir::Instruction* inst = def_use_mgr->GetDef(tc.id_to_fold); - bool succeeded = opt::FoldInstruction(inst); + bool succeeded = context->get_instruction_folder().FoldInstruction(inst); // Make sure the instruction folded as expected. EXPECT_TRUE(succeeded); @@ -1276,7 +1276,7 @@ TEST_P(DoubleInstructionFoldingTest, Case) { // Fold the instruction to test. opt::analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); ir::Instruction* inst = def_use_mgr->GetDef(tc.id_to_fold); - bool succeeded = opt::FoldInstruction(inst); + bool succeeded = context->get_instruction_folder().FoldInstruction(inst); // Make sure the instruction folded as expected. EXPECT_TRUE(succeeded); @@ -2034,7 +2034,8 @@ TEST_P(IntegerInstructionFoldingTestWithMap, Case) { // Fold the instruction to test. opt::analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); ir::Instruction* inst = def_use_mgr->GetDef(tc.id_to_fold); - inst = opt::FoldInstructionToConstant(inst, tc.id_map); + inst = context->get_instruction_folder().FoldInstructionToConstant(inst, + tc.id_map); // Make sure the instruction folded as expected. EXPECT_NE(inst, nullptr); @@ -2081,7 +2082,8 @@ TEST_P(BooleanInstructionFoldingTestWithMap, Case) { // Fold the instruction to test. opt::analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); ir::Instruction* inst = def_use_mgr->GetDef(tc.id_to_fold); - inst = opt::FoldInstructionToConstant(inst, tc.id_map); + inst = context->get_instruction_folder().FoldInstructionToConstant(inst, + tc.id_map); // Make sure the instruction folded as expected. EXPECT_NE(inst, nullptr); @@ -2131,7 +2133,7 @@ TEST_P(GeneralInstructionFoldingTest, Case) { opt::analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); ir::Instruction* inst = def_use_mgr->GetDef(tc.id_to_fold); std::unique_ptr original_inst(inst->Clone(context.get())); - bool succeeded = opt::FoldInstruction(inst); + bool succeeded = context->get_instruction_folder().FoldInstruction(inst); // Make sure the instruction folded as expected. EXPECT_EQ(inst->result_id(), original_inst->result_id()); @@ -3637,7 +3639,7 @@ TEST_P(ToNegateFoldingTest, Case) { opt::analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); ir::Instruction* inst = def_use_mgr->GetDef(tc.id_to_fold); std::unique_ptr original_inst(inst->Clone(context.get())); - bool succeeded = opt::FoldInstruction(inst); + bool succeeded = context->get_instruction_folder().FoldInstruction(inst); // Make sure the instruction folded as expected. EXPECT_EQ(inst->result_id(), original_inst->result_id()); @@ -3760,7 +3762,7 @@ TEST_P(MatchingInstructionFoldingTest, Case) { opt::analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); ir::Instruction* inst = def_use_mgr->GetDef(tc.id_to_fold); std::unique_ptr original_inst(inst->Clone(context.get())); - bool succeeded = opt::FoldInstruction(inst); + bool succeeded = context->get_instruction_folder().FoldInstruction(inst); EXPECT_EQ(succeeded, tc.expected_result); if (succeeded) { Match(tc.test_body, context.get()); @@ -5625,7 +5627,7 @@ TEST_P(MatchingInstructionWithNoResultFoldingTest, Case) { } assert(inst && "Invalid test. Could not find instruction to fold."); std::unique_ptr original_inst(inst->Clone(context.get())); - bool succeeded = opt::FoldInstruction(inst); + bool succeeded = context->get_instruction_folder().FoldInstruction(inst); EXPECT_EQ(succeeded, tc.expected_result); if (succeeded) { Match(tc.test_body, context.get());