Track uses and defs during parsing.
authorDejan Mircevski <deki@google.com>
Fri, 15 Jan 2016 16:25:11 +0000 (11:25 -0500)
committerDavid Neto <dneto@google.com>
Wed, 20 Jan 2016 22:00:58 +0000 (17:00 -0500)
Replace two other, imperfect mechanisms for use-def tracking.

Use ValidationState_t::entry_points to track entry points.

Concentrate undefined-ID diagnostics in a single place.

Move validate_types.h content into validate.h due to increased
inter-dependency.

Track uses of all IDs: TYPE_ID, SCOPE_ID, ...

Also update some blurbs.

Fix entry-point accumulation and move it outside ProcessIds().

Remove validate_types.h from CMakeLists.txt.

Blurb for spvIsIdType.

Remove redundant diagnostics for undefined IDs.

Join "can not" and reformat.

14 files changed:
CMakeLists.txt
source/operand.cpp
source/operand.h
source/validate.cpp
source/validate.h
source/validate_cfg.cpp
source/validate_id.cpp
source/validate_instruction.cpp
source/validate_layout.cpp
source/validate_passes.h
source/validate_ssa.cpp
source/validate_types.cpp
source/validate_types.h [deleted file]
test/ValidateID.cpp

index fa778c3..035987e 100644 (file)
@@ -121,7 +121,6 @@ set(SPIRV_SOURCES
   ${CMAKE_CURRENT_SOURCE_DIR}/source/text.h
   ${CMAKE_CURRENT_SOURCE_DIR}/source/text_handler.h
   ${CMAKE_CURRENT_SOURCE_DIR}/source/validate.h
-  ${CMAKE_CURRENT_SOURCE_DIR}/source/validate_types.h
   ${CMAKE_CURRENT_SOURCE_DIR}/source/assembly_grammar.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/source/binary.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/source/disassemble.cpp
index 864a1d6..7eae14e 100644 (file)
@@ -1367,3 +1367,17 @@ spv_operand_pattern_t spvAlternatePatternFollowingImmediate(
   // No result-id found, so just expect CIVs.
   return {SPV_OPERAND_TYPE_OPTIONAL_CIV};
 }
+
+bool spvIsIdType(spv_operand_type_t type) {
+  switch (type) {
+    case SPV_OPERAND_TYPE_ID:
+    case SPV_OPERAND_TYPE_TYPE_ID:
+    case SPV_OPERAND_TYPE_RESULT_ID:
+    case SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID:
+    case SPV_OPERAND_TYPE_SCOPE_ID:
+      return true;
+    default:
+      return false;
+  }
+  return false;
+}
index 96c0bc9..3e27c97 100644 (file)
@@ -125,4 +125,7 @@ spv_operand_type_t spvTakeFirstMatchableOperand(spv_operand_pattern_t* pattern);
 spv_operand_pattern_t spvAlternatePatternFollowingImmediate(
     const spv_operand_pattern_t& pattern);
 
+// Is the operand an ID?
+bool spvIsIdType(spv_operand_type_t type);
+
 #endif  // LIBSPIRV_OPERAND_H_
index f06ca2c..a09eb12 100644 (file)
@@ -25,7 +25,6 @@
 // MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
 
 #include "validate.h"
-#include "validate_types.h"
 #include "validate_passes.h"
 
 #include "binary.h"
@@ -206,81 +205,20 @@ spv_result_t spvValidateBasic(const spv_instruction_t* pInsts,
 }
 #endif
 
-spv_result_t spvValidateIDs(const spv_instruction_t* pInsts,
-                            const uint64_t count, const uint32_t bound,
-                            const spv_opcode_table opcodeTable,
-                            const spv_operand_table operandTable,
-                            const spv_ext_inst_table extInstTable,
-                            spv_position position,
-                            spv_diagnostic* pDiagnostic) {
-  std::vector<spv_id_info_t> idUses;
-  std::vector<spv_id_info_t> idDefs;
-
-  for (uint64_t instIndex = 0; instIndex < count; ++instIndex) {
-    const uint32_t* words = pInsts[instIndex].words.data();
-    SpvOp opcode;
-    spvOpcodeSplit(words[0], nullptr, &opcode);
-
-    spv_opcode_desc opcodeEntry = nullptr;
-    if (spvOpcodeTableValueLookup(opcodeTable, opcode, &opcodeEntry)) {
-      DIAGNOSTIC << "Invalid Opcode '" << opcode << "'.";
-      return SPV_ERROR_INVALID_BINARY;
-    }
-
-    spv_operand_desc operandEntry = nullptr;
-    position->index++;  // NOTE: Account for Opcode word
-    for (uint16_t index = 1; index < pInsts[instIndex].words.size();
-         ++index, position->index++) {
-      const uint32_t word = words[index];
-
-      spv_operand_type_t type = spvBinaryOperandInfo(
-          word, index, opcodeEntry, operandTable, &operandEntry);
-
-      if (SPV_OPERAND_TYPE_RESULT_ID == type || SPV_OPERAND_TYPE_ID == type) {
-        if (0 == word) {
-          DIAGNOSTIC << "Invalid ID of '0' is not allowed.";
-          return SPV_ERROR_INVALID_ID;
-        }
-        if (bound < word) {
-          DIAGNOSTIC << "Invalid ID '" << word << "' exceeds the bound '"
-                     << bound << "'.";
-          return SPV_ERROR_INVALID_ID;
-        }
-      }
-
-      if (SPV_OPERAND_TYPE_RESULT_ID == type) {
-        idDefs.push_back(
-            {word, opcodeEntry->opcode, &pInsts[instIndex], *position});
-      }
-
-      if (SPV_OPERAND_TYPE_ID == type) {
-        idUses.push_back({word, opcodeEntry->opcode, nullptr, *position});
-      }
-    }
-  }
-
-  // NOTE: Error on redefined ID
-  for (size_t outerIndex = 0; outerIndex < idDefs.size(); ++outerIndex) {
-    for (size_t innerIndex = 0; innerIndex < idDefs.size(); ++innerIndex) {
-      if (outerIndex == innerIndex) {
-        continue;
-      }
-      if (idDefs[outerIndex].id == idDefs[innerIndex].id) {
-        DIAGNOSTIC << "Multiply defined ID '" << idDefs[outerIndex].id << "'.";
-        return SPV_ERROR_INVALID_ID;
-      }
-    }
+spv_result_t spvValidateIDs(
+    const spv_instruction_t* pInsts, const uint64_t count,
+    const spv_opcode_table opcodeTable, const spv_operand_table operandTable,
+    const spv_ext_inst_table extInstTable, const ValidationState_t& state,
+    spv_position position, spv_diagnostic* pDiagnostic) {
+  auto undefd = state.usedefs().FindUsesWithoutDefs();
+  for (auto id : undefd) {
+    DIAGNOSTIC << "Undefined ID: " << id;
   }
-
-  // NOTE: Validate ID usage, including use of undefined ID's
   position->index = SPV_INDEX_INSTRUCTION;
-  if (spvValidateInstructionIDs(pInsts, count, idUses.data(), idUses.size(),
-                                idDefs.data(), idDefs.size(), opcodeTable,
-                                operandTable, extInstTable, position,
-                                pDiagnostic))
-    return SPV_ERROR_INVALID_ID;
-
-  return SPV_SUCCESS;
+  spvCheckReturn(spvValidateInstructionIDs(pInsts, count, opcodeTable,
+                                           operandTable, extInstTable, state,
+                                           position, pDiagnostic));
+  return undefd.empty() ? SPV_SUCCESS : SPV_ERROR_INVALID_ID;
 }
 
 namespace {
@@ -332,14 +270,28 @@ void DebugInstructionPass(ValidationState_t& _,
   }
 }
 
+// Collects use-def info about an instruction's IDs.
+void ProcessIds(ValidationState_t& _, const spv_parsed_instruction_t& inst) {
+  if (inst.result_id) {
+    _.usedefs().AddDef(
+        {inst.result_id, inst.opcode,
+         std::vector<uint32_t>(inst.words, inst.words + inst.num_words)});
+  }
+  for (auto op = inst.operands; op != inst.operands + inst.num_operands; ++op) {
+    if (spvIsIdType(op->type))
+      _.usedefs().AddUse(inst.words[op->offset]);
+  }
+}
+
 spv_result_t ProcessInstruction(void* user_data,
-                                 const spv_parsed_instruction_t* inst) {
+                                const spv_parsed_instruction_t* inst) {
   ValidationState_t& _ = *(reinterpret_cast<ValidationState_t*>(user_data));
   _.incrementInstructionCount();
+  if (inst->opcode == SpvOpEntryPoint) _.entry_points().push_back(inst->words[2]);
 
   DebugInstructionPass(_, inst);
-
   // TODO(umar): Perform data rules pass
+  ProcessIds(_, *inst);
   spvCheckReturn(ModuleLayoutPass(_, inst));
   spvCheckReturn(CfgPass(_, inst));
   spvCheckReturn(SsaPass(_, inst));
@@ -376,8 +328,8 @@ spv_result_t spvValidate(const spv_const_context context,
                                 ProcessInstruction, pDiagnostic));
 
   // TODO(umar): Add validation checks which require the parsing of the entire
-  // module. Use the information from the processInstructions pass to make
-  // the checks.
+  // module. Use the information from the ProcessInstruction pass to make the
+  // checks.
 
   if (vstate.unresolvedForwardIdCount() > 0) {
     stringstream ss;
@@ -408,10 +360,10 @@ spv_result_t spvValidate(const spv_const_context context,
 
   if (spvIsInBitfield(SPV_VALIDATE_ID_BIT, options)) {
     position.index = SPV_INDEX_INSTRUCTION;
-    spvCheckReturn(
-        spvValidateIDs(instructions.data(), instructions.size(), header.bound,
-                       context->opcode_table, context->operand_table,
-                       context->ext_inst_table, &position, pDiagnostic));
+    spvCheckReturn(spvValidateIDs(instructions.data(), instructions.size(),
+                                  context->opcode_table, context->operand_table,
+                                  context->ext_inst_table, vstate, &position,
+                                  pDiagnostic));
   }
 
   return SPV_SUCCESS;
index 86483d0..f2b5c64 100644 (file)
 #ifndef LIBSPIRV_VALIDATE_H_
 #define LIBSPIRV_VALIDATE_H_
 
-#include "instruction.h"
+#include <algorithm>
+#include <map>
+#include <string>
+#include <unordered_set>
+#include <utility>
+#include <vector>
+
 #include "libspirv/libspirv.h"
+
+#include "binary.h"
+#include "diagnostic.h"
+#include "instruction.h"
 #include "table.h"
 
 // Structures
 
+// Info about a result ID.
 typedef struct spv_id_info_t {
+  // Id value.
   uint32_t id;
+  // Opcode of the instruction defining the id.
   SpvOp opcode;
-  const spv_instruction_t* inst;
-  spv_position_t position;
+  // Binary words of the instruction defining the id.
+  std::vector<uint32_t> words;
 } spv_id_info_t;
 
+namespace libspirv {
+
+// This enum represents the sections of a SPIRV module. See section 2.4
+// of the SPIRV spec for additional details of the order. The enumerant values
+// are in the same order as the vector returned by GetModuleOrder
+enum ModuleLayoutSection {
+  kLayoutCapabilities,          // < Section 2.4 #1
+  kLayoutExtensions,            // < Section 2.4 #2
+  kLayoutExtInstImport,         // < Section 2.4 #3
+  kLayoutMemoryModel,           // < Section 2.4 #4
+  kLayoutEntryPoint,            // < Section 2.4 #5
+  kLayoutExecutionMode,         // < Section 2.4 #6
+  kLayoutDebug1,                // < Section 2.4 #7 > 1
+  kLayoutDebug2,                // < Section 2.4 #7 > 2
+  kLayoutAnnotations,           // < Section 2.4 #8
+  kLayoutTypes,                 // < Section 2.4 #9
+  kLayoutFunctionDeclarations,  // < Section 2.4 #10
+  kLayoutFunctionDefinitions    // < Section 2.4 #11
+};
+
+enum class FunctionDecl {
+  kFunctionDeclUnknown,      // < Unknown function declaration
+  kFunctionDeclDeclaration,  // < Function declaration
+  kFunctionDeclDefinition    // < Function definition
+};
+
+class ValidationState_t;
+
+// This class manages all function declaration and definitions in a module. It
+// handles the state and id information while parsing a function in the SPIR-V
+// binary.
+//
+// NOTE: This class is designed to be a Structure of Arrays. Therefore each
+// member variable is a vector whose elements represent the values for the
+// corresponding function in a SPIR-V module. Variables that are not vector
+// types are used to manage the state while parsing the function.
+class Functions {
+ public:
+  Functions(ValidationState_t& module);
+
+  // Registers the function in the module. Subsequent instructions will be
+  // called against this function
+  spv_result_t RegisterFunction(uint32_t id, uint32_t ret_type_id,
+                                uint32_t function_control,
+                                uint32_t function_type_id);
+
+  // Registers a function parameter in the current function
+  spv_result_t RegisterFunctionParameter(uint32_t id, uint32_t type_id);
+
+  // Register a function end instruction
+  spv_result_t RegisterFunctionEnd();
+
+  // Sets the declaration type of the current function
+  spv_result_t RegisterSetFunctionDeclType(FunctionDecl type);
+
+  // Registers a block in the current function. Subsequent block instructions
+  // will target this block
+  // @param id The ID of the label of the block
+  spv_result_t RegisterBlock(uint32_t id);
+
+  // Registers a variable in the current block
+  spv_result_t RegisterBlockVariable(uint32_t type_id, uint32_t id,
+                                     SpvStorageClass storage, uint32_t init_id);
+
+  spv_result_t RegisterBlockLoopMerge(uint32_t merge_id, uint32_t continue_id,
+                                      SpvLoopControlMask control);
+
+  spv_result_t RegisterBlockSelectionMerge(uint32_t merge_id,
+                                           SpvSelectionControlMask control);
+
+  // Registers the end of the block
+  spv_result_t RegisterBlockEnd();
+
+  // Returns the number of blocks in the current function being parsed
+  size_t get_block_count() const;
+
+  // Retuns true if called after a function instruction but before the
+  // function end instruction
+  bool in_function_body() const;
+
+  // Returns true if called after a label instruction but before a branch
+  // instruction
+  bool in_block() const;
+
+  libspirv::DiagnosticStream diag(spv_result_t error_code) const;
+
+ private:
+  // Parent module
+  ValidationState_t& module_;
+
+  // Funciton IDs in a module
+  std::vector<uint32_t> id_;
+
+  // OpTypeFunction IDs of each of the id_ functions
+  std::vector<uint32_t> type_id_;
+
+  // The type of declaration of each function
+  std::vector<FunctionDecl> declaration_type_;
+
+  // TODO(umar): Probably needs better abstractions
+  // The beginning of the block of functions
+  std::vector<std::vector<uint32_t>> block_ids_;
+
+  // The variable IDs of the functions
+  std::vector<std::vector<uint32_t>> variable_ids_;
+
+  // The function parameter ids of the functions
+  std::vector<std::vector<uint32_t>> parameter_ids_;
+
+  // NOTE: See correspoding getter functions
+  bool in_function_;
+  bool in_block_;
+};
+
+class ValidationState_t {
+ public:
+  ValidationState_t(spv_diagnostic* diagnostic, uint32_t options);
+
+  // Forward declares the id in the module
+  spv_result_t forwardDeclareId(uint32_t id);
+
+  // Removes a forward declared ID if it has been defined
+  spv_result_t removeIfForwardDeclared(uint32_t id);
+
+  // Assigns a name to an ID
+  void assignNameToId(uint32_t id, std::string name);
+
+  // Returns a string representation of the ID in the format <id>[Name] where
+  // the <id> is the numeric valid of the id and the Name is a name assigned by
+  // the OpName instruction
+  std::string getIdName(uint32_t id) const;
+
+  // Returns the number of ID which have been forward referenced but not defined
+  size_t unresolvedForwardIdCount() const;
+
+  // Returns a list of unresolved forward ids.
+  std::vector<uint32_t> unresolvedForwardIds() const;
+
+  // Returns true if the id has been defined
+  bool isDefinedId(uint32_t id) const;
+
+  // Returns true if an spv_validate_options_t option is enabled in the
+  // validation instruction
+  bool is_enabled(spv_validate_options_t flag) const;
+
+  // Increments the instruction count. Used for diagnostic
+  int incrementInstructionCount();
+
+  // Returns the current layout section which is being processed
+  ModuleLayoutSection getLayoutSection() const;
+
+  // Increments the module_layout_order_section_
+  void progressToNextLayoutSectionOrder();
+
+  // Determines if the op instruction is part of the current section
+  bool isOpcodeInCurrentLayoutSection(SpvOp op);
+
+  libspirv::DiagnosticStream diag(spv_result_t error_code) const;
+
+  // Returns the function states
+  Functions& get_functions();
+
+  // Retuns true if the called after a function instruction but before the
+  // function end instruction
+  bool in_function_body() const;
+
+  // Returns true if called after a label instruction but before a branch
+  // instruction
+  bool in_block() const;
+
+  // Keeps track of ID definitions and uses.
+  class UseDefTracker {
+   public:
+    void AddDef(const spv_id_info_t& def) { defs_.push_back(def); }
+
+    void AddUse(uint32_t id) { uses_.insert(id); }
+
+    // Finds id's def, if it exists.  If found, returns <true, def>.  Otherwise,
+    // returns <false, something>.
+    std::pair<bool, spv_id_info_t> FindDef(uint32_t id) const {
+      auto found =
+          std::find_if(defs_.cbegin(), defs_.cend(),
+                       [id](const spv_id_info_t& e) { return e.id == id; });
+      if (found == defs_.cend()) {
+        return std::make_pair(false, spv_id_info_t{});
+      } else {
+        return std::make_pair(true, *found);
+      }
+    }
+
+    // Returns uses of IDs lacking defs.
+    std::unordered_set<uint32_t> FindUsesWithoutDefs() const {
+      auto diff = uses_;
+      for (const auto d : defs_) diff.erase(d.id);
+      return diff;
+    }
+
+   private:
+    std::unordered_set<uint32_t> uses_;
+    std::vector<spv_id_info_t> defs_;
+  };
+
+  UseDefTracker& usedefs() { return usedefs_; }
+  const UseDefTracker& usedefs() const { return usedefs_; }
+
+  std::vector<uint32_t>& entry_points() { return entry_points_; }
+  const std::vector<uint32_t>& entry_points() const { return entry_points_; }
+
+ private:
+  spv_diagnostic* diagnostic_;
+  // Tracks the number of instructions evaluated by the validator
+  int instruction_counter_;
+
+  // IDs which have been forward declared but have not been defined
+  std::unordered_set<uint32_t> unresolved_forward_ids_;
+
+  // Validation options to determine the passes to execute
+  uint32_t validation_flags_;
+
+  std::map<uint32_t, std::string> operand_names_;
+
+  // The section of the code being processed
+  ModuleLayoutSection current_layout_section_;
+
+  Functions module_functions_;
+
+  std::vector<SpvCapability> module_capabilities_;
+
+  // Definitions and uses of all the IDs in the module.
+  UseDefTracker usedefs_;
+
+  // IDs that are entry points, ie, arguments to OpEntryPoint.
+  std::vector<uint32_t> entry_points_;
+};
+
+}  // namespace libspirv
+
 // Functions
 
 /// @brief Validate the ID usage of the instruction stream
 ///
 /// @param[in] pInsts stream of instructions
 /// @param[in] instCount number of instructions
-/// @param[in] pIdUses stream of ID uses
-/// @param[in] idUsesCount number of ID uses
-/// @param[in] pIdDefs stream of ID uses
-/// @param[in] idDefsCount number of ID uses
 /// @param[in] opcodeTable table of specified Opcodes
 /// @param[in] operandTable table of specified operands
+/// @param[in] usedefs use-def info from module parsing
 /// @param[in,out] position current position in the stream
 /// @param[out] pDiag contains diagnostic on failure
 ///
 /// @return result code
-spv_result_t spvValidateInstructionIDs(
-    const spv_instruction_t* pInsts, const uint64_t instCount,
-    const spv_id_info_t* pIdUses, const uint64_t idUsesCount,
-    const spv_id_info_t* pIdDefs, const uint64_t idDefsCount,
-    const spv_opcode_table opcodeTable, const spv_operand_table operandTable,
-    const spv_ext_inst_table extInstTable, spv_position position,
-    spv_diagnostic* pDiag);
+spv_result_t spvValidateInstructionIDs(const spv_instruction_t* pInsts,
+                                       const uint64_t instCount,
+                                       const spv_opcode_table opcodeTable,
+                                       const spv_operand_table operandTable,
+                                       const spv_ext_inst_table extInstTable,
+                                       const libspirv::ValidationState_t& state,
+                                       spv_position position,
+                                       spv_diagnostic* pDiag);
 
 /// @brief Validate the ID's within a SPIR-V binary
 ///
@@ -82,4 +330,7 @@ spv_result_t spvValidateIDs(const spv_instruction_t* pInstructions,
                             const spv_ext_inst_table extInstTable,
                             spv_position position, spv_diagnostic* pDiagnostic);
 
+#define spvCheckReturn(expression)                      \
+  if (spv_result_t error = (expression)) return error;
+
 #endif  // LIBSPIRV_VALIDATE_H_
index 6fa349e..50dc2b6 100644 (file)
@@ -25,7 +25,6 @@
 // MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
 
 #include "validate_passes.h"
-#include "validate_types.h"
 
 namespace libspirv {
 
index 31dd7a8..253ba83 100644 (file)
@@ -24,8 +24,7 @@
 // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 // MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
 
-#include <assert.h>
-
+#include <cassert>
 #include <iostream>
 #include <unordered_map>
 #include <vector>
     action;                         \
   }
 
+using UseDefTracker = libspirv::ValidationState_t::UseDefTracker;
+
 namespace {
+
 class idUsage {
  public:
   idUsage(const spv_opcode_table opcodeTableArg,
           const spv_operand_table operandTableArg,
-          const spv_ext_inst_table extInstTableArg, const spv_id_info_t* pIdUses,
-          const uint64_t idUsesCount, const spv_id_info_t* pIdDefs,
-          const uint64_t idDefsCount, const spv_instruction_t* pInsts,
-          const uint64_t instCountArg, spv_position positionArg,
+          const spv_ext_inst_table extInstTableArg,
+          const spv_instruction_t* pInsts, const uint64_t instCountArg,
+          const UseDefTracker& usedefs,
+          const std::vector<uint32_t>& entry_points, spv_position positionArg,
           spv_diagnostic* pDiagnosticArg)
       : opcodeTable(opcodeTableArg),
         operandTable(operandTableArg),
@@ -57,54 +59,15 @@ class idUsage {
         firstInst(pInsts),
         instCount(instCountArg),
         position(positionArg),
-        pDiagnostic(pDiagnosticArg) {
-    for (uint64_t idUsesIndex = 0; idUsesIndex < idUsesCount; ++idUsesIndex) {
-      idUses[pIdUses[idUsesIndex].id].push_back(pIdUses[idUsesIndex]);
-    }
-    for (uint64_t idDefsIndex = 0; idDefsIndex < idDefsCount; ++idDefsIndex) {
-      idDefs[pIdDefs[idDefsIndex].id] = pIdDefs[idDefsIndex];
-    }
-  }
+        pDiagnostic(pDiagnosticArg),
+        usedefs_(usedefs),
+        entry_points_(entry_points) {}
 
   bool isValid(const spv_instruction_t* inst);
 
   template <SpvOp>
   bool isValid(const spv_instruction_t* inst, const spv_opcode_desc);
 
-  std::unordered_map<uint32_t, spv_id_info_t>::iterator find(
-      const uint32_t& id) {
-    return idDefs.find(id);
-  }
-  std::unordered_map<uint32_t, spv_id_info_t>::const_iterator find(
-      const uint32_t& id) const {
-    return idDefs.find(id);
-  }
-
-  bool found(std::unordered_map<uint32_t, spv_id_info_t>::iterator item) {
-    return idDefs.end() != item;
-  }
-  bool found(std::unordered_map<uint32_t, spv_id_info_t>::const_iterator item) {
-    return idDefs.end() != item;
-  }
-
-  std::unordered_map<uint32_t, std::vector<spv_id_info_t>>::iterator findUses(
-      const uint32_t& id) {
-    return idUses.find(id);
-  }
-  std::unordered_map<uint32_t, std::vector<spv_id_info_t>>::const_iterator
-  findUses(const uint32_t& id) const {
-    return idUses.find(id);
-  }
-
-  bool foundUses(
-      std::unordered_map<uint32_t, std::vector<spv_id_info_t>>::iterator item) {
-    return idUses.end() != item;
-  }
-  bool foundUses(std::unordered_map<
-                 uint32_t, std::vector<spv_id_info_t>>::const_iterator item) {
-    return idUses.end() != item;
-  }
-
  private:
   const spv_opcode_table opcodeTable;
   const spv_operand_table operandTable;
@@ -113,8 +76,8 @@ class idUsage {
   const uint64_t instCount;
   spv_position position;
   spv_diagnostic* pDiagnostic;
-  std::unordered_map<uint32_t, std::vector<spv_id_info_t>> idUses;
-  std::unordered_map<uint32_t, spv_id_info_t> idDefs;
+  UseDefTracker usedefs_;
+  std::vector<uint32_t> entry_points_;
 };
 
 #define DIAG(INDEX)         \
@@ -128,42 +91,26 @@ bool idUsage::isValid<SpvOpUndef>(const spv_instruction_t *inst,
   assert(0 && "Unimplemented!");
   return false;
 }
-#endif
-
-template <>
-bool idUsage::isValid<SpvOpName>(const spv_instruction_t* inst,
-                                 const spv_opcode_desc) {
-  auto targetIndex = 1;
-  auto target = find(inst->words[targetIndex]);
-  spvCheck(!found(target), DIAG(targetIndex) << "OpName Target <id> '"
-                                             << inst->words[targetIndex]
-                                             << "' is not defined.";
-           return false);
-  return true;
-}
+#endif  // 0
 
 template <>
 bool idUsage::isValid<SpvOpMemberName>(const spv_instruction_t* inst,
                                        const spv_opcode_desc) {
   auto typeIndex = 1;
-  auto type = find(inst->words[typeIndex]);
-  spvCheck(!found(type), DIAG(typeIndex) << "OpMemberName Type <id> '"
-                                         << inst->words[typeIndex]
-                                         << "' is not defined.";
-           return false);
-  spvCheck(SpvOpTypeStruct != type->second.opcode,
-           DIAG(typeIndex) << "OpMemberName Type <id> '"
-                           << inst->words[typeIndex]
-                           << "' is not a struct type.";
-           return false);
+  auto type = usedefs_.FindDef(inst->words[typeIndex]);
+  if (!type.first || SpvOpTypeStruct != type.second.opcode) {
+    DIAG(typeIndex) << "OpMemberName Type <id> '" << inst->words[typeIndex]
+                    << "' is not a struct type.";
+    return false;
+  }
   auto memberIndex = 2;
   auto member = inst->words[memberIndex];
-  auto memberCount = (uint32_t)(type->second.inst->words.size() - 2);
+  auto memberCount = (uint32_t)(type.second.words.size() - 2);
   spvCheck(memberCount <= member, DIAG(memberIndex)
                                       << "OpMemberName Member <id> '"
                                       << inst->words[memberIndex]
                                       << "' index is larger than Type <id> '"
-                                      << type->second.id << "'s member count.";
+                                      << type.second.id << "'s member count.";
            return false);
   return true;
 }
@@ -172,27 +119,12 @@ template <>
 bool idUsage::isValid<SpvOpLine>(const spv_instruction_t* inst,
                                  const spv_opcode_desc) {
   auto fileIndex = 1;
-  auto file = find(inst->words[fileIndex]);
-  spvCheck(!found(file), DIAG(fileIndex) << "OpLine Target <id> '"
-                                         << inst->words[fileIndex]
-                                         << "' is not defined.";
-           return false);
-  spvCheck(SpvOpString != file->second.opcode,
-           DIAG(fileIndex) << "OpLine Target <id> '" << inst->words[fileIndex]
-                           << "' is not an OpString.";
-           return false);
-  return true;
-}
-
-template <>
-bool idUsage::isValid<SpvOpDecorate>(const spv_instruction_t* inst,
-                                     const spv_opcode_desc) {
-  auto targetIndex = 1;
-  auto target = find(inst->words[targetIndex]);
-  spvCheck(!found(target), DIAG(targetIndex) << "OpDecorate Target <id> '"
-                                             << inst->words[targetIndex]
-                                             << "' is not defined.";
-           return false);
+  auto file = usedefs_.FindDef(inst->words[fileIndex]);
+  if (!file.first || SpvOpString != file.second.opcode) {
+    DIAG(fileIndex) << "OpLine Target <id> '" << inst->words[fileIndex]
+                    << "' is not an OpString.";
+    return false;
+  }
   return true;
 }
 
@@ -200,20 +132,16 @@ template <>
 bool idUsage::isValid<SpvOpMemberDecorate>(const spv_instruction_t* inst,
                                            const spv_opcode_desc) {
   auto structTypeIndex = 1;
-  auto structType = find(inst->words[structTypeIndex]);
-  spvCheck(!found(structType), DIAG(structTypeIndex)
-                                   << "OpMemberDecorate Structure type <id> '"
-                                   << inst->words[structTypeIndex]
-                                   << "' is not defined.";
-           return false);
-  spvCheck(SpvOpTypeStruct != structType->second.inst->opcode,
-           DIAG(structTypeIndex) << "OpMemberDecorate Structure type <id> '"
-                                 << inst->words[structTypeIndex]
-                                 << "' is not a struct type.";
-           return false);
+  auto structType = usedefs_.FindDef(inst->words[structTypeIndex]);
+  if (!structType.first || SpvOpTypeStruct != structType.second.opcode) {
+    DIAG(structTypeIndex) << "OpMemberDecorate Structure type <id> '"
+                          << inst->words[structTypeIndex]
+                          << "' is not a struct type.";
+    return false;
+  }
   auto memberIndex = 2;
   auto member = inst->words[memberIndex];
-  auto memberCount = (uint32_t)(structType->second.inst->words.size() - 2);
+  auto memberCount = static_cast<uint32_t>(structType.second.words.size() - 2);
   spvCheck(memberCount < member, DIAG(memberIndex)
                                      << "OpMemberDecorate Structure type <id> '"
                                      << inst->words[memberIndex]
@@ -226,26 +154,13 @@ template <>
 bool idUsage::isValid<SpvOpGroupDecorate>(const spv_instruction_t* inst,
                                           const spv_opcode_desc) {
   auto decorationGroupIndex = 1;
-  auto decorationGroup = find(inst->words[decorationGroupIndex]);
-  spvCheck(!found(decorationGroup),
-           DIAG(decorationGroupIndex)
-               << "OpGroupDecorate Decoration group <id> '"
-               << inst->words[decorationGroupIndex] << "' is not defined.";
-           return false);
-  spvCheck(SpvOpDecorationGroup != decorationGroup->second.opcode,
-           DIAG(decorationGroupIndex)
-               << "OpGroupDecorate Decoration group <id> '"
-               << inst->words[decorationGroupIndex]
-               << "' is not a decoration group.";
-           return false);
-  for (size_t targetIndex = 2; targetIndex < inst->words.size();
-       ++targetIndex) {
-    auto target = find(inst->words[targetIndex]);
-    spvCheck(!found(target), DIAG(targetIndex)
-                                 << "OpGroupDecorate Target <id> '"
-                                 << inst->words[targetIndex]
-                                 << "' is not defined.";
-             return false);
+  auto decorationGroup = usedefs_.FindDef(inst->words[decorationGroupIndex]);
+  if (!decorationGroup.first ||
+      SpvOpDecorationGroup != decorationGroup.second.opcode) {
+    DIAG(decorationGroupIndex) << "OpGroupDecorate Decoration group <id> '"
+                               << inst->words[decorationGroupIndex]
+                               << "' is not a decoration group.";
+    return false;
   }
   return true;
 }
@@ -254,45 +169,41 @@ bool idUsage::isValid<SpvOpGroupDecorate>(const spv_instruction_t* inst,
 template <>
 bool idUsage::isValid<SpvOpGroupMemberDecorate>(
     const spv_instruction_t *inst, const spv_opcode_desc opcodeEntry) {}
-#endif
+#endif  // 0
 
 #if 0
 template <>
 bool idUsage::isValid<SpvOpExtInst>(const spv_instruction_t *inst,
                                     const spv_opcode_desc opcodeEntry) {}
-#endif
+#endif  // 0
 
 template <>
 bool idUsage::isValid<SpvOpEntryPoint>(const spv_instruction_t* inst,
                                        const spv_opcode_desc) {
   auto entryPointIndex = 2;
-  auto entryPoint = find(inst->words[entryPointIndex]);
-  spvCheck(!found(entryPoint), DIAG(entryPointIndex)
-                                   << "OpEntryPoint Entry Point <id> '"
-                                   << inst->words[entryPointIndex]
-                                   << "' is not defined.";
-           return false);
-  spvCheck(SpvOpFunction != entryPoint->second.opcode,
-           DIAG(entryPointIndex) << "OpEntryPoint Entry Point <id> '"
-                                 << inst->words[entryPointIndex]
-                                 << "' is not a function.";
-           return false);
+  auto entryPoint = usedefs_.FindDef(inst->words[entryPointIndex]);
+  if (!entryPoint.first || SpvOpFunction != entryPoint.second.opcode) {
+    DIAG(entryPointIndex) << "OpEntryPoint Entry Point <id> '"
+                          << inst->words[entryPointIndex]
+                          << "' is not a function.";
+    return false;
+  }
   // TODO: Check the entry point signature is void main(void), may be subject
   // to change
-  auto entryPointType = find(entryPoint->second.inst->words[4]);
-  spvCheck(!found(entryPointType), assert(0 && "Unreachable!"));
-  spvCheck(3 != entryPointType->second.inst->words.size(),
-           DIAG(entryPointIndex) << "OpEntryPoint Entry Point <id> '"
-                                 << inst->words[entryPointIndex]
-                                 << "'s function parameter count is not zero.";
-           return false);
-  auto returnType = find(entryPoint->second.inst->words[1]);
-  spvCheck(!found(returnType), assert(0 && "Unreachable!"));
-  spvCheck(SpvOpTypeVoid != returnType->second.opcode,
-           DIAG(entryPointIndex) << "OpEntryPoint Entry Point <id> '"
-                                 << inst->words[entryPointIndex]
-                                 << "'s function return type is not void.";
-           return false);
+  auto entryPointType = usedefs_.FindDef(entryPoint.second.words[4]);
+  if (!entryPointType.first || 3 != entryPointType.second.words.size()) {
+    DIAG(entryPointIndex) << "OpEntryPoint Entry Point <id> '"
+                          << inst->words[entryPointIndex]
+                          << "'s function parameter count is not zero.";
+    return false;
+  }
+  auto returnType = usedefs_.FindDef(entryPoint.second.words[1]);
+  if (!returnType.first || SpvOpTypeVoid != returnType.second.opcode) {
+    DIAG(entryPointIndex) << "OpEntryPoint Entry Point <id> '"
+                          << inst->words[entryPointIndex]
+                          << "'s function return type is not void.";
+    return false;
+  }
   return true;
 }
 
@@ -300,26 +211,16 @@ template <>
 bool idUsage::isValid<SpvOpExecutionMode>(const spv_instruction_t* inst,
                                           const spv_opcode_desc) {
   auto entryPointIndex = 1;
-  auto entryPoint = find(inst->words[entryPointIndex]);
-  spvCheck(!found(entryPoint), DIAG(entryPointIndex)
-                                   << "OpExecutionMode Entry Point <id> '"
-                                   << inst->words[entryPointIndex]
-                                   << "' is not defined.";
-           return false);
-  auto entryPointUses = findUses(inst->words[entryPointIndex]);
-  spvCheck(!foundUses(entryPointUses), assert(0 && "Unreachable!"));
-  bool foundEntryPointUse = false;
-  for (auto use : entryPointUses->second) {
-    if (SpvOpEntryPoint == use.opcode) {
-      foundEntryPointUse = true;
-    }
+  auto entryPointID = inst->words[entryPointIndex];
+  auto found =
+      std::find(entry_points_.cbegin(), entry_points_.cend(), entryPointID);
+  if (found == entry_points_.cend()) {
+    DIAG(entryPointIndex) << "OpExecutionMode Entry Point <id> '"
+                          << inst->words[entryPointIndex]
+                          << "' is not the Entry Point "
+                             "operand of an OpEntryPoint.";
+    return false;
   }
-  spvCheck(!foundEntryPointUse, DIAG(entryPointIndex)
-                                    << "OpExecutionMode Entry Point <id> '"
-                                    << inst->words[entryPointIndex]
-                                    << "' is not the Entry Point "
-                                       "operand of an OpEntryPoint.";
-           return false);
   return true;
 }
 
@@ -327,17 +228,14 @@ template <>
 bool idUsage::isValid<SpvOpTypeVector>(const spv_instruction_t* inst,
                                        const spv_opcode_desc) {
   auto componentIndex = 2;
-  auto componentType = find(inst->words[componentIndex]);
-  spvCheck(!found(componentType), DIAG(componentIndex)
-                                      << "OpTypeVector Component Type <id> '"
-                                      << inst->words[componentIndex]
-                                      << "' is not defined.";
-           return false);
-  spvCheck(!spvOpcodeIsScalarType(componentType->second.opcode),
-           DIAG(componentIndex) << "OpTypeVector Component Type <id> '"
-                                << inst->words[componentIndex]
-                                << "' is not a scalar type.";
-           return false);
+  auto componentType = usedefs_.FindDef(inst->words[componentIndex]);
+  if (!componentType.first ||
+      !spvOpcodeIsScalarType(componentType.second.opcode)) {
+    DIAG(componentIndex) << "OpTypeVector Component Type <id> '"
+                         << inst->words[componentIndex]
+                         << "' is not a scalar type.";
+    return false;
+  }
   return true;
 }
 
@@ -345,17 +243,13 @@ template <>
 bool idUsage::isValid<SpvOpTypeMatrix>(const spv_instruction_t* inst,
                                        const spv_opcode_desc) {
   auto columnTypeIndex = 2;
-  auto columnType = find(inst->words[columnTypeIndex]);
-  spvCheck(!found(columnType), DIAG(columnTypeIndex)
-                                   << "OpTypeMatrix Column Type <id> '"
-                                   << inst->words[columnTypeIndex]
-                                   << "' is not defined.";
-           return false);
-  spvCheck(SpvOpTypeVector != columnType->second.opcode,
-           DIAG(columnTypeIndex) << "OpTypeMatrix Column Type <id> '"
-                                 << inst->words[columnTypeIndex]
-                                 << "' is not a vector.";
-           return false);
+  auto columnType = usedefs_.FindDef(inst->words[columnTypeIndex]);
+  if (!columnType.first || SpvOpTypeVector != columnType.second.opcode) {
+    DIAG(columnTypeIndex) << "OpTypeMatrix Column Type <id> '"
+                          << inst->words[columnTypeIndex]
+                          << "' is not a vector.";
+    return false;
+  }
   return true;
 }
 
@@ -370,54 +264,41 @@ template <>
 bool idUsage::isValid<SpvOpTypeArray>(const spv_instruction_t* inst,
                                       const spv_opcode_desc) {
   auto elementTypeIndex = 2;
-  auto elementType = find(inst->words[elementTypeIndex]);
-  spvCheck(!found(elementType), DIAG(elementTypeIndex)
-                                    << "OpTypeArray Element Type <id> '"
-                                    << inst->words[elementTypeIndex]
-                                    << "' is not defined.";
-           return false);
-  spvCheck(!spvOpcodeGeneratesType(elementType->second.opcode),
-           DIAG(elementTypeIndex) << "OpTypeArray Element Type <id> '"
-                                  << inst->words[elementTypeIndex]
-                                  << "' is not a type.";
-           return false);
+  auto elementType = usedefs_.FindDef(inst->words[elementTypeIndex]);
+  if (!elementType.first ||
+      !spvOpcodeGeneratesType(elementType.second.opcode)) {
+    DIAG(elementTypeIndex) << "OpTypeArray Element Type <id> '"
+                           << inst->words[elementTypeIndex]
+                           << "' is not a type.";
+    return false;
+  }
   auto lengthIndex = 3;
-  auto length = find(inst->words[lengthIndex]);
-  spvCheck(!found(length), DIAG(lengthIndex) << "OpTypeArray Length <id> '"
-                                             << inst->words[lengthIndex]
-                                             << "' is not defined.";
-           return false);
-  spvCheck(SpvOpConstant != length->second.opcode &&
-               SpvOpSpecConstant != length->second.opcode,
-           DIAG(lengthIndex) << "OpTypeArray Length <id> '"
-                             << inst->words[lengthIndex]
-                             << "' is not a scalar constant type.";
-           return false);
+  auto length = usedefs_.FindDef(inst->words[lengthIndex]);
+  if (!length.first || (SpvOpConstant != length.second.opcode &&
+                        SpvOpSpecConstant != length.second.opcode)) {
+    DIAG(lengthIndex) << "OpTypeArray Length <id> '" << inst->words[lengthIndex]
+                      << "' is not a scalar constant type.";
+    return false;
+  }
 
   // NOTE: Check the initialiser value of the constant
-  auto constInst = length->second.inst;
+  auto constInst = length.second.words;
   auto constResultTypeIndex = 1;
-  auto constResultType = find(constInst->words[constResultTypeIndex]);
-  spvCheck(!found(constResultType), DIAG(lengthIndex)
-                                        << "OpTypeArray Length <id> '"
-                                        << inst->words[constResultTypeIndex]
-                                        << "' result type is not defined.";
-           return false);
-  spvCheck(SpvOpTypeInt != constResultType->second.opcode,
-           DIAG(lengthIndex) << "OpTypeArray Length <id> '"
-                             << inst->words[lengthIndex]
-                             << "' is not a constant integer type.";
-           return false);
-  if (4 == constInst->words.size()) {
-    spvCheck(1 > constInst->words[3], DIAG(lengthIndex)
-                                          << "OpTypeArray Length <id> '"
-                                          << inst->words[lengthIndex]
-                                          << "' value must be at least 1.";
+  auto constResultType = usedefs_.FindDef(constInst[constResultTypeIndex]);
+  if (!constResultType.first || SpvOpTypeInt != constResultType.second.opcode) {
+    DIAG(lengthIndex) << "OpTypeArray Length <id> '" << inst->words[lengthIndex]
+                      << "' is not a constant integer type.";
+    return false;
+  }
+  if (4 == constInst.size()) {
+    spvCheck(1 > constInst[3], DIAG(lengthIndex)
+                                   << "OpTypeArray Length <id> '"
+                                   << inst->words[lengthIndex]
+                                   << "' value must be at least 1.";
              return false);
-  } else if (5 == constInst->words.size()) {
-    uint64_t value =
-        constInst->words[3] | ((uint64_t)constInst->words[4]) << 32;
-    bool signedness = constResultType->second.inst->words[3] != 0;
+  } else if (5 == constInst.size()) {
+    uint64_t value = constInst[3] | ((uint64_t)constInst[4]) << 32;
+    bool signedness = constResultType.second.words[3] != 0;
     if (signedness) {
       spvCheck(1 > (int64_t)value, DIAG(lengthIndex)
                                        << "OpTypeArray Length <id> '"
@@ -438,17 +319,14 @@ template <>
 bool idUsage::isValid<SpvOpTypeRuntimeArray>(const spv_instruction_t* inst,
                                              const spv_opcode_desc) {
   auto elementTypeIndex = 2;
-  auto elementType = find(inst->words[elementTypeIndex]);
-  spvCheck(!found(elementType), DIAG(elementTypeIndex)
-                                    << "OpTypeRuntimeArray Element Type <id> '"
-                                    << inst->words[elementTypeIndex]
-                                    << "' is not defined.";
-           return false);
-  spvCheck(!spvOpcodeGeneratesType(elementType->second.opcode),
-           DIAG(elementTypeIndex) << "OpTypeRuntimeArray Element Type <id> '"
-                                  << inst->words[elementTypeIndex]
-                                  << "' is not a type.";
-           return false);
+  auto elementType = usedefs_.FindDef(inst->words[elementTypeIndex]);
+  if (!elementType.first ||
+      !spvOpcodeGeneratesType(elementType.second.opcode)) {
+    DIAG(elementTypeIndex) << "OpTypeRuntimeArray Element Type <id> '"
+                           << inst->words[elementTypeIndex]
+                           << "' is not a type.";
+    return false;
+  }
   return true;
 }
 
@@ -457,17 +335,14 @@ bool idUsage::isValid<SpvOpTypeStruct>(const spv_instruction_t* inst,
                                        const spv_opcode_desc) {
   for (size_t memberTypeIndex = 2; memberTypeIndex < inst->words.size();
        ++memberTypeIndex) {
-    auto memberType = find(inst->words[memberTypeIndex]);
-    spvCheck(!found(memberType), DIAG(memberTypeIndex)
-                                     << "OpTypeStruct Member Type <id> '"
-                                     << inst->words[memberTypeIndex]
-                                     << "' is not defined.";
-             return false);
-    spvCheck(!spvOpcodeGeneratesType(memberType->second.opcode),
-             DIAG(memberTypeIndex) << "OpTypeStruct Member Type <id> '"
-                                   << inst->words[memberTypeIndex]
-                                   << "' is not a type.";
-             return false);
+    auto memberType = usedefs_.FindDef(inst->words[memberTypeIndex]);
+    if (!memberType.first ||
+        !spvOpcodeGeneratesType(memberType.second.opcode)) {
+      DIAG(memberTypeIndex) << "OpTypeStruct Member Type <id> '"
+                            << inst->words[memberTypeIndex]
+                            << "' is not a type.";
+      return false;
+    }
   }
   return true;
 }
@@ -476,15 +351,12 @@ template <>
 bool idUsage::isValid<SpvOpTypePointer>(const spv_instruction_t* inst,
                                         const spv_opcode_desc) {
   auto typeIndex = 3;
-  auto type = find(inst->words[typeIndex]);
-  spvCheck(!found(type), DIAG(typeIndex) << "OpTypePointer Type <id> '"
-                                         << inst->words[typeIndex]
-                                         << "' is not defined.";
-           return false);
-  spvCheck(!spvOpcodeGeneratesType(type->second.opcode),
-           DIAG(typeIndex) << "OpTypePointer Type <id> '"
-                           << inst->words[typeIndex] << "' is not a type.";
-           return false);
+  auto type = usedefs_.FindDef(inst->words[typeIndex]);
+  if (!type.first || !spvOpcodeGeneratesType(type.second.opcode)) {
+    DIAG(typeIndex) << "OpTypePointer Type <id> '" << inst->words[typeIndex]
+                    << "' is not a type.";
+    return false;
+  }
   return true;
 }
 
@@ -492,30 +364,20 @@ template <>
 bool idUsage::isValid<SpvOpTypeFunction>(const spv_instruction_t* inst,
                                          const spv_opcode_desc) {
   auto returnTypeIndex = 2;
-  auto returnType = find(inst->words[returnTypeIndex]);
-  spvCheck(!found(returnType), DIAG(returnTypeIndex)
-                                   << "OpTypeFunction Return Type <id> '"
-                                   << inst->words[returnTypeIndex]
-                                   << "' is not defined";
-           return false);
-  spvCheck(!spvOpcodeGeneratesType(returnType->second.opcode),
-           DIAG(returnTypeIndex) << "OpTypeFunction Return Type <id> '"
-                                 << inst->words[returnTypeIndex]
-                                 << "' is not a type.";
-           return false);
+  auto returnType = usedefs_.FindDef(inst->words[returnTypeIndex]);
+  if (!returnType.first || !spvOpcodeGeneratesType(returnType.second.opcode)) {
+    DIAG(returnTypeIndex) << "OpTypeFunction Return Type <id> '"
+                          << inst->words[returnTypeIndex] << "' is not a type.";
+    return false;
+  }
   for (size_t paramTypeIndex = 3; paramTypeIndex < inst->words.size();
        ++paramTypeIndex) {
-    auto paramType = find(inst->words[paramTypeIndex]);
-    spvCheck(!found(paramType), DIAG(paramTypeIndex)
-                                    << "OpTypeFunction Parameter Type <id> '"
-                                    << inst->words[paramTypeIndex]
-                                    << "' is not defined.";
-             return false);
-    spvCheck(!spvOpcodeGeneratesType(paramType->second.opcode),
-             DIAG(paramTypeIndex) << "OpTypeFunction Parameter Type <id> '"
-                                  << inst->words[paramTypeIndex]
-                                  << "' is not a type.";
-             return false);
+    auto paramType = usedefs_.FindDef(inst->words[paramTypeIndex]);
+    if (!paramType.first || !spvOpcodeGeneratesType(paramType.second.opcode)) {
+      DIAG(paramTypeIndex) << "OpTypeFunction Parameter Type <id> '"
+                           << inst->words[paramTypeIndex] << "' is not a type.";
+      return false;
+    }
   }
   return true;
 }
@@ -531,17 +393,13 @@ template <>
 bool idUsage::isValid<SpvOpConstantTrue>(const spv_instruction_t* inst,
                                          const spv_opcode_desc) {
   auto resultTypeIndex = 1;
-  auto resultType = find(inst->words[resultTypeIndex]);
-  spvCheck(!found(resultType), DIAG(resultTypeIndex)
-                                   << "OpConstantTrue Result Type <id> '"
-                                   << inst->words[resultTypeIndex]
-                                   << "' is not defined.";
-           return false);
-  spvCheck(SpvOpTypeBool != resultType->second.opcode,
-           DIAG(resultTypeIndex) << "OpConstantTrue Result Type <id> '"
-                                 << inst->words[resultTypeIndex]
-                                 << "' is not a boolean type.";
-           return false);
+  auto resultType = usedefs_.FindDef(inst->words[resultTypeIndex]);
+  if (!resultType.first || SpvOpTypeBool != resultType.second.opcode) {
+    DIAG(resultTypeIndex) << "OpConstantTrue Result Type <id> '"
+                          << inst->words[resultTypeIndex]
+                          << "' is not a boolean type.";
+    return false;
+  }
   return true;
 }
 
@@ -549,17 +407,13 @@ template <>
 bool idUsage::isValid<SpvOpConstantFalse>(const spv_instruction_t* inst,
                                           const spv_opcode_desc) {
   auto resultTypeIndex = 1;
-  auto resultType = find(inst->words[resultTypeIndex]);
-  spvCheck(!found(resultType), DIAG(resultTypeIndex)
-                                   << "OpConstantFalse Result Type <id> '"
-                                   << inst->words[resultTypeIndex]
-                                   << "' is not defined.";
-           return false);
-  spvCheck(SpvOpTypeBool != resultType->second.opcode,
-           DIAG(resultTypeIndex) << "OpConstantFalse Result Type <id> '"
-                                 << inst->words[resultTypeIndex]
-                                 << "' is not a boolean type.";
-           return false);
+  auto resultType = usedefs_.FindDef(inst->words[resultTypeIndex]);
+  if (!resultType.first || SpvOpTypeBool != resultType.second.opcode) {
+    DIAG(resultTypeIndex) << "OpConstantFalse Result Type <id> '"
+                          << inst->words[resultTypeIndex]
+                          << "' is not a boolean type.";
+    return false;
+  }
   return true;
 }
 
@@ -567,18 +421,13 @@ template <>
 bool idUsage::isValid<SpvOpConstant>(const spv_instruction_t* inst,
                                      const spv_opcode_desc) {
   auto resultTypeIndex = 1;
-  auto resultType = find(inst->words[resultTypeIndex]);
-  spvCheck(!found(resultType), DIAG(resultTypeIndex)
-                                   << "OpConstant Result Type <id> '"
-                                   << inst->words[resultTypeIndex]
-                                   << "' is not defined.";
-           return false);
-  spvCheck(!spvOpcodeIsScalarType(resultType->second.opcode),
-           DIAG(resultTypeIndex)
-               << "OpConstant Result Type <id> '"
-               << inst->words[resultTypeIndex]
-               << "' is not a scalar integer or floating point type.";
-           return false);
+  auto resultType = usedefs_.FindDef(inst->words[resultTypeIndex]);
+  if (!resultType.first || !spvOpcodeIsScalarType(resultType.second.opcode)) {
+    DIAG(resultTypeIndex)
+        << "OpConstant Result Type <id> '" << inst->words[resultTypeIndex]
+        << "' is not a scalar integer or floating point type.";
+    return false;
+  }
   return true;
 }
 
@@ -586,185 +435,172 @@ template <>
 bool idUsage::isValid<SpvOpConstantComposite>(const spv_instruction_t* inst,
                                               const spv_opcode_desc) {
   auto resultTypeIndex = 1;
-  auto resultType = find(inst->words[resultTypeIndex]);
-  spvCheck(!found(resultType), DIAG(resultTypeIndex)
-                                   << "OpConstantComposite Result Type <id> '"
-                                   << inst->words[resultTypeIndex]
-                                   << "' is not defined.";
-           return false);
-  spvCheck(!spvOpcodeIsComposite(resultType->second.opcode),
-           DIAG(resultTypeIndex) << "OpConstantComposite Result Type <id> '"
-                                 << inst->words[resultTypeIndex]
-                                 << "' is not a composite type.";
-           return false);
+  auto resultType = usedefs_.FindDef(inst->words[resultTypeIndex]);
+  if (!resultType.first || !spvOpcodeIsComposite(resultType.second.opcode)) {
+    DIAG(resultTypeIndex) << "OpConstantComposite Result Type <id> '"
+                          << inst->words[resultTypeIndex]
+                          << "' is not a composite type.";
+    return false;
+  }
 
   auto constituentCount = inst->words.size() - 3;
-  switch (resultType->second.opcode) {
+  switch (resultType.second.opcode) {
     case SpvOpTypeVector: {
-      auto componentCount = resultType->second.inst->words[3];
+      auto componentCount = resultType.second.words[3];
       spvCheck(
           componentCount != constituentCount,
           // TODO: Output ID's on diagnostic
           DIAG(inst->words.size() - 1)
               << "OpConstantComposite Constituent <id> count does not match "
                  "Result Type <id> '"
-              << resultType->second.id << "'s vector component count.";
+              << resultType.second.id << "'s vector component count.";
           return false);
-      auto componentType = find(resultType->second.inst->words[2]);
-      spvCheck(!found(componentType), assert(0 && "Unreachable!"));
+      auto componentType = usedefs_.FindDef(resultType.second.words[2]);
+      assert(componentType.first);
       for (size_t constituentIndex = 3; constituentIndex < inst->words.size();
            constituentIndex++) {
-        auto constituent = find(inst->words[constituentIndex]);
-        spvCheck(!found(constituent), assert(0 && "Unreachable!"));
-        spvCheck(!spvOpcodeIsConstant(constituent->second.opcode),
-                 DIAG(constituentIndex)
-                     << "OpConstantComposite Constituent <id> '"
-                     << inst->words[constituentIndex] << "' is not a constant.";
-                 return false);
-        auto constituentResultType = find(constituent->second.inst->words[1]);
-        spvCheck(!found(constituentResultType), assert(0 && "Unreachable!"));
-        spvCheck(componentType->second.opcode !=
-                     constituentResultType->second.opcode,
-                 DIAG(constituentIndex)
-                     << "OpConstantComposite Constituent <id> '"
-                     << inst->words[constituentIndex]
-                     << "'s type does not match Result Type <id> '"
-                     << resultType->second.id << "'s vector element type.";
-                 return false);
+        auto constituent = usedefs_.FindDef(inst->words[constituentIndex]);
+        if (!constituent.first ||
+            !spvOpcodeIsConstant(constituent.second.opcode)) {
+          DIAG(constituentIndex) << "OpConstantComposite Constituent <id> '"
+                                 << inst->words[constituentIndex]
+                                 << "' is not a constant.";
+          return false;
+        }
+        auto constituentResultType =
+            usedefs_.FindDef(constituent.second.words[1]);
+        if (!constituentResultType.first ||
+            componentType.second.opcode !=
+                constituentResultType.second.opcode) {
+          DIAG(constituentIndex) << "OpConstantComposite Constituent <id> '"
+                                 << inst->words[constituentIndex]
+                                 << "'s type does not match Result Type <id> '"
+                                 << resultType.second.id
+                                 << "'s vector element type.";
+          return false;
+        }
       }
     } break;
     case SpvOpTypeMatrix: {
-      auto columnCount = resultType->second.inst->words[3];
+      auto columnCount = resultType.second.words[3];
       spvCheck(
           columnCount != constituentCount,
           // TODO: Output ID's on diagnostic
           DIAG(inst->words.size() - 1)
               << "OpConstantComposite Constituent <id> count does not match "
                  "Result Type <id> '"
-              << resultType->second.id << "'s matrix column count.";
+              << resultType.second.id << "'s matrix column count.";
           return false);
 
-      auto columnType = find(resultType->second.inst->words[2]);
-      spvCheck(!found(columnType), assert(0 && "Unreachable!"));
-      auto componentCount = columnType->second.inst->words[3];
-      auto componentType = find(columnType->second.inst->words[2]);
-      spvCheck(!found(componentType), assert(0 && "Unreachable!"));
+      auto columnType = usedefs_.FindDef(resultType.second.words[2]);
+      assert(columnType.first);
+      auto componentCount = columnType.second.words[3];
+      auto componentType = usedefs_.FindDef(columnType.second.words[2]);
+      assert(componentType.first);
 
       for (size_t constituentIndex = 3; constituentIndex < inst->words.size();
            constituentIndex++) {
-        auto constituent = find(inst->words[constituentIndex]);
-        spvCheck(!found(constituent),
-                 DIAG(constituentIndex)
-                     << "OpConstantComposite Constituent <id> '"
-                     << inst->words[constituentIndex] << "' is not defined.";
-                 return false);
-        spvCheck(SpvOpConstantComposite != constituent->second.opcode,
-                 DIAG(constituentIndex)
-                     << "OpConstantComposite Constituent <id> '"
-                     << inst->words[constituentIndex]
-                     << "' is not a constant composite.";
-                 return false);
-        auto vector = find(constituent->second.inst->words[1]);
-        spvCheck(!found(vector), assert(0 && "Unreachable!"));
-        spvCheck(columnType->second.opcode != vector->second.opcode,
+        auto constituent = usedefs_.FindDef(inst->words[constituentIndex]);
+        if (!constituent.first ||
+            SpvOpConstantComposite != constituent.second.opcode) {
+          DIAG(constituentIndex) << "OpConstantComposite Constituent <id> '"
+                                 << inst->words[constituentIndex]
+                                 << "' is not a constant composite.";
+          return false;
+        }
+        auto vector = usedefs_.FindDef(constituent.second.words[1]);
+        assert(vector.first);
+        spvCheck(columnType.second.opcode != vector.second.opcode,
                  DIAG(constituentIndex)
                      << "OpConstantComposite Constituent <id> '"
                      << inst->words[constituentIndex]
                      << "' type does not match Result Type <id> '"
-                     << resultType->second.id << "'s matrix column type.";
+                     << resultType.second.id << "'s matrix column type.";
                  return false);
-        auto vectorComponentType = find(vector->second.inst->words[2]);
-        spvCheck(!found(vectorComponentType), assert(0 && "Unreachable!"));
-        spvCheck(!spvOpcodeAreTypesEqual(componentType->second.inst,
-                                         vectorComponentType->second.inst),
+        auto vectorComponentType = usedefs_.FindDef(vector.second.words[2]);
+        assert(vectorComponentType.first);
+        spvCheck(componentType.second.id != vectorComponentType.second.id,
                  DIAG(constituentIndex)
                      << "OpConstantComposite Constituent <id> '"
                      << inst->words[constituentIndex]
                      << "' component type does not match Result Type <id> '"
-                     << resultType->second.id
+                     << resultType.second.id
                      << "'s matrix column component type.";
                  return false);
         spvCheck(
-            componentCount != vector->second.inst->words[3],
+            componentCount != vector.second.words[3],
             DIAG(constituentIndex)
                 << "OpConstantComposite Constituent <id> '"
                 << inst->words[constituentIndex]
                 << "' vector component count does not match Result Type <id> '"
-                << resultType->second.id << "'s vector component count.";
+                << resultType.second.id << "'s vector component count.";
             return false);
       }
     } break;
     case SpvOpTypeArray: {
-      auto elementType = find(resultType->second.inst->words[2]);
-      spvCheck(!found(elementType), assert(0 && "Unreachable!"));
-      auto length = find(resultType->second.inst->words[3]);
-      spvCheck(!found(length), assert(0 && "Unreachable!"));
-      spvCheck(length->second.inst->words[3] != constituentCount,
+      auto elementType = usedefs_.FindDef(resultType.second.words[2]);
+      assert(elementType.first);
+      auto length = usedefs_.FindDef(resultType.second.words[3]);
+      assert(length.first);
+      spvCheck(length.second.words[3] != constituentCount,
                DIAG(inst->words.size() - 1)
                    << "OpConstantComposite Constituent count does not match "
                       "Result Type <id> '"
-                   << resultType->second.id << "'s array length.";
+                   << resultType.second.id << "'s array length.";
                return false);
       for (size_t constituentIndex = 3; constituentIndex < inst->words.size();
            constituentIndex++) {
-        auto constituent = find(inst->words[constituentIndex]);
-        spvCheck(!found(constituent),
-                 DIAG(constituentIndex)
-                     << "OpConstantComposite Constituent <id> '"
-                     << inst->words[constituentIndex] << "' is not defined.";
-                 return false);
-        spvCheck(!spvOpcodeIsConstant(constituent->second.opcode),
-                 DIAG(constituentIndex)
-                     << "OpConstantComposite Constituent <id> '"
-                     << inst->words[constituentIndex] << "' is not a constant.";
-                 return false);
-        auto constituentType = find(constituent->second.inst->words[1]);
-        spvCheck(!found(constituentType), assert(0 && "Unreachable!"));
-        spvCheck(!spvOpcodeAreTypesEqual(elementType->second.inst,
-                                         constituentType->second.inst),
+        auto constituent = usedefs_.FindDef(inst->words[constituentIndex]);
+        if (!constituent.first ||
+            !spvOpcodeIsConstant(constituent.second.opcode)) {
+          DIAG(constituentIndex) << "OpConstantComposite Constituent <id> '"
+                                 << inst->words[constituentIndex]
+                                 << "' is not a constant.";
+          return false;
+        }
+        auto constituentType = usedefs_.FindDef(constituent.second.words[1]);
+        assert(constituentType.first);
+        spvCheck(elementType.second.id != constituentType.second.id,
                  DIAG(constituentIndex)
                      << "OpConstantComposite Constituent <id> '"
                      << inst->words[constituentIndex]
                      << "'s type does not match Result Type <id> '"
-                     << resultType->second.id << "'s array element type.";
+                     << resultType.second.id << "'s array element type.";
                  return false);
       }
     } break;
     case SpvOpTypeStruct: {
-      auto memberCount = resultType->second.inst->words.size() - 2;
+      auto memberCount = resultType.second.words.size() - 2;
       spvCheck(memberCount != constituentCount,
                DIAG(resultTypeIndex)
                    << "OpConstantComposite Constituent <id> '"
                    << inst->words[resultTypeIndex]
                    << "' count does not match Result Type <id> '"
-                   << resultType->second.id << "'s struct member count.";
+                   << resultType.second.id << "'s struct member count.";
                return false);
       for (uint32_t constituentIndex = 3, memberIndex = 2;
            constituentIndex < inst->words.size();
            constituentIndex++, memberIndex++) {
-        auto constituent = find(inst->words[constituentIndex]);
-        spvCheck(!found(constituent),
-                 DIAG(constituentIndex)
-                     << "OpConstantComposite Constituent <id> '"
-                     << inst->words[constituentIndex] << "' is not define.";
-                 return false);
-        spvCheck(!spvOpcodeIsConstant(constituent->second.opcode),
-                 DIAG(constituentIndex)
-                     << "OpConstantComposite Constituent <id> '"
-                     << inst->words[constituentIndex] << "' is not a constant.";
-                 return false);
-        auto constituentType = find(constituent->second.inst->words[1]);
-        spvCheck(!found(constituentType), assert(0 && "Unreachable!"));
-
-        auto memberType = find(resultType->second.inst->words[memberIndex]);
-        spvCheck(!found(memberType), assert(0 && "Unreachable!"));
-        spvCheck(!spvOpcodeAreTypesEqual(memberType->second.inst,
-                                         constituentType->second.inst),
+        auto constituent = usedefs_.FindDef(inst->words[constituentIndex]);
+        if (!constituent.first ||
+            !spvOpcodeIsConstant(constituent.second.opcode)) {
+          DIAG(constituentIndex) << "OpConstantComposite Constituent <id> '"
+                                 << inst->words[constituentIndex]
+                                 << "' is not a constant.";
+          return false;
+        }
+        auto constituentType = usedefs_.FindDef(constituent.second.words[1]);
+        assert(constituentType.first);
+
+        auto memberType =
+            usedefs_.FindDef(resultType.second.words[memberIndex]);
+        assert(memberType.first);
+        spvCheck(memberType.second.id != constituentType.second.id,
                  DIAG(constituentIndex)
                      << "OpConstantComposite Constituent <id> '"
                      << inst->words[constituentIndex]
                      << "' type does not match the Result Type <id> '"
-                     << resultType->second.id << "'s member type.";
+                     << resultType.second.id << "'s member type.";
                  return false);
       }
     } break;
@@ -777,17 +613,13 @@ template <>
 bool idUsage::isValid<SpvOpConstantSampler>(const spv_instruction_t* inst,
                                             const spv_opcode_desc) {
   auto resultTypeIndex = 1;
-  auto resultType = find(inst->words[resultTypeIndex]);
-  spvCheck(!found(resultType), DIAG(resultTypeIndex)
-                                   << "OpConstantSampler Result Type <id> '"
-                                   << inst->words[resultTypeIndex]
-                                   << "' is not defined.";
-           return false);
-  spvCheck(SpvOpTypeSampler != resultType->second.opcode,
-           DIAG(resultTypeIndex) << "OpConstantSampler Result Type <id> '"
-                                 << inst->words[resultTypeIndex]
-                                 << "' is not a sampler type.";
-           return false);
+  auto resultType = usedefs_.FindDef(inst->words[resultTypeIndex]);
+  if (!resultType.first || SpvOpTypeSampler != resultType.second.opcode) {
+    DIAG(resultTypeIndex) << "OpConstantSampler Result Type <id> '"
+                          << inst->words[resultTypeIndex]
+                          << "' is not a sampler type.";
+    return false;
+  }
   return true;
 }
 
@@ -795,46 +627,41 @@ template <>
 bool idUsage::isValid<SpvOpConstantNull>(const spv_instruction_t* inst,
                                          const spv_opcode_desc) {
   auto resultTypeIndex = 1;
-  auto resultType = find(inst->words[resultTypeIndex]);
-  spvCheck(!found(resultType), DIAG(resultTypeIndex)
-                                   << "OpConstantNull Result Type <id> '"
-                                   << inst->words[resultTypeIndex]
-                                   << "' is not defined.";
-           return false);
-  switch (resultType->second.inst->opcode) {
+  auto resultType = usedefs_.FindDef(inst->words[resultTypeIndex]);
+  if (!resultType.first) return false;
+  switch (resultType.second.opcode) {
     default: {
-      spvCheck(!spvOpcodeIsBasicTypeNullable(resultType->second.inst->opcode),
+      spvCheck(!spvOpcodeIsBasicTypeNullable(resultType.second.opcode),
                DIAG(resultTypeIndex) << "OpConstantNull Result Type <id> '"
                                      << inst->words[resultTypeIndex]
-                                     << "' can not be null.";
+                                     << "' cannot be null.";
                return false);
     } break;
     case SpvOpTypeVector: {
-      auto type = find(resultType->second.inst->words[2]);
-      spvCheck(!found(type), assert(0 && "Unreachable!"));
-      spvCheck(!spvOpcodeIsBasicTypeNullable(type->second.inst->opcode),
+      auto type = usedefs_.FindDef(resultType.second.words[2]);
+      assert(type.first);
+      spvCheck(!spvOpcodeIsBasicTypeNullable(type.second.opcode),
                DIAG(resultTypeIndex)
                    << "OpConstantNull Result Type <id> '"
                    << inst->words[resultTypeIndex]
-                   << "'s vector component type can not be null.";
+                   << "'s vector component type cannot be null.";
                return false);
     } break;
     case SpvOpTypeArray: {
-      auto type = find(resultType->second.inst->words[2]);
-      spvCheck(!found(type), assert(0 && "Unreachable!"));
-      spvCheck(!spvOpcodeIsBasicTypeNullable(type->second.inst->opcode),
-               DIAG(resultTypeIndex)
-                   << "OpConstantNull Result Type <id> '"
-                   << inst->words[resultTypeIndex]
-                   << "'s array element type can not be null.";
+      auto type = usedefs_.FindDef(resultType.second.words[2]);
+      assert(type.first);
+      spvCheck(!spvOpcodeIsBasicTypeNullable(type.second.opcode),
+               DIAG(resultTypeIndex) << "OpConstantNull Result Type <id> '"
+                                     << inst->words[resultTypeIndex]
+                                     << "'s array element type cannot be null.";
                return false);
     } break;
     case SpvOpTypeMatrix: {
-      auto columnType = find(resultType->second.inst->words[2]);
-      spvCheck(!found(columnType), assert(0 && "Unreachable!"));
-      auto type = find(columnType->second.inst->words[2]);
-      spvCheck(!found(type), assert(0 && "Unreachable!"));
-      spvCheck(!spvOpcodeIsBasicTypeNullable(type->second.inst->opcode),
+      auto columnType = usedefs_.FindDef(resultType.second.words[2]);
+      assert(columnType.first);
+      auto type = usedefs_.FindDef(columnType.second.words[2]);
+      assert(type.first);
+      spvCheck(!spvOpcodeIsBasicTypeNullable(type.second.opcode),
                DIAG(resultTypeIndex)
                    << "OpConstantNull Result Type <id> '"
                    << inst->words[resultTypeIndex]
@@ -843,15 +670,14 @@ bool idUsage::isValid<SpvOpConstantNull>(const spv_instruction_t* inst,
     } break;
     case SpvOpTypeStruct: {
       for (size_t elementIndex = 2;
-           elementIndex < resultType->second.inst->words.size();
-           ++elementIndex) {
-        auto element = find(resultType->second.inst->words[elementIndex]);
-        spvCheck(!found(element), assert(0 && "Unreachable!"));
-        spvCheck(!spvOpcodeIsBasicTypeNullable(element->second.inst->opcode),
+           elementIndex < resultType.second.words.size(); ++elementIndex) {
+        auto element = usedefs_.FindDef(resultType.second.words[elementIndex]);
+        assert(element.first);
+        spvCheck(!spvOpcodeIsBasicTypeNullable(element.second.opcode),
                  DIAG(resultTypeIndex)
                      << "OpConstantNull Result Type <id> '"
                      << inst->words[resultTypeIndex]
-                     << "'s struct element type can not be null.";
+                     << "'s struct element type cannot be null.";
                  return false);
       }
     } break;
@@ -863,17 +689,13 @@ template <>
 bool idUsage::isValid<SpvOpSpecConstantTrue>(const spv_instruction_t* inst,
                                              const spv_opcode_desc) {
   auto resultTypeIndex = 1;
-  auto resultType = find(inst->words[resultTypeIndex]);
-  spvCheck(!found(resultType), DIAG(resultTypeIndex)
-                                   << "OpSpecConstantTrue Result Type <id> '"
-                                   << inst->words[resultTypeIndex]
-                                   << "' is not defined.";
-           return false);
-  spvCheck(SpvOpTypeBool != resultType->second.opcode,
-           DIAG(resultTypeIndex) << "OpSpecConstantTrue Result Type <id> '"
-                                 << inst->words[resultTypeIndex]
-                                 << "' is not a boolean type.";
-           return false);
+  auto resultType = usedefs_.FindDef(inst->words[resultTypeIndex]);
+  if (!resultType.first || SpvOpTypeBool != resultType.second.opcode) {
+    DIAG(resultTypeIndex) << "OpSpecConstantTrue Result Type <id> '"
+                          << inst->words[resultTypeIndex]
+                          << "' is not a boolean type.";
+    return false;
+  }
   return true;
 }
 
@@ -881,17 +703,13 @@ template <>
 bool idUsage::isValid<SpvOpSpecConstantFalse>(const spv_instruction_t* inst,
                                               const spv_opcode_desc) {
   auto resultTypeIndex = 1;
-  auto resultType = find(inst->words[resultTypeIndex]);
-  spvCheck(!found(resultType), DIAG(resultTypeIndex)
-                                   << "OpSpecConstantFalse Result Type <id> '"
-                                   << inst->words[resultTypeIndex]
-                                   << "' is not defined.";
-           return false);
-  spvCheck(SpvOpTypeBool != resultType->second.opcode,
-           DIAG(resultTypeIndex) << "OpSpecConstantFalse Result Type <id> '"
-                                 << inst->words[resultTypeIndex]
-                                 << "' is not a boolean type.";
-           return false);
+  auto resultType = usedefs_.FindDef(inst->words[resultTypeIndex]);
+  if (!resultType.first || SpvOpTypeBool != resultType.second.opcode) {
+    DIAG(resultTypeIndex) << "OpSpecConstantFalse Result Type <id> '"
+                          << inst->words[resultTypeIndex]
+                          << "' is not a boolean type.";
+    return false;
+  }
   return true;
 }
 
@@ -899,17 +717,13 @@ template <>
 bool idUsage::isValid<SpvOpSpecConstant>(const spv_instruction_t* inst,
                                          const spv_opcode_desc) {
   auto resultTypeIndex = 1;
-  auto resultType = find(inst->words[resultTypeIndex]);
-  spvCheck(!found(resultType), DIAG(resultTypeIndex)
-                                   << "OpSpecConstant Result Type <id> '"
-                                   << inst->words[resultTypeIndex]
-                                   << "' is not defined.";
-           return false);
-  spvCheck(!spvOpcodeIsScalarType(resultType->second.opcode),
-           DIAG(resultTypeIndex) << "OpSpecConstant Result Type <id> '"
-                                 << inst->words[resultTypeIndex]
-                                 << "' is not a scalar type.";
-           return false);
+  auto resultType = usedefs_.FindDef(inst->words[resultTypeIndex]);
+  if (!resultType.first || !spvOpcodeIsScalarType(resultType.second.opcode)) {
+    DIAG(resultTypeIndex) << "OpSpecConstant Result Type <id> '"
+                          << inst->words[resultTypeIndex]
+                          << "' is not a scalar type.";
+    return false;
+  }
   return true;
 }
 
@@ -928,30 +742,22 @@ template <>
 bool idUsage::isValid<SpvOpVariable>(const spv_instruction_t* inst,
                                      const spv_opcode_desc opcodeEntry) {
   auto resultTypeIndex = 1;
-  auto resultType = find(inst->words[resultTypeIndex]);
-  spvCheck(!found(resultType), DIAG(resultTypeIndex)
-                                   << "OpVariable Result Type <id> '"
-                                   << inst->words[resultTypeIndex]
-                                   << "' is not defined.";
-           return false);
-  spvCheck(SpvOpTypePointer != resultType->second.opcode,
-           DIAG(resultTypeIndex) << "OpVariable Result Type <id> '"
-                                 << inst->words[resultTypeIndex]
-                                 << "' is not a pointer type.";
-           return false);
+  auto resultType = usedefs_.FindDef(inst->words[resultTypeIndex]);
+  if (!resultType.first || SpvOpTypePointer != resultType.second.opcode) {
+    DIAG(resultTypeIndex) << "OpVariable Result Type <id> '"
+                          << inst->words[resultTypeIndex]
+                          << "' is not a pointer type.";
+    return false;
+  }
   if (opcodeEntry->numTypes < inst->words.size()) {
     auto initialiserIndex = 4;
-    auto initialiser = find(inst->words[initialiserIndex]);
-    spvCheck(!found(initialiser), DIAG(initialiserIndex)
-                                      << "OpVariable Initializer <id> '"
-                                      << inst->words[initialiserIndex]
-                                      << "' is not defined.";
-             return false);
-    spvCheck(!spvOpcodeIsConstant(initialiser->second.opcode),
-             DIAG(initialiserIndex) << "OpVariable Initializer <id> '"
-                                    << inst->words[initialiserIndex]
-                                    << "' is not a constant.";
-             return false);
+    auto initialiser = usedefs_.FindDef(inst->words[initialiserIndex]);
+    if (!initialiser.first || !spvOpcodeIsConstant(initialiser.second.opcode)) {
+      DIAG(initialiserIndex) << "OpVariable Initializer <id> '"
+                             << inst->words[initialiserIndex]
+                             << "' is not a constant.";
+      return false;
+    }
   }
   return true;
 }
@@ -960,30 +766,26 @@ template <>
 bool idUsage::isValid<SpvOpLoad>(const spv_instruction_t* inst,
                                  const spv_opcode_desc) {
   auto resultTypeIndex = 1;
-  auto resultType = find(inst->words[resultTypeIndex]);
-  spvCheck(!found(resultType), DIAG(resultTypeIndex)
-                                   << "OpLoad Result Type <id> '"
-                                   << inst->words[resultTypeIndex]
-                                   << "' is not defind.";
+  auto resultType = usedefs_.FindDef(inst->words[resultTypeIndex]);
+  spvCheck(!resultType.first, DIAG(resultTypeIndex)
+                                  << "OpLoad Result Type <id> '"
+                                  << inst->words[resultTypeIndex]
+                                  << "' is not defind.";
            return false);
   auto pointerIndex = 3;
-  auto pointer = find(inst->words[pointerIndex]);
-  spvCheck(!found(pointer), DIAG(pointerIndex) << "OpLoad Pointer <id> '"
-                                               << inst->words[pointerIndex]
-                                               << "' is not defined.";
-           return false);
-  spvCheck(!spvOpcodeIsPointer(pointer->second.opcode),
-           DIAG(pointerIndex) << "OpLoad Pointer <id> '"
-                              << inst->words[pointerIndex]
-                              << "' is not a pointer.";
-           return false);
-  auto type = find(pointer->second.inst->words[1]);
-  spvCheck(!found(type), assert(0 && "Unreachable!"));
-  spvCheck(resultType != type, DIAG(resultTypeIndex)
-                                   << "OpLoad Result Type <id> '"
-                                   << inst->words[resultTypeIndex]
-                                   << " does not match Pointer <id> '"
-                                   << pointer->second.id << "'s type.";
+  auto pointer = usedefs_.FindDef(inst->words[pointerIndex]);
+  if (!pointer.first || !spvOpcodeIsPointer(pointer.second.opcode)) {
+    DIAG(pointerIndex) << "OpLoad Pointer <id> '" << inst->words[pointerIndex]
+                       << "' is not a pointer.";
+    return false;
+  }
+  auto type = usedefs_.FindDef(pointer.second.words[1]);
+  assert(type.first);
+  spvCheck(resultType.second.id != type.second.id,
+           DIAG(resultTypeIndex)
+               << "OpLoad Result Type <id> '" << inst->words[resultTypeIndex]
+               << " does not match Pointer <id> '" << pointer.second.id
+               << "'s type.";
            return false);
   return true;
 }
@@ -992,49 +794,41 @@ template <>
 bool idUsage::isValid<SpvOpStore>(const spv_instruction_t* inst,
                                   const spv_opcode_desc) {
   auto pointerIndex = 1;
-  auto pointer = find(inst->words[pointerIndex]);
-  spvCheck(!found(pointer), DIAG(pointerIndex) << "OpStore Pointer <id> '"
-                                               << inst->words[pointerIndex]
-                                               << "' is not defined.";
-           return false);
-  spvCheck(!spvOpcodeIsPointer(pointer->second.opcode),
-           DIAG(pointerIndex) << "OpStore Pointer <id> '"
-                              << inst->words[pointerIndex]
-                              << "' is not a pointer.";
-           return false);
-  auto pointerType = find(pointer->second.inst->words[1]);
-  spvCheck(!found(pointerType), assert(0 && "Unreachable!"));
-  auto type = find(pointerType->second.inst->words[3]);
-  spvCheck(!found(type), assert(0 && "Unreachable!"));
-  spvCheck(SpvOpTypeVoid == type->second.opcode,
-           DIAG(pointerIndex) << "OpStore Pointer <id> '"
-                              << inst->words[pointerIndex]
-                              << "'s type is void.";
+  auto pointer = usedefs_.FindDef(inst->words[pointerIndex]);
+  if (!pointer.first || !spvOpcodeIsPointer(pointer.second.opcode)) {
+    DIAG(pointerIndex) << "OpStore Pointer <id> '" << inst->words[pointerIndex]
+                       << "' is not a pointer.";
+    return false;
+  }
+  auto pointerType = usedefs_.FindDef(pointer.second.words[1]);
+  assert(pointerType.first);
+  auto type = usedefs_.FindDef(pointerType.second.words[3]);
+  assert(type.first);
+  spvCheck(SpvOpTypeVoid == type.second.opcode, DIAG(pointerIndex)
+                                                    << "OpStore Pointer <id> '"
+                                                    << inst->words[pointerIndex]
+                                                    << "'s type is void.";
            return false);
 
   auto objectIndex = 2;
-  auto object = find(inst->words[objectIndex]);
-  spvCheck(!found(object), DIAG(objectIndex) << "OpStore Object <id> '"
-                                             << inst->words[objectIndex]
-                                             << "' is not defined.";
-           return false);
-  spvCheck(!spvOpcodeIsObject(object->second.opcode),
-           DIAG(objectIndex) << "OpStore Object <id> '"
-                             << inst->words[objectIndex]
-                             << "' in not an object.";
-           return false);
-  auto objectType = find(object->second.inst->words[1]);
-  spvCheck(!found(objectType), assert(0 && "Unreachable!"));
-  spvCheck(SpvOpTypeVoid == objectType->second.opcode,
+  auto object = usedefs_.FindDef(inst->words[objectIndex]);
+  if (!object.first || !spvOpcodeIsObject(object.second.opcode)) {
+    DIAG(objectIndex) << "OpStore Object <id> '" << inst->words[objectIndex]
+                      << "' in not an object.";
+    return false;
+  }
+  auto objectType = usedefs_.FindDef(object.second.words[1]);
+  assert(objectType.first);
+  spvCheck(SpvOpTypeVoid == objectType.second.opcode,
            DIAG(objectIndex) << "OpStore Object <id> '"
                              << inst->words[objectIndex] << "'s type is void.";
            return false);
 
-  spvCheck(!spvOpcodeAreTypesEqual(type->second.inst, objectType->second.inst),
-           DIAG(pointerIndex) << "OpStore Pointer <id> '"
-                              << inst->words[pointerIndex]
-                              << "'s type does not match Object <id> '"
-                              << objectType->second.id << "'s type.";
+  spvCheck(type.second.id != objectType.second.id,
+           DIAG(pointerIndex)
+               << "OpStore Pointer <id> '" << inst->words[pointerIndex]
+               << "'s type does not match Object <id> '" << objectType.second.id
+               << "'s type.";
            return false);
   return true;
 }
@@ -1043,32 +837,25 @@ template <>
 bool idUsage::isValid<SpvOpCopyMemory>(const spv_instruction_t* inst,
                                        const spv_opcode_desc) {
   auto targetIndex = 1;
-  auto target = find(inst->words[targetIndex]);
-  spvCheck(!found(target), DIAG(targetIndex) << "OpCopyMemory Target <id> '"
-                                             << inst->words[targetIndex]
-                                             << "' is not defined.";
-           return false);
+  auto target = usedefs_.FindDef(inst->words[targetIndex]);
+  if (!target.first) return false;
   auto sourceIndex = 2;
-  auto source = find(inst->words[sourceIndex]);
-  spvCheck(!found(source), DIAG(targetIndex) << "OpCopyMemory Source <id> '"
-                                             << inst->words[targetIndex]
-                                             << "' is not defined.";
+  auto source = usedefs_.FindDef(inst->words[sourceIndex]);
+  if (!source.first) return false;
+  auto targetPointerType = usedefs_.FindDef(target.second.words[1]);
+  assert(targetPointerType.first);
+  auto targetType = usedefs_.FindDef(targetPointerType.second.words[3]);
+  assert(targetType.first);
+  auto sourcePointerType = usedefs_.FindDef(source.second.words[1]);
+  assert(sourcePointerType.first);
+  auto sourceType = usedefs_.FindDef(sourcePointerType.second.words[3]);
+  assert(sourceType.first);
+  spvCheck(targetType.second.id != sourceType.second.id,
+           DIAG(sourceIndex)
+               << "OpCopyMemory Target <id> '" << inst->words[sourceIndex]
+               << "'s type does not match Source <id> '" << sourceType.second.id
+               << "'s type.";
            return false);
-  auto targetPointerType = find(target->second.inst->words[1]);
-  spvCheck(!found(targetPointerType), assert(0 && "Unreachable!"));
-  auto targetType = find(targetPointerType->second.inst->words[3]);
-  spvCheck(!found(targetType), assert(0 && "Unreachable!"));
-  auto sourcePointerType = find(source->second.inst->words[1]);
-  spvCheck(!found(sourcePointerType), assert(0 && "Unreachable!"));
-  auto sourceType = find(sourcePointerType->second.inst->words[3]);
-  spvCheck(!found(sourceType), assert(0 && "Unreachable!"));
-  spvCheck(
-      !spvOpcodeAreTypesEqual(targetType->second.inst, sourceType->second.inst),
-      DIAG(sourceIndex) << "OpCopyMemory Target <id> '"
-                        << inst->words[sourceIndex]
-                        << "'s type does not match Source <id> '"
-                        << sourceType->second.id << "'s type.";
-      return false);
   return true;
 }
 
@@ -1076,57 +863,48 @@ template <>
 bool idUsage::isValid<SpvOpCopyMemorySized>(const spv_instruction_t* inst,
                                             const spv_opcode_desc) {
   auto targetIndex = 1;
-  auto target = find(inst->words[targetIndex]);
-  spvCheck(!found(target),
-           DIAG(targetIndex) << "OpCopyMemorySized Target <id> '"
-                             << inst->words[targetIndex] << "' is not defined.";
-           return false);
+  auto target = usedefs_.FindDef(inst->words[targetIndex]);
+  if (!target.first) return false;
   auto sourceIndex = 2;
-  auto source = find(inst->words[sourceIndex]);
-  spvCheck(!found(source),
-           DIAG(sourceIndex) << "OpCopyMemorySized Source <id> '"
-                             << inst->words[sourceIndex] << "' is not defined.";
-           return false);
+  auto source = usedefs_.FindDef(inst->words[sourceIndex]);
+  if (!source.first) return false;
   auto sizeIndex = 3;
-  auto size = find(inst->words[sizeIndex]);
-  spvCheck(!found(size), DIAG(sizeIndex) << "OpCopyMemorySized, Size <id> '"
-                                         << inst->words[sizeIndex]
-                                         << "' is not defined.";
-           return false);
-  auto targetPointerType = find(target->second.inst->words[1]);
-  spvCheck(!found(targetPointerType), assert(0 && "Unreachable!"));
-  spvCheck(SpvOpTypePointer != targetPointerType->second.opcode,
+  auto size = usedefs_.FindDef(inst->words[sizeIndex]);
+  if (!size.first) return false;
+  auto targetPointerType = usedefs_.FindDef(target.second.words[1]);
+  assert(targetPointerType.first);
+  spvCheck(SpvOpTypePointer != targetPointerType.second.opcode,
            DIAG(targetIndex) << "OpCopyMemorySized Target <id> '"
                              << inst->words[targetIndex]
                              << "' is not a pointer.";
            return false);
-  auto sourcePointerType = find(source->second.inst->words[1]);
-  spvCheck(!found(sourcePointerType), assert(0 && "Unreachable!"));
-  spvCheck(SpvOpTypePointer != sourcePointerType->second.opcode,
+  auto sourcePointerType = usedefs_.FindDef(source.second.words[1]);
+  assert(sourcePointerType.first);
+  spvCheck(SpvOpTypePointer != sourcePointerType.second.opcode,
            DIAG(sourceIndex) << "OpCopyMemorySized Source <id> '"
                              << inst->words[sourceIndex]
                              << "' is not a pointer.";
            return false);
-  switch (size->second.opcode) {
+  switch (size.second.opcode) {
     // TODO: The following opcode's are assumed to be valid, refer to the
     // following bug https://cvs.khronos.org/bugzilla/show_bug.cgi?id=13871 for
     // clarification
     case SpvOpConstant:
     case SpvOpSpecConstant: {
-      auto sizeType = find(size->second.inst->words[1]);
-      spvCheck(!found(sizeType), assert(0 && "Unreachable!"));
-      spvCheck(SpvOpTypeInt != sizeType->second.opcode,
+      auto sizeType = usedefs_.FindDef(size.second.words[1]);
+      assert(sizeType.first);
+      spvCheck(SpvOpTypeInt != sizeType.second.opcode,
                DIAG(sizeIndex) << "OpCopyMemorySized Size <id> '"
                                << inst->words[sizeIndex]
                                << "'s type is not an integer type.";
                return false);
     } break;
     case SpvOpVariable: {
-      auto pointerType = find(size->second.inst->words[1]);
-      spvCheck(!found(pointerType), assert(0 && "Unreachable!"));
-      auto sizeType = find(pointerType->second.inst->words[1]);
-      spvCheck(!found(sizeType), assert(0 && "Unreachable!"));
-      spvCheck(SpvOpTypeInt != sizeType->second.opcode,
+      auto pointerType = usedefs_.FindDef(size.second.words[1]);
+      assert(pointerType.first);
+      auto sizeType = usedefs_.FindDef(pointerType.second.words[1]);
+      assert(sizeType.first);
+      spvCheck(SpvOpTypeInt != sizeType.second.opcode,
                DIAG(sizeIndex) << "OpCopyMemorySized Size <id> '"
                                << inst->words[sizeIndex]
                                << "'s variable type is not an integer type.";
@@ -1177,31 +955,23 @@ template <>
 bool idUsage::isValid<SpvOpFunction>(const spv_instruction_t* inst,
                                      const spv_opcode_desc) {
   auto resultTypeIndex = 1;
-  auto resultType = find(inst->words[resultTypeIndex]);
-  spvCheck(!found(resultType), DIAG(resultTypeIndex)
-                                   << "OpFunction Result Type <id> '"
-                                   << inst->words[resultTypeIndex]
-                                   << "' is not defined.";
-           return false);
+  auto resultType = usedefs_.FindDef(inst->words[resultTypeIndex]);
+  if (!resultType.first) return false;
   auto functionTypeIndex = 4;
-  auto functionType = find(inst->words[functionTypeIndex]);
-  spvCheck(!found(functionType), DIAG(functionTypeIndex)
-                                     << "OpFunction Function Type <id> '"
-                                     << inst->words[functionTypeIndex]
-                                     << "' is not defined.";
-           return false);
-  spvCheck(SpvOpTypeFunction != functionType->second.opcode,
-           DIAG(functionTypeIndex) << "OpFunction Function Type <id> '"
-                                   << inst->words[functionTypeIndex]
-                                   << "' is not a function type.";
-           return false);
-  auto returnType = find(functionType->second.inst->words[2]);
-  spvCheck(!found(returnType), assert(0 && "Unreachable!"));
-  spvCheck(returnType != resultType,
+  auto functionType = usedefs_.FindDef(inst->words[functionTypeIndex]);
+  if (!functionType.first || SpvOpTypeFunction != functionType.second.opcode) {
+    DIAG(functionTypeIndex) << "OpFunction Function Type <id> '"
+                            << inst->words[functionTypeIndex]
+                            << "' is not a function type.";
+    return false;
+  }
+  auto returnType = usedefs_.FindDef(functionType.second.words[2]);
+  assert(returnType.first);
+  spvCheck(returnType.second.id != resultType.second.id,
            DIAG(resultTypeIndex) << "OpFunction Result Type <id> '"
                                  << inst->words[resultTypeIndex]
                                  << "' does not match the Function Type <id> '"
-                                 << resultType->second.id << "'s return type.";
+                                 << resultType.second.id << "'s return type.";
            return false);
   return true;
 }
@@ -1210,37 +980,34 @@ template <>
 bool idUsage::isValid<SpvOpFunctionParameter>(const spv_instruction_t* inst,
                                               const spv_opcode_desc) {
   auto resultTypeIndex = 1;
-  auto resultType = find(inst->words[resultTypeIndex]);
-  spvCheck(!found(resultType), DIAG(resultTypeIndex)
-                                   << "OpFunctionParameter Result Type <id> '"
-                                   << inst->words[resultTypeIndex]
-                                   << "' is not defined.";
-           return false);
+  auto resultType = usedefs_.FindDef(inst->words[resultTypeIndex]);
+  if (!resultType.first) return false;
   // NOTE: Find OpFunction & ensure OpFunctionParameter is not out of place.
   size_t paramIndex = 0;
   assert(firstInst < inst && "Invalid instruction pointer");
   while (firstInst != --inst) {
-    spvCheck(SpvOpFunction != inst->opcode && SpvOpFunctionParameter != inst->opcode,
-             DIAG(0) << "OpFunctionParameter is not preceded by OpFunction or "
-                        "OpFunctionParameter sequence.";
-             return false);
+    spvCheck(
+        SpvOpFunction != inst->opcode && SpvOpFunctionParameter != inst->opcode,
+        DIAG(0) << "OpFunctionParameter is not preceded by OpFunction or "
+                   "OpFunctionParameter sequence.";
+        return false);
     if (SpvOpFunction == inst->opcode) {
       break;
     } else {
       paramIndex++;
     }
   }
-  auto functionType = find(inst->words[4]);
-  spvCheck(!found(functionType), assert(0 && "Unreachable!"));
-  auto paramType = find(functionType->second.inst->words[paramIndex + 3]);
-  spvCheck(!found(paramType), assert(0 && "Unreachable!"));
-  spvCheck(
-      !spvOpcodeAreTypesEqual(resultType->second.inst, paramType->second.inst),
-      DIAG(resultTypeIndex) << "OpFunctionParameter Result Type <id> '"
-                            << inst->words[resultTypeIndex]
-                            << "' does not match the OpTypeFunction parameter "
-                               "type of the same index.";
-      return false);
+  auto functionType = usedefs_.FindDef(inst->words[4]);
+  assert(functionType.first);
+  auto paramType = usedefs_.FindDef(functionType.second.words[paramIndex + 3]);
+  assert(paramType.first);
+  spvCheck(resultType.second.id != paramType.second.id,
+           DIAG(resultTypeIndex)
+               << "OpFunctionParameter Result Type <id> '"
+               << inst->words[resultTypeIndex]
+               << "' does not match the OpTypeFunction parameter "
+                  "type of the same index.";
+           return false);
   return true;
 }
 
@@ -1248,37 +1015,27 @@ template <>
 bool idUsage::isValid<SpvOpFunctionCall>(const spv_instruction_t* inst,
                                          const spv_opcode_desc) {
   auto resultTypeIndex = 1;
-  auto resultType = find(inst->words[resultTypeIndex]);
-  spvCheck(!found(resultType), DIAG(resultTypeIndex)
-                                   << "OpFunctionCall Result Type <id> '"
-                                   << inst->words[resultTypeIndex]
-                                   << "' is not defined.";
-           return false);
+  auto resultType = usedefs_.FindDef(inst->words[resultTypeIndex]);
+  if (!resultType.first) return false;
   auto functionIndex = 3;
-  auto function = find(inst->words[functionIndex]);
-  spvCheck(!found(function), DIAG(functionIndex)
-                                 << "OpFunctionCall Function <id> '"
-                                 << inst->words[functionIndex]
-                                 << "' is not defined.";
-           return false);
-  spvCheck(SpvOpFunction != function->second.opcode,
-           DIAG(functionIndex) << "OpFunctionCall Function <id> '"
-                               << inst->words[functionIndex]
-                               << "' is not a function.";
+  auto function = usedefs_.FindDef(inst->words[functionIndex]);
+  if (!function.first || SpvOpFunction != function.second.opcode) {
+    DIAG(functionIndex) << "OpFunctionCall Function <id> '"
+                        << inst->words[functionIndex] << "' is not a function.";
+    return false;
+  }
+  auto returnType = usedefs_.FindDef(function.second.words[1]);
+  assert(returnType.first);
+  spvCheck(returnType.second.id != resultType.second.id,
+           DIAG(resultTypeIndex) << "OpFunctionCall Result Type <id> '"
+                                 << inst->words[resultTypeIndex]
+                                 << "'s type does not match Function <id> '"
+                                 << returnType.second.id << "'s return type.";
            return false);
-  auto returnType = find(function->second.inst->words[1]);
-  spvCheck(!found(returnType), assert(0 && "Unreachable!"));
-  spvCheck(
-      !spvOpcodeAreTypesEqual(returnType->second.inst, resultType->second.inst),
-      DIAG(resultTypeIndex)
-          << "OpFunctionCall Result Type <id> '" << inst->words[resultTypeIndex]
-          << "'s type does not match Function <id> '" << returnType->second.id
-          << "'s return type.";
-      return false);
-  auto functionType = find(function->second.inst->words[4]);
-  spvCheck(!found(functionType), assert(0 && "Unreachable!"));
+  auto functionType = usedefs_.FindDef(function.second.words[4]);
+  assert(functionType.first);
   auto functionCallArgCount = inst->words.size() - 4;
-  auto functionParamCount = functionType->second.inst->words.size() - 3;
+  auto functionParamCount = functionType.second.words.size() - 3;
   spvCheck(
       functionParamCount != functionCallArgCount,
       DIAG(inst->words.size() - 1)
@@ -1287,22 +1044,18 @@ bool idUsage::isValid<SpvOpFunctionCall>(const spv_instruction_t* inst,
       return false);
   for (size_t argumentIndex = 4, paramIndex = 3;
        argumentIndex < inst->words.size(); argumentIndex++, paramIndex++) {
-    auto argument = find(inst->words[argumentIndex]);
-    spvCheck(!found(argument), DIAG(argumentIndex)
-                                   << "OpFunctionCall Argument <id> '"
-                                   << inst->words[argumentIndex]
-                                   << "' is not defined.";
-             return false);
-    auto argumentType = find(argument->second.inst->words[1]);
-    spvCheck(!found(argumentType), assert(0 && "Unreachable!"));
-    auto parameterType = find(functionType->second.inst->words[paramIndex]);
-    spvCheck(!found(parameterType), assert(0 && "Unreachable!"));
-    spvCheck(!spvOpcodeAreTypesEqual(argumentType->second.inst,
-                                     parameterType->second.inst),
+    auto argument = usedefs_.FindDef(inst->words[argumentIndex]);
+    if (!argument.first) return false;
+    auto argumentType = usedefs_.FindDef(argument.second.words[1]);
+    assert(argumentType.first);
+    auto parameterType =
+        usedefs_.FindDef(functionType.second.words[paramIndex]);
+    assert(parameterType.first);
+    spvCheck(argumentType.second.id != parameterType.second.id,
              DIAG(argumentIndex) << "OpFunctionCall Argument <id> '"
                                  << inst->words[argumentIndex]
                                  << "'s type does not match Function <id> '"
-                                 << parameterType->second.id
+                                 << parameterType.second.id
                                  << "'s parameter type.";
              return false);
   }
@@ -1939,18 +1692,14 @@ template <>
 bool idUsage::isValid<SpvOpReturnValue>(const spv_instruction_t* inst,
                                         const spv_opcode_desc) {
   auto valueIndex = 1;
-  auto value = find(inst->words[valueIndex]);
-  spvCheck(!found(value), DIAG(valueIndex) << "OpReturnValue Value <id> '"
-                                           << inst->words[valueIndex]
-                                           << "' is not defined.";
-           return false);
-  spvCheck(!spvOpcodeIsValue(value->second.opcode),
-           DIAG(valueIndex) << "OpReturnValue Value <id> '"
-                            << inst->words[valueIndex]
-                            << "' does not represent a value.";
-           return false);
-  auto valueType = find(value->second.inst->words[1]);
-  spvCheck(!found(valueType), assert(0 && "Unreachable!"));
+  auto value = usedefs_.FindDef(inst->words[valueIndex]);
+  if (!value.first || !spvOpcodeIsValue(value.second.opcode)) {
+    DIAG(valueIndex) << "OpReturnValue Value <id> '" << inst->words[valueIndex]
+                     << "' does not represent a value.";
+    return false;
+  }
+  auto valueType = usedefs_.FindDef(value.second.words[1]);
+  assert(valueType.first);
   // NOTE: Find OpFunction
   const spv_instruction_t* function = inst - 1;
   while (firstInst != function) {
@@ -1960,20 +1709,18 @@ bool idUsage::isValid<SpvOpReturnValue>(const spv_instruction_t* inst,
   spvCheck(SpvOpFunction != function->opcode,
            DIAG(valueIndex) << "OpReturnValue is not in a basic block.";
            return false);
-  auto returnType = find(function->words[1]);
-  spvCheck(!found(returnType), assert(0 && "Unreachable!"));
-  if (SpvOpTypePointer == valueType->second.opcode) {
-    auto pointerValueType = find(valueType->second.inst->words[3]);
-    spvCheck(!found(pointerValueType), assert(0 && "Unreachable!"));
-    spvCheck(!spvOpcodeAreTypesEqual(returnType->second.inst,
-                                     pointerValueType->second.inst),
+  auto returnType = usedefs_.FindDef(function->words[1]);
+  assert(returnType.first);
+  if (SpvOpTypePointer == valueType.second.opcode) {
+    auto pointerValueType = usedefs_.FindDef(valueType.second.words[3]);
+    assert(pointerValueType.first);
+    spvCheck(returnType.second.id != pointerValueType.second.id,
              DIAG(valueIndex)
                  << "OpReturnValue Value <id> '" << inst->words[valueIndex]
                  << "'s pointer type does not match OpFunction's return type.";
              return false);
   } else {
-    spvCheck(!spvOpcodeAreTypesEqual(returnType->second.inst,
-                                     valueType->second.inst),
+    spvCheck(returnType.second.id != valueType.second.id,
              DIAG(valueIndex)
                  << "OpReturnValue Value <id> '" << inst->words[valueIndex]
                  << "'s type does not match OpFunction's return type.";
@@ -2378,10 +2125,8 @@ bool idUsage::isValid(const spv_instruction_t* inst) {
     return false;
   switch (inst->opcode) {
     FAIL(OpUndef)
-    CASE(OpName)
     CASE(OpMemberName)
     CASE(OpLine)
-    CASE(OpDecorate)
     CASE(OpMemberDecorate)
     CASE(OpGroupDecorate)
     FAIL(OpGroupMemberDecorate)
@@ -2592,15 +2337,16 @@ bool idUsage::isValid(const spv_instruction_t* inst) {
 }
 }  // anonymous namespace
 
-spv_result_t spvValidateInstructionIDs(
-    const spv_instruction_t* pInsts, const uint64_t instCount,
-    const spv_id_info_t* pIdUses, const uint64_t idUsesCount,
-    const spv_id_info_t* pIdDefs, const uint64_t idDefsCount,
-    const spv_opcode_table opcodeTable, const spv_operand_table operandTable,
-    const spv_ext_inst_table extInstTable, spv_position position,
-    spv_diagnostic* pDiag) {
-  idUsage idUsage(opcodeTable, operandTable, extInstTable, pIdUses, idUsesCount,
-                  pIdDefs, idDefsCount, pInsts, instCount, position, pDiag);
+spv_result_t spvValidateInstructionIDs(const spv_instruction_t* pInsts,
+                                       const uint64_t instCount,
+                                       const spv_opcode_table opcodeTable,
+                                       const spv_operand_table operandTable,
+                                       const spv_ext_inst_table extInstTable,
+                                       const libspirv::ValidationState_t& state,
+                                       spv_position position,
+                                       spv_diagnostic* pDiag) {
+  idUsage idUsage(opcodeTable, operandTable, extInstTable, pInsts, instCount,
+                  state.usedefs(), state.entry_points(), position, pDiag);
   for (uint64_t instIndex = 0; instIndex < instCount; ++instIndex) {
     spvCheck(!idUsage.isValid(&pInsts[instIndex]), return SPV_ERROR_INVALID_ID);
     position->index += pInsts[instIndex].words.size();
index a5d8d0c..cd35a4f 100644 (file)
@@ -27,7 +27,6 @@
 // Performs validation on instructions that appear inside of a SPIR-V block.
 
 #include "validate_passes.h"
-#include "validate_types.h"
 
 namespace libspirv {
 
index b9fb544..dd30c2b 100644 (file)
@@ -26,7 +26,6 @@
 
 // Source code for logical layout validation as described in section 2.4
 
-#include "validate_types.h"
 #include "validate_passes.h"
 #include "libspirv/libspirv.h"
 
index 5b78e54..de1d44a 100644 (file)
@@ -28,7 +28,7 @@
 #define LIBSPIRV_VALIDATE_PASSES_H_
 
 #include "binary.h"
-#include "validate_types.h"
+#include "validate.h"
 
 namespace libspirv
 {
index 7e148e5..2ce78ef 100644 (file)
@@ -112,7 +112,7 @@ spv_result_t SsaPass(ValidationState_t& _,
       switch (type) {
         case SPV_OPERAND_TYPE_RESULT_ID:
           _.removeIfForwardDeclared(*operand_ptr);
-          ret = _.defineId(*operand_ptr);
+          ret = SPV_SUCCESS;
           break;
         case SPV_OPERAND_TYPE_ID:
         case SPV_OPERAND_TYPE_TYPE_ID:
index 764cb34..4130450 100644 (file)
@@ -24,9 +24,6 @@
 // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 // MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
 
-#include "headers/spirv.h"
-#include "validate_types.h"
-
 #include <algorithm>
 #include <cassert>
 #include <map>
 #include <unordered_set>
 #include <vector>
 
+#include "headers/spirv.h"
+
+#include "validate.h"
+
 using std::find;
 using std::string;
 using std::unordered_set;
@@ -210,22 +211,12 @@ ValidationState_t::ValidationState_t(spv_diagnostic* diagnostic,
                                      uint32_t options)
     : diagnostic_(diagnostic),
       instruction_counter_(0),
-      defined_ids_{},
       unresolved_forward_ids_{},
       validation_flags_(options),
       operand_names_{},
-      current_layout_stage_(kLayoutCapabilities),
+      current_layout_section_(kLayoutCapabilities),
       module_functions_(*this) {}
 
-spv_result_t ValidationState_t::defineId(uint32_t id) {
-  if (defined_ids_.find(id) == end(defined_ids_)) {
-    defined_ids_.insert(id);
-  } else {
-    return diag(SPV_ERROR_INVALID_ID) << "ID cannot be assigned multiple times";
-  }
-  return SPV_SUCCESS;
-}
-
 spv_result_t ValidationState_t::forwardDeclareId(uint32_t id) {
   unresolved_forward_ids_.insert(id);
   return SPV_SUCCESS;
@@ -260,7 +251,7 @@ vector<uint32_t> ValidationState_t::unresolvedForwardIds() const {
 }
 
 bool ValidationState_t::isDefinedId(uint32_t id) const {
-  return defined_ids_.find(id) != end(defined_ids_);
+  return usedefs_.FindDef(id).first;
 }
 
 bool ValidationState_t::is_enabled(spv_validate_options_t flag) const {
@@ -273,19 +264,19 @@ int ValidationState_t::incrementInstructionCount() {
 }
 
 ModuleLayoutSection ValidationState_t::getLayoutSection() const {
-  return current_layout_stage_;
+  return current_layout_section_;
 }
 
 void ValidationState_t::progressToNextLayoutSectionOrder() {
   // Guard against going past the last element(kLayoutFunctionDefinitions)
-  if (current_layout_stage_ <= kLayoutFunctionDefinitions) {
-    current_layout_stage_ =
-        static_cast<ModuleLayoutSection>(current_layout_stage_ + 1);
+  if (current_layout_section_ <= kLayoutFunctionDefinitions) {
+    current_layout_section_ =
+        static_cast<ModuleLayoutSection>(current_layout_section_ + 1);
   }
 }
 
 bool ValidationState_t::isOpcodeInCurrentLayoutSection(SpvOp op) {
-  return IsInstructionInLayoutSection(current_layout_stage_, op);
+  return IsInstructionInLayoutSection(current_layout_section_, op);
 }
 
 DiagnosticStream ValidationState_t::diag(spv_result_t error_code) const {
diff --git a/source/validate_types.h b/source/validate_types.h
deleted file mode 100644 (file)
index 7725c89..0000000
+++ /dev/null
@@ -1,241 +0,0 @@
-// Copyright (c) 2015-2016 The Khronos Group Inc.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a
-// copy of this software and/or associated documentation files (the
-// "Materials"), to deal in the Materials without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Materials, and to
-// permit persons to whom the Materials are furnished to do so, subject to
-// the following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Materials.
-//
-// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS
-// KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS
-// SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT
-//    https://www.khronos.org/registry/
-//
-// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
-
-#ifndef LIBSPIRV_VALIDATE_TYPES_H_
-#define LIBSPIRV_VALIDATE_TYPES_H_
-
-#include "binary.h"
-#include "diagnostic.h"
-#include "libspirv/libspirv.h"
-
-#include <map>
-#include <string>
-#include <unordered_set>
-#include <vector>
-
-namespace libspirv {
-
-// This enum represents the sections of a SPIRV module. See section 2.4
-// of the SPIRV spec for additional details of the order. The enumerant values
-// are in the same order as the vector returned by GetModuleOrder
-enum ModuleLayoutSection {
-  kLayoutCapabilities,          // < Section 2.4 #1
-  kLayoutExtensions,            // < Section 2.4 #2
-  kLayoutExtInstImport,         // < Section 2.4 #3
-  kLayoutMemoryModel,           // < Section 2.4 #4
-  kLayoutEntryPoint,            // < Section 2.4 #5
-  kLayoutExecutionMode,         // < Section 2.4 #6
-  kLayoutDebug1,                // < Section 2.4 #7 > 1
-  kLayoutDebug2,                // < Section 2.4 #7 > 2
-  kLayoutAnnotations,           // < Section 2.4 #8
-  kLayoutTypes,                 // < Section 2.4 #9
-  kLayoutFunctionDeclarations,  // < Section 2.4 #10
-  kLayoutFunctionDefinitions    // < Section 2.4 #11
-};
-
-enum class FunctionDecl {
-  kFunctionDeclUnknown,      // < Unknown function declaration
-  kFunctionDeclDeclaration,  // < Function declaration
-  kFunctionDeclDefinition    // < Function definition
-};
-
-class ValidationState_t;
-
-// This class manages all function declaration and definitions in a module. It
-// handles the state and id information while parsing a function in the SPIR-V
-// binary.
-//
-// NOTE: This class is designed to be a Structure of Arrays. Therefore each
-// member variable is a vector whose elements represent the values for the
-// corresponding function in a SPIR-V module. Variables that are not vector
-// types are used to manage the state while parsing the function.
-class Functions {
- public:
-  Functions(ValidationState_t& module);
-
-  // Registers the function in the module. Subsequent instructions will be
-  // called against this function
-  spv_result_t RegisterFunction(uint32_t id, uint32_t ret_type_id,
-                                uint32_t function_control,
-                                uint32_t function_type_id);
-
-  // Registers a function parameter in the current function
-  spv_result_t RegisterFunctionParameter(uint32_t id, uint32_t type_id);
-
-  // Register a function end instruction
-  spv_result_t RegisterFunctionEnd();
-
-  // Sets the declaration type of the current function
-  spv_result_t RegisterSetFunctionDeclType(FunctionDecl type);
-
-  // Registers a block in the current function. Subsequent block instructions
-  // will target this block
-  // @param id The ID of the label of the block
-  spv_result_t RegisterBlock(uint32_t id);
-
-  // Registers a variable in the current block
-  spv_result_t RegisterBlockVariable(uint32_t type_id, uint32_t id,
-                                     SpvStorageClass storage, uint32_t init_id);
-
-  spv_result_t RegisterBlockLoopMerge(uint32_t merge_id, uint32_t continue_id,
-                                      SpvLoopControlMask control);
-
-  spv_result_t RegisterBlockSelectionMerge(uint32_t merge_id,
-                                           SpvSelectionControlMask control);
-
-  // Registers the end of the block
-  spv_result_t RegisterBlockEnd();
-
-  // Returns the number of blocks in the current function being parsed
-  size_t get_block_count() const;
-
-  // Retuns true if called after a function instruction but before the
-  // function end instruction
-  bool in_function_body() const;
-
-  // Returns true if called after a label instruction but before a branch
-  // instruction
-  bool in_block() const;
-
-  libspirv::DiagnosticStream diag(spv_result_t error_code) const;
-
- private:
-  // Parent module
-  ValidationState_t& module_;
-
-  // Funciton IDs in a module
-  std::vector<uint32_t> id_;
-
-  // OpTypeFunction IDs of each of the id_ functions
-  std::vector<uint32_t> type_id_;
-
-  // The type of declaration of each function
-  std::vector<FunctionDecl> declaration_type_;
-
-  // TODO(umar): Probably needs better abstractions
-  // The beginning of the block of functions
-  std::vector<std::vector<uint32_t>> block_ids_;
-
-  // The variable IDs of the functions
-  std::vector<std::vector<uint32_t>> variable_ids_;
-
-  // The function parameter ids of the functions
-  std::vector<std::vector<uint32_t>> parameter_ids_;
-
-  // NOTE: See correspoding getter functions
-  bool in_function_;
-  bool in_block_;
-};
-
-class ValidationState_t {
- public:
-  ValidationState_t(spv_diagnostic* diagnostic, uint32_t options);
-
-  // Defines the \p id for the module
-  spv_result_t defineId(uint32_t id);
-
-  // Forward declares the id in the module
-  spv_result_t forwardDeclareId(uint32_t id);
-
-  // Removes a forward declared ID if it has been defined
-  spv_result_t removeIfForwardDeclared(uint32_t id);
-
-  // Assigns a name to an ID
-  void assignNameToId(uint32_t id, std::string name);
-
-  // Returns a string representation of the ID in the format <id>[Name] where
-  // the <id> is the numeric valid of the id and the Name is a name assigned by
-  // the OpName instruction
-  std::string getIdName(uint32_t id) const;
-
-  // Returns the number of ID which have been forward referenced but not defined
-  size_t unresolvedForwardIdCount() const;
-
-  // Returns a list of unresolved forward ids.
-  std::vector<uint32_t> unresolvedForwardIds() const;
-
-  // Returns true if the id has been defined
-  bool isDefinedId(uint32_t id) const;
-
-  // Returns true if an spv_validate_options_t option is enabled in the
-  // validation instruction
-  bool is_enabled(spv_validate_options_t flag) const;
-
-  // Increments the instruction count. Used for diagnostic
-  int incrementInstructionCount();
-
-  // Returns the current layout section which is being processed
-  ModuleLayoutSection getLayoutSection() const;
-
-  // Increments the module_layout_order_stage_
-  void progressToNextLayoutSectionOrder();
-
-  // Determines if the op instruction is part of the current stage
-  bool isOpcodeInCurrentLayoutSection(SpvOp op);
-
-  libspirv::DiagnosticStream diag(spv_result_t error_code) const;
-
-  // Returns the function states
-  Functions& get_functions();
-
-  // Retuns true if the called after a function instruction but before the
-  // function end instruction
-  bool in_function_body() const;
-
-  // Returns true if called after a label instruction but before a branch
-  // instruction
-  bool in_block() const;
-
- private:
-  spv_diagnostic* diagnostic_;
-  // Tracks the number of instructions evaluated by the validator
-  int instruction_counter_;
-
-  // All IDs which have been defined
-  std::unordered_set<uint32_t> defined_ids_;
-
-  // IDs which have been forward declared but have not been defined
-  std::unordered_set<uint32_t> unresolved_forward_ids_;
-
-  // Validation options to determine the passes to execute
-  uint32_t validation_flags_;
-
-  std::map<uint32_t, std::string> operand_names_;
-
-  // The section of the code being processed
-  ModuleLayoutSection current_layout_stage_;
-
-  Functions module_functions_;
-
-  std::vector<SpvCapability> module_capabilities_;
-};
-}
-
-#define spvCheckReturn(expression)                      \
-  if (spv_result_t error = (expression)) return error;
-
-
-#endif
index f8e3184..64930fb 100644 (file)
@@ -229,7 +229,8 @@ TEST_F(ValidateID, OpExecutionModeGood) {
      OpFunctionEnd)";
   CHECK(spirv, SPV_SUCCESS);
 }
-TEST_F(ValidateID, OpExecutionModeEntryPointBad) {
+
+TEST_F(ValidateID, OpExecutionModeEntryPointMissing) {
   const char* spirv = R"(
      OpExecutionMode %3 LocalSize 1 1 1
 %1 = OpTypeVoid
@@ -241,6 +242,21 @@ TEST_F(ValidateID, OpExecutionModeEntryPointBad) {
   CHECK(spirv, SPV_ERROR_INVALID_ID);
 }
 
+TEST_F(ValidateID, OpExecutionModeEntryPointBad) {
+  const char* spirv = R"(
+     OpEntryPoint GLCompute %3 "" %a
+     OpExecutionMode %a LocalSize 1 1 1
+%void = OpTypeVoid
+%ptr = OpTypePointer Input %void
+%a = OpVariable %ptr Input
+%2 = OpTypeFunction %void
+%3 = OpFunction %void None %2
+%4 = OpLabel
+     OpReturn
+     OpFunctionEnd)";
+  CHECK(spirv, SPV_ERROR_INVALID_ID);
+}
+
 TEST_F(ValidateID, OpTypeVectorGood) {
   const char* spirv = R"(
 %1 = OpTypeFloat 32
@@ -1267,6 +1283,37 @@ TEST_F(ValidateID, OpReturnValueBad) {
   CHECK(spirv, SPV_ERROR_INVALID_ID);
 }
 
+TEST_F(ValidateID, UndefinedTypeId) {
+  const char* spirv = R"(
+%f32 = OpTypeFloat 32
+%atype = OpTypeRuntimeArray %f32
+%stype = OpTypeStruct %atype
+%ptrtype = OpTypePointer Input %stype
+%svar = OpVariable %ptrtype Input
+%s = OpLoad %stype %svar
+%len = OpArrayLength %undef %s 0
+)";
+  CHECK(spirv, SPV_ERROR_INVALID_ID);
+}
+
+TEST_F(ValidateID, UndefinedIdScope) {
+  const char* spirv = R"(
+%u32 = OpTypeInt 32 0
+%memsem = OpConstant %u32 0
+OpMemoryBarrier %undef %memsem
+)";
+  CHECK(spirv, SPV_ERROR_INVALID_ID);
+}
+
+TEST_F(ValidateID, UndefinedIdMemSem) {
+  const char* spirv = R"(
+%u32 = OpTypeInt 32 0
+%scope = OpConstant %u32 0
+OpMemoryBarrier %scope %undef
+)";
+  CHECK(spirv, SPV_ERROR_INVALID_ID);
+}
+
 // TODO: OpLifetimeStart
 // TODO: OpLifetimeStop
 // TODO: OpAtomicInit