spirv-fuzz: Respect rules for OpSampledImage (#3287)

The SPIR-V data rules say that all uses of an OpSampledImage
instruction must be in the same block as the instruction, and highly
restrict those instructions that can consume the result id of an
OpSampledImage.

This adapts the transformations that split blocks and create synonyms
to avoid separating an OpSampledImage use from its definition, and to
avoid synonym-creation instructions such as OpCopyObject consuming an
OpSampledImage result id.
This commit is contained in:
Alastair Donaldson 2020-04-14 20:17:42 +01:00 committed by GitHub
parent 7ce2db1763
commit f82d47003e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 197 additions and 3 deletions

View File

@ -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;

View File

@ -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<uint32_t> 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(

View File

@ -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<uint32_t>&& indices, uint32_t value) {
@ -1591,7 +1632,7 @@ void RunFuzzerAndReplayer(const std::string& shader,
std::vector<fuzzerutil::ModuleSupplier> 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

View File

@ -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

View File

@ -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