spirv-fuzz: Rework id descriptors (#2959)

A refactoring that separates the identification of an instruction from
the identification of a use in an instruction, to enable the former to
be used independently of the latter.
diff --git a/source/fuzz/CMakeLists.txt b/source/fuzz/CMakeLists.txt
index a555d38..11c9219 100644
--- a/source/fuzz/CMakeLists.txt
+++ b/source/fuzz/CMakeLists.txt
@@ -50,6 +50,7 @@
         fuzzer_pass_split_blocks.h
         fuzzer_util.h
         id_use_descriptor.h
+        instruction_descriptor.h
         protobufs/spirvfuzz_protobufs.h
         pseudo_random_generator.h
         random_generator.h
@@ -99,6 +100,7 @@
         fuzzer_pass_split_blocks.cpp
         fuzzer_util.cpp
         id_use_descriptor.cpp
+        instruction_descriptor.cpp
         pseudo_random_generator.cpp
         random_generator.cpp
         replayer.cpp
diff --git a/source/fuzz/force_render_red.cpp b/source/fuzz/force_render_red.cpp
index 3bc1172..46e23e8 100644
--- a/source/fuzz/force_render_red.cpp
+++ b/source/fuzz/force_render_red.cpp
@@ -15,6 +15,7 @@
 #include "source/fuzz/force_render_red.h"
 
 #include "source/fuzz/fact_manager.h"
+#include "source/fuzz/instruction_descriptor.h"
 #include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
 #include "source/fuzz/transformation_replace_constant_with_uniform.h"
 #include "source/fuzz/uniform_buffer_element_descriptor.h"
@@ -147,9 +148,10 @@
                                uint32_t greater_than_instruction,
                                uint32_t in_operand_index) {
   return MakeUnique<TransformationReplaceConstantWithUniform>(
-      transformation::MakeIdUseDescriptor(constant_id, SpvOpFOrdGreaterThan,
-                                          in_operand_index,
-                                          greater_than_instruction, 0),
+      MakeIdUseDescriptor(constant_id,
+                          MakeInstructionDescriptor(greater_than_instruction,
+                                                    SpvOpFOrdGreaterThan, 0),
+                          in_operand_index),
       fact_manager.GetUniformDescriptorsForConstant(ir_context, constant_id)[0],
       ir_context->TakeNextId(), ir_context->TakeNextId());
 }
diff --git a/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp b/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp
index 250bcc2..ce13837 100644
--- a/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp
+++ b/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp
@@ -85,8 +85,8 @@
                     : GetFuzzerContext()->GetFreshId();
 
             TransformationReplaceIdWithSynonym replace_id_transformation(
-                transformation::MakeIdUseDescriptorFromUse(
-                    GetIRContext(), use_inst, use_in_operand_index),
+                MakeIdUseDescriptorFromUse(GetIRContext(), use_inst,
+                                           use_in_operand_index),
                 *synonym_to_try, fresh_id_for_temporary);
 
             // The transformation should be applicable by construction.
diff --git a/source/fuzz/fuzzer_pass_obfuscate_constants.cpp b/source/fuzz/fuzzer_pass_obfuscate_constants.cpp
index 484c9b8..3df11ae 100644
--- a/source/fuzz/fuzzer_pass_obfuscate_constants.cpp
+++ b/source/fuzz/fuzzer_pass_obfuscate_constants.cpp
@@ -16,6 +16,7 @@
 
 #include <cmath>
 
+#include "source/fuzz/instruction_descriptor.h"
 #include "source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h"
 #include "source/fuzz/transformation_replace_constant_with_uniform.h"
 #include "source/opt/ir_context.h"
@@ -102,10 +103,11 @@
     // We randomly decide, based on the current depth of obfuscation, whether
     // to further obfuscate this operand.
     if (GetFuzzerContext()->GoDeeperInConstantObfuscation(depth)) {
-      auto in_operand_use = transformation::MakeIdUseDescriptor(
+      auto in_operand_use = MakeIdUseDescriptor(
           binary_operator_instruction->GetSingleWordInOperand(index),
-          binary_operator_instruction->opcode(), index,
-          binary_operator_instruction->result_id(), 0);
+          MakeInstructionDescriptor(binary_operator_instruction->result_id(),
+                                    binary_operator_instruction->opcode(), 0),
+          index);
       ObfuscateConstant(depth + 1, in_operand_use);
     }
   }
@@ -366,14 +368,17 @@
       // it.
       protobufs::IdUseDescriptor id_use_descriptor;
       id_use_descriptor.set_id_of_interest(operand_id);
-      id_use_descriptor.set_target_instruction_opcode(inst.opcode());
+      id_use_descriptor.mutable_enclosing_instruction()
+          ->set_target_instruction_opcode(inst.opcode());
+      id_use_descriptor.mutable_enclosing_instruction()
+          ->set_base_instruction_result_id(base_instruction_result_id);
+      id_use_descriptor.mutable_enclosing_instruction()
+          ->set_num_opcodes_to_ignore(
+              skipped_opcode_count.find(inst.opcode()) ==
+                      skipped_opcode_count.end()
+                  ? 0
+                  : skipped_opcode_count.at(inst.opcode()));
       id_use_descriptor.set_in_operand_index(in_operand_index);
-      id_use_descriptor.set_base_instruction_result_id(
-          base_instruction_result_id);
-      id_use_descriptor.set_num_opcodes_to_ignore(
-          skipped_opcode_count.find(inst.opcode()) == skipped_opcode_count.end()
-              ? 0
-              : skipped_opcode_count.at(inst.opcode()));
       constant_uses->push_back(id_use_descriptor);
     } break;
     default:
diff --git a/source/fuzz/id_use_descriptor.cpp b/source/fuzz/id_use_descriptor.cpp
index 8693a7d..105c310 100644
--- a/source/fuzz/id_use_descriptor.cpp
+++ b/source/fuzz/id_use_descriptor.cpp
@@ -14,71 +14,41 @@
 
 #include "source/fuzz/id_use_descriptor.h"
 
+#include "source/fuzz/instruction_descriptor.h"
+
 namespace spvtools {
 namespace fuzz {
 
-opt::Instruction* transformation::FindInstruction(
-    const protobufs::IdUseDescriptor& descriptor,
-    spvtools::opt::IRContext* context) {
-  for (auto& function : *context->module()) {
-    for (auto& block : function) {
-      bool found_base = block.id() == descriptor.base_instruction_result_id();
-      uint32_t num_ignored = 0;
-      for (auto& instruction : block) {
-        if (instruction.HasResultId() &&
-            instruction.result_id() ==
-                descriptor.base_instruction_result_id()) {
-          assert(!found_base &&
-                 "It should not be possible to find the base instruction "
-                 "multiple times.");
-          found_base = true;
-          assert(num_ignored == 0 &&
-                 "The skipped instruction count should only be incremented "
-                 "after the instruction base has been found.");
-        }
-        if (found_base &&
-            instruction.opcode() == descriptor.target_instruction_opcode()) {
-          if (num_ignored == descriptor.num_opcodes_to_ignore()) {
-            if (descriptor.in_operand_index() >= instruction.NumInOperands()) {
-              return nullptr;
-            }
-            auto in_operand =
-                instruction.GetInOperand(descriptor.in_operand_index());
-            if (in_operand.type != SPV_OPERAND_TYPE_ID) {
-              return nullptr;
-            }
-            if (in_operand.words[0] != descriptor.id_of_interest()) {
-              return nullptr;
-            }
-            return &instruction;
-          }
-          num_ignored++;
-        }
-      }
-      if (found_base) {
-        // We found the base instruction, but did not find the target
-        // instruction in the same block.
-        return nullptr;
-      }
-    }
+opt::Instruction* FindInstructionContainingUse(
+    const protobufs::IdUseDescriptor& id_use_descriptor,
+    opt::IRContext* context) {
+  auto result =
+      FindInstruction(id_use_descriptor.enclosing_instruction(), context);
+  if (!result) {
+    return nullptr;
   }
-  return nullptr;
-}
-
-protobufs::IdUseDescriptor transformation::MakeIdUseDescriptor(
-    uint32_t id_of_interest, SpvOp target_instruction_opcode,
-    uint32_t in_operand_index, uint32_t base_instruction_result_id,
-    uint32_t num_opcodes_to_ignore) {
-  protobufs::IdUseDescriptor result;
-  result.set_id_of_interest(id_of_interest);
-  result.set_target_instruction_opcode(target_instruction_opcode);
-  result.set_in_operand_index(in_operand_index);
-  result.set_base_instruction_result_id(base_instruction_result_id);
-  result.set_num_opcodes_to_ignore(num_opcodes_to_ignore);
+  if (id_use_descriptor.in_operand_index() >= result->NumInOperands()) {
+    return nullptr;
+  }
+  if (result->GetSingleWordInOperand(id_use_descriptor.in_operand_index()) !=
+      id_use_descriptor.id_of_interest()) {
+    return nullptr;
+  }
   return result;
 }
 
-protobufs::IdUseDescriptor transformation::MakeIdUseDescriptorFromUse(
+protobufs::IdUseDescriptor MakeIdUseDescriptor(
+    uint32_t id_of_interest,
+    const protobufs::InstructionDescriptor& enclosing_instruction,
+    uint32_t in_operand_index) {
+  protobufs::IdUseDescriptor result;
+  result.set_id_of_interest(id_of_interest);
+  *result.mutable_enclosing_instruction() = enclosing_instruction;
+  result.set_in_operand_index(in_operand_index);
+  return result;
+}
+
+protobufs::IdUseDescriptor MakeIdUseDescriptorFromUse(
     opt::IRContext* context, opt::Instruction* inst,
     uint32_t in_operand_index) {
   auto in_operand = inst->GetInOperand(in_operand_index);
@@ -94,9 +64,11 @@
       num_opcodes_to_ignore = 0;
     }
     if (&inst_in_block == inst) {
-      return MakeIdUseDescriptor(id_of_interest, inst->opcode(),
-                                 in_operand_index, base_instruction_result_id,
-                                 num_opcodes_to_ignore);
+      return MakeIdUseDescriptor(
+          id_of_interest,
+          MakeInstructionDescriptor(base_instruction_result_id, inst->opcode(),
+                                    num_opcodes_to_ignore),
+          in_operand_index);
     }
     if (inst_in_block.opcode() == inst->opcode()) {
       num_opcodes_to_ignore++;
diff --git a/source/fuzz/id_use_descriptor.h b/source/fuzz/id_use_descriptor.h
index 1657cb9..d18bb66 100644
--- a/source/fuzz/id_use_descriptor.h
+++ b/source/fuzz/id_use_descriptor.h
@@ -12,28 +12,28 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#ifndef SOURCE_FUZZ_ID_USE_LOCATOR_H_
-#define SOURCE_FUZZ_ID_USE_LOCATOR_H_
+#ifndef SOURCE_FUZZ_ID_USE_DESCRIPTOR_H_
+#define SOURCE_FUZZ_ID_USE_DESCRIPTOR_H_
 
 #include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
 #include "source/opt/ir_context.h"
 
 namespace spvtools {
 namespace fuzz {
-namespace transformation {
 
-// Looks for an instruction in |context| such that the id use represented by
-// |descriptor| is one of the operands to said instruction.  Returns |nullptr|
-// if no such instruction can be found.
-opt::Instruction* FindInstruction(const protobufs::IdUseDescriptor& descriptor,
-                                  opt::IRContext* context);
+// Looks for an instruction in |context| that contains a use
+// identified by |id_use_descriptor|.
+// Returns |nullptr| if no such instruction can be found.
+opt::Instruction* FindInstructionContainingUse(
+    const protobufs::IdUseDescriptor& id_use_descriptor,
+    opt::IRContext* context);
 
 // Creates an IdUseDescriptor protobuf message from the given components.
 // See the protobuf definition for details of what these components mean.
 protobufs::IdUseDescriptor MakeIdUseDescriptor(
-    uint32_t id_of_interest, SpvOp target_instruction_opcode,
-    uint32_t in_operand_index, uint32_t base_instruction_result_id,
-    uint32_t num_opcodes_to_ignore);
+    uint32_t id_of_interest,
+    const protobufs::InstructionDescriptor& enclosing_instruction,
+    uint32_t in_operand_index);
 
 // Given an id use, represented by the instruction |inst| that uses the id, and
 // the input operand index |in_operand_index| associated with the usage, returns
@@ -41,8 +41,7 @@
 protobufs::IdUseDescriptor MakeIdUseDescriptorFromUse(
     opt::IRContext* context, opt::Instruction* inst, uint32_t in_operand_index);
 
-}  // namespace transformation
 }  // namespace fuzz
 }  // namespace spvtools
 
-#endif  // SOURCE_FUZZ_ID_USE_LOCATOR_H_
+#endif  // SOURCE_FUZZ_ID_USE_DESCRIPTOR_H_
diff --git a/source/fuzz/instruction_descriptor.cpp b/source/fuzz/instruction_descriptor.cpp
new file mode 100644
index 0000000..bd48d48
--- /dev/null
+++ b/source/fuzz/instruction_descriptor.cpp
@@ -0,0 +1,70 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "source/fuzz/instruction_descriptor.h"
+
+namespace spvtools {
+namespace fuzz {
+
+opt::Instruction* FindInstruction(
+    const protobufs::InstructionDescriptor& instruction_descriptor,
+    spvtools::opt::IRContext* context) {
+  for (auto& function : *context->module()) {
+    for (auto& block : function) {
+      bool found_base =
+          block.id() == instruction_descriptor.base_instruction_result_id();
+      uint32_t num_ignored = 0;
+      for (auto& instruction : block) {
+        if (instruction.HasResultId() &&
+            instruction.result_id() ==
+                instruction_descriptor.base_instruction_result_id()) {
+          assert(!found_base &&
+                 "It should not be possible to find the base instruction "
+                 "multiple times.");
+          found_base = true;
+          assert(num_ignored == 0 &&
+                 "The skipped instruction count should only be incremented "
+                 "after the instruction base has been found.");
+        }
+        if (found_base &&
+            instruction.opcode() ==
+                instruction_descriptor.target_instruction_opcode()) {
+          if (num_ignored == instruction_descriptor.num_opcodes_to_ignore()) {
+            return &instruction;
+          }
+          num_ignored++;
+        }
+      }
+      if (found_base) {
+        // We found the base instruction, but did not find the target
+        // instruction in the same block.
+        return nullptr;
+      }
+    }
+  }
+  return nullptr;
+}
+
+protobufs::InstructionDescriptor MakeInstructionDescriptor(
+    uint32_t base_instruction_result_id, SpvOp target_instruction_opcode,
+    uint32_t num_opcodes_to_ignore) {
+  protobufs::InstructionDescriptor result;
+  result.set_base_instruction_result_id(base_instruction_result_id);
+  result.set_target_instruction_opcode(target_instruction_opcode);
+  result.set_num_opcodes_to_ignore(num_opcodes_to_ignore);
+  return result;
+}
+
+}  // namespace fuzz
+}  // namespace spvtools
diff --git a/source/fuzz/instruction_descriptor.h b/source/fuzz/instruction_descriptor.h
new file mode 100644
index 0000000..1164318
--- /dev/null
+++ b/source/fuzz/instruction_descriptor.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SOURCE_FUZZ_INSTRUCTION_DESCRIPTOR_H_
+#define SOURCE_FUZZ_INSTRUCTION_DESCRIPTOR_H_
+
+#include "source/fuzz/protobufs/spirvfuzz_protobufs.h"
+#include "source/opt/ir_context.h"
+
+namespace spvtools {
+namespace fuzz {
+
+// Looks for an instruction in |context| corresponding to |descriptor|.
+// Returns |nullptr| if no such instruction can be found.
+opt::Instruction* FindInstruction(
+    const protobufs::InstructionDescriptor& instruction_descriptor,
+    opt::IRContext* context);
+
+// Creates an InstructionDescriptor protobuf message from the given
+// components.  See the protobuf definition for details of what these
+// components mean.
+protobufs::InstructionDescriptor MakeInstructionDescriptor(
+    uint32_t base_instruction_result_id, SpvOp target_instruction_opcode,
+    uint32_t num_opcodes_to_ignore);
+
+}  // namespace fuzz
+}  // namespace spvtools
+
+#endif  // SOURCE_FUZZ_INSTRUCTION_DESCRIPTOR_H_
diff --git a/source/fuzz/protobufs/spvtoolsfuzz.proto b/source/fuzz/protobufs/spvtoolsfuzz.proto
index cf2c5de..1e4527c 100644
--- a/source/fuzz/protobufs/spvtoolsfuzz.proto
+++ b/source/fuzz/protobufs/spvtoolsfuzz.proto
@@ -21,6 +21,24 @@
 
 package spvtools.fuzz.protobufs;
 
+message InstructionDescriptor {
+
+  // Describes an instruction in some block of a function with respect to a
+  // base instruction.
+
+  // The id of an instruction after which the instruction being described is
+  // believed to be located.  It might be the using instruction itself.
+  uint32 base_instruction_result_id = 1;
+
+  // The opcode for the instruction being described.
+  uint32 target_instruction_opcode = 2;
+
+  // The number of matching opcodes to skip over when searching from the base
+  // instruction to the instruction being described.
+  uint32 num_opcodes_to_ignore = 3;
+
+}
+
 message IdUseDescriptor {
 
   // Describes a use of an id as an input operand to an instruction in some
@@ -28,10 +46,12 @@
 
   // Example:
   //   - id_of_interest = 42
-  //   - target_instruction_opcode = OpStore
+  //   - enclosing_instruction = (
+  //         base_instruction_result_id = 50,
+  //         target_instruction_opcode = OpStore
+  //         num_opcodes_to_ignore = 7
+  //     )
   //   - in_operand_index = 1
-  //   - base_instruction_result_id = 50
-  //   - num_opcodes_to_ignore = 7
   // represents a use of id 42 as input operand 1 to an OpStore instruction,
   // such that the OpStore instruction can be found in the same basic block as
   // the instruction with result id 50, and in particular is the 8th OpStore
@@ -41,20 +61,11 @@
   // An id that we would like to be able to find a use of.
   uint32 id_of_interest = 1;
 
-  // The opcode for the instruction that uses the id.
-  uint32 target_instruction_opcode = 2;
-
   // The input operand index at which the use is expected.
+  InstructionDescriptor enclosing_instruction = 2;
+
   uint32 in_operand_index = 3;
 
-  // The id of an instruction after which the instruction that contains the use
-  // is believed to occur; it might be the using instruction itself.
-  uint32 base_instruction_result_id = 4;
-
-  // The number of matching opcodes to skip over when searching for the using
-  // instruction from the base instruction.
-  uint32 num_opcodes_to_ignore = 5;
-
 }
 
 message DataDescriptor {
diff --git a/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.cpp b/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.cpp
index 0326814..b097767 100644
--- a/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.cpp
+++ b/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.cpp
@@ -238,7 +238,7 @@
 
   // The id use descriptor must identify some instruction
   auto instruction =
-      transformation::FindInstruction(message_.id_use_descriptor(), context);
+      FindInstructionContainingUse(message_.id_use_descriptor(), context);
   if (instruction == nullptr) {
     return false;
   }
@@ -268,7 +268,7 @@
       message_.fresh_id_for_binary_operation(), operands);
   opt::Instruction* result = binary_instruction.get();
   auto instruction_containing_constant_use =
-      transformation::FindInstruction(message_.id_use_descriptor(), context);
+      FindInstructionContainingUse(message_.id_use_descriptor(), context);
 
   // We want to insert the new instruction before the instruction that contains
   // the use of the boolean, but we need to go backwards one more instruction if
diff --git a/source/fuzz/transformation_replace_constant_with_uniform.cpp b/source/fuzz/transformation_replace_constant_with_uniform.cpp
index 48334ba..405776e 100644
--- a/source/fuzz/transformation_replace_constant_with_uniform.cpp
+++ b/source/fuzz/transformation_replace_constant_with_uniform.cpp
@@ -149,7 +149,7 @@
   // The id use descriptor must identify some instruction with respect to the
   // module.
   auto instruction_using_constant =
-      transformation::FindInstruction(message_.id_use_descriptor(), context);
+      FindInstructionContainingUse(message_.id_use_descriptor(), context);
   if (!instruction_using_constant) {
     return false;
   }
@@ -188,7 +188,7 @@
     spvtools::fuzz::FactManager* /*unused*/) const {
   // Get the instruction that contains the id use we wish to replace.
   auto instruction_containing_constant_use =
-      transformation::FindInstruction(message_.id_use_descriptor(), context);
+      FindInstructionContainingUse(message_.id_use_descriptor(), context);
   assert(instruction_containing_constant_use &&
          "Precondition requires that the id use can be found.");
   assert(instruction_containing_constant_use->GetSingleWordInOperand(
diff --git a/source/fuzz/transformation_replace_id_with_synonym.cpp b/source/fuzz/transformation_replace_id_with_synonym.cpp
index c96a519..3eb66b9 100644
--- a/source/fuzz/transformation_replace_id_with_synonym.cpp
+++ b/source/fuzz/transformation_replace_id_with_synonym.cpp
@@ -62,7 +62,7 @@
 
   // Does the id use descriptor in the transformation identify an instruction?
   auto use_instruction =
-      transformation::FindInstruction(message_.id_use_descriptor(), context);
+      FindInstructionContainingUse(message_.id_use_descriptor(), context);
   if (!use_instruction) {
     return false;
   }
@@ -101,7 +101,7 @@
     spvtools::opt::IRContext* context,
     spvtools::fuzz::FactManager* /*unused*/) const {
   auto instruction_to_change =
-      transformation::FindInstruction(message_.id_use_descriptor(), context);
+      FindInstructionContainingUse(message_.id_use_descriptor(), context);
 
   // Ultimately we are going to replace the id use identified in the
   // transformation with |replacement_id|, which will either be the synonym's
diff --git a/test/fuzz/transformation_replace_boolean_constant_with_constant_binary_test.cpp b/test/fuzz/transformation_replace_boolean_constant_with_constant_binary_test.cpp
index 704494b..bfc7fa7 100644
--- a/test/fuzz/transformation_replace_boolean_constant_with_constant_binary_test.cpp
+++ b/test/fuzz/transformation_replace_boolean_constant_with_constant_binary_test.cpp
@@ -16,6 +16,7 @@
 
 #include "source/fuzz/fuzzer_util.h"
 #include "source/fuzz/id_use_descriptor.h"
+#include "source/fuzz/instruction_descriptor.h"
 #include "test/fuzz/fuzz_test_util.h"
 
 namespace spvtools {
@@ -164,12 +165,14 @@
   FactManager fact_manager;
 
   std::vector<protobufs::IdUseDescriptor> uses_of_true = {
-      transformation::MakeIdUseDescriptor(41, SpvOpStore, 1, 44, 12),
-      transformation::MakeIdUseDescriptor(41, SpvOpLogicalOr, 0, 46, 0)};
+      MakeIdUseDescriptor(41, MakeInstructionDescriptor(44, SpvOpStore, 12), 1),
+      MakeIdUseDescriptor(41, MakeInstructionDescriptor(46, SpvOpLogicalOr, 0),
+                          0)};
 
   std::vector<protobufs::IdUseDescriptor> uses_of_false = {
-      transformation::MakeIdUseDescriptor(43, SpvOpStore, 1, 44, 13),
-      transformation::MakeIdUseDescriptor(43, SpvOpLogicalAnd, 1, 48, 0)};
+      MakeIdUseDescriptor(43, MakeInstructionDescriptor(44, SpvOpStore, 13), 1),
+      MakeIdUseDescriptor(43, MakeInstructionDescriptor(48, SpvOpLogicalAnd, 0),
+                          1)};
 
   const uint32_t fresh_id = 100;
 
@@ -529,10 +532,10 @@
 
   FactManager fact_manager;
 
-  auto use_of_true_in_if =
-      transformation::MakeIdUseDescriptor(13, SpvOpBranchConditional, 0, 10, 0);
-  auto use_of_false_in_while =
-      transformation::MakeIdUseDescriptor(21, SpvOpBranchConditional, 0, 16, 0);
+  auto use_of_true_in_if = MakeIdUseDescriptor(
+      13, MakeInstructionDescriptor(10, SpvOpBranchConditional, 0), 0);
+  auto use_of_false_in_while = MakeIdUseDescriptor(
+      21, MakeInstructionDescriptor(16, SpvOpBranchConditional, 0), 0);
 
   auto replacement_1 = TransformationReplaceBooleanConstantWithConstantBinary(
       use_of_true_in_if, 9, 11, SpvOpSLessThan, 100);
@@ -641,8 +644,8 @@
   FactManager fact_manager;
 
   auto replacement = TransformationReplaceBooleanConstantWithConstantBinary(
-      transformation::MakeIdUseDescriptor(9, SpvOpPhi, 0, 23, 0), 13, 15,
-      SpvOpSLessThan, 100);
+      MakeIdUseDescriptor(9, MakeInstructionDescriptor(23, SpvOpPhi, 0), 0), 13,
+      15, SpvOpSLessThan, 100);
 
   ASSERT_FALSE(replacement.IsApplicable(context.get(), fact_manager));
 }
diff --git a/test/fuzz/transformation_replace_constant_with_uniform_test.cpp b/test/fuzz/transformation_replace_constant_with_uniform_test.cpp
index 06ee025..ac2e3f9 100644
--- a/test/fuzz/transformation_replace_constant_with_uniform_test.cpp
+++ b/test/fuzz/transformation_replace_constant_with_uniform_test.cpp
@@ -13,6 +13,7 @@
 // limitations under the License.
 
 #include "source/fuzz/transformation_replace_constant_with_uniform.h"
+#include "source/fuzz/instruction_descriptor.h"
 #include "source/fuzz/uniform_buffer_element_descriptor.h"
 #include "test/fuzz/fuzz_test_util.h"
 
@@ -116,11 +117,11 @@
 
   // The constant ids are 9, 11 and 14, for 1, 2 and 3 respectively.
   protobufs::IdUseDescriptor use_of_9_in_store =
-      transformation::MakeIdUseDescriptor(9, SpvOpStore, 1, 8, 0);
+      MakeIdUseDescriptor(9, MakeInstructionDescriptor(8, SpvOpStore, 0), 1);
   protobufs::IdUseDescriptor use_of_11_in_add =
-      transformation::MakeIdUseDescriptor(11, SpvOpIAdd, 1, 12, 0);
+      MakeIdUseDescriptor(11, MakeInstructionDescriptor(12, SpvOpIAdd, 0), 1);
   protobufs::IdUseDescriptor use_of_14_in_add =
-      transformation::MakeIdUseDescriptor(14, SpvOpIAdd, 0, 15, 0);
+      MakeIdUseDescriptor(14, MakeInstructionDescriptor(15, SpvOpIAdd, 0), 0);
 
   // These transformations work: they match the facts.
   auto transformation_use_of_9_in_store =
@@ -167,7 +168,7 @@
   // The following transformation does not apply because the id descriptor is
   // not sensible.
   protobufs::IdUseDescriptor nonsense_id_use_descriptor =
-      transformation::MakeIdUseDescriptor(9, SpvOpIAdd, 0, 15, 0);
+      MakeIdUseDescriptor(9, MakeInstructionDescriptor(15, SpvOpIAdd, 0), 0);
   ASSERT_FALSE(TransformationReplaceConstantWithUniform(
                    nonsense_id_use_descriptor, blockname_a, 101, 102)
                    .IsApplicable(context.get(), fact_manager));
@@ -477,13 +478,13 @@
 
   // The constant ids are 13, 15, 17 and 20, for 1, 2, 3 and 4 respectively.
   protobufs::IdUseDescriptor use_of_13_in_store =
-      transformation::MakeIdUseDescriptor(13, SpvOpStore, 1, 21, 0);
+      MakeIdUseDescriptor(13, MakeInstructionDescriptor(21, SpvOpStore, 0), 1);
   protobufs::IdUseDescriptor use_of_15_in_add =
-      transformation::MakeIdUseDescriptor(15, SpvOpIAdd, 1, 16, 0);
+      MakeIdUseDescriptor(15, MakeInstructionDescriptor(16, SpvOpIAdd, 0), 1);
   protobufs::IdUseDescriptor use_of_17_in_add =
-      transformation::MakeIdUseDescriptor(17, SpvOpIAdd, 0, 19, 0);
+      MakeIdUseDescriptor(17, MakeInstructionDescriptor(19, SpvOpIAdd, 0), 0);
   protobufs::IdUseDescriptor use_of_20_in_store =
-      transformation::MakeIdUseDescriptor(20, SpvOpStore, 1, 19, 1);
+      MakeIdUseDescriptor(20, MakeInstructionDescriptor(19, SpvOpStore, 1), 1);
 
   // These transformations work: they match the facts.
   auto transformation_use_of_13_in_store =
@@ -703,7 +704,7 @@
 
   // The constant id is 9 for 0.
   protobufs::IdUseDescriptor use_of_9_in_store =
-      transformation::MakeIdUseDescriptor(9, SpvOpStore, 1, 8, 0);
+      MakeIdUseDescriptor(9, MakeInstructionDescriptor(8, SpvOpStore, 0), 1);
 
   // This transformation is not available because no uniform pointer to integer
   // type is present:
@@ -778,7 +779,7 @@
 
   // The constant id is 9 for 9.
   protobufs::IdUseDescriptor use_of_9_in_store =
-      transformation::MakeIdUseDescriptor(9, SpvOpStore, 1, 8, 0);
+      MakeIdUseDescriptor(9, MakeInstructionDescriptor(8, SpvOpStore, 0), 1);
 
   // This transformation is not available because no constant is present for the
   // index 1 required to index into the uniform buffer:
@@ -852,7 +853,7 @@
 
   // The constant id is 9 for 3.0.
   protobufs::IdUseDescriptor use_of_9_in_store =
-      transformation::MakeIdUseDescriptor(9, SpvOpStore, 1, 8, 0);
+      MakeIdUseDescriptor(9, MakeInstructionDescriptor(8, SpvOpStore, 0), 1);
 
   // This transformation is not available because no integer type is present to
   // allow a constant index to be expressed:
@@ -937,9 +938,9 @@
 
   // The constant ids for 9 and 10 are 9 and 11 respectively
   protobufs::IdUseDescriptor use_of_9_in_store =
-      transformation::MakeIdUseDescriptor(9, SpvOpStore, 1, 10, 0);
+      MakeIdUseDescriptor(9, MakeInstructionDescriptor(10, SpvOpStore, 0), 1);
   protobufs::IdUseDescriptor use_of_11_in_store =
-      transformation::MakeIdUseDescriptor(11, SpvOpStore, 1, 10, 1);
+      MakeIdUseDescriptor(11, MakeInstructionDescriptor(10, SpvOpStore, 1), 1);
 
   // These are right:
   ASSERT_TRUE(TransformationReplaceConstantWithUniform(use_of_9_in_store,
@@ -1220,58 +1221,58 @@
   std::vector<TransformationReplaceConstantWithUniform> transformations;
 
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
-      transformation::MakeIdUseDescriptor(18, SpvOpStore, 1, 20, 0),
+      MakeIdUseDescriptor(18, MakeInstructionDescriptor(20, SpvOpStore, 0), 1),
       uniform_f_a_4, 200, 201));
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
-      transformation::MakeIdUseDescriptor(22, SpvOpStore, 1, 23, 0),
+      MakeIdUseDescriptor(22, MakeInstructionDescriptor(23, SpvOpStore, 0), 1),
       uniform_f_a_3, 202, 203));
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
-      transformation::MakeIdUseDescriptor(25, SpvOpStore, 1, 26, 0),
+      MakeIdUseDescriptor(25, MakeInstructionDescriptor(26, SpvOpStore, 0), 1),
       uniform_f_a_2, 204, 205));
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
-      transformation::MakeIdUseDescriptor(28, SpvOpStore, 1, 29, 0),
+      MakeIdUseDescriptor(28, MakeInstructionDescriptor(29, SpvOpStore, 0), 1),
       uniform_f_a_1, 206, 207));
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
-      transformation::MakeIdUseDescriptor(31, SpvOpStore, 1, 32, 0),
+      MakeIdUseDescriptor(31, MakeInstructionDescriptor(32, SpvOpStore, 0), 1),
       uniform_f_a_0, 208, 209));
 
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
-      transformation::MakeIdUseDescriptor(30, SpvOpStore, 1, 35, 0),
+      MakeIdUseDescriptor(30, MakeInstructionDescriptor(35, SpvOpStore, 0), 1),
       uniform_f_b_w, 210, 211));
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
-      transformation::MakeIdUseDescriptor(27, SpvOpStore, 1, 37, 0),
+      MakeIdUseDescriptor(27, MakeInstructionDescriptor(37, SpvOpStore, 0), 1),
       uniform_f_b_z, 212, 213));
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
-      transformation::MakeIdUseDescriptor(24, SpvOpStore, 1, 39, 0),
+      MakeIdUseDescriptor(24, MakeInstructionDescriptor(39, SpvOpStore, 0), 1),
       uniform_f_b_y, 214, 215));
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
-      transformation::MakeIdUseDescriptor(21, SpvOpStore, 1, 41, 0),
+      MakeIdUseDescriptor(21, MakeInstructionDescriptor(41, SpvOpStore, 0), 1),
       uniform_f_b_x, 216, 217));
 
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
-      transformation::MakeIdUseDescriptor(44, SpvOpStore, 1, 45, 0),
+      MakeIdUseDescriptor(44, MakeInstructionDescriptor(45, SpvOpStore, 0), 1),
       uniform_f_c_z, 220, 221));
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
-      transformation::MakeIdUseDescriptor(46, SpvOpStore, 1, 47, 0),
+      MakeIdUseDescriptor(46, MakeInstructionDescriptor(47, SpvOpStore, 0), 1),
       uniform_f_c_y, 222, 223));
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
-      transformation::MakeIdUseDescriptor(48, SpvOpStore, 1, 49, 0),
+      MakeIdUseDescriptor(48, MakeInstructionDescriptor(49, SpvOpStore, 0), 1),
       uniform_f_c_x, 224, 225));
 
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
-      transformation::MakeIdUseDescriptor(50, SpvOpStore, 1, 52, 0),
+      MakeIdUseDescriptor(50, MakeInstructionDescriptor(52, SpvOpStore, 0), 1),
       uniform_f_d, 226, 227));
 
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
-      transformation::MakeIdUseDescriptor(53, SpvOpStore, 1, 54, 0),
+      MakeIdUseDescriptor(53, MakeInstructionDescriptor(54, SpvOpStore, 0), 1),
       uniform_h_x, 228, 229));
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
-      transformation::MakeIdUseDescriptor(55, SpvOpStore, 1, 56, 0),
+      MakeIdUseDescriptor(55, MakeInstructionDescriptor(56, SpvOpStore, 0), 1),
       uniform_h_y, 230, 231));
 
   transformations.emplace_back(TransformationReplaceConstantWithUniform(
-      transformation::MakeIdUseDescriptor(42, SpvOpStore, 1, 43, 0), uniform_g,
-      218, 219));
+      MakeIdUseDescriptor(42, MakeInstructionDescriptor(43, SpvOpStore, 0), 1),
+      uniform_g, 218, 219));
 
   for (auto& transformation : transformations) {
     ASSERT_TRUE(transformation.IsApplicable(context.get(), fact_manager));
diff --git a/test/fuzz/transformation_replace_id_with_synonym_test.cpp b/test/fuzz/transformation_replace_id_with_synonym_test.cpp
index cd79bbe..2e8a614 100644
--- a/test/fuzz/transformation_replace_id_with_synonym_test.cpp
+++ b/test/fuzz/transformation_replace_id_with_synonym_test.cpp
@@ -15,6 +15,7 @@
 #include "source/fuzz/transformation_replace_id_with_synonym.h"
 #include "source/fuzz/data_descriptor.h"
 #include "source/fuzz/id_use_descriptor.h"
+#include "source/fuzz/instruction_descriptor.h"
 #include "test/fuzz/fuzz_test_util.h"
 
 namespace spvtools {
@@ -226,7 +227,7 @@
   // %202 cannot replace %15 as in-operand 0 of %300, since %202 does not
   // dominate %300.
   auto synonym_does_not_dominate_use = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(15, SpvOpIAdd, 0, 300, 0),
+      MakeIdUseDescriptor(15, MakeInstructionDescriptor(300, SpvOpIAdd, 0), 0),
       MakeDataDescriptor(202, {}), 0);
   ASSERT_FALSE(
       synonym_does_not_dominate_use.IsApplicable(context.get(), fact_manager));
@@ -235,28 +236,31 @@
   // incoming value for block %72, and %202 does not dominate %72.
   auto synonym_does_not_dominate_use_op_phi =
       TransformationReplaceIdWithSynonym(
-          transformation::MakeIdUseDescriptor(15, SpvOpPhi, 2, 301, 0),
+          MakeIdUseDescriptor(15, MakeInstructionDescriptor(301, SpvOpPhi, 0),
+                              2),
           MakeDataDescriptor(202, {}), 0);
   ASSERT_FALSE(synonym_does_not_dominate_use_op_phi.IsApplicable(context.get(),
                                                                  fact_manager));
 
   // %200 is not a synonym for %84
   auto id_in_use_is_not_synonymous = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(84, SpvOpSGreaterThan, 0, 67, 0),
+      MakeIdUseDescriptor(
+          84, MakeInstructionDescriptor(67, SpvOpSGreaterThan, 0), 0),
       MakeDataDescriptor(200, {}), 0);
   ASSERT_FALSE(
       id_in_use_is_not_synonymous.IsApplicable(context.get(), fact_manager));
 
   // %86 is not a synonym for anything (and in particular not for %74)
   auto id_has_no_synonyms = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(86, SpvOpPhi, 2, 84, 0),
+      MakeIdUseDescriptor(86, MakeInstructionDescriptor(84, SpvOpPhi, 0), 2),
       MakeDataDescriptor(74, {}), 0);
   ASSERT_FALSE(id_has_no_synonyms.IsApplicable(context.get(), fact_manager));
 
   // This would lead to %207 = 'OpCopyObject %type %207' if it were allowed
   auto synonym_use_is_in_synonym_definition =
       TransformationReplaceIdWithSynonym(
-          transformation::MakeIdUseDescriptor(84, SpvOpCopyObject, 0, 207, 0),
+          MakeIdUseDescriptor(
+              84, MakeInstructionDescriptor(207, SpvOpCopyObject, 0), 0),
           MakeDataDescriptor(207, {}), 0);
   ASSERT_FALSE(synonym_use_is_in_synonym_definition.IsApplicable(context.get(),
                                                                  fact_manager));
@@ -264,14 +268,16 @@
   // The id use descriptor does not lead to a use (%84 is not used in the
   // definition of %207)
   auto bad_id_use_descriptor = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(84, SpvOpCopyObject, 0, 200, 0),
+      MakeIdUseDescriptor(
+          84, MakeInstructionDescriptor(200, SpvOpCopyObject, 0), 0),
       MakeDataDescriptor(207, {}), 0);
   ASSERT_FALSE(bad_id_use_descriptor.IsApplicable(context.get(), fact_manager));
 
   // This replacement would lead to an access chain into a struct using a
   // non-constant index.
   auto bad_access_chain = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(12, SpvOpAccessChain, 1, 14, 0),
+      MakeIdUseDescriptor(
+          12, MakeInstructionDescriptor(14, SpvOpAccessChain, 0), 1),
       MakeDataDescriptor(209, {}), 0);
   ASSERT_FALSE(bad_access_chain.IsApplicable(context.get(), fact_manager));
 }
@@ -287,7 +293,7 @@
   SetUpIdSynonyms(&fact_manager, context.get());
 
   auto global_constant_synonym = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(19, SpvOpStore, 1, 47, 0),
+      MakeIdUseDescriptor(19, MakeInstructionDescriptor(47, SpvOpStore, 0), 1),
       MakeDataDescriptor(210, {}), 0);
   ASSERT_TRUE(
       global_constant_synonym.IsApplicable(context.get(), fact_manager));
@@ -295,7 +301,8 @@
   ASSERT_TRUE(IsValid(env, context.get()));
 
   auto replace_vector_access_chain_index = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(54, SpvOpAccessChain, 1, 55, 0),
+      MakeIdUseDescriptor(
+          54, MakeInstructionDescriptor(55, SpvOpAccessChain, 0), 1),
       MakeDataDescriptor(204, {}), 0);
   ASSERT_TRUE(replace_vector_access_chain_index.IsApplicable(context.get(),
                                                              fact_manager));
@@ -305,21 +312,22 @@
   // This is an interesting case because it replaces something that is being
   // copied with something that is already a synonym.
   auto regular_replacement = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(15, SpvOpCopyObject, 0, 202, 0),
+      MakeIdUseDescriptor(
+          15, MakeInstructionDescriptor(202, SpvOpCopyObject, 0), 0),
       MakeDataDescriptor(201, {}), 0);
   ASSERT_TRUE(regular_replacement.IsApplicable(context.get(), fact_manager));
   regular_replacement.Apply(context.get(), &fact_manager);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   auto regular_replacement2 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(55, SpvOpStore, 0, 203, 0),
+      MakeIdUseDescriptor(55, MakeInstructionDescriptor(203, SpvOpStore, 0), 0),
       MakeDataDescriptor(203, {}), 0);
   ASSERT_TRUE(regular_replacement2.IsApplicable(context.get(), fact_manager));
   regular_replacement2.Apply(context.get(), &fact_manager);
   ASSERT_TRUE(IsValid(env, context.get()));
 
   auto good_op_phi = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(74, SpvOpPhi, 2, 86, 0),
+      MakeIdUseDescriptor(74, MakeInstructionDescriptor(86, SpvOpPhi, 0), 2),
       MakeDataDescriptor(205, {}), 0);
   ASSERT_TRUE(good_op_phi.IsApplicable(context.get(), fact_manager));
   good_op_phi.Apply(context.get(), &fact_manager);
@@ -503,7 +511,7 @@
   // Replace %10 with %100 in:
   // %11 = OpLoad %6 %10
   auto replacement1 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(10, SpvOpLoad, 0, 11, 0),
+      MakeIdUseDescriptor(10, MakeInstructionDescriptor(11, SpvOpLoad, 0), 0),
       MakeDataDescriptor(100, {}), 0);
   ASSERT_TRUE(replacement1.IsApplicable(context.get(), fact_manager));
   replacement1.Apply(context.get(), &fact_manager);
@@ -512,7 +520,7 @@
   // Replace %8 with %101 in:
   // OpStore %8 %11
   auto replacement2 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(8, SpvOpStore, 0, 11, 0),
+      MakeIdUseDescriptor(8, MakeInstructionDescriptor(11, SpvOpStore, 0), 0),
       MakeDataDescriptor(101, {}), 0);
   ASSERT_TRUE(replacement2.IsApplicable(context.get(), fact_manager));
   replacement2.Apply(context.get(), &fact_manager);
@@ -521,7 +529,7 @@
   // Replace %8 with %101 in:
   // %12 = OpLoad %6 %8
   auto replacement3 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(8, SpvOpLoad, 0, 12, 0),
+      MakeIdUseDescriptor(8, MakeInstructionDescriptor(12, SpvOpLoad, 0), 0),
       MakeDataDescriptor(101, {}), 0);
   ASSERT_TRUE(replacement3.IsApplicable(context.get(), fact_manager));
   replacement3.Apply(context.get(), &fact_manager);
@@ -530,7 +538,7 @@
   // Replace %10 with %100 in:
   // OpStore %10 %12
   auto replacement4 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(10, SpvOpStore, 0, 12, 0),
+      MakeIdUseDescriptor(10, MakeInstructionDescriptor(12, SpvOpStore, 0), 0),
       MakeDataDescriptor(100, {}), 0);
   ASSERT_TRUE(replacement4.IsApplicable(context.get(), fact_manager));
   replacement4.Apply(context.get(), &fact_manager);
@@ -631,7 +639,8 @@
   // Replace %14 with %100 in:
   // %16 = OpFunctionCall %2 %10 %14
   auto replacement = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(14, SpvOpFunctionCall, 1, 16, 0),
+      MakeIdUseDescriptor(
+          14, MakeInstructionDescriptor(16, SpvOpFunctionCall, 0), 1),
       MakeDataDescriptor(100, {}), 0);
   ASSERT_FALSE(replacement.IsApplicable(context.get(), fact_manager));
 }
@@ -809,7 +818,8 @@
   // Corresponds to d.*a*[2]
   // The index %16 used for a cannot be replaced
   auto replacement1 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(16, SpvOpAccessChain, 1, 20, 0),
+      MakeIdUseDescriptor(
+          16, MakeInstructionDescriptor(20, SpvOpAccessChain, 0), 1),
       MakeDataDescriptor(100, {}), 0);
   ASSERT_FALSE(replacement1.IsApplicable(context.get(), fact_manager));
 
@@ -817,7 +827,8 @@
   // Corresponds to h.*f*
   // The index %16 used for f cannot be replaced
   auto replacement2 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(16, SpvOpAccessChain, 1, 39, 0),
+      MakeIdUseDescriptor(
+          16, MakeInstructionDescriptor(39, SpvOpAccessChain, 0), 1),
       MakeDataDescriptor(100, {}), 0);
   ASSERT_FALSE(replacement2.IsApplicable(context.get(), fact_manager));
 
@@ -825,7 +836,8 @@
   // Corresponds to h.g.*a*[1]
   // The index %16 used for a cannot be replaced
   auto replacement3 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(16, SpvOpAccessChain, 2, 41, 0),
+      MakeIdUseDescriptor(
+          16, MakeInstructionDescriptor(41, SpvOpAccessChain, 0), 2),
       MakeDataDescriptor(100, {}), 0);
   ASSERT_FALSE(replacement3.IsApplicable(context.get(), fact_manager));
 
@@ -833,7 +845,8 @@
   // Corresponds to i[*0*].f
   // The index %16 used for 0 *can* be replaced
   auto replacement4 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(16, SpvOpAccessChain, 1, 52, 0),
+      MakeIdUseDescriptor(
+          16, MakeInstructionDescriptor(52, SpvOpAccessChain, 0), 1),
       MakeDataDescriptor(100, {}), 0);
   ASSERT_TRUE(replacement4.IsApplicable(context.get(), fact_manager));
   replacement4.Apply(context.get(), &fact_manager);
@@ -843,7 +856,8 @@
   // Corresponds to i[0].*f*
   // The index %16 used for f cannot be replaced
   auto replacement5 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(16, SpvOpAccessChain, 2, 52, 0),
+      MakeIdUseDescriptor(
+          16, MakeInstructionDescriptor(52, SpvOpAccessChain, 0), 2),
       MakeDataDescriptor(100, {}), 0);
   ASSERT_FALSE(replacement5.IsApplicable(context.get(), fact_manager));
 
@@ -851,7 +865,8 @@
   // Corresponds to i[1].g.*a*[0]
   // The index %16 used for a cannot be replaced
   auto replacement6 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(16, SpvOpAccessChain, 3, 53, 0),
+      MakeIdUseDescriptor(
+          16, MakeInstructionDescriptor(53, SpvOpAccessChain, 0), 3),
       MakeDataDescriptor(100, {}), 0);
   ASSERT_FALSE(replacement6.IsApplicable(context.get(), fact_manager));
 
@@ -859,7 +874,8 @@
   // Corresponds to i[1].g.a[*0*]
   // The index %16 used for 0 *can* be replaced
   auto replacement7 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(16, SpvOpAccessChain, 4, 53, 0),
+      MakeIdUseDescriptor(
+          16, MakeInstructionDescriptor(53, SpvOpAccessChain, 0), 4),
       MakeDataDescriptor(100, {}), 0);
   ASSERT_TRUE(replacement7.IsApplicable(context.get(), fact_manager));
   replacement7.Apply(context.get(), &fact_manager);
@@ -871,7 +887,8 @@
   // Corresponds to d.*b*[3]
   // The index %24 used for b cannot be replaced
   auto replacement8 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(21, SpvOpAccessChain, 1, 24, 0),
+      MakeIdUseDescriptor(
+          21, MakeInstructionDescriptor(24, SpvOpAccessChain, 0), 1),
       MakeDataDescriptor(101, {}), 0);
   ASSERT_FALSE(replacement8.IsApplicable(context.get(), fact_manager));
 
@@ -879,7 +896,8 @@
   // Corresponds to h.*g*.a[1]
   // The index %24 used for g cannot be replaced
   auto replacement9 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(21, SpvOpAccessChain, 1, 41, 0),
+      MakeIdUseDescriptor(
+          21, MakeInstructionDescriptor(41, SpvOpAccessChain, 0), 1),
       MakeDataDescriptor(101, {}), 0);
   ASSERT_FALSE(replacement9.IsApplicable(context.get(), fact_manager));
 
@@ -887,7 +905,8 @@
   // Corresponds to h.g.a[*1*]
   // The index %24 used for 1 *can* be replaced
   auto replacement10 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(21, SpvOpAccessChain, 3, 41, 0),
+      MakeIdUseDescriptor(
+          21, MakeInstructionDescriptor(41, SpvOpAccessChain, 0), 3),
       MakeDataDescriptor(101, {}), 0);
   ASSERT_TRUE(replacement10.IsApplicable(context.get(), fact_manager));
   replacement10.Apply(context.get(), &fact_manager);
@@ -897,7 +916,8 @@
   // Corresponds to h.*g*.b[0]
   // The index %24 used for g cannot be replaced
   auto replacement11 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(21, SpvOpAccessChain, 1, 44, 0),
+      MakeIdUseDescriptor(
+          21, MakeInstructionDescriptor(44, SpvOpAccessChain, 0), 1),
       MakeDataDescriptor(101, {}), 0);
   ASSERT_FALSE(replacement11.IsApplicable(context.get(), fact_manager));
 
@@ -905,7 +925,8 @@
   // Corresponds to h.g.*b*[0]
   // The index %24 used for b cannot be replaced
   auto replacement12 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(21, SpvOpAccessChain, 2, 44, 0),
+      MakeIdUseDescriptor(
+          21, MakeInstructionDescriptor(44, SpvOpAccessChain, 0), 2),
       MakeDataDescriptor(101, {}), 0);
   ASSERT_FALSE(replacement12.IsApplicable(context.get(), fact_manager));
 
@@ -913,7 +934,8 @@
   // Corresponds to h.*g*.c
   // The index %24 used for g cannot be replaced
   auto replacement13 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(21, SpvOpAccessChain, 1, 46, 0),
+      MakeIdUseDescriptor(
+          21, MakeInstructionDescriptor(46, SpvOpAccessChain, 0), 1),
       MakeDataDescriptor(101, {}), 0);
   ASSERT_FALSE(replacement13.IsApplicable(context.get(), fact_manager));
 
@@ -921,7 +943,8 @@
   // Corresponds to i[*1*].g.a[0]
   // The index %24 used for 1 *can* be replaced
   auto replacement14 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(21, SpvOpAccessChain, 1, 53, 0),
+      MakeIdUseDescriptor(
+          21, MakeInstructionDescriptor(53, SpvOpAccessChain, 0), 1),
       MakeDataDescriptor(101, {}), 0);
   ASSERT_TRUE(replacement14.IsApplicable(context.get(), fact_manager));
   replacement14.Apply(context.get(), &fact_manager);
@@ -931,7 +954,8 @@
   // Corresponds to i[1].*g*.a[0]
   // The index %24 used for g cannot be replaced
   auto replacement15 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(21, SpvOpAccessChain, 2, 53, 0),
+      MakeIdUseDescriptor(
+          21, MakeInstructionDescriptor(53, SpvOpAccessChain, 0), 2),
       MakeDataDescriptor(101, {}), 0);
   ASSERT_FALSE(replacement15.IsApplicable(context.get(), fact_manager));
 
@@ -939,7 +963,8 @@
   // Corresponds to i[2].*g*.b[1]
   // The index %24 used for g cannot be replaced
   auto replacement16 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(21, SpvOpAccessChain, 2, 56, 0),
+      MakeIdUseDescriptor(
+          21, MakeInstructionDescriptor(56, SpvOpAccessChain, 0), 2),
       MakeDataDescriptor(101, {}), 0);
   ASSERT_FALSE(replacement16.IsApplicable(context.get(), fact_manager));
 
@@ -947,7 +972,8 @@
   // Corresponds to i[2].g.*b*[1]
   // The index %24 used for b cannot be replaced
   auto replacement17 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(21, SpvOpAccessChain, 3, 56, 0),
+      MakeIdUseDescriptor(
+          21, MakeInstructionDescriptor(56, SpvOpAccessChain, 0), 3),
       MakeDataDescriptor(101, {}), 0);
   ASSERT_FALSE(replacement17.IsApplicable(context.get(), fact_manager));
 
@@ -955,7 +981,8 @@
   // Corresponds to i[3].*g*.c
   // The index %24 used for g cannot be replaced
   auto replacement18 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(21, SpvOpAccessChain, 2, 58, 0),
+      MakeIdUseDescriptor(
+          21, MakeInstructionDescriptor(58, SpvOpAccessChain, 0), 2),
       MakeDataDescriptor(101, {}), 0);
   ASSERT_FALSE(replacement18.IsApplicable(context.get(), fact_manager));
 
@@ -965,7 +992,8 @@
   // Corresponds to d.a[*2*]
   // The index %17 used for 2 *can* be replaced
   auto replacement19 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(17, SpvOpAccessChain, 2, 20, 0),
+      MakeIdUseDescriptor(
+          17, MakeInstructionDescriptor(20, SpvOpAccessChain, 0), 2),
       MakeDataDescriptor(102, {}), 0);
   ASSERT_TRUE(replacement19.IsApplicable(context.get(), fact_manager));
   replacement19.Apply(context.get(), &fact_manager);
@@ -975,7 +1003,8 @@
   // Corresponds to d.c
   // The index %17 used for c cannot be replaced
   auto replacement20 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(17, SpvOpAccessChain, 1, 27, 0),
+      MakeIdUseDescriptor(
+          17, MakeInstructionDescriptor(27, SpvOpAccessChain, 0), 1),
       MakeDataDescriptor(102, {}), 0);
   ASSERT_FALSE(replacement20.IsApplicable(context.get(), fact_manager));
 
@@ -983,7 +1012,8 @@
   // Corresponds to h.g.*c*
   // The index %17 used for c cannot be replaced
   auto replacement21 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(17, SpvOpAccessChain, 2, 46, 0),
+      MakeIdUseDescriptor(
+          17, MakeInstructionDescriptor(46, SpvOpAccessChain, 0), 2),
       MakeDataDescriptor(102, {}), 0);
   ASSERT_FALSE(replacement21.IsApplicable(context.get(), fact_manager));
 
@@ -991,7 +1021,8 @@
   // Corresponds to i[*2*].g.b[1]
   // The index %17 used for 2 *can* be replaced
   auto replacement22 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(17, SpvOpAccessChain, 1, 56, 0),
+      MakeIdUseDescriptor(
+          17, MakeInstructionDescriptor(56, SpvOpAccessChain, 0), 1),
       MakeDataDescriptor(102, {}), 0);
   ASSERT_TRUE(replacement22.IsApplicable(context.get(), fact_manager));
   replacement22.Apply(context.get(), &fact_manager);
@@ -1001,7 +1032,8 @@
   // Corresponds to i[3].g.*c*
   // The index %17 used for c cannot be replaced
   auto replacement23 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(17, SpvOpAccessChain, 3, 58, 0),
+      MakeIdUseDescriptor(
+          17, MakeInstructionDescriptor(58, SpvOpAccessChain, 0), 3),
       MakeDataDescriptor(102, {}), 0);
   ASSERT_FALSE(replacement23.IsApplicable(context.get(), fact_manager));
 
@@ -1011,7 +1043,8 @@
   // Corresponds to i[*3*].g.c
   // The index %57 used for 3 *can* be replaced
   auto replacement24 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(57, SpvOpAccessChain, 1, 58, 0),
+      MakeIdUseDescriptor(
+          57, MakeInstructionDescriptor(58, SpvOpAccessChain, 0), 1),
       MakeDataDescriptor(103, {}), 0);
   ASSERT_TRUE(replacement24.IsApplicable(context.get(), fact_manager));
   replacement24.Apply(context.get(), &fact_manager);
@@ -1023,7 +1056,8 @@
   // Corresponds to e[*17*]
   // The index %32 used for 17 *can* be replaced
   auto replacement25 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(32, SpvOpAccessChain, 1, 34, 0),
+      MakeIdUseDescriptor(
+          32, MakeInstructionDescriptor(34, SpvOpAccessChain, 0), 1),
       MakeDataDescriptor(106, {}), 0);
   ASSERT_TRUE(replacement25.IsApplicable(context.get(), fact_manager));
   replacement25.Apply(context.get(), &fact_manager);
@@ -1035,7 +1069,8 @@
   // Corresponds to h.g.b[*0*]
   // The index %43 used for 0 *can* be replaced
   auto replacement26 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(43, SpvOpAccessChain, 3, 44, 0),
+      MakeIdUseDescriptor(
+          43, MakeInstructionDescriptor(44, SpvOpAccessChain, 0), 3),
       MakeDataDescriptor(107, {}), 0);
   ASSERT_TRUE(replacement26.IsApplicable(context.get(), fact_manager));
   replacement26.Apply(context.get(), &fact_manager);
@@ -1047,7 +1082,8 @@
   // Corresponds to i[2].g.b[*1*]
   // The index %55 used for 1 *can* be replaced
   auto replacement27 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(55, SpvOpAccessChain, 4, 56, 0),
+      MakeIdUseDescriptor(
+          55, MakeInstructionDescriptor(56, SpvOpAccessChain, 0), 4),
       MakeDataDescriptor(108, {}), 0);
   ASSERT_TRUE(replacement27.IsApplicable(context.get(), fact_manager));
   replacement27.Apply(context.get(), &fact_manager);
@@ -1059,7 +1095,8 @@
   // Corresponds to d.b[*3*]
   // The index %8 used for 3 *can* be replaced
   auto replacement28 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(8, SpvOpAccessChain, 2, 24, 0),
+      MakeIdUseDescriptor(8, MakeInstructionDescriptor(24, SpvOpAccessChain, 0),
+                          2),
       MakeDataDescriptor(109, {}), 0);
   ASSERT_TRUE(replacement28.IsApplicable(context.get(), fact_manager));
   replacement28.Apply(context.get(), &fact_manager);
@@ -1267,11 +1304,13 @@
 
   // Replace %12 with %100[0] in '%25 = OpAccessChain %24 %20 %12'
   auto good_replacement_1 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(12, SpvOpAccessChain, 1, 25, 0),
+      MakeIdUseDescriptor(
+          12, MakeInstructionDescriptor(25, SpvOpAccessChain, 0), 1),
       MakeDataDescriptor(100, {0}), 102);
   // Bad: id already in use
   auto bad_replacement_1 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(12, SpvOpAccessChain, 1, 25, 0),
+      MakeIdUseDescriptor(
+          12, MakeInstructionDescriptor(25, SpvOpAccessChain, 0), 1),
       MakeDataDescriptor(100, {0}), 25);
   ASSERT_TRUE(good_replacement_1.IsApplicable(context.get(), fact_manager));
   ASSERT_FALSE(bad_replacement_1.IsApplicable(context.get(), fact_manager));
@@ -1280,11 +1319,11 @@
 
   // Replace %13 with %100[1] in 'OpStore %15 %13'
   auto good_replacement_2 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(13, SpvOpStore, 1, 100, 0),
+      MakeIdUseDescriptor(13, MakeInstructionDescriptor(100, SpvOpStore, 0), 1),
       MakeDataDescriptor(100, {1}), 103);
   // Bad: too many indices
   auto bad_replacement_2 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(13, SpvOpStore, 1, 100, 0),
+      MakeIdUseDescriptor(13, MakeInstructionDescriptor(100, SpvOpStore, 0), 1),
       MakeDataDescriptor(100, {1, 0}), 103);
   ASSERT_TRUE(good_replacement_2.IsApplicable(context.get(), fact_manager));
   ASSERT_FALSE(bad_replacement_2.IsApplicable(context.get(), fact_manager));
@@ -1293,11 +1332,13 @@
 
   // Replace %22 with %100[2] in '%23 = OpConvertSToF %16 %22'
   auto good_replacement_3 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(22, SpvOpConvertSToF, 0, 23, 0),
+      MakeIdUseDescriptor(
+          22, MakeInstructionDescriptor(23, SpvOpConvertSToF, 0), 0),
       MakeDataDescriptor(100, {2}), 104);
   // Bad: wrong input operand index
   auto bad_replacement_3 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(22, SpvOpConvertSToF, 1, 23, 0),
+      MakeIdUseDescriptor(
+          22, MakeInstructionDescriptor(23, SpvOpConvertSToF, 0), 1),
       MakeDataDescriptor(100, {2}), 104);
   ASSERT_TRUE(good_replacement_3.IsApplicable(context.get(), fact_manager));
   ASSERT_FALSE(bad_replacement_3.IsApplicable(context.get(), fact_manager));
@@ -1306,11 +1347,12 @@
 
   // Replace %28 with %101[0] in 'OpStore %33 %28'
   auto good_replacement_4 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(28, SpvOpStore, 1, 33, 0),
+      MakeIdUseDescriptor(28, MakeInstructionDescriptor(33, SpvOpStore, 0), 1),
       MakeDataDescriptor(101, {0}), 105);
   // Bad: id use descriptor does not identify an appropriate instruction
   auto bad_replacement_4 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(28, SpvOpCopyObject, 1, 33, 0),
+      MakeIdUseDescriptor(28, MakeInstructionDescriptor(33, SpvOpCopyObject, 0),
+                          1),
       MakeDataDescriptor(101, {0}), 105);
   ASSERT_TRUE(good_replacement_4.IsApplicable(context.get(), fact_manager));
   ASSERT_FALSE(bad_replacement_4.IsApplicable(context.get(), fact_manager));
@@ -1319,11 +1361,13 @@
 
   // Replace %23 with %101[1] in '%50 = OpCopyObject %16 %23'
   auto good_replacement_5 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(23, SpvOpCopyObject, 0, 50, 0),
+      MakeIdUseDescriptor(23, MakeInstructionDescriptor(50, SpvOpCopyObject, 0),
+                          0),
       MakeDataDescriptor(101, {1}), 106);
   // Bad: wrong synonym fact being used
   auto bad_replacement_5 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(23, SpvOpCopyObject, 0, 50, 0),
+      MakeIdUseDescriptor(23, MakeInstructionDescriptor(50, SpvOpCopyObject, 0),
+                          0),
       MakeDataDescriptor(101, {0}), 106);
   ASSERT_TRUE(good_replacement_5.IsApplicable(context.get(), fact_manager));
   ASSERT_FALSE(bad_replacement_5.IsApplicable(context.get(), fact_manager));
@@ -1332,11 +1376,11 @@
 
   // Replace %32 with %101[2] in 'OpStore %33 %32'
   auto good_replacement_6 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(32, SpvOpStore, 1, 33, 1),
+      MakeIdUseDescriptor(32, MakeInstructionDescriptor(33, SpvOpStore, 1), 1),
       MakeDataDescriptor(101, {2}), 107);
   // Bad: id 1001 does not exist
   auto bad_replacement_6 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(32, SpvOpStore, 1, 33, 1),
+      MakeIdUseDescriptor(32, MakeInstructionDescriptor(33, SpvOpStore, 1), 1),
       MakeDataDescriptor(1001, {2}), 107);
   ASSERT_TRUE(good_replacement_6.IsApplicable(context.get(), fact_manager));
   ASSERT_FALSE(bad_replacement_6.IsApplicable(context.get(), fact_manager));
@@ -1345,11 +1389,13 @@
 
   // Replace %23 with %101[3] in '%51 = OpCopyObject %16 %23'
   auto good_replacement_7 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(23, SpvOpCopyObject, 0, 51, 0),
+      MakeIdUseDescriptor(23, MakeInstructionDescriptor(51, SpvOpCopyObject, 0),
+                          0),
       MakeDataDescriptor(101, {3}), 108);
   // Bad: id 0 is invalid
   auto bad_replacement_7 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(0, SpvOpCopyObject, 0, 51, 0),
+      MakeIdUseDescriptor(0, MakeInstructionDescriptor(51, SpvOpCopyObject, 0),
+                          0),
       MakeDataDescriptor(101, {3}), 108);
   ASSERT_TRUE(good_replacement_7.IsApplicable(context.get(), fact_manager));
   ASSERT_FALSE(bad_replacement_7.IsApplicable(context.get(), fact_manager));
@@ -1498,7 +1544,7 @@
 
   // Replace %23 with %100[0] in '%26 = OpFAdd %7 %23 %25'
   auto replacement_1 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(23, SpvOpFAdd, 0, 26, 0),
+      MakeIdUseDescriptor(23, MakeInstructionDescriptor(26, SpvOpFAdd, 0), 0),
       MakeDataDescriptor(100, {0}), 101);
   ASSERT_TRUE(replacement_1.IsApplicable(context.get(), fact_manager));
   replacement_1.Apply(context.get(), &fact_manager);
@@ -1506,7 +1552,7 @@
 
   // Replace %25 with %100[1] in '%26 = OpFAdd %7 %23 %25'
   auto replacement_2 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(25, SpvOpFAdd, 1, 26, 0),
+      MakeIdUseDescriptor(25, MakeInstructionDescriptor(26, SpvOpFAdd, 0), 1),
       MakeDataDescriptor(100, {1}), 102);
   ASSERT_TRUE(replacement_2.IsApplicable(context.get(), fact_manager));
   replacement_2.Apply(context.get(), &fact_manager);
@@ -1656,8 +1702,8 @@
 
   // Replace %45 with %100[1] in '%46 = OpCompositeConstruct %32 %35 %45'
   auto replacement_1 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(45, SpvOpCompositeConstruct, 1, 46,
-                                          0),
+      MakeIdUseDescriptor(
+          45, MakeInstructionDescriptor(46, SpvOpCompositeConstruct, 0), 1),
       MakeDataDescriptor(100, {1}), 201);
   ASSERT_TRUE(replacement_1.IsApplicable(context.get(), fact_manager));
   replacement_1.Apply(context.get(), &fact_manager);
@@ -1666,8 +1712,8 @@
   // Replace second occurrence of %27 with %101[0] in '%28 =
   // OpCompositeConstruct %8 %27 %27'
   auto replacement_2 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(27, SpvOpCompositeConstruct, 1, 28,
-                                          0),
+      MakeIdUseDescriptor(
+          27, MakeInstructionDescriptor(28, SpvOpCompositeConstruct, 0), 1),
       MakeDataDescriptor(101, {0}), 202);
   ASSERT_TRUE(replacement_2.IsApplicable(context.get(), fact_manager));
   replacement_2.Apply(context.get(), &fact_manager);
@@ -1675,8 +1721,8 @@
 
   // Replace %36 with %101[1] in '%45 = OpCompositeConstruct %31 %36 %41 %44'
   auto replacement_3 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(36, SpvOpCompositeConstruct, 0, 45,
-                                          0),
+      MakeIdUseDescriptor(
+          36, MakeInstructionDescriptor(45, SpvOpCompositeConstruct, 0), 0),
       MakeDataDescriptor(101, {1}), 203);
   ASSERT_TRUE(replacement_3.IsApplicable(context.get(), fact_manager));
   replacement_3.Apply(context.get(), &fact_manager);
@@ -1685,8 +1731,8 @@
   // Replace first occurrence of %27 with %101[2] in '%28 = OpCompositeConstruct
   // %8 %27 %27'
   auto replacement_4 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(27, SpvOpCompositeConstruct, 0, 28,
-                                          0),
+      MakeIdUseDescriptor(
+          27, MakeInstructionDescriptor(28, SpvOpCompositeConstruct, 0), 0),
       MakeDataDescriptor(101, {2}), 204);
   ASSERT_TRUE(replacement_4.IsApplicable(context.get(), fact_manager));
   replacement_4.Apply(context.get(), &fact_manager);
@@ -1694,7 +1740,7 @@
 
   // Replace %22 with %102[0] in 'OpStore %23 %22'
   auto replacement_5 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(22, SpvOpStore, 1, 23, 0),
+      MakeIdUseDescriptor(22, MakeInstructionDescriptor(23, SpvOpStore, 0), 1),
       MakeDataDescriptor(102, {0}), 205);
   ASSERT_TRUE(replacement_5.IsApplicable(context.get(), fact_manager));
   replacement_5.Apply(context.get(), &fact_manager);
@@ -1915,7 +1961,8 @@
 
   // Replace %20 with %100[0] in '%80 = OpCopyObject %16 %20'
   auto replacement_1 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(20, SpvOpCopyObject, 0, 80, 0),
+      MakeIdUseDescriptor(20, MakeInstructionDescriptor(80, SpvOpCopyObject, 0),
+                          0),
       MakeDataDescriptor(100, {0}), 200);
   ASSERT_TRUE(replacement_1.IsApplicable(context.get(), fact_manager));
   replacement_1.Apply(context.get(), &fact_manager);
@@ -1923,7 +1970,8 @@
 
   // Replace %54 with %100[3] in '%56 = OpFOrdNotEqual %30 %54 %55'
   auto replacement_2 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(54, SpvOpFOrdNotEqual, 0, 56, 0),
+      MakeIdUseDescriptor(
+          54, MakeInstructionDescriptor(56, SpvOpFOrdNotEqual, 0), 0),
       MakeDataDescriptor(100, {3}), 201);
   ASSERT_TRUE(replacement_2.IsApplicable(context.get(), fact_manager));
   replacement_2.Apply(context.get(), &fact_manager);
@@ -1931,7 +1979,7 @@
 
   // Replace %15 with %101[0] in 'OpStore %12 %15'
   auto replacement_3 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(15, SpvOpStore, 1, 64, 0),
+      MakeIdUseDescriptor(15, MakeInstructionDescriptor(64, SpvOpStore, 0), 1),
       MakeDataDescriptor(101, {0}), 202);
   ASSERT_TRUE(replacement_3.IsApplicable(context.get(), fact_manager));
   replacement_3.Apply(context.get(), &fact_manager);
@@ -1939,7 +1987,8 @@
 
   // Replace %19 with %101[2] in '%81 = OpVectorShuffle %16 %19 %19 0 0 1'
   auto replacement_4 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(19, SpvOpVectorShuffle, 0, 81, 0),
+      MakeIdUseDescriptor(
+          19, MakeInstructionDescriptor(81, SpvOpVectorShuffle, 0), 0),
       MakeDataDescriptor(101, {2}), 203);
   ASSERT_TRUE(replacement_4.IsApplicable(context.get(), fact_manager));
   replacement_4.Apply(context.get(), &fact_manager);
@@ -1948,8 +1997,8 @@
   // Replace %27 with %102[0] in '%82 = OpCompositeConstruct %21 %26 %27 %28
   // %25'
   auto replacement_5 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(27, SpvOpCompositeConstruct, 1, 82,
-                                          0),
+      MakeIdUseDescriptor(
+          27, MakeInstructionDescriptor(82, SpvOpCompositeConstruct, 0), 1),
       MakeDataDescriptor(102, {0}), 204);
   ASSERT_TRUE(replacement_5.IsApplicable(context.get(), fact_manager));
   replacement_5.Apply(context.get(), &fact_manager);
@@ -1957,7 +2006,8 @@
 
   // Replace %15 with %102[1] in '%83 = OpCopyObject %10 %15'
   auto replacement_6 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(15, SpvOpCopyObject, 0, 83, 0),
+      MakeIdUseDescriptor(15, MakeInstructionDescriptor(83, SpvOpCopyObject, 0),
+                          0),
       MakeDataDescriptor(102, {1}), 205);
   ASSERT_TRUE(replacement_6.IsApplicable(context.get(), fact_manager));
   replacement_6.Apply(context.get(), &fact_manager);
@@ -1965,7 +2015,8 @@
 
   // Replace %33 with %103[0] in '%86 = OpCopyObject %30 %33'
   auto replacement_7 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(33, SpvOpCopyObject, 0, 86, 0),
+      MakeIdUseDescriptor(33, MakeInstructionDescriptor(86, SpvOpCopyObject, 0),
+                          0),
       MakeDataDescriptor(103, {0}), 206);
   ASSERT_TRUE(replacement_7.IsApplicable(context.get(), fact_manager));
   replacement_7.Apply(context.get(), &fact_manager);
@@ -1973,7 +2024,8 @@
 
   // Replace %47 with %103[1] in '%84 = OpCopyObject %39 %47'
   auto replacement_8 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(47, SpvOpCopyObject, 0, 84, 0),
+      MakeIdUseDescriptor(47, MakeInstructionDescriptor(84, SpvOpCopyObject, 0),
+                          0),
       MakeDataDescriptor(103, {1}), 207);
   ASSERT_TRUE(replacement_8.IsApplicable(context.get(), fact_manager));
   replacement_8.Apply(context.get(), &fact_manager);
@@ -1981,7 +2033,8 @@
 
   // Replace %42 with %104[0] in '%85 = OpCopyObject %30 %42'
   auto replacement_9 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(42, SpvOpCopyObject, 0, 85, 0),
+      MakeIdUseDescriptor(42, MakeInstructionDescriptor(85, SpvOpCopyObject, 0),
+                          0),
       MakeDataDescriptor(104, {0}), 208);
   ASSERT_TRUE(replacement_9.IsApplicable(context.get(), fact_manager));
   replacement_9.Apply(context.get(), &fact_manager);
@@ -1989,7 +2042,8 @@
 
   // Replace %45 with %104[1] in '%63 = OpLogicalOr %30 %45 %46'
   auto replacement_10 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(45, SpvOpLogicalOr, 0, 63, 0),
+      MakeIdUseDescriptor(45, MakeInstructionDescriptor(63, SpvOpLogicalOr, 0),
+                          0),
       MakeDataDescriptor(104, {1}), 209);
   ASSERT_TRUE(replacement_10.IsApplicable(context.get(), fact_manager));
   replacement_10.Apply(context.get(), &fact_manager);
@@ -1997,7 +2051,7 @@
 
   // Replace %38 with %105[0] in 'OpStore %36 %38'
   auto replacement_11 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(38, SpvOpStore, 1, 85, 0),
+      MakeIdUseDescriptor(38, MakeInstructionDescriptor(85, SpvOpStore, 0), 1),
       MakeDataDescriptor(105, {0}), 210);
   ASSERT_TRUE(replacement_11.IsApplicable(context.get(), fact_manager));
   replacement_11.Apply(context.get(), &fact_manager);
@@ -2005,7 +2059,8 @@
 
   // Replace %46 with %105[2] in '%62 = OpLogicalAnd %30 %45 %46'
   auto replacement_12 = TransformationReplaceIdWithSynonym(
-      transformation::MakeIdUseDescriptor(46, SpvOpLogicalAnd, 1, 62, 0),
+      MakeIdUseDescriptor(46, MakeInstructionDescriptor(62, SpvOpLogicalAnd, 0),
+                          1),
       MakeDataDescriptor(105, {2}), 211);
   ASSERT_TRUE(replacement_12.IsApplicable(context.get(), fact_manager));
   replacement_12.Apply(context.get(), &fact_manager);