Add the IRContext (part 1)

This is the first part of adding the IRContext.  This class is meant to
hold the extra data that is build on top of the module that it
owns.

The first part will simply create the IRContext class and get it passed
to the passes in place of the module.  For now it does not have any
functionality of its own, but it acts more as a wrapper for the module.

The functions that I added to the IRContext are those that either
traverse the headers or add to them.  I did this because we may decide
to have other ways of dealing with these sections (for example adding a
type pool, or use the decoration manager).

I also added the function that add to the header because the IRContext
needs to know when an instruction is added to update other data
structures appropriately.

Note that there is still lots of work that needs to be done.  There are
still many places that change the module, and do not inform the context.
That will be the next step.
diff --git a/source/link/linker.cpp b/source/link/linker.cpp
index 51e720a..9d35363 100644
--- a/source/link/linker.cpp
+++ b/source/link/linker.cpp
@@ -105,7 +105,7 @@
 // TODO(pierremoreau): What should be the proper behaviour with built-in
 //                     symbols?
 static spv_result_t GetImportExportPairs(const MessageConsumer& consumer,
-                                         const Module& linked_module,
+                                         const ir::IRContext& linked_context,
                                          const DefUseManager& def_use_manager,
                                          const DecorationManager& decoration_manager,
                                          LinkageTable* linkings_to_do);
@@ -135,7 +135,7 @@
 static spv_result_t RemoveLinkageSpecificInstructions(
     const MessageConsumer& consumer, bool create_executable,
     const LinkageTable& linkings_to_do, DecorationManager* decoration_manager,
-    Module* linked_module);
+    ir::IRContext* linked_context);
 
 // Structs for holding the data members for SpvLinker.
 struct Linker::Impl {
@@ -224,13 +224,14 @@
   libspirv::AssemblyGrammar grammar(impl_->context);
   res = MergeModules(consumer, modules, grammar, linked_module.get());
   if (res != SPV_SUCCESS) return res;
+  ir::IRContext linked_context(std::move(linked_module));
 
-  DefUseManager def_use_manager(consumer, linked_module.get());
+  DefUseManager def_use_manager(consumer, linked_context.module());
 
   // Phase 4: Find the import/export pairs
   LinkageTable linkings_to_do;
-  DecorationManager decoration_manager(linked_module.get());
-  res = GetImportExportPairs(consumer, *linked_module, def_use_manager,
+  DecorationManager decoration_manager(linked_context.module());
+  res = GetImportExportPairs(consumer, linked_context, def_use_manager,
                              decoration_manager, &linkings_to_do);
   if (res != SPV_SUCCESS) return res;
 
@@ -243,30 +244,30 @@
   PassManager manager;
   manager.SetMessageConsumer(consumer);
   manager.AddPass<RemoveDuplicatesPass>();
-  opt::Pass::Status pass_res = manager.Run(linked_module.get());
+  opt::Pass::Status pass_res = manager.Run(&linked_context);
   if (pass_res == opt::Pass::Status::Failure) return SPV_ERROR_INVALID_DATA;
 
   // Phase 7: Remove linkage specific instructions, such as import/export
   // attributes, linkage capability, etc. if applicable
   res = RemoveLinkageSpecificInstructions(consumer, !options.GetCreateLibrary(),
                                           linkings_to_do, &decoration_manager,
-                                          linked_module.get());
+                                          &linked_context);
   if (res != SPV_SUCCESS) return res;
 
   // Phase 8: Rematch import variables/functions to export variables/functions
   // TODO(pierremoreau): Keep the previous DefUseManager up-to-date
-  DefUseManager def_use_manager2(consumer, linked_module.get());
+  DefUseManager def_use_manager2(consumer, linked_context.module());
   for (const auto& linking_entry : linkings_to_do)
     def_use_manager2.ReplaceAllUsesWith(linking_entry.imported_symbol.id,
                                         linking_entry.exported_symbol.id);
 
   // Phase 9: Compact the IDs used in the module
   manager.AddPass<opt::CompactIdsPass>();
-  pass_res = manager.Run(linked_module.get());
+  pass_res = manager.Run(&linked_context);
   if (pass_res == opt::Pass::Status::Failure) return SPV_ERROR_INVALID_DATA;
 
   // Phase 10: Output the module
-  linked_module->ToBinary(&linked_binary, true);
+  linked_context.module()->ToBinary(&linked_binary, true);
 
   return SPV_SUCCESS;
 }
@@ -476,7 +477,7 @@
 }
 
 static spv_result_t GetImportExportPairs(const MessageConsumer& consumer,
-                                         const Module& linked_module,
+                                         const ir::IRContext& linked_context,
                                          const DefUseManager& def_use_manager,
                                          const DecorationManager& decoration_manager,
                                          LinkageTable* linkings_to_do) {
@@ -491,7 +492,7 @@
   std::unordered_map<std::string, std::vector<LinkageSymbolInfo>> exports;
 
   // Figure out the imports and exports
-  for (const auto& decoration : linked_module.annotations()) {
+  for (const auto& decoration : linked_context.annotations()) {
     if (decoration.opcode() != SpvOpDecorate ||
         decoration.GetSingleWordInOperand(1u) != SpvDecorationLinkageAttributes)
       continue;
@@ -532,8 +533,8 @@
 
       // range-based for loop calls begin()/end(), but never cbegin()/cend(),
       // which will not work here.
-      for (auto func_iter = linked_module.cbegin();
-           func_iter != linked_module.cend(); ++func_iter) {
+      for (auto func_iter = linked_context.module()->cbegin();
+           func_iter != linked_context.module()->cend(); ++func_iter) {
         if (func_iter->result_id() != id) continue;
         func_iter->ForEachParam([&symbol_info](const Instruction* inst) {
           symbol_info.parameter_ids.push_back(inst->result_id());
@@ -628,7 +629,7 @@
 static spv_result_t RemoveLinkageSpecificInstructions(
     const MessageConsumer& consumer, bool create_executable,
     const LinkageTable& linkings_to_do, DecorationManager* decoration_manager,
-    Module* linked_module) {
+    ir::IRContext* linked_context) {
   spv_position_t position = {};
 
   if (decoration_manager == nullptr)
@@ -638,7 +639,7 @@
               "should "
               "not "
               "be empty.";
-  if (linked_module == nullptr)
+  if (linked_context == nullptr)
     return libspirv::DiagnosticStream(position, consumer,
                                       SPV_ERROR_INVALID_DATA)
            << "|linked_module| of RemoveLinkageSpecificInstructions should not "
@@ -670,8 +671,8 @@
 
   // Remove prototypes of imported functions
   for (const auto& linking_entry : linkings_to_do) {
-    for (auto func_iter = linked_module->begin();
-         func_iter != linked_module->end();) {
+    for (auto func_iter = linked_context->module()->begin();
+         func_iter != linked_context->module()->end();) {
       if (func_iter->result_id() == linking_entry.imported_symbol.id)
         func_iter = func_iter.Erase();
       else
@@ -681,12 +682,12 @@
 
   // Remove declarations of imported variables
   for (const auto& linking_entry : linkings_to_do) {
-    for (auto& inst : linked_module->types_values())
+    for (auto& inst : linked_context->types_values())
       if (inst.result_id() == linking_entry.imported_symbol.id) inst.ToNop();
   }
 
   // Remove import linkage attributes
-  for (auto& inst : linked_module->annotations())
+  for (auto& inst : linked_context->annotations())
     if (inst.opcode() == SpvOpDecorate &&
         inst.GetSingleWordOperand(1u) == SpvDecorationLinkageAttributes &&
         inst.GetSingleWordOperand(3u) == SpvLinkageTypeImport)
@@ -695,13 +696,13 @@
   // Remove export linkage attributes and Linkage capability if making an
   // executable
   if (create_executable) {
-    for (auto& inst : linked_module->annotations())
+    for (auto& inst : linked_context->annotations())
       if (inst.opcode() == SpvOpDecorate &&
           inst.GetSingleWordOperand(1u) == SpvDecorationLinkageAttributes &&
           inst.GetSingleWordOperand(3u) == SpvLinkageTypeExport)
         inst.ToNop();
 
-    for (auto& inst : linked_module->capabilities())
+    for (auto& inst : linked_context->capabilities())
       if (inst.GetSingleWordInOperand(0u) == SpvCapabilityLinkage) {
         inst.ToNop();
         // The RemoveDuplicatesPass did remove duplicated capabilities, so we
diff --git a/source/opt/CMakeLists.txt b/source/opt/CMakeLists.txt
index 99fe747..e72f49c 100644
--- a/source/opt/CMakeLists.txt
+++ b/source/opt/CMakeLists.txt
@@ -36,6 +36,7 @@
   insert_extract_elim.h
   instruction.h
   ir_loader.h
+  ir_context.h
   local_access_chain_convert_pass.h
   local_single_block_elim_pass.h
   local_single_store_elim_pass.h
@@ -80,6 +81,7 @@
   insert_extract_elim.cpp
   instruction.cpp
   ir_loader.cpp
+  ir_context.cpp
   local_access_chain_convert_pass.cpp
   local_single_block_elim_pass.cpp
   local_single_store_elim_pass.cpp
@@ -97,7 +99,8 @@
   types.cpp
   type_manager.cpp
   unify_const_pass.cpp
-  instruction_list.cpp)
+  instruction_list.cpp
+)
 
 spvtools_default_compile_options(SPIRV-Tools-opt)
 target_include_directories(SPIRV-Tools-opt
diff --git a/source/opt/aggressive_dead_code_elim_pass.cpp b/source/opt/aggressive_dead_code_elim_pass.cpp
index 0be802a..e4dc5de 100644
--- a/source/opt/aggressive_dead_code_elim_pass.cpp
+++ b/source/opt/aggressive_dead_code_elim_pass.cpp
@@ -395,9 +395,9 @@
   return modified;
 }
 
-void AggressiveDCEPass::Initialize(ir::Module* module) {
-  InitializeProcessing(module);
-  InitializeCFGCleanup(module);
+void AggressiveDCEPass::Initialize(ir::IRContext* c) {
+  InitializeProcessing(c);
+  InitializeCFGCleanup(c);
 
   // Clear collections
   worklist_ = std::queue<ir::Instruction*>{};
@@ -436,8 +436,8 @@
 
 AggressiveDCEPass::AggressiveDCEPass() {}
 
-Pass::Status AggressiveDCEPass::Process(ir::Module* module) {
-  Initialize(module);
+Pass::Status AggressiveDCEPass::Process(ir::IRContext* c) {
+  Initialize(c);
   return ProcessImpl();
 }
 
diff --git a/source/opt/aggressive_dead_code_elim_pass.h b/source/opt/aggressive_dead_code_elim_pass.h
index e8a0f81..7e0bf37 100644
--- a/source/opt/aggressive_dead_code_elim_pass.h
+++ b/source/opt/aggressive_dead_code_elim_pass.h
@@ -43,7 +43,7 @@
 
   AggressiveDCEPass();
   const char* name() const override { return "eliminate-dead-code-aggressive"; }
-  Status Process(ir::Module*) override;
+  Status Process(ir::IRContext* c) override;
 
  private:
   // Return true if |varId| is variable of |storageClass|.
@@ -127,7 +127,7 @@
   // TODO(): Remove useless control constructs.
   bool AggressiveDCE(ir::Function* func);
 
-  void Initialize(ir::Module* module);
+  void Initialize(ir::IRContext* c);
   Pass::Status ProcessImpl();
 
   // True if current function has a call instruction contained in it
diff --git a/source/opt/block_merge_pass.cpp b/source/opt/block_merge_pass.cpp
index 8c3c161..470bb2a 100644
--- a/source/opt/block_merge_pass.cpp
+++ b/source/opt/block_merge_pass.cpp
@@ -17,6 +17,7 @@
 #include "block_merge_pass.h"
 
 #include "iterator.h"
+#include "ir_context.h"
 
 namespace spvtools {
 namespace opt {
@@ -94,8 +95,8 @@
   return modified;
 }
 
-void BlockMergePass::Initialize(ir::Module* module) {
-  InitializeProcessing(module);
+void BlockMergePass::Initialize(ir::IRContext* c) {
+  InitializeProcessing(c);
 
   // Initialize extension whitelist
   InitExtensions();
@@ -126,8 +127,8 @@
 
 BlockMergePass::BlockMergePass() {}
 
-Pass::Status BlockMergePass::Process(ir::Module* module) {
-  Initialize(module);
+Pass::Status BlockMergePass::Process(ir::IRContext* c) {
+  Initialize(c);
   return ProcessImpl();
 }
 
diff --git a/source/opt/block_merge_pass.h b/source/opt/block_merge_pass.h
index ee7e082..3ebd358 100644
--- a/source/opt/block_merge_pass.h
+++ b/source/opt/block_merge_pass.h
@@ -28,6 +28,7 @@
 #include "def_use_manager.h"
 #include "module.h"
 #include "pass.h"
+#include "ir_context.h"
 
 namespace spvtools {
 namespace opt {
@@ -37,7 +38,7 @@
  public:
   BlockMergePass();
   const char* name() const override { return "merge-blocks"; }
-  Status Process(ir::Module*) override;
+  Status Process(ir::IRContext*) override;
 
  private:
   // Return true if |labId| has multiple refs. Do not count OpName.
@@ -56,7 +57,7 @@
   // Return true if all extensions in this module are allowed by this pass.
   bool AllExtensionsSupported() const;
 
-  void Initialize(ir::Module* module);
+  void Initialize(ir::IRContext* c);
   Pass::Status ProcessImpl();
 
   // Extensions supported by this pass.
diff --git a/source/opt/cfg_cleanup_pass.cpp b/source/opt/cfg_cleanup_pass.cpp
index cb57290..946c85c 100644
--- a/source/opt/cfg_cleanup_pass.cpp
+++ b/source/opt/cfg_cleanup_pass.cpp
@@ -27,17 +27,17 @@
 namespace spvtools {
 namespace opt {
 
-void CFGCleanupPass::Initialize(ir::Module* module) {
-  InitializeProcessing(module);
-  InitializeCFGCleanup(module);
+void CFGCleanupPass::Initialize(ir::IRContext* c) {
+  InitializeProcessing(c);
+  InitializeCFGCleanup(c);
 }
 
-Pass::Status CFGCleanupPass::Process(ir::Module* module) {
-  Initialize(module);
+Pass::Status CFGCleanupPass::Process(ir::IRContext* c) {
+  Initialize(c);
 
   // Process all entry point functions.
   ProcessFunction pfn = [this](ir::Function* fp) { return CFGCleanup(fp); };
-  bool modified = ProcessReachableCallTree(pfn, module);
+  bool modified = ProcessReachableCallTree(pfn, context());
   return modified ? Pass::Status::SuccessWithChange
                   : Pass::Status::SuccessWithoutChange;
 }
diff --git a/source/opt/cfg_cleanup_pass.h b/source/opt/cfg_cleanup_pass.h
index d26f16b..450882f 100644
--- a/source/opt/cfg_cleanup_pass.h
+++ b/source/opt/cfg_cleanup_pass.h
@@ -26,11 +26,11 @@
  public:
   CFGCleanupPass() = default;
   const char* name() const override { return "cfg-cleanup"; }
-  Status Process(ir::Module*) override;
+  Status Process(ir::IRContext* c) override;
 
  private:
   // Initialize the pass.
-  void Initialize(ir::Module* module);
+  void Initialize(ir::IRContext* c);
 };
 
 }  // namespace opt
diff --git a/source/opt/common_uniform_elim_pass.cpp b/source/opt/common_uniform_elim_pass.cpp
index 4b42fd5..752ef93 100644
--- a/source/opt/common_uniform_elim_pass.cpp
+++ b/source/opt/common_uniform_elim_pass.cpp
@@ -16,6 +16,7 @@
 
 #include "common_uniform_elim_pass.h"
 #include "cfa.h"
+#include "ir_context.h"
 
 namespace spvtools {
 namespace opt {
@@ -542,8 +543,8 @@
     return modified;
 }
 
-void CommonUniformElimPass::Initialize(ir::Module* module) {
-  InitializeProcessing(module);
+void CommonUniformElimPass::Initialize(ir::IRContext* c) {
+  InitializeProcessing(c);
 
   // Clear collections.
   comp2idx2inst_.clear();
@@ -598,8 +599,8 @@
 
 CommonUniformElimPass::CommonUniformElimPass() {}
 
-Pass::Status CommonUniformElimPass::Process(ir::Module* module) {
-  Initialize(module);
+Pass::Status CommonUniformElimPass::Process(ir::IRContext* c) {
+  Initialize(c);
   return ProcessImpl();
 }
 
diff --git a/source/opt/common_uniform_elim_pass.h b/source/opt/common_uniform_elim_pass.h
index f7abd7c..5aa7f8b 100644
--- a/source/opt/common_uniform_elim_pass.h
+++ b/source/opt/common_uniform_elim_pass.h
@@ -29,6 +29,7 @@
 #include "module.h"
 #include "basic_block.h"
 #include "pass.h"
+#include "ir_context.h"
 
 namespace spvtools {
 namespace opt {
@@ -43,7 +44,7 @@
 
   CommonUniformElimPass();
   const char* name() const override { return "eliminate-common-uniform"; }
-  Status Process(ir::Module*) override;
+  Status Process(ir::IRContext*) override;
 
  private:
   // Returns true if |opcode| is a non-ptr access chain op
@@ -173,7 +174,7 @@
     return (op == SpvOpDecorate || op == SpvOpDecorateId);
   }
 
-  void Initialize(ir::Module* module);
+  void Initialize(ir::IRContext* c);
   Pass::Status ProcessImpl();
 
   // Decorations for the module we are processing
diff --git a/source/opt/compact_ids_pass.cpp b/source/opt/compact_ids_pass.cpp
index 4f78fa5..5a7c485 100644
--- a/source/opt/compact_ids_pass.cpp
+++ b/source/opt/compact_ids_pass.cpp
@@ -13,6 +13,7 @@
 // limitations under the License.
 
 #include "compact_ids_pass.h"
+#include "ir_context.h"
 
 #include <cassert>
 #include <unordered_map>
@@ -23,13 +24,13 @@
 using ir::Instruction;
 using ir::Operand;
 
-Pass::Status CompactIdsPass::Process(ir::Module* module) {
-  InitializeProcessing(module);
+Pass::Status CompactIdsPass::Process(ir::IRContext* c) {
+  InitializeProcessing(c);
 
   bool modified = false;
   std::unordered_map<uint32_t, uint32_t> result_id_mapping;
 
-  module->ForEachInst([&result_id_mapping, &modified] (Instruction* inst) {
+  c->module()->ForEachInst([&result_id_mapping, &modified] (Instruction* inst) {
     auto operand = inst->begin();
     while (operand != inst->end()) {
       const auto type = operand->type;
@@ -60,7 +61,7 @@
   }, true);
 
   if (modified)
-    module->SetIdBound(static_cast<uint32_t>(result_id_mapping.size() + 1));
+    c->SetIdBound(static_cast<uint32_t>(result_id_mapping.size() + 1));
 
   return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
 }
diff --git a/source/opt/compact_ids_pass.h b/source/opt/compact_ids_pass.h
index 41918dd..702620e 100644
--- a/source/opt/compact_ids_pass.h
+++ b/source/opt/compact_ids_pass.h
@@ -17,6 +17,7 @@
 
 #include "module.h"
 #include "pass.h"
+#include "ir_context.h"
 
 namespace spvtools {
 namespace opt {
@@ -25,7 +26,7 @@
 class CompactIdsPass : public Pass {
  public:
   const char* name() const override { return "compact-ids"; }
-  Status Process(ir::Module*) override;
+  Status Process(ir::IRContext*) override;
 };
 
 }  // namespace opt
diff --git a/source/opt/dead_branch_elim_pass.cpp b/source/opt/dead_branch_elim_pass.cpp
index 92486fa..82317f4 100644
--- a/source/opt/dead_branch_elim_pass.cpp
+++ b/source/opt/dead_branch_elim_pass.cpp
@@ -345,8 +345,8 @@
   return modified;
 }
 
-void DeadBranchElimPass::Initialize(ir::Module* module) {
-  InitializeProcessing(module);
+void DeadBranchElimPass::Initialize(ir::IRContext* c) {
+  InitializeProcessing(c);
 
   // Initialize function and block maps
   id2block_.clear();
@@ -396,7 +396,7 @@
 
 DeadBranchElimPass::DeadBranchElimPass() {}
 
-Pass::Status DeadBranchElimPass::Process(ir::Module* module) {
+Pass::Status DeadBranchElimPass::Process(ir::IRContext* module) {
   Initialize(module);
   return ProcessImpl();
 }
diff --git a/source/opt/dead_branch_elim_pass.h b/source/opt/dead_branch_elim_pass.h
index 12a2c38..a27c571 100644
--- a/source/opt/dead_branch_elim_pass.h
+++ b/source/opt/dead_branch_elim_pass.h
@@ -44,7 +44,7 @@
 
   DeadBranchElimPass();
   const char* name() const override { return "eliminate-dead-branches"; }
-  Status Process(ir::Module*) override;
+  Status Process(ir::IRContext* context) override;
 
  private:
   // If |condId| is boolean constant, return conditional value in |condVal| and
@@ -92,7 +92,7 @@
   // Return true if all extensions in this module are allowed by this pass.
   bool AllExtensionsSupported() const;
 
-  void Initialize(ir::Module* module);
+  void Initialize(ir::IRContext* c);
   Pass::Status ProcessImpl();
 
   // All backedge branches in current function
diff --git a/source/opt/dead_variable_elimination.cpp b/source/opt/dead_variable_elimination.cpp
index ac14f47..da6b982 100644
--- a/source/opt/dead_variable_elimination.cpp
+++ b/source/opt/dead_variable_elimination.cpp
@@ -21,20 +21,20 @@
 
 // This optimization removes global variables that are not needed because they
 // are definitely not accessed.
-Pass::Status DeadVariableElimination::Process(spvtools::ir::Module* module) {
+Pass::Status DeadVariableElimination::Process(ir::IRContext* c) {
   // The algorithm will compute the reference count for every global variable.
   // Anything with a reference count of 0 will then be deleted.  For variables
-  // that might have references that are not explicit in this module, we use the
+  // that might have references that are not explicit in this context, we use the
   // value kMustKeep as the reference count.
-  InitializeProcessing(module);
+  InitializeProcessing(c);
 
   //  Decoration manager to help organize decorations.
-  analysis::DecorationManager decoration_manager(module);
+  analysis::DecorationManager decoration_manager(context()->module());
 
   std::vector<uint32_t> ids_to_remove;
 
   // Get the reference count for all of the global OpVariable instructions.
-  for (auto& inst : module->types_values()) {
+  for (auto& inst : context()->types_values()) {
     if (inst.opcode() != SpvOp::SpvOpVariable) {
       continue;
     }
diff --git a/source/opt/dead_variable_elimination.h b/source/opt/dead_variable_elimination.h
index a0992cc..8412b1e 100644
--- a/source/opt/dead_variable_elimination.h
+++ b/source/opt/dead_variable_elimination.h
@@ -27,7 +27,7 @@
 class DeadVariableElimination : public MemPass {
  public:
   const char* name() const override { return "dead-variable-elimination"; }
-  Status Process(ir::Module*) override;
+  Status Process(ir::IRContext* c) override;
 
  private:
   // Deletes the OpVariable instruction who result id is |result_id|.
diff --git a/source/opt/eliminate_dead_constant_pass.cpp b/source/opt/eliminate_dead_constant_pass.cpp
index a36c29a..c77a701 100644
--- a/source/opt/eliminate_dead_constant_pass.cpp
+++ b/source/opt/eliminate_dead_constant_pass.cpp
@@ -21,18 +21,19 @@
 #include "def_use_manager.h"
 #include "log.h"
 #include "reflect.h"
+#include "ir_context.h"
 
 namespace spvtools {
 namespace opt {
 
-Pass::Status EliminateDeadConstantPass::Process(ir::Module* module) {
-  analysis::DefUseManager def_use(consumer(), module);
+Pass::Status EliminateDeadConstantPass::Process(ir::IRContext* irContext) {
+  analysis::DefUseManager def_use(consumer(), irContext->module());
   std::unordered_set<ir::Instruction*> working_list;
   // Traverse all the instructions to get the initial set of dead constants as
   // working list and count number of real uses for constants. Uses in
   // annotation instructions do not count.
   std::unordered_map<ir::Instruction*, size_t> use_counts;
-  std::vector<ir::Instruction*> constants = module->GetConstants();
+  std::vector<ir::Instruction*> constants = irContext->GetConstants();
   for (auto* c : constants) {
     uint32_t const_id = c->result_id();
     size_t count = 0;
diff --git a/source/opt/eliminate_dead_constant_pass.h b/source/opt/eliminate_dead_constant_pass.h
index 6107cad..cf6957f 100644
--- a/source/opt/eliminate_dead_constant_pass.h
+++ b/source/opt/eliminate_dead_constant_pass.h
@@ -17,6 +17,7 @@
 
 #include "module.h"
 #include "pass.h"
+#include "ir_context.h"
 
 namespace spvtools {
 namespace opt {
@@ -25,7 +26,7 @@
 class EliminateDeadConstantPass : public Pass {
  public:
   const char* name() const override { return "eliminate-dead-const"; }
-  Status Process(ir::Module*) override;
+  Status Process(ir::IRContext*) override;
 };
 
 }  // namespace opt
diff --git a/source/opt/eliminate_dead_functions_pass.cpp b/source/opt/eliminate_dead_functions_pass.cpp
index ad4b730..96f9999 100644
--- a/source/opt/eliminate_dead_functions_pass.cpp
+++ b/source/opt/eliminate_dead_functions_pass.cpp
@@ -19,8 +19,8 @@
 namespace spvtools {
 namespace opt {
 
-Pass::Status EliminateDeadFunctionsPass::Process(ir::Module* module) {
-  InitializeProcessing(module);
+Pass::Status EliminateDeadFunctionsPass::Process(ir::IRContext* c) {
+  InitializeProcessing(c);
 
   // Identify live functions first.  Those that are not live
   // are dead.
@@ -29,10 +29,11 @@
     live_function_set.insert(fp);
     return false;
   };
-  ProcessReachableCallTree(mark_live, module);
+  ProcessReachableCallTree(mark_live, context());
 
   bool modified = false;
-  for (auto funcIter = module->begin(); funcIter != module->end();) {
+  for (auto funcIter = get_module()->begin();
+       funcIter != get_module()->end();) {
     if (live_function_set.count(&*funcIter) == 0) {
       modified = true;
       EliminateFunction(&*funcIter);
diff --git a/source/opt/eliminate_dead_functions_pass.h b/source/opt/eliminate_dead_functions_pass.h
index a7d0742..dae2fca 100644
--- a/source/opt/eliminate_dead_functions_pass.h
+++ b/source/opt/eliminate_dead_functions_pass.h
@@ -27,7 +27,7 @@
 class EliminateDeadFunctionsPass : public MemPass {
  public:
   const char* name() const override { return "eliminate-dead-functions"; }
-  Status Process(ir::Module*) override;
+  Status Process(ir::IRContext* c) override;
 
  private:
   void EliminateFunction(ir::Function* func);
diff --git a/source/opt/flatten_decoration_pass.cpp b/source/opt/flatten_decoration_pass.cpp
index 3ba4a9a..094c580 100644
--- a/source/opt/flatten_decoration_pass.cpp
+++ b/source/opt/flatten_decoration_pass.cpp
@@ -13,6 +13,7 @@
 // limitations under the License.
 
 #include "flatten_decoration_pass.h"
+#include "ir_context.h"
 
 #include <cassert>
 #include <vector>
@@ -28,8 +29,8 @@
 using Words = std::vector<uint32_t>;
 using OrderedUsesMap = std::unordered_map<uint32_t, Words>;
 
-Pass::Status FlattenDecorationPass::Process(ir::Module* module) {
-  InitializeProcessing(module);
+Pass::Status FlattenDecorationPass::Process(ir::IRContext* c) {
+  InitializeProcessing(c);
 
   bool modified = false;
 
@@ -44,7 +45,7 @@
   // their indices, in of appearance.
   OrderedUsesMap member_uses;
 
-  auto annotations = module->annotations();
+  auto annotations = context()->annotations();
 
   // On the first pass, record each OpDecorationGroup with its ordered uses.
   // Rely on unordered_map::operator[] to create its entries on first access.
@@ -74,7 +75,7 @@
   // equivalent normal and struct member uses.
   auto inst_iter = annotations.begin();
   // We have to re-evaluate the end pointer
-  while (inst_iter != module->annotations().end()) {
+  while (inst_iter != context()->annotations().end()) {
     // Should we replace this instruction?
     bool replace = false;
     switch (inst_iter->opcode()) {
@@ -145,8 +146,8 @@
   // An OpDecorationGroup instruction might not have been used by an
   // OpGroupDecorate or OpGroupMemberDecorate instruction.
   if (!group_ids.empty()) {
-    for (auto debug_inst_iter = module->debug2_begin();
-         debug_inst_iter != module->debug2_end();) {
+    for (auto debug_inst_iter = context()->debug2_begin();
+         debug_inst_iter != context()->debug2_end();) {
       if (debug_inst_iter->opcode() == SpvOp::SpvOpName) {
         const uint32_t target = debug_inst_iter->GetSingleWordOperand(0);
         if (group_ids.count(target)) {
diff --git a/source/opt/flatten_decoration_pass.h b/source/opt/flatten_decoration_pass.h
index bcdfdc0..345b39b 100644
--- a/source/opt/flatten_decoration_pass.h
+++ b/source/opt/flatten_decoration_pass.h
@@ -17,6 +17,7 @@
 
 #include "module.h"
 #include "pass.h"
+#include "ir_context.h"
 
 namespace spvtools {
 namespace opt {
@@ -25,7 +26,7 @@
 class FlattenDecorationPass : public Pass {
  public:
   const char* name() const override { return "flatten-decoration"; }
-  Status Process(ir::Module*) override;
+  Status Process(ir::IRContext*) override;
 };
 
 }  // namespace opt
diff --git a/source/opt/fold_spec_constant_op_and_composite_pass.cpp b/source/opt/fold_spec_constant_op_and_composite_pass.cpp
index a37db6a..db9891e 100644
--- a/source/opt/fold_spec_constant_op_and_composite_pass.cpp
+++ b/source/opt/fold_spec_constant_op_and_composite_pass.cpp
@@ -20,6 +20,7 @@
 
 #include "constants.h"
 #include "make_unique.h"
+#include "ir_context.h"
 
 namespace spvtools {
 namespace opt {
@@ -244,21 +245,21 @@
 FoldSpecConstantOpAndCompositePass::FoldSpecConstantOpAndCompositePass()
     : max_id_(0), type_mgr_(nullptr), id_to_const_val_() {}
 
-Pass::Status FoldSpecConstantOpAndCompositePass::Process(ir::Module* module) {
-  Initialize(module);
-  return ProcessImpl(module);
+Pass::Status FoldSpecConstantOpAndCompositePass::Process(ir::IRContext* irContext) {
+  Initialize(irContext);
+  return ProcessImpl(irContext);
 }
 
-void FoldSpecConstantOpAndCompositePass::Initialize(ir::Module* module) {
-  InitializeProcessing(module);
-  type_mgr_.reset(new analysis::TypeManager(consumer(), *module));
+void FoldSpecConstantOpAndCompositePass::Initialize(ir::IRContext* irContext) {
+  InitializeProcessing(irContext);
+  type_mgr_.reset(new analysis::TypeManager(consumer(), *irContext->module()));
   for (const auto& id_def : get_def_use_mgr()->id_to_defs()) {
     max_id_ = std::max(max_id_, id_def.first);
   }
 };
 
 Pass::Status FoldSpecConstantOpAndCompositePass::ProcessImpl(
-    ir::Module* module) {
+    ir::IRContext* irContext) {
   bool modified = false;
   // Traverse through all the constant defining instructions. For Normal
   // Constants whose values are determined and do not depend on OpUndef
@@ -280,10 +281,10 @@
   // the dependee Spec Constants, all its dependent constants must have been
   // processed and all its dependent Spec Constants should have been folded if
   // possible.
-  for (ir::Module::inst_iterator inst_iter = module->types_values_begin();
+  for (ir::Module::inst_iterator inst_iter = irContext->types_values_begin();
        // Need to re-evaluate the end iterator since we may modify the list of
        // instructions in this section of the module as the process goes.
-       inst_iter != module->types_values_end(); ++inst_iter) {
+       inst_iter != irContext->types_values_end(); ++inst_iter) {
     ir::Instruction* inst = &*inst_iter;
     // Collect constant values of normal constants and process the
     // OpSpecConstantOp and OpSpecConstantComposite instructions if possible.
diff --git a/source/opt/fold_spec_constant_op_and_composite_pass.h b/source/opt/fold_spec_constant_op_and_composite_pass.h
index b41fe76..fd1ff6b 100644
--- a/source/opt/fold_spec_constant_op_and_composite_pass.h
+++ b/source/opt/fold_spec_constant_op_and_composite_pass.h
@@ -24,6 +24,7 @@
 #include "module.h"
 #include "pass.h"
 #include "type_manager.h"
+#include "ir_context.h"
 
 namespace spvtools {
 namespace opt {
@@ -35,19 +36,19 @@
 
   const char* name() const override { return "fold-spec-const-op-composite"; }
 
-  Status Process(ir::Module* module) override;
+  Status Process(ir::IRContext* irContext) override;
 
  private:
   // Initializes the type manager, def-use manager and get the maximal id used
   // in the module.
-  void Initialize(ir::Module* module);
+  void Initialize(ir::IRContext* irContext);
 
   // The real entry of processing. Iterates through the types-constants-globals
   // section of the given module, finds the Spec Constants defined with
   // OpSpecConstantOp and OpSpecConstantComposite instructions. If the result
   // value of those spec constants can be folded, fold them to their
   // corresponding normal constants.
-  Status ProcessImpl(ir::Module*);
+  Status ProcessImpl(ir::IRContext* irContext);
 
   // Processes the OpSpecConstantOp instruction pointed by the given
   // instruction iterator, folds it to normal constants if possible. Returns
diff --git a/source/opt/freeze_spec_constant_value_pass.cpp b/source/opt/freeze_spec_constant_value_pass.cpp
index 8855c41..b47eb4a 100644
--- a/source/opt/freeze_spec_constant_value_pass.cpp
+++ b/source/opt/freeze_spec_constant_value_pass.cpp
@@ -13,13 +13,14 @@
 // limitations under the License.
 
 #include "freeze_spec_constant_value_pass.h"
+#include "ir_context.h"
 
 namespace spvtools {
 namespace opt {
 
-Pass::Status FreezeSpecConstantValuePass::Process(ir::Module* module) {
+Pass::Status FreezeSpecConstantValuePass::Process(ir::IRContext* irContext) {
   bool modified = false;
-  module->ForEachInst([&modified](ir::Instruction* inst) {
+  irContext->module()->ForEachInst([&modified](ir::Instruction* inst) {
     switch (inst->opcode()) {
       case SpvOp::SpvOpSpecConstant:
         inst->SetOpcode(SpvOp::SpvOpConstant);
diff --git a/source/opt/freeze_spec_constant_value_pass.h b/source/opt/freeze_spec_constant_value_pass.h
index a04ff03..8d163d0 100644
--- a/source/opt/freeze_spec_constant_value_pass.h
+++ b/source/opt/freeze_spec_constant_value_pass.h
@@ -17,6 +17,7 @@
 
 #include "module.h"
 #include "pass.h"
+#include "ir_context.h"
 
 namespace spvtools {
 namespace opt {
@@ -25,7 +26,7 @@
 class FreezeSpecConstantValuePass : public Pass {
  public:
   const char* name() const override { return "freeze-spec-const"; }
-  Status Process(ir::Module*) override;
+  Status Process(ir::IRContext*) override;
 };
 
 }  // namespace opt
diff --git a/source/opt/inline_exhaustive_pass.cpp b/source/opt/inline_exhaustive_pass.cpp
index bd60f04..5be0329 100644
--- a/source/opt/inline_exhaustive_pass.cpp
+++ b/source/opt/inline_exhaustive_pass.cpp
@@ -49,8 +49,8 @@
   return modified;
 }
 
-void InlineExhaustivePass::Initialize(ir::Module* module) {
-  InitializeInline(module);
+void InlineExhaustivePass::Initialize(ir::IRContext* c) {
+  InitializeInline(c);
 };
 
 Pass::Status InlineExhaustivePass::ProcessImpl() {
@@ -64,8 +64,8 @@
 
 InlineExhaustivePass::InlineExhaustivePass() {}
 
-Pass::Status InlineExhaustivePass::Process(ir::Module* module) {
-  Initialize(module);
+Pass::Status InlineExhaustivePass::Process(ir::IRContext* c) {
+  Initialize(c);
   return ProcessImpl();
 }
 
diff --git a/source/opt/inline_exhaustive_pass.h b/source/opt/inline_exhaustive_pass.h
index e4773d8..9d3076b 100644
--- a/source/opt/inline_exhaustive_pass.h
+++ b/source/opt/inline_exhaustive_pass.h
@@ -35,7 +35,7 @@
 
  public:
   InlineExhaustivePass();
-  Status Process(ir::Module*) override;
+  Status Process(ir::IRContext* c) override;
 
   const char* name() const override { return "inline-entry-points-exhaustive"; }
 
@@ -44,7 +44,7 @@
   // all code that is inlined into func. Return true if func is modified.
   bool InlineExhaustive(ir::Function* func);
 
-  void Initialize(ir::Module* module);
+  void Initialize(ir::IRContext* c);
   Pass::Status ProcessImpl();
 };
 
diff --git a/source/opt/inline_opaque_pass.cpp b/source/opt/inline_opaque_pass.cpp
index a9debad..e4762eb 100644
--- a/source/opt/inline_opaque_pass.cpp
+++ b/source/opt/inline_opaque_pass.cpp
@@ -97,8 +97,8 @@
   return modified;
 }
 
-void InlineOpaquePass::Initialize(ir::Module* module) {
-  InitializeInline(module);
+void InlineOpaquePass::Initialize(ir::IRContext* c) {
+  InitializeInline(c);
 };
 
 Pass::Status InlineOpaquePass::ProcessImpl() {
@@ -112,8 +112,8 @@
 
 InlineOpaquePass::InlineOpaquePass() {}
 
-Pass::Status InlineOpaquePass::Process(ir::Module* module) {
-  Initialize(module);
+Pass::Status InlineOpaquePass::Process(ir::IRContext* c) {
+  Initialize(c);
   return ProcessImpl();
 }
 
diff --git a/source/opt/inline_opaque_pass.h b/source/opt/inline_opaque_pass.h
index e166617..6ad1a36 100644
--- a/source/opt/inline_opaque_pass.h
+++ b/source/opt/inline_opaque_pass.h
@@ -35,7 +35,7 @@
 
  public:
   InlineOpaquePass();
-  Status Process(ir::Module*) override;
+  Status Process(ir::IRContext* c) override;
 
   const char* name() const override { return "inline-entry-points-opaque"; }
 
@@ -51,7 +51,7 @@
   // if func is modified.
   bool InlineOpaque(ir::Function* func);
 
-  void Initialize(ir::Module* module);
+  void Initialize(ir::IRContext* c);
   Pass::Status ProcessImpl();
 };
 
diff --git a/source/opt/inline_pass.cpp b/source/opt/inline_pass.cpp
index 905539d..7627355 100644
--- a/source/opt/inline_pass.cpp
+++ b/source/opt/inline_pass.cpp
@@ -53,7 +53,7 @@
       {{spv_operand_type_t::SPV_OPERAND_TYPE_STORAGE_CLASS,
         {uint32_t(storage_class)}},
        {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {type_id}}}));
-  get_module()->AddType(std::move(type_inst));
+  context()->AddType(std::move(type_inst));
   return resultId;
 }
 
@@ -648,8 +648,8 @@
          no_return_in_loop_.cend();
 }
 
-void InlinePass::InitializeInline(ir::Module* module) {
-  InitializeProcessing(module);
+void InlinePass::InitializeInline(ir::IRContext* c) {
+  InitializeProcessing(c);
 
   false_id_ = 0;
 
diff --git a/source/opt/inline_pass.h b/source/opt/inline_pass.h
index 1e867b6..2451ae9 100644
--- a/source/opt/inline_pass.h
+++ b/source/opt/inline_pass.h
@@ -161,7 +161,7 @@
       std::vector<std::unique_ptr<ir::BasicBlock>>& new_blocks);
 
   // Initialize state for optimization of |module|
-  void InitializeInline(ir::Module* module);
+  void InitializeInline(ir::IRContext* c);
 
   // Map from function's result id to function.
   std::unordered_map<uint32_t, ir::Function*> id2function_;
diff --git a/source/opt/insert_extract_elim.cpp b/source/opt/insert_extract_elim.cpp
index 6a78127..c7ff9e6 100644
--- a/source/opt/insert_extract_elim.cpp
+++ b/source/opt/insert_extract_elim.cpp
@@ -17,6 +17,7 @@
 #include "insert_extract_elim.h"
 
 #include "iterator.h"
+#include "ir_context.h"
 
 namespace spvtools {
 namespace opt {
@@ -121,8 +122,8 @@
   return modified;
 }
 
-void InsertExtractElimPass::Initialize(ir::Module* module) {
-  InitializeProcessing(module);
+void InsertExtractElimPass::Initialize(ir::IRContext* c) {
+  InitializeProcessing(c);
 
   // Initialize extension whitelist
   InitExtensions();
@@ -153,8 +154,8 @@
 
 InsertExtractElimPass::InsertExtractElimPass() {}
 
-Pass::Status InsertExtractElimPass::Process(ir::Module* module) {
-  Initialize(module);
+Pass::Status InsertExtractElimPass::Process(ir::IRContext* c) {
+  Initialize(c);
   return ProcessImpl();
 }
 
diff --git a/source/opt/insert_extract_elim.h b/source/opt/insert_extract_elim.h
index fa3c8fb..fc47580 100644
--- a/source/opt/insert_extract_elim.h
+++ b/source/opt/insert_extract_elim.h
@@ -28,6 +28,7 @@
 #include "def_use_manager.h"
 #include "module.h"
 #include "pass.h"
+#include "ir_context.h"
 
 namespace spvtools {
 namespace opt {
@@ -37,7 +38,7 @@
  public:
   InsertExtractElimPass();
   const char* name() const override { return "eliminate-insert-extract"; }
-  Status Process(ir::Module*) override;
+  Status Process(ir::IRContext*) override;
 
  private:
   // Return true if indices of extract |extInst| and insert |insInst| match
@@ -67,7 +68,7 @@
   // Return true if all extensions in this module are allowed by this pass.
   bool AllExtensionsSupported() const;
 
-  void Initialize(ir::Module* module);
+  void Initialize(ir::IRContext* c);
   Pass::Status ProcessImpl();
 
   // Extensions supported by this pass.
diff --git a/source/opt/ir_context.cpp b/source/opt/ir_context.cpp
new file mode 100644
index 0000000..c966eea
--- /dev/null
+++ b/source/opt/ir_context.cpp
@@ -0,0 +1,15 @@
+// Copyright (c) 2017 Google Inc.
+//
+// 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 "ir_context.h"
diff --git a/source/opt/ir_context.h b/source/opt/ir_context.h
new file mode 100644
index 0000000..d1b9800
--- /dev/null
+++ b/source/opt/ir_context.h
@@ -0,0 +1,292 @@
+// Copyright (c) 2017 Google Inc.
+//
+// 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 SPIRV_TOOLS_IR_CONTEXT_H
+#define SPIRV_TOOLS_IR_CONTEXT_H
+
+#include "module.h"
+
+#include <iostream>
+
+namespace spvtools {
+namespace ir {
+
+class IRContext {
+ public:
+  IRContext(std::unique_ptr<Module>&& m) : module_(std::move(m)) {}
+  Module* module() const { return module_.get(); }
+
+  inline void SetIdBound(uint32_t i);
+  inline uint32_t IdBound() const;
+
+  // Returns a vector of pointers to constant-creation instructions in this
+  // context.
+  inline std::vector<Instruction*> GetConstants();
+  inline std::vector<const Instruction*> GetConstants() const;
+
+  // Iterators for annotation instructions contained in this context.
+  inline Module::inst_iterator annotation_begin();
+  inline Module::inst_iterator annotation_end();
+  inline IteratorRange<Module::inst_iterator> annotations();
+  inline IteratorRange<Module::const_inst_iterator> annotations() const;
+
+  // Iterators for capabilities instructions contained in this module.
+  inline Module::inst_iterator capability_begin();
+  inline Module::inst_iterator capability_end();
+  inline IteratorRange<Module::inst_iterator> capabilities();
+  inline IteratorRange<Module::const_inst_iterator> capabilities() const;
+
+  // Iterators for types, constants and global variables instructions.
+  inline ir::Module::inst_iterator types_values_begin();
+  inline ir::Module::inst_iterator types_values_end();
+  inline IteratorRange<Module::inst_iterator> types_values();
+  inline IteratorRange<Module::const_inst_iterator> types_values() const;
+
+  // Iterators for extension instructions contained in this module.
+  inline Module::inst_iterator ext_inst_import_begin();
+  inline Module::inst_iterator ext_inst_import_end();
+
+  // There are several kinds of debug instructions, according to where they can
+  // appear in the logical layout of a module:
+  //  - Section 7a:  OpString, OpSourceExtension, OpSource, OpSourceContinued
+  //  - Section 7b:  OpName, OpMemberName
+  //  - Section 7c:  OpModuleProcessed
+  //  - Mostly anywhere: OpLine and OpNoLine
+  //
+
+  // Iterators for debug 1 instructions (excluding OpLine & OpNoLine) contained
+  // in this module.  These are for layout section 7a.
+  inline Module::inst_iterator debug1_begin();
+  inline Module::inst_iterator debug1_end();
+  inline IteratorRange<Module::inst_iterator> debugs1();
+  inline IteratorRange<Module::const_inst_iterator> debugs1() const;
+
+  // Iterators for debug 2 instructions (excluding OpLine & OpNoLine) contained
+  // in this module.  These are for layout section 7b.
+  inline Module::inst_iterator debug2_begin();
+  inline Module::inst_iterator debug2_end();
+  inline IteratorRange<Module::inst_iterator> debugs2();
+  inline IteratorRange<Module::const_inst_iterator> debugs2() const;
+
+  // Iterators for debug 3 instructions (excluding OpLine & OpNoLine) contained
+  // in this module.  These are for layout section 7c.
+  inline Module::inst_iterator debug3_begin();
+  inline Module::inst_iterator debug3_end();
+  inline IteratorRange<Module::inst_iterator> debugs3();
+  inline IteratorRange<Module::const_inst_iterator> debugs3() const;
+
+  // Clears all debug instructions (excluding OpLine & OpNoLine).
+  inline void debug_clear();
+
+  // Appends a capability instruction to this module.
+  inline void AddCapability(std::unique_ptr<Instruction>&& c);
+  // Appends an extension instruction to this module.
+  inline void AddExtension(std::unique_ptr<Instruction>&& e);
+  // Appends an extended instruction set instruction to this module.
+  inline void AddExtInstImport(std::unique_ptr<Instruction>&& e);
+  // Set the memory model for this module.
+  inline void SetMemoryModel(std::unique_ptr<Instruction>&& m);
+  // Appends an entry point instruction to this module.
+  inline void AddEntryPoint(std::unique_ptr<Instruction>&& e);
+  // Appends an execution mode instruction to this module.
+  inline void AddExecutionMode(std::unique_ptr<Instruction>&& e);
+  // Appends a debug 1 instruction (excluding OpLine & OpNoLine) to this module.
+  // "debug 1" instructions are the ones in layout section 7.a), see section
+  // 2.4 Logical Layout of a Module from the SPIR-V specification.
+  inline void AddDebug1Inst(std::unique_ptr<Instruction>&& d);
+  // Appends a debug 2 instruction (excluding OpLine & OpNoLine) to this module.
+  // "debug 2" instructions are the ones in layout section 7.b), see section
+  // 2.4 Logical Layout of a Module from the SPIR-V specification.
+  inline void AddDebug2Inst(std::unique_ptr<Instruction>&& d);
+  // Appends a debug 3 instruction (OpModuleProcessed) to this module.
+  // This is due to decision by the SPIR Working Group, pending publication.
+  inline void AddDebug3Inst(std::unique_ptr<Instruction>&& d);
+  // Appends an annotation instruction to this module.
+  inline void AddAnnotationInst(std::unique_ptr<Instruction>&& a);
+  // Appends a type-declaration instruction to this module.
+  inline void AddType(std::unique_ptr<Instruction>&& t);
+  // Appends a constant, global variable, or OpUndef instruction to this module.
+  inline void AddGlobalValue(std::unique_ptr<Instruction>&& v);
+  // Appends a function to this module.
+  inline void AddFunction(std::unique_ptr<Function>&& f);
+
+ private:
+  std::unique_ptr<Module> module_;
+};
+
+void IRContext::SetIdBound(uint32_t i) { module_->SetIdBound(i); }
+
+uint32_t IRContext::IdBound() const { return module()->IdBound(); }
+
+std::vector<Instruction*> spvtools::ir::IRContext::GetConstants() {
+  return module()->GetConstants();
+}
+
+std::vector<const Instruction*> IRContext::GetConstants() const {
+  return ((const Module*)module())->GetConstants();
+}
+
+Module::inst_iterator IRContext::annotation_begin() {
+  return module()->annotation_begin();
+}
+
+Module::inst_iterator IRContext::annotation_end() {
+  return module()->annotation_end();
+}
+
+IteratorRange<Module::inst_iterator> IRContext::annotations() {
+  return module_->annotations();
+}
+
+IteratorRange<Module::const_inst_iterator> IRContext::annotations() const {
+  return ((const Module*)module_.get())->annotations();
+}
+
+Module::inst_iterator IRContext::capability_begin() {
+  return module()->capability_begin();
+}
+
+Module::inst_iterator IRContext::capability_end() {
+  return module()->capability_end();
+}
+
+IteratorRange<Module::inst_iterator> IRContext::capabilities() {
+  return module()->capabilities();
+}
+
+IteratorRange<Module::const_inst_iterator> IRContext::capabilities() const {
+  return ((const Module*)module())->capabilities();
+}
+
+ir::Module::inst_iterator IRContext::types_values_begin() {
+  return module()->types_values_begin();
+}
+
+ir::Module::inst_iterator IRContext::types_values_end() {
+  return module()->types_values_end();
+}
+
+IteratorRange<Module::inst_iterator> IRContext::types_values() {
+  return module()->types_values();
+}
+
+IteratorRange<Module::const_inst_iterator> IRContext::types_values() const {
+  return ((const Module*)module_.get())->types_values();
+}
+
+Module::inst_iterator IRContext::ext_inst_import_begin() {
+  return module()->ext_inst_import_begin();
+}
+
+Module::inst_iterator IRContext::ext_inst_import_end() {
+  return module()->ext_inst_import_end();
+}
+
+Module::inst_iterator IRContext::debug1_begin() {
+  return module()->debug1_begin();
+}
+
+Module::inst_iterator IRContext::debug1_end() { return module()->debug1_end(); }
+
+IteratorRange<Module::inst_iterator> IRContext::debugs1() {
+  return module()->debugs1();
+}
+
+IteratorRange<Module::const_inst_iterator> IRContext::debugs1() const {
+  return ((const Module*)module_.get())->debugs1();
+}
+
+Module::inst_iterator IRContext::debug2_begin() {
+  return module()->debug2_begin();
+}
+Module::inst_iterator IRContext::debug2_end() { return module()->debug2_end(); }
+
+IteratorRange<Module::inst_iterator> IRContext::debugs2() {
+  return module()->debugs2();
+}
+
+IteratorRange<Module::const_inst_iterator> IRContext::debugs2() const {
+  return ((const Module*)module_.get())->debugs2();
+}
+
+Module::inst_iterator IRContext::debug3_begin() {
+  return module()->debug3_begin();
+}
+
+Module::inst_iterator IRContext::debug3_end() { return module()->debug3_end(); }
+
+IteratorRange<Module::inst_iterator> IRContext::debugs3() {
+  return module()->debugs3();
+}
+
+IteratorRange<Module::const_inst_iterator> IRContext::debugs3() const {
+  return ((const Module*)module_.get())->debugs3();
+}
+
+void IRContext::debug_clear() { module_->debug_clear(); }
+
+void IRContext::AddCapability(std::unique_ptr<Instruction>&& c) {
+  module()->AddCapability(std::move(c));
+}
+
+void IRContext::AddExtension(std::unique_ptr<Instruction>&& e) {
+  module()->AddExtension(std::move(e));
+}
+
+void IRContext::AddExtInstImport(std::unique_ptr<Instruction>&& e) {
+  module()->AddExtInstImport(std::move(e));
+}
+
+void IRContext::SetMemoryModel(std::unique_ptr<Instruction>&& m) {
+  module()->SetMemoryModel(std::move(m));
+}
+
+void IRContext::AddEntryPoint(std::unique_ptr<Instruction>&& e) {
+  module()->AddEntryPoint(std::move(e));
+}
+
+void IRContext::AddExecutionMode(std::unique_ptr<Instruction>&& e) {
+  module()->AddExecutionMode(std::move(e));
+}
+
+void IRContext::AddDebug1Inst(std::unique_ptr<Instruction>&& d) {
+  module()->AddDebug1Inst(std::move(d));
+}
+
+void IRContext::AddDebug2Inst(std::unique_ptr<Instruction>&& d) {
+  module()->AddDebug2Inst(std::move(d));
+}
+
+void IRContext::AddDebug3Inst(std::unique_ptr<Instruction>&& d) {
+  module()->AddDebug3Inst(std::move(d));
+}
+
+void IRContext::AddAnnotationInst(std::unique_ptr<Instruction>&& a) {
+  module()->AddAnnotationInst(std::move(a));
+}
+
+void IRContext::AddType(std::unique_ptr<Instruction>&& t) {
+  module()->AddType(std::move(t));
+}
+
+void IRContext::AddGlobalValue(std::unique_ptr<Instruction>&& v) {
+  module()->AddGlobalValue(std::move(v));
+}
+
+void IRContext::AddFunction(std::unique_ptr<Function>&& f) {
+  module()->AddFunction(std::move(f));
+}
+}  // namespace ir
+}  // namespace spvtools
+#endif  // SPIRV_TOOLS_IR_CONTEXT_H
diff --git a/source/opt/local_access_chain_convert_pass.cpp b/source/opt/local_access_chain_convert_pass.cpp
index 582a1f3..f50c815 100644
--- a/source/opt/local_access_chain_convert_pass.cpp
+++ b/source/opt/local_access_chain_convert_pass.cpp
@@ -249,8 +249,8 @@
   return modified;
 }
 
-void LocalAccessChainConvertPass::Initialize(ir::Module* module) {
-  InitializeProcessing(module);
+void LocalAccessChainConvertPass::Initialize(ir::IRContext* c) {
+  InitializeProcessing(c);
 
   // Initialize Target Variable Caches
   seen_target_vars_.clear();
@@ -300,8 +300,8 @@
 
 LocalAccessChainConvertPass::LocalAccessChainConvertPass() {}
 
-Pass::Status LocalAccessChainConvertPass::Process(ir::Module* module) {
-  Initialize(module);
+Pass::Status LocalAccessChainConvertPass::Process(ir::IRContext* c) {
+  Initialize(c);
   return ProcessImpl();
 }
 
diff --git a/source/opt/local_access_chain_convert_pass.h b/source/opt/local_access_chain_convert_pass.h
index 9cd726d..07c03a4 100644
--- a/source/opt/local_access_chain_convert_pass.h
+++ b/source/opt/local_access_chain_convert_pass.h
@@ -38,7 +38,7 @@
  public:
   LocalAccessChainConvertPass();
   const char* name() const override { return "convert-local-access-chains"; }
-  Status Process(ir::Module*) override;
+  Status Process(ir::IRContext* c) override;
 
   using ProcessFunction = std::function<bool(ir::Function*)>;
 
@@ -107,7 +107,7 @@
   // Return true if all extensions in this module are allowed by this pass.
   bool AllExtensionsSupported() const;
 
-  void Initialize(ir::Module* module);
+  void Initialize(ir::IRContext* c);
   Pass::Status ProcessImpl();
 
   // Variables with only supported references, ie. loads and stores using
diff --git a/source/opt/local_single_block_elim_pass.cpp b/source/opt/local_single_block_elim_pass.cpp
index 1d21953..c175cc4 100644
--- a/source/opt/local_single_block_elim_pass.cpp
+++ b/source/opt/local_single_block_elim_pass.cpp
@@ -137,8 +137,8 @@
   return modified;
 }
 
-void LocalSingleBlockLoadStoreElimPass::Initialize(ir::Module* module) {
-  InitializeProcessing(module);
+void LocalSingleBlockLoadStoreElimPass::Initialize(ir::IRContext* c) {
+  InitializeProcessing(c);
 
   // Initialize Target Type Caches
   seen_target_vars_.clear();
@@ -186,8 +186,8 @@
 
 LocalSingleBlockLoadStoreElimPass::LocalSingleBlockLoadStoreElimPass() {}
 
-Pass::Status LocalSingleBlockLoadStoreElimPass::Process(ir::Module* module) {
-  Initialize(module);
+Pass::Status LocalSingleBlockLoadStoreElimPass::Process(ir::IRContext* c) {
+  Initialize(c);
   return ProcessImpl();
 }
 
diff --git a/source/opt/local_single_block_elim_pass.h b/source/opt/local_single_block_elim_pass.h
index b6503bf..f178304 100644
--- a/source/opt/local_single_block_elim_pass.h
+++ b/source/opt/local_single_block_elim_pass.h
@@ -38,7 +38,7 @@
  public:
   LocalSingleBlockLoadStoreElimPass();
   const char* name() const override { return "eliminate-local-single-block"; }
-  Status Process(ir::Module*) override;
+  Status Process(ir::IRContext* c) override;
 
  private:
   // Return true if all uses of |varId| are only through supported reference
@@ -62,7 +62,7 @@
   // Return true if all extensions in this module are supported by this pass.
   bool AllExtensionsSupported() const;
 
-  void Initialize(ir::Module* module);
+  void Initialize(ir::IRContext* c);
   Pass::Status ProcessImpl();
 
   // Map from function scope variable to a store of that variable in the
diff --git a/source/opt/local_single_store_elim_pass.cpp b/source/opt/local_single_store_elim_pass.cpp
index fc36643..f50f70b 100644
--- a/source/opt/local_single_store_elim_pass.cpp
+++ b/source/opt/local_single_store_elim_pass.cpp
@@ -232,8 +232,8 @@
   return modified;
 }
 
-void LocalSingleStoreElimPass::Initialize(ir::Module* module) {
-  InitializeProcessing(module);
+void LocalSingleStoreElimPass::Initialize(ir::IRContext* irContext) {
+  InitializeProcessing(irContext);
 
   // Initialize function and block maps
   label2block_.clear();
@@ -289,8 +289,8 @@
 
 LocalSingleStoreElimPass::LocalSingleStoreElimPass() {}
 
-Pass::Status LocalSingleStoreElimPass::Process(ir::Module* module) {
-  Initialize(module);
+Pass::Status LocalSingleStoreElimPass::Process(ir::IRContext* irContext) {
+  Initialize(irContext);
   return ProcessImpl();
 }
 
diff --git a/source/opt/local_single_store_elim_pass.h b/source/opt/local_single_store_elim_pass.h
index d72e0e6..4a7dacb 100644
--- a/source/opt/local_single_store_elim_pass.h
+++ b/source/opt/local_single_store_elim_pass.h
@@ -40,7 +40,7 @@
  public:
   LocalSingleStoreElimPass();
   const char* name() const override { return "eliminate-local-single-store"; }
-  Status Process(ir::Module*) override;
+  Status Process(ir::IRContext* irContext) override;
 
  private:
   // Return true if all refs through |ptrId| are only loads or stores and
@@ -98,7 +98,7 @@
   // Return true if all extensions in this module are allowed by this pass.
   bool AllExtensionsSupported() const;
 
-  void Initialize(ir::Module* module);
+  void Initialize(ir::IRContext* irContext);
   Pass::Status ProcessImpl();
 
   // Map from block's label id to block
diff --git a/source/opt/local_ssa_elim_pass.cpp b/source/opt/local_ssa_elim_pass.cpp
index 1c11e23..1ec7db5 100644
--- a/source/opt/local_ssa_elim_pass.cpp
+++ b/source/opt/local_ssa_elim_pass.cpp
@@ -46,8 +46,8 @@
   return modified;
 }
 
-void LocalMultiStoreElimPass::Initialize(ir::Module* module) {
-  InitializeProcessing(module);
+void LocalMultiStoreElimPass::Initialize(ir::IRContext* c) {
+  InitializeProcessing(c);
 
   // Initialize extension whitelist
   InitExtensions();
@@ -92,8 +92,8 @@
 
 LocalMultiStoreElimPass::LocalMultiStoreElimPass() {}
 
-Pass::Status LocalMultiStoreElimPass::Process(ir::Module* module) {
-  Initialize(module);
+Pass::Status LocalMultiStoreElimPass::Process(ir::IRContext* c) {
+  Initialize(c);
   return ProcessImpl();
 }
 
diff --git a/source/opt/local_ssa_elim_pass.h b/source/opt/local_ssa_elim_pass.h
index 951d2a0..65177bb 100644
--- a/source/opt/local_ssa_elim_pass.h
+++ b/source/opt/local_ssa_elim_pass.h
@@ -43,7 +43,7 @@
 
   LocalMultiStoreElimPass();
   const char* name() const override { return "eliminate-local-multi-store"; }
-  Status Process(ir::Module*) override;
+  Status Process(ir::IRContext* c) override;
 
  private:
   // Initialize extensions whitelist
@@ -59,7 +59,7 @@
   // the runtime and effectiveness of this function.
   bool EliminateMultiStoreLocal(ir::Function* func);
 
-  void Initialize(ir::Module* module);
+  void Initialize(ir::IRContext* c);
   Pass::Status ProcessImpl();
 
   // Extensions supported by this pass.
diff --git a/source/opt/mem_pass.cpp b/source/opt/mem_pass.cpp
index 4dc8aa2..9c4b0a4 100644
--- a/source/opt/mem_pass.cpp
+++ b/source/opt/mem_pass.cpp
@@ -854,10 +854,10 @@
   return modified;
 }
 
-void MemPass::InitializeCFGCleanup(ir::Module* module) {
+void MemPass::InitializeCFGCleanup(ir::IRContext* c) {
   // Initialize block lookup map.
   label2block_.clear();
-  for (auto& fn : *module) {
+  for (auto& fn : *c->module()) {
     for (auto& block : fn) {
       label2block_[block.id()] = &block;
 
diff --git a/source/opt/mem_pass.h b/source/opt/mem_pass.h
index b5bb72d..6a18aeb 100644
--- a/source/opt/mem_pass.h
+++ b/source/opt/mem_pass.h
@@ -42,8 +42,8 @@
 
  protected:
   // Initialize basic data structures for the pass.
-  void InitializeProcessing(ir::Module* module) {
-    Pass::InitializeProcessing(module);
+  void InitializeProcessing(ir::IRContext* c) {
+    Pass::InitializeProcessing(c);
     FindNamedOrDecoratedIds();
   }
 
@@ -110,7 +110,7 @@
   void ReplaceAndDeleteLoad(ir::Instruction* loadInst, uint32_t replId);
 
   // Initialize CFG Cleanup variables
-  void InitializeCFGCleanup(ir::Module* module);
+  void InitializeCFGCleanup(ir::IRContext* context);
 
   // Call all the cleanup helper functions on |func|.
   bool CFGCleanup(ir::Function* func);
diff --git a/source/opt/null_pass.h b/source/opt/null_pass.h
index 832e891..4685f66 100644
--- a/source/opt/null_pass.h
+++ b/source/opt/null_pass.h
@@ -25,7 +25,7 @@
 class NullPass : public Pass {
  public:
   const char* name() const override { return "null"; }
-  Status Process(ir::Module*) override { return Status::SuccessWithoutChange; }
+  Status Process(ir::IRContext*) override { return Status::SuccessWithoutChange; }
 };
 
 }  // namespace opt
diff --git a/source/opt/optimizer.cpp b/source/opt/optimizer.cpp
index 2fdfe21..664a870 100644
--- a/source/opt/optimizer.cpp
+++ b/source/opt/optimizer.cpp
@@ -103,14 +103,15 @@
       BuildModule(impl_->target_env, impl_->pass_manager.consumer(),
                   original_binary, original_binary_size);
   if (module == nullptr) return false;
+  ir::IRContext context(std::move(module));
 
-  auto status = impl_->pass_manager.Run(module.get());
+  auto status = impl_->pass_manager.Run(&context);
   if (status == opt::Pass::Status::SuccessWithChange ||
       (status == opt::Pass::Status::SuccessWithoutChange &&
        (optimized_binary->data() != original_binary ||
         optimized_binary->size() != original_binary_size))) {
     optimized_binary->clear();
-    module->ToBinary(optimized_binary, /* skip_nop = */ true);
+    context.module()->ToBinary(optimized_binary, /* skip_nop = */ true);
   }
 
   return status != opt::Pass::Status::Failure;
diff --git a/source/opt/pass.cpp b/source/opt/pass.cpp
index a651b90..5f7fba5 100644
--- a/source/opt/pass.cpp
+++ b/source/opt/pass.cpp
@@ -43,7 +43,7 @@
       consumer_(nullptr),
       def_use_mgr_(nullptr),
       next_id_(0),
-      module_(nullptr) {}
+      context_(nullptr) {}
 
 void Pass::AddCalls(ir::Function* func, std::queue<uint32_t>* todo) {
   for (auto bi = func->begin(); bi != func->end(); ++bi)
@@ -64,20 +64,21 @@
   return ProcessCallTreeFromRoots(pfn, id2function, &roots);
 }
 
-bool Pass::ProcessReachableCallTree(ProcessFunction& pfn, ir::Module* module) {
+bool Pass::ProcessReachableCallTree(ProcessFunction& pfn,
+                                    ir::IRContext* irContext) {
   // Map from function's result id to function
   std::unordered_map<uint32_t, ir::Function*> id2function;
-  for (auto& fn : *module) id2function[fn.result_id()] = &fn;
+  for (auto& fn : *irContext->module()) id2function[fn.result_id()] = &fn;
 
   std::queue<uint32_t> roots;
 
   // Add all entry points since they can be reached from outside the module.
-  for (auto& e : module->entry_points())
+  for (auto& e : irContext->module()->entry_points())
     roots.push(e.GetSingleWordInOperand(kEntryPointFunctionIdInIdx));
 
   // Add all exported functions since they can be reached from outside the
   // module.
-  for (auto& a : module->annotations()) {
+  for (auto& a : irContext->annotations()) {
     // TODO: Handle group decorations as well.  Currently not generate by any
     // front-end, but could be coming.
     if (a.opcode() == SpvOp::SpvOpDecorate) {
diff --git a/source/opt/pass.h b/source/opt/pass.h
index 96b3529..ed85a3e 100644
--- a/source/opt/pass.h
+++ b/source/opt/pass.h
@@ -27,6 +27,7 @@
 #include "module.h"
 #include "spirv-tools/libspirv.hpp"
 #include "basic_block.h"
+#include "ir_context.h"
 
 namespace spvtools {
 namespace opt {
@@ -79,7 +80,10 @@
   }
 
   // Returns a pointer to the current module for this pass.
-  ir::Module* get_module() const { return module_; }
+  ir::Module* get_module() const { return context_->module(); }
+
+  // Returns a pointer to the current context for this pass.
+  ir::IRContext* context() const { return context_; }
 
   // Add to |todo| all ids of functions called in |func|.
   void AddCalls(ir::Function* func, std::queue<uint32_t>* todo);
@@ -92,7 +96,7 @@
   // Applies |pfn| to every function in the call trees rooted at the entry
   // points and exported functions.  Returns true if any call |pfn| returns
   // true.  By convention |pfn| should return true if it modified the module.
-  bool ProcessReachableCallTree(ProcessFunction& pfn, ir::Module* module);
+  bool ProcessReachableCallTree(ProcessFunction& pfn, ir::IRContext* irContext);
 
   // Applies |pfn| to every function in the call trees rooted at the elements of
   // |roots|.  Returns true if any call to |pfn| returns true.  By convention
@@ -106,21 +110,21 @@
   // Processes the given |module|. Returns Status::Failure if errors occur when
   // processing. Returns the corresponding Status::Success if processing is
   // succesful to indicate whether changes are made to the module.
-  virtual Status Process(ir::Module* module) = 0;
+  virtual Status Process(ir::IRContext* context) = 0;
 
  protected:
   // Initialize basic data structures for the pass. This sets up the def-use
   // manager, module and other attributes. TODO(dnovillo): Some of this should
   // be done during pass instantiation. Other things should be outside the pass
   // altogether (e.g., def-use manager).
-  virtual void InitializeProcessing(ir::Module* module) {
-    module_ = module;
-    next_id_ = module_->IdBound();
+  virtual void InitializeProcessing(ir::IRContext* c) {
+    context_ = c;
+    next_id_ = context_->IdBound();
     def_use_mgr_.reset(new analysis::DefUseManager(consumer(), get_module()));
     block2structured_succs_.clear();
     label2preds_.clear();
     id2block_.clear();
-    for (auto& fn : *module_) {
+    for (auto& fn : *context_->module()) {
       for (auto& blk : fn) {
         id2block_[blk.id()] = &blk;
       }
@@ -153,9 +157,9 @@
 
   // Return the next available Id and increment it.
   inline uint32_t TakeNextId() {
-    assert(module_ && next_id_ > 0);
+    assert(context_ && next_id_ > 0);
     uint32_t retval = next_id_++;
-    module_->SetIdBound(next_id_);
+    context_->SetIdBound(next_id_);
     return retval;
   }
 
@@ -193,7 +197,7 @@
   uint32_t next_id_;
 
   // The module that the pass is being applied to.
-  ir::Module* module_;
+  ir::IRContext* context_;
 };
 
 }  // namespace opt
diff --git a/source/opt/pass_manager.cpp b/source/opt/pass_manager.cpp
index 2def7bb..4260a78 100644
--- a/source/opt/pass_manager.cpp
+++ b/source/opt/pass_manager.cpp
@@ -13,20 +13,22 @@
 // limitations under the License.
 
 #include "pass_manager.h"
+#include "ir_context.h"
 
 namespace spvtools {
 namespace opt {
 
-Pass::Status PassManager::Run(ir::Module* module) {
+Pass::Status PassManager::Run(ir::IRContext* context) {
   auto status = Pass::Status::SuccessWithoutChange;
   for (const auto& pass : passes_) {
-    const auto one_status = pass->Process(module);
+    const auto one_status = pass->Process(context);
     if (one_status == Pass::Status::Failure) return one_status;
     if (one_status == Pass::Status::SuccessWithChange) status = one_status;
   }
+
   // Set the Id bound in the header in case a pass forgot to do so.
   if (status == Pass::Status::SuccessWithChange) {
-    module->SetIdBound(module->ComputeIdBound());
+    context->SetIdBound(context->module()->ComputeIdBound());
   }
   passes_.clear();
   return status;
diff --git a/source/opt/pass_manager.h b/source/opt/pass_manager.h
index 2cf9159..1193f5d 100644
--- a/source/opt/pass_manager.h
+++ b/source/opt/pass_manager.h
@@ -23,6 +23,7 @@
 #include "pass.h"
 
 #include "spirv-tools/libspirv.hpp"
+#include "ir_context.h"
 
 namespace spvtools {
 namespace opt {
@@ -65,7 +66,7 @@
   // whether changes are made to the module.
   //
   // After running all the passes, they are removed from the list.
-  Pass::Status Run(ir::Module* module);
+  Pass::Status Run(ir::IRContext* context);
 
  private:
   // Consumer for messages.
diff --git a/source/opt/remove_duplicates_pass.cpp b/source/opt/remove_duplicates_pass.cpp
index 51d2d44..6b78f3e 100644
--- a/source/opt/remove_duplicates_pass.cpp
+++ b/source/opt/remove_duplicates_pass.cpp
@@ -24,6 +24,7 @@
 
 #include "decoration_manager.h"
 #include "opcode.h"
+#include "ir_context.h"
 
 namespace spvtools {
 namespace opt {
@@ -34,23 +35,23 @@
 using opt::analysis::DefUseManager;
 using opt::analysis::DecorationManager;
 
-Pass::Status RemoveDuplicatesPass::Process(Module* module) {
-  DefUseManager defUseManager(consumer(), module);
-  DecorationManager decManager(module);
+Pass::Status RemoveDuplicatesPass::Process(ir::IRContext* irContext) {
+  DefUseManager defUseManager(consumer(), irContext->module());
+  DecorationManager decManager(irContext->module());
 
-  bool modified = RemoveDuplicateCapabilities(module);
-  modified |= RemoveDuplicatesExtInstImports(module, defUseManager);
-  modified |= RemoveDuplicateTypes(module, defUseManager, decManager);
-  modified |= RemoveDuplicateDecorations(module);
+  bool modified = RemoveDuplicateCapabilities(irContext);
+  modified |= RemoveDuplicatesExtInstImports(irContext, defUseManager);
+  modified |= RemoveDuplicateTypes(irContext, defUseManager, decManager);
+  modified |= RemoveDuplicateDecorations(irContext);
 
   return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
 }
 
-bool RemoveDuplicatesPass::RemoveDuplicateCapabilities(Module* module) const {
+bool RemoveDuplicatesPass::RemoveDuplicateCapabilities(ir::IRContext* irContext) const {
   bool modified = false;
 
   std::unordered_set<uint32_t> capabilities;
-  for (auto i = module->capability_begin(); i != module->capability_end();) {
+  for (auto i = irContext->capability_begin(); i != irContext->capability_end();) {
     auto res = capabilities.insert(i->GetSingleWordOperand(0u));
 
     if (res.second) {
@@ -67,12 +68,12 @@
 }
 
 bool RemoveDuplicatesPass::RemoveDuplicatesExtInstImports(
-    Module* module, analysis::DefUseManager& defUseManager) const {
+    ir::IRContext* irContext, analysis::DefUseManager& defUseManager) const {
   bool modified = false;
 
   std::unordered_map<std::string, SpvId> extInstImports;
-  for (auto i = module->ext_inst_import_begin();
-       i != module->ext_inst_import_end();) {
+  for (auto i = irContext->ext_inst_import_begin();
+       i != irContext->ext_inst_import_end();) {
     auto res = extInstImports.emplace(
         reinterpret_cast<const char*>(i->GetInOperand(0u).words.data()),
         i->result_id());
@@ -91,14 +92,14 @@
 }
 
 bool RemoveDuplicatesPass::RemoveDuplicateTypes(
-    Module* module, DefUseManager& defUseManager,
+    ir::IRContext* irContext, DefUseManager& defUseManager,
     DecorationManager& decManager) const {
   bool modified = false;
 
   std::vector<Instruction> visitedTypes;
 
-  for (auto i = module->types_values_begin();
-       i != module->types_values_end();) {
+  for (auto i = irContext->types_values_begin();
+       i != irContext->types_values_end();) {
     // We only care about types.
     if (!spvOpcodeGeneratesType((i->opcode())) &&
         i->opcode() != SpvOpTypeForwardPointer) {
@@ -131,19 +132,19 @@
 }
 
 bool RemoveDuplicatesPass::RemoveDuplicateDecorations(
-    ir::Module* module) const {
+    ir::IRContext* irContext) const {
   bool modified = false;
 
   std::unordered_map<SpvId, const Instruction*> constants;
-  for (const auto& i : module->types_values())
+  for (const auto& i : irContext->types_values())
     if (i.opcode() == SpvOpConstant) constants[i.result_id()] = &i;
-  for (const auto& i : module->types_values())
+  for (const auto& i : irContext->types_values())
     if (i.opcode() == SpvOpConstant) constants[i.result_id()] = &i;
 
   std::vector<const Instruction*> visitedDecorations;
 
-  opt::analysis::DecorationManager decorationManager(module);
-  for (auto i = module->annotation_begin(); i != module->annotation_end();) {
+  opt::analysis::DecorationManager decorationManager(irContext->module());
+  for (auto i = irContext->annotation_begin(); i != irContext->annotation_end();) {
     // Is the current decoration equal to one of the decorations we have aready
     // visited?
     bool alreadyVisited = false;
diff --git a/source/opt/remove_duplicates_pass.h b/source/opt/remove_duplicates_pass.h
index fcf4a05..02570c6 100644
--- a/source/opt/remove_duplicates_pass.h
+++ b/source/opt/remove_duplicates_pass.h
@@ -21,6 +21,7 @@
 #include "def_use_manager.h"
 #include "module.h"
 #include "pass.h"
+#include "ir_context.h"
 
 namespace spvtools {
 namespace opt {
@@ -32,7 +33,7 @@
 class RemoveDuplicatesPass : public Pass {
  public:
   const char* name() const override { return "remove-duplicates"; }
-  Status Process(ir::Module*) override;
+  Status Process(ir::IRContext*) override;
   // Returns whether two types are equal, and have the same decorations.
   static bool AreTypesEqual(const ir::Instruction& inst1,
                             const ir::Instruction& inst2,
@@ -40,13 +41,13 @@
                             const analysis::DecorationManager& decoManager);
 
  private:
-  bool RemoveDuplicateCapabilities(ir::Module* module) const;
+  bool RemoveDuplicateCapabilities(ir::IRContext* irContext) const;
   bool RemoveDuplicatesExtInstImports(
-      ir::Module* module, analysis::DefUseManager& defUseManager) const;
-  bool RemoveDuplicateTypes(ir::Module* module,
+      ir::IRContext* irContext, analysis::DefUseManager& defUseManager) const;
+  bool RemoveDuplicateTypes(ir::IRContext* irContext,
                             analysis::DefUseManager& defUseManager,
                             analysis::DecorationManager& decManager) const;
-  bool RemoveDuplicateDecorations(ir::Module* module) const;
+  bool RemoveDuplicateDecorations(ir::IRContext* irContext) const;
 };
 
 }  // namespace opt
diff --git a/source/opt/set_spec_constant_default_value_pass.cpp b/source/opt/set_spec_constant_default_value_pass.cpp
index 02422ca..422051c 100644
--- a/source/opt/set_spec_constant_default_value_pass.cpp
+++ b/source/opt/set_spec_constant_default_value_pass.cpp
@@ -26,6 +26,7 @@
 #include "type_manager.h"
 #include "types.h"
 #include "util/parse_number.h"
+#include "ir_context.h"
 
 namespace spvtools {
 namespace opt {
@@ -187,7 +188,7 @@
 }
 };
 
-Pass::Status SetSpecConstantDefaultValuePass::Process(ir::Module* module) {
+Pass::Status SetSpecConstantDefaultValuePass::Process(ir::IRContext* irContext) {
   // The operand index of decoration target in an OpDecorate instruction.
   const uint32_t kTargetIdOperandIndex = 0;
   // The operand index of the decoration literal in an OpDecorate instruction.
@@ -201,8 +202,8 @@
   const uint32_t kOpSpecConstantLiteralInOperandIndex = 0;
 
   bool modified = false;
-  analysis::DefUseManager def_use_mgr(consumer(), module);
-  analysis::TypeManager type_mgr(consumer(), *module);
+  analysis::DefUseManager def_use_mgr(consumer(), irContext->module());
+  analysis::TypeManager type_mgr(consumer(), *irContext->module());
   // Scan through all the annotation instructions to find 'OpDecorate SpecId'
   // instructions. Then extract the decoration target of those instructions.
   // The decoration targets should be spec constant defining instructions with
@@ -212,7 +213,7 @@
   // is found for a spec id, the string will be parsed according to the target
   // spec constant type. The parsed value will be used to replace the original
   // default value of the target spec constant.
-  for (ir::Instruction& inst : module->annotations()) {
+  for (ir::Instruction& inst : irContext->annotations()) {
     // Only process 'OpDecorate SpecId' instructions
     if (inst.opcode() != SpvOp::SpvOpDecorate) continue;
     if (inst.NumOperands() != kOpDecorateSpecIdNumOperands) continue;
diff --git a/source/opt/set_spec_constant_default_value_pass.h b/source/opt/set_spec_constant_default_value_pass.h
index 15fcc0a..dc4efca 100644
--- a/source/opt/set_spec_constant_default_value_pass.h
+++ b/source/opt/set_spec_constant_default_value_pass.h
@@ -21,6 +21,7 @@
 
 #include "module.h"
 #include "pass.h"
+#include "ir_context.h"
 
 namespace spvtools {
 namespace opt {
@@ -50,7 +51,7 @@
       : spec_id_to_value_str_(), spec_id_to_value_bit_pattern_(std::move(default_values)) {}
 
   const char* name() const override { return "set-spec-const-default-value"; }
-  Status Process(ir::Module*) override;
+  Status Process(ir::IRContext*) override;
 
   // Parses the given null-terminated C string to get a mapping from Spec Id to
   // default value strings. Returns a unique pointer of the mapping from spec
diff --git a/source/opt/strength_reduction_pass.cpp b/source/opt/strength_reduction_pass.cpp
index 1da9cce..28c80fd 100644
--- a/source/opt/strength_reduction_pass.cpp
+++ b/source/opt/strength_reduction_pass.cpp
@@ -23,6 +23,7 @@
 #include "def_use_manager.h"
 #include "log.h"
 #include "reflect.h"
+#include "ir_context.h"
 
 namespace {
 // Count the number of trailing zeros in the binary representation of
@@ -52,8 +53,8 @@
 namespace spvtools {
 namespace opt {
 
-Pass::Status StrengthReductionPass::Process(ir::Module* module) {
-  InitializeProcessing(module);
+Pass::Status StrengthReductionPass::Process(ir::IRContext* c) {
+  InitializeProcessing(c);
 
   // Initialize the member variables on a per module basis.
   bool modified = false;
@@ -199,7 +200,7 @@
                           {0});
   std::unique_ptr<ir::Instruction> newType(new ir::Instruction(
       SpvOp::SpvOpTypeInt, type_id, 0, {widthOperand, signOperand}));
-  get_module()->AddType(std::move(newType));
+  context()->AddType(std::move(newType));
   return type_id;
 }
 
diff --git a/source/opt/strength_reduction_pass.h b/source/opt/strength_reduction_pass.h
index 699f070..8981660 100644
--- a/source/opt/strength_reduction_pass.h
+++ b/source/opt/strength_reduction_pass.h
@@ -18,6 +18,7 @@
 #include "def_use_manager.h"
 #include "module.h"
 #include "pass.h"
+#include "ir_context.h"
 
 namespace spvtools {
 namespace opt {
@@ -26,7 +27,7 @@
 class StrengthReductionPass : public Pass {
  public:
   const char* name() const override { return "strength-reduction"; }
-  Status Process(ir::Module*) override;
+  Status Process(ir::IRContext*) override;
 
  private:
   // Replaces multiple by power of 2 with an equivalent bit shift.
diff --git a/source/opt/strip_debug_info_pass.cpp b/source/opt/strip_debug_info_pass.cpp
index 3b4b443..6cbba42 100644
--- a/source/opt/strip_debug_info_pass.cpp
+++ b/source/opt/strip_debug_info_pass.cpp
@@ -13,16 +13,17 @@
 // limitations under the License.
 
 #include "strip_debug_info_pass.h"
+#include "ir_context.h"
 
 namespace spvtools {
 namespace opt {
 
-Pass::Status StripDebugInfoPass::Process(ir::Module* module) {
-  bool modified = !module->debugs1().empty() || !module->debugs2().empty() ||
-                  !module->debugs3().empty();
-  module->debug_clear();
+Pass::Status StripDebugInfoPass::Process(ir::IRContext* irContext) {
+  bool modified = !irContext->debugs1().empty() || !irContext->debugs2().empty() ||
+                  !irContext->debugs3().empty();
+  irContext->debug_clear();
 
-  module->ForEachInst([&modified](ir::Instruction* inst) {
+  irContext->module()->ForEachInst([&modified](ir::Instruction* inst) {
     modified |= !inst->dbg_line_insts().empty();
     inst->dbg_line_insts().clear();
   });
diff --git a/source/opt/strip_debug_info_pass.h b/source/opt/strip_debug_info_pass.h
index 626f9a8..3e88c94 100644
--- a/source/opt/strip_debug_info_pass.h
+++ b/source/opt/strip_debug_info_pass.h
@@ -17,6 +17,7 @@
 
 #include "module.h"
 #include "pass.h"
+#include "ir_context.h"
 
 namespace spvtools {
 namespace opt {
@@ -25,7 +26,7 @@
 class StripDebugInfoPass : public Pass {
  public:
   const char* name() const override { return "strip-debug"; }
-  Status Process(ir::Module* module) override;
+  Status Process(ir::IRContext* irContext) override;
 };
 
 }  // namespace opt
diff --git a/source/opt/unify_const_pass.cpp b/source/opt/unify_const_pass.cpp
index c216e12..93ae273 100644
--- a/source/opt/unify_const_pass.cpp
+++ b/source/opt/unify_const_pass.cpp
@@ -19,6 +19,7 @@
 
 #include "def_use_manager.h"
 #include "make_unique.h"
+#include "ir_context.h"
 
 namespace spvtools {
 namespace opt {
@@ -102,13 +103,13 @@
 };
 }  // anonymous namespace
 
-Pass::Status UnifyConstantPass::Process(ir::Module* module) {
-  InitializeProcessing(module);
+Pass::Status UnifyConstantPass::Process(ir::IRContext* c) {
+  InitializeProcessing(c);
   bool modified = false;
   ResultIdTrie defined_constants;
-  analysis::DefUseManager def_use_mgr(consumer(), module);
+  analysis::DefUseManager def_use_mgr(consumer(), get_module());
 
-  for (ir::Instruction& inst : module->types_values()) {
+  for (ir::Instruction& inst : context()->types_values()) {
     // Do not handle the instruction when there are decorations upon the result
     // id.
     if (def_use_mgr.GetAnnotations(inst.result_id()).size() != 0) {
diff --git a/source/opt/unify_const_pass.h b/source/opt/unify_const_pass.h
index 4fd8c64..913ed49 100644
--- a/source/opt/unify_const_pass.h
+++ b/source/opt/unify_const_pass.h
@@ -17,6 +17,7 @@
 
 #include "module.h"
 #include "pass.h"
+#include "ir_context.h"
 
 namespace spvtools {
 namespace opt {
@@ -25,7 +26,7 @@
 class UnifyConstantPass : public Pass {
  public:
   const char* name() const override { return "unify-const"; }
-  Status Process(ir::Module*) override;
+  Status Process(ir::IRContext*) override;
 };
 
 }  // namespace opt
diff --git a/test/opt/line_debug_info_test.cpp b/test/opt/line_debug_info_test.cpp
index 43edc94..2bb7948 100644
--- a/test/opt/line_debug_info_test.cpp
+++ b/test/opt/line_debug_info_test.cpp
@@ -23,9 +23,9 @@
 class NopifyPass : public opt::Pass {
  public:
   const char* name() const override { return "NopifyPass"; }
-  Status Process(ir::Module* module) override {
+  Status Process(ir::IRContext* irContext) override {
     bool modified = false;
-    module->ForEachInst(
+    irContext->module()->ForEachInst(
         [&modified](ir::Instruction* inst) {
           inst->ToNop();
           modified = true;
diff --git a/test/opt/pass_fixture.h b/test/opt/pass_fixture.h
index c5c45d9..bf59648 100644
--- a/test/opt/pass_fixture.h
+++ b/test/opt/pass_fixture.h
@@ -61,10 +61,12 @@
           opt::Pass::Status::Failure);
     }
 
-    const auto status = pass->Process(module.get());
+    ir::IRContext context(std::move(module));
+
+    const auto status = pass->Process(&context);
 
     std::vector<uint32_t> binary;
-    module->ToBinary(&binary, skip_nop);
+    context.module()->ToBinary(&binary, skip_nop);
     return std::make_tuple(binary, status);
   }
 
@@ -169,11 +171,12 @@
     std::unique_ptr<ir::Module> module = BuildModule(
         SPV_ENV_UNIVERSAL_1_1, nullptr, original, assemble_options_);
     ASSERT_NE(nullptr, module);
+    ir::IRContext context(std::move(module));
 
-    manager_->Run(module.get());
+    manager_->Run(&context);
 
     std::vector<uint32_t> binary;
-    module->ToBinary(&binary, /* skip_nop = */ false);
+    context.module()->ToBinary(&binary, /* skip_nop = */ false);
 
     std::string optimized;
     EXPECT_TRUE(tools_.Disassemble(binary, &optimized,
diff --git a/test/opt/pass_manager_test.cpp b/test/opt/pass_manager_test.cpp
index d06ad3d..d54e745 100644
--- a/test/opt/pass_manager_test.cpp
+++ b/test/opt/pass_manager_test.cpp
@@ -74,8 +74,8 @@
 class AppendOpNopPass : public opt::Pass {
  public:
   const char* name() const override { return "AppendOpNop"; }
-  Status Process(ir::Module* module) override {
-    module->AddDebug1Inst(MakeUnique<ir::Instruction>());
+  Status Process(ir::IRContext* irContext) override {
+    irContext->AddDebug1Inst(MakeUnique<ir::Instruction>());
     return Status::SuccessWithChange;
   }
 };
@@ -87,9 +87,9 @@
   explicit AppendMultipleOpNopPass(uint32_t num_nop) : num_nop_(num_nop) {}
 
   const char* name() const override { return "AppendOpNop"; }
-  Status Process(ir::Module* module) override {
+  Status Process(ir::IRContext* irContext) override {
     for (uint32_t i = 0; i < num_nop_; i++) {
-      module->AddDebug1Inst(MakeUnique<ir::Instruction>());
+      irContext->AddDebug1Inst(MakeUnique<ir::Instruction>());
     }
     return Status::SuccessWithChange;
   }
@@ -102,9 +102,9 @@
 class DuplicateInstPass : public opt::Pass {
  public:
   const char* name() const override { return "DuplicateInst"; }
-  Status Process(ir::Module* module) override {
-    auto inst = MakeUnique<ir::Instruction>(*(--module->debug1_end()));
-    module->AddDebug1Inst(std::move(inst));
+  Status Process(ir::IRContext* irContext) override {
+    auto inst = MakeUnique<ir::Instruction>(*(--irContext->debug1_end()));
+    irContext->AddDebug1Inst(std::move(inst));
     return Status::SuccessWithChange;
   }
 };
@@ -139,10 +139,10 @@
   explicit AppendTypeVoidInstPass(uint32_t result_id) : result_id_(result_id) {}
 
   const char* name() const override { return "AppendTypeVoidInstPass"; }
-  Status Process(ir::Module* module) override {
+  Status Process(ir::IRContext* irContext) override {
     auto inst = MakeUnique<ir::Instruction>(SpvOpTypeVoid, 0, result_id_,
                                             std::vector<ir::Operand>{});
-    module->AddType(std::move(inst));
+    irContext->AddType(std::move(inst));
     return Status::SuccessWithChange;
   }
 
@@ -151,33 +151,35 @@
 };
 
 TEST(PassManager, RecomputeIdBoundAutomatically) {
-  ir::Module module;
-  EXPECT_THAT(GetIdBound(module), Eq(0u));
+  std::unique_ptr<ir::Module> module(new ir::Module());
+  ir::IRContext context(std::move(module));
+  EXPECT_THAT(GetIdBound(*context.module()), Eq(0u));
+
 
   opt::PassManager manager;
-  manager.Run(&module);
+  manager.Run(&context);
   manager.AddPass<AppendOpNopPass>();
   // With no ID changes, the ID bound does not change.
-  EXPECT_THAT(GetIdBound(module), Eq(0u));
+  EXPECT_THAT(GetIdBound(*context.module()), Eq(0u));
 
   // Now we force an Id of 100 to be used.
   manager.AddPass(MakeUnique<AppendTypeVoidInstPass>(100));
-  EXPECT_THAT(GetIdBound(module), Eq(0u));
-  manager.Run(&module);
+  EXPECT_THAT(GetIdBound(*context.module()), Eq(0u));
+  manager.Run(&context);
   // The Id has been updated automatically, even though the pass
   // did not update it.
-  EXPECT_THAT(GetIdBound(module), Eq(101u));
+  EXPECT_THAT(GetIdBound(*context.module()), Eq(101u));
 
   // Try one more time!
   manager.AddPass(MakeUnique<AppendTypeVoidInstPass>(200));
-  manager.Run(&module);
-  EXPECT_THAT(GetIdBound(module), Eq(201u));
+  manager.Run(&context);
+  EXPECT_THAT(GetIdBound(*context.module()), Eq(201u));
 
   // Add another pass, but which uses a lower Id.
   manager.AddPass(MakeUnique<AppendTypeVoidInstPass>(10));
-  manager.Run(&module);
+  manager.Run(&context);
   // The Id stays high.
-  EXPECT_THAT(GetIdBound(module), Eq(201u));
+  EXPECT_THAT(GetIdBound(*context.module()), Eq(201u));
 }
 
 }  // anonymous namespace
diff --git a/test/opt/pass_test.cpp b/test/opt/pass_test.cpp
index a6298ca..012ea72 100644
--- a/test/opt/pass_test.cpp
+++ b/test/opt/pass_test.cpp
@@ -27,8 +27,8 @@
 class DummyPass : public opt::Pass {
  public:
   const char* name() const override { return "dummy-pass"; }
-  Status Process(ir::Module* module) override {
-    return module ? Status::SuccessWithoutChange : Status::Failure;
+  Status Process(ir::IRContext* irContext) override {
+    return irContext ? Status::SuccessWithoutChange : Status::Failure;
   }
 };
 }  // namespace
@@ -137,13 +137,15 @@
                   SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
   EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
                              << text << std::endl;
+  ir::IRContext context(std::move(module));
+
   DummyPass testPass;
   std::vector<uint32_t> processed;
   opt::Pass::ProcessFunction mark_visited = [&processed](ir::Function* fp) {
     processed.push_back(fp->result_id());
     return false;
   };
-  testPass.ProcessReachableCallTree(mark_visited, module.get());
+  testPass.ProcessReachableCallTree(mark_visited, &context);
   EXPECT_THAT(processed, UnorderedElementsAre(10, 11, 12, 13));
 }
 
@@ -188,13 +190,15 @@
                   SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
   EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
                              << text << std::endl;
+  ir::IRContext context(std::move(module));
+
   DummyPass testPass;
   std::vector<uint32_t> processed;
   opt::Pass::ProcessFunction mark_visited = [&processed](ir::Function* fp) {
     processed.push_back(fp->result_id());
     return false;
   };
-  testPass.ProcessReachableCallTree(mark_visited, module.get());
+  testPass.ProcessReachableCallTree(mark_visited, &context);
   EXPECT_THAT(processed, UnorderedElementsAre(10, 11, 12));
 }
 
@@ -229,13 +233,15 @@
                   SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
   EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
                              << text << std::endl;
+  ir::IRContext context(std::move(module));
+
   DummyPass testPass;
   std::vector<uint32_t> processed;
   opt::Pass::ProcessFunction mark_visited = [&processed](ir::Function* fp) {
     processed.push_back(fp->result_id());
     return false;
   };
-  testPass.ProcessReachableCallTree(mark_visited, module.get());
+  testPass.ProcessReachableCallTree(mark_visited, &context);
   EXPECT_THAT(processed, UnorderedElementsAre(10));
 }
 }  // namespace