spirv-fuzz: Do not flatten conditionals that create synonyms (#4030)
Fixes #4024.
diff --git a/source/fuzz/transformation_flatten_conditional_branch.cpp b/source/fuzz/transformation_flatten_conditional_branch.cpp
index dec933c..fdee513 100644
--- a/source/fuzz/transformation_flatten_conditional_branch.cpp
+++ b/source/fuzz/transformation_flatten_conditional_branch.cpp
@@ -512,15 +512,31 @@
return false;
}
+ // The base objects for all data descriptors involved in synonym facts.
+ std::unordered_set<uint32_t> synonym_base_objects;
+ for (auto* synonym :
+ transformation_context.GetFactManager()->GetAllSynonyms()) {
+ synonym_base_objects.insert(synonym->object());
+ }
+
// Check all of the instructions in the block.
- bool all_instructions_compatible =
- block->WhileEachInst([ir_context, instructions_that_need_ids](
- opt::Instruction* instruction) {
+ bool all_instructions_compatible = block->WhileEachInst(
+ [ir_context, instructions_that_need_ids,
+ &synonym_base_objects](opt::Instruction* instruction) {
// We can ignore OpLabel instructions.
if (instruction->opcode() == SpvOpLabel) {
return true;
}
+ // If the instruction is the base object of some synonym then we
+ // conservatively bail out: if a synonym ends up depending on an
+ // instruction that needs to be enclosed in a side-effect wrapper then
+ // it might no longer hold after we flatten the conditional.
+ if (instruction->result_id() &&
+ synonym_base_objects.count(instruction->result_id())) {
+ return false;
+ }
+
// If the instruction is a branch, it must be an unconditional branch.
if (instruction->IsBranch()) {
return instruction->opcode() == SpvOpBranch;
diff --git a/source/fuzz/transformation_flatten_conditional_branch.h b/source/fuzz/transformation_flatten_conditional_branch.h
index e8cb414..9bdae93 100644
--- a/source/fuzz/transformation_flatten_conditional_branch.h
+++ b/source/fuzz/transformation_flatten_conditional_branch.h
@@ -41,6 +41,8 @@
// single-exit region.
// - The region must not contain barrier or OpSampledImage instructions.
// - The region must not contain selection or loop constructs.
+ // - The region must not define ids that are the base objects for existing
+ // synonym facts.
// - For each instruction that requires additional fresh ids, then:
// - if the instruction is mapped to the required ids for enclosing it by
// |message_.side_effect_wrapper_info|, these must be valid (the
diff --git a/test/fuzz/transformation_flatten_conditional_branch_test.cpp b/test/fuzz/transformation_flatten_conditional_branch_test.cpp
index e0697d4..540275a 100644
--- a/test/fuzz/transformation_flatten_conditional_branch_test.cpp
+++ b/test/fuzz/transformation_flatten_conditional_branch_test.cpp
@@ -2135,6 +2135,62 @@
.IsApplicable(context.get(), transformation_context));
}
+TEST(TransformationFlattenConditionalBranchTest, ContainsSynonymCreation) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 320
+ OpName %4 "main"
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeBool
+ %7 = OpConstantFalse %6
+ %8 = OpTypeInt 32 0
+ %9 = OpTypePointer Function %8
+ %10 = OpConstant %8 42
+ %80 = OpConstant %8 0
+ %4 = OpFunction %2 None %3
+ %11 = OpLabel
+ %20 = OpVariable %9 Function
+ OpBranch %12
+ %12 = OpLabel
+ OpSelectionMerge %31 None
+ OpBranchConditional %7 %30 %31
+ %30 = OpLabel
+ OpStore %20 %10
+ %21 = OpLoad %8 %20
+ OpBranch %31
+ %31 = OpLabel
+ OpBranch %14
+ %14 = OpLabel
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ spvtools::ValidatorOptions validator_options;
+ ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
+ kConsoleMessageConsumer));
+ TransformationContext transformation_context(
+ MakeUnique<FactManager>(context.get()), validator_options);
+
+ transformation_context.GetFactManager()->AddFactDataSynonym(
+ MakeDataDescriptor(10, {}), MakeDataDescriptor(21, {}));
+ ASSERT_FALSE(TransformationFlattenConditionalBranch(
+ 12, true, 0, 0, 0,
+ {MakeSideEffectWrapperInfo(
+ MakeInstructionDescriptor(30, SpvOpStore, 0), 100, 101),
+ MakeSideEffectWrapperInfo(
+ MakeInstructionDescriptor(21, SpvOpLoad, 0), 102, 103,
+ 104, 105, 106, 80)})
+ .IsApplicable(context.get(), transformation_context));
+}
+
} // namespace
} // namespace fuzz
} // namespace spvtools