spirv-fuzz: Use overflow ids when duplicating regions (#3878)
Fixes #3786.
diff --git a/source/fuzz/transformation_duplicate_region_with_selection.cpp b/source/fuzz/transformation_duplicate_region_with_selection.cpp
index 3b264df..2d03968 100644
--- a/source/fuzz/transformation_duplicate_region_with_selection.cpp
+++ b/source/fuzz/transformation_duplicate_region_with_selection.cpp
@@ -47,7 +47,8 @@
}
bool TransformationDuplicateRegionWithSelection::IsApplicable(
- opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
+ opt::IRContext* ir_context,
+ const TransformationContext& transformation_context) const {
// Instruction with the id |condition_id| must exist and must be of a bool
// type.
auto bool_instr =
@@ -189,9 +190,6 @@
}
// Get the maps from the protobuf.
- // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3786):
- // Consider additionally providing overflow ids to make this
- // transformation more applicable when shrinking.
std::map<uint32_t, uint32_t> original_label_to_duplicate_label =
fuzzerutil::RepeatedUInt32PairToMap(
message_.original_label_to_duplicate_label());
@@ -204,44 +202,44 @@
fuzzerutil::RepeatedUInt32PairToMap(message_.original_id_to_phi_id());
for (auto block : region_set) {
- auto label =
- ir_context->get_def_use_mgr()->GetDef(block->id())->result_id();
// The label of every block in the region must be present in the map
- // |original_label_to_duplicate_label|.
- if (original_label_to_duplicate_label.count(label) == 0) {
- return false;
- }
- auto duplicate_label = original_label_to_duplicate_label[label];
- // Each id assigned to labels in the region must be distinct and fresh.
- if (!duplicate_label ||
- !CheckIdIsFreshAndNotUsedByThisTransformation(
- duplicate_label, ir_context, &ids_used_by_this_transformation)) {
- return false;
+ // |original_label_to_duplicate_label|, unless overflow ids are present.
+ if (original_label_to_duplicate_label.count(block->id()) == 0) {
+ if (!transformation_context.GetOverflowIdSource()->HasOverflowIds()) {
+ return false;
+ }
+ } else {
+ auto duplicate_label = original_label_to_duplicate_label[block->id()];
+ // Each id assigned to labels in the region must be distinct and fresh.
+ if (!duplicate_label ||
+ !CheckIdIsFreshAndNotUsedByThisTransformation(
+ duplicate_label, ir_context, &ids_used_by_this_transformation)) {
+ return false;
+ }
}
for (auto instr : *block) {
if (!instr.HasResultId()) {
continue;
}
// Every instruction with a result id in the region must be present in the
- // map |original_id_to_duplicate_id|.
+ // map |original_id_to_duplicate_id|, unless overflow ids are present.
if (original_id_to_duplicate_id.count(instr.result_id()) == 0) {
- return false;
- }
- auto duplicate_id = original_id_to_duplicate_id[instr.result_id()];
- // Id assigned to this result id in the region must be distinct and fresh.
- if (!duplicate_id ||
- !CheckIdIsFreshAndNotUsedByThisTransformation(
- duplicate_id, ir_context, &ids_used_by_this_transformation)) {
- return false;
+ if (!transformation_context.GetOverflowIdSource()->HasOverflowIds()) {
+ return false;
+ }
+ } else {
+ auto duplicate_id = original_id_to_duplicate_id[instr.result_id()];
+ // Id assigned to this result id in the region must be distinct and
+ // fresh.
+ if (!duplicate_id ||
+ !CheckIdIsFreshAndNotUsedByThisTransformation(
+ duplicate_id, ir_context, &ids_used_by_this_transformation)) {
+ return false;
+ }
}
if (&instr == &*exit_block->tail() ||
fuzzerutil::IdIsAvailableBeforeInstruction(
ir_context, &*exit_block->tail(), instr.result_id())) {
- // Every instruction with a result id available at the end of the region
- // must be present in the map |original_id_to_phi_id|.
- if (original_id_to_phi_id.count(instr.result_id()) == 0) {
- return false;
- }
// Using pointers with OpPhi requires capability VariablePointers.
// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3787):
// Consider not adding OpPhi instructions for the pointers which are
@@ -252,13 +250,23 @@
SpvCapabilityVariablePointers)) {
return false;
}
- auto phi_id = original_id_to_phi_id[instr.result_id()];
- // Id assigned to this result id in the region must be distinct and
- // fresh.
- if (!phi_id ||
- !CheckIdIsFreshAndNotUsedByThisTransformation(
- phi_id, ir_context, &ids_used_by_this_transformation)) {
- return false;
+
+ // Every instruction with a result id available at the end of the region
+ // must be present in the map |original_id_to_phi_id|, unless overflow
+ // ids are present.
+ if (original_id_to_phi_id.count(instr.result_id()) == 0) {
+ if (!transformation_context.GetOverflowIdSource()->HasOverflowIds()) {
+ return false;
+ }
+ } else {
+ auto phi_id = original_id_to_phi_id[instr.result_id()];
+ // Id assigned to this result id in the region must be distinct and
+ // fresh.
+ if (!phi_id ||
+ !CheckIdIsFreshAndNotUsedByThisTransformation(
+ phi_id, ir_context, &ids_used_by_this_transformation)) {
+ return false;
+ }
}
}
}
@@ -267,7 +275,8 @@
}
void TransformationDuplicateRegionWithSelection::Apply(
- opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
+ opt::IRContext* ir_context,
+ TransformationContext* transformation_context) const {
fuzzerutil::UpdateModuleIdBound(ir_context, message_.new_entry_fresh_id());
fuzzerutil::UpdateModuleIdBound(ir_context, message_.merge_label_fresh_id());
@@ -304,6 +313,35 @@
std::map<uint32_t, uint32_t> original_id_to_phi_id =
fuzzerutil::RepeatedUInt32PairToMap(message_.original_id_to_phi_id());
+ // Use oveflow ids to fill in any required ids that are missing from these
+ // maps.
+ for (auto block : region_blocks) {
+ if (original_label_to_duplicate_label.count(block->id()) == 0) {
+ original_label_to_duplicate_label.insert(
+ {block->id(),
+ transformation_context->GetOverflowIdSource()->GetNextOverflowId()});
+ }
+ for (auto instr : *block) {
+ if (!instr.HasResultId()) {
+ continue;
+ }
+ if (original_id_to_duplicate_id.count(instr.result_id()) == 0) {
+ original_id_to_duplicate_id.insert(
+ {instr.result_id(), transformation_context->GetOverflowIdSource()
+ ->GetNextOverflowId()});
+ }
+ if (&instr == &*exit_block->tail() ||
+ fuzzerutil::IdIsAvailableBeforeInstruction(
+ ir_context, &*exit_block->tail(), instr.result_id())) {
+ if (original_id_to_phi_id.count(instr.result_id()) == 0) {
+ original_id_to_phi_id.insert(
+ {instr.result_id(), transformation_context->GetOverflowIdSource()
+ ->GetNextOverflowId()});
+ }
+ }
+ }
+ }
+
// Before adding duplicate blocks, we need to update the OpPhi instructions in
// the successors of the |exit_block|. We know that the execution of the
// transformed region will end in |merge_block|. Hence, we need to change all
diff --git a/test/fuzz/transformation_duplicate_region_with_selection_test.cpp b/test/fuzz/transformation_duplicate_region_with_selection_test.cpp
index 6398f88..d63cfdf 100644
--- a/test/fuzz/transformation_duplicate_region_with_selection_test.cpp
+++ b/test/fuzz/transformation_duplicate_region_with_selection_test.cpp
@@ -14,6 +14,7 @@
#include "source/fuzz/transformation_duplicate_region_with_selection.h"
+#include "source/fuzz/counter_overflow_id_source.h"
#include "test/fuzz/fuzz_test_util.h"
namespace spvtools {
@@ -504,6 +505,7 @@
ASSERT_FALSE(
transformation_bad_7.IsApplicable(context.get(), transformation_context));
+#ifndef NDEBUG
// Bad: Instruction with id 15 is from the original region and is available
// at the end of the region but it is not present in the
// |original_id_to_phi_id|.
@@ -511,8 +513,9 @@
TransformationDuplicateRegionWithSelection(
500, 19, 501, 800, 800, {{800, 100}}, {{13, 201}, {15, 202}},
{{13, 301}});
- ASSERT_FALSE(
- transformation_bad_8.IsApplicable(context.get(), transformation_context));
+ ASSERT_DEATH(
+ transformation_bad_8.IsApplicable(context.get(), transformation_context),
+ "Bad attempt to query whether overflow ids are available.");
// Bad: Instruction with id 15 is from the original region but it is
// not present in the |original_id_to_duplicate_id|.
@@ -520,8 +523,10 @@
TransformationDuplicateRegionWithSelection(500, 19, 501, 800, 800,
{{800, 100}}, {{13, 201}},
{{13, 301}, {15, 302}});
- ASSERT_FALSE(
- transformation_bad_9.IsApplicable(context.get(), transformation_context));
+ ASSERT_DEATH(
+ transformation_bad_9.IsApplicable(context.get(), transformation_context),
+ "Bad attempt to query whether overflow ids are available.");
+#endif
// Bad: |condition_id| does not refer to the valid instruction.
TransformationDuplicateRegionWithSelection transformation_bad_10 =
@@ -1740,6 +1745,130 @@
transformation_bad.IsApplicable(context.get(), transformation_context));
}
+TEST(TransformationDuplicateRegionWithSelectionTest, OverflowIds) {
+ // This test checks that the transformation correctly uses overflow ids, when
+ // they are both needed and provided.
+
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %6 "fun("
+ OpName %10 "s"
+ OpName %17 "b"
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %8 = OpTypeInt 32 1
+ %9 = OpTypePointer Function %8
+ %11 = OpConstant %8 0
+ %13 = OpConstant %8 2
+ %15 = OpTypeBool
+ %16 = OpTypePointer Function %15
+ %18 = OpConstantTrue %15
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %17 = OpVariable %16 Function
+ OpStore %17 %18
+ %19 = OpFunctionCall %2 %6
+ OpReturn
+ OpFunctionEnd
+ %6 = OpFunction %2 None %3
+ %7 = OpLabel
+ %10 = OpVariable %9 Function
+ OpBranch %50
+ %50 = OpLabel
+ OpStore %10 %11
+ %12 = OpLoad %8 %10
+ %14 = OpIAdd %8 %12 %13
+ OpStore %10 %14
+ OpKill
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_4;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ spvtools::ValidatorOptions validator_options;
+ auto overflow_ids_unique_ptr = MakeUnique<CounterOverflowIdSource>(1000);
+ auto overflow_ids_ptr = overflow_ids_unique_ptr.get();
+ TransformationContext transformation_context(
+ MakeUnique<FactManager>(context.get()), validator_options,
+ std::move(overflow_ids_unique_ptr));
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ // The mappings do not provide sufficient ids, thus overflow ids are required.
+ TransformationDuplicateRegionWithSelection transformation_good_1 =
+ TransformationDuplicateRegionWithSelection(500, 18, 501, 50, 50, {},
+ {{12, 201}}, {{14, 302}});
+ ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(),
+ transformation_context));
+ ApplyAndCheckFreshIds(transformation_good_1, context.get(),
+ &transformation_context,
+ overflow_ids_ptr->GetIssuedOverflowIds());
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ std::string expected_shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %6 "fun("
+ OpName %10 "s"
+ OpName %17 "b"
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %8 = OpTypeInt 32 1
+ %9 = OpTypePointer Function %8
+ %11 = OpConstant %8 0
+ %13 = OpConstant %8 2
+ %15 = OpTypeBool
+ %16 = OpTypePointer Function %15
+ %18 = OpConstantTrue %15
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %17 = OpVariable %16 Function
+ OpStore %17 %18
+ %19 = OpFunctionCall %2 %6
+ OpReturn
+ OpFunctionEnd
+ %6 = OpFunction %2 None %3
+ %7 = OpLabel
+ %10 = OpVariable %9 Function
+ OpBranch %500
+ %500 = OpLabel
+ OpSelectionMerge %501 None
+ OpBranchConditional %18 %50 %1000
+ %50 = OpLabel
+ OpStore %10 %11
+ %12 = OpLoad %8 %10
+ %14 = OpIAdd %8 %12 %13
+ OpStore %10 %14
+ OpBranch %501
+ %1000 = OpLabel
+ OpStore %10 %11
+ %201 = OpLoad %8 %10
+ %1002 = OpIAdd %8 %201 %13
+ OpStore %10 %1002
+ OpBranch %501
+ %501 = OpLabel
+ %1001 = OpPhi %8 %12 %50 %201 %1000
+ %302 = OpPhi %8 %14 %50 %1002 %1000
+ OpKill
+ OpFunctionEnd
+ )";
+
+ ASSERT_TRUE(IsEqual(env, expected_shader, context.get()));
+}
+
} // namespace
} // namespace fuzz
} // namespace spvtools