mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-11-25 13:00:04 +00:00
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:
parent
9ecbcf5fc8
commit
a45d4cac61
@ -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.
|
||||
|
@ -14,6 +14,8 @@
|
||||
|
||||
#include "const_folding_rules.h"
|
||||
|
||||
#include "ir_context.h"
|
||||
|
||||
namespace spvtools {
|
||||
namespace opt {
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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 &&
|
||||
|
@ -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
|
||||
|
@ -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 =
|
||||
|
@ -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() {
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
@ -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,
|
||||
|
@ -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(
|
||||
|
@ -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());
|
||||
|
Loading…
Reference in New Issue
Block a user