spirv-fuzz: Support identical predecessors in TransformationPropagateInstructionUp (#3689)
Support identical predecessors in TransformationPropagateInstructionUp.
A basic block may have multiple identical predecessors as follows:
%1 = OpLabel
OpSelectionMerge %2 None
OpBranchConditional %true %2 %2
%2 = OpLabel
...
This case wasn't supported before.
diff --git a/source/fuzz/fuzzer_pass_propagate_instructions_up.cpp b/source/fuzz/fuzzer_pass_propagate_instructions_up.cpp
index 52af774..2042d7c 100644
--- a/source/fuzz/fuzzer_pass_propagate_instructions_up.cpp
+++ b/source/fuzz/fuzzer_pass_propagate_instructions_up.cpp
@@ -44,7 +44,13 @@
GetIRContext(), block.id())) {
std::map<uint32_t, uint32_t> fresh_ids;
for (auto id : GetIRContext()->cfg()->preds(block.id())) {
- fresh_ids[id] = GetFuzzerContext()->GetFreshId();
+ auto& fresh_id = fresh_ids[id];
+
+ if (!fresh_id) {
+ // Create a fresh id if it hasn't been created yet. |fresh_id| will
+ // be default-initialized to 0 in this case.
+ fresh_id = GetFuzzerContext()->GetFreshId();
+ }
}
ApplyTransformation(
diff --git a/source/fuzz/transformation_propagate_instruction_up.cpp b/source/fuzz/transformation_propagate_instruction_up.cpp
index c41ee5b..adf3a51 100644
--- a/source/fuzz/transformation_propagate_instruction_up.cpp
+++ b/source/fuzz/transformation_propagate_instruction_up.cpp
@@ -99,15 +99,18 @@
const auto predecessor_id_to_fresh_id = fuzzerutil::RepeatedUInt32PairToMap(
message_.predecessor_id_to_fresh_id());
- std::vector<uint32_t> maybe_fresh_ids;
for (auto id : ir_context->cfg()->preds(message_.block_id())) {
// Each predecessor must have a fresh id in the |predecessor_id_to_fresh_id|
// map.
if (!predecessor_id_to_fresh_id.count(id)) {
return false;
}
+ }
- maybe_fresh_ids.push_back(predecessor_id_to_fresh_id.at(id));
+ std::vector<uint32_t> maybe_fresh_ids;
+ maybe_fresh_ids.reserve(predecessor_id_to_fresh_id.size());
+ for (const auto& entry : predecessor_id_to_fresh_id) {
+ maybe_fresh_ids.push_back(entry.second);
}
// All ids must be unique and fresh.
@@ -129,7 +132,15 @@
opt::Instruction::OperandList op_phi_operands;
const auto predecessor_id_to_fresh_id = fuzzerutil::RepeatedUInt32PairToMap(
message_.predecessor_id_to_fresh_id());
+ std::unordered_set<uint32_t> visited_predecessors;
for (auto predecessor_id : ir_context->cfg()->preds(message_.block_id())) {
+ // A block can have multiple identical predecessors.
+ if (visited_predecessors.count(predecessor_id)) {
+ continue;
+ }
+
+ visited_predecessors.insert(predecessor_id);
+
auto new_result_id = predecessor_id_to_fresh_id.at(predecessor_id);
// Compute InOperands for the OpPhi instruction to be inserted later.
diff --git a/test/fuzz/transformation_propagate_instruction_up_test.cpp b/test/fuzz/transformation_propagate_instruction_up_test.cpp
index 9837a01..de20e15 100644
--- a/test/fuzz/transformation_propagate_instruction_up_test.cpp
+++ b/test/fuzz/transformation_propagate_instruction_up_test.cpp
@@ -815,6 +815,81 @@
ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
}
+TEST(TransformationPropagateInstructionUpTest, MultipleIdenticalPredecessors) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %11 = OpConstant %6 23
+ %12 = OpTypeBool
+ %13 = OpConstantTrue %12
+ %4 = OpFunction %2 None %3
+
+ %5 = OpLabel
+ OpSelectionMerge %9 None
+ OpBranchConditional %13 %9 %9
+
+ %9 = OpLabel
+ %14 = OpPhi %6 %11 %5
+ %10 = OpCopyObject %6 %14
+ OpReturn
+
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+
+ TransformationPropagateInstructionUp transformation(9, {{{5, 40}}});
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ std::string after_transformation = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %11 = OpConstant %6 23
+ %12 = OpTypeBool
+ %13 = OpConstantTrue %12
+ %4 = OpFunction %2 None %3
+
+ %5 = OpLabel
+ %40 = OpCopyObject %6 %11
+ OpSelectionMerge %9 None
+ OpBranchConditional %13 %9 %9
+
+ %9 = OpLabel
+ %10 = OpPhi %6 %40 %5
+ %14 = OpPhi %6 %11 %5
+ OpReturn
+
+ OpFunctionEnd
+ )";
+
+ ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
} // namespace
} // namespace fuzz
} // namespace spvtools