spirv-fuzz: Optimize transformations (#4216)

Avoids blanket invalidation of analyses in several transformations,
instead updating the def-use manager and instruction to block mapping.
diff --git a/source/fuzz/transformation_access_chain.cpp b/source/fuzz/transformation_access_chain.cpp
index 7eeda31..97d4e04 100644
--- a/source/fuzz/transformation_access_chain.cpp
+++ b/source/fuzz/transformation_access_chain.cpp
@@ -228,6 +228,11 @@
 
   uint32_t id_pairs_used = 0;
 
+  opt::Instruction* instruction_to_insert_before =
+      FindInstruction(message_.instruction_to_insert_before(), ir_context);
+  opt::BasicBlock* enclosing_block =
+      ir_context->get_instr_block(instruction_to_insert_before);
+
   // Go through the index ids in turn.
   for (auto index_id : message_.index_id()) {
     uint32_t index_value;
@@ -280,29 +285,37 @@
 
       // Clamp the integer and add the corresponding instructions in the module
       // if |add_clamping_instructions| is set.
-      auto instruction_to_insert_before =
-          FindInstruction(message_.instruction_to_insert_before(), ir_context);
 
       // Compare the index with the bound via an instruction of the form:
       //   %fresh_ids.first = OpULessThanEqual %bool %int_id %bound_minus_one.
       fuzzerutil::UpdateModuleIdBound(ir_context, fresh_ids.first());
-      instruction_to_insert_before->InsertBefore(MakeUnique<opt::Instruction>(
+      auto comparison_instruction = MakeUnique<opt::Instruction>(
           ir_context, SpvOpULessThanEqual, bool_type_id, fresh_ids.first(),
           opt::Instruction::OperandList(
               {{SPV_OPERAND_TYPE_ID, {index_instruction->result_id()}},
-               {SPV_OPERAND_TYPE_ID, {bound_minus_one_id}}})));
+               {SPV_OPERAND_TYPE_ID, {bound_minus_one_id}}}));
+      auto comparison_instruction_ptr = comparison_instruction.get();
+      instruction_to_insert_before->InsertBefore(
+          std::move(comparison_instruction));
+      ir_context->get_def_use_mgr()->AnalyzeInstDefUse(
+          comparison_instruction_ptr);
+      ir_context->set_instr_block(comparison_instruction_ptr, enclosing_block);
 
       // Select the index if in-bounds, otherwise one less than the bound:
       //   %fresh_ids.second = OpSelect %int_type %fresh_ids.first %int_id
       //                           %bound_minus_one
       fuzzerutil::UpdateModuleIdBound(ir_context, fresh_ids.second());
-      instruction_to_insert_before->InsertBefore(MakeUnique<opt::Instruction>(
+      auto select_instruction = MakeUnique<opt::Instruction>(
           ir_context, SpvOpSelect, int_type_inst->result_id(),
           fresh_ids.second(),
           opt::Instruction::OperandList(
               {{SPV_OPERAND_TYPE_ID, {fresh_ids.first()}},
                {SPV_OPERAND_TYPE_ID, {index_instruction->result_id()}},
-               {SPV_OPERAND_TYPE_ID, {bound_minus_one_id}}})));
+               {SPV_OPERAND_TYPE_ID, {bound_minus_one_id}}}));
+      auto select_instruction_ptr = select_instruction.get();
+      instruction_to_insert_before->InsertBefore(std::move(select_instruction));
+      ir_context->get_def_use_mgr()->AnalyzeInstDefUse(select_instruction_ptr);
+      ir_context->set_instr_block(select_instruction_ptr, enclosing_block);
 
       new_index_id = fresh_ids.second();
 
@@ -326,13 +339,14 @@
   // Add the access chain instruction to the module, and update the module's
   // id bound.
   fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
-  FindInstruction(message_.instruction_to_insert_before(), ir_context)
-      ->InsertBefore(MakeUnique<opt::Instruction>(
-          ir_context, SpvOpAccessChain, result_type, message_.fresh_id(),
-          operands));
-
-  // Conservatively invalidate all analyses.
-  ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+  auto access_chain_instruction = MakeUnique<opt::Instruction>(
+      ir_context, SpvOpAccessChain, result_type, message_.fresh_id(), operands);
+  auto access_chain_instruction_ptr = access_chain_instruction.get();
+  instruction_to_insert_before->InsertBefore(
+      std::move(access_chain_instruction));
+  ir_context->get_def_use_mgr()->AnalyzeInstDefUse(
+      access_chain_instruction_ptr);
+  ir_context->set_instr_block(access_chain_instruction_ptr, enclosing_block);
 
   // If the base pointer's pointee value was irrelevant, the same is true of
   // the pointee value of the result of this access chain.
diff --git a/source/fuzz/transformation_add_copy_memory.cpp b/source/fuzz/transformation_add_copy_memory.cpp
index 1fcb9ed..5eb4bdc 100644
--- a/source/fuzz/transformation_add_copy_memory.cpp
+++ b/source/fuzz/transformation_add_copy_memory.cpp
@@ -99,15 +99,8 @@
   auto* insert_before_inst =
       FindInstruction(message_.instruction_descriptor(), ir_context);
   assert(insert_before_inst);
-
-  auto insert_before_iter = fuzzerutil::GetIteratorForInstruction(
-      ir_context->get_instr_block(insert_before_inst), insert_before_inst);
-
-  insert_before_iter.InsertBefore(MakeUnique<opt::Instruction>(
-      ir_context, SpvOpCopyMemory, 0, 0,
-      opt::Instruction::OperandList{
-          {SPV_OPERAND_TYPE_ID, {message_.fresh_id()}},
-          {SPV_OPERAND_TYPE_ID, {message_.source_id()}}}));
+  opt::BasicBlock* enclosing_block =
+      ir_context->get_instr_block(insert_before_inst);
 
   // Add global or local variable to copy memory into.
   auto storage_class = static_cast<SpvStorageClass>(message_.storage_class());
@@ -118,23 +111,35 @@
       storage_class);
 
   if (storage_class == SpvStorageClassPrivate) {
-    fuzzerutil::AddGlobalVariable(ir_context, message_.fresh_id(), type_id,
-                                  storage_class, message_.initializer_id());
+    opt::Instruction* new_global =
+        fuzzerutil::AddGlobalVariable(ir_context, message_.fresh_id(), type_id,
+                                      storage_class, message_.initializer_id());
+    ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_global);
   } else {
     assert(storage_class == SpvStorageClassFunction &&
            "Storage class can be either Private or Function");
-    fuzzerutil::AddLocalVariable(ir_context, message_.fresh_id(), type_id,
-                                 ir_context->get_instr_block(insert_before_inst)
-                                     ->GetParent()
-                                     ->result_id(),
-                                 message_.initializer_id());
+    opt::Function* enclosing_function = enclosing_block->GetParent();
+    opt::Instruction* new_local = fuzzerutil::AddLocalVariable(
+        ir_context, message_.fresh_id(), type_id,
+        enclosing_function->result_id(), message_.initializer_id());
+    ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_local);
+    ir_context->set_instr_block(new_local, &*enclosing_function->entry());
   }
 
-  fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
+  auto insert_before_iter = fuzzerutil::GetIteratorForInstruction(
+      enclosing_block, insert_before_inst);
 
-  // Make sure our changes are analyzed
-  ir_context->InvalidateAnalysesExceptFor(
-      opt::IRContext::Analysis::kAnalysisNone);
+  auto new_instruction = MakeUnique<opt::Instruction>(
+      ir_context, SpvOpCopyMemory, 0, 0,
+      opt::Instruction::OperandList{
+          {SPV_OPERAND_TYPE_ID, {message_.fresh_id()}},
+          {SPV_OPERAND_TYPE_ID, {message_.source_id()}}});
+  auto new_instruction_ptr = new_instruction.get();
+  insert_before_iter.InsertBefore(std::move(new_instruction));
+  ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr);
+  ir_context->set_instr_block(new_instruction_ptr, enclosing_block);
+
+  fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
 
   // Even though the copy memory instruction will - at least temporarily - lead
   // to the destination and source pointers referring to identical values, this
diff --git a/source/fuzz/transformation_add_synonym.cpp b/source/fuzz/transformation_add_synonym.cpp
index a516916..40768e2 100644
--- a/source/fuzz/transformation_add_synonym.cpp
+++ b/source/fuzz/transformation_add_synonym.cpp
@@ -102,14 +102,19 @@
     opt::IRContext* ir_context,
     TransformationContext* transformation_context) const {
   // Add a synonymous instruction.
-  FindInstruction(message_.insert_before(), ir_context)
-      ->InsertBefore(
-          MakeSynonymousInstruction(ir_context, *transformation_context));
+  auto new_instruction =
+      MakeSynonymousInstruction(ir_context, *transformation_context);
+  auto new_instruction_ptr = new_instruction.get();
+  auto insert_before = FindInstruction(message_.insert_before(), ir_context);
+  insert_before->InsertBefore(std::move(new_instruction));
 
   fuzzerutil::UpdateModuleIdBound(ir_context, message_.synonym_fresh_id());
 
-  ir_context->InvalidateAnalysesExceptFor(
-      opt::IRContext::Analysis::kAnalysisNone);
+  // Inform the def-use manager about the new instruction and record its basic
+  // block.
+  ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr);
+  ir_context->set_instr_block(new_instruction_ptr,
+                              ir_context->get_instr_block(insert_before));
 
   // Propagate PointeeValueIsIrrelevant fact.
   const auto* new_synonym_type = ir_context->get_type_mgr()->GetType(
diff --git a/source/fuzz/transformation_composite_extract.cpp b/source/fuzz/transformation_composite_extract.cpp
index 8031be9..647cd74 100644
--- a/source/fuzz/transformation_composite_extract.cpp
+++ b/source/fuzz/transformation_composite_extract.cpp
@@ -91,13 +91,12 @@
 
   auto insert_before =
       FindInstruction(message_.instruction_to_insert_before(), ir_context);
-  auto new_instruction = MakeUnique<opt::Instruction>(
-      ir_context, SpvOpCompositeExtract, extracted_type, message_.fresh_id(),
-      extract_operands);
-  auto new_instruction_ptr = new_instruction.get();
-  insert_before->InsertBefore(std::move(new_instruction));
-  ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr);
-  ir_context->set_instr_block(new_instruction_ptr,
+  opt::Instruction* new_instruction =
+      insert_before->InsertBefore(MakeUnique<opt::Instruction>(
+          ir_context, SpvOpCompositeExtract, extracted_type,
+          message_.fresh_id(), extract_operands));
+  ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction);
+  ir_context->set_instr_block(new_instruction,
                               ir_context->get_instr_block(insert_before));
 
   fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
diff --git a/source/fuzz/transformation_composite_insert.cpp b/source/fuzz/transformation_composite_insert.cpp
index 9e7480c..14c3a1b 100644
--- a/source/fuzz/transformation_composite_insert.cpp
+++ b/source/fuzz/transformation_composite_insert.cpp
@@ -124,15 +124,21 @@
   auto composite_type_id =
       fuzzerutil::GetTypeId(ir_context, message_.composite_id());
 
-  FindInstruction(message_.instruction_to_insert_before(), ir_context)
-      ->InsertBefore(MakeUnique<opt::Instruction>(
-          ir_context, SpvOpCompositeInsert, composite_type_id,
-          message_.fresh_id(), std::move(in_operands)));
+  auto insert_before =
+      FindInstruction(message_.instruction_to_insert_before(), ir_context);
+  auto new_instruction = MakeUnique<opt::Instruction>(
+      ir_context, SpvOpCompositeInsert, composite_type_id, message_.fresh_id(),
+      std::move(in_operands));
+  auto new_instruction_ptr = new_instruction.get();
+  insert_before->InsertBefore(std::move(new_instruction));
 
   fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
 
-  // We have modified the module so most analyzes are now invalid.
-  ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+  // Inform the def-use manager about the new instruction and record its basic
+  // block.
+  ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr);
+  ir_context->set_instr_block(new_instruction_ptr,
+                              ir_context->get_instr_block(insert_before));
 
   // Add data synonym facts that arise from the insertion.
   AddDataSynonymFacts(ir_context, transformation_context);
diff --git a/source/fuzz/transformation_equation_instruction.cpp b/source/fuzz/transformation_equation_instruction.cpp
index ae3a468..1e5dae9 100644
--- a/source/fuzz/transformation_equation_instruction.cpp
+++ b/source/fuzz/transformation_equation_instruction.cpp
@@ -84,18 +84,16 @@
     rhs_id.push_back(id);
   }
 
-  auto new_instruction = MakeUnique<opt::Instruction>(
-      ir_context, static_cast<SpvOp>(message_.opcode()),
-      MaybeGetResultTypeId(ir_context), message_.fresh_id(),
-      std::move(in_operands));
-  auto new_instruction_ptr = new_instruction.get();
-
   auto insert_before =
       FindInstruction(message_.instruction_to_insert_before(), ir_context);
-  insert_before->InsertBefore(std::move(new_instruction));
+  opt::Instruction* new_instruction =
+      insert_before->InsertBefore(MakeUnique<opt::Instruction>(
+          ir_context, static_cast<SpvOp>(message_.opcode()),
+          MaybeGetResultTypeId(ir_context), message_.fresh_id(),
+          std::move(in_operands)));
 
-  ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr);
-  ir_context->set_instr_block(new_instruction_ptr,
+  ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction);
+  ir_context->set_instr_block(new_instruction,
                               ir_context->get_instr_block(insert_before));
 
   // Add an equation fact as long as the result id is not irrelevant (it could
diff --git a/source/fuzz/transformation_load.cpp b/source/fuzz/transformation_load.cpp
index c04698f..e22f8dd 100644
--- a/source/fuzz/transformation_load.cpp
+++ b/source/fuzz/transformation_load.cpp
@@ -83,12 +83,19 @@
   uint32_t result_type = fuzzerutil::GetPointeeTypeIdFromPointerType(
       ir_context, fuzzerutil::GetTypeId(ir_context, message_.pointer_id()));
   fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
-  FindInstruction(message_.instruction_to_insert_before(), ir_context)
-      ->InsertBefore(MakeUnique<opt::Instruction>(
-          ir_context, SpvOpLoad, result_type, message_.fresh_id(),
-          opt::Instruction::OperandList(
-              {{SPV_OPERAND_TYPE_ID, {message_.pointer_id()}}})));
-  ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+  auto insert_before =
+      FindInstruction(message_.instruction_to_insert_before(), ir_context);
+  auto new_instruction = MakeUnique<opt::Instruction>(
+      ir_context, SpvOpLoad, result_type, message_.fresh_id(),
+      opt::Instruction::OperandList(
+          {{SPV_OPERAND_TYPE_ID, {message_.pointer_id()}}}));
+  auto new_instruction_ptr = new_instruction.get();
+  insert_before->InsertBefore(std::move(new_instruction));
+  // Inform the def-use manager about the new instruction and record its basic
+  // block.
+  ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr);
+  ir_context->set_instr_block(new_instruction_ptr,
+                              ir_context->get_instr_block(insert_before));
 }
 
 protobufs::Transformation TransformationLoad::ToMessage() const {
diff --git a/source/fuzz/transformation_mutate_pointer.cpp b/source/fuzz/transformation_mutate_pointer.cpp
index 9ee2360..516a0d6 100644
--- a/source/fuzz/transformation_mutate_pointer.cpp
+++ b/source/fuzz/transformation_mutate_pointer.cpp
@@ -92,36 +92,47 @@
   auto* insert_before_inst =
       FindInstruction(message_.insert_before(), ir_context);
   assert(insert_before_inst && "|insert_before| descriptor is invalid");
+  opt::BasicBlock* enclosing_block =
+      ir_context->get_instr_block(insert_before_inst);
 
   auto pointee_type_id = fuzzerutil::GetPointeeTypeIdFromPointerType(
       ir_context, fuzzerutil::GetTypeId(ir_context, message_.pointer_id()));
 
   // Back up the original value.
-  insert_before_inst->InsertBefore(MakeUnique<opt::Instruction>(
+  auto backup_instruction = MakeUnique<opt::Instruction>(
       ir_context, SpvOpLoad, pointee_type_id, message_.fresh_id(),
       opt::Instruction::OperandList{
-          {SPV_OPERAND_TYPE_ID, {message_.pointer_id()}}}));
+          {SPV_OPERAND_TYPE_ID, {message_.pointer_id()}}});
+  auto backup_instruction_ptr = backup_instruction.get();
+  insert_before_inst->InsertBefore(std::move(backup_instruction));
+  ir_context->get_def_use_mgr()->AnalyzeInstDefUse(backup_instruction_ptr);
+  ir_context->set_instr_block(backup_instruction_ptr, enclosing_block);
 
   // Insert a new value.
-  insert_before_inst->InsertBefore(MakeUnique<opt::Instruction>(
+  auto new_value_instruction = MakeUnique<opt::Instruction>(
       ir_context, SpvOpStore, 0, 0,
       opt::Instruction::OperandList{
           {SPV_OPERAND_TYPE_ID, {message_.pointer_id()}},
           {SPV_OPERAND_TYPE_ID,
            {fuzzerutil::MaybeGetZeroConstant(
-               ir_context, *transformation_context, pointee_type_id, true)}}}));
+               ir_context, *transformation_context, pointee_type_id, true)}}});
+  auto new_value_instruction_ptr = new_value_instruction.get();
+  insert_before_inst->InsertBefore(std::move(new_value_instruction));
+  ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_value_instruction_ptr);
+  ir_context->set_instr_block(new_value_instruction_ptr, enclosing_block);
 
   // Restore the original value.
-  insert_before_inst->InsertBefore(MakeUnique<opt::Instruction>(
+  auto restore_instruction = MakeUnique<opt::Instruction>(
       ir_context, SpvOpStore, 0, 0,
       opt::Instruction::OperandList{
           {SPV_OPERAND_TYPE_ID, {message_.pointer_id()}},
-          {SPV_OPERAND_TYPE_ID, {message_.fresh_id()}}}));
+          {SPV_OPERAND_TYPE_ID, {message_.fresh_id()}}});
+  auto restore_instruction_ptr = restore_instruction.get();
+  insert_before_inst->InsertBefore(std::move(restore_instruction));
+  ir_context->get_def_use_mgr()->AnalyzeInstDefUse(restore_instruction_ptr);
+  ir_context->set_instr_block(restore_instruction_ptr, enclosing_block);
 
   fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
-
-  // Make sure analyses represent the correct state of the module.
-  ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
 }
 
 protobufs::Transformation TransformationMutatePointer::ToMessage() const {
diff --git a/source/fuzz/transformation_push_id_through_variable.cpp b/source/fuzz/transformation_push_id_through_variable.cpp
index 06473a6..0df1da6 100644
--- a/source/fuzz/transformation_push_id_through_variable.cpp
+++ b/source/fuzz/transformation_push_id_through_variable.cpp
@@ -104,6 +104,10 @@
   auto value_instruction =
       ir_context->get_def_use_mgr()->GetDef(message_.value_id());
 
+  opt::Instruction* insert_before =
+      FindInstruction(message_.instruction_descriptor(), ir_context);
+  opt::BasicBlock* enclosing_block = ir_context->get_instr_block(insert_before);
+
   // A pointer type instruction pointing to the value type must be defined.
   auto pointer_type_id = fuzzerutil::MaybeGetPointerType(
       ir_context, value_instruction->type_id(),
@@ -112,36 +116,42 @@
 
   // Adds whether a global or local variable.
   if (message_.variable_storage_class() == SpvStorageClassPrivate) {
-    fuzzerutil::AddGlobalVariable(ir_context, message_.variable_id(),
-                                  pointer_type_id, SpvStorageClassPrivate,
-                                  message_.initializer_id());
+    opt::Instruction* global_variable = fuzzerutil::AddGlobalVariable(
+        ir_context, message_.variable_id(), pointer_type_id,
+        SpvStorageClassPrivate, message_.initializer_id());
+    ir_context->get_def_use_mgr()->AnalyzeInstDefUse(global_variable);
   } else {
-    auto function_id = ir_context
-                           ->get_instr_block(FindInstruction(
-                               message_.instruction_descriptor(), ir_context))
-                           ->GetParent()
-                           ->result_id();
-    fuzzerutil::AddLocalVariable(ir_context, message_.variable_id(),
-                                 pointer_type_id, function_id,
-                                 message_.initializer_id());
+    opt::Function* function =
+        ir_context
+            ->get_instr_block(
+                FindInstruction(message_.instruction_descriptor(), ir_context))
+            ->GetParent();
+    opt::Instruction* local_variable = fuzzerutil::AddLocalVariable(
+        ir_context, message_.variable_id(), pointer_type_id,
+        function->result_id(), message_.initializer_id());
+    ir_context->get_def_use_mgr()->AnalyzeInstDefUse(local_variable);
+    ir_context->set_instr_block(local_variable, &*function->entry());
   }
 
   // First, insert the OpLoad instruction before |instruction_descriptor| and
   // then insert the OpStore instruction before the OpLoad instruction.
   fuzzerutil::UpdateModuleIdBound(ir_context, message_.value_synonym_id());
-  FindInstruction(message_.instruction_descriptor(), ir_context)
-      ->InsertBefore(MakeUnique<opt::Instruction>(
+  opt::Instruction* load_instruction =
+      insert_before->InsertBefore(MakeUnique<opt::Instruction>(
           ir_context, SpvOpLoad, value_instruction->type_id(),
           message_.value_synonym_id(),
           opt::Instruction::OperandList(
-              {{SPV_OPERAND_TYPE_ID, {message_.variable_id()}}})))
-      ->InsertBefore(MakeUnique<opt::Instruction>(
+              {{SPV_OPERAND_TYPE_ID, {message_.variable_id()}}})));
+  opt::Instruction* store_instruction =
+      load_instruction->InsertBefore(MakeUnique<opt::Instruction>(
           ir_context, SpvOpStore, 0, 0,
           opt::Instruction::OperandList(
               {{SPV_OPERAND_TYPE_ID, {message_.variable_id()}},
                {SPV_OPERAND_TYPE_ID, {message_.value_id()}}})));
-
-  ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+  ir_context->get_def_use_mgr()->AnalyzeInstDefUse(store_instruction);
+  ir_context->set_instr_block(store_instruction, enclosing_block);
+  ir_context->get_def_use_mgr()->AnalyzeInstDefUse(load_instruction);
+  ir_context->set_instr_block(load_instruction, enclosing_block);
 
   // We should be able to create a synonym of |value_id| if it's not irrelevant.
   if (fuzzerutil::CanMakeSynonymOf(ir_context, *transformation_context,
diff --git a/source/fuzz/transformation_replace_constant_with_uniform.cpp b/source/fuzz/transformation_replace_constant_with_uniform.cpp
index 99bcbff..c6698c0 100644
--- a/source/fuzz/transformation_replace_constant_with_uniform.cpp
+++ b/source/fuzz/transformation_replace_constant_with_uniform.cpp
@@ -253,28 +253,39 @@
   auto* insert_before_inst = GetInsertBeforeInstruction(ir_context);
   assert(insert_before_inst &&
          "There must exist an insertion point for OpAccessChain and OpLoad");
+  opt::BasicBlock* enclosing_block =
+      ir_context->get_instr_block(insert_before_inst);
 
   // Add an access chain instruction to target the uniform element.
-  insert_before_inst->InsertBefore(
-      MakeAccessChainInstruction(ir_context, constant_type_id));
+  auto access_chain_instruction =
+      MakeAccessChainInstruction(ir_context, constant_type_id);
+  auto access_chain_instruction_ptr = access_chain_instruction.get();
+  insert_before_inst->InsertBefore(std::move(access_chain_instruction));
+  ir_context->get_def_use_mgr()->AnalyzeInstDefUse(
+      access_chain_instruction_ptr);
+  ir_context->set_instr_block(access_chain_instruction_ptr, enclosing_block);
 
   // Add a load from this access chain.
-  insert_before_inst->InsertBefore(
-      MakeLoadInstruction(ir_context, constant_type_id));
+  auto load_instruction = MakeLoadInstruction(ir_context, constant_type_id);
+  auto load_instruction_ptr = load_instruction.get();
+  insert_before_inst->InsertBefore(std::move(load_instruction));
+  ir_context->get_def_use_mgr()->AnalyzeInstDefUse(load_instruction_ptr);
+  ir_context->set_instr_block(load_instruction_ptr, enclosing_block);
 
   // Adjust the instruction containing the usage of the constant so that this
   // usage refers instead to the result of the load.
   instruction_containing_constant_use->SetInOperand(
       message_.id_use_descriptor().in_operand_index(),
       {message_.fresh_id_for_load()});
+  ir_context->get_def_use_mgr()->EraseUseRecordsOfOperandIds(
+      instruction_containing_constant_use);
+  ir_context->get_def_use_mgr()->AnalyzeInstUse(
+      instruction_containing_constant_use);
 
   // Update the module id bound to reflect the new instructions.
   fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id_for_load());
   fuzzerutil::UpdateModuleIdBound(ir_context,
                                   message_.fresh_id_for_access_chain());
-
-  ir_context->InvalidateAnalysesExceptFor(
-      opt::IRContext::Analysis::kAnalysisNone);
 }
 
 protobufs::Transformation TransformationReplaceConstantWithUniform::ToMessage()
diff --git a/source/fuzz/transformation_replace_copy_object_with_store_load.cpp b/source/fuzz/transformation_replace_copy_object_with_store_load.cpp
index 0b5b453..e0643bf 100644
--- a/source/fuzz/transformation_replace_copy_object_with_store_load.cpp
+++ b/source/fuzz/transformation_replace_copy_object_with_store_load.cpp
@@ -87,6 +87,10 @@
   assert(copy_object_instruction &&
          copy_object_instruction->opcode() == SpvOpCopyObject &&
          "The required OpCopyObject instruction must be defined.");
+
+  opt::BasicBlock* enclosing_block =
+      ir_context->get_instr_block(copy_object_instruction);
+
   // Get id used as a source by the OpCopyObject instruction.
   uint32_t src_operand = copy_object_instruction->GetSingleWordInOperand(0);
   // A pointer type instruction pointing to the value type must be defined.
@@ -97,37 +101,46 @@
 
   // Adds a global or local variable (according to the storage class).
   if (message_.variable_storage_class() == SpvStorageClassPrivate) {
-    fuzzerutil::AddGlobalVariable(ir_context, message_.fresh_variable_id(),
-                                  pointer_type_id, SpvStorageClassPrivate,
-                                  message_.variable_initializer_id());
+    opt::Instruction* new_global = fuzzerutil::AddGlobalVariable(
+        ir_context, message_.fresh_variable_id(), pointer_type_id,
+        SpvStorageClassPrivate, message_.variable_initializer_id());
+    ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_global);
   } else {
-    auto function_id = ir_context->get_instr_block(copy_object_instruction)
-                           ->GetParent()
-                           ->result_id();
-    fuzzerutil::AddLocalVariable(ir_context, message_.fresh_variable_id(),
-                                 pointer_type_id, function_id,
-                                 message_.variable_initializer_id());
+    opt::Function* function =
+        ir_context->get_instr_block(copy_object_instruction)->GetParent();
+    opt::Instruction* new_local = fuzzerutil::AddLocalVariable(
+        ir_context, message_.fresh_variable_id(), pointer_type_id,
+        function->result_id(), message_.variable_initializer_id());
+    ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_local);
+    ir_context->set_instr_block(new_local, &*function->begin());
   }
 
   // First, insert the OpLoad instruction before the OpCopyObject instruction
   // and then insert the OpStore instruction before the OpLoad instruction.
   fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_variable_id());
-  copy_object_instruction
-      ->InsertBefore(MakeUnique<opt::Instruction>(
+  opt::Instruction* load_instruction =
+      copy_object_instruction->InsertBefore(MakeUnique<opt::Instruction>(
           ir_context, SpvOpLoad, copy_object_instruction->type_id(),
           message_.copy_object_result_id(),
           opt::Instruction::OperandList(
-              {{SPV_OPERAND_TYPE_ID, {message_.fresh_variable_id()}}})))
-      ->InsertBefore(MakeUnique<opt::Instruction>(
+              {{SPV_OPERAND_TYPE_ID, {message_.fresh_variable_id()}}})));
+  opt::Instruction* store_instruction =
+      load_instruction->InsertBefore(MakeUnique<opt::Instruction>(
           ir_context, SpvOpStore, 0, 0,
           opt::Instruction::OperandList(
               {{SPV_OPERAND_TYPE_ID, {message_.fresh_variable_id()}},
                {SPV_OPERAND_TYPE_ID, {src_operand}}})));
+
+  // Register the new instructions with the def-use manager, and record their
+  // enclosing block.
+  ir_context->get_def_use_mgr()->AnalyzeInstDefUse(store_instruction);
+  ir_context->get_def_use_mgr()->AnalyzeInstDefUse(load_instruction);
+  ir_context->set_instr_block(store_instruction, enclosing_block);
+  ir_context->set_instr_block(load_instruction, enclosing_block);
+
   // Remove the CopyObject instruction.
   ir_context->KillInst(copy_object_instruction);
 
-  ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
-
   if (!transformation_context->GetFactManager()->IdIsIrrelevant(
           message_.copy_object_result_id()) &&
       !transformation_context->GetFactManager()->IdIsIrrelevant(src_operand)) {
diff --git a/source/fuzz/transformation_replace_irrelevant_id.cpp b/source/fuzz/transformation_replace_irrelevant_id.cpp
index 6ffba9e..a71f96a 100644
--- a/source/fuzz/transformation_replace_irrelevant_id.cpp
+++ b/source/fuzz/transformation_replace_irrelevant_id.cpp
@@ -107,9 +107,12 @@
       message_.id_use_descriptor().in_operand_index(),
       {message_.replacement_id()});
 
-  // Invalidate the analyses, since the usage of ids has been changed.
-  ir_context->InvalidateAnalysesExceptFor(
-      opt::IRContext::Analysis::kAnalysisNone);
+  ir_context->get_def_use_mgr()->EraseUseRecordsOfOperandIds(
+      instruction_to_change);
+  ir_context->get_def_use_mgr()->AnalyzeInstUse(instruction_to_change);
+
+  // No analyses need to be invalidated, since the transformation is local to a
+  // block, and the def-use analysis has been updated.
 }
 
 protobufs::Transformation TransformationReplaceIrrelevantId::ToMessage() const {
diff --git a/source/fuzz/transformation_store.cpp b/source/fuzz/transformation_store.cpp
index 2017f15..f8c6d01 100644
--- a/source/fuzz/transformation_store.cpp
+++ b/source/fuzz/transformation_store.cpp
@@ -109,13 +109,20 @@
 
 void TransformationStore::Apply(opt::IRContext* ir_context,
                                 TransformationContext* /*unused*/) const {
-  FindInstruction(message_.instruction_to_insert_before(), ir_context)
-      ->InsertBefore(MakeUnique<opt::Instruction>(
-          ir_context, SpvOpStore, 0, 0,
-          opt::Instruction::OperandList(
-              {{SPV_OPERAND_TYPE_ID, {message_.pointer_id()}},
-               {SPV_OPERAND_TYPE_ID, {message_.value_id()}}})));
-  ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone);
+  auto insert_before =
+      FindInstruction(message_.instruction_to_insert_before(), ir_context);
+  auto new_instruction = MakeUnique<opt::Instruction>(
+      ir_context, SpvOpStore, 0, 0,
+      opt::Instruction::OperandList(
+          {{SPV_OPERAND_TYPE_ID, {message_.pointer_id()}},
+           {SPV_OPERAND_TYPE_ID, {message_.value_id()}}}));
+  auto new_instruction_ptr = new_instruction.get();
+  insert_before->InsertBefore(std::move(new_instruction));
+  // Inform the def-use manager about the new instruction and record its basic
+  // block.
+  ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr);
+  ir_context->set_instr_block(new_instruction_ptr,
+                              ir_context->get_instr_block(insert_before));
 }
 
 protobufs::Transformation TransformationStore::ToMessage() const {
diff --git a/source/fuzz/transformation_vector_shuffle.cpp b/source/fuzz/transformation_vector_shuffle.cpp
index ea34eee..ac0e3cc 100644
--- a/source/fuzz/transformation_vector_shuffle.cpp
+++ b/source/fuzz/transformation_vector_shuffle.cpp
@@ -130,13 +130,18 @@
 
   // Add a shuffle instruction right before the instruction identified by
   // |message_.instruction_to_insert_before|.
-  FindInstruction(message_.instruction_to_insert_before(), ir_context)
-      ->InsertBefore(MakeUnique<opt::Instruction>(
+  auto insert_before =
+      FindInstruction(message_.instruction_to_insert_before(), ir_context);
+  opt::Instruction* new_instruction =
+      insert_before->InsertBefore(MakeUnique<opt::Instruction>(
           ir_context, SpvOpVectorShuffle, result_type_id, message_.fresh_id(),
           shuffle_operands));
   fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id());
-  ir_context->InvalidateAnalysesExceptFor(
-      opt::IRContext::Analysis::kAnalysisNone);
+  // Inform the def-use manager about the new instruction and record its basic
+  // block.
+  ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction);
+  ir_context->set_instr_block(new_instruction,
+                              ir_context->get_instr_block(insert_before));
 
   AddDataSynonymFacts(ir_context, transformation_context);
 }
diff --git a/test/fuzz/transformation_push_id_through_variable_test.cpp b/test/fuzz/transformation_push_id_through_variable_test.cpp
index cd45c4c..7487cab 100644
--- a/test/fuzz/transformation_push_id_through_variable_test.cpp
+++ b/test/fuzz/transformation_push_id_through_variable_test.cpp
@@ -341,7 +341,26 @@
   auto transformation = TransformationPushIdThroughVariable(
       value_id, value_synonym_id, variable_id, variable_storage_class,
       initializer_id, instruction_descriptor);
+  ASSERT_EQ(nullptr, context->get_def_use_mgr()->GetDef(value_synonym_id));
+  ASSERT_EQ(nullptr, context->get_instr_block(value_synonym_id));
+  ASSERT_EQ(nullptr, context->get_def_use_mgr()->GetDef(variable_id));
+  ASSERT_EQ(nullptr, context->get_instr_block(variable_id));
   ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
+  ASSERT_EQ(SpvOpLoad,
+            context->get_def_use_mgr()->GetDef(value_synonym_id)->opcode());
+  ASSERT_EQ(36, context->get_instr_block(value_synonym_id)->id());
+  ASSERT_EQ(SpvOpVariable,
+            context->get_def_use_mgr()->GetDef(variable_id)->opcode());
+  ASSERT_EQ(5, context->get_instr_block(variable_id)->id());
+  uint32_t variable_use_count = 0;
+  context->get_def_use_mgr()->ForEachUse(
+      variable_id,
+      [&variable_use_count](opt::Instruction* inst, uint32_t /*unused*/) {
+        ASSERT_TRUE(inst->opcode() == SpvOpLoad ||
+                    inst->opcode() == SpvOpStore);
+        variable_use_count++;
+      });
+  ASSERT_EQ(2, variable_use_count);
 
   value_id = 21;
   value_synonym_id = 102;