Use id_map in Fold*ToConstant

The folding routines are suppose to use the id_map provided to map the
ids in the instruction.  The ones I just added are missing it.
This commit is contained in:
Steven Perron 2018-01-22 14:48:43 -05:00
parent 6c409e30a2
commit c4835e1bd8
2 changed files with 137 additions and 16 deletions

View File

@ -211,10 +211,13 @@ uint32_t FoldScalars(SpvOp opcode,
// 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. 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, uint32_t* result) {
// 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) {
SpvOp opcode = inst->opcode();
ir::IRContext* context = inst->context();
analysis::ConstantManager* const_manger = context->get_constant_mgr();
@ -226,7 +229,7 @@ bool FoldBinaryIntegerOpToConstant(ir::Instruction* inst, uint32_t* result) {
if (operand->type != SPV_OPERAND_TYPE_ID) {
return false;
}
ids[i] = operand->words[0];
ids[i] = id_map(operand->words[0]);
const analysis::Constant* constant =
const_manger->FindDeclaredConstant(ids[i]);
constants[i] = (constant != nullptr ? constant->AsIntConstant() : nullptr);
@ -397,9 +400,11 @@ bool FoldBinaryIntegerOpToConstant(ir::Instruction* inst, uint32_t* result) {
}
// Returns true if |inst| is a binary operation on two boolean values, and folds
// to a constant boolean value. If |inst| can be folded, the result value is
// returned in |*result|.
bool FoldBinaryBooleanOpToConstant(ir::Instruction* inst, uint32_t* result) {
// 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) {
SpvOp opcode = inst->opcode();
ir::IRContext* context = inst->context();
analysis::ConstantManager* const_manger = context->get_constant_mgr();
@ -411,7 +416,7 @@ bool FoldBinaryBooleanOpToConstant(ir::Instruction* inst, uint32_t* result) {
if (operand->type != SPV_OPERAND_TYPE_ID) {
return false;
}
ids[i] = operand->words[0];
ids[i] = id_map(operand->words[0]);
const analysis::Constant* constant =
const_manger->FindDeclaredConstant(ids[i]);
constants[i] = (constant != nullptr ? constant->AsBoolConstant() : nullptr);
@ -446,16 +451,19 @@ bool FoldBinaryBooleanOpToConstant(ir::Instruction* inst, uint32_t* result) {
return false;
}
// Returns true if |inst| can be folded to an constant. 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, uint32_t* result) {
// 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) {
assert(IsFoldableOpcode(inst->opcode()) &&
"Unhandled instruction opcode in FoldScalars");
switch (inst->NumInOperands()) {
case 2:
return FoldBinaryIntegerOpToConstant(inst, result) ||
FoldBinaryBooleanOpToConstant(inst, result);
return FoldBinaryIntegerOpToConstant(inst, id_map, result) ||
FoldBinaryBooleanOpToConstant(inst, id_map, result);
default:
return false;
}
@ -589,7 +597,7 @@ ir::Instruction* FoldInstructionToConstant(
}
if (!successful) {
successful = FoldIntegerOpToConstant(inst, &result_val);
successful = FoldIntegerOpToConstant(inst, id_map, &result_val);
}
if (successful) {

View File

@ -75,6 +75,8 @@ TEST_P(IntegerInstructionFoldingTest, Case) {
}
// Returns a common SPIR-V header for all of the test that follow.
#define INT_0_ID 100
#define TRUE_ID 101
const std::string& Header() {
static const std::string header = R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
@ -87,6 +89,7 @@ OpName %main "main"
%void_func = OpTypeFunction %void
%bool = OpTypeBool
%true = OpConstantTrue %bool
%101 = OpConstantTrue %bool ; Need a def with an numerical id to define id maps.
%false = OpConstantFalse %bool
%short = OpTypeInt 16 1
%int = OpTypeInt 32 1
@ -98,6 +101,7 @@ OpName %main "main"
%short_0 = OpConstant %short 0
%short_3 = OpConstant %short 3
%int_0 = OpConstant %int 0
%100 = OpConstant %int 0 ; Need a def with an numerical id to define id maps.
%int_3 = OpConstant %int 3
%int_min = OpConstant %int -2147483648
%int_max = OpConstant %int 2147483647
@ -988,4 +992,113 @@ INSTANTIATE_TEST_CASE_P(TestCase, InstructionNotFoldedTest,
2, nullptr)
));
// clang-format on
template <class ResultType>
struct InstructionFoldingCaseWithMap {
InstructionFoldingCaseWithMap(const std::string& tb, uint32_t id,
ResultType result,
std::function<uint32_t(uint32_t)> map)
: test_body(tb), id_to_fold(id), expected_result(result), id_map(map) {}
std::string test_body;
uint32_t id_to_fold;
ResultType expected_result;
std::function<uint32_t(uint32_t)> id_map;
};
using IntegerInstructionFoldingTestWithMap =
::testing::TestWithParam<InstructionFoldingCaseWithMap<uint32_t>>;
TEST_P(IntegerInstructionFoldingTestWithMap, Case) {
const auto& tc = GetParam();
// Build module.
std::unique_ptr<ir::IRContext> context =
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, tc.test_body,
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
ASSERT_NE(nullptr, context);
// 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::FoldInstruction(inst, tc.id_map);
// Make sure the instruction folded as expected.
EXPECT_NE(inst, nullptr);
if (inst != nullptr) {
EXPECT_EQ(inst->opcode(), SpvOpConstant);
opt::analysis::ConstantManager* const_mrg = context->get_constant_mgr();
const opt::analysis::IntConstant* result =
const_mrg->GetConstantFromInst(inst)->AsIntConstant();
EXPECT_NE(result, nullptr);
if (result != nullptr) {
EXPECT_EQ(result->GetU32BitValue(), tc.expected_result);
}
}
}
// clang-format off
INSTANTIATE_TEST_CASE_P(TestCase, IntegerInstructionFoldingTestWithMap,
::testing::Values(
// Test case 0: fold %3 = 0; %3 * n
InstructionFoldingCaseWithMap<uint32_t>(
Header() + "%main = OpFunction %void None %void_func\n" +
"%main_lab = OpLabel\n" +
"%n = OpVariable %_ptr_int Function\n" +
"%load = OpLoad %int %n\n" +
"%3 = OpCopyObject %int %int_0\n"
"%2 = OpIMul %int %3 %load\n" +
"OpReturn\n" +
"OpFunctionEnd",
2, 0, [](uint32_t id) {return (id == 3 ? INT_0_ID : id);})
));
// clang-format on
using BooleanInstructionFoldingTestWithMap =
::testing::TestWithParam<InstructionFoldingCaseWithMap<bool>>;
TEST_P(BooleanInstructionFoldingTestWithMap, Case) {
const auto& tc = GetParam();
// Build module.
std::unique_ptr<ir::IRContext> context =
BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, tc.test_body,
SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
ASSERT_NE(nullptr, context);
// 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::FoldInstruction(inst, tc.id_map);
// Make sure the instruction folded as expected.
EXPECT_NE(inst, nullptr);
if (inst != nullptr) {
std::vector<SpvOp> bool_opcodes = {SpvOpConstantTrue, SpvOpConstantFalse};
EXPECT_THAT(bool_opcodes, Contains(inst->opcode()));
opt::analysis::ConstantManager* const_mrg = context->get_constant_mgr();
const opt::analysis::BoolConstant* result =
const_mrg->GetConstantFromInst(inst)->AsBoolConstant();
EXPECT_NE(result, nullptr);
if (result != nullptr) {
EXPECT_EQ(result->value(), tc.expected_result);
}
}
}
// clang-format off
INSTANTIATE_TEST_CASE_P(TestCase, BooleanInstructionFoldingTestWithMap,
::testing::Values(
// Test case 0: fold %3 = true; %3 || n
InstructionFoldingCaseWithMap<bool>(
Header() + "%main = OpFunction %void None %void_func\n" +
"%main_lab = OpLabel\n" +
"%n = OpVariable %_ptr_bool Function\n" +
"%load = OpLoad %bool %n\n" +
"%3 = OpCopyObject %bool %true\n" +
"%2 = OpLogicalOr %bool %3 %load\n" +
"OpReturn\n" +
"OpFunctionEnd",
2, true, [](uint32_t id) {return (id == 3 ? TRUE_ID : id);})
));
// clang-format on
} // anonymous namespace