mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2025-01-11 09:00:06 +00:00
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:
parent
7ce2db1763
commit
f82d47003e
@ -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;
|
||||
|
@ -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(
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user