spirv-fuzz: Permute OpPhi instruction operands (#3421)
Fixes #3415.
diff --git a/source/fuzz/CMakeLists.txt b/source/fuzz/CMakeLists.txt
index 547256f..fd6c5c9 100644
--- a/source/fuzz/CMakeLists.txt
+++ b/source/fuzz/CMakeLists.txt
@@ -64,6 +64,7 @@
fuzzer_pass_outline_functions.h
fuzzer_pass_permute_blocks.h
fuzzer_pass_permute_function_parameters.h
+ fuzzer_pass_permute_phi_operands.h
fuzzer_pass_push_ids_through_variables.h
fuzzer_pass_replace_linear_algebra_instructions.h
fuzzer_pass_split_blocks.h
@@ -116,6 +117,7 @@
transformation_move_block_down.h
transformation_outline_function.h
transformation_permute_function_parameters.h
+ transformation_permute_phi_operands.h
transformation_push_id_through_variable.h
transformation_replace_boolean_constant_with_constant_binary.h
transformation_replace_constant_with_uniform.h
@@ -168,6 +170,7 @@
fuzzer_pass_outline_functions.cpp
fuzzer_pass_permute_blocks.cpp
fuzzer_pass_permute_function_parameters.cpp
+ fuzzer_pass_permute_phi_operands.cpp
fuzzer_pass_push_ids_through_variables.cpp
fuzzer_pass_replace_linear_algebra_instructions.cpp
fuzzer_pass_split_blocks.cpp
@@ -219,6 +222,7 @@
transformation_move_block_down.cpp
transformation_outline_function.cpp
transformation_permute_function_parameters.cpp
+ transformation_permute_phi_operands.cpp
transformation_push_id_through_variable.cpp
transformation_replace_boolean_constant_with_constant_binary.cpp
transformation_replace_constant_with_uniform.cpp
diff --git a/source/fuzz/fuzzer.cpp b/source/fuzz/fuzzer.cpp
index 3d738f7..26585c7 100644
--- a/source/fuzz/fuzzer.cpp
+++ b/source/fuzz/fuzzer.cpp
@@ -47,6 +47,7 @@
#include "source/fuzz/fuzzer_pass_outline_functions.h"
#include "source/fuzz/fuzzer_pass_permute_blocks.h"
#include "source/fuzz/fuzzer_pass_permute_function_parameters.h"
+#include "source/fuzz/fuzzer_pass_permute_phi_operands.h"
#include "source/fuzz/fuzzer_pass_push_ids_through_variables.h"
#include "source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.h"
#include "source/fuzz/fuzzer_pass_split_blocks.h"
@@ -305,6 +306,9 @@
MaybeAddPass<FuzzerPassAddNoContractionDecorations>(
&final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
transformation_sequence_out);
+ MaybeAddPass<FuzzerPassPermutePhiOperands>(
+ &final_passes, ir_context.get(), &transformation_context, &fuzzer_context,
+ transformation_sequence_out);
MaybeAddPass<FuzzerPassSwapCommutableOperands>(
&final_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 58ec531..7f10642 100644
--- a/source/fuzz/fuzzer_context.cpp
+++ b/source/fuzz/fuzzer_context.cpp
@@ -63,6 +63,7 @@
const std::pair<uint32_t, uint32_t> kChanceOfObfuscatingConstant = {10, 90};
const std::pair<uint32_t, uint32_t> kChanceOfOutliningFunction = {10, 90};
const std::pair<uint32_t, uint32_t> kChanceOfPermutingParameters = {30, 90};
+const std::pair<uint32_t, uint32_t> kChanceOfPermutingPhiOperands = {30, 90};
const std::pair<uint32_t, uint32_t> kChanceOfPushingIdThroughVariable = {5, 50};
const std::pair<uint32_t, uint32_t> kChanceOfReplacingIdWithSynonym = {10, 90};
const std::pair<uint32_t, uint32_t>
@@ -165,6 +166,8 @@
ChooseBetweenMinAndMax(kChanceOfOutliningFunction);
chance_of_permuting_parameters_ =
ChooseBetweenMinAndMax(kChanceOfPermutingParameters);
+ chance_of_permuting_phi_operands_ =
+ ChooseBetweenMinAndMax(kChanceOfPermutingPhiOperands);
chance_of_pushing_id_through_variable_ =
ChooseBetweenMinAndMax(kChanceOfPushingIdThroughVariable);
chance_of_replacing_id_with_synonym_ =
diff --git a/source/fuzz/fuzzer_context.h b/source/fuzz/fuzzer_context.h
index d73f814..f4bf1a7 100644
--- a/source/fuzz/fuzzer_context.h
+++ b/source/fuzz/fuzzer_context.h
@@ -185,6 +185,9 @@
uint32_t GetChanceOfPermutingParameters() {
return chance_of_permuting_parameters_;
}
+ uint32_t GetChanceOfPermutingPhiOperands() {
+ return chance_of_permuting_phi_operands_;
+ }
uint32_t GetChanceOfPushingIdThroughVariable() {
return chance_of_pushing_id_through_variable_;
}
@@ -290,6 +293,7 @@
uint32_t chance_of_obfuscating_constant_;
uint32_t chance_of_outlining_function_;
uint32_t chance_of_permuting_parameters_;
+ uint32_t chance_of_permuting_phi_operands_;
uint32_t chance_of_pushing_id_through_variable_;
uint32_t chance_of_replacing_id_with_synonym_;
uint32_t chance_of_replacing_linear_algebra_instructions_;
diff --git a/source/fuzz/fuzzer_pass_permute_phi_operands.cpp b/source/fuzz/fuzzer_pass_permute_phi_operands.cpp
new file mode 100644
index 0000000..c241d9d
--- /dev/null
+++ b/source/fuzz/fuzzer_pass_permute_phi_operands.cpp
@@ -0,0 +1,64 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// 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 <numeric>
+#include <vector>
+
+#include "source/fuzz/fuzzer_context.h"
+#include "source/fuzz/fuzzer_pass_permute_phi_operands.h"
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "source/fuzz/transformation_permute_phi_operands.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassPermutePhiOperands::FuzzerPassPermutePhiOperands(
+ opt::IRContext* ir_context, TransformationContext* transformation_context,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations)
+ : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+ transformations) {}
+
+FuzzerPassPermutePhiOperands::~FuzzerPassPermutePhiOperands() = default;
+
+void FuzzerPassPermutePhiOperands::Apply() {
+ ForEachInstructionWithInstructionDescriptor(
+ [this](opt::Function* /*unused*/, opt::BasicBlock* /*unused*/,
+ opt::BasicBlock::iterator inst_it,
+ const protobufs::InstructionDescriptor& /*unused*/) {
+ const auto& inst = *inst_it;
+
+ if (inst.opcode() != SpvOpPhi) {
+ return;
+ }
+
+ if (!GetFuzzerContext()->ChoosePercentage(
+ GetFuzzerContext()->GetChanceOfPermutingPhiOperands())) {
+ return;
+ }
+
+ // Create a vector of indices for each pair of operands in the |inst|.
+ // OpPhi always has an even number of operands.
+ std::vector<uint32_t> permutation(inst.NumInOperands() / 2);
+ std::iota(permutation.begin(), permutation.end(), 0);
+ GetFuzzerContext()->Shuffle(&permutation);
+
+ ApplyTransformation(TransformationPermutePhiOperands(
+ inst.result_id(), std::move(permutation)));
+ });
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/source/fuzz/fuzzer_pass_permute_phi_operands.h b/source/fuzz/fuzzer_pass_permute_phi_operands.h
new file mode 100644
index 0000000..974c2c1
--- /dev/null
+++ b/source/fuzz/fuzzer_pass_permute_phi_operands.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// 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_PERMUTE_PHI_OPERANDS_H_
+#define SOURCE_FUZZ_FUZZER_PASS_PERMUTE_PHI_OPERANDS_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Iterates over all instructions in the module and randomly decides for each
+// OpPhi instruction whether to permute its operands.
+class FuzzerPassPermutePhiOperands : public FuzzerPass {
+ public:
+ FuzzerPassPermutePhiOperands(
+ opt::IRContext* ir_context, TransformationContext* transformation_context,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations);
+
+ ~FuzzerPassPermutePhiOperands() override;
+
+ void Apply() override;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SOURCE_FUZZ_FUZZER_PASS_PERMUTE_PHI_OPERANDS_H_
diff --git a/source/fuzz/fuzzer_util.cpp b/source/fuzz/fuzzer_util.cpp
index 435ac04..d439d47 100644
--- a/source/fuzz/fuzzer_util.cpp
+++ b/source/fuzz/fuzzer_util.cpp
@@ -12,6 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#include <algorithm>
+#include <unordered_set>
+
#include "source/fuzz/fuzzer_util.h"
#include "source/opt/build_module.h"
@@ -652,6 +655,26 @@
{SPV_OPERAND_TYPE_ID, {initializer_id}}}));
}
+bool HasDuplicates(const std::vector<uint32_t>& arr) {
+ return std::unordered_set<uint32_t>(arr.begin(), arr.end()).size() !=
+ arr.size();
+}
+
+bool IsPermutationOfRange(const std::vector<uint32_t>& arr, uint32_t lo,
+ uint32_t hi) {
+ if (arr.empty()) {
+ return lo > hi;
+ }
+
+ if (HasDuplicates(arr)) {
+ return false;
+ }
+
+ auto min_max = std::minmax_element(arr.begin(), arr.end());
+ return arr.size() == hi - lo + 1 && *min_max.first == lo &&
+ *min_max.second == hi;
+}
+
} // namespace fuzzerutil
} // namespace fuzz
diff --git a/source/fuzz/fuzzer_util.h b/source/fuzz/fuzzer_util.h
index 77e390e..99cfff9 100644
--- a/source/fuzz/fuzzer_util.h
+++ b/source/fuzz/fuzzer_util.h
@@ -252,6 +252,15 @@
uint32_t type_id, uint32_t function_id,
uint32_t initializer_id);
+// Returns true if the vector |arr| has duplicates.
+bool HasDuplicates(const std::vector<uint32_t>& arr);
+
+// Checks that the given vector |arr| contains a permutation of a range
+// [lo, hi]. That being said, all elements in the range are present without
+// duplicates. If |arr| is empty, returns true iff |lo > hi|.
+bool IsPermutationOfRange(const std::vector<uint32_t>& arr, uint32_t lo,
+ uint32_t hi);
+
} // namespace fuzzerutil
} // namespace fuzz
diff --git a/source/fuzz/protobufs/spvtoolsfuzz.proto b/source/fuzz/protobufs/spvtoolsfuzz.proto
index 9ebabf7..71273e7 100644
--- a/source/fuzz/protobufs/spvtoolsfuzz.proto
+++ b/source/fuzz/protobufs/spvtoolsfuzz.proto
@@ -379,6 +379,7 @@
TransformationAddSpecConstantOp add_spec_constant_op = 48;
TransformationReplaceLinearAlgebraInstruction replace_linear_algebra_instruction = 49;
TransformationSwapConditionalBranchOperands swap_conditional_branch_operands = 50;
+ TransformationPermutePhiOperands permute_phi_operands = 51;
// Add additional option using the next available number.
}
}
@@ -1005,6 +1006,19 @@
}
+message TransformationPermutePhiOperands {
+
+ // Permutes operands of some OpPhi instruction.
+
+ // Result id of the instruction to apply the transformation to.
+ uint32 result_id = 1;
+
+ // A sequence of numbers in the range [0, n/2 - 1] where |n| is the number
+ // of operands of the OpPhi instruction with |result_id|.
+ repeated uint32 permutation = 2;
+
+}
+
message TransformationPushIdThroughVariable {
// A transformation that makes |value_synonym_id| and |value_id| to be
diff --git a/source/fuzz/transformation.cpp b/source/fuzz/transformation.cpp
index da08e88..686b46f 100644
--- a/source/fuzz/transformation.cpp
+++ b/source/fuzz/transformation.cpp
@@ -52,6 +52,7 @@
#include "source/fuzz/transformation_move_block_down.h"
#include "source/fuzz/transformation_outline_function.h"
#include "source/fuzz/transformation_permute_function_parameters.h"
+#include "source/fuzz/transformation_permute_phi_operands.h"
#include "source/fuzz/transformation_push_id_through_variable.h"
#include "source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h"
#include "source/fuzz/transformation_replace_constant_with_uniform.h"
@@ -170,6 +171,9 @@
kPermuteFunctionParameters:
return MakeUnique<TransformationPermuteFunctionParameters>(
message.permute_function_parameters());
+ case protobufs::Transformation::TransformationCase::kPermutePhiOperands:
+ return MakeUnique<TransformationPermutePhiOperands>(
+ message.permute_phi_operands());
case protobufs::Transformation::TransformationCase::kPushIdThroughVariable:
return MakeUnique<TransformationPushIdThroughVariable>(
message.push_id_through_variable());
diff --git a/source/fuzz/transformation_permute_function_parameters.cpp b/source/fuzz/transformation_permute_function_parameters.cpp
index 0f1220e..4b9561c 100644
--- a/source/fuzz/transformation_permute_function_parameters.cpp
+++ b/source/fuzz/transformation_permute_function_parameters.cpp
@@ -12,7 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include <unordered_set>
#include <vector>
#include "source/fuzz/fuzzer_util.h"
@@ -53,7 +52,8 @@
const auto* function_type = fuzzerutil::GetFunctionType(ir_context, function);
assert(function_type && "Function type is null");
- const auto& permutation = message_.permutation();
+ std::vector<uint32_t> permutation(message_.permutation().begin(),
+ message_.permutation().end());
// Don't take return type into account
auto arg_size = function_type->NumInOperands() - 1;
@@ -63,21 +63,20 @@
return false;
}
- // Check that all indices are valid
- // and unique integers from the [0, n-1] set
- std::unordered_set<uint32_t> unique_indices;
- for (auto index : permutation) {
- // We don't compare |index| with 0 since it's an unsigned integer
- if (index >= arg_size) {
- return false;
- }
+ // Check that permutation doesn't have duplicated values.
+ assert(!fuzzerutil::HasDuplicates(permutation) &&
+ "Permutation has duplicates");
- unique_indices.insert(index);
+ // Check that elements in permutation are in range [0, arg_size - 1].
+ //
+ // We must check whether the permutation is empty first because in that case
+ // |arg_size - 1| will produce |std::numeric_limits<uint32_t>::max()| since
+ // it's an unsigned integer.
+ if (!permutation.empty() &&
+ !fuzzerutil::IsPermutationOfRange(permutation, 0, arg_size - 1)) {
+ return false;
}
- // Check that permutation doesn't have duplicated values
- assert(unique_indices.size() == arg_size && "Permutation has duplicates");
-
// Check that new function's type is valid:
// - Has the same number of operands
// - Has the same result type as the old one
diff --git a/source/fuzz/transformation_permute_phi_operands.cpp b/source/fuzz/transformation_permute_phi_operands.cpp
new file mode 100644
index 0000000..95e7a1f
--- /dev/null
+++ b/source/fuzz/transformation_permute_phi_operands.cpp
@@ -0,0 +1,94 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// 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 <vector>
+
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/transformation_permute_phi_operands.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationPermutePhiOperands::TransformationPermutePhiOperands(
+ const spvtools::fuzz::protobufs::TransformationPermutePhiOperands& message)
+ : message_(message) {}
+
+TransformationPermutePhiOperands::TransformationPermutePhiOperands(
+ uint32_t result_id, const std::vector<uint32_t>& permutation) {
+ message_.set_result_id(result_id);
+
+ for (auto index : permutation) {
+ message_.add_permutation(index);
+ }
+}
+
+bool TransformationPermutePhiOperands::IsApplicable(
+ opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
+ // Check that |message_.result_id| is valid.
+ const auto* inst =
+ ir_context->get_def_use_mgr()->GetDef(message_.result_id());
+ if (!inst || inst->opcode() != SpvOpPhi) {
+ return false;
+ }
+
+ // Check that |message_.permutation| has expected size.
+ auto expected_permutation_size = inst->NumInOperands() / 2;
+ if (static_cast<uint32_t>(message_.permutation().size()) !=
+ expected_permutation_size) {
+ return false;
+ }
+
+ // Check that |message_.permutation| has elements in range
+ // [0, expected_permutation_size - 1].
+ std::vector<uint32_t> permutation(message_.permutation().begin(),
+ message_.permutation().end());
+ assert(!fuzzerutil::HasDuplicates(permutation) &&
+ "Permutation has duplicates");
+
+ // We must check whether the permutation is empty first because in that case
+ // |expected_permutation_size - 1| will produce
+ // |std::numeric_limits<uint32_t>::max()| since it's an unsigned integer.
+ return permutation.empty() ||
+ fuzzerutil::IsPermutationOfRange(permutation, 0,
+ expected_permutation_size - 1);
+}
+
+void TransformationPermutePhiOperands::Apply(
+ opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
+ auto* inst = ir_context->get_def_use_mgr()->GetDef(message_.result_id());
+ assert(inst);
+
+ opt::Instruction::OperandList permuted_operands;
+ permuted_operands.reserve(inst->NumInOperands());
+
+ for (auto index : message_.permutation()) {
+ permuted_operands.push_back(std::move(inst->GetInOperand(2 * index)));
+ permuted_operands.push_back(std::move(inst->GetInOperand(2 * index + 1)));
+ }
+
+ inst->SetInOperands(std::move(permuted_operands));
+
+ // Make sure our changes are analyzed
+ ir_context->InvalidateAnalysesExceptFor(
+ opt::IRContext::Analysis::kAnalysisNone);
+}
+
+protobufs::Transformation TransformationPermutePhiOperands::ToMessage() const {
+ protobufs::Transformation result;
+ *result.mutable_permute_phi_operands() = message_;
+ return result;
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/source/fuzz/transformation_permute_phi_operands.h b/source/fuzz/transformation_permute_phi_operands.h
new file mode 100644
index 0000000..df242e3
--- /dev/null
+++ b/source/fuzz/transformation_permute_phi_operands.h
@@ -0,0 +1,56 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// 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_PERMUTE_PHI_OPERANDS_H_
+#define SOURCE_FUZZ_TRANSFORMATION_PERMUTE_PHI_OPERANDS_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 TransformationPermutePhiOperands : public Transformation {
+ public:
+ explicit TransformationPermutePhiOperands(
+ const protobufs::TransformationPermutePhiOperands& message);
+
+ TransformationPermutePhiOperands(uint32_t result_id,
+ const std::vector<uint32_t>& permutation);
+
+ // - |result_id| must be a valid id of some OpPhi instruction in the module.
+ // - |permutation| must contain elements in the range [0, n/2 - 1] where |n|
+ // is a number of operands to the instruction with |result_id|. All elements
+ // must be unique (i.e. |permutation.size() == n / 2|).
+ bool IsApplicable(
+ opt::IRContext* ir_context,
+ const TransformationContext& transformation_context) const override;
+
+ // Permutes operands of the OpPhi instruction with |result_id| according to
+ // the elements in |permutation|.
+ void Apply(opt::IRContext* ir_context,
+ TransformationContext* transformation_context) const override;
+
+ protobufs::Transformation ToMessage() const override;
+
+ private:
+ protobufs::TransformationPermutePhiOperands message_;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SOURCE_FUZZ_TRANSFORMATION_PERMUTE_PHI_OPERANDS_H_
diff --git a/test/fuzz/CMakeLists.txt b/test/fuzz/CMakeLists.txt
index dd3c181..9a59742 100644
--- a/test/fuzz/CMakeLists.txt
+++ b/test/fuzz/CMakeLists.txt
@@ -58,6 +58,7 @@
transformation_move_block_down_test.cpp
transformation_outline_function_test.cpp
transformation_permute_function_parameters_test.cpp
+ transformation_permute_phi_operands_test.cpp
transformation_push_id_through_variable_test.cpp
transformation_replace_boolean_constant_with_constant_binary_test.cpp
transformation_replace_constant_with_uniform_test.cpp
diff --git a/test/fuzz/transformation_permute_function_parameters_test.cpp b/test/fuzz/transformation_permute_function_parameters_test.cpp
index a4a7c00..13daff6 100644
--- a/test/fuzz/transformation_permute_function_parameters_test.cpp
+++ b/test/fuzz/transformation_permute_function_parameters_test.cpp
@@ -220,10 +220,17 @@
ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 0, {0, 1})
.IsApplicable(context.get(), transformation_context));
- // Permutation has invalid values
+ // Permutation has invalid values 1
ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 0, {3, 1, 0})
.IsApplicable(context.get(), transformation_context));
+#ifndef NDEBUG
+ // Permutation has invalid values 2
+ ASSERT_DEATH(TransformationPermuteFunctionParameters(22, 0, {2, 2, 1})
+ .IsApplicable(context.get(), transformation_context),
+ "Permutation has duplicates");
+#endif
+
// Type id is not an OpTypeFunction instruction
ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 42, {2, 1, 0})
.IsApplicable(context.get(), transformation_context));
diff --git a/test/fuzz/transformation_permute_phi_operands_test.cpp b/test/fuzz/transformation_permute_phi_operands_test.cpp
new file mode 100644
index 0000000..c0a428a
--- /dev/null
+++ b/test/fuzz/transformation_permute_phi_operands_test.cpp
@@ -0,0 +1,154 @@
+// Copyright (c) 2020 Vasyl Teliman
+//
+// 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_permute_phi_operands.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationPermutePhiOperandsTest, 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 = OpTypeInt 32 1
+ %7 = OpTypePointer Function %6
+ %9 = OpConstant %6 0
+ %11 = OpConstant %6 1
+ %14 = OpTypeBool
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %8 = OpVariable %7 Function
+ %10 = OpVariable %7 Function
+ OpStore %8 %9
+ OpStore %10 %11
+ %12 = OpLoad %6 %8
+ %13 = OpLoad %6 %10
+ %15 = OpSLessThan %14 %12 %13
+ OpSelectionMerge %17 None
+ OpBranchConditional %15 %16 %21
+ %16 = OpLabel
+ %18 = OpLoad %6 %10
+ %19 = OpLoad %6 %8
+ %20 = OpIAdd %6 %19 %18
+ OpBranch %17
+ %21 = OpLabel
+ %22 = OpLoad %6 %10
+ %23 = OpLoad %6 %8
+ %24 = OpISub %6 %23 %22
+ OpBranch %17
+ %17 = OpLabel
+ %25 = OpPhi %6 %20 %16 %24 %21
+ OpStore %8 %25
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ 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);
+
+ // Result id is invalid.
+ ASSERT_FALSE(TransformationPermutePhiOperands(26, {}).IsApplicable(
+ context.get(), transformation_context));
+
+ // Result id is not of an OpPhi instruction.
+ ASSERT_FALSE(TransformationPermutePhiOperands(24, {}).IsApplicable(
+ context.get(), transformation_context));
+
+ // Result id is not of an OpPhi instruction.
+ ASSERT_FALSE(TransformationPermutePhiOperands(24, {}).IsApplicable(
+ context.get(), transformation_context));
+
+ // Permutation has invalid size.
+ ASSERT_FALSE(TransformationPermutePhiOperands(25, {0, 1, 2})
+ .IsApplicable(context.get(), transformation_context));
+
+#ifndef NDEBUG
+ // Permutation has duplicates.
+ ASSERT_DEATH(TransformationPermutePhiOperands(25, {0, 0})
+ .IsApplicable(context.get(), transformation_context),
+ "Permutation has duplicates");
+#endif
+
+ // Permutation's values are not in range.
+ ASSERT_FALSE(TransformationPermutePhiOperands(25, {1, 2})
+ .IsApplicable(context.get(), transformation_context));
+
+ TransformationPermutePhiOperands transformation(25, {1, 0});
+ ASSERT_TRUE(
+ transformation.IsApplicable(context.get(), transformation_context));
+ transformation.Apply(context.get(), &transformation_context);
+
+ 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 = OpTypeInt 32 1
+ %7 = OpTypePointer Function %6
+ %9 = OpConstant %6 0
+ %11 = OpConstant %6 1
+ %14 = OpTypeBool
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %8 = OpVariable %7 Function
+ %10 = OpVariable %7 Function
+ OpStore %8 %9
+ OpStore %10 %11
+ %12 = OpLoad %6 %8
+ %13 = OpLoad %6 %10
+ %15 = OpSLessThan %14 %12 %13
+ OpSelectionMerge %17 None
+ OpBranchConditional %15 %16 %21
+ %16 = OpLabel
+ %18 = OpLoad %6 %10
+ %19 = OpLoad %6 %8
+ %20 = OpIAdd %6 %19 %18
+ OpBranch %17
+ %21 = OpLabel
+ %22 = OpLoad %6 %10
+ %23 = OpLoad %6 %8
+ %24 = OpISub %6 %23 %22
+ OpBranch %17
+ %17 = OpLabel
+ %25 = OpPhi %6 %24 %21 %20 %16
+ OpStore %8 %25
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+} // namespace
+} // namespace fuzz
+} // namespace spvtools