diff --git a/source/fuzz/fuzzer_util.cpp b/source/fuzz/fuzzer_util.cpp index 4d85984c5..f09943fe6 100644 --- a/source/fuzz/fuzzer_util.cpp +++ b/source/fuzz/fuzzer_util.cpp @@ -218,6 +218,12 @@ bool CanInsertOpcodeBeforeInstruction( } bool CanMakeSynonymOf(opt::IRContext* ir_context, opt::Instruction* inst) { + if (inst->opcode() == SpvOpSampledImage) { + // The SPIR-V data rules say that only very specific instructions may + // may consume the result id of an OpSampledImage, and this excludes the + // instructions that are used for making synonyms. + return false; + } if (!inst->HasResultId()) { // We can only make a synonym of an instruction that generates an id. return false; diff --git a/source/fuzz/transformation_split_block.cpp b/source/fuzz/transformation_split_block.cpp index 3de081e03..b020d98a0 100644 --- a/source/fuzz/transformation_split_block.cpp +++ b/source/fuzz/transformation_split_block.cpp @@ -76,8 +76,34 @@ bool TransformationSplitBlock::IsApplicable( } // We cannot split before an OpPhi unless the OpPhi has exactly one // associated incoming edge. - return !(split_before->opcode() == SpvOpPhi && - split_before->NumInOperands() != 2); + if (split_before->opcode() == SpvOpPhi && + split_before->NumInOperands() != 2) { + return false; + } + + // Splitting the block must not separate the definition of an OpSampledImage + // from its use: the SPIR-V data rules require them to be in the same block. + std::set sampled_image_result_ids; + bool before_split = true; + for (auto& instruction : *block_to_split) { + if (&instruction == &*split_before) { + before_split = false; + } + if (before_split) { + if (instruction.opcode() == SpvOpSampledImage) { + sampled_image_result_ids.insert(instruction.result_id()); + } + } else { + if (!instruction.WhileEachInId( + [&sampled_image_result_ids](uint32_t* id) -> bool { + return !sampled_image_result_ids.count(*id); + })) { + return false; + } + } + } + + return true; } void TransformationSplitBlock::Apply( diff --git a/test/fuzz/fuzzer_replayer_test.cpp b/test/fuzz/fuzzer_replayer_test.cpp index b9024b9db..1e7c64323 100644 --- a/test/fuzz/fuzzer_replayer_test.cpp +++ b/test/fuzz/fuzzer_replayer_test.cpp @@ -1553,6 +1553,47 @@ const std::string kTestShader5 = R"( OpFunctionEnd )"; +// Some miscellaneous SPIR-V. + +const std::string kTestShader6 = R"( + OpCapability Shader + OpCapability SampledBuffer + OpCapability ImageBuffer + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %40 %41 + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 450 + OpDecorate %40 DescriptorSet 0 + OpDecorate %40 Binding 69 + OpDecorate %41 DescriptorSet 0 + OpDecorate %41 Binding 1 + %54 = OpTypeFloat 32 + %76 = OpTypeVector %54 4 + %55 = OpConstant %54 0 + %56 = OpTypeVector %54 3 + %94 = OpTypeVector %54 2 + %112 = OpConstantComposite %94 %55 %55 + %57 = OpConstantComposite %56 %55 %55 %55 + %15 = OpTypeImage %54 2D 2 0 0 1 Unknown + %114 = OpTypePointer UniformConstant %15 + %38 = OpTypeSampler + %125 = OpTypePointer UniformConstant %38 + %132 = OpTypeVoid + %133 = OpTypeFunction %132 + %45 = OpTypeSampledImage %15 + %40 = OpVariable %114 UniformConstant + %41 = OpVariable %125 UniformConstant + %2 = OpFunction %132 None %133 + %164 = OpLabel + %184 = OpLoad %15 %40 + %213 = OpLoad %38 %41 + %216 = OpSampledImage %45 %184 %213 + %217 = OpImageSampleImplicitLod %76 %216 %112 Bias %55 + OpReturn + OpFunctionEnd + )"; + void AddConstantUniformFact(protobufs::FactSequence* facts, uint32_t descriptor_set, uint32_t binding, std::vector&& indices, uint32_t value) { @@ -1591,7 +1632,7 @@ void RunFuzzerAndReplayer(const std::string& shader, std::vector donor_suppliers; for (auto donor : {&kTestShader1, &kTestShader2, &kTestShader3, &kTestShader4, - &kTestShader5}) { + &kTestShader5, &kTestShader6}) { donor_suppliers.emplace_back([donor]() { return BuildModule(env, kConsoleMessageConsumer, *donor, kFuzzAssembleOption); @@ -1682,6 +1723,13 @@ TEST(FuzzerReplayerTest, Miscellaneous5) { kNumFuzzerRuns); } +TEST(FuzzerReplayerTest, Miscellaneous6) { + // Do some fuzzer runs, starting from an initial seed of 57 (seed value chosen + // arbitrarily). + RunFuzzerAndReplayer(kTestShader6, protobufs::FactSequence(), 57, + kNumFuzzerRuns); +} + } // namespace } // namespace fuzz } // namespace spvtools diff --git a/test/fuzz/transformation_copy_object_test.cpp b/test/fuzz/transformation_copy_object_test.cpp index fa5a5b12e..22eecf63d 100644 --- a/test/fuzz/transformation_copy_object_test.cpp +++ b/test/fuzz/transformation_copy_object_test.cpp @@ -718,6 +718,64 @@ TEST(TransformationCopyObjectTest, PropagateIrrelevantPointeeFact) { transformation_context.GetFactManager()->PointeeValueIsIrrelevant(101)); } +TEST(TransformationCopyObject, DoNotCopyOpSampledImage) { + // This checks that we do not try to copy the result id of an OpSampledImage + // instruction. + std::string shader = R"( + OpCapability Shader + OpCapability SampledBuffer + OpCapability ImageBuffer + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %40 %41 + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 450 + OpDecorate %40 DescriptorSet 0 + OpDecorate %40 Binding 69 + OpDecorate %41 DescriptorSet 0 + OpDecorate %41 Binding 1 + %54 = OpTypeFloat 32 + %76 = OpTypeVector %54 4 + %55 = OpConstant %54 0 + %56 = OpTypeVector %54 3 + %94 = OpTypeVector %54 2 + %112 = OpConstantComposite %94 %55 %55 + %57 = OpConstantComposite %56 %55 %55 %55 + %15 = OpTypeImage %54 2D 2 0 0 1 Unknown + %114 = OpTypePointer UniformConstant %15 + %38 = OpTypeSampler + %125 = OpTypePointer UniformConstant %38 + %132 = OpTypeVoid + %133 = OpTypeFunction %132 + %45 = OpTypeSampledImage %15 + %40 = OpVariable %114 UniformConstant + %41 = OpVariable %125 UniformConstant + %2 = OpFunction %132 None %133 + %164 = OpLabel + %184 = OpLoad %15 %40 + %213 = OpLoad %38 %41 + %216 = OpSampledImage %45 %184 %213 + %217 = OpImageSampleImplicitLod %76 %216 %112 Bias %55 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + + FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); + + ASSERT_FALSE( + TransformationCopyObject( + 216, MakeInstructionDescriptor(217, SpvOpImageSampleImplicitLod, 0), + 500) + .IsApplicable(context.get(), transformation_context)); +} + } // namespace } // namespace fuzz } // namespace spvtools diff --git a/test/fuzz/transformation_split_block_test.cpp b/test/fuzz/transformation_split_block_test.cpp index 8e9e8a4f1..30bac026c 100644 --- a/test/fuzz/transformation_split_block_test.cpp +++ b/test/fuzz/transformation_split_block_test.cpp @@ -866,6 +866,62 @@ TEST(TransformationSplitBlockTest, DeadBlockShouldSplitToTwoDeadBlocks) { ASSERT_TRUE(IsEqual(env, after_split, context.get())); } +TEST(TransformationSplitBlockTest, DoNotSplitUseOfOpSampledImage) { + // This checks that we cannot split the definition of an OpSampledImage + // from its use. + std::string shader = R"( + OpCapability Shader + OpCapability SampledBuffer + OpCapability ImageBuffer + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %40 %41 + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 450 + OpDecorate %40 DescriptorSet 0 + OpDecorate %40 Binding 69 + OpDecorate %41 DescriptorSet 0 + OpDecorate %41 Binding 1 + %54 = OpTypeFloat 32 + %76 = OpTypeVector %54 4 + %55 = OpConstant %54 0 + %56 = OpTypeVector %54 3 + %94 = OpTypeVector %54 2 + %112 = OpConstantComposite %94 %55 %55 + %57 = OpConstantComposite %56 %55 %55 %55 + %15 = OpTypeImage %54 2D 2 0 0 1 Unknown + %114 = OpTypePointer UniformConstant %15 + %38 = OpTypeSampler + %125 = OpTypePointer UniformConstant %38 + %132 = OpTypeVoid + %133 = OpTypeFunction %132 + %45 = OpTypeSampledImage %15 + %40 = OpVariable %114 UniformConstant + %41 = OpVariable %125 UniformConstant + %2 = OpFunction %132 None %133 + %164 = OpLabel + %184 = OpLoad %15 %40 + %213 = OpLoad %38 %41 + %216 = OpSampledImage %45 %184 %213 + %217 = OpImageSampleImplicitLod %76 %216 %112 Bias %55 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + + FactManager fact_manager; + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context(&fact_manager, + validator_options); + + auto split = TransformationSplitBlock( + MakeInstructionDescriptor(217, SpvOpImageSampleImplicitLod, 0), 500); + ASSERT_FALSE(split.IsApplicable(context.get(), transformation_context)); +} + } // namespace } // namespace fuzz } // namespace spvtools