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)'