Refactor IDs definition and use tracking
authorUmar Arshad <umar@arrayfire.com>
Fri, 8 Jul 2016 13:44:10 +0000 (09:44 -0400)
committerDavid Neto <dneto@google.com>
Tue, 26 Jul 2016 17:36:41 +0000 (13:36 -0400)
* Creates an ID class which manages definition and use of IDs
* Moved tracking code from validate.cpp to validate_id.cpp
* Rename and combine SsaPass and ProcessIds into IdPass
* Remove module dependency in Function

12 files changed:
source/CMakeLists.txt
source/val/Function.cpp
source/val/Function.h
source/val/Id.cpp [new file with mode: 0644]
source/val/Id.h [new file with mode: 0644]
source/val/ValidationState.cpp
source/val/ValidationState.h
source/validate.cpp
source/validate.h
source/validate_id.cpp
source/validate_ssa.cpp [deleted file]
test/Validate.SSA.cpp

index de227d6..4b22994 100644 (file)
@@ -167,10 +167,10 @@ set(SPIRV_SOURCES
   ${CMAKE_CURRENT_SOURCE_DIR}/validate_id.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/validate_instruction.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/validate_layout.cpp
-  ${CMAKE_CURRENT_SOURCE_DIR}/validate_ssa.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/val/BasicBlock.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/val/Construct.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/val/Function.cpp
+  ${CMAKE_CURRENT_SOURCE_DIR}/val/Id.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/val/ValidationState.cpp)
 
 # The software_version.cpp file includes build-version.inc.
index d13915e..036fae8 100644 (file)
 
 #include "val/BasicBlock.h"
 #include "val/Construct.h"
-#include "val/ValidationState.h"
 
 using std::ignore;
 using std::list;
 using std::make_pair;
 using std::pair;
-using std::string;
 using std::tie;
 using std::vector;
 
 namespace libspirv {
-namespace {
 
-void printDot(const BasicBlock& other, const ValidationState_t& module) {
-  string block_string;
-  if (other.successors()->empty()) {
-    block_string += "end ";
-  } else {
-    for (auto block : *other.successors()) {
-      block_string += module.getIdOrName(block->id()) + " ";
-    }
-  }
-  printf("%10s -> {%s\b}\n", module.getIdOrName(other.id()).c_str(),
-         block_string.c_str());
-}
-}  /// namespace
+// Universal Limit of ResultID + 1
+static const uint32_t kInvalidId = 0x400000;
 
 Function::Function(uint32_t function_id, uint32_t result_type_id,
                    SpvFunctionControlMask function_control,
-                   uint32_t function_type_id, ValidationState_t& module)
-    : module_(module),
-      id_(function_id),
+                   uint32_t function_type_id)
+    : id_(function_id),
       function_type_id_(function_type_id),
       result_type_id_(result_type_id),
       function_control_(function_control),
@@ -86,9 +71,6 @@ bool Function::IsFirstBlock(uint32_t block_id) const {
 
 spv_result_t Function::RegisterFunctionParameter(uint32_t parameter_id,
                                                  uint32_t type_id) {
-  assert(module_.in_function_body() == true &&
-         "RegisterFunctionParameter can only be called when parsing the binary "
-         "outside of another function");
   assert(current_block_ == nullptr &&
          "RegisterFunctionParameter can only be called when parsing the binary "
          "ouside of a block");
@@ -133,25 +115,6 @@ spv_result_t Function::RegisterSelectionMerge(uint32_t merge_id) {
   return SPV_SUCCESS;
 }
 
-void Function::PrintDotGraph() const {
-  if (first_block()) {
-    string func_name(module_.getIdOrName(id_));
-    printf("digraph %s {\n", func_name.c_str());
-    PrintBlocks();
-    printf("}\n");
-  }
-}
-
-void Function::PrintBlocks() const {
-  if (first_block()) {
-    printf("%10s -> %s\n", module_.getIdOrName(id_).c_str(),
-           module_.getIdOrName(first_block()->id()).c_str());
-    for (const auto& block : blocks_) {
-      printDot(block.second, module_);
-    }
-  }
-}
-
 spv_result_t Function::RegisterSetFunctionDeclType(FunctionDecl type) {
   assert(declaration_type_ == FunctionDecl::kFunctionDeclUnknown);
   declaration_type_ = type;
@@ -159,12 +122,6 @@ spv_result_t Function::RegisterSetFunctionDeclType(FunctionDecl type) {
 }
 
 spv_result_t Function::RegisterBlock(uint32_t block_id, bool is_definition) {
-  assert(module_.in_function_body() == true &&
-         "RegisterBlocks can only be called when parsing a binary inside of a "
-         "function");
-  assert(module_.current_layout_section() !=
-             ModuleLayoutSection::kLayoutFunctionDeclarations &&
-         "RegisterBlocks cannot be called within a function declaration");
   assert(
       declaration_type_ == FunctionDecl::kFunctionDeclDefinition &&
       "RegisterBlocks can only be called after declaration_type_ is defined");
@@ -191,9 +148,6 @@ spv_result_t Function::RegisterBlock(uint32_t block_id, bool is_definition) {
 
 void Function::RegisterBlockEnd(vector<uint32_t> next_list,
                                 SpvOp branch_instruction) {
-  assert(module_.in_function_body() == true &&
-         "RegisterBlockEnd can only be called when parsing a binary in a "
-         "function");
   assert(
       current_block_ &&
       "RegisterBlockEnd can only be called when parsing a binary in a block");
index a8175e9..7a810e1 100644 (file)
@@ -35,6 +35,7 @@
 #include "spirv-tools/libspirv.h"
 #include "spirv/1.1/spirv.h"
 #include "val/BasicBlock.h"
+#include "val/Construct.h"
 
 namespace libspirv {
 
@@ -44,17 +45,13 @@ enum class FunctionDecl {
   kFunctionDeclDefinition    /// < Function definition
 };
 
-class Construct;
-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.
 class Function {
  public:
   Function(uint32_t id, uint32_t result_type_id,
-           SpvFunctionControlMask function_control, uint32_t function_type_id,
-           ValidationState_t& module);
+           SpvFunctionControlMask function_control, uint32_t function_type_id);
 
   /// Registers a function parameter in the current function
   /// @return Returns SPV_SUCCESS if the call was successful
@@ -185,9 +182,6 @@ class Function {
   void PrintBlocks() const;
 
  private:
-  /// Parent module
-  ValidationState_t& module_;
-
   /// The result id of the OpLabel that defined this block
   uint32_t id_;
 
diff --git a/source/val/Id.cpp b/source/val/Id.cpp
new file mode 100644 (file)
index 0000000..2a0e01a
--- /dev/null
@@ -0,0 +1,62 @@
+// 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.
+
+#include "val/Id.h"
+
+namespace libspirv {
+#define OPERATOR(OP)                               \
+  bool operator OP(const Id& lhs, const Id& rhs) { \
+    return lhs.id_ OP rhs.id_;                     \
+  }                                                \
+  bool operator OP(const Id& lhs, uint32_t rhs) { return lhs.id_ OP rhs; }
+
+OPERATOR(<)
+OPERATOR(==)
+#undef OPERATOR
+
+Id::Id(const uint32_t result_id)
+    : id_(result_id),
+      type_id_(0),
+      opcode_(SpvOpNop),
+      defining_function_(nullptr),
+      defining_block_(nullptr),
+      uses_(),
+      words_(0) {}
+
+Id::Id(const spv_parsed_instruction_t* inst, Function* function,
+       BasicBlock* block)
+    : id_(inst->result_id),
+      type_id_(inst->type_id),
+      opcode_(static_cast<SpvOp>(inst->opcode)),
+      defining_function_(function),
+      defining_block_(block),
+      uses_(),
+      words_(inst->words, inst->words + inst->num_words) {}
+
+void Id::RegisterUse(const BasicBlock* block) {
+  if (block) { uses_.insert(block); }
+}
+}  // namespace libspirv
diff --git a/source/val/Id.h b/source/val/Id.h
new file mode 100644 (file)
index 0000000..c6e36cc
--- /dev/null
@@ -0,0 +1,135 @@
+// 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_VAL_ID_H_
+#define LIBSPIRV_VAL_ID_H_
+
+#include <cstdint>
+
+#include <functional>
+#include <set>
+
+#include "spirv-tools/libspirv.h"
+#include "val/Function.h"
+
+namespace libspirv {
+
+class BasicBlock;
+
+/// Represents a definition of any ID
+///
+/// This class represents the a definition of an Id in a module. This object can
+/// be formed as a complete, or incomplete Id. A complete Id allows you to
+/// reference all of the properties of this class and forms a fully defined
+/// object in the module. The incomplete Id is defined only by its integer value
+/// in a module and can only be used to search in a data structure.
+class Id {
+ public:
+  /// This constructor creates an incomplete Id. This constructor can be used to
+  /// create Id that are used to find other Ids
+  explicit Id(const uint32_t result_id = 0);
+
+  /// This constructor creates a complete Id.
+  explicit Id(const spv_parsed_instruction_t* inst,
+              Function* function = nullptr, BasicBlock* block = nullptr);
+
+  /// Registers a use of the Id
+  void RegisterUse(const BasicBlock* block = nullptr);
+
+  /// returns the id of the Id
+  operator uint32_t() const { return id_; }
+
+  uint32_t id() const { return id_; }
+  uint32_t type_id() const { return type_id_; }
+  SpvOp opcode() const { return opcode_; }
+
+  /// returns the Function where the id was defined. nullptr if it was defined
+  /// outside of a Function
+  const Function* defining_function() const { return defining_function_; }
+
+  /// returns the BasicBlock where the id was defined. nullptr if it was defined
+  /// outside of a BasicBlock
+  const BasicBlock* defining_block() const { return defining_block_; }
+
+  /// Returns the set of blocks where this Id was used
+  const std::set<const BasicBlock*>& uses() const { return uses_; }
+
+  /// The words used to define the Id
+  const std::vector<uint32_t>& words() const { return words_; }
+
+ private:
+  /// The integer that identifies the Id
+  uint32_t id_;
+
+  /// The type of the Id
+  uint32_t type_id_;
+
+  /// The opcode used to define the Id
+  SpvOp opcode_;
+
+  /// The function in which the Id was defined
+  Function* defining_function_;
+
+  /// The block in which the Id was defined
+  BasicBlock* defining_block_;
+
+  /// The blocks in which the Id was used
+  std::set<const BasicBlock*> uses_;
+
+  /// The words of the instuction that defined the Id
+  std::vector<uint32_t> words_;
+
+#define OPERATOR(OP)                                     \
+  friend bool operator OP(const Id& lhs, const Id& rhs); \
+  friend bool operator OP(const Id& lhs, uint32_t rhs)
+  OPERATOR(<);
+  OPERATOR(==);
+#undef OPERATOR
+};
+
+#define OPERATOR(OP)                              \
+  bool operator OP(const Id& lhs, const Id& rhs); \
+  bool operator OP(const Id& lhs, uint32_t rhs)
+
+OPERATOR(<);
+OPERATOR(==);
+#undef OPERATOR
+
+}  // namespace libspirv
+
+// custom specialization of std::hash for Id
+namespace std {
+template <>
+struct hash<libspirv::Id> {
+  typedef libspirv::Id argument_type;
+  typedef std::size_t result_type;
+  result_type operator()(const argument_type& id) const {
+    return hash<uint32_t>()(id);
+  }
+};
+}  /// namespace std
+
+#endif  // LIBSPIRV_VAL_ID_H_
index 217b488..eba0d7f 100644 (file)
 #include "val/Function.h"
 
 using std::list;
+using std::make_pair;
+using std::pair;
 using std::string;
+using std::unordered_map;
 using std::vector;
 
 namespace libspirv {
@@ -248,7 +251,18 @@ vector<uint32_t> ValidationState_t::UnresolvedForwardIds() const {
 }
 
 bool ValidationState_t::IsDefinedId(uint32_t id) const {
-  return usedefs_.FindDef(id).first;
+  return all_definitions_.find(Id{id}) != end(all_definitions_);
+}
+
+const Id* ValidationState_t::FindDef(uint32_t id) const {
+  if (all_definitions_.count(Id{id}) == 0) {
+    return nullptr;
+  } else {
+    /// We are in a const function, so we cannot use defs.operator[]().
+    /// Luckily we know the key exists, so defs_.at() won't throw an
+    /// exception.
+    return &all_definitions_.at(id);
+  }
 }
 
 // Increments the instruction count. Used for diagnostic
@@ -327,9 +341,7 @@ void ValidationState_t::set_memory_model(SpvMemoryModel mm) {
   memory_model_ = mm;
 }
 
-SpvMemoryModel ValidationState_t::memory_model() const {
-  return memory_model_;
-}
+SpvMemoryModel ValidationState_t::memory_model() const { return memory_model_; }
 
 spv_result_t ValidationState_t::RegisterFunction(
     uint32_t id, uint32_t ret_type_id, SpvFunctionControlMask function_control,
@@ -339,7 +351,7 @@ spv_result_t ValidationState_t::RegisterFunction(
          "of another function");
   in_function_ = true;
   module_functions_.emplace_back(id, ret_type_id, function_control,
-                                 function_type_id, *this);
+                                 function_type_id);
 
   // TODO(umar): validate function type and type_id
 
@@ -358,4 +370,26 @@ spv_result_t ValidationState_t::RegisterFunctionEnd() {
   return SPV_SUCCESS;
 }
 
+void ValidationState_t::AddId(const spv_parsed_instruction_t& inst) {
+  if (in_function_body()) {
+    if (in_block()) {
+      all_definitions_[inst.result_id] =
+          Id{&inst, &current_function(), current_function().current_block()};
+    } else {
+      all_definitions_[inst.result_id] = Id{&inst, &current_function()};
+    }
+  } else {
+    all_definitions_[inst.result_id] = Id{&inst};
+  }
+}
+
+void ValidationState_t::RegisterUseId(uint32_t used_id) {
+  auto used = all_definitions_.find(used_id);
+  if (used != end(all_definitions_)) {
+    if (in_function_body())
+      used->second.RegisterUse(current_function().current_block());
+    else
+      used->second.RegisterUse(nullptr);
+  }
+}
 }  /// namespace libspirv
index af5e54c..65d9144 100644 (file)
 #include "spirv-tools/libspirv.h"
 #include "spirv/1.1/spirv.h"
 #include "spirv_definition.h"
+#include "val/Id.h"
 
 namespace libspirv {
 
-// Universal Limit of ResultID + 1
-static const uint32_t kInvalidId = 0x400000;
-
-// Info about a result ID.
-typedef struct spv_id_info_t {
-  /// Id value.
-  uint32_t id;
-  /// Type id, or 0 if no type.
-  uint32_t type_id;
-  /// Opcode of the instruction defining the id.
-  SpvOp opcode;
-  /// Binary words of the instruction defining the id.
-  std::vector<uint32_t> words;
-} spv_id_info_t;
-
 /// 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
@@ -138,41 +124,6 @@ class ValidationState_t {
   /// instruction
   bool in_block() const;
 
-  /// Keeps track of ID definitions and uses.
-  class UseDefTracker {
-   public:
-    void AddDef(const spv_id_info_t& def) { defs_[def.id] = 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 {
-      if (defs_.count(id) == 0) {
-        return std::make_pair(false, spv_id_info_t{});
-      } else {
-        /// We are in a const function, so we cannot use defs.operator[]().
-        /// Luckily we know the key exists, so defs_.at() won't throw an
-        /// exception.
-        return std::make_pair(true, defs_.at(id));
-      }
-    }
-
-    /// Returns uses of IDs lacking defs.
-    std::unordered_set<uint32_t> FindUsesWithoutDefs() const {
-      auto diff = uses_;
-      for (const auto d : defs_) diff.erase(d.first);
-      return diff;
-    }
-
-   private:
-    std::unordered_set<uint32_t> uses_;
-    std::unordered_map<uint32_t, spv_id_info_t> defs_;
-  };
-
-  UseDefTracker& usedefs() { return usedefs_; }
-  const UseDefTracker& usedefs() const { return usedefs_; }
-
   /// Returns a list of entry point function ids
   std::vector<uint32_t>& entry_points() { return entry_points_; }
   const std::vector<uint32_t>& entry_points() const { return entry_points_; }
@@ -210,6 +161,20 @@ class ValidationState_t {
 
   AssemblyGrammar& grammar() { return grammar_; }
 
+  /// Adds an id to the module
+  void AddId(const spv_parsed_instruction_t& inst);
+
+  /// Register Id use
+  void RegisterUseId(uint32_t used_id);
+
+  /// Finds id's def, if it exists.  If found, returns the definition otherwise
+  /// nullptr
+  const Id* FindDef(uint32_t id) const;
+
+  const std::unordered_map<uint32_t, Id>& all_definitions() const {
+    return all_definitions_;
+  }
+
  private:
   spv_diagnostic* diagnostic_;
   /// Tracks the number of instructions evaluated by the validator
@@ -231,8 +196,7 @@ class ValidationState_t {
   spv_capability_mask_t
       module_capabilities_;  /// Module's declared capabilities.
 
-  /// Definitions and uses of all the IDs in the module.
-  UseDefTracker usedefs_;
+  std::unordered_map<uint32_t, Id> all_definitions_;
 
   /// IDs that are entry points, ie, arguments to OpEntryPoint.
   std::vector<uint32_t> entry_points_;
index 6e10a8a..c5779a9 100644 (file)
@@ -59,7 +59,7 @@ using std::vector;
 using libspirv::CfgPass;
 using libspirv::InstructionPass;
 using libspirv::ModuleLayoutPass;
-using libspirv::SsaPass;
+using libspirv::IdPass;
 using libspirv::ValidationState_t;
 
 spv_result_t spvValidateIDs(
@@ -67,15 +67,11 @@ spv_result_t spvValidateIDs(
     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;
-  }
   position->index = SPV_INDEX_INSTRUCTION;
   spvCheckReturn(spvValidateInstructionIDs(pInsts, count, opcodeTable,
                                            operandTable, extInstTable, state,
                                            position, pDiagnostic));
-  return undefd.empty() ? SPV_SUCCESS : SPV_ERROR_INVALID_ID;
+  return SPV_SUCCESS;
 }
 
 namespace {
@@ -127,18 +123,6 @@ 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.type_id, static_cast<SpvOp>(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) {
   ValidationState_t& _ = *(reinterpret_cast<ValidationState_t*>(user_data));
@@ -148,15 +132,53 @@ spv_result_t ProcessInstruction(void* user_data,
 
   DebugInstructionPass(_, inst);
   // TODO(umar): Perform data rules pass
-  ProcessIds(_, *inst);
+  spvCheckReturn(IdPass(_, inst));
   spvCheckReturn(ModuleLayoutPass(_, inst));
   spvCheckReturn(CfgPass(_, inst));
-  spvCheckReturn(SsaPass(_, inst));
   spvCheckReturn(InstructionPass(_, inst));
 
   return SPV_SUCCESS;
 }
 
+void printDot(const ValidationState_t& _, const libspirv::BasicBlock& other) {
+  string block_string;
+  if (other.successors()->empty()) {
+    block_string += "end ";
+  } else {
+    for (auto block : *other.successors()) {
+      block_string += _.getIdOrName(block->id()) + " ";
+    }
+  }
+  printf("%10s -> {%s\b}\n", _.getIdOrName(other.id()).c_str(),
+         block_string.c_str());
+}
+
+void PrintBlocks(ValidationState_t _, libspirv::Function func) {
+  assert(func.first_block());
+
+  printf("%10s -> %s\n", _.getIdOrName(func.id()).c_str(),
+         _.getIdOrName(func.first_block()->id()).c_str());
+  for (const auto& block : func.ordered_blocks()) {
+    printDot(_, *block);
+  }
+}
+
+#ifdef __clang__
+#define UNUSED(func) [[gnu::unused]] func
+#elif defined(__GNUC__)
+#define UNUSED(func) func __attribute__((unused)); func
+#elif defined(_MSC_VER)
+#define UNUSED(func) func
+#endif
+
+UNUSED(void PrintDotGraph(ValidationState_t _, libspirv::Function func)) {
+  if (func.first_block()) {
+    string func_name(_.getIdOrName(func.id()));
+    printf("digraph %s {\n", func_name.c_str());
+    PrintBlocks(_, func);
+    printf("}\n");
+  }
+}
 }  // anonymous namespace
 
 spv_result_t spvValidate(const spv_const_context context,
index 35ec9a6..7f41119 100644 (file)
@@ -30,7 +30,6 @@
 #include <algorithm>
 #include <array>
 #include <functional>
-#include <list>
 #include <map>
 #include <string>
 #include <unordered_map>
@@ -115,9 +114,8 @@ spv_result_t ModuleLayoutPass(ValidationState_t& _,
 spv_result_t CfgPass(ValidationState_t& _,
                      const spv_parsed_instruction_t* inst);
 
-/// Performs SSA validation of a module
-spv_result_t SsaPass(ValidationState_t& _,
-                     const spv_parsed_instruction_t* inst);
+/// Performs Id and SSA validation of a module
+spv_result_t IdPass(ValidationState_t& _, const spv_parsed_instruction_t* inst);
 
 /// Performs instruction validation.
 spv_result_t InstructionPass(ValidationState_t& _,
index e98b339..78550d4 100644 (file)
@@ -43,7 +43,9 @@
     action;                         \
   }
 
-using UseDefTracker = libspirv::ValidationState_t::UseDefTracker;
+using libspirv::ValidationState_t;
+using std::function;
+using std::ignore;
 
 namespace {
 
@@ -55,7 +57,7 @@ class idUsage {
           const spv_instruction_t* pInsts, const uint64_t instCountArg,
           const SpvMemoryModel memoryModelArg,
           const SpvAddressingModel addressingModelArg,
-          const UseDefTracker& usedefs,
+          const ValidationState_t& module,
           const std::vector<uint32_t>& entry_points, spv_position positionArg,
           spv_diagnostic* pDiagnosticArg)
       : opcodeTable(opcodeTableArg),
@@ -67,7 +69,7 @@ class idUsage {
         addressingModel(addressingModelArg),
         position(positionArg),
         pDiagnostic(pDiagnosticArg),
-        usedefs_(usedefs),
+        module_(module),
         entry_points_(entry_points) {}
 
   bool isValid(const spv_instruction_t* inst);
@@ -85,7 +87,7 @@ class idUsage {
   const SpvAddressingModel addressingModel;
   spv_position position;
   spv_diagnostic* pDiagnostic;
-  UseDefTracker usedefs_;
+  ValidationState_t module_;
   std::vector<uint32_t> entry_points_;
 };
 
@@ -106,20 +108,20 @@ template <>
 bool idUsage::isValid<SpvOpMemberName>(const spv_instruction_t* inst,
                                        const spv_opcode_desc) {
   auto typeIndex = 1;
-  auto type = usedefs_.FindDef(inst->words[typeIndex]);
-  if (!type.first || SpvOpTypeStruct != type.second.opcode) {
+  auto type = module_.FindDef(inst->words[typeIndex]);
+  if (!type || SpvOpTypeStruct != type->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.words.size() - 2);
+  auto memberCount = (uint32_t)(type->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->id() << "'s member count.";
            return false);
   return true;
 }
@@ -128,8 +130,8 @@ template <>
 bool idUsage::isValid<SpvOpLine>(const spv_instruction_t* inst,
                                  const spv_opcode_desc) {
   auto fileIndex = 1;
-  auto file = usedefs_.FindDef(inst->words[fileIndex]);
-  if (!file.first || SpvOpString != file.second.opcode) {
+  auto file = module_.FindDef(inst->words[fileIndex]);
+  if (!file || SpvOpString != file->opcode()) {
     DIAG(fileIndex) << "OpLine Target <id> '" << inst->words[fileIndex]
                     << "' is not an OpString.";
     return false;
@@ -141,8 +143,8 @@ template <>
 bool idUsage::isValid<SpvOpMemberDecorate>(const spv_instruction_t* inst,
                                            const spv_opcode_desc) {
   auto structTypeIndex = 1;
-  auto structType = usedefs_.FindDef(inst->words[structTypeIndex]);
-  if (!structType.first || SpvOpTypeStruct != structType.second.opcode) {
+  auto structType = module_.FindDef(inst->words[structTypeIndex]);
+  if (!structType || SpvOpTypeStruct != structType->opcode()) {
     DIAG(structTypeIndex) << "OpMemberDecorate Structure type <id> '"
                           << inst->words[structTypeIndex]
                           << "' is not a struct type.";
@@ -150,7 +152,7 @@ bool idUsage::isValid<SpvOpMemberDecorate>(const spv_instruction_t* inst,
   }
   auto memberIndex = 2;
   auto member = inst->words[memberIndex];
-  auto memberCount = static_cast<uint32_t>(structType.second.words.size() - 2);
+  auto memberCount = static_cast<uint32_t>(structType->words().size() - 2);
   spvCheck(memberCount < member, DIAG(memberIndex)
                                      << "OpMemberDecorate Structure type <id> '"
                                      << inst->words[memberIndex]
@@ -163,9 +165,8 @@ template <>
 bool idUsage::isValid<SpvOpGroupDecorate>(const spv_instruction_t* inst,
                                           const spv_opcode_desc) {
   auto decorationGroupIndex = 1;
-  auto decorationGroup = usedefs_.FindDef(inst->words[decorationGroupIndex]);
-  if (!decorationGroup.first ||
-      SpvOpDecorationGroup != decorationGroup.second.opcode) {
+  auto decorationGroup = module_.FindDef(inst->words[decorationGroupIndex]);
+  if (!decorationGroup || SpvOpDecorationGroup != decorationGroup->opcode()) {
     DIAG(decorationGroupIndex) << "OpGroupDecorate Decoration group <id> '"
                                << inst->words[decorationGroupIndex]
                                << "' is not a decoration group.";
@@ -190,8 +191,8 @@ template <>
 bool idUsage::isValid<SpvOpEntryPoint>(const spv_instruction_t* inst,
                                        const spv_opcode_desc) {
   auto entryPointIndex = 2;
-  auto entryPoint = usedefs_.FindDef(inst->words[entryPointIndex]);
-  if (!entryPoint.first || SpvOpFunction != entryPoint.second.opcode) {
+  auto entryPoint = module_.FindDef(inst->words[entryPointIndex]);
+  if (!entryPoint || SpvOpFunction != entryPoint->opcode()) {
     DIAG(entryPointIndex) << "OpEntryPoint Entry Point <id> '"
                           << inst->words[entryPointIndex]
                           << "' is not a function.";
@@ -202,16 +203,16 @@ bool idUsage::isValid<SpvOpEntryPoint>(const spv_instruction_t* inst,
   if (executionModel != SpvExecutionModelKernel) {
     // TODO: Check the entry point signature is void main(void), may be subject
     // to change
-    auto entryPointType = usedefs_.FindDef(entryPoint.second.words[4]);
-    if (!entryPointType.first || 3 != entryPointType.second.words.size()) {
+    auto entryPointType = module_.FindDef(entryPoint->words()[4]);
+    if (!entryPointType || 3 != entryPointType->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.type_id);
-  if (!returnType.first || SpvOpTypeVoid != returnType.second.opcode) {
+  auto returnType = module_.FindDef(entryPoint->type_id());
+  if (!returnType || SpvOpTypeVoid != returnType->opcode()) {
     DIAG(entryPointIndex) << "OpEntryPoint Entry Point <id> '"
                           << inst->words[entryPointIndex]
                           << "'s function return type is not void.";
@@ -241,9 +242,8 @@ template <>
 bool idUsage::isValid<SpvOpTypeVector>(const spv_instruction_t* inst,
                                        const spv_opcode_desc) {
   auto componentIndex = 2;
-  auto componentType = usedefs_.FindDef(inst->words[componentIndex]);
-  if (!componentType.first ||
-      !spvOpcodeIsScalarType(componentType.second.opcode)) {
+  auto componentType = module_.FindDef(inst->words[componentIndex]);
+  if (!componentType || !spvOpcodeIsScalarType(componentType->opcode())) {
     DIAG(componentIndex) << "OpTypeVector Component Type <id> '"
                          << inst->words[componentIndex]
                          << "' is not a scalar type.";
@@ -256,8 +256,8 @@ template <>
 bool idUsage::isValid<SpvOpTypeMatrix>(const spv_instruction_t* inst,
                                        const spv_opcode_desc) {
   auto columnTypeIndex = 2;
-  auto columnType = usedefs_.FindDef(inst->words[columnTypeIndex]);
-  if (!columnType.first || SpvOpTypeVector != columnType.second.opcode) {
+  auto columnType = module_.FindDef(inst->words[columnTypeIndex]);
+  if (!columnType || SpvOpTypeVector != columnType->opcode()) {
     DIAG(columnTypeIndex) << "OpTypeMatrix Column Type <id> '"
                           << inst->words[columnTypeIndex]
                           << "' is not a vector.";
@@ -297,36 +297,35 @@ template <>
 bool idUsage::isValid<SpvOpTypeArray>(const spv_instruction_t* inst,
                                       const spv_opcode_desc) {
   auto elementTypeIndex = 2;
-  auto elementType = usedefs_.FindDef(inst->words[elementTypeIndex]);
-  if (!elementType.first ||
-      !spvOpcodeGeneratesType(elementType.second.opcode)) {
+  auto elementType = module_.FindDef(inst->words[elementTypeIndex]);
+  if (!elementType || !spvOpcodeGeneratesType(elementType->opcode())) {
     DIAG(elementTypeIndex) << "OpTypeArray Element Type <id> '"
                            << inst->words[elementTypeIndex]
                            << "' is not a type.";
     return false;
   }
   auto lengthIndex = 3;
-  auto length = usedefs_.FindDef(inst->words[lengthIndex]);
-  if (!length.first || !spvOpcodeIsConstant(length.second.opcode)) {
+  auto length = module_.FindDef(inst->words[lengthIndex]);
+  if (!length || !spvOpcodeIsConstant(length->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.words;
+  auto constInst = length->words();
   auto constResultTypeIndex = 1;
-  auto constResultType = usedefs_.FindDef(constInst[constResultTypeIndex]);
-  if (!constResultType.first || SpvOpTypeInt != constResultType.second.opcode) {
+  auto constResultType = module_.FindDef(constInst[constResultTypeIndex]);
+  if (!constResultType || SpvOpTypeInt != constResultType->opcode()) {
     DIAG(lengthIndex) << "OpTypeArray Length <id> '" << inst->words[lengthIndex]
                       << "' is not a constant integer type.";
     return false;
   }
 
-  switch (length.second.opcode) {
+  switch (length->opcode()) {
     case SpvOpSpecConstant:
     case SpvOpConstant:
-      if (aboveZero(length.second.words, constResultType.second.words)) break;
+      if (aboveZero(length->words(), constResultType->words())) break;
     // Else fall through!
     case SpvOpConstantNull: {
       DIAG(lengthIndex) << "OpTypeArray Length <id> '"
@@ -347,9 +346,8 @@ template <>
 bool idUsage::isValid<SpvOpTypeRuntimeArray>(const spv_instruction_t* inst,
                                              const spv_opcode_desc) {
   auto elementTypeIndex = 2;
-  auto elementType = usedefs_.FindDef(inst->words[elementTypeIndex]);
-  if (!elementType.first ||
-      !spvOpcodeGeneratesType(elementType.second.opcode)) {
+  auto elementType = module_.FindDef(inst->words[elementTypeIndex]);
+  if (!elementType || !spvOpcodeGeneratesType(elementType->opcode())) {
     DIAG(elementTypeIndex) << "OpTypeRuntimeArray Element Type <id> '"
                            << inst->words[elementTypeIndex]
                            << "' is not a type.";
@@ -363,9 +361,8 @@ 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 = usedefs_.FindDef(inst->words[memberTypeIndex]);
-    if (!memberType.first ||
-        !spvOpcodeGeneratesType(memberType.second.opcode)) {
+    auto memberType = module_.FindDef(inst->words[memberTypeIndex]);
+    if (!memberType || !spvOpcodeGeneratesType(memberType->opcode())) {
       DIAG(memberTypeIndex) << "OpTypeStruct Member Type <id> '"
                             << inst->words[memberTypeIndex]
                             << "' is not a type.";
@@ -379,8 +376,8 @@ template <>
 bool idUsage::isValid<SpvOpTypePointer>(const spv_instruction_t* inst,
                                         const spv_opcode_desc) {
   auto typeIndex = 3;
-  auto type = usedefs_.FindDef(inst->words[typeIndex]);
-  if (!type.first || !spvOpcodeGeneratesType(type.second.opcode)) {
+  auto type = module_.FindDef(inst->words[typeIndex]);
+  if (!type || !spvOpcodeGeneratesType(type->opcode())) {
     DIAG(typeIndex) << "OpTypePointer Type <id> '" << inst->words[typeIndex]
                     << "' is not a type.";
     return false;
@@ -392,16 +389,16 @@ template <>
 bool idUsage::isValid<SpvOpTypeFunction>(const spv_instruction_t* inst,
                                          const spv_opcode_desc) {
   auto returnTypeIndex = 2;
-  auto returnType = usedefs_.FindDef(inst->words[returnTypeIndex]);
-  if (!returnType.first || !spvOpcodeGeneratesType(returnType.second.opcode)) {
+  auto returnType = module_.FindDef(inst->words[returnTypeIndex]);
+  if (!returnType || !spvOpcodeGeneratesType(returnType->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 = usedefs_.FindDef(inst->words[paramTypeIndex]);
-    if (!paramType.first || !spvOpcodeGeneratesType(paramType.second.opcode)) {
+    auto paramType = module_.FindDef(inst->words[paramTypeIndex]);
+    if (!paramType || !spvOpcodeGeneratesType(paramType->opcode())) {
       DIAG(paramTypeIndex) << "OpTypeFunction Parameter Type <id> '"
                            << inst->words[paramTypeIndex] << "' is not a type.";
       return false;
@@ -421,8 +418,8 @@ template <>
 bool idUsage::isValid<SpvOpConstantTrue>(const spv_instruction_t* inst,
                                          const spv_opcode_desc) {
   auto resultTypeIndex = 1;
-  auto resultType = usedefs_.FindDef(inst->words[resultTypeIndex]);
-  if (!resultType.first || SpvOpTypeBool != resultType.second.opcode) {
+  auto resultType = module_.FindDef(inst->words[resultTypeIndex]);
+  if (!resultType || SpvOpTypeBool != resultType->opcode()) {
     DIAG(resultTypeIndex) << "OpConstantTrue Result Type <id> '"
                           << inst->words[resultTypeIndex]
                           << "' is not a boolean type.";
@@ -435,8 +432,8 @@ template <>
 bool idUsage::isValid<SpvOpConstantFalse>(const spv_instruction_t* inst,
                                           const spv_opcode_desc) {
   auto resultTypeIndex = 1;
-  auto resultType = usedefs_.FindDef(inst->words[resultTypeIndex]);
-  if (!resultType.first || SpvOpTypeBool != resultType.second.opcode) {
+  auto resultType = module_.FindDef(inst->words[resultTypeIndex]);
+  if (!resultType || SpvOpTypeBool != resultType->opcode()) {
     DIAG(resultTypeIndex) << "OpConstantFalse Result Type <id> '"
                           << inst->words[resultTypeIndex]
                           << "' is not a boolean type.";
@@ -449,8 +446,8 @@ template <>
 bool idUsage::isValid<SpvOpConstantComposite>(const spv_instruction_t* inst,
                                               const spv_opcode_desc) {
   auto resultTypeIndex = 1;
-  auto resultType = usedefs_.FindDef(inst->words[resultTypeIndex]);
-  if (!resultType.first || !spvOpcodeIsComposite(resultType.second.opcode)) {
+  auto resultType = module_.FindDef(inst->words[resultTypeIndex]);
+  if (!resultType || !spvOpcodeIsComposite(resultType->opcode())) {
     DIAG(resultTypeIndex) << "OpConstantComposite Result Type <id> '"
                           << inst->words[resultTypeIndex]
                           << "' is not a composite type.";
@@ -458,163 +455,155 @@ bool idUsage::isValid<SpvOpConstantComposite>(const spv_instruction_t* inst,
   }
 
   auto constituentCount = inst->words.size() - 3;
-  switch (resultType.second.opcode) {
+  switch (resultType->opcode()) {
     case SpvOpTypeVector: {
-      auto componentCount = resultType.second.words[3];
+      auto componentCount = resultType->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->id() << "'s vector component count.";
           return false);
-      auto componentType = usedefs_.FindDef(resultType.second.words[2]);
-      assert(componentType.first);
+      auto componentType = module_.FindDef(resultType->words()[2]);
+      assert(componentType);
       for (size_t constituentIndex = 3; constituentIndex < inst->words.size();
            constituentIndex++) {
-        auto constituent = usedefs_.FindDef(inst->words[constituentIndex]);
-        if (!constituent.first ||
-            !spvOpcodeIsConstant(constituent.second.opcode)) {
+        auto constituent = module_.FindDef(inst->words[constituentIndex]);
+        if (!constituent || !spvOpcodeIsConstant(constituent->opcode())) {
           DIAG(constituentIndex) << "OpConstantComposite Constituent <id> '"
                                  << inst->words[constituentIndex]
                                  << "' is not a constant.";
           return false;
         }
-        auto constituentResultType =
-            usedefs_.FindDef(constituent.second.type_id);
-        if (!constituentResultType.first ||
-            componentType.second.opcode !=
-                constituentResultType.second.opcode) {
+        auto constituentResultType = module_.FindDef(constituent->type_id());
+        if (!constituentResultType ||
+            componentType->opcode() != constituentResultType->opcode()) {
           DIAG(constituentIndex) << "OpConstantComposite Constituent <id> '"
                                  << inst->words[constituentIndex]
                                  << "'s type does not match Result Type <id> '"
-                                 << resultType.second.id
+                                 << resultType->id()
                                  << "'s vector element type.";
           return false;
         }
       }
     } break;
     case SpvOpTypeMatrix: {
-      auto columnCount = resultType.second.words[3];
+      auto columnCount = resultType->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->id() << "'s matrix column count.";
           return false);
 
-      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);
+      auto columnType = module_.FindDef(resultType->words()[2]);
+      assert(columnType);
+      auto componentCount = columnType->words()[3];
+      auto componentType = module_.FindDef(columnType->words()[2]);
+      assert(componentType);
 
       for (size_t constituentIndex = 3; constituentIndex < inst->words.size();
            constituentIndex++) {
-        auto constituent = usedefs_.FindDef(inst->words[constituentIndex]);
-        if (!constituent.first ||
-            SpvOpConstantComposite != constituent.second.opcode) {
+        auto constituent = module_.FindDef(inst->words[constituentIndex]);
+        if (!constituent || SpvOpConstantComposite != constituent->opcode()) {
           DIAG(constituentIndex) << "OpConstantComposite Constituent <id> '"
                                  << inst->words[constituentIndex]
                                  << "' is not a constant composite.";
           return false;
         }
-        auto vector = usedefs_.FindDef(constituent.second.type_id);
-        assert(vector.first);
-        spvCheck(columnType.second.opcode != vector.second.opcode,
+        auto vector = module_.FindDef(constituent->type_id());
+        assert(vector);
+        spvCheck(columnType->opcode() != vector->opcode(),
                  DIAG(constituentIndex)
                      << "OpConstantComposite Constituent <id> '"
                      << inst->words[constituentIndex]
                      << "' type does not match Result Type <id> '"
-                     << resultType.second.id << "'s matrix column type.";
+                     << resultType->id() << "'s matrix column type.";
                  return false);
-        auto vectorComponentType = usedefs_.FindDef(vector.second.words[2]);
-        assert(vectorComponentType.first);
-        spvCheck(componentType.second.id != vectorComponentType.second.id,
+        auto vectorComponentType = module_.FindDef(vector->words()[2]);
+        assert(vectorComponentType);
+        spvCheck(componentType->id() != vectorComponentType->id(),
                  DIAG(constituentIndex)
                      << "OpConstantComposite Constituent <id> '"
                      << inst->words[constituentIndex]
                      << "' component type does not match Result Type <id> '"
-                     << resultType.second.id
-                     << "'s matrix column component type.";
+                     << resultType->id() << "'s matrix column component type.";
                  return false);
         spvCheck(
-            componentCount != vector.second.words[3],
+            componentCount != vector->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->id() << "'s vector component count.";
             return false);
       }
     } break;
     case SpvOpTypeArray: {
-      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,
+      auto elementType = module_.FindDef(resultType->words()[2]);
+      assert(elementType);
+      auto length = module_.FindDef(resultType->words()[3]);
+      assert(length);
+      spvCheck(length->words()[3] != constituentCount,
                DIAG(inst->words.size() - 1)
                    << "OpConstantComposite Constituent count does not match "
                       "Result Type <id> '"
-                   << resultType.second.id << "'s array length.";
+                   << resultType->id() << "'s array length.";
                return false);
       for (size_t constituentIndex = 3; constituentIndex < inst->words.size();
            constituentIndex++) {
-        auto constituent = usedefs_.FindDef(inst->words[constituentIndex]);
-        if (!constituent.first ||
-            !spvOpcodeIsConstant(constituent.second.opcode)) {
+        auto constituent = module_.FindDef(inst->words[constituentIndex]);
+        if (!constituent || !spvOpcodeIsConstant(constituent->opcode())) {
           DIAG(constituentIndex) << "OpConstantComposite Constituent <id> '"
                                  << inst->words[constituentIndex]
                                  << "' is not a constant.";
           return false;
         }
-        auto constituentType = usedefs_.FindDef(constituent.second.type_id);
-        assert(constituentType.first);
-        spvCheck(elementType.second.id != constituentType.second.id,
+        auto constituentType = module_.FindDef(constituent->type_id());
+        assert(constituentType);
+        spvCheck(elementType->id() != constituentType->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->id() << "'s array element type.";
                  return false);
       }
     } break;
     case SpvOpTypeStruct: {
-      auto memberCount = resultType.second.words.size() - 2;
+      auto memberCount = resultType->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->id() << "'s struct member count.";
                return false);
       for (uint32_t constituentIndex = 3, memberIndex = 2;
            constituentIndex < inst->words.size();
            constituentIndex++, memberIndex++) {
-        auto constituent = usedefs_.FindDef(inst->words[constituentIndex]);
-        if (!constituent.first ||
-            !spvOpcodeIsConstant(constituent.second.opcode)) {
+        auto constituent = module_.FindDef(inst->words[constituentIndex]);
+        if (!constituent || !spvOpcodeIsConstant(constituent->opcode())) {
           DIAG(constituentIndex) << "OpConstantComposite Constituent <id> '"
                                  << inst->words[constituentIndex]
                                  << "' is not a constant.";
           return false;
         }
-        auto constituentType = usedefs_.FindDef(constituent.second.type_id);
-        assert(constituentType.first);
+        auto constituentType = module_.FindDef(constituent->type_id());
+        assert(constituentType);
 
-        auto memberType =
-            usedefs_.FindDef(resultType.second.words[memberIndex]);
-        assert(memberType.first);
-        spvCheck(memberType.second.id != constituentType.second.id,
+        auto memberType = module_.FindDef(resultType->words()[memberIndex]);
+        assert(memberType);
+        spvCheck(memberType->id() != constituentType->id(),
                  DIAG(constituentIndex)
                      << "OpConstantComposite Constituent <id> '"
                      << inst->words[constituentIndex]
                      << "' type does not match the Result Type <id> '"
-                     << resultType.second.id << "'s member type.";
+                     << resultType->id() << "'s member type.";
                  return false);
       }
     } break;
@@ -627,8 +616,8 @@ template <>
 bool idUsage::isValid<SpvOpConstantSampler>(const spv_instruction_t* inst,
                                             const spv_opcode_desc) {
   auto resultTypeIndex = 1;
-  auto resultType = usedefs_.FindDef(inst->words[resultTypeIndex]);
-  if (!resultType.first || SpvOpTypeSampler != resultType.second.opcode) {
+  auto resultType = module_.FindDef(inst->words[resultTypeIndex]);
+  if (!resultType || SpvOpTypeSampler != resultType->opcode()) {
     DIAG(resultTypeIndex) << "OpConstantSampler Result Type <id> '"
                           << inst->words[resultTypeIndex]
                           << "' is not a sampler type.";
@@ -638,10 +627,10 @@ bool idUsage::isValid<SpvOpConstantSampler>(const spv_instruction_t* inst,
 }
 
 // True if instruction defines a type that can have a null value, as defined by
-// the SPIR-V spec.  Tracks composite-type components through usedefs to check
+// the SPIR-V spec.  Tracks composite-type components through module to check
 // nullability transitively.
 bool IsTypeNullable(const std::vector<uint32_t>& instruction,
-                    const UseDefTracker& usedefs) {
+                    const ValidationState_t& module) {
   uint16_t opcode;
   uint16_t word_count;
   spvOpcodeSplit(instruction[0], &word_count, &opcode);
@@ -658,15 +647,14 @@ bool IsTypeNullable(const std::vector<uint32_t>& instruction,
     case SpvOpTypeArray:
     case SpvOpTypeMatrix:
     case SpvOpTypeVector: {
-      auto base_type = usedefs.FindDef(instruction[2]);
-      return base_type.first && IsTypeNullable(base_type.second.words, usedefs);
+      auto base_type = module.FindDef(instruction[2]);
+      return base_type && IsTypeNullable(base_type->words(), module);
     }
     case SpvOpTypeStruct: {
       for (size_t elementIndex = 2; elementIndex < instruction.size();
            ++elementIndex) {
-        auto element = usedefs.FindDef(instruction[elementIndex]);
-        if (!element.first || !IsTypeNullable(element.second.words, usedefs))
-          return false;
+        auto element = module.FindDef(instruction[elementIndex]);
+        if (!element || !IsTypeNullable(element->words(), module)) return false;
       }
       return true;
     }
@@ -679,8 +667,8 @@ template <>
 bool idUsage::isValid<SpvOpConstantNull>(const spv_instruction_t* inst,
                                          const spv_opcode_desc) {
   auto resultTypeIndex = 1;
-  auto resultType = usedefs_.FindDef(inst->words[resultTypeIndex]);
-  if (!resultType.first || !IsTypeNullable(resultType.second.words, usedefs_)) {
+  auto resultType = module_.FindDef(inst->words[resultTypeIndex]);
+  if (!resultType || !IsTypeNullable(resultType->words(), module_)) {
     DIAG(resultTypeIndex) << "OpConstantNull Result Type <id> '"
                           << inst->words[resultTypeIndex]
                           << "' cannot have a null value.";
@@ -693,8 +681,8 @@ template <>
 bool idUsage::isValid<SpvOpSpecConstantTrue>(const spv_instruction_t* inst,
                                              const spv_opcode_desc) {
   auto resultTypeIndex = 1;
-  auto resultType = usedefs_.FindDef(inst->words[resultTypeIndex]);
-  if (!resultType.first || SpvOpTypeBool != resultType.second.opcode) {
+  auto resultType = module_.FindDef(inst->words[resultTypeIndex]);
+  if (!resultType || SpvOpTypeBool != resultType->opcode()) {
     DIAG(resultTypeIndex) << "OpSpecConstantTrue Result Type <id> '"
                           << inst->words[resultTypeIndex]
                           << "' is not a boolean type.";
@@ -707,8 +695,8 @@ template <>
 bool idUsage::isValid<SpvOpSpecConstantFalse>(const spv_instruction_t* inst,
                                               const spv_opcode_desc) {
   auto resultTypeIndex = 1;
-  auto resultType = usedefs_.FindDef(inst->words[resultTypeIndex]);
-  if (!resultType.first || SpvOpTypeBool != resultType.second.opcode) {
+  auto resultType = module_.FindDef(inst->words[resultTypeIndex]);
+  if (!resultType || SpvOpTypeBool != resultType->opcode()) {
     DIAG(resultTypeIndex) << "OpSpecConstantFalse Result Type <id> '"
                           << inst->words[resultTypeIndex]
                           << "' is not a boolean type.";
@@ -732,8 +720,8 @@ template <>
 bool idUsage::isValid<SpvOpVariable>(const spv_instruction_t* inst,
                                      const spv_opcode_desc opcodeEntry) {
   auto resultTypeIndex = 1;
-  auto resultType = usedefs_.FindDef(inst->words[resultTypeIndex]);
-  if (!resultType.first || SpvOpTypePointer != resultType.second.opcode) {
+  auto resultType = module_.FindDef(inst->words[resultTypeIndex]);
+  if (!resultType || SpvOpTypePointer != resultType->opcode()) {
     DIAG(resultTypeIndex) << "OpVariable Result Type <id> '"
                           << inst->words[resultTypeIndex]
                           << "' is not a pointer type.";
@@ -741,8 +729,8 @@ bool idUsage::isValid<SpvOpVariable>(const spv_instruction_t* inst,
   }
   if (opcodeEntry->numTypes < inst->words.size()) {
     auto initialiserIndex = 4;
-    auto initialiser = usedefs_.FindDef(inst->words[initialiserIndex]);
-    if (!initialiser.first || !spvOpcodeIsConstant(initialiser.second.opcode)) {
+    auto initialiser = module_.FindDef(inst->words[initialiserIndex]);
+    if (!initialiser || !spvOpcodeIsConstant(initialiser->opcode())) {
       DIAG(initialiserIndex) << "OpVariable Initializer <id> '"
                              << inst->words[initialiserIndex]
                              << "' is not a constant.";
@@ -756,34 +744,32 @@ template <>
 bool idUsage::isValid<SpvOpLoad>(const spv_instruction_t* inst,
                                  const spv_opcode_desc) {
   auto resultTypeIndex = 1;
-  auto resultType = usedefs_.FindDef(inst->words[resultTypeIndex]);
-  spvCheck(!resultType.first, DIAG(resultTypeIndex)
-                                  << "OpLoad Result Type <id> '"
-                                  << inst->words[resultTypeIndex]
-                                  << "' is not defind.";
+  auto resultType = module_.FindDef(inst->words[resultTypeIndex]);
+  spvCheck(!resultType, DIAG(resultTypeIndex) << "OpLoad Result Type <id> '"
+                                              << inst->words[resultTypeIndex]
+                                              << "' is not defind.";
            return false);
   auto pointerIndex = 3;
-  auto pointer = usedefs_.FindDef(inst->words[pointerIndex]);
-  if (!pointer.first ||
-      (addressingModel == SpvAddressingModelLogical &&
-       !spvOpcodeReturnsLogicalPointer(pointer.second.opcode))) {
+  auto pointer = module_.FindDef(inst->words[pointerIndex]);
+  if (!pointer || (addressingModel == SpvAddressingModelLogical &&
+                   !spvOpcodeReturnsLogicalPointer(pointer->opcode()))) {
     DIAG(pointerIndex) << "OpLoad Pointer <id> '" << inst->words[pointerIndex]
                        << "' is not a pointer.";
     return false;
   }
-  auto pointerType = usedefs_.FindDef(pointer.second.type_id);
-  if (!pointerType.first || pointerType.second.opcode != SpvOpTypePointer) {
+  auto pointerType = module_.FindDef(pointer->type_id());
+  if (!pointerType || pointerType->opcode() != SpvOpTypePointer) {
     DIAG(pointerIndex) << "OpLoad type for pointer <id> '"
                        << inst->words[pointerIndex]
                        << "' is not a pointer type.";
     return false;
   }
-  auto pointeeType = usedefs_.FindDef(pointerType.second.words[3]);
-  if (!pointeeType.first || resultType.second.id != pointeeType.second.id) {
+  auto pointeeType = module_.FindDef(pointerType->words()[3]);
+  if (!pointeeType || resultType->id() != pointeeType->id()) {
     DIAG(resultTypeIndex) << "OpLoad Result Type <id> '"
                           << inst->words[resultTypeIndex]
-                          << "' does not match Pointer <id> '"
-                          << pointer.second.id << "'s type.";
+                          << "' does not match Pointer <id> '" << pointer->id()
+                          << "'s type.";
     return false;
   }
   return true;
@@ -793,47 +779,46 @@ template <>
 bool idUsage::isValid<SpvOpStore>(const spv_instruction_t* inst,
                                   const spv_opcode_desc) {
   auto pointerIndex = 1;
-  auto pointer = usedefs_.FindDef(inst->words[pointerIndex]);
-  if (!pointer.first ||
-      (addressingModel == SpvAddressingModelLogical &&
-       !spvOpcodeReturnsLogicalPointer(pointer.second.opcode))) {
+  auto pointer = module_.FindDef(inst->words[pointerIndex]);
+  if (!pointer || (addressingModel == SpvAddressingModelLogical &&
+                   !spvOpcodeReturnsLogicalPointer(pointer->opcode()))) {
     DIAG(pointerIndex) << "OpStore Pointer <id> '" << inst->words[pointerIndex]
                        << "' is not a pointer.";
     return false;
   }
-  auto pointerType = usedefs_.FindDef(pointer.second.type_id);
-  if (!pointer.first || pointerType.second.opcode != SpvOpTypePointer) {
+  auto pointerType = module_.FindDef(pointer->type_id());
+  if (!pointer || pointerType->opcode() != SpvOpTypePointer) {
     DIAG(pointerIndex) << "OpStore type for pointer <id> '"
                        << inst->words[pointerIndex]
                        << "' is not a pointer type.";
     return false;
   }
-  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.";
+  auto type = module_.FindDef(pointerType->words()[3]);
+  assert(type);
+  spvCheck(SpvOpTypeVoid == type->opcode(), DIAG(pointerIndex)
+                                                << "OpStore Pointer <id> '"
+                                                << inst->words[pointerIndex]
+                                                << "'s type is void.";
            return false);
 
   auto objectIndex = 2;
-  auto object = usedefs_.FindDef(inst->words[objectIndex]);
-  if (!object.first || !object.second.type_id) {
+  auto object = module_.FindDef(inst->words[objectIndex]);
+  if (!object || !object->type_id()) {
     DIAG(objectIndex) << "OpStore Object <id> '" << inst->words[objectIndex]
                       << "' is not an object.";
     return false;
   }
-  auto objectType = usedefs_.FindDef(object.second.type_id);
-  assert(objectType.first);
-  spvCheck(SpvOpTypeVoid == objectType.second.opcode,
+  auto objectType = module_.FindDef(object->type_id());
+  assert(objectType);
+  spvCheck(SpvOpTypeVoid == objectType->opcode(),
            DIAG(objectIndex) << "OpStore Object <id> '"
                              << inst->words[objectIndex] << "'s type is void.";
            return false);
 
-  spvCheck(type.second.id != objectType.second.id,
+  spvCheck(type->id() != objectType->id(),
            DIAG(pointerIndex)
                << "OpStore Pointer <id> '" << inst->words[pointerIndex]
-               << "'s type does not match Object <id> '" << objectType.second.id
+               << "'s type does not match Object <id> '" << objectType->id()
                << "'s type.";
            return false);
   return true;
@@ -843,23 +828,23 @@ template <>
 bool idUsage::isValid<SpvOpCopyMemory>(const spv_instruction_t* inst,
                                        const spv_opcode_desc) {
   auto targetIndex = 1;
-  auto target = usedefs_.FindDef(inst->words[targetIndex]);
-  if (!target.first) return false;
+  auto target = module_.FindDef(inst->words[targetIndex]);
+  if (!target) return false;
   auto sourceIndex = 2;
-  auto source = usedefs_.FindDef(inst->words[sourceIndex]);
-  if (!source.first) return false;
-  auto targetPointerType = usedefs_.FindDef(target.second.type_id);
-  assert(targetPointerType.first);
-  auto targetType = usedefs_.FindDef(targetPointerType.second.words[3]);
-  assert(targetType.first);
-  auto sourcePointerType = usedefs_.FindDef(source.second.type_id);
-  assert(sourcePointerType.first);
-  auto sourceType = usedefs_.FindDef(sourcePointerType.second.words[3]);
-  assert(sourceType.first);
-  spvCheck(targetType.second.id != sourceType.second.id,
+  auto source = module_.FindDef(inst->words[sourceIndex]);
+  if (!source) return false;
+  auto targetPointerType = module_.FindDef(target->type_id());
+  assert(targetPointerType);
+  auto targetType = module_.FindDef(targetPointerType->words()[3]);
+  assert(targetType);
+  auto sourcePointerType = module_.FindDef(source->type_id());
+  assert(sourcePointerType);
+  auto sourceType = module_.FindDef(sourcePointerType->words()[3]);
+  assert(sourceType);
+  spvCheck(targetType->id() != sourceType->id(),
            DIAG(sourceIndex)
                << "OpCopyMemory Target <id> '" << inst->words[sourceIndex]
-               << "'s type does not match Source <id> '" << sourceType.second.id
+               << "'s type does not match Source <id> '" << sourceType->id()
                << "'s type.";
            return false);
   return true;
@@ -869,47 +854,45 @@ template <>
 bool idUsage::isValid<SpvOpCopyMemorySized>(const spv_instruction_t* inst,
                                             const spv_opcode_desc) {
   auto targetIndex = 1;
-  auto target = usedefs_.FindDef(inst->words[targetIndex]);
-  if (!target.first) return false;
+  auto target = module_.FindDef(inst->words[targetIndex]);
+  if (!target) return false;
   auto sourceIndex = 2;
-  auto source = usedefs_.FindDef(inst->words[sourceIndex]);
-  if (!source.first) return false;
+  auto source = module_.FindDef(inst->words[sourceIndex]);
+  if (!source) return false;
   auto sizeIndex = 3;
-  auto size = usedefs_.FindDef(inst->words[sizeIndex]);
-  if (!size.first) return false;
-  auto targetPointerType = usedefs_.FindDef(target.second.type_id);
-  spvCheck(!targetPointerType.first ||
-               SpvOpTypePointer != targetPointerType.second.opcode,
-           DIAG(targetIndex) << "OpCopyMemorySized Target <id> '"
-                             << inst->words[targetIndex]
-                             << "' is not a pointer.";
-           return false);
-  auto sourcePointerType = usedefs_.FindDef(source.second.type_id);
-  spvCheck(!sourcePointerType.first ||
-               SpvOpTypePointer != sourcePointerType.second.opcode,
-           DIAG(sourceIndex) << "OpCopyMemorySized Source <id> '"
-                             << inst->words[sourceIndex]
-                             << "' is not a pointer.";
-           return false);
-  switch (size.second.opcode) {
+  auto size = module_.FindDef(inst->words[sizeIndex]);
+  if (!size) return false;
+  auto targetPointerType = module_.FindDef(target->type_id());
+  spvCheck(
+      !targetPointerType || SpvOpTypePointer != targetPointerType->opcode(),
+      DIAG(targetIndex) << "OpCopyMemorySized Target <id> '"
+                        << inst->words[targetIndex] << "' is not a pointer.";
+      return false);
+  auto sourcePointerType = module_.FindDef(source->type_id());
+  spvCheck(
+      !sourcePointerType || SpvOpTypePointer != sourcePointerType->opcode(),
+      DIAG(sourceIndex) << "OpCopyMemorySized Source <id> '"
+                        << inst->words[sourceIndex] << "' is not a pointer.";
+      return false);
+  switch (size->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 = usedefs_.FindDef(size.second.type_id);
-      assert(sizeType.first);
-      spvCheck(SpvOpTypeInt != sizeType.second.opcode,
+      auto sizeType = module_.FindDef(size->type_id());
+      assert(sizeType);
+      spvCheck(SpvOpTypeInt != sizeType->opcode(),
                DIAG(sizeIndex) << "OpCopyMemorySized Size <id> '"
                                << inst->words[sizeIndex]
                                << "'s type is not an integer type.";
                return false);
     } break;
     case SpvOpVariable: {
-      auto pointerType = usedefs_.FindDef(size.second.type_id);
-      assert(pointerType.first);
-      auto sizeType = usedefs_.FindDef(pointerType.second.type_id);
-      spvCheck(!sizeType.first || SpvOpTypeInt != sizeType.second.opcode,
+      auto pointerType = module_.FindDef(size->type_id());
+      assert(pointerType);
+      auto sizeType = module_.FindDef(pointerType->type_id());
+      spvCheck(!sizeType || SpvOpTypeInt != sizeType->opcode(),
                DIAG(sizeIndex) << "OpCopyMemorySized Size <id> '"
                                << inst->words[sizeIndex]
                                << "'s variable type is not an integer type.";
@@ -960,23 +943,23 @@ template <>
 bool idUsage::isValid<SpvOpFunction>(const spv_instruction_t* inst,
                                      const spv_opcode_desc) {
   auto resultTypeIndex = 1;
-  auto resultType = usedefs_.FindDef(inst->words[resultTypeIndex]);
-  if (!resultType.first) return false;
+  auto resultType = module_.FindDef(inst->words[resultTypeIndex]);
+  if (!resultType) return false;
   auto functionTypeIndex = 4;
-  auto functionType = usedefs_.FindDef(inst->words[functionTypeIndex]);
-  if (!functionType.first || SpvOpTypeFunction != functionType.second.opcode) {
+  auto functionType = module_.FindDef(inst->words[functionTypeIndex]);
+  if (!functionType || SpvOpTypeFunction != functionType->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,
+  auto returnType = module_.FindDef(functionType->words()[2]);
+  assert(returnType);
+  spvCheck(returnType->id() != resultType->id(),
            DIAG(resultTypeIndex) << "OpFunction Result Type <id> '"
                                  << inst->words[resultTypeIndex]
                                  << "' does not match the Function Type <id> '"
-                                 << resultType.second.id << "'s return type.";
+                                 << resultType->id() << "'s return type.";
            return false);
   return true;
 }
@@ -985,8 +968,8 @@ template <>
 bool idUsage::isValid<SpvOpFunctionParameter>(const spv_instruction_t* inst,
                                               const spv_opcode_desc) {
   auto resultTypeIndex = 1;
-  auto resultType = usedefs_.FindDef(inst->words[resultTypeIndex]);
-  if (!resultType.first) return false;
+  auto resultType = module_.FindDef(inst->words[resultTypeIndex]);
+  if (!resultType) return false;
   // NOTE: Find OpFunction & ensure OpFunctionParameter is not out of place.
   size_t paramIndex = 0;
   assert(firstInst < inst && "Invalid instruction pointer");
@@ -997,17 +980,17 @@ bool idUsage::isValid<SpvOpFunctionParameter>(const spv_instruction_t* inst,
       paramIndex++;
     }
   }
-  auto functionType = usedefs_.FindDef(inst->words[4]);
-  assert(functionType.first);
-  if (paramIndex >= functionType.second.words.size() - 3) {
+  auto functionType = module_.FindDef(inst->words[4]);
+  assert(functionType);
+  if (paramIndex >= functionType->words().size() - 3) {
     DIAG(0) << "Too many OpFunctionParameters for " << inst->words[2]
-            << ": expected " << functionType.second.words.size() - 3
+            << ": expected " << functionType->words().size() - 3
             << " based on the function's type";
     return false;
   }
-  auto paramType = usedefs_.FindDef(functionType.second.words[paramIndex + 3]);
-  assert(paramType.first);
-  spvCheck(resultType.second.id != paramType.second.id,
+  auto paramType = module_.FindDef(functionType->words()[paramIndex + 3]);
+  assert(paramType);
+  spvCheck(resultType->id() != paramType->id(),
            DIAG(resultTypeIndex)
                << "OpFunctionParameter Result Type <id> '"
                << inst->words[resultTypeIndex]
@@ -1021,27 +1004,27 @@ template <>
 bool idUsage::isValid<SpvOpFunctionCall>(const spv_instruction_t* inst,
                                          const spv_opcode_desc) {
   auto resultTypeIndex = 1;
-  auto resultType = usedefs_.FindDef(inst->words[resultTypeIndex]);
-  if (!resultType.first) return false;
+  auto resultType = module_.FindDef(inst->words[resultTypeIndex]);
+  if (!resultType) return false;
   auto functionIndex = 3;
-  auto function = usedefs_.FindDef(inst->words[functionIndex]);
-  if (!function.first || SpvOpFunction != function.second.opcode) {
+  auto function = module_.FindDef(inst->words[functionIndex]);
+  if (!function || SpvOpFunction != function->opcode()) {
     DIAG(functionIndex) << "OpFunctionCall Function <id> '"
                         << inst->words[functionIndex] << "' is not a function.";
     return false;
   }
-  auto returnType = usedefs_.FindDef(function.second.type_id);
-  assert(returnType.first);
-  spvCheck(returnType.second.id != resultType.second.id,
+  auto returnType = module_.FindDef(function->type_id());
+  assert(returnType);
+  spvCheck(returnType->id() != resultType->id(),
            DIAG(resultTypeIndex) << "OpFunctionCall Result Type <id> '"
                                  << inst->words[resultTypeIndex]
                                  << "'s type does not match Function <id> '"
-                                 << returnType.second.id << "'s return type.";
+                                 << returnType->id() << "'s return type.";
            return false);
-  auto functionType = usedefs_.FindDef(function.second.words[4]);
-  assert(functionType.first);
+  auto functionType = module_.FindDef(function->words()[4]);
+  assert(functionType);
   auto functionCallArgCount = inst->words.size() - 4;
-  auto functionParamCount = functionType.second.words.size() - 3;
+  auto functionParamCount = functionType->words().size() - 3;
   spvCheck(
       functionParamCount != functionCallArgCount,
       DIAG(inst->words.size() - 1)
@@ -1050,19 +1033,17 @@ 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 = usedefs_.FindDef(inst->words[argumentIndex]);
-    if (!argument.first) return false;
-    auto argumentType = usedefs_.FindDef(argument.second.type_id);
-    assert(argumentType.first);
-    auto parameterType =
-        usedefs_.FindDef(functionType.second.words[paramIndex]);
-    assert(parameterType.first);
-    spvCheck(argumentType.second.id != parameterType.second.id,
+    auto argument = module_.FindDef(inst->words[argumentIndex]);
+    if (!argument) return false;
+    auto argumentType = module_.FindDef(argument->type_id());
+    assert(argumentType);
+    auto parameterType = module_.FindDef(functionType->words()[paramIndex]);
+    assert(parameterType);
+    spvCheck(argumentType->id() != parameterType->id(),
              DIAG(argumentIndex) << "OpFunctionCall Argument <id> '"
                                  << inst->words[argumentIndex]
                                  << "'s type does not match Function <id> '"
-                                 << parameterType.second.id
-                                 << "'s parameter type.";
+                                 << parameterType->id() << "'s parameter type.";
              return false);
   }
   return true;
@@ -1698,22 +1679,22 @@ template <>
 bool idUsage::isValid<SpvOpReturnValue>(const spv_instruction_t* inst,
                                         const spv_opcode_desc) {
   auto valueIndex = 1;
-  auto value = usedefs_.FindDef(inst->words[valueIndex]);
-  if (!value.first || !value.second.type_id) {
+  auto value = module_.FindDef(inst->words[valueIndex]);
+  if (!value || !value->type_id()) {
     DIAG(valueIndex) << "OpReturnValue Value <id> '" << inst->words[valueIndex]
                      << "' does not represent a value.";
     return false;
   }
-  auto valueType = usedefs_.FindDef(value.second.type_id);
-  if (!valueType.first || SpvOpTypeVoid == valueType.second.opcode) {
-    DIAG(valueIndex) << "OpReturnValue value's type <id> '"
-                     << value.second.type_id << "' is missing or void.";
+  auto valueType = module_.FindDef(value->type_id());
+  if (!valueType || SpvOpTypeVoid == valueType->opcode()) {
+    DIAG(valueIndex) << "OpReturnValue value's type <id> '" << value->type_id()
+                     << "' is missing or void.";
     return false;
   }
   if (addressingModel == SpvAddressingModelLogical &&
-      SpvOpTypePointer == valueType.second.opcode) {
+      SpvOpTypePointer == valueType->opcode()) {
     DIAG(valueIndex)
-        << "OpReturnValue value's type <id> '" << value.second.type_id
+        << "OpReturnValue value's type <id> '" << value->type_id()
         << "' is a pointer, which is invalid in the Logical addressing model.";
     return false;
   }
@@ -1726,8 +1707,8 @@ 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 = usedefs_.FindDef(function->words[1]);
-  spvCheck(!returnType.first || returnType.second.id != valueType.second.id,
+  auto returnType = module_.FindDef(function->words[1]);
+  spvCheck(!returnType || returnType->id() != valueType->id(),
            DIAG(valueIndex)
                << "OpReturnValue Value <id> '" << inst->words[valueIndex]
                << "'s type does not match OpFunction's return type.";
@@ -2338,8 +2319,114 @@ bool idUsage::isValid(const spv_instruction_t* inst) {
 #undef TODO
 #undef CASE
 }
+// This function takes the opcode of an instruction and returns
+// a function object that will return true if the index
+// of the operand can be forwarad declared. This function will
+// used in the SSA validation stage of the pipeline
+function<bool(unsigned)> getCanBeForwardDeclaredFunction(SpvOp opcode) {
+  function<bool(unsigned index)> out;
+  switch (opcode) {
+    case SpvOpExecutionMode:
+    case SpvOpEntryPoint:
+    case SpvOpName:
+    case SpvOpMemberName:
+    case SpvOpSelectionMerge:
+    case SpvOpDecorate:
+    case SpvOpMemberDecorate:
+    case SpvOpBranch:
+    case SpvOpLoopMerge:
+      out = [](unsigned) { return true; };
+      break;
+    case SpvOpGroupDecorate:
+    case SpvOpGroupMemberDecorate:
+    case SpvOpBranchConditional:
+    case SpvOpSwitch:
+      out = [](unsigned index) { return index != 0; };
+      break;
+
+    case SpvOpFunctionCall:
+      out = [](unsigned index) { return index == 2; };
+      break;
+
+    case SpvOpPhi:
+      out = [](unsigned index) { return index > 1; };
+      break;
+
+    case SpvOpEnqueueKernel:
+      out = [](unsigned index) { return index == 8; };
+      break;
+
+    case SpvOpGetKernelNDrangeSubGroupCount:
+    case SpvOpGetKernelNDrangeMaxSubGroupSize:
+      out = [](unsigned index) { return index == 3; };
+      break;
+
+    case SpvOpGetKernelWorkGroupSize:
+    case SpvOpGetKernelPreferredWorkGroupSizeMultiple:
+      out = [](unsigned index) { return index == 2; };
+      break;
+
+    default:
+      out = [](unsigned) { return false; };
+      break;
+  }
+  return out;
+}
 }  // anonymous namespace
 
+namespace libspirv {
+
+// Performs SSA validation on the IDs of an instruction. The
+// can_have_forward_declared_ids  functor should return true if the
+// instruction operand's ID can be forward referenced.
+//
+// TODO(umar): Use dominators to correctly validate SSA. For example, the result
+// id from a 'then' block cannot dominate its usage in the 'else' block. This
+// is not yet performed by this function.
+spv_result_t IdPass(ValidationState_t& _,
+                    const spv_parsed_instruction_t* inst) {
+  auto can_have_forward_declared_ids =
+      getCanBeForwardDeclaredFunction(static_cast<SpvOp>(inst->opcode));
+
+  for (unsigned i = 0; i < inst->num_operands; i++) {
+    const spv_parsed_operand_t& operand = inst->operands[i];
+    const spv_operand_type_t& type = operand.type;
+    const uint32_t* operand_ptr = inst->words + operand.offset;
+
+    auto ret = SPV_ERROR_INTERNAL;
+    switch (type) {
+      case SPV_OPERAND_TYPE_RESULT_ID:
+        _.RemoveIfForwardDeclared(*operand_ptr);
+        _.AddId(*inst);
+        ret = SPV_SUCCESS;
+        break;
+      case SPV_OPERAND_TYPE_ID:
+      case SPV_OPERAND_TYPE_TYPE_ID:
+      case SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID:
+      case SPV_OPERAND_TYPE_SCOPE_ID:
+        if (_.IsDefinedId(*operand_ptr)) {
+          _.RegisterUseId(*operand_ptr);
+          ret = SPV_SUCCESS;
+        } else if (can_have_forward_declared_ids(i)) {
+          ret = _.ForwardDeclareId(*operand_ptr);
+        } else {
+          ret = _.diag(SPV_ERROR_INVALID_ID) << "ID "
+                                             << _.getIdName(*operand_ptr)
+                                             << " has not been defined";
+        }
+        break;
+      default:
+        ret = SPV_SUCCESS;
+        break;
+    }
+    if (SPV_SUCCESS != ret) {
+      return ret;
+    }
+  }
+  return SPV_SUCCESS;
+}
+}  // namespace libspirv
+
 spv_result_t spvValidateInstructionIDs(const spv_instruction_t* pInsts,
                                        const uint64_t instCount,
                                        const spv_opcode_table opcodeTable,
@@ -2349,8 +2436,8 @@ spv_result_t spvValidateInstructionIDs(const spv_instruction_t* pInsts,
                                        spv_position position,
                                        spv_diagnostic* pDiag) {
   idUsage idUsage(opcodeTable, operandTable, extInstTable, pInsts, instCount,
-                  state.memory_model(), state.addressing_model(),
-                  state.usedefs(), state.entry_points(), position, pDiag);
+                  state.memory_model(), state.addressing_model(), state,
+                  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();
diff --git a/source/validate_ssa.cpp b/source/validate_ssa.cpp
deleted file mode 100644 (file)
index 914d5ca..0000000
+++ /dev/null
@@ -1,142 +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.
-
-#include "validate.h"
-
-#include <functional>
-
-#include "opcode.h"
-#include "val/ValidationState.h"
-
-using std::function;
-using libspirv::ValidationState_t;
-
-namespace {
-// This function takes the opcode of an instruction and returns
-// a function object that will return true if the index
-// of the operand can be forwarad declared. This function will
-// used in the SSA validation stage of the pipeline
-function<bool(unsigned)> getCanBeForwardDeclaredFunction(SpvOp opcode) {
-  function<bool(unsigned index)> out;
-  switch (opcode) {
-    case SpvOpExecutionMode:
-    case SpvOpEntryPoint:
-    case SpvOpName:
-    case SpvOpMemberName:
-    case SpvOpSelectionMerge:
-    case SpvOpDecorate:
-    case SpvOpMemberDecorate:
-    case SpvOpBranch:
-    case SpvOpLoopMerge:
-      out = [](unsigned) { return true; };
-      break;
-    case SpvOpGroupDecorate:
-    case SpvOpGroupMemberDecorate:
-    case SpvOpBranchConditional:
-    case SpvOpSwitch:
-      out = [](unsigned index) { return index != 0; };
-      break;
-
-    case SpvOpFunctionCall:
-      out = [](unsigned index) { return index == 2; };
-      break;
-
-    case SpvOpPhi:
-      out = [](unsigned index) { return index > 1; };
-      break;
-
-    case SpvOpEnqueueKernel:
-      out = [](unsigned index) { return index == 8; };
-      break;
-
-    case SpvOpGetKernelNDrangeSubGroupCount:
-    case SpvOpGetKernelNDrangeMaxSubGroupSize:
-      out = [](unsigned index) { return index == 3; };
-      break;
-
-    case SpvOpGetKernelWorkGroupSize:
-    case SpvOpGetKernelPreferredWorkGroupSizeMultiple:
-      out = [](unsigned index) { return index == 2; };
-      break;
-
-    default:
-      out = [](unsigned) { return false; };
-      break;
-  }
-  return out;
-}
-}  // namespace
-
-namespace libspirv {
-
-// Performs SSA validation on the IDs of an instruction. The
-// can_have_forward_declared_ids  functor should return true if the
-// instruction operand's ID can be forward referenced.
-//
-// TODO(umar): Use dominators to correctly validate SSA. For example, the result
-// id from a 'then' block cannot dominate its usage in the 'else' block. This
-// is not yet performed by this function.
-spv_result_t SsaPass(ValidationState_t& _,
-                     const spv_parsed_instruction_t* inst) {
-  auto can_have_forward_declared_ids =
-      getCanBeForwardDeclaredFunction(static_cast<SpvOp>(inst->opcode));
-
-  for (unsigned i = 0; i < inst->num_operands; i++) {
-    const spv_parsed_operand_t& operand = inst->operands[i];
-    const spv_operand_type_t& type = operand.type;
-    const uint32_t* operand_ptr = inst->words + operand.offset;
-
-    auto ret = SPV_ERROR_INTERNAL;
-    switch (type) {
-      case SPV_OPERAND_TYPE_RESULT_ID:
-        _.RemoveIfForwardDeclared(*operand_ptr);
-        ret = SPV_SUCCESS;
-        break;
-      case SPV_OPERAND_TYPE_ID:
-      case SPV_OPERAND_TYPE_TYPE_ID:
-      case SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID:
-      case SPV_OPERAND_TYPE_SCOPE_ID:
-        if (_.IsDefinedId(*operand_ptr)) {
-          ret = SPV_SUCCESS;
-        } else if (can_have_forward_declared_ids(i)) {
-          ret = _.ForwardDeclareId(*operand_ptr);
-        } else {
-          ret = _.diag(SPV_ERROR_INVALID_ID) << "ID "
-                                             << _.getIdName(*operand_ptr)
-                                             << " has not been defined";
-        }
-        break;
-      default:
-        ret = SPV_SUCCESS;
-        break;
-    }
-    if (SPV_SUCCESS != ret) {
-      return ret;
-    }
-  }
-  return SPV_SUCCESS;
-}
-}  // namespace libspirv
index 09f0533..03d7940 100644 (file)
 #include <utility>
 
 using ::testing::HasSubstr;
+using ::testing::MatchesRegex;
 
 using std::string;
 using std::pair;
 using std::stringstream;
 
 namespace {
-using Validate = spvtest::ValidateBase<pair<string, bool>>;
+using ValidateSSA = spvtest::ValidateBase<pair<string, bool>>;
 
-TEST_F(Validate, Default) {
+TEST_F(ValidateSSA, Default) {
   char str[] = R"(
      OpCapability Shader
      OpMemoryModel Logical GLSL450
@@ -60,7 +61,7 @@ TEST_F(Validate, Default) {
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
-TEST_F(Validate, IdUndefinedBad) {
+TEST_F(ValidateSSA, IdUndefinedBad) {
   char str[] = R"(
           OpCapability Shader
           OpMemoryModel Logical GLSL450
@@ -77,7 +78,7 @@ TEST_F(Validate, IdUndefinedBad) {
   EXPECT_THAT(getDiagnosticString(), HasSubstr("missing"));
 }
 
-TEST_F(Validate, IdRedefinedBad) {
+TEST_F(ValidateSSA, IdRedefinedBad) {
   char str[] = R"(
      OpCapability Shader
      OpMemoryModel Logical GLSL450
@@ -93,7 +94,7 @@ TEST_F(Validate, IdRedefinedBad) {
   ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
 }
 
-TEST_F(Validate, DominateUsageBad) {
+TEST_F(ValidateSSA, DominateUsageBad) {
   char str[] = R"(
      OpCapability Shader
      OpMemoryModel Logical GLSL450
@@ -106,7 +107,7 @@ TEST_F(Validate, DominateUsageBad) {
   EXPECT_THAT(getDiagnosticString(), HasSubstr("not_dominant"));
 }
 
-TEST_F(Validate, ForwardNameGood) {
+TEST_F(ValidateSSA, ForwardNameGood) {
   char str[] = R"(
      OpCapability Shader
      OpMemoryModel Logical GLSL450
@@ -122,7 +123,7 @@ TEST_F(Validate, ForwardNameGood) {
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
-TEST_F(Validate, ForwardNameMissingTargetBad) {
+TEST_F(ValidateSSA, ForwardNameMissingTargetBad) {
   char str[] = R"(
       OpCapability Shader
       OpMemoryModel Logical GLSL450
@@ -133,7 +134,7 @@ TEST_F(Validate, ForwardNameMissingTargetBad) {
   EXPECT_THAT(getDiagnosticString(), HasSubstr("main"));
 }
 
-TEST_F(Validate, ForwardMemberNameGood) {
+TEST_F(ValidateSSA, ForwardMemberNameGood) {
   char str[] = R"(
            OpCapability Shader
            OpMemoryModel Logical GLSL450
@@ -147,7 +148,7 @@ TEST_F(Validate, ForwardMemberNameGood) {
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
-TEST_F(Validate, ForwardMemberNameMissingTargetBad) {
+TEST_F(ValidateSSA, ForwardMemberNameMissingTargetBad) {
   char str[] = R"(
            OpCapability Shader
            OpMemoryModel Logical GLSL450
@@ -162,7 +163,7 @@ TEST_F(Validate, ForwardMemberNameMissingTargetBad) {
   EXPECT_THAT(getDiagnosticString(), HasSubstr("size"));
 }
 
-TEST_F(Validate, ForwardDecorateGood) {
+TEST_F(ValidateSSA, ForwardDecorateGood) {
   char str[] = R"(
            OpCapability Shader
            OpMemoryModel Logical GLSL450
@@ -175,7 +176,7 @@ TEST_F(Validate, ForwardDecorateGood) {
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
-TEST_F(Validate, ForwardDecorateInvalidIDBad) {
+TEST_F(ValidateSSA, ForwardDecorateInvalidIDBad) {
   char str[] = R"(
            OpCapability Shader
            OpMemoryModel Logical GLSL450
@@ -196,7 +197,7 @@ TEST_F(Validate, ForwardDecorateInvalidIDBad) {
   EXPECT_THAT(getDiagnosticString(), HasSubstr("missing"));
 }
 
-TEST_F(Validate, ForwardMemberDecorateGood) {
+TEST_F(ValidateSSA, ForwardMemberDecorateGood) {
   char str[] = R"(
            OpCapability Shader
            OpMemoryModel Logical GLSL450
@@ -210,7 +211,7 @@ TEST_F(Validate, ForwardMemberDecorateGood) {
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
-TEST_F(Validate, ForwardMemberDecorateInvalidIdBad) {
+TEST_F(ValidateSSA, ForwardMemberDecorateInvalidIdBad) {
   char str[] = R"(
            OpCapability Shader
            OpMemoryModel Logical GLSL450
@@ -226,7 +227,7 @@ TEST_F(Validate, ForwardMemberDecorateInvalidIdBad) {
   EXPECT_THAT(getDiagnosticString(), HasSubstr("missing"));
 }
 
-TEST_F(Validate, ForwardGroupDecorateGood) {
+TEST_F(ValidateSSA, ForwardGroupDecorateGood) {
   char str[] = R"(
           OpCapability Shader
           OpMemoryModel Logical GLSL450
@@ -243,7 +244,7 @@ TEST_F(Validate, ForwardGroupDecorateGood) {
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
-TEST_F(Validate, ForwardGroupDecorateMissingGroupBad) {
+TEST_F(ValidateSSA, ForwardGroupDecorateMissingGroupBad) {
   char str[] = R"(
            OpCapability Shader
            OpMemoryModel Logical GLSL450
@@ -262,7 +263,7 @@ TEST_F(Validate, ForwardGroupDecorateMissingGroupBad) {
   EXPECT_THAT(getDiagnosticString(), HasSubstr("missing"));
 }
 
-TEST_F(Validate, ForwardGroupDecorateMissingTargetBad) {
+TEST_F(ValidateSSA, ForwardGroupDecorateMissingTargetBad) {
   char str[] = R"(
            OpCapability Shader
            OpMemoryModel Logical GLSL450
@@ -281,7 +282,7 @@ TEST_F(Validate, ForwardGroupDecorateMissingTargetBad) {
   EXPECT_THAT(getDiagnosticString(), HasSubstr("missing"));
 }
 
-TEST_F(Validate, ForwardGroupDecorateDecorationGroupDominateBad) {
+TEST_F(ValidateSSA, ForwardGroupDecorateDecorationGroupDominateBad) {
   char str[] = R"(
            OpCapability Shader
            OpMemoryModel Logical GLSL450
@@ -300,7 +301,7 @@ TEST_F(Validate, ForwardGroupDecorateDecorationGroupDominateBad) {
   EXPECT_THAT(getDiagnosticString(), HasSubstr("group"));
 }
 
-TEST_F(Validate, ForwardDecorateInvalidIdBad) {
+TEST_F(ValidateSSA, ForwardDecorateInvalidIdBad) {
   char str[] = R"(
            OpCapability Shader
            OpMemoryModel Logical GLSL450
@@ -321,7 +322,7 @@ TEST_F(Validate, ForwardDecorateInvalidIdBad) {
   EXPECT_THAT(getDiagnosticString(), HasSubstr("missing"));
 }
 
-TEST_F(Validate, FunctionCallGood) {
+TEST_F(ValidateSSA, FunctionCallGood) {
   char str[] = R"(
          OpCapability Shader
          OpMemoryModel Logical GLSL450
@@ -348,7 +349,7 @@ TEST_F(Validate, FunctionCallGood) {
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
-TEST_F(Validate, ForwardFunctionCallGood) {
+TEST_F(ValidateSSA, ForwardFunctionCallGood) {
   char str[] = R"(
          OpCapability Shader
          OpMemoryModel Logical GLSL450
@@ -375,7 +376,7 @@ TEST_F(Validate, ForwardFunctionCallGood) {
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
-TEST_F(Validate, ForwardBranchConditionalGood) {
+TEST_F(ValidateSSA, ForwardBranchConditionalGood) {
   char str[] = R"(
             OpCapability Shader
             OpMemoryModel Logical GLSL450
@@ -401,7 +402,7 @@ TEST_F(Validate, ForwardBranchConditionalGood) {
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
-TEST_F(Validate, ForwardBranchConditionalWithWeightsGood) {
+TEST_F(ValidateSSA, ForwardBranchConditionalWithWeightsGood) {
   char str[] = R"(
            OpCapability Shader
            OpMemoryModel Logical GLSL450
@@ -427,7 +428,7 @@ TEST_F(Validate, ForwardBranchConditionalWithWeightsGood) {
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
-TEST_F(Validate, ForwardBranchConditionalNonDominantConditionBad) {
+TEST_F(ValidateSSA, ForwardBranchConditionalNonDominantConditionBad) {
   char str[] = R"(
            OpCapability Shader
            OpMemoryModel Logical GLSL450
@@ -456,7 +457,7 @@ TEST_F(Validate, ForwardBranchConditionalNonDominantConditionBad) {
   EXPECT_THAT(getDiagnosticString(), HasSubstr("conditional"));
 }
 
-TEST_F(Validate, ForwardBranchConditionalMissingTargetBad) {
+TEST_F(ValidateSSA, ForwardBranchConditionalMissingTargetBad) {
   char str[] = R"(
            OpCapability Shader
            OpMemoryModel Logical GLSL450
@@ -497,6 +498,10 @@ const string kBasicTypes = R"(
 %intt   =  OpTypeInt 32 1
 %uintt  =  OpTypeInt 32 0
 %vfunct =  OpTypeFunction %voidt
+%intptrt = OpTypePointer UniformConstant %intt
+%zero      = OpConstant %intt 0
+%one       = OpConstant %intt 1
+%ten       = OpConstant %intt 10
 )";
 
 const string kKernelTypesAndConstants = R"(
@@ -507,7 +512,6 @@ const string kKernelTypesAndConstants = R"(
 %ndt     = OpTypeStruct %intt %arr3t %arr3t %arr3t
 
 %eventt  = OpTypeEvent
-%intptrt = OpTypePointer UniformConstant %int8t
 
 %offset = OpConstant %intt 0
 %local  = OpConstant %intt 1
@@ -541,7 +545,7 @@ const string kKernelDefinition = R"(
           OpFunctionEnd
 )";
 
-TEST_F(Validate, EnqueueKernelGood) {
+TEST_F(ValidateSSA, EnqueueKernelGood) {
   string str = kHeader + kBasicTypes + kKernelTypesAndConstants +
                kKernelDefinition + R"(
                 %main   = OpFunction %voidt None %vfunct
@@ -558,7 +562,7 @@ TEST_F(Validate, EnqueueKernelGood) {
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
-TEST_F(Validate, ForwardEnqueueKernelGood) {
+TEST_F(ValidateSSA, ForwardEnqueueKernelGood) {
   string str = kHeader + kBasicTypes + kKernelTypesAndConstants + R"(
                 %main   = OpFunction %voidt None %vfunct
                 %mainl  = OpLabel
@@ -575,7 +579,7 @@ TEST_F(Validate, ForwardEnqueueKernelGood) {
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
-TEST_F(Validate, EnqueueMissingFunctionBad) {
+TEST_F(ValidateSSA, EnqueueMissingFunctionBad) {
   string str = kHeader + "OpName %kfunc \"kfunc\"" + kBasicTypes +
                kKernelTypesAndConstants + R"(
                 %main   = OpFunction %voidt None %vfunct
@@ -610,7 +614,7 @@ string forwardKernelNonDominantParameterBaseCode(string name = string()) {
   return out;
 }
 
-TEST_F(Validate, ForwardEnqueueKernelMissingParameter1Bad) {
+TEST_F(ValidateSSA, ForwardEnqueueKernelMissingParameter1Bad) {
   string str = forwardKernelNonDominantParameterBaseCode("missing") + R"(
                 %err    = OpEnqueueKernel %missing %dqueue %flags %ndval
                                         %nevent %event %revent %kfunc %firstp
@@ -623,7 +627,7 @@ TEST_F(Validate, ForwardEnqueueKernelMissingParameter1Bad) {
   EXPECT_THAT(getDiagnosticString(), HasSubstr("missing"));
 }
 
-TEST_F(Validate, ForwardEnqueueKernelNonDominantParameter2Bad) {
+TEST_F(ValidateSSA, ForwardEnqueueKernelNonDominantParameter2Bad) {
   string str = forwardKernelNonDominantParameterBaseCode("dqueue2") + R"(
                 %err     = OpEnqueueKernel %uintt %dqueue2 %flags %ndval
                                             %nevent %event %revent %kfunc
@@ -637,7 +641,7 @@ TEST_F(Validate, ForwardEnqueueKernelNonDominantParameter2Bad) {
   EXPECT_THAT(getDiagnosticString(), HasSubstr("dqueue2"));
 }
 
-TEST_F(Validate, ForwardEnqueueKernelNonDominantParameter3Bad) {
+TEST_F(ValidateSSA, ForwardEnqueueKernelNonDominantParameter3Bad) {
   string str = forwardKernelNonDominantParameterBaseCode("ndval2") + R"(
                 %err    = OpEnqueueKernel %uintt %dqueue %flags %ndval2
                                         %nevent %event %revent %kfunc %firstp
@@ -651,7 +655,7 @@ TEST_F(Validate, ForwardEnqueueKernelNonDominantParameter3Bad) {
   EXPECT_THAT(getDiagnosticString(), HasSubstr("ndval2"));
 }
 
-TEST_F(Validate, ForwardEnqueueKernelNonDominantParameter4Bad) {
+TEST_F(ValidateSSA, ForwardEnqueueKernelNonDominantParameter4Bad) {
   string str = forwardKernelNonDominantParameterBaseCode("nevent2") + R"(
               %err    = OpEnqueueKernel %uintt %dqueue %flags %ndval %nevent2
                                         %event %revent %kfunc %firstp %psize
@@ -665,7 +669,7 @@ TEST_F(Validate, ForwardEnqueueKernelNonDominantParameter4Bad) {
   EXPECT_THAT(getDiagnosticString(), HasSubstr("nevent2"));
 }
 
-TEST_F(Validate, ForwardEnqueueKernelNonDominantParameter5Bad) {
+TEST_F(ValidateSSA, ForwardEnqueueKernelNonDominantParameter5Bad) {
   string str = forwardKernelNonDominantParameterBaseCode("event2") + R"(
               %err     = OpEnqueueKernel %uintt %dqueue %flags %ndval %nevent
                                         %event2 %revent %kfunc %firstp %psize
@@ -679,7 +683,7 @@ TEST_F(Validate, ForwardEnqueueKernelNonDominantParameter5Bad) {
   EXPECT_THAT(getDiagnosticString(), HasSubstr("event2"));
 }
 
-TEST_F(Validate, ForwardEnqueueKernelNonDominantParameter6Bad) {
+TEST_F(ValidateSSA, ForwardEnqueueKernelNonDominantParameter6Bad) {
   string str = forwardKernelNonDominantParameterBaseCode("revent2") + R"(
               %err     = OpEnqueueKernel %uintt %dqueue %flags %ndval %nevent
                                         %event %revent2 %kfunc %firstp %psize
@@ -693,7 +697,7 @@ TEST_F(Validate, ForwardEnqueueKernelNonDominantParameter6Bad) {
   EXPECT_THAT(getDiagnosticString(), HasSubstr("revent2"));
 }
 
-TEST_F(Validate, ForwardEnqueueKernelNonDominantParameter8Bad) {
+TEST_F(ValidateSSA, ForwardEnqueueKernelNonDominantParameter8Bad) {
   string str = forwardKernelNonDominantParameterBaseCode("firstp2") + R"(
               %err     = OpEnqueueKernel %uintt %dqueue %flags %ndval %nevent
                                         %event %revent %kfunc %firstp2 %psize
@@ -707,7 +711,7 @@ TEST_F(Validate, ForwardEnqueueKernelNonDominantParameter8Bad) {
   EXPECT_THAT(getDiagnosticString(), HasSubstr("firstp2"));
 }
 
-TEST_F(Validate, ForwardEnqueueKernelNonDominantParameter9Bad) {
+TEST_F(ValidateSSA, ForwardEnqueueKernelNonDominantParameter9Bad) {
   string str = forwardKernelNonDominantParameterBaseCode("psize2") + R"(
               %err    = OpEnqueueKernel %uintt %dqueue %flags %ndval %nevent
                                         %event %revent %kfunc %firstp %psize2
@@ -721,7 +725,7 @@ TEST_F(Validate, ForwardEnqueueKernelNonDominantParameter9Bad) {
   EXPECT_THAT(getDiagnosticString(), HasSubstr("psize2"));
 }
 
-TEST_F(Validate, ForwardEnqueueKernelNonDominantParameter10Bad) {
+TEST_F(ValidateSSA, ForwardEnqueueKernelNonDominantParameter10Bad) {
   string str = forwardKernelNonDominantParameterBaseCode("palign2") + R"(
               %err     = OpEnqueueKernel %uintt %dqueue %flags %ndval %nevent
                                         %event %revent %kfunc %firstp %psize
@@ -735,7 +739,7 @@ TEST_F(Validate, ForwardEnqueueKernelNonDominantParameter10Bad) {
   EXPECT_THAT(getDiagnosticString(), HasSubstr("palign2"));
 }
 
-TEST_F(Validate, ForwardEnqueueKernelNonDominantParameter11Bad) {
+TEST_F(ValidateSSA, ForwardEnqueueKernelNonDominantParameter11Bad) {
   string str = forwardKernelNonDominantParameterBaseCode("lsize2") + R"(
               %err     = OpEnqueueKernel %uintt %dqueue %flags %ndval %nevent
                                         %event %revent %kfunc %firstp %psize
@@ -758,14 +762,14 @@ pair<string, bool> cases[] = {
     {"OpGetKernelWorkGroupSize", kNoNDrange},
     {"OpGetKernelPreferredWorkGroupSizeMultiple", kNoNDrange}};
 
-INSTANTIATE_TEST_CASE_P(KernelArgs, Validate, ::testing::ValuesIn(cases),);
+INSTANTIATE_TEST_CASE_P(KernelArgs, ValidateSSA, ::testing::ValuesIn(cases), );
 
 static const string return_instructions = R"(
   OpReturn
   OpFunctionEnd
 )";
 
-TEST_P(Validate, GetKernelGood) {
+TEST_P(ValidateSSA, GetKernelGood) {
   string instruction = GetParam().first;
   bool with_ndrange = GetParam().second;
   string ndrange_param = with_ndrange ? " %ndval " : " ";
@@ -781,7 +785,7 @@ TEST_P(Validate, GetKernelGood) {
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
-TEST_P(Validate, ForwardGetKernelGood) {
+TEST_P(ValidateSSA, ForwardGetKernelGood) {
   string instruction = GetParam().first;
   bool with_ndrange = GetParam().second;
   string ndrange_param = with_ndrange ? " %ndval " : " ";
@@ -801,7 +805,7 @@ TEST_P(Validate, ForwardGetKernelGood) {
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
-TEST_P(Validate, ForwardGetKernelMissingDefinitionBad) {
+TEST_P(ValidateSSA, ForwardGetKernelMissingDefinitionBad) {
   string instruction = GetParam().first;
   bool with_ndrange = GetParam().second;
   string ndrange_param = with_ndrange ? " %ndval " : " ";
@@ -818,7 +822,7 @@ TEST_P(Validate, ForwardGetKernelMissingDefinitionBad) {
   EXPECT_THAT(getDiagnosticString(), HasSubstr("missing"));
 }
 
-TEST_P(Validate, ForwardGetKernelNDrangeSubGroupCountMissingParameter1Bad) {
+TEST_P(ValidateSSA, ForwardGetKernelNDrangeSubGroupCountMissingParameter1Bad) {
   string instruction = GetParam().first;
   bool with_ndrange = GetParam().second;
   string ndrange_param = with_ndrange ? " %ndval " : " ";
@@ -835,7 +839,8 @@ TEST_P(Validate, ForwardGetKernelNDrangeSubGroupCountMissingParameter1Bad) {
   EXPECT_THAT(getDiagnosticString(), HasSubstr("missing"));
 }
 
-TEST_P(Validate, ForwardGetKernelNDrangeSubGroupCountNonDominantParameter2Bad) {
+TEST_P(ValidateSSA,
+       ForwardGetKernelNDrangeSubGroupCountNonDominantParameter2Bad) {
   string instruction = GetParam().first;
   bool with_ndrange = GetParam().second;
   string ndrange_param = with_ndrange ? " %ndval2 " : " ";
@@ -855,7 +860,8 @@ TEST_P(Validate, ForwardGetKernelNDrangeSubGroupCountNonDominantParameter2Bad) {
   }
 }
 
-TEST_P(Validate, ForwardGetKernelNDrangeSubGroupCountNonDominantParameter4Bad) {
+TEST_P(ValidateSSA,
+       ForwardGetKernelNDrangeSubGroupCountNonDominantParameter4Bad) {
   string instruction = GetParam().first;
   bool with_ndrange = GetParam().second;
   string ndrange_param = with_ndrange ? " %ndval " : " ";
@@ -873,7 +879,8 @@ TEST_P(Validate, ForwardGetKernelNDrangeSubGroupCountNonDominantParameter4Bad) {
   EXPECT_THAT(getDiagnosticString(), HasSubstr("firstp2"));
 }
 
-TEST_P(Validate, ForwardGetKernelNDrangeSubGroupCountNonDominantParameter5Bad) {
+TEST_P(ValidateSSA,
+       ForwardGetKernelNDrangeSubGroupCountNonDominantParameter5Bad) {
   string instruction = GetParam().first;
   bool with_ndrange = GetParam().second;
   string ndrange_param = with_ndrange ? " %ndval " : " ";
@@ -891,7 +898,8 @@ TEST_P(Validate, ForwardGetKernelNDrangeSubGroupCountNonDominantParameter5Bad) {
   EXPECT_THAT(getDiagnosticString(), HasSubstr("psize2"));
 }
 
-TEST_P(Validate, ForwardGetKernelNDrangeSubGroupCountNonDominantParameter6Bad) {
+TEST_P(ValidateSSA,
+       ForwardGetKernelNDrangeSubGroupCountNonDominantParameter6Bad) {
   string instruction = GetParam().first;
   bool with_ndrange = GetParam().second;
   string ndrange_param = with_ndrange ? " %ndval " : " ";
@@ -911,12 +919,9 @@ TEST_P(Validate, ForwardGetKernelNDrangeSubGroupCountNonDominantParameter6Bad) {
   }
 }
 
-TEST_F(Validate, PhiGood) {
+TEST_F(ValidateSSA, PhiGood) {
   string str = kHeader + kBasicTypes +
                R"(
-%zero      = OpConstant %intt 0
-%one       = OpConstant %intt 1
-%ten       = OpConstant %intt 10
 %func      = OpFunction %voidt None %vfunct
 %preheader = OpLabel
 %init      = OpCopyObject %intt %zero
@@ -937,12 +942,9 @@ TEST_F(Validate, PhiGood) {
   ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
 }
 
-TEST_F(Validate, PhiMissingTypeBad) {
+TEST_F(ValidateSSA, PhiMissingTypeBad) {
   string str = kHeader + "OpName %missing \"missing\"" + kBasicTypes +
                R"(
-%zero      = OpConstant %intt 0
-%one       = OpConstant %intt 1
-%ten       = OpConstant %intt 10
 %func      = OpFunction %voidt None %vfunct
 %preheader = OpLabel
 %init      = OpCopyObject %intt %zero
@@ -964,12 +966,9 @@ TEST_F(Validate, PhiMissingTypeBad) {
   EXPECT_THAT(getDiagnosticString(), HasSubstr("missing"));
 }
 
-TEST_F(Validate, PhiMissingIdBad) {
+TEST_F(ValidateSSA, PhiMissingIdBad) {
   string str = kHeader + "OpName %missing \"missing\"" + kBasicTypes +
                R"(
-%zero      = OpConstant %intt 0
-%one       = OpConstant %intt 1
-%ten       = OpConstant %intt 10
 %func      = OpFunction %voidt None %vfunct
 %preheader = OpLabel
 %init      = OpCopyObject %intt %zero
@@ -991,12 +990,9 @@ TEST_F(Validate, PhiMissingIdBad) {
   EXPECT_THAT(getDiagnosticString(), HasSubstr("missing"));
 }
 
-TEST_F(Validate, PhiMissingLabelBad) {
+TEST_F(ValidateSSA, PhiMissingLabelBad) {
   string str = kHeader + "OpName %missing \"missing\"" + kBasicTypes +
                R"(
-%zero      = OpConstant %intt 0
-%one       = OpConstant %intt 1
-%ten       = OpConstant %intt 10
 %func      = OpFunction %voidt None %vfunct
 %preheader = OpLabel
 %init      = OpCopyObject %intt %zero