spirv-fuzz: Swap positions of two functions in a module (#4236)
Adds a new transformation class that swaps the syntactic position of
two functions in the module, and a fuzzer pass to apply it.
Fixes #4026.
diff --git a/source/fuzz/CMakeLists.txt b/source/fuzz/CMakeLists.txt
index 5601a0d..a806166 100644
--- a/source/fuzz/CMakeLists.txt
+++ b/source/fuzz/CMakeLists.txt
@@ -121,6 +121,7 @@
fuzzer_pass_split_blocks.h
fuzzer_pass_swap_commutable_operands.h
fuzzer_pass_swap_conditional_branch_operands.h
+ fuzzer_pass_swap_functions.h
fuzzer_pass_toggle_access_chain_instruction.h
fuzzer_pass_wrap_regions_in_selections.h
fuzzer_util.h
@@ -224,6 +225,7 @@
transformation_swap_commutable_operands.h
transformation_swap_conditional_branch_operands.h
transformation_swap_function_variables.h
+ transformation_swap_two_functions.h
transformation_toggle_access_chain_instruction.h
transformation_vector_shuffle.h
transformation_wrap_early_terminator_in_function.h
@@ -314,6 +316,7 @@
fuzzer_pass_split_blocks.cpp
fuzzer_pass_swap_commutable_operands.cpp
fuzzer_pass_swap_conditional_branch_operands.cpp
+ fuzzer_pass_swap_functions.cpp
fuzzer_pass_toggle_access_chain_instruction.cpp
fuzzer_pass_wrap_regions_in_selections.cpp
fuzzer_util.cpp
@@ -415,6 +418,7 @@
transformation_swap_commutable_operands.cpp
transformation_swap_conditional_branch_operands.cpp
transformation_swap_function_variables.cpp
+ transformation_swap_two_functions.cpp
transformation_toggle_access_chain_instruction.cpp
transformation_vector_shuffle.cpp
transformation_wrap_early_terminator_in_function.cpp
diff --git a/source/fuzz/fuzzer.cpp b/source/fuzz/fuzzer.cpp
index 45cfac4..fe88a55 100644
--- a/source/fuzz/fuzzer.cpp
+++ b/source/fuzz/fuzzer.cpp
@@ -87,6 +87,7 @@
#include "source/fuzz/fuzzer_pass_split_blocks.h"
#include "source/fuzz/fuzzer_pass_swap_commutable_operands.h"
#include "source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h"
+#include "source/fuzz/fuzzer_pass_swap_functions.h"
#include "source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h"
#include "source/fuzz/fuzzer_pass_wrap_regions_in_selections.h"
#include "source/fuzz/pass_management/repeated_pass_manager.h"
@@ -240,6 +241,7 @@
MaybeAddFinalPass<FuzzerPassPermuteFunctionVariables>(&final_passes_);
MaybeAddFinalPass<FuzzerPassPermutePhiOperands>(&final_passes_);
MaybeAddFinalPass<FuzzerPassSwapCommutableOperands>(&final_passes_);
+ MaybeAddFinalPass<FuzzerPassSwapFunctions>(&final_passes_);
MaybeAddFinalPass<FuzzerPassToggleAccessChainInstruction>(&final_passes_);
}
diff --git a/source/fuzz/fuzzer_context.cpp b/source/fuzz/fuzzer_context.cpp
index 47da794..9e9650f 100644
--- a/source/fuzz/fuzzer_context.cpp
+++ b/source/fuzz/fuzzer_context.cpp
@@ -153,10 +153,11 @@
const std::pair<uint32_t, uint32_t> kChanceOfReplacingParametersWithStruct = {
20, 40};
const std::pair<uint32_t, uint32_t> kChanceOfSplittingBlock = {40, 95};
-const std::pair<uint32_t, uint32_t> kChanceOfSwappingConditionalBranchOperands =
- {10, 70};
const std::pair<uint32_t, uint32_t>
kChanceOfSwappingAnotherPairOfFunctionVariables = {30, 90};
+const std::pair<uint32_t, uint32_t> kChanceOfSwappingConditionalBranchOperands =
+ {10, 70};
+const std::pair<uint32_t, uint32_t> kChanceOfSwappingFunctions = {10, 90};
const std::pair<uint32_t, uint32_t> kChanceOfTogglingAccessChainInstruction = {
20, 90};
const std::pair<uint32_t, uint32_t> kChanceOfWrappingRegionInSelection = {70,
@@ -362,6 +363,8 @@
ChooseBetweenMinAndMax(kChanceOfSwappingAnotherPairOfFunctionVariables);
chance_of_swapping_conditional_branch_operands_ =
ChooseBetweenMinAndMax(kChanceOfSwappingConditionalBranchOperands);
+ chance_of_swapping_functions_ =
+ ChooseBetweenMinAndMax(kChanceOfSwappingFunctions);
chance_of_toggling_access_chain_instruction_ =
ChooseBetweenMinAndMax(kChanceOfTogglingAccessChainInstruction);
chance_of_wrapping_region_in_selection_ =
diff --git a/source/fuzz/fuzzer_context.h b/source/fuzz/fuzzer_context.h
index db2a074..ef2cc2c 100644
--- a/source/fuzz/fuzzer_context.h
+++ b/source/fuzz/fuzzer_context.h
@@ -369,6 +369,11 @@
uint32_t GetChanceOfSwappingConditionalBranchOperands() const {
return chance_of_swapping_conditional_branch_operands_;
}
+
+ uint32_t GetChanceOfSwappingFunctions() const {
+ return chance_of_swapping_functions_;
+ }
+
uint32_t GetChanceOfTogglingAccessChainInstruction() const {
return chance_of_toggling_access_chain_instruction_;
}
@@ -554,6 +559,7 @@
uint32_t chance_of_splitting_block_;
uint32_t chance_of_swapping_another_pair_of_function_variables_;
uint32_t chance_of_swapping_conditional_branch_operands_;
+ uint32_t chance_of_swapping_functions_;
uint32_t chance_of_toggling_access_chain_instruction_;
uint32_t chance_of_wrapping_region_in_selection_;
diff --git a/source/fuzz/fuzzer_pass_swap_functions.cpp b/source/fuzz/fuzzer_pass_swap_functions.cpp
new file mode 100644
index 0000000..ba48c67
--- /dev/null
+++ b/source/fuzz/fuzzer_pass_swap_functions.cpp
@@ -0,0 +1,53 @@
+// Copyright (c) 2021 Shiyu Liu
+//
+// 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_swap_functions.h"
+
+#include "source/fuzz/fuzzer_context.h"
+#include "source/fuzz/transformation_swap_two_functions.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassSwapFunctions::FuzzerPassSwapFunctions(
+ opt::IRContext* ir_context, TransformationContext* transformation_context,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations)
+ : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+ transformations) {}
+
+void FuzzerPassSwapFunctions::Apply() {
+ // Collect all function ids in a module.
+ std::vector<uint32_t> function_ids;
+ for (auto& function : *GetIRContext()->module()) {
+ function_ids.emplace_back(function.result_id());
+ }
+
+ size_t id_size = function_ids.size();
+ // Iterate through every combination of id i & j where i!=j.
+ for (size_t i = 0; i < id_size - 1; ++i) {
+ for (size_t j = i + 1; j < id_size; ++j) {
+ // Perform function swap randomly.
+ if (!GetFuzzerContext()->ChoosePercentage(
+ GetFuzzerContext()->GetChanceOfSwappingFunctions())) {
+ continue;
+ }
+ TransformationSwapTwoFunctions transformation(function_ids[i],
+ function_ids[j]);
+ }
+ }
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/source/fuzz/fuzzer_pass_swap_functions.h b/source/fuzz/fuzzer_pass_swap_functions.h
new file mode 100644
index 0000000..ac551f6
--- /dev/null
+++ b/source/fuzz/fuzzer_pass_swap_functions.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2021 Shiyu Liu
+//
+// 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_SWAP_FUNCTIONS_H_
+#define SOURCE_FUZZ_FUZZER_PASS_SWAP_FUNCTIONS_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Randomly swap functions within a module.
+class FuzzerPassSwapFunctions : public FuzzerPass {
+ public:
+ FuzzerPassSwapFunctions(opt::IRContext* ir_context,
+ TransformationContext* transformation_context,
+ FuzzerContext* fuzzer_context,
+ protobufs::TransformationSequence* transformations);
+
+ void Apply() override;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SOURCE_FUZZ_FUZZER_PASS_SWAP_FUNCTIONS_H_
diff --git a/source/fuzz/fuzzer_util.cpp b/source/fuzz/fuzzer_util.cpp
index aa0d30e..900619a 100644
--- a/source/fuzz/fuzzer_util.cpp
+++ b/source/fuzz/fuzzer_util.cpp
@@ -1925,6 +1925,15 @@
return true;
}
+opt::Module::iterator GetFunctionIterator(opt::IRContext* ir_context,
+ uint32_t function_id) {
+ for (auto iter = ir_context->module()->begin();
+ iter != ir_context->module()->end(); ++iter) {
+ if ((*iter).result_id() == function_id) return iter;
+ }
+ return ir_context->module()->end();
+}
+
} // namespace fuzzerutil
} // namespace fuzz
} // namespace spvtools
diff --git a/source/fuzz/fuzzer_util.h b/source/fuzz/fuzzer_util.h
index fcbf1c6..dd7bd96 100644
--- a/source/fuzz/fuzzer_util.h
+++ b/source/fuzz/fuzzer_util.h
@@ -24,6 +24,7 @@
#include "source/opt/basic_block.h"
#include "source/opt/instruction.h"
#include "source/opt/ir_context.h"
+#include "source/opt/module.h"
#include "spirv-tools/libspirv.hpp"
namespace spvtools {
@@ -596,6 +597,12 @@
uint32_t block_id,
opt::Instruction new_terminator);
+// Return the iterator that points to the function with the corresponding
+// function id. If the function is not found, return the pointer pointing to
+// module()->end().
+opt::Module::iterator GetFunctionIterator(opt::IRContext* ir_context,
+ uint32_t function_id);
+
} // namespace fuzzerutil
} // namespace fuzz
} // namespace spvtools
diff --git a/source/fuzz/protobufs/spvtoolsfuzz.proto b/source/fuzz/protobufs/spvtoolsfuzz.proto
index c2879fb..2ca03db 100644
--- a/source/fuzz/protobufs/spvtoolsfuzz.proto
+++ b/source/fuzz/protobufs/spvtoolsfuzz.proto
@@ -558,6 +558,7 @@
TransformationMergeFunctionReturns merge_function_returns = 84;
TransformationExpandVectorReduction expand_vector_reduction = 85;
TransformationSwapFunctionVariables swap_function_variables = 86;
+ TransformationSwapTwoFunctions swap_two_functions = 87;
// Add additional option using the next available number.
}
}
@@ -2268,6 +2269,15 @@
uint32 result_id1 = 1;
// Result id of the second variable.
uint32 result_id2 = 2;
+
+}
+
+message TransformationSwapTwoFunctions {
+ // A transformation that swaps the position of two functions within the same module.
+
+ // the IDs for the two functions that are swapped.
+ uint32 function_id1 = 1;
+ uint32 function_id2 = 2;
}
message TransformationToggleAccessChainInstruction {
diff --git a/source/fuzz/transformation.cpp b/source/fuzz/transformation.cpp
index c27b582..4ea0c77 100644
--- a/source/fuzz/transformation.cpp
+++ b/source/fuzz/transformation.cpp
@@ -99,6 +99,7 @@
#include "source/fuzz/transformation_swap_commutable_operands.h"
#include "source/fuzz/transformation_swap_conditional_branch_operands.h"
#include "source/fuzz/transformation_swap_function_variables.h"
+#include "source/fuzz/transformation_swap_two_functions.h"
#include "source/fuzz/transformation_toggle_access_chain_instruction.h"
#include "source/fuzz/transformation_vector_shuffle.h"
#include "source/fuzz/transformation_wrap_early_terminator_in_function.h"
@@ -365,6 +366,9 @@
case protobufs::Transformation::TransformationCase::kSwapFunctionVariables:
return MakeUnique<TransformationSwapFunctionVariables>(
message.swap_function_variables());
+ case protobufs::Transformation::TransformationCase::kSwapTwoFunctions:
+ return MakeUnique<TransformationSwapTwoFunctions>(
+ message.swap_two_functions());
case protobufs::Transformation::TransformationCase::
kToggleAccessChainInstruction:
return MakeUnique<TransformationToggleAccessChainInstruction>(
diff --git a/source/fuzz/transformation_swap_two_functions.cpp b/source/fuzz/transformation_swap_two_functions.cpp
new file mode 100644
index 0000000..85d9e79
--- /dev/null
+++ b/source/fuzz/transformation_swap_two_functions.cpp
@@ -0,0 +1,72 @@
+// Copyright (c) 2021 Shiyu Liu
+//
+// 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_swap_two_functions.h"
+
+#include "source/opt/function.h"
+#include "source/opt/module.h"
+
+#include "source/fuzz/fuzzer_util.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationSwapTwoFunctions::TransformationSwapTwoFunctions(
+ protobufs::TransformationSwapTwoFunctions message)
+ : message_(std::move(message)) {}
+
+TransformationSwapTwoFunctions::TransformationSwapTwoFunctions(uint32_t id1,
+ uint32_t id2) {
+ assert(id1 != id2 && "The two function ids cannot be the same.");
+ message_.set_function_id1(id1);
+ message_.set_function_id2(id2);
+}
+
+bool TransformationSwapTwoFunctions::IsApplicable(
+ opt::IRContext* ir_context, const TransformationContext& /*unused*/) const {
+ auto func1_ptr = ir_context->GetFunction(message_.function_id1());
+ auto func2_ptr = ir_context->GetFunction(message_.function_id2());
+ return func1_ptr != nullptr && func2_ptr != nullptr;
+}
+
+void TransformationSwapTwoFunctions::Apply(
+ opt::IRContext* ir_context, TransformationContext* /*unused*/) const {
+ opt::Module::iterator func1_it =
+ fuzzerutil::GetFunctionIterator(ir_context, message_.function_id1());
+ opt::Module::iterator func2_it =
+ fuzzerutil::GetFunctionIterator(ir_context, message_.function_id2());
+
+ assert(func1_it != ir_context->module()->end() &&
+ "Could not find function 1.");
+ assert(func2_it != ir_context->module()->end() &&
+ "Could not find function 2.");
+
+ // Two function pointers are all set, swap the two functions within the
+ // module.
+ std::iter_swap(func1_it.Get(), func2_it.Get());
+}
+
+protobufs::Transformation TransformationSwapTwoFunctions::ToMessage() const {
+ protobufs::Transformation result;
+ *result.mutable_swap_two_functions() = message_;
+ return result;
+}
+
+std::unordered_set<uint32_t> TransformationSwapTwoFunctions::GetFreshIds()
+ const {
+ return std::unordered_set<uint32_t>();
+}
+
+} // namespace fuzz
+} // namespace spvtools
diff --git a/source/fuzz/transformation_swap_two_functions.h b/source/fuzz/transformation_swap_two_functions.h
new file mode 100644
index 0000000..1b6d6e8
--- /dev/null
+++ b/source/fuzz/transformation_swap_two_functions.h
@@ -0,0 +1,56 @@
+// Copyright (c) 2021 Shiyu Liu
+//
+// 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_SWAP_TWO_FUNCTIONS_H_
+#define SOURCE_FUZZ_TRANSFORMATION_SWAP_TWO_FUNCTIONS_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 TransformationSwapTwoFunctions : public Transformation {
+ public:
+ explicit TransformationSwapTwoFunctions(
+ protobufs::TransformationSwapTwoFunctions message);
+
+ TransformationSwapTwoFunctions(uint32_t function_id1, uint32_t function_id2);
+
+ // |function_id1| and |function_id1| should all be existing ids.
+ // Swap function operation is only permitted if:
+ // - both ids must be ids of functions.
+ // - both ids can be found in the module.
+ // - function_id1 and function_id2 are not the same.
+ bool IsApplicable(
+ opt::IRContext* ir_context,
+ const TransformationContext& transformation_context) const override;
+
+ // OpFunction with |function_id1| and |function_id1| are swapped.
+ void Apply(opt::IRContext* ir_context,
+ TransformationContext* transformation_context) const override;
+
+ std::unordered_set<uint32_t> GetFreshIds() const override;
+ protobufs::Transformation ToMessage() const override;
+
+ private:
+ protobufs::TransformationSwapTwoFunctions message_;
+};
+
+} // namespace fuzz
+} // namespace spvtools
+
+#endif // SOURCE_FUZZ_TRANSFORMATION_SWAP_TWO_FUNCTIONS_H_
diff --git a/test/fuzz/CMakeLists.txt b/test/fuzz/CMakeLists.txt
index 4353689..a2213b8 100644
--- a/test/fuzz/CMakeLists.txt
+++ b/test/fuzz/CMakeLists.txt
@@ -115,6 +115,7 @@
transformation_swap_commutable_operands_test.cpp
transformation_swap_conditional_branch_operands_test.cpp
transformation_swap_function_variables_test.cpp
+ transformation_swap_two_functions_test.cpp
transformation_toggle_access_chain_instruction_test.cpp
transformation_record_synonymous_constants_test.cpp
transformation_vector_shuffle_test.cpp
diff --git a/test/fuzz/transformation_swap_two_functions_test.cpp b/test/fuzz/transformation_swap_two_functions_test.cpp
new file mode 100644
index 0000000..38d6a07
--- /dev/null
+++ b/test/fuzz/transformation_swap_two_functions_test.cpp
@@ -0,0 +1,244 @@
+// Copyright (c) 2021 Shiyu Liu
+//
+// 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_swap_two_functions.h"
+
+#include "gtest/gtest.h"
+#include "source/fuzz/fuzzer_util.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationSwapTwoFunctionsTest, SimpleTest) {
+ // float multiplyBy2(in float value) {
+ // return value*2.0;
+ // }
+
+ // float multiplyBy4(in float value) {
+ // return multiplyBy2(value)*2.0;
+ // }
+
+ // float multiplyBy8(in float value) {
+ // return multiplyBy2(value)*multiplyBy4(value);
+ // }
+
+ // layout(location=0) in float value;
+ // void main() { //4
+ // multiplyBy2(3.7); //10
+ // multiplyBy4(3.9); //13
+ // multiplyBy8(5.0); //16
+ // }
+
+ std::string shader = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %48
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %10 "multiplyBy2(f1;"
+ OpName %9 "value"
+ OpName %13 "multiplyBy4(f1;"
+ OpName %12 "value"
+ OpName %16 "multiplyBy8(f1;"
+ OpName %15 "value"
+ OpName %23 "param"
+ OpName %29 "param"
+ OpName %32 "param"
+ OpName %39 "param"
+ OpName %42 "param"
+ OpName %45 "param"
+ OpName %48 "value"
+ OpDecorate %48 Location 0
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypePointer Function %6
+ %8 = OpTypeFunction %6 %7
+ %19 = OpConstant %6 2
+ %38 = OpConstant %6 3.70000005
+ %41 = OpConstant %6 3.9000001
+ %44 = OpConstant %6 5
+ %47 = OpTypePointer Input %6
+ %48 = OpVariable %47 Input
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %39 = OpVariable %7 Function
+ %42 = OpVariable %7 Function
+ %45 = OpVariable %7 Function
+ OpStore %39 %38
+ %40 = OpFunctionCall %6 %10 %39
+ OpStore %42 %41
+ %43 = OpFunctionCall %6 %13 %42
+ OpStore %45 %44
+ %46 = OpFunctionCall %6 %16 %45
+ OpReturn
+ OpFunctionEnd
+ %10 = OpFunction %6 None %8
+ %9 = OpFunctionParameter %7
+ %11 = OpLabel
+ %18 = OpLoad %6 %9
+ %20 = OpFMul %6 %18 %19
+ OpReturnValue %20
+ OpFunctionEnd
+ %13 = OpFunction %6 None %8
+ %12 = OpFunctionParameter %7
+ %14 = OpLabel
+ %23 = OpVariable %7 Function
+ %24 = OpLoad %6 %12
+ OpStore %23 %24
+ %25 = OpFunctionCall %6 %10 %23
+ %26 = OpFMul %6 %25 %19
+ OpReturnValue %26
+ OpFunctionEnd
+ %16 = OpFunction %6 None %8
+ %15 = OpFunctionParameter %7
+ %17 = OpLabel
+ %29 = OpVariable %7 Function
+ %32 = OpVariable %7 Function
+ %30 = OpLoad %6 %15
+ OpStore %29 %30
+ %31 = OpFunctionCall %6 %10 %29
+ %33 = OpLoad %6 %15
+ OpStore %32 %33
+ %34 = OpFunctionCall %6 %13 %32
+ %35 = OpFMul %6 %31 %34
+ OpReturnValue %35
+ OpFunctionEnd
+ )";
+ const auto env = SPV_ENV_UNIVERSAL_1_3;
+ const auto consumer = nullptr;
+ const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
+ spvtools::ValidatorOptions validator_options;
+
+ // Check context validity.
+ ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
+ kConsoleMessageConsumer));
+
+ TransformationContext transformation_context(
+ MakeUnique<FactManager>(context.get()), validator_options);
+
+#ifndef NDEBUG
+ // Function should not swap with itself.
+ ASSERT_DEATH(TransformationSwapTwoFunctions(4, 4).IsApplicable(
+ context.get(), transformation_context),
+ "The two function ids cannot be the same.");
+#endif
+
+ // Function with id 29 does not exist.
+ ASSERT_FALSE(TransformationSwapTwoFunctions(10, 29).IsApplicable(
+ context.get(), transformation_context));
+
+ // Function with id 30 does not exist.
+ ASSERT_FALSE(TransformationSwapTwoFunctions(30, 13).IsApplicable(
+ context.get(), transformation_context));
+
+ // Both functions with id 5 and 6 do not exist.
+ ASSERT_FALSE(TransformationSwapTwoFunctions(5, 6).IsApplicable(
+ context.get(), transformation_context));
+
+ // Function with result_id 10 and 13 should swap successfully.
+ auto swap_test5 = TransformationSwapTwoFunctions(10, 13);
+ ASSERT_TRUE(swap_test5.IsApplicable(context.get(), transformation_context));
+ ApplyAndCheckFreshIds(swap_test5, context.get(), &transformation_context);
+ ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
+ kConsoleMessageConsumer));
+
+ std::string after_transformation = R"(
+ OpCapability Shader
+ %1 = OpExtInstImport "GLSL.std.450"
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint Fragment %4 "main" %48
+ OpExecutionMode %4 OriginUpperLeft
+ OpSource ESSL 310
+ OpName %4 "main"
+ OpName %10 "multiplyBy2(f1;"
+ OpName %9 "value"
+ OpName %13 "multiplyBy4(f1;"
+ OpName %12 "value"
+ OpName %16 "multiplyBy8(f1;"
+ OpName %15 "value"
+ OpName %23 "param"
+ OpName %29 "param"
+ OpName %32 "param"
+ OpName %39 "param"
+ OpName %42 "param"
+ OpName %45 "param"
+ OpName %48 "value"
+ OpDecorate %48 Location 0
+ %2 = OpTypeVoid
+ %3 = OpTypeFunction %2
+ %6 = OpTypeFloat 32
+ %7 = OpTypePointer Function %6
+ %8 = OpTypeFunction %6 %7
+ %19 = OpConstant %6 2
+ %38 = OpConstant %6 3.70000005
+ %41 = OpConstant %6 3.9000001
+ %44 = OpConstant %6 5
+ %47 = OpTypePointer Input %6
+ %48 = OpVariable %47 Input
+ %4 = OpFunction %2 None %3
+ %5 = OpLabel
+ %39 = OpVariable %7 Function
+ %42 = OpVariable %7 Function
+ %45 = OpVariable %7 Function
+ OpStore %39 %38
+ %40 = OpFunctionCall %6 %10 %39
+ OpStore %42 %41
+ %43 = OpFunctionCall %6 %13 %42
+ OpStore %45 %44
+ %46 = OpFunctionCall %6 %16 %45
+ OpReturn
+ OpFunctionEnd
+ %13 = OpFunction %6 None %8
+ %12 = OpFunctionParameter %7
+ %14 = OpLabel
+ %23 = OpVariable %7 Function
+ %24 = OpLoad %6 %12
+ OpStore %23 %24
+ %25 = OpFunctionCall %6 %10 %23
+ %26 = OpFMul %6 %25 %19
+ OpReturnValue %26
+ OpFunctionEnd
+ %10 = OpFunction %6 None %8
+ %9 = OpFunctionParameter %7
+ %11 = OpLabel
+ %18 = OpLoad %6 %9
+ %20 = OpFMul %6 %18 %19
+ OpReturnValue %20
+ OpFunctionEnd
+ %16 = OpFunction %6 None %8
+ %15 = OpFunctionParameter %7
+ %17 = OpLabel
+ %29 = OpVariable %7 Function
+ %32 = OpVariable %7 Function
+ %30 = OpLoad %6 %15
+ OpStore %29 %30
+ %31 = OpFunctionCall %6 %10 %29
+ %33 = OpLoad %6 %15
+ OpStore %32 %33
+ %34 = OpFunctionCall %6 %13 %32
+ %35 = OpFMul %6 %31 %34
+ OpReturnValue %35
+ OpFunctionEnd
+ )";
+ ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+} // namespace
+} // namespace fuzz
+} // namespace spvtools
diff --git a/utils/check_copyright.py b/utils/check_copyright.py
index a24d8f3..c525123 100755
--- a/utils/check_copyright.py
+++ b/utils/check_copyright.py
@@ -38,7 +38,8 @@
'Advanced Micro Devices, Inc.',
'Stefano Milizia',
'Alastair F. Donaldson',
- 'Mostafa Ashraf']
+ 'Mostafa Ashraf',
+ 'Shiyu Liu']
CURRENT_YEAR='2021'
YEARS = '(2014-2016|2015-2016|2015-2020|2016|2016-2017|2017|2017-2019|2018|2019|2020|2021)'