spirv-fuzz: vector shuffle transformation (#3015)
Inroduces a new transformation that adds a vector shuffle instruction
to the module, with associated facts about how the result vector of
the shuffle relates to the input vectors.
A fuzzer pass to add such transformations is not yet in place.
diff --git a/source/fuzz/CMakeLists.txt b/source/fuzz/CMakeLists.txt
index 627fcba..97f8976 100644
--- a/source/fuzz/CMakeLists.txt
+++ b/source/fuzz/CMakeLists.txt
@@ -80,6 +80,7 @@
transformation_set_memory_operands_mask.h
transformation_set_selection_control.h
transformation_split_block.h
+ transformation_vector_shuffle.h
uniform_buffer_element_descriptor.h
${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.h
@@ -132,6 +133,7 @@
transformation_set_memory_operands_mask.cpp
transformation_set_selection_control.cpp
transformation_split_block.cpp
+ transformation_vector_shuffle.cpp
uniform_buffer_element_descriptor.cpp
${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.cc
)
diff --git a/source/fuzz/protobufs/spvtoolsfuzz.proto b/source/fuzz/protobufs/spvtoolsfuzz.proto
index b558ca8..17984f9 100644
--- a/source/fuzz/protobufs/spvtoolsfuzz.proto
+++ b/source/fuzz/protobufs/spvtoolsfuzz.proto
@@ -189,6 +189,7 @@
TransformationAddNoContractionDecoration add_no_contraction_decoration = 19;
TransformationSetMemoryOperandsMask set_memory_operands_mask = 20;
TransformationCompositeExtract composite_extract = 21;
+ TransformationVectorShuffle vector_shuffle = 22;
// Add additional option using the next available number.
}
}
@@ -537,3 +538,25 @@
uint32 fresh_id = 2;
}
+
+message TransformationVectorShuffle {
+
+ // A transformation that adds a vector shuffle instruction.
+
+ // A descriptor for an instruction in a block before which the new
+ // OpVectorShuffle instruction should be inserted
+ InstructionDescriptor instruction_to_insert_before = 1;
+
+ // Result id for the shuffle operation.
+ uint32 fresh_id = 2;
+
+ // Id of the first vector operand.
+ uint32 vector1 = 3;
+
+ // Id of the second vector operand.
+ uint32 vector2 = 4;
+
+ // Indices that indicate which components of the input vectors should be used.
+ repeated uint32 component = 5;
+
+}
diff --git a/source/fuzz/transformation.cpp b/source/fuzz/transformation.cpp
index ddfb2a8..d8fc92f 100644
--- a/source/fuzz/transformation.cpp
+++ b/source/fuzz/transformation.cpp
@@ -37,6 +37,7 @@
#include "source/fuzz/transformation_set_memory_operands_mask.h"
#include "source/fuzz/transformation_set_selection_control.h"
#include "source/fuzz/transformation_split_block.h"
+#include "source/fuzz/transformation_vector_shuffle.h"
#include "source/util/make_unique.h"
namespace spvtools {
@@ -107,6 +108,8 @@
message.set_selection_control());
case protobufs::Transformation::TransformationCase::kSplitBlock:
return MakeUnique<TransformationSplitBlock>(message.split_block());
+ case protobufs::Transformation::TransformationCase::kVectorShuffle:
+ return MakeUnique<TransformationVectorShuffle>(message.vector_shuffle());
case protobufs::Transformation::TRANSFORMATION_NOT_SET:
assert(false && "An unset transformation was encountered.");
return nullptr;
diff --git a/source/fuzz/transformation_vector_shuffle.cpp b/source/fuzz/transformation_vector_shuffle.cpp
new file mode 100644
index 0000000..e2d889d
--- /dev/null
+++ b/source/fuzz/transformation_vector_shuffle.cpp
@@ -0,0 +1,203 @@
+// Copyright (c) 2019 Google LLC
+//
+// 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_vector_shuffle.h"
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationVectorShuffle::TransformationVectorShuffle(
+ const spvtools::fuzz::protobufs::TransformationVectorShuffle& message)
+ : message_(message) {}
+
+TransformationVectorShuffle::TransformationVectorShuffle(
+ const protobufs::InstructionDescriptor& instruction_to_insert_before,
+ uint32_t fresh_id, uint32_t vector1, uint32_t vector2,
+ const std::vector<uint32_t>& component) {
+ *message_.mutable_instruction_to_insert_before() =
+ instruction_to_insert_before;
+ message_.set_fresh_id(fresh_id);
+ message_.set_vector1(vector1);
+ message_.set_vector2(vector2);
+ for (auto a_component : component) {
+ message_.add_component(a_component);
+ }
+}
+
+bool TransformationVectorShuffle::IsApplicable(
+ opt::IRContext* context,
+ const spvtools::fuzz::FactManager& /*unused*/) const {
+ // The fresh id must not already be in use.
+ if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) {
+ return false;
+ }
+ // The instruction before which the shuffle will be inserted must exist.
+ auto instruction_to_insert_before =
+ FindInstruction(message_.instruction_to_insert_before(), context);
+ if (!instruction_to_insert_before) {
+ return false;
+ }
+ // The first vector must be an instruction with a type id
+ auto vector1_instruction =
+ context->get_def_use_mgr()->GetDef(message_.vector1());
+ if (!vector1_instruction || !vector1_instruction->type_id()) {
+ return false;
+ }
+ // The second vector must be an instruction with a type id
+ auto vector2_instruction =
+ context->get_def_use_mgr()->GetDef(message_.vector2());
+ if (!vector2_instruction || !vector2_instruction->type_id()) {
+ return false;
+ }
+ auto vector1_type =
+ context->get_type_mgr()->GetType(vector1_instruction->type_id());
+ // The first vector instruction's type must actually be a vector type.
+ if (!vector1_type->AsVector()) {
+ return false;
+ }
+ auto vector2_type =
+ context->get_type_mgr()->GetType(vector2_instruction->type_id());
+ // The second vector instruction's type must actually be a vector type.
+ if (!vector2_type->AsVector()) {
+ return false;
+ }
+ // The element types of the vectors must be the same.
+ if (vector1_type->AsVector()->element_type() !=
+ vector2_type->AsVector()->element_type()) {
+ return false;
+ }
+ uint32_t combined_size = vector1_type->AsVector()->element_count() +
+ vector2_type->AsVector()->element_count();
+ for (auto a_compoment : message_.component()) {
+ // 0xFFFFFFFF is used to represent an undefined component. Unless
+ // undefined, a component must be less than the combined size of the
+ // vectors.
+ if (a_compoment != 0xFFFFFFFF && a_compoment >= combined_size) {
+ return false;
+ }
+ }
+ // The module must already declare an appropriate type in which to store the
+ // result of the shuffle.
+ if (!GetResultTypeId(context, *vector1_type->AsVector()->element_type())) {
+ return false;
+ }
+ // Each of the vectors used in the shuffle must be available at the insertion
+ // point.
+ for (auto used_instruction : {vector1_instruction, vector2_instruction}) {
+ if (auto block = context->get_instr_block(used_instruction)) {
+ if (!context->GetDominatorAnalysis(block->GetParent())
+ ->Dominates(used_instruction, instruction_to_insert_before)) {
+ return false;
+ }
+ }
+ }
+
+ // It must be legitimate to insert an OpVectorShuffle before the identified
+ // instruction.
+ return fuzzerutil::CanInsertOpcodeBeforeInstruction(
+ SpvOpVectorShuffle, instruction_to_insert_before);
+}
+
+void TransformationVectorShuffle::Apply(
+ opt::IRContext* context, spvtools::fuzz::FactManager* fact_manager) const {
+ // Make input operands for a shuffle instruction - these comprise the two
+ // vectors being shuffled, followed by the integer literal components.
+ opt::Instruction::OperandList shuffle_operands = {
+ {SPV_OPERAND_TYPE_ID, {message_.vector1()}},
+ {SPV_OPERAND_TYPE_ID, {message_.vector2()}}};
+ for (auto a_component : message_.component()) {
+ shuffle_operands.push_back(
+ {SPV_OPERAND_TYPE_LITERAL_INTEGER, {a_component}});
+ }
+
+ uint32_t result_type_id = GetResultTypeId(
+ context, *GetVectorType(context, message_.vector1())->element_type());
+
+ // Add a shuffle instruction right before the instruction identified by
+ // |message_.instruction_to_insert_before|.
+ FindInstruction(message_.instruction_to_insert_before(), context)
+ ->InsertBefore(MakeUnique<opt::Instruction>(
+ context, SpvOpVectorShuffle, result_type_id, message_.fresh_id(),
+ shuffle_operands));
+ fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id());
+ context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone);
+
+ // Add synonym facts relating the defined elements of the shuffle result to
+ // the vector components that they come from.
+ for (uint32_t component_index = 0;
+ component_index < static_cast<uint32_t>(message_.component_size());
+ component_index++) {
+ uint32_t component = message_.component(component_index);
+ if (component == 0xFFFFFFFF) {
+ // This component is undefined, so move on - but first note that the
+ // overall shuffle result cannot be synonymous with any vector.
+ continue;
+ }
+
+ // This describes the element of the result vector associated with
+ // |component_index|.
+ protobufs::DataDescriptor descriptor_for_result_component =
+ MakeDataDescriptor(message_.fresh_id(), {component_index});
+
+ protobufs::DataDescriptor descriptor_for_source_component;
+
+ // Get a data descriptor for the component of the input vector to which
+ // |component| refers.
+ if (component <
+ GetVectorType(context, message_.vector1())->element_count()) {
+ descriptor_for_source_component =
+ MakeDataDescriptor(message_.vector1(), {component});
+ } else {
+ auto index_into_vector_2 =
+ component -
+ GetVectorType(context, message_.vector1())->element_count();
+ assert(index_into_vector_2 <
+ GetVectorType(context, message_.vector2())->element_count() &&
+ "Vector shuffle index is out of bounds.");
+ descriptor_for_source_component =
+ MakeDataDescriptor(message_.vector2(), {index_into_vector_2});
+ }
+
+ // Add a fact relating this input vector component with the associated
+ // result component.
+ fact_manager->AddFactDataSynonym(descriptor_for_result_component,
+ descriptor_for_source_component, context);
+ }
+}
+
+protobufs::Transformation TransformationVectorShuffle::ToMessage() const {
+ protobufs::Transformation result;
+ *result.mutable_vector_shuffle() = message_;
+ return result;
+}
+
+uint32_t TransformationVectorShuffle::GetResultTypeId(
+ opt::IRContext* context, const opt::analysis::Type& element_type) const {
+ opt::analysis::Vector result_type(
+ &element_type, static_cast<uint32_t>(message_.component_size()));
+ return context->get_type_mgr()->GetId(&result_type);
+}
+
+opt::analysis::Vector* TransformationVectorShuffle::GetVectorType(
+ opt::IRContext* context, uint32_t id_of_vector) {
+ return context->get_type_mgr()
+ ->GetType(context->get_def_use_mgr()->GetDef(id_of_vector)->type_id())
+ ->AsVector();
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/source/fuzz/transformation_vector_shuffle.h b/source/fuzz/transformation_vector_shuffle.h
new file mode 100644
index 0000000..81ed227
--- /dev/null
+++ b/source/fuzz/transformation_vector_shuffle.h
@@ -0,0 +1,82 @@
+// Copyright (c) 2019 Google LLC
+//
+// 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_VECTOR_SHUFFLE_H_
+#define SOURCE_FUZZ_TRANSFORMATION_VECTOR_SHUFFLE_H_
+
+#include "source/fuzz/fact_manager.h"
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/fuzz/transformation.h"
+#include "source/opt/ir_context.h"
+#include "source/opt/types.h"
+
+namespace spvtools {
+namespace fuzz {
+
+class TransformationVectorShuffle : public Transformation {
+ public:
+ explicit TransformationVectorShuffle(
+ const protobufs::TransformationVectorShuffle& message);
+
+ TransformationVectorShuffle(
+ const protobufs::InstructionDescriptor& instruction_to_insert_before,
+ uint32_t fresh_id, uint32_t vector1, uint32_t vector2,
+ const std::vector<uint32_t>& component);
+
+ // - |message_.fresh_id| must not be in use
+ // - |message_.instruction_to_insert_before| must identify an instruction
+ // before which it is legitimate to insert an OpVectorShuffle
+ // - |message_.vector1| and |message_.vector2| must be instructions of vector
+ // type, and the element types of these vectors must be the same
+ // - Each element of |message_.component| must either be 0xFFFFFFFF
+ // (representing an undefined component), or must be less than the combined
+ // sizes of the input vectors
+ // - The module must already contain a vector type with the same element type
+ // as |message_.vector1| and |message_.vector2|, and with the size of
+ // |message_component| as its element count
+ bool IsApplicable(opt::IRContext* context,
+ const FactManager& fact_manager) const override;
+
+ // Inserts an OpVectorShuffle instruction before
+ // |message_.instruction_to_insert_before|, shuffles vectors
+ // |message_.vector1| and |message_.vector2| using the indices provided by
+ // |message_.component|, into |message_.fresh_id|. Adds a fact to the fact
+ // manager recording the fact each element of |message_.fresh_id| is
+ // synonymous with the element of |message_.vector1| or |message_.vector2|
+ // from which it came (with undefined components being ignored). If the
+ // result vector is a contiguous sub-range of one of the input vectors, a
+ // fact is added to record that |message_.fresh_id| is synonymous with this
+ // sub-range.
+ void Apply(opt::IRContext* context, FactManager* fact_manager) const override;
+
+ protobufs::Transformation ToMessage() const override;
+
+ private:
+ // Returns a type id that already exists in |context| suitable for
+ // representing the result of the shuffle, where |element_type| is known to
+ // be the common element type of the vectors to which the shuffle is being
+ // applied. Returns 0 if no such id exists.
+ uint32_t GetResultTypeId(opt::IRContext* context,
+ const opt::analysis::Type& element_type) const;
+
+ static opt::analysis::Vector* GetVectorType(opt::IRContext* context,
+ uint32_t id_of_vector);
+
+ protobufs::TransformationVectorShuffle message_;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SOURCE_FUZZ_TRANSFORMATION_VECTOR_SHUFFLE_H_
diff --git a/test/fuzz/CMakeLists.txt b/test/fuzz/CMakeLists.txt
index cfa89f9..2a17090 100644
--- a/test/fuzz/CMakeLists.txt
+++ b/test/fuzz/CMakeLists.txt
@@ -42,6 +42,7 @@
transformation_set_memory_operands_mask_test.cpp
transformation_set_selection_control_test.cpp
transformation_split_block_test.cpp
+ transformation_vector_shuffle_test.cpp
uniform_buffer_element_descriptor_test.cpp)
if (${SPIRV_ENABLE_LONG_FUZZER_TESTS})
diff --git a/test/fuzz/transformation_vector_shuffle_test.cpp b/test/fuzz/transformation_vector_shuffle_test.cpp
new file mode 100644
index 0000000..385c38b
--- /dev/null
+++ b/test/fuzz/transformation_vector_shuffle_test.cpp
@@ -0,0 +1,532 @@
+// Copyright (c) 2019 Google LLC
+//
+// 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_vector_shuffle.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationVectorShuffle, BasicTest) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeBool
+ %7 = OpTypeVector %6 2
+ %10 = OpConstantTrue %6
+ %11 = OpConstantFalse %6
+ %12 = OpConstantComposite %7 %10 %11
+ %112 = OpUndef %7
+ %13 = OpTypeVector %6 3
+ %16 = OpConstantComposite %13 %10 %11 %10
+ %17 = OpTypeVector %6 4
+ %20 = OpConstantComposite %17 %10 %11 %10 %11
+ %21 = OpTypeInt 32 1
+ %22 = OpTypeVector %21 2
+ %25 = OpConstant %21 1
+ %26 = OpConstant %21 0
+ %27 = OpConstantComposite %22 %25 %26
+ %28 = OpTypeVector %21 3
+ %31 = OpConstantComposite %28 %25 %26 %25
+ %32 = OpTypeVector %21 4
+ %33 = OpTypePointer Function %32
+ %35 = OpConstantComposite %32 %25 %26 %25 %26
+ %36 = OpTypeInt 32 0
+ %37 = OpTypeVector %36 2
+ %40 = OpConstant %36 1
+ %41 = OpConstant %36 0
+ %42 = OpConstantComposite %37 %40 %41
+ %43 = OpTypeVector %36 3
+ %46 = OpConstantComposite %43 %40 %41 %40
+ %47 = OpTypeVector %36 4
+ %50 = OpConstantComposite %47 %40 %41 %40 %41
+ %51 = OpTypeFloat 32
+ %55 = OpConstant %51 1
+ %56 = OpConstant %51 0
+ %58 = OpTypeVector %51 3
+ %61 = OpConstantComposite %58 %55 %56 %55
+ %62 = OpTypeVector %51 4
+ %65 = OpConstantComposite %62 %55 %56 %55 %56
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpSelectionMerge %100 None
+ OpBranchConditional %10 %101 %102
+ %101 = OpLabel
+ %103 = OpCompositeConstruct %62 %55 %55 %55 %56
+ OpBranch %100
+ %102 = OpLabel
+ OpBranch %100
+ %100 = OpLabel
+ OpReturn
+ 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()));
+
+ FactManager fact_manager;
+
+ fact_manager.AddFactDataSynonym(MakeDataDescriptor(10, {}),
+ MakeDataDescriptor(12, {0}), context.get());
+ fact_manager.AddFactDataSynonym(MakeDataDescriptor(11, {}),
+ MakeDataDescriptor(12, {1}), context.get());
+
+ fact_manager.AddFactDataSynonym(MakeDataDescriptor(10, {}),
+ MakeDataDescriptor(16, {0}), context.get());
+ fact_manager.AddFactDataSynonym(MakeDataDescriptor(11, {}),
+ MakeDataDescriptor(16, {1}), context.get());
+ fact_manager.AddFactDataSynonym(MakeDataDescriptor(10, {}),
+ MakeDataDescriptor(16, {2}), context.get());
+
+ fact_manager.AddFactDataSynonym(MakeDataDescriptor(10, {}),
+ MakeDataDescriptor(20, {0}), context.get());
+ fact_manager.AddFactDataSynonym(MakeDataDescriptor(11, {}),
+ MakeDataDescriptor(20, {1}), context.get());
+ fact_manager.AddFactDataSynonym(MakeDataDescriptor(10, {}),
+ MakeDataDescriptor(20, {2}), context.get());
+ fact_manager.AddFactDataSynonym(MakeDataDescriptor(11, {}),
+ MakeDataDescriptor(20, {3}), context.get());
+
+ fact_manager.AddFactDataSynonym(MakeDataDescriptor(25, {}),
+ MakeDataDescriptor(27, {0}), context.get());
+ fact_manager.AddFactDataSynonym(MakeDataDescriptor(26, {}),
+ MakeDataDescriptor(27, {1}), context.get());
+
+ fact_manager.AddFactDataSynonym(MakeDataDescriptor(25, {}),
+ MakeDataDescriptor(31, {0}), context.get());
+ fact_manager.AddFactDataSynonym(MakeDataDescriptor(26, {}),
+ MakeDataDescriptor(31, {1}), context.get());
+ fact_manager.AddFactDataSynonym(MakeDataDescriptor(25, {}),
+ MakeDataDescriptor(31, {2}), context.get());
+
+ fact_manager.AddFactDataSynonym(MakeDataDescriptor(25, {}),
+ MakeDataDescriptor(35, {0}), context.get());
+ fact_manager.AddFactDataSynonym(MakeDataDescriptor(26, {}),
+ MakeDataDescriptor(35, {1}), context.get());
+ fact_manager.AddFactDataSynonym(MakeDataDescriptor(25, {}),
+ MakeDataDescriptor(35, {2}), context.get());
+ fact_manager.AddFactDataSynonym(MakeDataDescriptor(26, {}),
+ MakeDataDescriptor(35, {3}), context.get());
+
+ fact_manager.AddFactDataSynonym(MakeDataDescriptor(40, {}),
+ MakeDataDescriptor(42, {0}), context.get());
+ fact_manager.AddFactDataSynonym(MakeDataDescriptor(41, {}),
+ MakeDataDescriptor(42, {1}), context.get());
+
+ fact_manager.AddFactDataSynonym(MakeDataDescriptor(40, {}),
+ MakeDataDescriptor(46, {0}), context.get());
+ fact_manager.AddFactDataSynonym(MakeDataDescriptor(41, {}),
+ MakeDataDescriptor(46, {1}), context.get());
+ fact_manager.AddFactDataSynonym(MakeDataDescriptor(40, {}),
+ MakeDataDescriptor(46, {2}), context.get());
+
+ fact_manager.AddFactDataSynonym(MakeDataDescriptor(40, {}),
+ MakeDataDescriptor(50, {0}), context.get());
+ fact_manager.AddFactDataSynonym(MakeDataDescriptor(41, {}),
+ MakeDataDescriptor(50, {1}), context.get());
+ fact_manager.AddFactDataSynonym(MakeDataDescriptor(40, {}),
+ MakeDataDescriptor(50, {2}), context.get());
+ fact_manager.AddFactDataSynonym(MakeDataDescriptor(41, {}),
+ MakeDataDescriptor(50, {3}), context.get());
+
+ fact_manager.AddFactDataSynonym(MakeDataDescriptor(55, {}),
+ MakeDataDescriptor(61, {0}), context.get());
+ fact_manager.AddFactDataSynonym(MakeDataDescriptor(56, {}),
+ MakeDataDescriptor(61, {1}), context.get());
+ fact_manager.AddFactDataSynonym(MakeDataDescriptor(55, {}),
+ MakeDataDescriptor(61, {2}), context.get());
+
+ fact_manager.AddFactDataSynonym(MakeDataDescriptor(55, {}),
+ MakeDataDescriptor(65, {0}), context.get());
+ fact_manager.AddFactDataSynonym(MakeDataDescriptor(56, {}),
+ MakeDataDescriptor(65, {1}), context.get());
+ fact_manager.AddFactDataSynonym(MakeDataDescriptor(55, {}),
+ MakeDataDescriptor(65, {2}), context.get());
+ fact_manager.AddFactDataSynonym(MakeDataDescriptor(56, {}),
+ MakeDataDescriptor(65, {3}), context.get());
+
+ // %103 does not dominate the return instruction.
+ ASSERT_FALSE(TransformationVectorShuffle(
+ MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 103, 65,
+ {3, 5, 7})
+ .IsApplicable(context.get(), fact_manager));
+
+ // Illegal to shuffle a bvec2 and a vec3
+ ASSERT_FALSE(TransformationVectorShuffle(
+ MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 112, 61,
+ {0, 2, 4})
+ .IsApplicable(context.get(), fact_manager));
+
+ // Illegal to shuffle an ivec2 and a uvec4
+ ASSERT_FALSE(TransformationVectorShuffle(
+ MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 27, 50,
+ {1, 3, 5})
+ .IsApplicable(context.get(), fact_manager));
+
+ // Vector 1 does not exist
+ ASSERT_FALSE(TransformationVectorShuffle(
+ MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 300, 50,
+ {1, 3, 5})
+ .IsApplicable(context.get(), fact_manager));
+
+ // Vector 2 does not exist
+ ASSERT_FALSE(TransformationVectorShuffle(
+ MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 27, 300,
+ {1, 3, 5})
+ .IsApplicable(context.get(), fact_manager));
+
+ // Index out of range
+ ASSERT_FALSE(
+ TransformationVectorShuffle(
+ MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 12, 112, {0, 20})
+ .IsApplicable(context.get(), fact_manager));
+
+ // Too many indices
+ ASSERT_FALSE(TransformationVectorShuffle(
+ MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 12, 112,
+ {0, 1, 0, 1, 0, 1, 0, 1})
+ .IsApplicable(context.get(), fact_manager));
+
+ // Too few indices
+ ASSERT_FALSE(
+ TransformationVectorShuffle(
+ MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 12, 112, {})
+ .IsApplicable(context.get(), fact_manager));
+
+ // Too few indices again
+ ASSERT_FALSE(
+ TransformationVectorShuffle(
+ MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 12, 112, {0})
+ .IsApplicable(context.get(), fact_manager));
+
+ // Indices define unknown type: we do not have vec2
+ ASSERT_FALSE(
+ TransformationVectorShuffle(
+ MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 65, 65, {0, 1})
+ .IsApplicable(context.get(), fact_manager));
+
+ // The instruction to insert before does not exist
+ ASSERT_FALSE(TransformationVectorShuffle(
+ MakeInstructionDescriptor(100, SpvOpCompositeConstruct, 1),
+ 201, 20, 12, {0xFFFFFFFF, 3, 5})
+ .IsApplicable(context.get(), fact_manager));
+
+ // The 'fresh' id is already in use
+ ASSERT_FALSE(
+ TransformationVectorShuffle(
+ MakeInstructionDescriptor(100, SpvOpReturn, 0), 12, 12, 112, {})
+ .IsApplicable(context.get(), fact_manager));
+
+ protobufs::DataDescriptor temp_dd;
+
+ TransformationVectorShuffle transformation1(
+ MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 12, 112, {1, 0});
+ ASSERT_TRUE(transformation1.IsApplicable(context.get(), fact_manager));
+ transformation1.Apply(context.get(), &fact_manager);
+ temp_dd = MakeDataDescriptor(200, {0});
+ ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(11, {}), temp_dd,
+ context.get()));
+ temp_dd = MakeDataDescriptor(200, {1});
+ ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(10, {}), temp_dd,
+ context.get()));
+
+ TransformationVectorShuffle transformation2(
+ MakeInstructionDescriptor(100, SpvOpReturn, 0), 201, 20, 12,
+ {0xFFFFFFFF, 3, 5});
+ ASSERT_TRUE(transformation2.IsApplicable(context.get(), fact_manager));
+ transformation2.Apply(context.get(), &fact_manager);
+ temp_dd = MakeDataDescriptor(201, {1});
+ ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(11, {}), temp_dd,
+ context.get()));
+ temp_dd = MakeDataDescriptor(201, {2});
+ ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(11, {}), temp_dd,
+ context.get()));
+
+ TransformationVectorShuffle transformation3(
+ MakeInstructionDescriptor(100, SpvOpReturn, 0), 202, 27, 35, {5, 4, 1});
+ ASSERT_TRUE(transformation3.IsApplicable(context.get(), fact_manager));
+ transformation3.Apply(context.get(), &fact_manager);
+ temp_dd = MakeDataDescriptor(202, {0});
+ ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(26, {}), temp_dd,
+ context.get()));
+ temp_dd = MakeDataDescriptor(202, {1});
+ ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(25, {}), temp_dd,
+ context.get()));
+ temp_dd = MakeDataDescriptor(202, {2});
+ ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(26, {}), temp_dd,
+ context.get()));
+
+ TransformationVectorShuffle transformation4(
+ MakeInstructionDescriptor(100, SpvOpReturn, 0), 203, 42, 46, {0, 1});
+ ASSERT_TRUE(transformation4.IsApplicable(context.get(), fact_manager));
+ transformation4.Apply(context.get(), &fact_manager);
+ temp_dd = MakeDataDescriptor(203, {0});
+ ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {}), temp_dd,
+ context.get()));
+ temp_dd = MakeDataDescriptor(203, {1});
+ ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(41, {}), temp_dd,
+ context.get()));
+
+ TransformationVectorShuffle transformation5(
+ MakeInstructionDescriptor(100, SpvOpReturn, 0), 204, 42, 46, {2, 3, 4});
+ ASSERT_TRUE(transformation5.IsApplicable(context.get(), fact_manager));
+ transformation5.Apply(context.get(), &fact_manager);
+ temp_dd = MakeDataDescriptor(204, {0});
+ ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {}), temp_dd,
+ context.get()));
+ temp_dd = MakeDataDescriptor(204, {1});
+ ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(41, {}), temp_dd,
+ context.get()));
+ temp_dd = MakeDataDescriptor(204, {2});
+ ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {}), temp_dd,
+ context.get()));
+
+ TransformationVectorShuffle transformation6(
+ MakeInstructionDescriptor(100, SpvOpReturn, 0), 205, 42, 42,
+ {0, 1, 2, 3});
+ ASSERT_TRUE(transformation6.IsApplicable(context.get(), fact_manager));
+ transformation6.Apply(context.get(), &fact_manager);
+ temp_dd = MakeDataDescriptor(205, {0});
+ ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {}), temp_dd,
+ context.get()));
+ temp_dd = MakeDataDescriptor(205, {1});
+ ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(41, {}), temp_dd,
+ context.get()));
+ temp_dd = MakeDataDescriptor(205, {2});
+ ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(40, {}), temp_dd,
+ context.get()));
+ temp_dd = MakeDataDescriptor(205, {3});
+ ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(41, {}), temp_dd,
+ context.get()));
+
+ // swizzle vec4 from vec4 and vec4 using some undefs
+ TransformationVectorShuffle transformation7(
+ MakeInstructionDescriptor(100, SpvOpReturn, 0), 206, 65, 65,
+ {0xFFFFFFFF, 3, 6, 0xFFFFFFFF});
+ ASSERT_TRUE(transformation7.IsApplicable(context.get(), fact_manager));
+ transformation7.Apply(context.get(), &fact_manager);
+ temp_dd = MakeDataDescriptor(206, {1});
+ ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(56, {}), temp_dd,
+ context.get()));
+
+ std::string after_transformation = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main"
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeBool
+ %7 = OpTypeVector %6 2
+ %10 = OpConstantTrue %6
+ %11 = OpConstantFalse %6
+ %12 = OpConstantComposite %7 %10 %11
+ %112 = OpUndef %7
+ %13 = OpTypeVector %6 3
+ %16 = OpConstantComposite %13 %10 %11 %10
+ %17 = OpTypeVector %6 4
+ %20 = OpConstantComposite %17 %10 %11 %10 %11
+ %21 = OpTypeInt 32 1
+ %22 = OpTypeVector %21 2
+ %25 = OpConstant %21 1
+ %26 = OpConstant %21 0
+ %27 = OpConstantComposite %22 %25 %26
+ %28 = OpTypeVector %21 3
+ %31 = OpConstantComposite %28 %25 %26 %25
+ %32 = OpTypeVector %21 4
+ %33 = OpTypePointer Function %32
+ %35 = OpConstantComposite %32 %25 %26 %25 %26
+ %36 = OpTypeInt 32 0
+ %37 = OpTypeVector %36 2
+ %40 = OpConstant %36 1
+ %41 = OpConstant %36 0
+ %42 = OpConstantComposite %37 %40 %41
+ %43 = OpTypeVector %36 3
+ %46 = OpConstantComposite %43 %40 %41 %40
+ %47 = OpTypeVector %36 4
+ %50 = OpConstantComposite %47 %40 %41 %40 %41
+ %51 = OpTypeFloat 32
+ %55 = OpConstant %51 1
+ %56 = OpConstant %51 0
+ %58 = OpTypeVector %51 3
+ %61 = OpConstantComposite %58 %55 %56 %55
+ %62 = OpTypeVector %51 4
+ %65 = OpConstantComposite %62 %55 %56 %55 %56
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ OpSelectionMerge %100 None
+ OpBranchConditional %10 %101 %102
+ %101 = OpLabel
+ %103 = OpCompositeConstruct %62 %55 %55 %55 %56
+ OpBranch %100
+ %102 = OpLabel
+ OpBranch %100
+ %100 = OpLabel
+ %200 = OpVectorShuffle %7 %12 %112 1 0
+ %201 = OpVectorShuffle %13 %20 %12 0xFFFFFFFF 3 5
+ %202 = OpVectorShuffle %28 %27 %35 5 4 1
+ %203 = OpVectorShuffle %37 %42 %46 0 1
+ %204 = OpVectorShuffle %43 %42 %46 2 3 4
+ %205 = OpVectorShuffle %47 %42 %42 0 1 2 3
+ %206 = OpVectorShuffle %62 %65 %65 0xFFFFFFFF 3 6 0xFFFFFFFF
+ OpReturn
+ OpFunctionEnd
+ )";
+ ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationVectorShuffleTest, IllegalInsertionPoints) {
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %51 %27
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %25 "buf"
+ OpMemberName %25 0 "value"
+ OpName %27 ""
+ OpName %51 "color"
+ OpMemberDecorate %25 0 Offset 0
+ OpDecorate %25 Block
+ OpDecorate %27 DescriptorSet 0
+ OpDecorate %27 Binding 0
+ OpDecorate %51 Location 0
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypeVector %6 4
+ %150 = OpTypeVector %6 2
+ %10 = OpConstant %6 0.300000012
+ %11 = OpConstant %6 0.400000006
+ %12 = OpConstant %6 0.5
+ %13 = OpConstant %6 1
+ %14 = OpConstantComposite %7 %10 %11 %12 %13
+ %15 = OpTypeInt 32 1
+ %18 = OpConstant %15 0
+ %25 = OpTypeStruct %6
+ %26 = OpTypePointer Uniform %25
+ %27 = OpVariable %26 Uniform
+ %28 = OpTypePointer Uniform %6
+ %32 = OpTypeBool
+ %103 = OpConstantTrue %32
+ %34 = OpConstant %6 0.100000001
+ %48 = OpConstant %15 1
+ %50 = OpTypePointer Output %7
+ %51 = OpVariable %50 Output
+ %100 = OpTypePointer Function %6
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %101 = OpVariable %100 Function
+ %102 = OpVariable %100 Function
+ OpBranch %19
+ %19 = OpLabel
+ %60 = OpPhi %7 %14 %5 %58 %20
+ %59 = OpPhi %15 %18 %5 %49 %20
+ %29 = OpAccessChain %28 %27 %18
+ %30 = OpLoad %6 %29
+ %31 = OpConvertFToS %15 %30
+ %33 = OpSLessThan %32 %59 %31
+ OpLoopMerge %21 %20 None
+ OpBranchConditional %33 %20 %21
+ %20 = OpLabel
+ %39 = OpCompositeExtract %6 %60 0
+ %40 = OpFAdd %6 %39 %34
+ %55 = OpCompositeInsert %7 %40 %60 0
+ %44 = OpCompositeExtract %6 %60 1
+ %45 = OpFSub %6 %44 %34
+ %58 = OpCompositeInsert %7 %45 %55 1
+ %49 = OpIAdd %15 %59 %48
+ OpBranch %19
+ %21 = OpLabel
+ OpStore %51 %60
+ OpSelectionMerge %105 None
+ OpBranchConditional %103 %104 %105
+ %104 = OpLabel
+ OpBranch %105
+ %105 = OpLabel
+ OpReturn
+ 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()));
+
+ FactManager fact_manager;
+
+ // Cannot insert before the OpVariables of a function.
+ ASSERT_FALSE(
+ TransformationVectorShuffle(
+ MakeInstructionDescriptor(101, SpvOpVariable, 0), 200, 14, 14, {0, 1})
+ .IsApplicable(context.get(), fact_manager));
+ ASSERT_FALSE(
+ TransformationVectorShuffle(
+ MakeInstructionDescriptor(101, SpvOpVariable, 1), 200, 14, 14, {1, 2})
+ .IsApplicable(context.get(), fact_manager));
+ ASSERT_FALSE(
+ TransformationVectorShuffle(
+ MakeInstructionDescriptor(102, SpvOpVariable, 0), 200, 14, 14, {1, 2})
+ .IsApplicable(context.get(), fact_manager));
+ // OK to insert right after the OpVariables.
+ ASSERT_FALSE(
+ TransformationVectorShuffle(
+ MakeInstructionDescriptor(102, SpvOpBranch, 1), 200, 14, 14, {1, 1})
+ .IsApplicable(context.get(), fact_manager));
+
+ // Cannot insert before the OpPhis of a block.
+ ASSERT_FALSE(
+ TransformationVectorShuffle(MakeInstructionDescriptor(60, SpvOpPhi, 0),
+ 200, 14, 14, {2, 0})
+ .IsApplicable(context.get(), fact_manager));
+ ASSERT_FALSE(
+ TransformationVectorShuffle(MakeInstructionDescriptor(59, SpvOpPhi, 0),
+ 200, 14, 14, {3, 0})
+ .IsApplicable(context.get(), fact_manager));
+ // OK to insert after the OpPhis.
+ ASSERT_TRUE(TransformationVectorShuffle(
+ MakeInstructionDescriptor(59, SpvOpAccessChain, 0), 200, 14,
+ 14, {3, 4})
+ .IsApplicable(context.get(), fact_manager));
+
+ // Cannot insert before OpLoopMerge
+ ASSERT_FALSE(TransformationVectorShuffle(
+ MakeInstructionDescriptor(33, SpvOpBranchConditional, 0),
+ 200, 14, 14, {3})
+ .IsApplicable(context.get(), fact_manager));
+
+ // Cannot insert before OpSelectionMerge
+ ASSERT_FALSE(TransformationVectorShuffle(
+ MakeInstructionDescriptor(21, SpvOpBranchConditional, 0),
+ 200, 14, 14, {2})
+ .IsApplicable(context.get(), fact_manager));
+}
+
+} // namespace
+} // namespace fuzz
+} // namespace spvtools