spirv-fuzz: TransformationWrapVectorSynonym that rewrites scalar operations using vectors (#4376)

Adds a new transformation that rewrites a scalar operation (like
OpFAdd, opISub) as an equivalent vector operation, adding a synonym
between the scalar result and an appropriate component of the vector
result.

Fixes #4195.
diff --git a/BUILD.gn b/BUILD.gn
index 270814f..791d7a3 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -1062,6 +1062,8 @@
       "source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h",
       "source/fuzz/fuzzer_pass_wrap_regions_in_selections.cpp",
       "source/fuzz/fuzzer_pass_wrap_regions_in_selections.h",
+      "source/fuzz/fuzzer_pass_wrap_vector_synonym.cpp",
+      "source/fuzz/fuzzer_pass_wrap_vector_synonym.h",
       "source/fuzz/fuzzer_util.cpp",
       "source/fuzz/fuzzer_util.h",
       "source/fuzz/id_use_descriptor.cpp",
@@ -1272,6 +1274,8 @@
       "source/fuzz/transformation_wrap_early_terminator_in_function.h",
       "source/fuzz/transformation_wrap_region_in_selection.cpp",
       "source/fuzz/transformation_wrap_region_in_selection.h",
+      "source/fuzz/transformation_wrap_vector_synonym.cpp",
+      "source/fuzz/transformation_wrap_vector_synonym.h",
       "source/fuzz/uniform_buffer_element_descriptor.cpp",
       "source/fuzz/uniform_buffer_element_descriptor.h",
     ]
diff --git a/source/fuzz/CMakeLists.txt b/source/fuzz/CMakeLists.txt
index a806166..dd674dd 100644
--- a/source/fuzz/CMakeLists.txt
+++ b/source/fuzz/CMakeLists.txt
@@ -124,6 +124,7 @@
         fuzzer_pass_swap_functions.h
         fuzzer_pass_toggle_access_chain_instruction.h
         fuzzer_pass_wrap_regions_in_selections.h
+        fuzzer_pass_wrap_vector_synonym.h
         fuzzer_util.h
         id_use_descriptor.h
         instruction_descriptor.h
@@ -230,6 +231,7 @@
         transformation_vector_shuffle.h
         transformation_wrap_early_terminator_in_function.h
         transformation_wrap_region_in_selection.h
+        transformation_wrap_vector_synonym.h
         uniform_buffer_element_descriptor.h
         ${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.h
 
@@ -319,6 +321,7 @@
         fuzzer_pass_swap_functions.cpp
         fuzzer_pass_toggle_access_chain_instruction.cpp
         fuzzer_pass_wrap_regions_in_selections.cpp
+        fuzzer_pass_wrap_vector_synonym.cpp
         fuzzer_util.cpp
         id_use_descriptor.cpp
         instruction_descriptor.cpp
@@ -423,6 +426,7 @@
         transformation_vector_shuffle.cpp
         transformation_wrap_early_terminator_in_function.cpp
         transformation_wrap_region_in_selection.cpp
+        transformation_wrap_vector_synonym.cpp
         uniform_buffer_element_descriptor.cpp
         ${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.cc
         )
diff --git a/source/fuzz/fuzzer.cpp b/source/fuzz/fuzzer.cpp
index fe88a55..61c4ddb 100644
--- a/source/fuzz/fuzzer.cpp
+++ b/source/fuzz/fuzzer.cpp
@@ -90,6 +90,7 @@
 #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/fuzzer_pass_wrap_vector_synonym.h"
 #include "source/fuzz/pass_management/repeated_pass_manager.h"
 #include "source/fuzz/pass_management/repeated_pass_recommender_standard.h"
 #include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
@@ -214,6 +215,7 @@
     MaybeAddRepeatedPass<FuzzerPassSwapBranchConditionalOperands>(
         &pass_instances_);
     MaybeAddRepeatedPass<FuzzerPassWrapRegionsInSelections>(&pass_instances_);
+    MaybeAddRepeatedPass<FuzzerPassWrapVectorSynonym>(&pass_instances_);
     // There is a theoretical possibility that no pass instances were created
     // until now; loop again if so.
   } while (pass_instances_.GetPasses().empty());
diff --git a/source/fuzz/fuzzer_context.cpp b/source/fuzz/fuzzer_context.cpp
index 9e9650f..eff702a 100644
--- a/source/fuzz/fuzzer_context.cpp
+++ b/source/fuzz/fuzzer_context.cpp
@@ -162,6 +162,7 @@
     20, 90};
 const std::pair<uint32_t, uint32_t> kChanceOfWrappingRegionInSelection = {70,
                                                                           90};
+const std::pair<uint32_t, uint32_t> kChanceOfWrappingVectorSynonym = {10, 90};
 
 // Default limits for various quantities that are chosen during fuzzing.
 // Keep them in alphabetical order.
@@ -369,6 +370,8 @@
       ChooseBetweenMinAndMax(kChanceOfTogglingAccessChainInstruction);
   chance_of_wrapping_region_in_selection_ =
       ChooseBetweenMinAndMax(kChanceOfWrappingRegionInSelection);
+  chance_of_wrapping_vector_synonym_ =
+      ChooseBetweenMinAndMax(kChanceOfWrappingVectorSynonym);
 }
 
 FuzzerContext::~FuzzerContext() = default;
diff --git a/source/fuzz/fuzzer_context.h b/source/fuzz/fuzzer_context.h
index ef2cc2c..b3f8cc3 100644
--- a/source/fuzz/fuzzer_context.h
+++ b/source/fuzz/fuzzer_context.h
@@ -381,6 +381,10 @@
     return chance_of_wrapping_region_in_selection_;
   }
 
+  uint32_t GetChanceOfWrappingVectorSynonym() const {
+    return chance_of_wrapping_vector_synonym_;
+  }
+
   // Other functions to control transformations. Keep them in alphabetical
   // order.
   uint32_t GetMaximumEquivalenceClassSizeForDataSynonymFactClosure() const {
@@ -425,6 +429,9 @@
   uint32_t GetRandomIndexForCompositeInsert(uint32_t number_of_components) {
     return random_generator_->RandomUint32(number_of_components);
   }
+  uint32_t GetRandomIndexForWrappingVector(uint32_t vector_width) {
+    return random_generator_->RandomUint32(vector_width);
+  }
   int64_t GetRandomValueForStepConstantInLoop() {
     return random_generator_->RandomUint64(UINT64_MAX);
   }
@@ -462,6 +469,9 @@
     // Ensure that the number of unused components is non-zero.
     return random_generator_->RandomUint32(max_unused_component_count) + 1;
   }
+  uint32_t GetWidthOfWrappingVector() {
+    return 2 + random_generator_->RandomUint32(3);
+  }
   bool GoDeeperInConstantObfuscation(uint32_t depth) {
     return go_deeper_in_constant_obfuscation_(depth, random_generator_.get());
   }
@@ -562,6 +572,7 @@
   uint32_t chance_of_swapping_functions_;
   uint32_t chance_of_toggling_access_chain_instruction_;
   uint32_t chance_of_wrapping_region_in_selection_;
+  uint32_t chance_of_wrapping_vector_synonym_;
 
   // Limits associated with various quantities for which random values are
   // chosen during fuzzing.
diff --git a/source/fuzz/fuzzer_pass_wrap_vector_synonym.cpp b/source/fuzz/fuzzer_pass_wrap_vector_synonym.cpp
new file mode 100644
index 0000000..3f324df
--- /dev/null
+++ b/source/fuzz/fuzzer_pass_wrap_vector_synonym.cpp
@@ -0,0 +1,122 @@
+// 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_wrap_vector_synonym.h"
+#include "source/fuzz/fuzzer_context.h"
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/transformation_composite_construct.h"
+#include "source/fuzz/transformation_wrap_vector_synonym.h"
+
+namespace spvtools {
+namespace fuzz {
+
+FuzzerPassWrapVectorSynonym::FuzzerPassWrapVectorSynonym(
+    opt::IRContext* ir_context, TransformationContext* transformation_context,
+    FuzzerContext* fuzzer_context,
+    protobufs::TransformationSequence* transformations)
+    : FuzzerPass(ir_context, transformation_context, fuzzer_context,
+                 transformations) {}
+
+void FuzzerPassWrapVectorSynonym::Apply() {
+  ForEachInstructionWithInstructionDescriptor(
+      [this](opt::Function* /*unused*/, opt::BasicBlock* /*unused*/,
+             opt::BasicBlock::iterator instruction_iterator,
+             const protobufs::InstructionDescriptor& instruction_descriptor)
+          -> void {
+
+        // Randomly decide whether to wrap it to a vector operation.
+        if (!GetFuzzerContext()->ChoosePercentage(
+                GetFuzzerContext()->GetChanceOfWrappingVectorSynonym())) {
+          return;
+        }
+
+        // The transformation is not applicable if the instruction has missing
+        // result id, type id, or is not supported type.
+        if (!TransformationWrapVectorSynonym::IsInstructionSupported(
+                GetIRContext(), *instruction_iterator)) {
+          return;
+        }
+
+        // The transformation will not be applicable if the id of the scalar
+        // operation is irrelevant.
+        if (GetTransformationContext()->GetFactManager()->IdIsIrrelevant(
+                instruction_iterator->result_id())) {
+          return;
+        }
+
+        // It must be valid to insert an OpCompositeConstruct instruction
+        // before |instruction_iterator|.
+        if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(
+                SpvOpCompositeConstruct, instruction_iterator)) {
+          return;
+        }
+        // Get the scalar type represented by the targeted instruction id.
+        uint32_t operand_type_id = instruction_iterator->type_id();
+
+        // Get a random vector size from 2 to 4.
+        uint32_t vector_size = GetFuzzerContext()->GetWidthOfWrappingVector();
+
+        // Randomly choose a position that target ids should be placed at.
+        // The position is in range [0, n - 1], where n is the size of the
+        // vector.
+        uint32_t position =
+            GetFuzzerContext()->GetRandomIndexForWrappingVector(vector_size);
+
+        // Target ids are the two scalar ids from the original instruction.
+        uint32_t target_id1 = instruction_iterator->GetSingleWordInOperand(0);
+        uint32_t target_id2 = instruction_iterator->GetSingleWordInOperand(1);
+
+        // Stores the ids of scalar constants.
+        std::vector<uint32_t> vec1_components;
+        std::vector<uint32_t> vec2_components;
+
+        // Populate components based on vector type and size.
+        for (uint32_t i = 0; i < vector_size; ++i) {
+          if (i == position) {
+            vec1_components.emplace_back(target_id1);
+            vec2_components.emplace_back(target_id2);
+          } else {
+            vec1_components.emplace_back(
+                FindOrCreateZeroConstant(operand_type_id, true));
+            vec2_components.emplace_back(
+                FindOrCreateZeroConstant(operand_type_id, true));
+          }
+        }
+
+        // Add two OpCompositeConstruct to the module with result id returned.
+
+        // Add the first OpCompositeConstruct that wraps the id of the first
+        // operand.
+        uint32_t result_id1 = GetFuzzerContext()->GetFreshId();
+        ApplyTransformation(TransformationCompositeConstruct(
+            operand_type_id, vec1_components, instruction_descriptor,
+            result_id1));
+
+        // Add the second OpCompositeConstruct that wraps the id of the second
+        // operand.
+        uint32_t result_id2 = GetFuzzerContext()->GetFreshId();
+        ApplyTransformation(TransformationCompositeConstruct(
+            operand_type_id, vec2_components, instruction_descriptor,
+            result_id2));
+
+        // Apply transformation to do vector operation and add synonym between
+        // the result vector id and the id of the original instruction.
+        ApplyTransformation(TransformationWrapVectorSynonym(
+            instruction_iterator->result_id(), result_id1, result_id2,
+            GetFuzzerContext()->GetFreshId(), position));
+      });
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/source/fuzz/fuzzer_pass_wrap_vector_synonym.h b/source/fuzz/fuzzer_pass_wrap_vector_synonym.h
new file mode 100644
index 0000000..647d99d
--- /dev/null
+++ b/source/fuzz/fuzzer_pass_wrap_vector_synonym.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_WRAP_VECTOR_SYNONYM_H_
+#define SOURCE_FUZZ_FUZZER_PASS_WRAP_VECTOR_SYNONYM_H_
+
+#include "source/fuzz/fuzzer_pass.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Randomly wrap a scalar operation into a vector operation.
+class FuzzerPassWrapVectorSynonym : public FuzzerPass {
+ public:
+  FuzzerPassWrapVectorSynonym(
+      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_WRAP_VECTOR_SYNONYM_H_
diff --git a/source/fuzz/pass_management/repeated_pass_instances.h b/source/fuzz/pass_management/repeated_pass_instances.h
index 80ac087..da61fda 100644
--- a/source/fuzz/pass_management/repeated_pass_instances.h
+++ b/source/fuzz/pass_management/repeated_pass_instances.h
@@ -73,6 +73,7 @@
 #include "source/fuzz/fuzzer_pass_split_blocks.h"
 #include "source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h"
 #include "source/fuzz/fuzzer_pass_wrap_regions_in_selections.h"
+#include "source/fuzz/fuzzer_pass_wrap_vector_synonym.h"
 
 namespace spvtools {
 namespace fuzz {
@@ -168,6 +169,7 @@
   REPEATED_PASS_INSTANCE(SplitBlocks);
   REPEATED_PASS_INSTANCE(SwapBranchConditionalOperands);
   REPEATED_PASS_INSTANCE(WrapRegionsInSelections);
+  REPEATED_PASS_INSTANCE(WrapVectorSynonym);
 #undef REPEATED_PASS_INSTANCE
 
  public:
diff --git a/source/fuzz/pass_management/repeated_pass_recommender_standard.cpp b/source/fuzz/pass_management/repeated_pass_recommender_standard.cpp
index a933848..6c61c0d 100644
--- a/source/fuzz/pass_management/repeated_pass_recommender_standard.cpp
+++ b/source/fuzz/pass_management/repeated_pass_recommender_standard.cpp
@@ -351,6 +351,12 @@
          pass_instances_->GetReplaceIrrelevantIds(),
          pass_instances_->GetFlattenConditionalBranches()});
   }
+  if (&pass == pass_instances_->GetWrapVectorSynonym()) {
+    // This transformation introduces synonym facts and irrelevant ids.
+    return RandomOrderAndNonNull({pass_instances_->GetApplyIdSynonyms(),
+                                  pass_instances_->GetReplaceIrrelevantIds()});
+  }
+
   assert(false && "Unreachable: every fuzzer pass should be dealt with.");
   return {};
 }
diff --git a/source/fuzz/protobufs/spvtoolsfuzz.proto b/source/fuzz/protobufs/spvtoolsfuzz.proto
index 657e807..5618c14 100644
--- a/source/fuzz/protobufs/spvtoolsfuzz.proto
+++ b/source/fuzz/protobufs/spvtoolsfuzz.proto
@@ -559,6 +559,7 @@
     TransformationExpandVectorReduction expand_vector_reduction = 85;
     TransformationSwapFunctionVariables swap_function_variables = 86;
     TransformationSwapTwoFunctions swap_two_functions = 87;
+    TransformationWrapVectorSynonym wrap_vector_synonym = 88;
     // Add additional option using the next available number.
   }
 }
@@ -2371,3 +2372,39 @@
   bool branch_condition = 3;
 
 }
+
+message TransformationWrapVectorSynonym {
+  // A transformation that wraps an arithmetic operation into a vector operation
+  // and get the result of the original operation from the corresponding index.
+  // For instance, for this transformation, an scalar operation between two scalars:
+  // define op ∈ {+, -, *}
+  // c = a op b
+  //
+  // requires the availability of two vectors:
+  //
+  // va = vector(..., a, ...)
+  // vb = vector(..., b, ...)
+  //
+  // where a and b are in the same position i in each of their correponding vector
+  // and a is synonymous with va[i] and b is synonymous with vb[i].
+  //
+  // The transformation then add an instruction vc = va op vb where c is synonymous
+  // with vc[i].
+
+  // The result if of the original scalar operation instruction.
+  uint32 instruction_id = 1;
+
+  // The result id for the first vector that contains the first value of the scalar operation.
+  uint32 vector_operand1 = 2;
+
+  // The result id for the second vector that contains the second value of the scalar operation.
+  uint32 vector_operand2 = 3;
+
+  // A fresh id for the resulted vector from the addition of the first and second vector.
+  uint32 fresh_id = 4;
+
+  // The position in the vector where the value of original instruction is located. Must be in
+  // the corresponding vector range.
+  uint32 scalar_position = 5;
+
+}
diff --git a/source/fuzz/transformation.cpp b/source/fuzz/transformation.cpp
index 4ea0c77..70a302b 100644
--- a/source/fuzz/transformation.cpp
+++ b/source/fuzz/transformation.cpp
@@ -104,6 +104,7 @@
 #include "source/fuzz/transformation_vector_shuffle.h"
 #include "source/fuzz/transformation_wrap_early_terminator_in_function.h"
 #include "source/fuzz/transformation_wrap_region_in_selection.h"
+#include "source/fuzz/transformation_wrap_vector_synonym.h"
 #include "source/util/make_unique.h"
 
 namespace spvtools {
@@ -382,6 +383,9 @@
     case protobufs::Transformation::TransformationCase::kWrapRegionInSelection:
       return MakeUnique<TransformationWrapRegionInSelection>(
           message.wrap_region_in_selection());
+    case protobufs::Transformation::TransformationCase::kWrapVectorSynonym:
+      return MakeUnique<TransformationWrapVectorSynonym>(
+          message.wrap_vector_synonym());
     case protobufs::Transformation::TRANSFORMATION_NOT_SET:
       assert(false && "An unset transformation was encountered.");
       return nullptr;
diff --git a/source/fuzz/transformation_wrap_vector_synonym.cpp b/source/fuzz/transformation_wrap_vector_synonym.cpp
new file mode 100644
index 0000000..86eb51c
--- /dev/null
+++ b/source/fuzz/transformation_wrap_vector_synonym.cpp
@@ -0,0 +1,182 @@
+// 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_wrap_vector_synonym.h"
+
+#include "source/fuzz/data_descriptor.h"
+#include "source/fuzz/fuzzer_util.h"
+#include "source/opt/instruction.h"
+
+namespace spvtools {
+namespace fuzz {
+
+TransformationWrapVectorSynonym::TransformationWrapVectorSynonym(
+    protobufs::TransformationWrapVectorSynonym message)
+    : message_(std::move(message)) {}
+
+TransformationWrapVectorSynonym::TransformationWrapVectorSynonym(
+    uint32_t instruction_id, uint32_t vector_operand1, uint32_t vector_operand2,
+    uint32_t fresh_id, uint32_t pos) {
+  message_.set_instruction_id(instruction_id);
+  message_.set_vector_operand1(vector_operand1);
+  message_.set_vector_operand2(vector_operand2);
+  message_.set_fresh_id(fresh_id);
+  message_.set_scalar_position(pos);
+}
+
+bool TransformationWrapVectorSynonym::IsApplicable(
+    opt::IRContext* ir_context,
+    const TransformationContext& transformation_context) const {
+  // |fresh_id| must be fresh.
+  if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) {
+    return false;
+  }
+
+  const opt::Instruction* instruction =
+      ir_context->get_def_use_mgr()->GetDef(message_.instruction_id());
+
+  // |instruction_id| must refer to an existing instruction.
+  if (instruction == nullptr) {
+    return false;
+  }
+
+  if (!IsInstructionSupported(ir_context, *instruction)) {
+    return false;
+  }
+
+  assert(!transformation_context.GetFactManager()->IdIsIrrelevant(
+             instruction->result_id()) &&
+         "Result id of the scalar operation must be relevant.");
+
+  // |vector_operand1| and |vector_operand2| must exist.
+  auto vec1 = ir_context->get_def_use_mgr()->GetDef(message_.vector_operand1());
+  auto vec2 = ir_context->get_def_use_mgr()->GetDef(message_.vector_operand2());
+
+  if (vec1 == nullptr || vec2 == nullptr) {
+    return false;
+  }
+
+  // The 2 vectors must be the same valid vector type.
+  auto vec1_type_id = vec1->type_id();
+  auto vec2_type_id = vec2->type_id();
+
+  if (vec1_type_id != vec2_type_id) {
+    return false;
+  }
+
+  if (ir_context->get_def_use_mgr()->GetDef(vec1_type_id)->opcode() !=
+      SpvOpTypeVector) {
+    return false;
+  }
+
+  // |scalar_position| needs to be a non-negative integer less than the vector
+  // length.
+  // OpTypeVector instruction has the component count at index 2.
+  if (message_.scalar_position() >= ir_context->get_def_use_mgr()
+                                        ->GetDef(vec1_type_id)
+                                        ->GetSingleWordInOperand(1)) {
+    return false;
+  }
+
+  if (!transformation_context.GetFactManager()->IsSynonymous(
+          MakeDataDescriptor(message_.vector_operand1(),
+                             {message_.scalar_position()}),
+          MakeDataDescriptor(instruction->GetSingleWordInOperand(0), {}))) {
+    return false;
+  }
+
+  if (!transformation_context.GetFactManager()->IsSynonymous(
+          MakeDataDescriptor(message_.vector_operand2(),
+                             {message_.scalar_position()}),
+          MakeDataDescriptor(instruction->GetSingleWordInOperand(1), {}))) {
+    return false;
+  }
+
+  return true;
+}
+
+void TransformationWrapVectorSynonym::Apply(
+    opt::IRContext* ir_context,
+    TransformationContext* transformation_context) const {
+  // Create an instruction descriptor for the original instruction.
+  auto instruction =
+      ir_context->get_def_use_mgr()->GetDef(message_.instruction_id());
+  auto destination_block = ir_context->get_instr_block(instruction);
+
+  //  Populate input operand list with two vectors for vector operation.
+  opt::Instruction::OperandList in_operands;
+  in_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.vector_operand1()}});
+  in_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.vector_operand2()}});
+
+  // Make a new arithmetic instruction: %fresh_id = OpXX %type_id %result_id1
+  // %result_id2.
+  auto vec_type_id = ir_context->get_def_use_mgr()
+                         ->GetDef(message_.vector_operand1())
+                         ->type_id();
+  auto new_instruction = MakeUnique<opt::Instruction>(
+      ir_context, instruction->opcode(), vec_type_id, message_.fresh_id(),
+      std::move(in_operands));
+  auto new_instruction_ptr = new_instruction.get();
+  instruction->InsertBefore(std::move(new_instruction));
+  ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr);
+  ir_context->set_instr_block(new_instruction_ptr, destination_block);
+
+  // Add |fresh_id| to id bound.
+  fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
+
+  // Add synonyms between |fresh_id| and |instruction_id|.
+  transformation_context->GetFactManager()->AddFactDataSynonym(
+      MakeDataDescriptor(message_.fresh_id(), {message_.scalar_position()}),
+      MakeDataDescriptor(message_.instruction_id(), {}));
+}
+
+protobufs::Transformation TransformationWrapVectorSynonym::ToMessage() const {
+  protobufs::Transformation result;
+  *result.mutable_wrap_vector_synonym() = message_;
+  return result;
+}
+
+std::unordered_set<uint32_t> TransformationWrapVectorSynonym::GetFreshIds()
+    const {
+  return std::unordered_set<uint32_t>{message_.fresh_id()};
+}
+
+bool TransformationWrapVectorSynonym::IsInstructionSupported(
+    opt::IRContext* ir_context, const opt::Instruction& instruction) {
+  if (!instruction.result_id() || !instruction.type_id()) {
+    return false;
+  }
+  auto type_instruction =
+      ir_context->get_def_use_mgr()->GetDef(instruction.type_id());
+
+  if ((type_instruction->opcode() != SpvOpTypeInt &&
+       type_instruction->opcode() != SpvOpTypeFloat)) {
+    return false;
+  }
+
+  switch (instruction.opcode()) {
+    case SpvOpIAdd:
+    case SpvOpISub:
+    case SpvOpIMul:
+    case SpvOpFAdd:
+    case SpvOpFSub:
+    case SpvOpFMul:
+      return true;
+    default:
+      return false;
+  }
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/source/fuzz/transformation_wrap_vector_synonym.h b/source/fuzz/transformation_wrap_vector_synonym.h
new file mode 100644
index 0000000..008211a
--- /dev/null
+++ b/source/fuzz/transformation_wrap_vector_synonym.h
@@ -0,0 +1,77 @@
+// 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_WRAP_VECTOR_SYNONYM_H_
+#define SOURCE_FUZZ_TRANSFORMATION_WRAP_VECTOR_SYNONYM_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 TransformationWrapVectorSynonym : public Transformation {
+ public:
+  explicit TransformationWrapVectorSynonym(
+      protobufs::TransformationWrapVectorSynonym message);
+
+  TransformationWrapVectorSynonym(uint32_t instruction_id,
+                                  uint32_t vector_operand1,
+                                  uint32_t vector_operand2, uint32_t fresh_id,
+                                  uint32_t pos);
+  // - |instruction_id| must be the id of a supported arithmetic operation
+  //   and must be relevant.
+  // - |vector_operand1| and |vector_operand2| represents the result ids of the
+  //   two vector operands.
+  // - |fresh_id| is an unused id that will be used as a result id of the
+  //   created instruction.
+  // - |vector_operand1| and |vector_operand2| must have the same vector type
+  //   that is supported by this transformation.
+  // - |pos| is an index of the operands of |instruction_id| in the
+  //   |vector_operand1| and |vector_operand2|. It must be less than the size
+  //   of those vector operands.
+  bool IsApplicable(
+      opt::IRContext* ir_context,
+      const TransformationContext& transformation_context) const override;
+
+  // Adds a new instruction before the |instruction_id| with |fresh_id|
+  // result id and |instruction_id|'s opcode. The added instruction has
+  // two operands: |vector_operand1| and |vector_operand2| and its type
+  // id is equal to the type ids of those operands. A new fact is added
+  // to the fact manager specifying that |fresh_id[pos]| is synonymous
+  // to |instruction_id|.
+  void Apply(opt::IRContext* ir_context,
+             TransformationContext* transformation_context) const override;
+
+  std::unordered_set<uint32_t> GetFreshIds() const override;
+  protobufs::Transformation ToMessage() const override;
+
+  // Checks whether the instruction given is supported by the transformation.
+  // A valid instruction must:
+  // - has both result id and type id.
+  // - is a supported scalar operation instruction.
+  // - has a supported type that is either int or float.
+  static bool IsInstructionSupported(opt::IRContext* ir_context,
+                                     const opt::Instruction& instruction);
+
+ private:
+  protobufs::TransformationWrapVectorSynonym message_;
+};
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_TRANSFORMATION_WRAP_VECTOR_SYNONYM_H_
diff --git a/test/fuzz/CMakeLists.txt b/test/fuzz/CMakeLists.txt
index 8acebde..56af0b9 100644
--- a/test/fuzz/CMakeLists.txt
+++ b/test/fuzz/CMakeLists.txt
@@ -122,6 +122,7 @@
           transformation_vector_shuffle_test.cpp
           transformation_wrap_early_terminator_in_function_test.cpp
           transformation_wrap_region_in_selection_test.cpp
+          transformation_wrap_vector_synonym_test.cpp
           uniform_buffer_element_descriptor_test.cpp)
 
   if (${SPIRV_ENABLE_LONG_FUZZER_TESTS})
diff --git a/test/fuzz/transformation_wrap_vector_synonym_test.cpp b/test/fuzz/transformation_wrap_vector_synonym_test.cpp
new file mode 100644
index 0000000..dae78a5
--- /dev/null
+++ b/test/fuzz/transformation_wrap_vector_synonym_test.cpp
@@ -0,0 +1,1394 @@
+// 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_wrap_vector_synonym.h"
+
+#include "gtest/gtest.h"
+#include "source/fuzz/fuzzer_util.h"
+#include "source/fuzz/instruction_descriptor.h"
+#include "test/fuzz/fuzz_test_util.h"
+
+namespace spvtools {
+namespace fuzz {
+namespace {
+
+TEST(TransformationWrapVectorSynonym, BasicTest) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %97
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %9 = OpConstant %6 10
+         %11 = OpConstant %6 -5
+         %12 = OpTypeVector %6 2
+         %13 = OpTypePointer Function %12
+         %18 = OpTypeInt 32 0
+         %19 = OpTypePointer Function %18
+         %21 = OpConstant %18 8
+         %23 = OpConstant %18 2
+         %24 = OpTypeVector %18 3
+         %25 = OpTypePointer Function %24
+         %31 = OpTypeFloat 32
+         %32 = OpTypePointer Function %31
+         %34 = OpConstant %31 3.29999995
+         %36 = OpConstant %31 1.10000002
+         %37 = OpTypeVector %31 4
+         %38 = OpTypePointer Function %37
+         %96 = OpTypePointer Input %31
+         %97 = OpVariable %96 Input
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %10 = OpVariable %7 Function
+         %14 = OpVariable %13 Function
+         %20 = OpVariable %19 Function
+         %22 = OpVariable %19 Function
+         %26 = OpVariable %25 Function
+         %33 = OpVariable %32 Function
+         %35 = OpVariable %32 Function
+         %39 = OpVariable %38 Function
+         %47 = OpVariable %7 Function
+         %51 = OpVariable %7 Function
+         %55 = OpVariable %7 Function
+         %59 = OpVariable %7 Function
+         %63 = OpVariable %19 Function
+         %67 = OpVariable %19 Function
+         %71 = OpVariable %19 Function
+         %75 = OpVariable %19 Function
+         %79 = OpVariable %32 Function
+         %83 = OpVariable %32 Function
+         %87 = OpVariable %32 Function
+         %91 = OpVariable %32 Function
+               OpStore %8 %9
+               OpStore %10 %11
+         %15 = OpLoad %6 %8
+         %16 = OpLoad %6 %10
+         %17 = OpCompositeConstruct %12 %15 %16
+               OpStore %14 %17
+               OpStore %20 %21
+               OpStore %22 %23
+         %27 = OpLoad %18 %20
+         %28 = OpLoad %18 %20
+         %29 = OpLoad %18 %22
+         %30 = OpCompositeConstruct %24 %27 %28 %29
+               OpStore %26 %30
+               OpStore %33 %34
+               OpStore %35 %36
+         %40 = OpLoad %31 %33
+         %41 = OpLoad %31 %33
+         %42 = OpLoad %31 %35
+         %43 = OpLoad %31 %35
+         %44 = OpCompositeConstruct %37 %40 %41 %42 %43
+         %45 = OpLoad %37 %39
+         %46 = OpVectorShuffle %37 %45 %44 5 6 7 4
+               OpStore %39 %46
+         %48 = OpLoad %6 %8
+         %49 = OpLoad %6 %10
+        %100 = OpCompositeConstruct %12 %48 %48
+        %101 = OpCompositeConstruct %12 %49 %49
+         %50 = OpIAdd %6 %48 %49
+               OpStore %47 %50
+         %52 = OpLoad %6 %8
+         %53 = OpLoad %6 %10
+         %54 = OpISub %6 %52 %53
+               OpStore %51 %54
+         %56 = OpLoad %6 %8
+         %57 = OpLoad %6 %10
+         %58 = OpIMul %6 %56 %57
+               OpStore %55 %58
+         %60 = OpLoad %6 %8
+         %61 = OpLoad %6 %10
+         %62 = OpSDiv %6 %60 %61
+               OpStore %59 %62
+         %64 = OpLoad %18 %20
+         %65 = OpLoad %18 %22
+         %66 = OpIAdd %18 %64 %65
+               OpStore %63 %66
+         %68 = OpLoad %18 %20
+         %69 = OpLoad %18 %22
+         %70 = OpISub %18 %68 %69
+               OpStore %67 %70
+         %72 = OpLoad %18 %20
+         %73 = OpLoad %18 %22
+         %74 = OpIMul %18 %72 %73
+               OpStore %71 %74
+         %76 = OpLoad %18 %20
+         %77 = OpLoad %18 %22
+         %78 = OpUDiv %18 %76 %77
+               OpStore %75 %78
+         %80 = OpLoad %31 %33
+         %81 = OpLoad %31 %35
+         %82 = OpFAdd %31 %80 %81
+               OpStore %79 %82
+         %84 = OpLoad %31 %33
+         %85 = OpLoad %31 %35
+         %86 = OpFSub %31 %84 %85
+               OpStore %83 %86
+         %88 = OpLoad %31 %33
+         %89 = OpLoad %31 %35
+         %90 = OpFMul %31 %88 %89
+               OpStore %87 %90
+         %92 = OpLoad %31 %33
+         %93 = OpLoad %31 %35
+         %94 = OpFDiv %31 %92 %93
+               OpStore %91 %94
+               OpReturn
+               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);
+
+  // Vec Type Id |   Vector Type  |  Element Type id |   Element Type  |
+  // ------------+----------------+------------------+-----------------+
+  //     12      |      vec2      |         6        |      int32      |
+  //     24      |      vec3      |        18        |     uint32      |
+  //     37      |      vec4      |        31        |      float      |
+
+  // Instruction Id | Opcode  | Type Id | constant id 1 | constant id 2 |
+  // ---------------+---------+---------+---------------+---------------+
+  //       50       | OpIAdd  |    6    |      48       |      49       |
+  //       54       | OpISub  |    6    |      52       |      53       |
+  //       58       | OpIMul  |    6    |      56       |      57       |
+  //       62       | OpSDiv  |    6    |      60       |      61       |
+  //       66       | OpIAdd  |    18   |      64       |      65       |
+  //       70       | OpISub  |    18   |      68       |      69       |
+  //       74       | OpIMul  |    18   |      72       |      73       |
+  //       78       | OpUDiv  |    18   |      76       |      77       |
+  //       82       | OpFAdd  |    31   |      80       |      81       |
+  //       86       | OpFSub  |    31   |      84       |      85       |
+  //       90       | OpFMul  |    31   |      88       |      89       |
+  //       94       | OpFDiv  |    31   |      92       |      93       |
+
+  // Assert that the target scalar instruction result id is relevant.
+  ASSERT_FALSE(transformation_context.GetFactManager()->IdIsIrrelevant(50));
+  transformation_context.GetFactManager()->AddFactDataSynonym(
+      MakeDataDescriptor(100, {1}), MakeDataDescriptor(48, {}));
+  transformation_context.GetFactManager()->AddFactDataSynonym(
+      MakeDataDescriptor(101, {1}), MakeDataDescriptor(49, {}));
+
+  // The following are all invalid use.
+  {
+    // Bad: Instruction id does not exist.
+    TransformationWrapVectorSynonym wrap_add_int_bad1(103, 100, 101, 102, 1);
+    ASSERT_FALSE(
+        wrap_add_int_bad1.IsApplicable(context.get(), transformation_context));
+
+    // Bad: Instruction id given is not of a valid arithmetic operation typed
+    // instruction.
+    TransformationWrapVectorSynonym wrap_add_int_bad2(80, 100, 101, 102, 1);
+    ASSERT_FALSE(
+        wrap_add_int_bad1.IsApplicable(context.get(), transformation_context));
+
+    // Bad: the id for the first vector does not exist.
+    TransformationWrapVectorSynonym wrap_add_int_bad3(50, 105, 101, 102, 1);
+    ASSERT_FALSE(
+        wrap_add_int_bad3.IsApplicable(context.get(), transformation_context));
+
+    // Bad: the id for the second vector does not exist.
+    TransformationWrapVectorSynonym wrap_add_int_bad4(50, 100, 105, 102, 1);
+    ASSERT_FALSE(
+        wrap_add_int_bad4.IsApplicable(context.get(), transformation_context));
+
+    // Bad: vector id is not fresh.
+    TransformationWrapVectorSynonym wrap_add_int_bad6(50, 100, 101, 94, 1);
+    ASSERT_FALSE(
+        wrap_add_int_bad6.IsApplicable(context.get(), transformation_context));
+
+    // Bad: The position goes out of bound for the given vector type.
+    TransformationWrapVectorSynonym wrap_add_int_bad8(50, 100, 101, 102, 2);
+    ASSERT_FALSE(
+        wrap_add_int_bad8.IsApplicable(context.get(), transformation_context));
+
+    // Bad: The original instruction is not a valid scalar operation
+    // instruction.
+    TransformationWrapVectorSynonym wrap_add_int(27, 100, 101, 102, 1);
+    ASSERT_FALSE(
+        wrap_add_int.IsApplicable(context.get(), transformation_context));
+  }
+
+  // Good: The following transformation should be applicable.
+  TransformationWrapVectorSynonym wrap_add_int(50, 100, 101, 102, 1);
+  ASSERT_TRUE(wrap_add_int.IsApplicable(context.get(), transformation_context));
+  // Insert an arithmetic instruction of the same type to add two vectors.
+  ApplyAndCheckFreshIds(wrap_add_int, context.get(), &transformation_context);
+
+  // |instruction_id| and id at |scalar_position of the result vector should be
+  // synonyms.
+  ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+      MakeDataDescriptor(102, {1}), MakeDataDescriptor(50, {})));
+
+  // After applying transformations, the instruction:
+  //
+  // %102 = OpIAdd %12 %100 %101
+  //
+  // should be added before:
+  //
+  // %50 = OpIAdd %6 %48 %49
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %97
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %9 = OpConstant %6 10
+         %11 = OpConstant %6 -5
+         %12 = OpTypeVector %6 2
+         %13 = OpTypePointer Function %12
+         %18 = OpTypeInt 32 0
+         %19 = OpTypePointer Function %18
+         %21 = OpConstant %18 8
+         %23 = OpConstant %18 2
+         %24 = OpTypeVector %18 3
+         %25 = OpTypePointer Function %24
+         %31 = OpTypeFloat 32
+         %32 = OpTypePointer Function %31
+         %34 = OpConstant %31 3.29999995
+         %36 = OpConstant %31 1.10000002
+         %37 = OpTypeVector %31 4
+         %38 = OpTypePointer Function %37
+         %96 = OpTypePointer Input %31
+         %97 = OpVariable %96 Input
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %10 = OpVariable %7 Function
+         %14 = OpVariable %13 Function
+         %20 = OpVariable %19 Function
+         %22 = OpVariable %19 Function
+         %26 = OpVariable %25 Function
+         %33 = OpVariable %32 Function
+         %35 = OpVariable %32 Function
+         %39 = OpVariable %38 Function
+         %47 = OpVariable %7 Function
+         %51 = OpVariable %7 Function
+         %55 = OpVariable %7 Function
+         %59 = OpVariable %7 Function
+         %63 = OpVariable %19 Function
+         %67 = OpVariable %19 Function
+         %71 = OpVariable %19 Function
+         %75 = OpVariable %19 Function
+         %79 = OpVariable %32 Function
+         %83 = OpVariable %32 Function
+         %87 = OpVariable %32 Function
+         %91 = OpVariable %32 Function
+               OpStore %8 %9
+               OpStore %10 %11
+         %15 = OpLoad %6 %8
+         %16 = OpLoad %6 %10
+         %17 = OpCompositeConstruct %12 %15 %16
+               OpStore %14 %17
+               OpStore %20 %21
+               OpStore %22 %23
+         %27 = OpLoad %18 %20
+         %28 = OpLoad %18 %20
+         %29 = OpLoad %18 %22
+         %30 = OpCompositeConstruct %24 %27 %28 %29
+               OpStore %26 %30
+               OpStore %33 %34
+               OpStore %35 %36
+         %40 = OpLoad %31 %33
+         %41 = OpLoad %31 %33
+         %42 = OpLoad %31 %35
+         %43 = OpLoad %31 %35
+         %44 = OpCompositeConstruct %37 %40 %41 %42 %43
+         %45 = OpLoad %37 %39
+         %46 = OpVectorShuffle %37 %45 %44 5 6 7 4
+               OpStore %39 %46
+         %48 = OpLoad %6 %8
+         %49 = OpLoad %6 %10
+        %100 = OpCompositeConstruct %12 %48 %48
+        %101 = OpCompositeConstruct %12 %49 %49
+        %102 = OpIAdd %12 %100 %101
+         %50 = OpIAdd %6 %48 %49
+               OpStore %47 %50
+         %52 = OpLoad %6 %8
+         %53 = OpLoad %6 %10
+         %54 = OpISub %6 %52 %53
+               OpStore %51 %54
+         %56 = OpLoad %6 %8
+         %57 = OpLoad %6 %10
+         %58 = OpIMul %6 %56 %57
+               OpStore %55 %58
+         %60 = OpLoad %6 %8
+         %61 = OpLoad %6 %10
+         %62 = OpSDiv %6 %60 %61
+               OpStore %59 %62
+         %64 = OpLoad %18 %20
+         %65 = OpLoad %18 %22
+         %66 = OpIAdd %18 %64 %65
+               OpStore %63 %66
+         %68 = OpLoad %18 %20
+         %69 = OpLoad %18 %22
+         %70 = OpISub %18 %68 %69
+               OpStore %67 %70
+         %72 = OpLoad %18 %20
+         %73 = OpLoad %18 %22
+         %74 = OpIMul %18 %72 %73
+               OpStore %71 %74
+         %76 = OpLoad %18 %20
+         %77 = OpLoad %18 %22
+         %78 = OpUDiv %18 %76 %77
+               OpStore %75 %78
+         %80 = OpLoad %31 %33
+         %81 = OpLoad %31 %35
+         %82 = OpFAdd %31 %80 %81
+               OpStore %79 %82
+         %84 = OpLoad %31 %33
+         %85 = OpLoad %31 %35
+         %86 = OpFSub %31 %84 %85
+               OpStore %83 %86
+         %88 = OpLoad %31 %33
+         %89 = OpLoad %31 %35
+         %90 = OpFMul %31 %88 %89
+               OpStore %87 %90
+         %92 = OpLoad %31 %33
+         %93 = OpLoad %31 %35
+         %94 = OpFDiv %31 %92 %93
+               OpStore %91 %94
+               OpReturn
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationWrapVectorSynonym, OperationSupportTest) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %97
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %9 = OpConstant %6 10
+         %11 = OpConstant %6 -5
+         %12 = OpTypeVector %6 2
+         %13 = OpTypePointer Function %12
+         %18 = OpTypeInt 32 0
+         %19 = OpTypePointer Function %18
+         %21 = OpConstant %18 8
+         %23 = OpConstant %18 2
+         %24 = OpTypeVector %18 3
+         %25 = OpTypePointer Function %24
+         %31 = OpTypeFloat 32
+         %32 = OpTypePointer Function %31
+         %34 = OpConstant %31 3.29999995
+         %36 = OpConstant %31 1.10000002
+         %37 = OpTypeVector %31 4
+         %38 = OpTypePointer Function %37
+         %96 = OpTypePointer Input %31
+         %97 = OpVariable %96 Input
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %10 = OpVariable %7 Function
+         %14 = OpVariable %13 Function
+         %20 = OpVariable %19 Function
+         %22 = OpVariable %19 Function
+         %26 = OpVariable %25 Function
+         %33 = OpVariable %32 Function
+         %35 = OpVariable %32 Function
+         %39 = OpVariable %38 Function
+         %47 = OpVariable %7 Function
+         %51 = OpVariable %7 Function
+         %55 = OpVariable %7 Function
+         %59 = OpVariable %7 Function
+         %63 = OpVariable %19 Function
+         %67 = OpVariable %19 Function
+         %71 = OpVariable %19 Function
+         %75 = OpVariable %19 Function
+         %79 = OpVariable %32 Function
+         %83 = OpVariable %32 Function
+         %87 = OpVariable %32 Function
+         %91 = OpVariable %32 Function
+               OpStore %8 %9
+               OpStore %10 %11
+         %15 = OpLoad %6 %8
+         %16 = OpLoad %6 %10
+         %17 = OpCompositeConstruct %12 %15 %16
+               OpStore %14 %17
+               OpStore %20 %21
+               OpStore %22 %23
+         %27 = OpLoad %18 %20
+         %28 = OpLoad %18 %20
+         %29 = OpLoad %18 %22
+         %30 = OpCompositeConstruct %24 %27 %28 %29
+               OpStore %26 %30
+               OpStore %33 %34
+               OpStore %35 %36
+         %40 = OpLoad %31 %33
+         %41 = OpLoad %31 %33
+         %42 = OpLoad %31 %35
+         %43 = OpLoad %31 %35
+         %44 = OpCompositeConstruct %37 %40 %41 %42 %43
+         %45 = OpLoad %37 %39
+         %46 = OpVectorShuffle %37 %45 %44 5 6 7 4
+               OpStore %39 %46
+         %48 = OpLoad %6 %8
+         %49 = OpLoad %6 %10
+         %50 = OpIAdd %6 %48 %49
+               OpStore %47 %50
+         %52 = OpLoad %6 %8
+         %53 = OpLoad %6 %10
+        %100 = OpCompositeConstruct %12 %52 %52
+        %101 = OpCompositeConstruct %12 %53 %53
+         %54 = OpISub %6 %52 %53
+               OpStore %51 %54
+         %56 = OpLoad %6 %8
+         %57 = OpLoad %6 %10
+        %103 = OpCompositeConstruct %12 %56 %56
+        %104 = OpCompositeConstruct %12 %57 %57
+         %58 = OpIMul %6 %56 %57
+               OpStore %55 %58
+         %60 = OpLoad %6 %8
+         %61 = OpLoad %6 %10
+         %62 = OpSDiv %6 %60 %61
+               OpStore %59 %62
+         %64 = OpLoad %18 %20
+         %65 = OpLoad %18 %22
+        %106 = OpCompositeConstruct %24 %64 %64 %64
+        %107 = OpCompositeConstruct %24 %65 %65 %65
+         %66 = OpIAdd %18 %64 %65
+               OpStore %63 %66
+         %68 = OpLoad %18 %20
+         %69 = OpLoad %18 %22
+        %109 = OpCompositeConstruct %24 %68 %68 %68
+        %110 = OpCompositeConstruct %24 %69 %69 %69
+         %70 = OpISub %18 %68 %69
+               OpStore %67 %70
+         %72 = OpLoad %18 %20
+         %73 = OpLoad %18 %22
+        %112 = OpCompositeConstruct %24 %72 %72 %72
+        %113 = OpCompositeConstruct %24 %73 %73 %73
+         %74 = OpIMul %18 %72 %73
+               OpStore %71 %74
+         %76 = OpLoad %18 %20
+         %77 = OpLoad %18 %22
+         %78 = OpUDiv %18 %76 %77
+               OpStore %75 %78
+         %80 = OpLoad %31 %33
+         %81 = OpLoad %31 %35
+        %115 = OpCompositeConstruct %37 %80 %80 %80 %80
+        %116 = OpCompositeConstruct %37 %81 %81 %81 %81
+         %82 = OpFAdd %31 %80 %81
+               OpStore %79 %82
+         %84 = OpLoad %31 %33
+         %85 = OpLoad %31 %35
+        %118 = OpCompositeConstruct %37 %84 %84 %84 %84
+        %119 = OpCompositeConstruct %37 %85 %85 %85 %85
+         %86 = OpFSub %31 %84 %85
+               OpStore %83 %86
+         %88 = OpLoad %31 %33
+         %89 = OpLoad %31 %35
+        %121 = OpCompositeConstruct %37 %88 %88 %88 %88
+        %122 = OpCompositeConstruct %37 %89 %89 %89 %89
+         %90 = OpFMul %31 %88 %89
+               OpStore %87 %90
+         %92 = OpLoad %31 %33
+         %93 = OpLoad %31 %35
+         %94 = OpFDiv %31 %92 %93
+               OpStore %91 %94
+               OpReturn
+               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;
+
+  ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
+                                               kConsoleMessageConsumer));
+
+  TransformationContext transformation_context(
+      MakeUnique<FactManager>(context.get()), validator_options);
+
+  {
+    // Add synonym facts between the vector operands at pos and the operands to
+    // the scalar instruction.
+    transformation_context.GetFactManager()->AddFactDataSynonym(
+        MakeDataDescriptor(100, {1}), MakeDataDescriptor(52, {}));
+    transformation_context.GetFactManager()->AddFactDataSynonym(
+        MakeDataDescriptor(101, {1}), MakeDataDescriptor(53, {}));
+
+    transformation_context.GetFactManager()->AddFactDataSynonym(
+        MakeDataDescriptor(103, {0}), MakeDataDescriptor(56, {}));
+    transformation_context.GetFactManager()->AddFactDataSynonym(
+        MakeDataDescriptor(104, {0}), MakeDataDescriptor(57, {}));
+
+    transformation_context.GetFactManager()->AddFactDataSynonym(
+        MakeDataDescriptor(106, {2}), MakeDataDescriptor(64, {}));
+    transformation_context.GetFactManager()->AddFactDataSynonym(
+        MakeDataDescriptor(107, {2}), MakeDataDescriptor(65, {}));
+
+    transformation_context.GetFactManager()->AddFactDataSynonym(
+        MakeDataDescriptor(109, {2}), MakeDataDescriptor(68, {}));
+    transformation_context.GetFactManager()->AddFactDataSynonym(
+        MakeDataDescriptor(110, {2}), MakeDataDescriptor(69, {}));
+
+    transformation_context.GetFactManager()->AddFactDataSynonym(
+        MakeDataDescriptor(112, {1}), MakeDataDescriptor(72, {}));
+    transformation_context.GetFactManager()->AddFactDataSynonym(
+        MakeDataDescriptor(113, {1}), MakeDataDescriptor(73, {}));
+
+    transformation_context.GetFactManager()->AddFactDataSynonym(
+        MakeDataDescriptor(115, {2}), MakeDataDescriptor(80, {}));
+    transformation_context.GetFactManager()->AddFactDataSynonym(
+        MakeDataDescriptor(116, {2}), MakeDataDescriptor(81, {}));
+
+    transformation_context.GetFactManager()->AddFactDataSynonym(
+        MakeDataDescriptor(118, {3}), MakeDataDescriptor(84, {}));
+    transformation_context.GetFactManager()->AddFactDataSynonym(
+        MakeDataDescriptor(119, {3}), MakeDataDescriptor(85, {}));
+
+    transformation_context.GetFactManager()->AddFactDataSynonym(
+        MakeDataDescriptor(121, {1}), MakeDataDescriptor(88, {}));
+    transformation_context.GetFactManager()->AddFactDataSynonym(
+        MakeDataDescriptor(122, {1}), MakeDataDescriptor(89, {}));
+  }
+
+  // Test OpISub for signed integer.
+  {
+    // Good: The following transformation should be applicable.
+    TransformationWrapVectorSynonym wrap_sub_int(54, 100, 101, 102, 1);
+    ASSERT_TRUE(
+        wrap_sub_int.IsApplicable(context.get(), transformation_context));
+    ApplyAndCheckFreshIds(wrap_sub_int, context.get(), &transformation_context);
+
+    // |instruction_id| and id at |scalar_position of the result vector should
+    // be synonyms.
+    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+        MakeDataDescriptor(102, {1}), MakeDataDescriptor(54, {})));
+  }
+
+  // Test OpIMul for signed integer.
+  {
+    // Good: The following transformation should be applicable.
+    TransformationWrapVectorSynonym wrap_mul_int(58, 103, 104, 105, 0);
+    ASSERT_TRUE(
+        wrap_mul_int.IsApplicable(context.get(), transformation_context));
+    ApplyAndCheckFreshIds(wrap_mul_int, context.get(), &transformation_context);
+
+    // |instruction_id| and id at |scalar_position of the result vector should
+    // be synonyms.
+    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+        MakeDataDescriptor(105, {0}), MakeDataDescriptor(58, {})));
+  }
+
+  // Test OpIAdd for unsigned integer.
+  {
+    // Good: The following transformation should be applicable.
+    TransformationWrapVectorSynonym wrap_add_uint(66, 106, 107, 108, 2);
+    ASSERT_TRUE(
+        wrap_add_uint.IsApplicable(context.get(), transformation_context));
+    ApplyAndCheckFreshIds(wrap_add_uint, context.get(),
+                          &transformation_context);
+
+    // |instruction_id| and id at |scalar_position of the result vector should
+    // be synonyms.
+    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+        MakeDataDescriptor(108, {2}), MakeDataDescriptor(66, {})));
+  }
+
+  // Test OpISub for signed integer.
+  {
+    // Good: The following transformation should be applicable.
+    TransformationWrapVectorSynonym wrap_sub_uint(70, 109, 110, 111, 2);
+    ASSERT_TRUE(
+        wrap_sub_uint.IsApplicable(context.get(), transformation_context));
+    ApplyAndCheckFreshIds(wrap_sub_uint, context.get(),
+                          &transformation_context);
+
+    // |instruction_id| and id at |scalar_position of the result vector should
+    // be synonyms.
+    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+        MakeDataDescriptor(111, {2}), MakeDataDescriptor(70, {})));
+  }
+
+  // Test OpIMul for signed integer.
+  {
+    // Good: The following transformation should be applicable.
+    TransformationWrapVectorSynonym wrap_mul_uint(74, 112, 113, 114, 1);
+    ASSERT_TRUE(
+        wrap_mul_uint.IsApplicable(context.get(), transformation_context));
+    ApplyAndCheckFreshIds(wrap_mul_uint, context.get(),
+                          &transformation_context);
+
+    // |instruction_id| and id at |scalar_position of the result vector should
+    // be synonyms.
+    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+        MakeDataDescriptor(114, {1}), MakeDataDescriptor(74, {})));
+  }
+
+  // Test OpFAdd for float.
+  {
+    // Good: The following transformation should be applicable.
+    TransformationWrapVectorSynonym wrap_add_float(82, 115, 116, 117, 2);
+    ASSERT_TRUE(
+        wrap_add_float.IsApplicable(context.get(), transformation_context));
+    ApplyAndCheckFreshIds(wrap_add_float, context.get(),
+                          &transformation_context);
+
+    // |instruction_id| and id at |scalar_position of the result vector should
+    // be synonyms.
+    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+        MakeDataDescriptor(117, {2}), MakeDataDescriptor(82, {})));
+  }
+
+  // Test OpFSub for float.
+  {
+    // Good: The following transformation should be applicable.
+    TransformationWrapVectorSynonym wrap_add_float(86, 118, 119, 120, 3);
+    ASSERT_TRUE(
+        wrap_add_float.IsApplicable(context.get(), transformation_context));
+    ApplyAndCheckFreshIds(wrap_add_float, context.get(),
+                          &transformation_context);
+
+    // |instruction_id| and id at |scalar_position of the result vector should
+    // be synonyms.
+    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+        MakeDataDescriptor(120, {3}), MakeDataDescriptor(86, {})));
+  }
+
+  // Test OpFMul for float.
+  {
+    // Good: The following transformation should be applicable.
+    TransformationWrapVectorSynonym wrap_mul_float(90, 121, 122, 123, 1);
+    ASSERT_TRUE(
+        wrap_mul_float.IsApplicable(context.get(), transformation_context));
+    ApplyAndCheckFreshIds(wrap_mul_float, context.get(),
+                          &transformation_context);
+
+    // |instruction_id| and id at |scalar_position of the result vector should
+    // be synonyms.
+    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+        MakeDataDescriptor(123, {1}), MakeDataDescriptor(90, {})));
+  }
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %97
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %9 = OpConstant %6 10
+         %11 = OpConstant %6 -5
+         %12 = OpTypeVector %6 2
+         %13 = OpTypePointer Function %12
+         %18 = OpTypeInt 32 0
+         %19 = OpTypePointer Function %18
+         %21 = OpConstant %18 8
+         %23 = OpConstant %18 2
+         %24 = OpTypeVector %18 3
+         %25 = OpTypePointer Function %24
+         %31 = OpTypeFloat 32
+         %32 = OpTypePointer Function %31
+         %34 = OpConstant %31 3.29999995
+         %36 = OpConstant %31 1.10000002
+         %37 = OpTypeVector %31 4
+         %38 = OpTypePointer Function %37
+         %96 = OpTypePointer Input %31
+         %97 = OpVariable %96 Input
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %10 = OpVariable %7 Function
+         %14 = OpVariable %13 Function
+         %20 = OpVariable %19 Function
+         %22 = OpVariable %19 Function
+         %26 = OpVariable %25 Function
+         %33 = OpVariable %32 Function
+         %35 = OpVariable %32 Function
+         %39 = OpVariable %38 Function
+         %47 = OpVariable %7 Function
+         %51 = OpVariable %7 Function
+         %55 = OpVariable %7 Function
+         %59 = OpVariable %7 Function
+         %63 = OpVariable %19 Function
+         %67 = OpVariable %19 Function
+         %71 = OpVariable %19 Function
+         %75 = OpVariable %19 Function
+         %79 = OpVariable %32 Function
+         %83 = OpVariable %32 Function
+         %87 = OpVariable %32 Function
+         %91 = OpVariable %32 Function
+               OpStore %8 %9
+               OpStore %10 %11
+         %15 = OpLoad %6 %8
+         %16 = OpLoad %6 %10
+         %17 = OpCompositeConstruct %12 %15 %16
+               OpStore %14 %17
+               OpStore %20 %21
+               OpStore %22 %23
+         %27 = OpLoad %18 %20
+         %28 = OpLoad %18 %20
+         %29 = OpLoad %18 %22
+         %30 = OpCompositeConstruct %24 %27 %28 %29
+               OpStore %26 %30
+               OpStore %33 %34
+               OpStore %35 %36
+         %40 = OpLoad %31 %33
+         %41 = OpLoad %31 %33
+         %42 = OpLoad %31 %35
+         %43 = OpLoad %31 %35
+         %44 = OpCompositeConstruct %37 %40 %41 %42 %43
+         %45 = OpLoad %37 %39
+         %46 = OpVectorShuffle %37 %45 %44 5 6 7 4
+               OpStore %39 %46
+         %48 = OpLoad %6 %8
+         %49 = OpLoad %6 %10
+         %50 = OpIAdd %6 %48 %49
+               OpStore %47 %50
+         %52 = OpLoad %6 %8
+         %53 = OpLoad %6 %10
+        %100 = OpCompositeConstruct %12 %52 %52
+        %101 = OpCompositeConstruct %12 %53 %53
+        %102 = OpISub %12 %100 %101
+         %54 = OpISub %6 %52 %53
+               OpStore %51 %54
+         %56 = OpLoad %6 %8
+         %57 = OpLoad %6 %10
+        %103 = OpCompositeConstruct %12 %56 %56
+        %104 = OpCompositeConstruct %12 %57 %57
+        %105 = OpIMul %12 %103 %104
+         %58 = OpIMul %6 %56 %57
+               OpStore %55 %58
+         %60 = OpLoad %6 %8
+         %61 = OpLoad %6 %10
+         %62 = OpSDiv %6 %60 %61
+               OpStore %59 %62
+         %64 = OpLoad %18 %20
+         %65 = OpLoad %18 %22
+        %106 = OpCompositeConstruct %24 %64 %64 %64
+        %107 = OpCompositeConstruct %24 %65 %65 %65
+        %108 = OpIAdd %24 %106 %107
+         %66 = OpIAdd %18 %64 %65
+               OpStore %63 %66
+         %68 = OpLoad %18 %20
+         %69 = OpLoad %18 %22
+        %109 = OpCompositeConstruct %24 %68 %68 %68
+        %110 = OpCompositeConstruct %24 %69 %69 %69
+        %111 = OpISub %24 %109 %110
+         %70 = OpISub %18 %68 %69
+               OpStore %67 %70
+         %72 = OpLoad %18 %20
+         %73 = OpLoad %18 %22
+        %112 = OpCompositeConstruct %24 %72 %72 %72
+        %113 = OpCompositeConstruct %24 %73 %73 %73
+        %114 = OpIMul %24 %112 %113
+         %74 = OpIMul %18 %72 %73
+               OpStore %71 %74
+         %76 = OpLoad %18 %20
+         %77 = OpLoad %18 %22
+         %78 = OpUDiv %18 %76 %77
+               OpStore %75 %78
+         %80 = OpLoad %31 %33
+         %81 = OpLoad %31 %35
+        %115 = OpCompositeConstruct %37 %80 %80 %80 %80
+        %116 = OpCompositeConstruct %37 %81 %81 %81 %81
+        %117 = OpFAdd %37 %115 %116
+         %82 = OpFAdd %31 %80 %81
+               OpStore %79 %82
+         %84 = OpLoad %31 %33
+         %85 = OpLoad %31 %35
+        %118 = OpCompositeConstruct %37 %84 %84 %84 %84
+        %119 = OpCompositeConstruct %37 %85 %85 %85 %85
+        %120 = OpFSub %37 %118 %119
+         %86 = OpFSub %31 %84 %85
+               OpStore %83 %86
+         %88 = OpLoad %31 %33
+         %89 = OpLoad %31 %35
+        %121 = OpCompositeConstruct %37 %88 %88 %88 %88
+        %122 = OpCompositeConstruct %37 %89 %89 %89 %89
+        %123 = OpFMul %37 %121 %122
+         %90 = OpFMul %31 %88 %89
+               OpStore %87 %90
+         %92 = OpLoad %31 %33
+         %93 = OpLoad %31 %35
+         %94 = OpFDiv %31 %92 %93
+               OpStore %91 %94
+               OpReturn
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+TEST(TransformationWrapVectorSynonym, DivSupportTest) {
+  std::string shader = R"(
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %97
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 32 1
+          %7 = OpTypePointer Function %6
+          %9 = OpConstant %6 10
+         %11 = OpConstant %6 -5
+         %12 = OpTypeVector %6 2
+         %13 = OpTypePointer Function %12
+         %18 = OpTypeInt 32 0
+         %19 = OpTypePointer Function %18
+         %21 = OpConstant %18 8
+         %23 = OpConstant %18 2
+         %24 = OpTypeVector %18 3
+         %25 = OpTypePointer Function %24
+         %31 = OpTypeFloat 32
+         %32 = OpTypePointer Function %31
+         %34 = OpConstant %31 3.29999995
+         %36 = OpConstant %31 1.10000002
+         %37 = OpTypeVector %31 4
+         %38 = OpTypePointer Function %37
+         %96 = OpTypePointer Input %31
+         %97 = OpVariable %96 Input
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %10 = OpVariable %7 Function
+         %14 = OpVariable %13 Function
+         %20 = OpVariable %19 Function
+         %22 = OpVariable %19 Function
+         %26 = OpVariable %25 Function
+         %33 = OpVariable %32 Function
+         %35 = OpVariable %32 Function
+         %39 = OpVariable %38 Function
+         %47 = OpVariable %7 Function
+         %51 = OpVariable %7 Function
+         %55 = OpVariable %7 Function
+         %59 = OpVariable %7 Function
+         %63 = OpVariable %19 Function
+         %67 = OpVariable %19 Function
+         %71 = OpVariable %19 Function
+         %75 = OpVariable %19 Function
+         %79 = OpVariable %32 Function
+         %83 = OpVariable %32 Function
+         %87 = OpVariable %32 Function
+         %91 = OpVariable %32 Function
+               OpStore %8 %9
+               OpStore %10 %11
+         %15 = OpLoad %6 %8
+         %16 = OpLoad %6 %10
+         %17 = OpCompositeConstruct %12 %15 %16
+               OpStore %14 %17
+               OpStore %20 %21
+               OpStore %22 %23
+         %27 = OpLoad %18 %20
+         %28 = OpLoad %18 %20
+         %29 = OpLoad %18 %22
+         %30 = OpCompositeConstruct %24 %27 %28 %29
+               OpStore %26 %30
+               OpStore %33 %34
+               OpStore %35 %36
+         %40 = OpLoad %31 %33
+         %41 = OpLoad %31 %33
+         %42 = OpLoad %31 %35
+         %43 = OpLoad %31 %35
+         %44 = OpCompositeConstruct %37 %40 %41 %42 %43
+         %45 = OpLoad %37 %39
+         %46 = OpVectorShuffle %37 %45 %44 5 6 7 4
+               OpStore %39 %46
+         %48 = OpLoad %6 %8
+         %49 = OpLoad %6 %10
+         %50 = OpIAdd %6 %48 %49
+               OpStore %47 %50
+         %52 = OpLoad %6 %8
+         %53 = OpLoad %6 %10
+         %54 = OpISub %6 %52 %53
+               OpStore %51 %54
+         %56 = OpLoad %6 %8
+         %57 = OpLoad %6 %10
+         %58 = OpIMul %6 %56 %57
+               OpStore %55 %58
+         %60 = OpLoad %6 %8
+         %61 = OpLoad %6 %10
+        %100 = OpCompositeConstruct %12 %60 %60
+        %101 = OpCompositeConstruct %12 %61 %61
+         %62 = OpSDiv %6 %60 %61
+               OpStore %59 %62
+         %64 = OpLoad %18 %20
+         %65 = OpLoad %18 %22
+         %66 = OpIAdd %18 %64 %65
+               OpStore %63 %66
+         %68 = OpLoad %18 %20
+         %69 = OpLoad %18 %22
+         %70 = OpISub %18 %68 %69
+               OpStore %67 %70
+         %72 = OpLoad %18 %20
+         %73 = OpLoad %18 %22
+         %74 = OpIMul %18 %72 %73
+               OpStore %71 %74
+         %76 = OpLoad %18 %20
+         %77 = OpLoad %18 %22
+        %102 = OpCompositeConstruct %24 %76 %76 %76
+        %103 = OpCompositeConstruct %24 %77 %77 %77
+         %78 = OpUDiv %18 %76 %77
+               OpStore %75 %78
+         %80 = OpLoad %31 %33
+         %81 = OpLoad %31 %35
+         %82 = OpFAdd %31 %80 %81
+               OpStore %79 %82
+         %84 = OpLoad %31 %33
+         %85 = OpLoad %31 %35
+         %86 = OpFSub %31 %84 %85
+               OpStore %83 %86
+         %88 = OpLoad %31 %33
+         %89 = OpLoad %31 %35
+         %90 = OpFMul %31 %88 %89
+               OpStore %87 %90
+         %92 = OpLoad %31 %33
+         %93 = OpLoad %31 %35
+        %104 = OpCompositeConstruct %37 %92 %92 %92 %92
+        %105 = OpCompositeConstruct %37 %93 %93 %93 %93
+         %94 = OpFDiv %31 %92 %93
+               OpStore %91 %94
+               OpReturn
+               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;
+
+  ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
+                                               kConsoleMessageConsumer));
+
+  TransformationContext transformation_context(
+      MakeUnique<FactManager>(context.get()), validator_options);
+
+  transformation_context.GetFactManager()->AddFactDataSynonym(
+      MakeDataDescriptor(100, {1}), MakeDataDescriptor(60, {}));
+  transformation_context.GetFactManager()->AddFactDataSynonym(
+      MakeDataDescriptor(101, {1}), MakeDataDescriptor(61, {}));
+
+  transformation_context.GetFactManager()->AddFactDataSynonym(
+      MakeDataDescriptor(102, {1}), MakeDataDescriptor(76, {}));
+  transformation_context.GetFactManager()->AddFactDataSynonym(
+      MakeDataDescriptor(103, {1}), MakeDataDescriptor(77, {}));
+
+  transformation_context.GetFactManager()->AddFactDataSynonym(
+      MakeDataDescriptor(104, {1}), MakeDataDescriptor(92, {}));
+  transformation_context.GetFactManager()->AddFactDataSynonym(
+      MakeDataDescriptor(105, {1}), MakeDataDescriptor(93, {}));
+
+  // Div operations are not currently supported.
+  {
+    TransformationWrapVectorSynonym wrap_div_bad1(62, 100, 101, 106, 1);
+    ASSERT_FALSE(
+        wrap_div_bad1.IsApplicable(context.get(), transformation_context));
+
+    TransformationWrapVectorSynonym wrap_div_bad2(78, 102, 103, 106, 1);
+    ASSERT_FALSE(
+        wrap_div_bad2.IsApplicable(context.get(), transformation_context));
+
+    TransformationWrapVectorSynonym wrap_div_bad3(94, 104, 105, 106, 1);
+    ASSERT_FALSE(
+        wrap_div_bad3.IsApplicable(context.get(), transformation_context));
+  }
+}
+
+TEST(TransformationWrapVectorSynonym, AdditionalWidthSupportTest) {
+  std::string shader = R"(
+               OpCapability Shader
+               OpCapability Int64
+               OpCapability Float64
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %97
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 64 1
+          %7 = OpTypePointer Function %6
+          %9 = OpConstant %6 10
+         %11 = OpConstant %6 -5
+         %12 = OpTypeVector %6 2
+         %13 = OpTypePointer Function %12
+         %18 = OpTypeInt 64 0
+         %19 = OpTypePointer Function %18
+         %21 = OpConstant %18 8
+         %23 = OpConstant %18 2
+         %24 = OpTypeVector %18 3
+         %25 = OpTypePointer Function %24
+         %31 = OpTypeFloat 64
+         %32 = OpTypePointer Function %31
+         %34 = OpConstant %31 3.29999995
+         %36 = OpConstant %31 1.10000002
+         %37 = OpTypeVector %31 4
+         %38 = OpTypePointer Function %37
+         %96 = OpTypePointer Input %31
+         %97 = OpVariable %96 Input
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %10 = OpVariable %7 Function
+         %14 = OpVariable %13 Function
+         %20 = OpVariable %19 Function
+         %22 = OpVariable %19 Function
+         %26 = OpVariable %25 Function
+         %33 = OpVariable %32 Function
+         %35 = OpVariable %32 Function
+         %39 = OpVariable %38 Function
+         %47 = OpVariable %7 Function
+         %51 = OpVariable %7 Function
+         %55 = OpVariable %7 Function
+         %59 = OpVariable %7 Function
+         %63 = OpVariable %19 Function
+         %67 = OpVariable %19 Function
+         %71 = OpVariable %19 Function
+         %75 = OpVariable %19 Function
+         %79 = OpVariable %32 Function
+         %83 = OpVariable %32 Function
+         %87 = OpVariable %32 Function
+         %91 = OpVariable %32 Function
+               OpStore %8 %9
+               OpStore %10 %11
+         %15 = OpLoad %6 %8
+         %16 = OpLoad %6 %10
+         %17 = OpCompositeConstruct %12 %15 %16
+               OpStore %14 %17
+               OpStore %20 %21
+               OpStore %22 %23
+         %27 = OpLoad %18 %20
+         %28 = OpLoad %18 %20
+         %29 = OpLoad %18 %22
+         %30 = OpCompositeConstruct %24 %27 %28 %29
+               OpStore %26 %30
+               OpStore %33 %34
+               OpStore %35 %36
+         %40 = OpLoad %31 %33
+         %41 = OpLoad %31 %33
+         %42 = OpLoad %31 %35
+         %43 = OpLoad %31 %35
+         %44 = OpCompositeConstruct %37 %40 %41 %42 %43
+         %45 = OpLoad %37 %39
+         %46 = OpVectorShuffle %37 %45 %44 5 6 7 4
+               OpStore %39 %46
+         %48 = OpLoad %6 %8
+         %49 = OpLoad %6 %10
+        %100 = OpCompositeConstruct %12 %48 %48
+        %101 = OpCompositeConstruct %12 %49 %49
+         %50 = OpIAdd %6 %48 %49
+               OpStore %47 %50
+         %52 = OpLoad %6 %8
+         %53 = OpLoad %6 %10
+         %54 = OpISub %6 %52 %53
+               OpStore %51 %54
+         %56 = OpLoad %6 %8
+         %57 = OpLoad %6 %10
+         %58 = OpIMul %6 %56 %57
+               OpStore %55 %58
+         %60 = OpLoad %6 %8
+         %61 = OpLoad %6 %10
+         %62 = OpSDiv %6 %60 %61
+               OpStore %59 %62
+         %64 = OpLoad %18 %20
+         %65 = OpLoad %18 %22
+         %66 = OpIAdd %18 %64 %65
+               OpStore %63 %66
+         %68 = OpLoad %18 %20
+         %69 = OpLoad %18 %22
+        %103 = OpCompositeConstruct %24 %68 %68 %68
+        %104 = OpCompositeConstruct %24 %69 %69 %69
+         %70 = OpISub %18 %68 %69
+               OpStore %67 %70
+         %72 = OpLoad %18 %20
+         %73 = OpLoad %18 %22
+         %74 = OpIMul %18 %72 %73
+               OpStore %71 %74
+         %76 = OpLoad %18 %20
+         %77 = OpLoad %18 %22
+         %78 = OpUDiv %18 %76 %77
+               OpStore %75 %78
+         %80 = OpLoad %31 %33
+         %81 = OpLoad %31 %35
+         %82 = OpFAdd %31 %80 %81
+               OpStore %79 %82
+         %84 = OpLoad %31 %33
+         %85 = OpLoad %31 %35
+         %86 = OpFSub %31 %84 %85
+               OpStore %83 %86
+         %88 = OpLoad %31 %33
+         %89 = OpLoad %31 %35
+        %106 = OpCompositeConstruct %37 %88 %88 %88 %88
+        %107 = OpCompositeConstruct %37 %89 %89 %89 %89
+         %90 = OpFMul %31 %88 %89
+               OpStore %87 %90
+         %92 = OpLoad %31 %33
+         %93 = OpLoad %31 %35
+         %94 = OpFDiv %31 %92 %93
+               OpStore %91 %94
+               OpReturn
+               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);
+
+  // Vec Type Id |   Vector Type  |  Element Type id |   Element Type  |
+  // ------------+----------------+------------------+-----------------+
+  //     12      |      vec2      |         6        |      int64      |
+  //     24      |      vec3      |        18        |     uint64      |
+  //     37      |      vec4      |        31        |    float64      |
+
+  // Test support for 64-bit signed int.
+  {
+    // Assert that the target scalar instruction result id is relevant.
+    ASSERT_FALSE(transformation_context.GetFactManager()->IdIsIrrelevant(50));
+    transformation_context.GetFactManager()->AddFactDataSynonym(
+        MakeDataDescriptor(100, {1}), MakeDataDescriptor(48, {}));
+    transformation_context.GetFactManager()->AddFactDataSynonym(
+        MakeDataDescriptor(101, {1}), MakeDataDescriptor(49, {}));
+
+    // Good: The following transformation should be applicable.
+    TransformationWrapVectorSynonym wrap_add_int64(50, 100, 101, 102, 1);
+    ASSERT_TRUE(
+        wrap_add_int64.IsApplicable(context.get(), transformation_context));
+    // Insert an arithmetic instruction of the same type to add two vectors.
+    ApplyAndCheckFreshIds(wrap_add_int64, context.get(),
+                          &transformation_context);
+
+    // |instruction_id| and id at |scalar_position of the result vector should
+    // be synonyms.
+    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+        MakeDataDescriptor(102, {1}), MakeDataDescriptor(50, {})));
+  }
+
+  // Test support for 64-bit unsigned int.
+  {
+    ASSERT_FALSE(transformation_context.GetFactManager()->IdIsIrrelevant(70));
+    transformation_context.GetFactManager()->AddFactDataSynonym(
+        MakeDataDescriptor(103, {2}), MakeDataDescriptor(68, {}));
+    transformation_context.GetFactManager()->AddFactDataSynonym(
+        MakeDataDescriptor(104, {2}), MakeDataDescriptor(69, {}));
+
+    // Good: The following transformation should be applicable.
+    TransformationWrapVectorSynonym wrap_sub_uint64(70, 103, 104, 105, 2);
+    ASSERT_TRUE(
+        wrap_sub_uint64.IsApplicable(context.get(), transformation_context));
+
+    ApplyAndCheckFreshIds(wrap_sub_uint64, context.get(),
+                          &transformation_context);
+
+    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+        MakeDataDescriptor(105, {2}), MakeDataDescriptor(70, {})));
+  }
+
+  // Test support for 64-bit float.
+  {
+    ASSERT_FALSE(transformation_context.GetFactManager()->IdIsIrrelevant(90));
+    transformation_context.GetFactManager()->AddFactDataSynonym(
+        MakeDataDescriptor(106, {3}), MakeDataDescriptor(88, {}));
+    transformation_context.GetFactManager()->AddFactDataSynonym(
+        MakeDataDescriptor(107, {3}), MakeDataDescriptor(89, {}));
+
+    // Good: The following transformation should be applicable.
+    TransformationWrapVectorSynonym wrap_mul_float64(90, 106, 107, 108, 3);
+    ASSERT_TRUE(
+        wrap_mul_float64.IsApplicable(context.get(), transformation_context));
+
+    ApplyAndCheckFreshIds(wrap_mul_float64, context.get(),
+                          &transformation_context);
+
+    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
+        MakeDataDescriptor(108, {3}), MakeDataDescriptor(90, {})));
+  }
+
+  std::string after_transformation = R"(
+               OpCapability Shader
+               OpCapability Int64
+               OpCapability Float64
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Fragment %4 "main" %97
+               OpExecutionMode %4 OriginUpperLeft
+               OpSource ESSL 310
+               OpName %4 "main"
+          %2 = OpTypeVoid
+          %3 = OpTypeFunction %2
+          %6 = OpTypeInt 64 1
+          %7 = OpTypePointer Function %6
+          %9 = OpConstant %6 10
+         %11 = OpConstant %6 -5
+         %12 = OpTypeVector %6 2
+         %13 = OpTypePointer Function %12
+         %18 = OpTypeInt 64 0
+         %19 = OpTypePointer Function %18
+         %21 = OpConstant %18 8
+         %23 = OpConstant %18 2
+         %24 = OpTypeVector %18 3
+         %25 = OpTypePointer Function %24
+         %31 = OpTypeFloat 64
+         %32 = OpTypePointer Function %31
+         %34 = OpConstant %31 3.29999995
+         %36 = OpConstant %31 1.10000002
+         %37 = OpTypeVector %31 4
+         %38 = OpTypePointer Function %37
+         %96 = OpTypePointer Input %31
+         %97 = OpVariable %96 Input
+          %4 = OpFunction %2 None %3
+          %5 = OpLabel
+          %8 = OpVariable %7 Function
+         %10 = OpVariable %7 Function
+         %14 = OpVariable %13 Function
+         %20 = OpVariable %19 Function
+         %22 = OpVariable %19 Function
+         %26 = OpVariable %25 Function
+         %33 = OpVariable %32 Function
+         %35 = OpVariable %32 Function
+         %39 = OpVariable %38 Function
+         %47 = OpVariable %7 Function
+         %51 = OpVariable %7 Function
+         %55 = OpVariable %7 Function
+         %59 = OpVariable %7 Function
+         %63 = OpVariable %19 Function
+         %67 = OpVariable %19 Function
+         %71 = OpVariable %19 Function
+         %75 = OpVariable %19 Function
+         %79 = OpVariable %32 Function
+         %83 = OpVariable %32 Function
+         %87 = OpVariable %32 Function
+         %91 = OpVariable %32 Function
+               OpStore %8 %9
+               OpStore %10 %11
+         %15 = OpLoad %6 %8
+         %16 = OpLoad %6 %10
+         %17 = OpCompositeConstruct %12 %15 %16
+               OpStore %14 %17
+               OpStore %20 %21
+               OpStore %22 %23
+         %27 = OpLoad %18 %20
+         %28 = OpLoad %18 %20
+         %29 = OpLoad %18 %22
+         %30 = OpCompositeConstruct %24 %27 %28 %29
+               OpStore %26 %30
+               OpStore %33 %34
+               OpStore %35 %36
+         %40 = OpLoad %31 %33
+         %41 = OpLoad %31 %33
+         %42 = OpLoad %31 %35
+         %43 = OpLoad %31 %35
+         %44 = OpCompositeConstruct %37 %40 %41 %42 %43
+         %45 = OpLoad %37 %39
+         %46 = OpVectorShuffle %37 %45 %44 5 6 7 4
+               OpStore %39 %46
+         %48 = OpLoad %6 %8
+         %49 = OpLoad %6 %10
+        %100 = OpCompositeConstruct %12 %48 %48
+        %101 = OpCompositeConstruct %12 %49 %49
+        %102 = OpIAdd %12 %100 %101
+         %50 = OpIAdd %6 %48 %49
+               OpStore %47 %50
+         %52 = OpLoad %6 %8
+         %53 = OpLoad %6 %10
+         %54 = OpISub %6 %52 %53
+               OpStore %51 %54
+         %56 = OpLoad %6 %8
+         %57 = OpLoad %6 %10
+         %58 = OpIMul %6 %56 %57
+               OpStore %55 %58
+         %60 = OpLoad %6 %8
+         %61 = OpLoad %6 %10
+         %62 = OpSDiv %6 %60 %61
+               OpStore %59 %62
+         %64 = OpLoad %18 %20
+         %65 = OpLoad %18 %22
+         %66 = OpIAdd %18 %64 %65
+               OpStore %63 %66
+         %68 = OpLoad %18 %20
+         %69 = OpLoad %18 %22
+        %103 = OpCompositeConstruct %24 %68 %68 %68
+        %104 = OpCompositeConstruct %24 %69 %69 %69
+        %105 = OpISub %24 %103 %104
+         %70 = OpISub %18 %68 %69
+               OpStore %67 %70
+         %72 = OpLoad %18 %20
+         %73 = OpLoad %18 %22
+         %74 = OpIMul %18 %72 %73
+               OpStore %71 %74
+         %76 = OpLoad %18 %20
+         %77 = OpLoad %18 %22
+         %78 = OpUDiv %18 %76 %77
+               OpStore %75 %78
+         %80 = OpLoad %31 %33
+         %81 = OpLoad %31 %35
+         %82 = OpFAdd %31 %80 %81
+               OpStore %79 %82
+         %84 = OpLoad %31 %33
+         %85 = OpLoad %31 %35
+         %86 = OpFSub %31 %84 %85
+               OpStore %83 %86
+         %88 = OpLoad %31 %33
+         %89 = OpLoad %31 %35
+        %106 = OpCompositeConstruct %37 %88 %88 %88 %88
+        %107 = OpCompositeConstruct %37 %89 %89 %89 %89
+        %108 = OpFMul %37 %106 %107
+         %90 = OpFMul %31 %88 %89
+               OpStore %87 %90
+         %92 = OpLoad %31 %33
+         %93 = OpLoad %31 %35
+         %94 = OpFDiv %31 %92 %93
+               OpStore %91 %94
+               OpReturn
+               OpFunctionEnd
+  )";
+  ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
+}
+
+}  // namespace
+}  // namespace fuzz
+}  // namespace spvtools