spirv-fuzz: Add image sample unused components transformation (#3439)
Fixes #3375.
diff --git a/source/fuzz/CMakeLists.txt b/source/fuzz/CMakeLists.txt
index 7bd9ff2..b54b1fb 100644
--- a/source/fuzz/CMakeLists.txt
+++ b/source/fuzz/CMakeLists.txt
@@ -46,6 +46,7 @@
fuzzer_pass_add_equation_instructions.h
fuzzer_pass_add_function_calls.h
fuzzer_pass_add_global_variables.h
+ fuzzer_pass_add_image_sample_unused_components.h
fuzzer_pass_add_loads.h
fuzzer_pass_add_local_variables.h
fuzzer_pass_add_no_contraction_decorations.h
@@ -96,6 +97,7 @@
transformation_add_function.h
transformation_add_global_undef.h
transformation_add_global_variable.h
+ transformation_add_image_sample_unused_components.h
transformation_add_local_variable.h
transformation_add_no_contraction_decoration.h
transformation_add_parameter.h
@@ -158,6 +160,7 @@
fuzzer_pass_add_equation_instructions.cpp
fuzzer_pass_add_function_calls.cpp
fuzzer_pass_add_global_variables.cpp
+ fuzzer_pass_add_image_sample_unused_components.cpp
fuzzer_pass_add_loads.cpp
fuzzer_pass_add_local_variables.cpp
fuzzer_pass_add_no_contraction_decorations.cpp
@@ -207,6 +210,7 @@
transformation_add_function.cpp
transformation_add_global_undef.cpp
transformation_add_global_variable.cpp
+ transformation_add_image_sample_unused_components.cpp
transformation_add_local_variable.cpp
transformation_add_no_contraction_decoration.cpp
transformation_add_parameter.cpp
diff --git a/source/fuzz/fuzzer.cpp b/source/fuzz/fuzzer.cpp
index 6cb6202..f08ce04 100644
--- a/source/fuzz/fuzzer.cpp
+++ b/source/fuzz/fuzzer.cpp
@@ -30,6 +30,7 @@
#include "source/fuzz/fuzzer_pass_add_equation_instructions.h"
#include "source/fuzz/fuzzer_pass_add_function_calls.h"
#include "source/fuzz/fuzzer_pass_add_global_variables.h"
+#include "source/fuzz/fuzzer_pass_add_image_sample_unused_components.h"
#include "source/fuzz/fuzzer_pass_add_loads.h"
#include "source/fuzz/fuzzer_pass_add_local_variables.h"
#include "source/fuzz/fuzzer_pass_add_no_contraction_decorations.h"
@@ -224,6 +225,9 @@
MaybeAddPass<FuzzerPassAddGlobalVariables>(
&passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
+ MaybeAddPass<FuzzerPassAddImageSampleUnusedComponents>(
+ &passes, ir_context.get(), &transformation_context, &fuzzer_context,
+ transformation_sequence_out);
MaybeAddPass<FuzzerPassAddLoads>(&passes, ir_context.get(),
&transformation_context, &fuzzer_context,
transformation_sequence_out);
diff --git a/source/fuzz/fuzzer_context.cpp b/source/fuzz/fuzzer_context.cpp
index 63aef85..5d4096b 100644
--- a/source/fuzz/fuzzer_context.cpp
+++ b/source/fuzz/fuzzer_context.cpp
@@ -34,6 +34,8 @@
const std::pair<uint32_t, uint32_t> kChanceOfAddingEquationInstruction = {5,
90};
const std::pair<uint32_t, uint32_t> kChanceOfAddingGlobalVariable = {20, 90};
+const std::pair<uint32_t, uint32_t> kChanceOfAddingImageSampleUnusedComponents =
+ {20, 90};
const std::pair<uint32_t, uint32_t> kChanceOfAddingLoad = {5, 50};
const std::pair<uint32_t, uint32_t> kChanceOfAddingLocalVariable = {20, 90};
const std::pair<uint32_t, uint32_t> kChanceOfAddingMatrixType = {20, 70};
@@ -137,6 +139,8 @@
chance_of_adding_global_variable_ =
ChooseBetweenMinAndMax(kChanceOfAddingGlobalVariable);
chance_of_adding_load_ = ChooseBetweenMinAndMax(kChanceOfAddingLoad);
+ chance_of_adding_image_sample_unused_components_ =
+ ChooseBetweenMinAndMax(kChanceOfAddingImageSampleUnusedComponents);
chance_of_adding_local_variable_ =
ChooseBetweenMinAndMax(kChanceOfAddingLocalVariable);
chance_of_adding_matrix_type_ =
diff --git a/source/fuzz/fuzzer_context.h b/source/fuzz/fuzzer_context.h
index 2bd4634..edf27a2 100644
--- a/source/fuzz/fuzzer_context.h
+++ b/source/fuzz/fuzzer_context.h
@@ -128,6 +128,9 @@
uint32_t GetChanceOfAddingGlobalVariable() {
return chance_of_adding_global_variable_;
}
+ uint32_t GetChanceOfAddingImageSampleUnusedComponents() {
+ return chance_of_adding_image_sample_unused_components_;
+ }
uint32_t GetChanceOfAddingLoad() { return chance_of_adding_load_; }
uint32_t GetChanceOfAddingLocalVariable() {
return chance_of_adding_local_variable_;
@@ -268,6 +271,11 @@
// Ensure that the array size is non-zero.
return random_generator_->RandomUint32(max_new_array_size_limit_ - 1) + 1;
}
+ uint32_t GetRandomUnusedComponentCountForImageSample(
+ uint32_t max_unused_component_count) {
+ // Ensure that the number of unused components is non-zero.
+ return random_generator_->RandomUint32(max_unused_component_count) + 1;
+ }
bool GoDeeperInConstantObfuscation(uint32_t depth) {
return go_deeper_in_constant_obfuscation_(depth, random_generator_);
}
@@ -289,6 +297,7 @@
uint32_t chance_of_adding_dead_continue_;
uint32_t chance_of_adding_equation_instruction_;
uint32_t chance_of_adding_global_variable_;
+ uint32_t chance_of_adding_image_sample_unused_components_;
uint32_t chance_of_adding_load_;
uint32_t chance_of_adding_local_variable_;
uint32_t chance_of_adding_matrix_type_;
diff --git a/source/fuzz/fuzzer_pass_add_image_sample_unused_components.cpp b/source/fuzz/fuzzer_pass_add_image_sample_unused_components.cpp
new file mode 100644
index 0000000..01fd282
--- /dev/null
+++ b/source/fuzz/fuzzer_pass_add_image_sample_unused_components.cpp
@@ -0,0 +1,200 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/fuzzer_pass_add_image_sample_unused_components.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "source/fuzz/transformation_add_image_sample_unused_components.h"
+#include "source/fuzz/transformation_composite_construct.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassAddImageSampleUnusedComponents::
+ FuzzerPassAddImageSampleUnusedComponents(
+ opt::IRContext* ir_context,
+ TransformationContext* transformation_context,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations)
+ : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+ transformations) {}
+
+FuzzerPassAddImageSampleUnusedComponents::
+ ~FuzzerPassAddImageSampleUnusedComponents() = default;
+
+void FuzzerPassAddImageSampleUnusedComponents::Apply() {
+ // SPIR-V module to help understand the transformation.
+ //
+ // OpCapability Shader
+ // %1 = OpExtInstImport "GLSL.std.450"
+ // OpMemoryModel Logical GLSL450
+ // OpEntryPoint Fragment %15 "main" %12 %14
+ // OpExecutionMode %15 OriginUpperLeft
+ //
+ // ; Decorations
+ // OpDecorate %12 Location 0 ; Input color variable location
+ // OpDecorate %13 DescriptorSet 0 ; Image coordinate variable
+ // descriptor set OpDecorate %13 Binding 0 ; Image coordinate
+ // variable binding OpDecorate %14 Location 0 ; Fragment color
+ // variable location
+ //
+ // ; Types
+ // %2 = OpTypeVoid
+ // %3 = OpTypeFunction %2
+ // %4 = OpTypeFloat 32
+ // %5 = OpTypeVector %4 2
+ // %6 = OpTypeVector %4 4
+ // %7 = OpTypeImage %4 2D 0 0 0 1 Rgba32f
+ // %8 = OpTypeSampledImage %7
+ // %9 = OpTypePointer Input %5
+ // %10 = OpTypePointer UniformConstant %8
+ // %11 = OpTypePointer Output %6
+ //
+ // ; Variables
+ // %12 = OpVariable %9 Input ; Input image coordinate variable
+ // %13 = OpVariable %10 UniformConstant ; Image variable
+ // %14 = OpVariable %11 Output ; Fragment color variable
+ //
+ // ; main function
+ // %15 = OpFunction %2 None %3
+ // %16 = OpLabel
+ // %17 = OpLoad %5 %12
+ // %18 = OpLoad %8 %13
+ // %19 = OpImageSampleImplicitLod %6 %18 %17
+ // OpStore %14 %19
+ // OpReturn
+ // OpFunctionEnd
+
+ GetIRContext()->module()->ForEachInst([this](opt::Instruction* instruction) {
+ // |instruction| %19 = OpImageSampleImplicitLod %6 %18 %17
+ if (!spvOpcodeIsImageSample(instruction->opcode())) {
+ return;
+ }
+
+ if (!GetFuzzerContext()->ChoosePercentage(
+ GetFuzzerContext()
+ ->GetChanceOfAddingImageSampleUnusedComponents())) {
+ return;
+ }
+
+ // Gets image sample coordinate information.
+ // |coordinate_instruction| %17 = OpLoad %5 %12
+ uint32_t coordinate_id = instruction->GetSingleWordInOperand(1);
+ auto coordinate_instruction =
+ GetIRContext()->get_def_use_mgr()->GetDef(coordinate_id);
+ auto coordinate_type = GetIRContext()->get_type_mgr()->GetType(
+ coordinate_instruction->type_id());
+
+ // If the coordinate is a 4-dimensional vector, then no unused components
+ // may be added.
+ if (coordinate_type->AsVector() &&
+ coordinate_type->AsVector()->element_count() == 4) {
+ return;
+ }
+
+ // If the coordinate is a scalar, then at most 3 unused components may be
+ // added. If the coordinate is a vector, then the maximum number of unused
+ // components depends on the vector size.
+ // For the sample module, the coordinate type instruction is %5 =
+ // OpTypeVector %4 2, thus |max_unused_component_count| = 4 - 2 = 2.
+ uint32_t max_unused_component_count =
+ coordinate_type->AsInteger() || coordinate_type->AsFloat()
+ ? 3
+ : 4 - coordinate_type->AsVector()->element_count();
+
+ // |unused_component_count| may be 1 or 2.
+ uint32_t unused_component_count =
+ GetFuzzerContext()->GetRandomUnusedComponentCountForImageSample(
+ max_unused_component_count);
+
+ // Gets a type for the zero-unused components.
+ uint32_t zero_constant_type_id;
+ switch (unused_component_count) {
+ case 1:
+ // If the coordinate is an integer or float, then the unused components
+ // type is the same as the coordinate. If the coordinate is a vector,
+ // then the unused components type is the same as the vector components
+ // type.
+ zero_constant_type_id =
+ coordinate_type->AsInteger() || coordinate_type->AsFloat()
+ ? coordinate_instruction->type_id()
+ : GetIRContext()->get_type_mgr()->GetId(
+ coordinate_type->AsVector()->element_type());
+ break;
+ case 2:
+ case 3:
+ // If the coordinate is an integer or float, then the unused components
+ // type is the same as the coordinate. If the coordinate is a vector,
+ // then the unused components type is the same as the coordinate
+ // components type.
+ // |zero_constant_type_id| %5 = OpTypeVector %4 2
+ zero_constant_type_id =
+ coordinate_type->AsInteger() || coordinate_type->AsFloat()
+ ? FindOrCreateVectorType(coordinate_instruction->type_id(),
+ unused_component_count)
+ : FindOrCreateVectorType(
+ GetIRContext()->get_type_mgr()->GetId(
+ coordinate_type->AsVector()->element_type()),
+ unused_component_count);
+ break;
+ default:
+ assert(false && "Should be unreachable.");
+ zero_constant_type_id = 0;
+ break;
+ }
+
+ // Gets |coordinate_type| again because the module may have changed due to
+ // the use of FindOrCreateVectorType above.
+ coordinate_type = GetIRContext()->get_type_mgr()->GetType(
+ coordinate_instruction->type_id());
+
+ // If the new vector type with unused components does not exist, then create
+ // it. |coordinate_with_unused_components_type_id| %6 = OpTypeVector %4 4
+ uint32_t coordinate_with_unused_components_type_id =
+ coordinate_type->AsInteger() || coordinate_type->AsFloat()
+ ? FindOrCreateVectorType(coordinate_instruction->type_id(),
+ 1 + unused_component_count)
+ : FindOrCreateVectorType(
+ GetIRContext()->get_type_mgr()->GetId(
+ coordinate_type->AsVector()->element_type()),
+ coordinate_type->AsVector()->element_count() +
+ unused_component_count);
+
+ // Inserts an OpCompositeConstruct instruction which
+ // represents the coordinate with unused components.
+ // |coordinate_with_unused_components_id|
+ // %22 = OpCompositeConstruct %6 %17 %21
+ uint32_t coordinate_with_unused_components_id =
+ GetFuzzerContext()->GetFreshId();
+ ApplyTransformation(TransformationCompositeConstruct(
+ coordinate_with_unused_components_type_id,
+ {coordinate_instruction->result_id(),
+ // FindOrCreateZeroConstant
+ // %20 = OpConstant %4 0
+ // %21 = OpConstantComposite %5 %20 %20
+ FindOrCreateZeroConstant(zero_constant_type_id)},
+ MakeInstructionDescriptor(GetIRContext(), instruction),
+ coordinate_with_unused_components_id));
+
+ // Tries to add unused components to the image sample coordinate.
+ // %19 = OpImageSampleImplicitLod %6 %18 %22
+ ApplyTransformation(TransformationAddImageSampleUnusedComponents(
+ coordinate_with_unused_components_id,
+ MakeInstructionDescriptor(GetIRContext(), instruction)));
+ });
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/source/fuzz/fuzzer_pass_add_image_sample_unused_components.h b/source/fuzz/fuzzer_pass_add_image_sample_unused_components.h
new file mode 100644
index 0000000..26374c3
--- /dev/null
+++ b/source/fuzz/fuzzer_pass_add_image_sample_unused_components.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_IMAGE_SAMPLE_UNUSED_COMPONENTS_H_
+#define SOURCE_FUZZ_FUZZER_PASS_ADD_IMAGE_SAMPLE_UNUSED_COMPONENTS_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// This fuzzer pass searches for image sample instructions in the module and
+// randomly applies the transformation to add unused components to the image
+// sample coordinate.
+class FuzzerPassAddImageSampleUnusedComponents : public FuzzerPass {
+ public:
+ FuzzerPassAddImageSampleUnusedComponents(
+ opt::IRContext* ir_context, TransformationContext* transformation_context,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations);
+
+ ~FuzzerPassAddImageSampleUnusedComponents();
+
+ void Apply() override;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_IMAGE_SAMPLE_UNUSED_COMPONENTS_H_
diff --git a/source/fuzz/protobufs/spvtoolsfuzz.proto b/source/fuzz/protobufs/spvtoolsfuzz.proto
index b913d02..ce95f97 100644
--- a/source/fuzz/protobufs/spvtoolsfuzz.proto
+++ b/source/fuzz/protobufs/spvtoolsfuzz.proto
@@ -383,6 +383,7 @@
TransformationAddParameter add_parameter = 52;
TransformationAddCopyMemory add_copy_memory = 53;
TransformationInvertComparisonOperator invert_comparison_operator = 54;
+ TransformationAddImageSampleUnusedComponents add_image_sample_unused_components = 55;
// Add additional option using the next available number.
}
}
@@ -616,6 +617,18 @@
}
+message TransformationAddImageSampleUnusedComponents {
+
+ // A transformation that adds unused components to an image sample coordinate.
+
+ // An vector id with the original coordinate and the unused components.
+ uint32 coordinate_with_unused_components_id = 1;
+
+ // A descriptor for an image sample instruction.
+ InstructionDescriptor instruction_descriptor = 2;
+
+}
+
message TransformationAddLocalVariable {
// Adds a local variable of the given type (which must be a pointer with
diff --git a/source/fuzz/transformation.cpp b/source/fuzz/transformation.cpp
index 26c93ef..29960e4 100644
--- a/source/fuzz/transformation.cpp
+++ b/source/fuzz/transformation.cpp
@@ -29,6 +29,7 @@
#include "source/fuzz/transformation_add_function.h"
#include "source/fuzz/transformation_add_global_undef.h"
#include "source/fuzz/transformation_add_global_variable.h"
+#include "source/fuzz/transformation_add_image_sample_unused_components.h"
#include "source/fuzz/transformation_add_local_variable.h"
#include "source/fuzz/transformation_add_no_contraction_decoration.h"
#include "source/fuzz/transformation_add_parameter.h"
@@ -112,6 +113,10 @@
case protobufs::Transformation::TransformationCase::kAddGlobalVariable:
return MakeUnique<TransformationAddGlobalVariable>(
message.add_global_variable());
+ case protobufs::Transformation::TransformationCase::
+ kAddImageSampleUnusedComponents:
+ return MakeUnique<TransformationAddImageSampleUnusedComponents>(
+ message.add_image_sample_unused_components());
case protobufs::Transformation::TransformationCase::kAddLocalVariable:
return MakeUnique<TransformationAddLocalVariable>(
message.add_local_variable());
diff --git a/source/fuzz/transformation_add_image_sample_unused_components.cpp b/source/fuzz/transformation_add_image_sample_unused_components.cpp
new file mode 100644
index 0000000..1be1d43
--- /dev/null
+++ b/source/fuzz/transformation_add_image_sample_unused_components.cpp
@@ -0,0 +1,117 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_add_image_sample_unused_components.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationAddImageSampleUnusedComponents::
+ TransformationAddImageSampleUnusedComponents(
+ const spvtools::fuzz::protobufs::
+ TransformationAddImageSampleUnusedComponents& message)
+ : message_(message) {}
+
+TransformationAddImageSampleUnusedComponents::
+ TransformationAddImageSampleUnusedComponents(
+ uint32_t coordinate_with_unused_components_id,
+ const protobufs::InstructionDescriptor& instruction_descriptor) {
+ message_.set_coordinate_with_unused_components_id(
+ coordinate_with_unused_components_id);
+ *message_.mutable_instruction_descriptor() = instruction_descriptor;
+}
+
+bool TransformationAddImageSampleUnusedComponents::IsApplicable(
+ opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
+ auto image_sample_instruction =
+ FindInstruction(message_.instruction_descriptor(), ir_context);
+
+ // The image sample instruction must be defined.
+ if (image_sample_instruction == nullptr) {
+ return false;
+ }
+
+ // The instruction must be an image sample instruction.
+ if (!spvOpcodeIsImageSample(image_sample_instruction->opcode())) {
+ return false;
+ }
+
+ uint32_t coordinate_id = image_sample_instruction->GetSingleWordInOperand(1);
+ auto coordinate_instruction =
+ ir_context->get_def_use_mgr()->GetDef(coordinate_id);
+ auto coordinate_type =
+ ir_context->get_type_mgr()->GetType(coordinate_instruction->type_id());
+
+ // It must be possible to add unused components.
+ if (coordinate_type->AsVector() &&
+ coordinate_type->AsVector()->element_count() == 4) {
+ return false;
+ }
+
+ auto coordinate_with_unused_components_instruction =
+ ir_context->get_def_use_mgr()->GetDef(
+ message_.coordinate_with_unused_components_id());
+
+ // The coordinate with unused components instruction must be defined.
+ if (coordinate_with_unused_components_instruction == nullptr) {
+ return false;
+ }
+
+ // It must be an OpCompositeConstruct instruction such that it can be checked
+ // that the original components are present.
+ if (coordinate_with_unused_components_instruction->opcode() !=
+ SpvOpCompositeConstruct) {
+ return false;
+ }
+
+ // The first constituent must be the original coordinate.
+ if (coordinate_with_unused_components_instruction->GetSingleWordInOperand(
+ 0) != coordinate_id) {
+ return false;
+ }
+
+ auto coordinate_with_unused_components_type =
+ ir_context->get_type_mgr()->GetType(
+ coordinate_with_unused_components_instruction->type_id());
+
+ // |coordinate_with_unused_components_type| must be a vector.
+ if (!coordinate_with_unused_components_type->AsVector()) {
+ return false;
+ }
+
+ return true;
+}
+
+void TransformationAddImageSampleUnusedComponents::Apply(
+ opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
+ // Sets the coordinate operand.
+ auto image_sample_instruction =
+ FindInstruction(message_.instruction_descriptor(), ir_context);
+ image_sample_instruction->SetInOperand(
+ 1, {message_.coordinate_with_unused_components_id()});
+ ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+}
+
+protobufs::Transformation
+TransformationAddImageSampleUnusedComponents::ToMessage() const {
+ protobufs::Transformation result;
+ *result.mutable_add_image_sample_unused_components() = message_;
+ return result;
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/source/fuzz/transformation_add_image_sample_unused_components.h b/source/fuzz/transformation_add_image_sample_unused_components.h
new file mode 100644
index 0000000..7493481
--- /dev/null
+++ b/source/fuzz/transformation_add_image_sample_unused_components.h
@@ -0,0 +1,57 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_IMAGE_SAMPLE_UNUSED_COMPONENTS_H_
+#define SOURCE_FUZZ_TRANSFORMATION_ADD_IMAGE_SAMPLE_UNUSED_COMPONENTS_H_
+
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/fuzz/transformation_context.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationAddImageSampleUnusedComponents : public Transformation {
+ public:
+ explicit TransformationAddImageSampleUnusedComponents(
+ const protobufs::TransformationAddImageSampleUnusedComponents& message);
+
+ TransformationAddImageSampleUnusedComponents(
+ uint32_t coordinate_with_unused_components_id,
+ const protobufs::InstructionDescriptor& instruction_descriptor);
+
+ // - |coordinate_with_unused_components_id| must identify a vector such that
+ // the first components match the components of the image sample coordinate.
+ // - |message_.instruction_descriptor| must identify an image sample
+ // instruction
+ bool IsApplicable(
+ opt::IRContext* ir_context,
+ const TransformationContext& transformation_context) const override;
+
+ // Add unused components to an image sample coordinate by replacing the
+ // coordinate with |coordinate_with_unused_components_id|.
+ void Apply(opt::IRContext* ir_context,
+ TransformationContext* transformation_context) const override;
+
+ protobufs::Transformation ToMessage() const override;
+
+ private:
+ protobufs::TransformationAddImageSampleUnusedComponents message_;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_IMAGE_SAMPLE_UNUSED_COMPONENTS_H_
diff --git a/source/opcode.cpp b/source/opcode.cpp
index 079def6..0a7a95d 100644
--- a/source/opcode.cpp
+++ b/source/opcode.cpp
@@ -666,6 +666,26 @@
}
}
+bool spvOpcodeIsImageSample(const SpvOp opcode) {
+ switch (opcode) {
+ case SpvOpImageSampleImplicitLod:
+ case SpvOpImageSampleExplicitLod:
+ case SpvOpImageSampleDrefImplicitLod:
+ case SpvOpImageSampleDrefExplicitLod:
+ case SpvOpImageSampleProjImplicitLod:
+ case SpvOpImageSampleProjExplicitLod:
+ case SpvOpImageSampleProjDrefImplicitLod:
+ case SpvOpImageSampleProjDrefExplicitLod:
+ case SpvOpImageSparseSampleImplicitLod:
+ case SpvOpImageSparseSampleExplicitLod:
+ case SpvOpImageSparseSampleDrefImplicitLod:
+ case SpvOpImageSparseSampleDrefExplicitLod:
+ return true;
+ default:
+ return false;
+ }
+}
+
std::vector<uint32_t> spvOpcodeMemorySemanticsOperandIndices(SpvOp opcode) {
switch (opcode) {
case SpvOpMemoryBarrier:
diff --git a/source/opcode.h b/source/opcode.h
index 0d8ec92..9aeba76 100644
--- a/source/opcode.h
+++ b/source/opcode.h
@@ -137,6 +137,9 @@
// Returns true for opcodes that represents linear algebra instructions.
bool spvOpcodeIsLinearAlgebra(SpvOp opcode);
+// Returns true for opcodes that represents an image sample instruction.
+bool spvOpcodeIsImageSample(SpvOp opcode);
+
// Returns a vector containing the indices of the memory semantics <id>
// operands for |opcode|.
std::vector<uint32_t> spvOpcodeMemorySemanticsOperandIndices(SpvOp opcode);
diff --git a/test/fuzz/CMakeLists.txt b/test/fuzz/CMakeLists.txt
index 8a71686..ddd6c68 100644
--- a/test/fuzz/CMakeLists.txt
+++ b/test/fuzz/CMakeLists.txt
@@ -36,6 +36,7 @@
transformation_add_function_test.cpp
transformation_add_global_undef_test.cpp
transformation_add_global_variable_test.cpp
+ transformation_add_image_sample_unused_components_test.cpp
transformation_add_local_variable_test.cpp
transformation_add_no_contraction_decoration_test.cpp
transformation_add_parameter_test.cpp
diff --git a/test/fuzz/transformation_add_image_sample_unused_components_test.cpp b/test/fuzz/transformation_add_image_sample_unused_components_test.cpp
new file mode 100644
index 0000000..fc78f9f
--- /dev/null
+++ b/test/fuzz/transformation_add_image_sample_unused_components_test.cpp
@@ -0,0 +1,256 @@
+// Copyright (c) 2020 André Perez Maselco
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/transformation_add_image_sample_unused_components.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationAddImageSampleUnusedComponentsTest, IsApplicable) {
+ std::string shader = R"(
+ OpCapability Shader
+ OpCapability LiteralSampler
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %18 "main" %17
+ OpExecutionMode %18 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %18 "main"
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %4 = OpTypeFloat 32
+ %5 = OpTypeVector %4 2
+ %6 = OpTypeVector %4 3
+ %7 = OpTypeVector %4 4
+ %8 = OpTypeImage %4 2D 0 0 0 1 Rgba32f
+ %9 = OpTypePointer Image %8
+ %10 = OpTypeSampledImage %8
+ %11 = OpTypeSampler
+ %12 = OpConstant %4 1
+ %13 = OpConstant %4 2
+ %14 = OpConstant %4 3
+ %15 = OpConstant %4 4
+ %16 = OpConstantSampler %11 None 0 Linear
+ %17 = OpVariable %9 Image
+ %18 = OpFunction %2 None %3
+ %19 = OpLabel
+ %20 = OpLoad %8 %17
+ %21 = OpSampledImage %10 %20 %16
+ %22 = OpCompositeConstruct %5 %12 %13
+ %23 = OpCompositeConstruct %6 %22 %14
+ %24 = OpCompositeConstruct %7 %23 %15
+ %25 = OpImageSampleImplicitLod %7 %21 %22
+ %26 = OpImageSampleExplicitLod %7 %21 %23 Lod %12
+ %27 = OpImageSampleExplicitLod %7 %21 %24 Lod %12
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_5;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+
+ // Tests applicable image instruction.
+ auto instruction_descriptor =
+ MakeInstructionDescriptor(25, SpvOpImageSampleImplicitLod, 0);
+ auto transformation =
+ TransformationAddImageSampleUnusedComponents(23, instruction_descriptor);
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+
+ instruction_descriptor =
+ MakeInstructionDescriptor(26, SpvOpImageSampleExplicitLod, 0);
+ transformation =
+ TransformationAddImageSampleUnusedComponents(24, instruction_descriptor);
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+
+ // Tests undefined image instructions.
+ instruction_descriptor =
+ MakeInstructionDescriptor(27, SpvOpImageSampleImplicitLod, 0);
+ transformation =
+ TransformationAddImageSampleUnusedComponents(23, instruction_descriptor);
+ ASSERT_FALSE(
+ transformation.IsApplicable(context.get(), transformation_context));
+
+ instruction_descriptor =
+ MakeInstructionDescriptor(28, SpvOpImageSampleExplicitLod, 0);
+ transformation =
+ TransformationAddImageSampleUnusedComponents(23, instruction_descriptor);
+ ASSERT_FALSE(
+ transformation.IsApplicable(context.get(), transformation_context));
+
+ // Tests non-image instructions.
+ instruction_descriptor = MakeInstructionDescriptor(19, SpvOpLabel, 0);
+ transformation =
+ TransformationAddImageSampleUnusedComponents(24, instruction_descriptor);
+ ASSERT_FALSE(
+ transformation.IsApplicable(context.get(), transformation_context));
+
+ instruction_descriptor = MakeInstructionDescriptor(20, SpvOpLoad, 0);
+ transformation =
+ TransformationAddImageSampleUnusedComponents(24, instruction_descriptor);
+ ASSERT_FALSE(
+ transformation.IsApplicable(context.get(), transformation_context));
+
+ // Tests coordinate operand being a vec4.
+ instruction_descriptor =
+ MakeInstructionDescriptor(27, SpvOpImageSampleExplicitLod, 0);
+ transformation =
+ TransformationAddImageSampleUnusedComponents(22, instruction_descriptor);
+ ASSERT_FALSE(
+ transformation.IsApplicable(context.get(), transformation_context));
+
+ // Tests undefined coordinate with unused operands.
+ instruction_descriptor =
+ MakeInstructionDescriptor(25, SpvOpImageSampleImplicitLod, 0);
+ transformation =
+ TransformationAddImageSampleUnusedComponents(27, instruction_descriptor);
+ ASSERT_FALSE(
+ transformation.IsApplicable(context.get(), transformation_context));
+
+ // Tests coordinate with unused operands being a non-OpCompositeConstruct
+ // instruction.
+ instruction_descriptor =
+ MakeInstructionDescriptor(25, SpvOpImageSampleImplicitLod, 0);
+ transformation =
+ TransformationAddImageSampleUnusedComponents(21, instruction_descriptor);
+ ASSERT_FALSE(
+ transformation.IsApplicable(context.get(), transformation_context));
+
+ // Tests the first OpCompositeConstruct constituent not being the original
+ // coordinate.
+ instruction_descriptor =
+ MakeInstructionDescriptor(25, SpvOpImageSampleImplicitLod, 0);
+ transformation =
+ TransformationAddImageSampleUnusedComponents(22, instruction_descriptor);
+ ASSERT_FALSE(
+ transformation.IsApplicable(context.get(), transformation_context));
+}
+
+TEST(TransformationAddImageSampleUnusedComponentsTest, Apply) {
+ std::string reference_shader = R"(
+ OpCapability Shader
+ OpCapability LiteralSampler
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %18 "main" %17
+ OpExecutionMode %18 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %18 "main"
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %4 = OpTypeFloat 32
+ %5 = OpTypeVector %4 2
+ %6 = OpTypeVector %4 3
+ %7 = OpTypeVector %4 4
+ %8 = OpTypeImage %4 2D 0 0 0 1 Rgba32f
+ %9 = OpTypePointer Image %8
+ %10 = OpTypeSampledImage %8
+ %11 = OpTypeSampler
+ %12 = OpConstant %4 1
+ %13 = OpConstant %4 2
+ %14 = OpConstant %4 3
+ %15 = OpConstant %4 4
+ %16 = OpConstantSampler %11 None 0 Linear
+ %17 = OpVariable %9 Image
+ %18 = OpFunction %2 None %3
+ %19 = OpLabel
+ %20 = OpLoad %8 %17
+ %21 = OpSampledImage %10 %20 %16
+ %22 = OpCompositeConstruct %5 %12 %13
+ %23 = OpCompositeConstruct %6 %22 %14
+ %24 = OpCompositeConstruct %7 %23 %15
+ %25 = OpImageSampleImplicitLod %7 %21 %22
+ %26 = OpImageSampleExplicitLod %7 %21 %23 Lod %12
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_5;
+ const auto consumer = nullptr;
+ const auto context =
+ BuildModule(env, consumer, reference_shader, kFuzzAssembleOption);
+ ASSERT_TRUE(IsValid(env, context.get()));
+
+ FactManager fact_manager;
+ spvtools::ValidatorOptions validator_options;
+ TransformationContext transformation_context(&fact_manager,
+ validator_options);
+
+ auto instruction_descriptor =
+ MakeInstructionDescriptor(25, SpvOpImageSampleImplicitLod, 0);
+ auto transformation =
+ TransformationAddImageSampleUnusedComponents(23, instruction_descriptor);
+ transformation.Apply(context.get(), &transformation_context);
+
+ instruction_descriptor =
+ MakeInstructionDescriptor(26, SpvOpImageSampleExplicitLod, 0);
+ transformation =
+ TransformationAddImageSampleUnusedComponents(24, instruction_descriptor);
+ transformation.Apply(context.get(), &transformation_context);
+
+ std::string variant_shader = R"(
+ OpCapability Shader
+ OpCapability LiteralSampler
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %18 "main" %17
+ OpExecutionMode %18 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %18 "main"
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %4 = OpTypeFloat 32
+ %5 = OpTypeVector %4 2
+ %6 = OpTypeVector %4 3
+ %7 = OpTypeVector %4 4
+ %8 = OpTypeImage %4 2D 0 0 0 1 Rgba32f
+ %9 = OpTypePointer Image %8
+ %10 = OpTypeSampledImage %8
+ %11 = OpTypeSampler
+ %12 = OpConstant %4 1
+ %13 = OpConstant %4 2
+ %14 = OpConstant %4 3
+ %15 = OpConstant %4 4
+ %16 = OpConstantSampler %11 None 0 Linear
+ %17 = OpVariable %9 Image
+ %18 = OpFunction %2 None %3
+ %19 = OpLabel
+ %20 = OpLoad %8 %17
+ %21 = OpSampledImage %10 %20 %16
+ %22 = OpCompositeConstruct %5 %12 %13
+ %23 = OpCompositeConstruct %6 %22 %14
+ %24 = OpCompositeConstruct %7 %23 %15
+ %25 = OpImageSampleImplicitLod %7 %21 %23
+ %26 = OpImageSampleExplicitLod %7 %21 %24 Lod %12
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ ASSERT_TRUE(IsEqual(env, variant_shader, context.get()));
+}
+
+} // namespace
+} // namespace fuzz
+} // namespace spvtools