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.
This commit is contained in:
Steven Perron 2018-07-05 14:19:50 -04:00 committed by David Neto
parent 9ecbcf5fc8
commit a45d4cac61
12 changed files with 257 additions and 200 deletions

View File

@ -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.

View File

@ -14,6 +14,8 @@
#include "const_folding_rules.h"
#include "ir_context.h"
namespace spvtools {
namespace opt {

View File

@ -18,11 +18,6 @@
#include <vector>
#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 {

View File

@ -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<bool>(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<uint32_t>& operand_words) {
uint32_t InstructionFolder::OperateWords(
SpvOp opcode, const std::vector<uint32_t>& 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<const analysis::Constant*> 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<const analysis::Constant*>& operands) {
uint32_t InstructionFolder::FoldScalars(
SpvOp opcode,
const std::vector<const analysis::Constant*>& operands) const {
assert(IsFoldableOpcode(opcode) &&
"Unhandled instruction opcode in FoldScalars");
std::vector<uint32_t> 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<uint32_t(uint32_t)> id_map,
uint32_t* result) {
bool InstructionFolder::FoldBinaryIntegerOpToConstant(
ir::Instruction* inst, std::function<uint32_t(uint32_t)> 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<uint32_t(uint32_t)> id_map,
uint32_t* result) {
bool InstructionFolder::FoldBinaryBooleanOpToConstant(
ir::Instruction* inst, std::function<uint32_t(uint32_t)> 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<uint32_t(uint32_t)> id_map,
uint32_t* result) {
bool InstructionFolder::FoldIntegerOpToConstant(
ir::Instruction* inst, std::function<uint32_t(uint32_t)> 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<uint32_t> FoldVectors(
std::vector<uint32_t> InstructionFolder::FoldVectors(
SpvOp opcode, uint32_t num_dims,
const std::vector<const analysis::Constant*>& operands) {
const std::vector<const analysis::Constant*>& operands) const {
assert(IsFoldableOpcode(opcode) &&
"Unhandled instruction opcode in FoldVectors");
std::vector<uint32_t> result;
@ -547,7 +521,7 @@ std::vector<uint32_t> 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<uint32_t(uint32_t)> id_map) {
ir::Instruction* InstructionFolder::FoldInstructionToConstant(
ir::Instruction* inst, std::function<uint32_t(uint32_t)> 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 &&

View File

@ -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<const analysis::Constant*>& 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<const analysis::Constant*>& 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<uint32_t> FoldVectors(
SpvOp opcode, uint32_t num_dims,
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
// faill with a call to assert.
std::vector<uint32_t> FoldVectors(
SpvOp opcode, uint32_t num_dims,
const std::vector<const analysis::Constant*>& 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<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(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<uint32_t(uint32_t)> 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<uint32_t>& 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<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(ir::Instruction* inst,
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(ir::Instruction* inst,
std::function<uint32_t(uint32_t)> 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

View File

@ -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<uint32_t> result_vec =
FoldVectors(spec_opcode, num_dims, operands);
context()->get_instruction_folder().FoldVectors(spec_opcode, num_dims,
operands);
std::vector<const analysis::Constant*> result_vector_components;
for (uint32_t r : result_vec) {
if (auto rc =

View File

@ -16,7 +16,7 @@
#include <limits>
#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<const analysis::Constant*>&) {
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<const analysis::Constant*>&) {
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<ir::Instruction> 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<ir::Instruction> 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() {

View File

@ -19,9 +19,6 @@
#include <vector>
#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<FoldingRule>& GetRulesForOpcode(SpvOp opcode) {
const std::vector<FoldingRule>& GetRulesForOpcode(SpvOp opcode) const {
auto it = rules_.find(opcode);
if (it != rules_.end()) {
return it->second;

View File

@ -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 {

View File

@ -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<opt::LivenessAnalysis> reg_pressure_;
std::unique_ptr<opt::ValueNumberTable> vn_table_;
std::unique_ptr<opt::InstructionFolder> inst_folder_;
};
inline ir::IRContext::Analysis operator|(ir::IRContext::Analysis lhs,

View File

@ -45,18 +45,20 @@ bool SimplificationPass::SimplifyFunction(ir::Function* function) {
std::unordered_set<ir::Instruction*> process_phis;
std::unordered_set<ir::Instruction*> inst_to_kill;
std::unordered_set<ir::Instruction*> 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(

View File

@ -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<ir::Instruction> 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<ir::Instruction> 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<ir::Instruction> 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<ir::Instruction> 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());