Handle conflict between debug info and existing validation rule (#3104)

* Allow OpExtInst for DebugInfo between secion 9 and 10

Fixes #3086

* Handle spirv-opt errors on DebugInfo Ext

* Add IR Loader test

* Fix ir loader bug

* Handle DebugFunction/DebugTypeMember forward reference

* Add test cases (forward reference to function)

* Support old DebugInfo extension

* Validate local debug info out of function
diff --git a/Android.mk b/Android.mk
index 7794c76..4fab1ec 100644
--- a/Android.mk
+++ b/Android.mk
@@ -239,7 +239,8 @@
 		    --extinst-grammar=$(3) \
 		    --extinst-output-base=$(1)/$(2)
 		@echo "[$(TARGET_ARCH_ABI)] Generate language specific header for $(2): headers <= grammar"
-$(LOCAL_PATH)/source/ext_inst.cpp: $(1)/$(2).h
+$(foreach F,$(SPVTOOLS_SRC_FILES) $(SPVTOOLS_OPT_SRC_FILES),$(LOCAL_PATH)/$F ) \
+	: $(1)/$(2).h
 endef
 # We generate language-specific headers for DebugInfo and OpenCL.DebugInfo.100
 $(eval $(call gen_spvtools_lang_headers,$(SPVTOOLS_OUT_PATH),DebugInfo,$(SPV_DEBUGINFO_GRAMMAR)))
diff --git a/source/ext_inst.cpp b/source/ext_inst.cpp
index 53ebb3c..e69c3c9 100644
--- a/source/ext_inst.cpp
+++ b/source/ext_inst.cpp
@@ -138,6 +138,14 @@
   return false;
 }
 
+bool spvExtInstIsDebugInfo(const spv_ext_inst_type_t type) {
+  if (type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100 ||
+      type == SPV_EXT_INST_TYPE_DEBUGINFO) {
+    return true;
+  }
+  return false;
+}
+
 spv_result_t spvExtInstTableNameLookup(const spv_ext_inst_table table,
                                        const spv_ext_inst_type_t type,
                                        const char* name,
diff --git a/source/ext_inst.h b/source/ext_inst.h
index b42d82b..aff6e30 100644
--- a/source/ext_inst.h
+++ b/source/ext_inst.h
@@ -24,6 +24,9 @@
 // Returns true if the extended instruction set is non-semantic
 bool spvExtInstIsNonSemantic(const spv_ext_inst_type_t type);
 
+// Returns true if the extended instruction set is debug info
+bool spvExtInstIsDebugInfo(const spv_ext_inst_type_t type);
+
 // Finds the named extented instruction of the given type in the given extended
 // instruction table. On success, returns SPV_SUCCESS and writes a handle of
 // the instruction entry into *entry.
diff --git a/source/link/linker.cpp b/source/link/linker.cpp
index 14e61a8..da6f0a7 100644
--- a/source/link/linker.cpp
+++ b/source/link/linker.cpp
@@ -324,6 +324,11 @@
       linked_module->AddDebug3Inst(
           std::unique_ptr<Instruction>(inst.Clone(linked_context)));
 
+  for (const auto& module : input_modules)
+    for (const auto& inst : module->ext_inst_debuginfo())
+      linked_module->AddExtInstDebugInfo(
+          std::unique_ptr<Instruction>(inst.Clone(linked_context)));
+
   // If the generated module uses SPIR-V 1.1 or higher, add an
   // OpModuleProcessed instruction about the linking step.
   if (linked_module->version() >= 0x10100) {
diff --git a/source/operand.cpp b/source/operand.cpp
index 39d17a6..3042606 100644
--- a/source/operand.cpp
+++ b/source/operand.cpp
@@ -19,6 +19,8 @@
 
 #include <algorithm>
 
+#include "DebugInfo.h"
+#include "OpenCLDebugInfo100.h"
 #include "source/macro.h"
 #include "source/spirv_constant.h"
 #include "source/spirv_target_env.h"
@@ -512,3 +514,37 @@
   }
   return out;
 }
+
+std::function<bool(unsigned)> spvDbgInfoExtOperandCanBeForwardDeclaredFunction(
+    spv_ext_inst_type_t ext_type, uint32_t key) {
+  // TODO(https://gitlab.khronos.org/spirv/SPIR-V/issues/532): Forward
+  // references for debug info instructions are still in discussion. We must
+  // update the following lines of code when we conclude the spec.
+  std::function<bool(unsigned index)> out;
+  if (ext_type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100) {
+    switch (OpenCLDebugInfo100Instructions(key)) {
+      case OpenCLDebugInfo100DebugFunction:
+        out = [](unsigned index) { return index == 13; };
+        break;
+      case OpenCLDebugInfo100DebugTypeComposite:
+        out = [](unsigned index) { return index >= 13; };
+        break;
+      default:
+        out = [](unsigned) { return false; };
+        break;
+    }
+  } else {
+    switch (DebugInfoInstructions(key)) {
+      case DebugInfoDebugFunction:
+        out = [](unsigned index) { return index == 13; };
+        break;
+      case DebugInfoDebugTypeComposite:
+        out = [](unsigned index) { return index >= 12; };
+        break;
+      default:
+        out = [](unsigned) { return false; };
+        break;
+    }
+  }
+  return out;
+}
diff --git a/source/operand.h b/source/operand.h
index 15a1825..7c73c6f 100644
--- a/source/operand.h
+++ b/source/operand.h
@@ -141,4 +141,11 @@
 std::function<bool(unsigned)> spvOperandCanBeForwardDeclaredFunction(
     SpvOp opcode);
 
+// Takes the instruction key of a debug info extension instruction
+// and returns a function object that will return true if the index
+// of the operand can be forward declared. This function will
+// used in the SSA validation stage of the pipeline
+std::function<bool(unsigned)> spvDbgInfoExtOperandCanBeForwardDeclaredFunction(
+    spv_ext_inst_type_t ext_type, uint32_t key);
+
 #endif  // SOURCE_OPERAND_H_
diff --git a/source/opt/instrument_pass.cpp b/source/opt/instrument_pass.cpp
index dfcd164..b1a6edb 100644
--- a/source/opt/instrument_pass.cpp
+++ b/source/opt/instrument_pass.cpp
@@ -988,6 +988,10 @@
     (void)i;
     ++module_offset;
   }
+  for (auto& i : module->ext_inst_debuginfo()) {
+    (void)i;
+    ++module_offset;
+  }
   for (auto& i : module->annotations()) {
     (void)i;
     ++module_offset;
diff --git a/source/opt/ir_context.h b/source/opt/ir_context.h
index 45bf129..723a2bb 100644
--- a/source/opt/ir_context.h
+++ b/source/opt/ir_context.h
@@ -187,6 +187,14 @@
   inline IteratorRange<Module::inst_iterator> debugs3();
   inline IteratorRange<Module::const_inst_iterator> debugs3() const;
 
+  // Iterators for debug info instructions (excluding OpLine & OpNoLine)
+  // contained in this module.  These are OpExtInst for OpenCL.DebugInfo.100
+  // or DebugInfo extension placed between section 9 and 10.
+  inline Module::inst_iterator ext_inst_debuginfo_begin();
+  inline Module::inst_iterator ext_inst_debuginfo_end();
+  inline IteratorRange<Module::inst_iterator> ext_inst_debuginfo();
+  inline IteratorRange<Module::const_inst_iterator> ext_inst_debuginfo() const;
+
   // Add |capability| to the module, if it is not already enabled.
   inline void AddCapability(SpvCapability capability);
 
@@ -215,6 +223,8 @@
   // Appends a debug 3 instruction (OpModuleProcessed) to this module.
   // This is due to decision by the SPIR Working Group, pending publication.
   inline void AddDebug3Inst(std::unique_ptr<Instruction>&& d);
+  // Appends a OpExtInst for DebugInfo to this module.
+  inline void AddExtInstDebugInfo(std::unique_ptr<Instruction>&& d);
   // Appends an annotation instruction to this module.
   inline void AddAnnotationInst(std::unique_ptr<Instruction>&& a);
   // Appends a type-declaration instruction to this module.
@@ -925,6 +935,23 @@
   return ((const Module*)module_.get())->debugs3();
 }
 
+Module::inst_iterator IRContext::ext_inst_debuginfo_begin() {
+  return module()->ext_inst_debuginfo_begin();
+}
+
+Module::inst_iterator IRContext::ext_inst_debuginfo_end() {
+  return module()->ext_inst_debuginfo_end();
+}
+
+IteratorRange<Module::inst_iterator> IRContext::ext_inst_debuginfo() {
+  return module()->ext_inst_debuginfo();
+}
+
+IteratorRange<Module::const_inst_iterator> IRContext::ext_inst_debuginfo()
+    const {
+  return ((const Module*)module_.get())->ext_inst_debuginfo();
+}
+
 void IRContext::AddCapability(SpvCapability capability) {
   if (!get_feature_mgr()->HasCapability(capability)) {
     std::unique_ptr<Instruction> capability_inst(new Instruction(
@@ -1018,6 +1045,10 @@
   module()->AddDebug3Inst(std::move(d));
 }
 
+void IRContext::AddExtInstDebugInfo(std::unique_ptr<Instruction>&& d) {
+  module()->AddExtInstDebugInfo(std::move(d));
+}
+
 void IRContext::AddAnnotationInst(std::unique_ptr<Instruction>&& a) {
   if (AreAnalysesValid(kAnalysisDecorations)) {
     get_decoration_mgr()->AddDecoration(a.get());
diff --git a/source/opt/ir_loader.cpp b/source/opt/ir_loader.cpp
index e21e680..836012f 100644
--- a/source/opt/ir_loader.cpp
+++ b/source/opt/ir_loader.cpp
@@ -16,6 +16,8 @@
 
 #include <utility>
 
+#include "DebugInfo.h"
+#include "OpenCLDebugInfo100.h"
 #include "source/ext_inst.h"
 #include "source/opt/log.h"
 #include "source/opt/reflect.h"
@@ -118,6 +120,9 @@
                  (opcode == SpvOpExtInst &&
                   spvExtInstIsNonSemantic(inst->ext_inst_type))) {
         module_->AddGlobalValue(std::move(spv_inst));
+      } else if (opcode == SpvOpExtInst &&
+                 spvExtInstIsDebugInfo(inst->ext_inst_type)) {
+        module_->AddExtInstDebugInfo(std::move(spv_inst));
       } else {
         Errorf(consumer_, src, loc,
                "Unhandled inst type (opcode: %d) found outside function "
@@ -136,6 +141,39 @@
         }
         function_->AddParameter(std::move(spv_inst));
       } else {
+        if (opcode == SpvOpExtInst &&
+            spvExtInstIsDebugInfo(inst->ext_inst_type)) {
+          const uint32_t ext_inst_index = inst->words[4];
+          if (inst->ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100) {
+            const OpenCLDebugInfo100Instructions ext_inst_key =
+                OpenCLDebugInfo100Instructions(ext_inst_index);
+            if (ext_inst_key != OpenCLDebugInfo100DebugScope &&
+                ext_inst_key != OpenCLDebugInfo100DebugNoScope &&
+                ext_inst_key != OpenCLDebugInfo100DebugDeclare &&
+                ext_inst_key != OpenCLDebugInfo100DebugValue) {
+              Errorf(consumer_, src, loc,
+                     "Debug info extension instruction other than DebugScope, "
+                     "DebugNoScope, DebugDeclare, and DebugValue found inside "
+                     "function",
+                     opcode);
+              return false;
+            }
+          } else {
+            const DebugInfoInstructions ext_inst_key =
+                DebugInfoInstructions(ext_inst_index);
+            if (ext_inst_key != DebugInfoDebugScope &&
+                ext_inst_key != DebugInfoDebugNoScope &&
+                ext_inst_key != DebugInfoDebugDeclare &&
+                ext_inst_key != DebugInfoDebugValue) {
+              Errorf(consumer_, src, loc,
+                     "Debug info extension instruction other than DebugScope, "
+                     "DebugNoScope, DebugDeclare, and DebugValue found inside "
+                     "function",
+                     opcode);
+              return false;
+            }
+          }
+        }
         block_->AddInstruction(std::move(spv_inst));
       }
     }
diff --git a/source/opt/module.cpp b/source/opt/module.cpp
index c7fc247..4403894 100644
--- a/source/opt/module.cpp
+++ b/source/opt/module.cpp
@@ -95,6 +95,7 @@
   DELEGATE(debugs1_);
   DELEGATE(debugs2_);
   DELEGATE(debugs3_);
+  DELEGATE(ext_inst_debuginfo_);
   DELEGATE(annotations_);
   DELEGATE(types_values_);
   for (auto& i : functions_) i->ForEachInst(f, run_on_debug_line_insts);
@@ -117,6 +118,7 @@
   for (auto& i : debugs3_) DELEGATE(i);
   for (auto& i : annotations_) DELEGATE(i);
   for (auto& i : types_values_) DELEGATE(i);
+  for (auto& i : ext_inst_debuginfo_) DELEGATE(i);
   for (auto& i : functions_) {
     static_cast<const Function*>(i.get())->ForEachInst(f,
                                                        run_on_debug_line_insts);
diff --git a/source/opt/module.h b/source/opt/module.h
index aefa2a5..fc53d35 100644
--- a/source/opt/module.h
+++ b/source/opt/module.h
@@ -102,6 +102,10 @@
   // This is due to decision by the SPIR Working Group, pending publication.
   inline void AddDebug3Inst(std::unique_ptr<Instruction> d);
 
+  // Appends a debug info extension (OpenCL.DebugInfo.100 or DebugInfo)
+  // instruction to this module.
+  inline void AddExtInstDebugInfo(std::unique_ptr<Instruction> d);
+
   // Appends an annotation instruction to this module.
   inline void AddAnnotationInst(std::unique_ptr<Instruction> a);
 
@@ -182,6 +186,14 @@
   inline IteratorRange<inst_iterator> debugs3();
   inline IteratorRange<const_inst_iterator> debugs3() const;
 
+  // Iterators for debug info instructions (excluding OpLine & OpNoLine)
+  // contained in this module.  These are OpExtInst for OpenCL.DebugInfo.100
+  // or DebugInfo extension placed between section 9 and 10.
+  inline inst_iterator ext_inst_debuginfo_begin();
+  inline inst_iterator ext_inst_debuginfo_end();
+  inline IteratorRange<inst_iterator> ext_inst_debuginfo();
+  inline IteratorRange<const_inst_iterator> ext_inst_debuginfo() const;
+
   // Iterators for entry point instructions contained in this module
   inline IteratorRange<inst_iterator> entry_points();
   inline IteratorRange<const_inst_iterator> entry_points() const;
@@ -274,6 +286,7 @@
   InstructionList debugs1_;
   InstructionList debugs2_;
   InstructionList debugs3_;
+  InstructionList ext_inst_debuginfo_;
   InstructionList annotations_;
   // Type declarations, constants, and global variable declarations.
   InstructionList types_values_;
@@ -323,6 +336,10 @@
   debugs3_.push_back(std::move(d));
 }
 
+inline void Module::AddExtInstDebugInfo(std::unique_ptr<Instruction> d) {
+  ext_inst_debuginfo_.push_back(std::move(d));
+}
+
 inline void Module::AddAnnotationInst(std::unique_ptr<Instruction> a) {
   annotations_.push_back(std::move(a));
 }
@@ -403,6 +420,22 @@
   return make_range(debugs3_.begin(), debugs3_.end());
 }
 
+inline Module::inst_iterator Module::ext_inst_debuginfo_begin() {
+  return ext_inst_debuginfo_.begin();
+}
+inline Module::inst_iterator Module::ext_inst_debuginfo_end() {
+  return ext_inst_debuginfo_.end();
+}
+
+inline IteratorRange<Module::inst_iterator> Module::ext_inst_debuginfo() {
+  return make_range(ext_inst_debuginfo_.begin(), ext_inst_debuginfo_.end());
+}
+
+inline IteratorRange<Module::const_inst_iterator> Module::ext_inst_debuginfo()
+    const {
+  return make_range(ext_inst_debuginfo_.begin(), ext_inst_debuginfo_.end());
+}
+
 inline IteratorRange<Module::inst_iterator> Module::entry_points() {
   return make_range(entry_points_.begin(), entry_points_.end());
 }
diff --git a/source/opt/strip_debug_info_pass.cpp b/source/opt/strip_debug_info_pass.cpp
index 936c966..c86ce57 100644
--- a/source/opt/strip_debug_info_pass.cpp
+++ b/source/opt/strip_debug_info_pass.cpp
@@ -75,6 +75,7 @@
 
   for (auto& dbg : context()->debugs2()) to_kill.push_back(&dbg);
   for (auto& dbg : context()->debugs3()) to_kill.push_back(&dbg);
+  for (auto& dbg : context()->ext_inst_debuginfo()) to_kill.push_back(&dbg);
 
   // OpName must come first, since they may refer to other debug instructions.
   // If they are after the instructions that refer to, then they will be killed
diff --git a/source/opt/strip_reflect_info_pass.cpp b/source/opt/strip_reflect_info_pass.cpp
index c231ead..8b0f2db 100644
--- a/source/opt/strip_reflect_info_pass.cpp
+++ b/source/opt/strip_reflect_info_pass.cpp
@@ -77,6 +77,7 @@
   for (auto& dbg : context()->debugs1()) to_remove.push_back(&dbg);
   for (auto& dbg : context()->debugs2()) to_remove.push_back(&dbg);
   for (auto& dbg : context()->debugs3()) to_remove.push_back(&dbg);
+  for (auto& dbg : context()->ext_inst_debuginfo()) to_remove.push_back(&dbg);
 
   // remove any extended inst imports that are non semantic
   std::unordered_set<uint32_t> non_semantic_sets;
diff --git a/source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.cpp b/source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.cpp
index da61c8d..ce66691 100644
--- a/source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.cpp
+++ b/source/reduce/remove_unreferenced_instruction_reduction_opportunity_finder.cpp
@@ -52,6 +52,13 @@
     result.push_back(MakeUnique<RemoveInstructionReductionOpportunity>(&inst));
   }
 
+  for (auto& inst : context->module()->ext_inst_debuginfo()) {
+    if (context->get_def_use_mgr()->NumUses(&inst) > 0) {
+      continue;
+    }
+    result.push_back(MakeUnique<RemoveInstructionReductionOpportunity>(&inst));
+  }
+
   for (auto& inst : context->module()->types_values()) {
     if (context->get_def_use_mgr()->NumUsers(&inst) > 0) {
       continue;
diff --git a/source/val/instruction.h b/source/val/instruction.h
index e30bfbc..617cb06 100644
--- a/source/val/instruction.h
+++ b/source/val/instruction.h
@@ -91,6 +91,12 @@
            spvExtInstIsNonSemantic(inst_.ext_inst_type);
   }
 
+  /// True if this is an OpExtInst for debug info extension.
+  bool IsDebugInfo() const {
+    return opcode() == SpvOp::SpvOpExtInst &&
+           spvExtInstIsDebugInfo(inst_.ext_inst_type);
+  }
+
   // Casts the words belonging to the operand under |index| to |T| and returns.
   template <typename T>
   T GetOperandAs(size_t index) const {
diff --git a/source/val/validate_adjacency.cpp b/source/val/validate_adjacency.cpp
index 108e361..64655b0 100644
--- a/source/val/validate_adjacency.cpp
+++ b/source/val/validate_adjacency.cpp
@@ -54,6 +54,16 @@
         adjacency_status =
             adjacency_status == IN_NEW_FUNCTION ? IN_ENTRY_BLOCK : PHI_VALID;
         break;
+      case SpvOpExtInst:
+        // If it is a debug info instruction, we do not change the status to
+        // allow debug info instructions before OpVariable in a function.
+        // TODO(https://gitlab.khronos.org/spirv/SPIR-V/issues/533): We need
+        // to discuss the location of DebugScope, DebugNoScope, DebugDeclare,
+        // and DebugValue.
+        if (!spvExtInstIsDebugInfo(inst.ext_inst_type())) {
+          adjacency_status = PHI_AND_VAR_INVALID;
+        }
+        break;
       case SpvOpPhi:
         if (adjacency_status != PHI_VALID) {
           return _.diag(SPV_ERROR_INVALID_DATA, &inst)
diff --git a/source/val/validate_function.cpp b/source/val/validate_function.cpp
index f995ab3..f130eac 100644
--- a/source/val/validate_function.cpp
+++ b/source/val/validate_function.cpp
@@ -88,7 +88,7 @@
     const auto* use = pair.first;
     if (std::find(acceptable.begin(), acceptable.end(), use->opcode()) ==
             acceptable.end() &&
-        !use->IsNonSemantic()) {
+        !use->IsNonSemantic() && !use->IsDebugInfo()) {
       return _.diag(SPV_ERROR_INVALID_ID, use)
              << "Invalid use of function result id " << _.getIdName(inst->id())
              << ".";
diff --git a/source/val/validate_id.cpp b/source/val/validate_id.cpp
index 7406330..c171d31 100644
--- a/source/val/validate_id.cpp
+++ b/source/val/validate_id.cpp
@@ -131,7 +131,11 @@
 // instruction operand's ID can be forward referenced.
 spv_result_t IdPass(ValidationState_t& _, Instruction* inst) {
   auto can_have_forward_declared_ids =
-      spvOperandCanBeForwardDeclaredFunction(inst->opcode());
+      inst->opcode() == SpvOpExtInst &&
+              spvExtInstIsDebugInfo(inst->ext_inst_type())
+          ? spvDbgInfoExtOperandCanBeForwardDeclaredFunction(
+                inst->ext_inst_type(), inst->word(4))
+          : spvOperandCanBeForwardDeclaredFunction(inst->opcode());
 
   // Keep track of a result id defined by this instruction.  0 means it
   // does not define an id.
diff --git a/source/val/validate_layout.cpp b/source/val/validate_layout.cpp
index 259befe..707ad52 100644
--- a/source/val/validate_layout.cpp
+++ b/source/val/validate_layout.cpp
@@ -14,15 +14,16 @@
 
 // Source code for logical layout validation as described in section 2.4
 
-#include "source/val/validate.h"
-
 #include <cassert>
 
+#include "DebugInfo.h"
+#include "OpenCLDebugInfo100.h"
 #include "source/diagnostic.h"
 #include "source/opcode.h"
 #include "source/operand.h"
 #include "source/val/function.h"
 #include "source/val/instruction.h"
+#include "source/val/validate.h"
 #include "source/val/validation_state.h"
 
 namespace spvtools {
@@ -46,6 +47,53 @@
                  << "Non-semantic OpExtInst must not appear before types "
                  << "section";
         }
+      } else if (spvExtInstIsDebugInfo(inst->ext_inst_type())) {
+        const uint32_t ext_inst_index = inst->word(4);
+        bool local_debug_info = false;
+        if (inst->ext_inst_type() == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100) {
+          const OpenCLDebugInfo100Instructions ext_inst_key =
+              OpenCLDebugInfo100Instructions(ext_inst_index);
+          if (ext_inst_key == OpenCLDebugInfo100DebugScope ||
+              ext_inst_key == OpenCLDebugInfo100DebugNoScope ||
+              ext_inst_key == OpenCLDebugInfo100DebugDeclare ||
+              ext_inst_key == OpenCLDebugInfo100DebugValue) {
+            local_debug_info = true;
+          }
+        } else {
+          const DebugInfoInstructions ext_inst_key =
+              DebugInfoInstructions(ext_inst_index);
+          if (ext_inst_key == DebugInfoDebugScope ||
+              ext_inst_key == DebugInfoDebugNoScope ||
+              ext_inst_key == DebugInfoDebugDeclare ||
+              ext_inst_key == DebugInfoDebugValue) {
+            local_debug_info = true;
+          }
+        }
+
+        if (local_debug_info) {
+          if (_.in_function_body() == false) {
+            // DebugScope, DebugNoScope, DebugDeclare, DebugValue must
+            // appear in a function body.
+            return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
+                   << "DebugScope, DebugNoScope, DebugDeclare, DebugValue "
+                   << "of debug info extension must appear in a function "
+                   << "body";
+          }
+        } else {
+          // Debug info extinst opcodes other than DebugScope, DebugNoScope,
+          // DebugDeclare, DebugValue must be placed between section 9 (types,
+          // constants, global variables) and section 10 (function
+          // declarations).
+          if (_.current_layout_section() < kLayoutTypes ||
+              _.current_layout_section() >= kLayoutFunctionDeclarations) {
+            return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
+                   << "Debug info extension instructions other than "
+                   << "DebugScope, DebugNoScope, DebugDeclare, DebugValue "
+                   << "must appear between section 9 (types, constants, "
+                   << "global variables) and section 10 (function "
+                   << "declarations)";
+          }
+        }
       } else {
         // otherwise they must be used in a block
         if (_.current_layout_section() < kLayoutFunctionDefinitions) {
@@ -182,6 +230,53 @@
                    << "Non-semantic OpExtInst within function definition must "
                       "appear in a block";
           }
+        } else if (spvExtInstIsDebugInfo(inst->ext_inst_type())) {
+          const uint32_t ext_inst_index = inst->word(4);
+          bool local_debug_info = false;
+          if (inst->ext_inst_type() == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100) {
+            const OpenCLDebugInfo100Instructions ext_inst_key =
+                OpenCLDebugInfo100Instructions(ext_inst_index);
+            if (ext_inst_key == OpenCLDebugInfo100DebugScope ||
+                ext_inst_key == OpenCLDebugInfo100DebugNoScope ||
+                ext_inst_key == OpenCLDebugInfo100DebugDeclare ||
+                ext_inst_key == OpenCLDebugInfo100DebugValue) {
+              local_debug_info = true;
+            }
+          } else {
+            const DebugInfoInstructions ext_inst_key =
+                DebugInfoInstructions(ext_inst_index);
+            if (ext_inst_key == DebugInfoDebugScope ||
+                ext_inst_key == DebugInfoDebugNoScope ||
+                ext_inst_key == DebugInfoDebugDeclare ||
+                ext_inst_key == DebugInfoDebugValue) {
+              local_debug_info = true;
+            }
+          }
+
+          if (local_debug_info) {
+            if (_.in_function_body() == false) {
+              // DebugScope, DebugNoScope, DebugDeclare, DebugValue must
+              // appear in a function body.
+              return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
+                     << "DebugScope, DebugNoScope, DebugDeclare, DebugValue "
+                     << "of debug info extension must appear in a function "
+                     << "body";
+            }
+          } else {
+            // Debug info extinst opcodes other than DebugScope, DebugNoScope,
+            // DebugDeclare, DebugValue must be placed between section 9 (types,
+            // constants, global variables) and section 10 (function
+            // declarations).
+            if (_.current_layout_section() < kLayoutTypes ||
+                _.current_layout_section() >= kLayoutFunctionDeclarations) {
+              return _.diag(SPV_ERROR_INVALID_LAYOUT, inst)
+                     << "Debug info extension instructions other than "
+                     << "DebugScope, DebugNoScope, DebugDeclare, DebugValue "
+                     << "must appear between section 9 (types, constants, "
+                     << "global variables) and section 10 (function "
+                     << "declarations)";
+            }
+          }
         } else {
           // otherwise they must be used in a block
           if (_.in_block() == false) {
diff --git a/test/opt/ir_loader_test.cpp b/test/opt/ir_loader_test.cpp
index ac5c520..c60e853 100644
--- a/test/opt/ir_loader_test.cpp
+++ b/test/opt/ir_loader_test.cpp
@@ -129,6 +129,621 @@
   // clang-format on
 }
 
+TEST(IrBuilder, ConsumeDebugInfoInst) {
+  // /* HLSL */
+  //
+  // struct VS_OUTPUT {
+  //   float4 pos : SV_POSITION;
+  //   float4 color : COLOR;
+  // };
+  //
+  // VS_OUTPUT main(float4 pos : POSITION,
+  //                float4 color : COLOR) {
+  //   VS_OUTPUT vout;
+  //   vout.pos = pos;
+  //   vout.color = color;
+  //   return vout;
+  // }
+  DoRoundTripCheck(R"(OpCapability Shader
+%1 = OpExtInstImport "OpenCL.DebugInfo.100"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %main "main" %pos %color %gl_Position %out_var_COLOR
+%7 = OpString "simple_vs.hlsl"
+%8 = OpString "#line 1 \"simple_vs.hlsl\"
+struct VS_OUTPUT {
+  float4 pos : SV_POSITION;
+  float4 color : COLOR;
+};
+
+VS_OUTPUT main(float4 pos : POSITION,
+               float4 color : COLOR) {
+  VS_OUTPUT vout;
+  vout.pos = pos;
+  vout.color = color;
+  return vout;
+}
+"
+OpSource HLSL 600 %7 "#line 1 \"simple_vs.hlsl\"
+struct VS_OUTPUT {
+  float4 pos : SV_POSITION;
+  float4 color : COLOR;
+};
+
+VS_OUTPUT main(float4 pos : POSITION,
+               float4 color : COLOR) {
+  VS_OUTPUT vout;
+  vout.pos = pos;
+  vout.color = color;
+  return vout;
+}
+"
+%9 = OpString "struct VS_OUTPUT"
+%10 = OpString "float"
+%11 = OpString "pos : SV_POSITION"
+%12 = OpString "color : COLOR"
+%13 = OpString "VS_OUTPUT"
+%14 = OpString "main"
+%15 = OpString "VS_OUTPUT_main_v4f_v4f"
+%16 = OpString "pos : POSITION"
+%17 = OpString "color : COLOR"
+%18 = OpString "vout"
+OpName %out_var_COLOR "out.var.COLOR"
+OpName %main "main"
+OpName %VS_OUTPUT "VS_OUTPUT"
+OpMemberName %VS_OUTPUT 0 "pos"
+OpMemberName %VS_OUTPUT 1 "color"
+OpName %pos "pos"
+OpName %color "color"
+OpName %vout "vout"
+OpDecorate %gl_Position BuiltIn Position
+OpDecorate %pos Location 0
+OpDecorate %color Location 1
+OpDecorate %out_var_COLOR Location 0
+%int = OpTypeInt 32 1
+%int_0 = OpConstant %int 0
+%int_1 = OpConstant %int 1
+%int_32 = OpConstant %int 32
+%int_128 = OpConstant %int 128
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%_ptr_Input_v4float = OpTypePointer Input %v4float
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%void = OpTypeVoid
+%31 = OpTypeFunction %void
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+%VS_OUTPUT = OpTypeStruct %v4float %v4float
+%_ptr_Function_VS_OUTPUT = OpTypePointer Function %VS_OUTPUT
+OpLine %7 6 23
+%pos = OpVariable %_ptr_Input_v4float Input
+OpLine %7 7 23
+%color = OpVariable %_ptr_Input_v4float Input
+OpLine %7 2 16
+%gl_Position = OpVariable %_ptr_Output_v4float Output
+OpLine %7 3 18
+%out_var_COLOR = OpVariable %_ptr_Output_v4float Output
+%34 = OpExtInst %void %1 DebugSource %7 %8
+%35 = OpExtInst %void %1 DebugCompilationUnit 2 4 %34 HLSL
+%36 = OpExtInst %void %1 DebugTypeComposite %9 Structure %34 1 1 %35 %13 %int_128 FlagIsProtected|FlagIsPrivate %37 %38
+%39 = OpExtInst %void %1 DebugTypeBasic %10 %int_32 Float
+%40 = OpExtInst %void %1 DebugTypeVector %39 4
+%37 = OpExtInst %void %1 DebugTypeMember %11 %40 %34 2 3 %36 %int_0 %int_128 FlagIsProtected|FlagIsPrivate
+%38 = OpExtInst %void %1 DebugTypeMember %12 %40 %34 3 3 %36 %int_128 %int_128 FlagIsProtected|FlagIsPrivate
+%41 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %36 %40 %40
+%42 = OpExtInst %void %1 DebugExpression
+%43 = OpExtInst %void %1 DebugFunction %14 %41 %34 6 1 %35 %15 FlagIsProtected|FlagIsPrivate 7 %main
+%44 = OpExtInst %void %1 DebugLocalVariable %16 %40 %34 6 16 %43 FlagIsLocal 0
+%45 = OpExtInst %void %1 DebugLocalVariable %17 %40 %34 7 16 %43 FlagIsLocal 1
+%46 = OpExtInst %void %1 DebugLocalVariable %18 %36 %34 8 3 %43 FlagIsLocal
+%47 = OpExtInst %void %1 DebugDeclare %44 %pos %42
+%48 = OpExtInst %void %1 DebugDeclare %45 %color %42
+OpLine %7 6 1
+%main = OpFunction %void None %31
+%49 = OpLabel
+%50 = OpExtInst %void %1 DebugScope %43
+OpLine %7 8 13
+%vout = OpVariable %_ptr_Function_VS_OUTPUT Function
+%51 = OpExtInst %void %1 DebugDeclare %46 %vout %42
+OpLine %7 9 14
+%52 = OpLoad %v4float %pos
+OpLine %7 9 3
+%53 = OpAccessChain %_ptr_Function_v4float %vout %int_0
+%54 = OpExtInst %void %1 DebugValue %46 %53 %42 %int_0
+OpStore %53 %52
+OpLine %7 10 16
+%55 = OpLoad %v4float %color
+OpLine %7 10 3
+%56 = OpAccessChain %_ptr_Function_v4float %vout %int_1
+%57 = OpExtInst %void %1 DebugValue %46 %56 %42 %int_1
+OpStore %56 %55
+OpLine %7 11 10
+%58 = OpLoad %VS_OUTPUT %vout
+OpLine %7 11 3
+%59 = OpCompositeExtract %v4float %58 0
+OpStore %gl_Position %59
+%60 = OpCompositeExtract %v4float %58 1
+OpStore %out_var_COLOR %60
+OpReturn
+OpFunctionEnd
+)");
+}
+
+TEST(IrBuilder, ConsumeDebugInfoLexicalScopeInst) {
+  // /* HLSL */
+  //
+  // float4 func2(float arg2) {   // func2_block
+  //   return float4(arg2, 0, 0, 0);
+  // }
+  //
+  // float4 func1(float arg1) {   // func1_block
+  //   if (arg1 > 1) {       // if_true_block
+  //     return float4(0, 0, 0, 0);
+  //   }
+  //   return func2(arg1);   // if_merge_block
+  // }
+  //
+  // float4 main(float pos : POSITION) : SV_POSITION {  // main
+  //   return func1(pos);
+  // }
+  DoRoundTripCheck(R"(OpCapability Shader
+%1 = OpExtInstImport "OpenCL.DebugInfo.100"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %main "main" %pos %gl_Position
+%5 = OpString "block/block.hlsl"
+%6 = OpString "#line 1 \"block/block.hlsl\"
+float4 func2(float arg2) {
+  return float4(arg2, 0, 0, 0);
+}
+
+float4 func1(float arg1) {
+  if (arg1 > 1) {
+    return float4(0, 0, 0, 0);
+  }
+  return func2(arg1);
+}
+
+float4 main(float pos : POSITION) : SV_POSITION {
+  return func1(pos);
+}
+"
+OpSource HLSL 600 %5 "#line 1 \"block/block.hlsl\"
+float4 func2(float arg2) {
+  return float4(arg2, 0, 0, 0);
+}
+
+float4 func1(float arg1) {
+  if (arg1 > 1) {
+    return float4(0, 0, 0, 0);
+  }
+  return func2(arg1);
+}
+
+float4 main(float pos : POSITION) : SV_POSITION {
+  return func1(pos);
+}
+"
+%7 = OpString "float"
+%8 = OpString "main"
+%9 = OpString "v4f_main_f"
+%10 = OpString "v4f_func1_f"
+%11 = OpString "v4f_func2_f"
+%12 = OpString "pos : POSITION"
+%13 = OpString "func1"
+%14 = OpString "func2"
+OpName %main "main"
+OpName %pos "pos"
+OpName %bb_entry "bb.entry"
+OpName %param_var_arg1 "param.var.arg1"
+OpName %func1 "func1"
+OpName %arg1 "arg1"
+OpName %bb_entry_0 "bb.entry"
+OpName %param_var_arg2 "param.var.arg2"
+OpName %if_true "if.true"
+OpName %if_merge "if.merge"
+OpName %func2 "func2"
+OpName %arg2 "arg2"
+OpName %bb_entry_1 "bb.entry"
+OpDecorate %gl_Position BuiltIn Position
+OpDecorate %pos Location 0
+%float = OpTypeFloat 32
+%int = OpTypeInt 32 1
+%float_1 = OpConstant %float 1
+%float_0 = OpConstant %float 0
+%int_32 = OpConstant %int 32
+%v4float = OpTypeVector %float 4
+%32 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
+%_ptr_Input_float = OpTypePointer Input %float
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%void = OpTypeVoid
+%36 = OpTypeFunction %void
+%_ptr_Function_float = OpTypePointer Function %float
+%38 = OpTypeFunction %v4float %_ptr_Function_float
+%bool = OpTypeBool
+OpLine %5 12 25
+%pos = OpVariable %_ptr_Input_float Input
+OpLine %5 12 37
+%gl_Position = OpVariable %_ptr_Output_v4float Output
+%40 = OpExtInst %void %1 DebugSource %5 %6
+%41 = OpExtInst %void %1 DebugCompilationUnit 2 4 %40 HLSL
+%42 = OpExtInst %void %1 DebugTypeBasic %7 %int_32 Float
+%43 = OpExtInst %void %1 DebugTypeVector %42 4
+%44 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %43 %42
+%45 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %43 %42
+%46 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %43 %42
+%47 = OpExtInst %void %1 DebugFunction %8 %44 %40 12 1 %41 %9 FlagIsProtected|FlagIsPrivate 13 %main
+%48 = OpExtInst %void %1 DebugFunction %13 %45 %40 5 1 %41 %10 FlagIsProtected|FlagIsPrivate 13 %func1
+%49 = OpExtInst %void %1 DebugFunction %14 %46 %40 1 1 %41 %11 FlagIsProtected|FlagIsPrivate 13 %func2
+%50 = OpExtInst %void %1 DebugLexicalBlock %40 6 17 %48
+%51 = OpExtInst %void %1 DebugLexicalBlock %40 9 3 %48
+OpLine %5 12 1
+%main = OpFunction %void None %36
+%bb_entry = OpLabel
+%52 = OpExtInst %void %1 DebugScope %47
+OpLine %5 13 16
+%param_var_arg1 = OpVariable %_ptr_Function_float Function
+%53 = OpLoad %float %pos
+OpStore %param_var_arg1 %53
+OpLine %5 13 10
+%54 = OpFunctionCall %v4float %func1 %param_var_arg1
+OpLine %5 13 3
+OpStore %gl_Position %54
+OpReturn
+OpFunctionEnd
+OpLine %5 5 1
+%func1 = OpFunction %v4float None %38
+OpLine %5 5 20
+%arg1 = OpFunctionParameter %_ptr_Function_float
+%bb_entry_0 = OpLabel
+%55 = OpExtInst %void %1 DebugScope %48
+OpLine %5 9 16
+%param_var_arg2 = OpVariable %_ptr_Function_float Function
+OpLine %5 6 7
+%56 = OpLoad %float %arg1
+OpLine %5 6 12
+%57 = OpFOrdGreaterThan %bool %56 %float_1
+OpLine %5 6 17
+OpSelectionMerge %if_merge None
+OpBranchConditional %57 %if_true %if_merge
+%if_true = OpLabel
+%58 = OpExtInst %void %1 DebugScope %50
+OpLine %5 7 5
+OpReturnValue %32
+%if_merge = OpLabel
+%59 = OpExtInst %void %1 DebugScope %51
+OpLine %5 9 16
+%60 = OpLoad %float %arg1
+OpStore %param_var_arg2 %60
+OpLine %5 9 10
+%61 = OpFunctionCall %v4float %func2 %param_var_arg2
+OpLine %5 9 3
+OpReturnValue %61
+OpFunctionEnd
+OpLine %5 1 1
+%func2 = OpFunction %v4float None %38
+OpLine %5 1 20
+%arg2 = OpFunctionParameter %_ptr_Function_float
+%bb_entry_1 = OpLabel
+%62 = OpExtInst %void %1 DebugScope %49
+OpLine %5 2 17
+%63 = OpLoad %float %arg2
+%64 = OpCompositeConstruct %v4float %63 %float_0 %float_0 %float_0
+OpLine %5 2 3
+OpReturnValue %64
+OpFunctionEnd
+)");
+}
+
+TEST(IrBuilder, ConsumeDebugInlinedAt) {
+  // /* HLSL */
+  //
+  // float4 func2(float arg2) {   // func2_block
+  //   return float4(arg2, 0, 0, 0);
+  // }
+  //
+  // float4 func1(float arg1) {   // func1_block
+  //   if (arg1 > 1) {       // if_true_block
+  //     return float4(0, 0, 0, 0);
+  //   }
+  //   return func2(arg1);   // if_merge_block
+  // }
+  //
+  // float4 main(float pos : POSITION) : SV_POSITION {  // main
+  //   return func1(pos);
+  // }
+  //
+  // TODO(https://gitlab.khronos.org/spirv/SPIR-V/issues/533): In the following
+  // SPIRV code, we use DebugInfoNone to reference opted-out function from
+  // DebugFunction similar to opted-out global variable for DebugGlobalVariable,
+  // but this is not a part of the spec yet. We are still in discussion and we
+  // must correct it if our decision is different.
+  DoRoundTripCheck(R"(OpCapability Shader
+%1 = OpExtInstImport "OpenCL.DebugInfo.100"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %main "main" %pos %gl_Position
+%5 = OpString "block/block.hlsl"
+%6 = OpString "#line 1 \"block/block.hlsl\"
+float4 func2(float arg2) {
+  return float4(arg2, 0, 0, 0);
+}
+
+float4 func1(float arg1) {
+  if (arg1 > 1) {
+    return float4(0, 0, 0, 0);
+  }
+  return func2(arg1);
+}
+
+float4 main(float pos : POSITION) : SV_POSITION {
+  return func1(pos);
+}
+"
+OpSource HLSL 600 %5 "#line 1 \"block/block.hlsl\"
+float4 func2(float arg2) {
+  return float4(arg2, 0, 0, 0);
+}
+
+float4 func1(float arg1) {
+  if (arg1 > 1) {
+    return float4(0, 0, 0, 0);
+  }
+  return func2(arg1);
+}
+
+float4 main(float pos : POSITION) : SV_POSITION {
+  return func1(pos);
+}
+"
+%7 = OpString "float"
+%8 = OpString "main"
+%9 = OpString "v4f_main_f"
+%10 = OpString "v4f_func1_f"
+%11 = OpString "v4f_func2_f"
+%12 = OpString "pos : POSITION"
+%13 = OpString "func1"
+%14 = OpString "func2"
+OpName %main "main"
+OpName %pos "pos"
+OpName %bb_entry "bb.entry"
+OpName %if_true "if.true"
+OpName %if_merge "if.merge"
+OpDecorate %gl_Position BuiltIn Position
+OpDecorate %pos Location 0
+%float = OpTypeFloat 32
+%int = OpTypeInt 32 1
+%float_1 = OpConstant %float 1
+%float_0 = OpConstant %float 0
+%int_32 = OpConstant %int 32
+%v4float = OpTypeVector %float 4
+%24 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
+%_ptr_Input_float = OpTypePointer Input %float
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%void = OpTypeVoid
+%28 = OpTypeFunction %void
+%_ptr_Function_float = OpTypePointer Function %float
+%30 = OpTypeFunction %v4float %_ptr_Function_float
+%bool = OpTypeBool
+OpLine %5 12 25
+%pos = OpVariable %_ptr_Input_float Input
+OpLine %5 12 37
+%gl_Position = OpVariable %_ptr_Output_v4float Output
+%32 = OpExtInst %void %1 DebugInfoNone
+%33 = OpExtInst %void %1 DebugSource %5 %6
+%34 = OpExtInst %void %1 DebugCompilationUnit 2 4 %33 HLSL
+%35 = OpExtInst %void %1 DebugTypeBasic %7 %int_32 Float
+%36 = OpExtInst %void %1 DebugTypeVector %35 4
+%37 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %36 %35
+%38 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %36 %35
+%39 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %36 %35
+%40 = OpExtInst %void %1 DebugFunction %8 %37 %33 12 1 %34 %9 FlagIsProtected|FlagIsPrivate 13 %main
+%41 = OpExtInst %void %1 DebugFunction %13 %38 %33 5 1 %34 %10 FlagIsProtected|FlagIsPrivate 13 %32
+%42 = OpExtInst %void %1 DebugFunction %14 %39 %33 1 1 %34 %11 FlagIsProtected|FlagIsPrivate 13 %32
+%43 = OpExtInst %void %1 DebugLexicalBlock %33 12 49 %40
+%44 = OpExtInst %void %1 DebugLexicalBlock %33 5 26 %41
+%45 = OpExtInst %void %1 DebugLexicalBlock %33 1 26 %42
+%46 = OpExtInst %void %1 DebugLexicalBlock %33 6 17 %44
+%47 = OpExtInst %void %1 DebugLexicalBlock %33 9 3 %44
+%48 = OpExtInst %void %1 DebugInlinedAt 9 %47
+%49 = OpExtInst %void %1 DebugInlinedAt 13 %43
+%50 = OpExtInst %void %1 DebugInlinedAt 13 %43 %48
+OpLine %5 12 1
+%main = OpFunction %void None %28
+%bb_entry = OpLabel
+%51 = OpExtInst %void %1 DebugScope %44 %49
+OpLine %5 6 7
+%52 = OpLoad %float %pos
+OpLine %5 6 12
+%53 = OpFOrdGreaterThan %bool %52 %float_1
+OpLine %5 6 17
+OpSelectionMerge %if_merge None
+OpBranchConditional %53 %if_true %if_merge
+%if_true = OpLabel
+%54 = OpExtInst %void %1 DebugScope %46 %49
+OpLine %5 7 5
+OpStore %gl_Position %24
+OpReturn
+%if_merge = OpLabel
+%55 = OpExtInst %void %1 DebugScope %45 %50
+OpLine %5 2 17
+%56 = OpLoad %float %pos
+OpLine %5 2 10
+%57 = OpCompositeConstruct %v4float %56 %float_0 %float_0 %float_0
+%58 = OpExtInst %void %1 DebugScope %43
+OpLine %5 13 3
+OpStore %gl_Position %57
+OpReturn
+OpFunctionEnd
+)");
+}
+
+TEST(IrBuilder, DebugInfoInstInFunctionOutOfBlock) {
+  // /* HLSL */
+  //
+  // float4 func2(float arg2) {   // func2_block
+  //   return float4(arg2, 0, 0, 0);
+  // }
+  //
+  // float4 func1(float arg1) {   // func1_block
+  //   if (arg1 > 1) {       // if_true_block
+  //     return float4(0, 0, 0, 0);
+  //   }
+  //   return func2(arg1);   // if_merge_block
+  // }
+  //
+  // float4 main(float pos : POSITION) : SV_POSITION {  // main
+  //   return func1(pos);
+  // }
+  const std::string text = R"(OpCapability Shader
+%1 = OpExtInstImport "OpenCL.DebugInfo.100"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %main "main" %pos %gl_Position
+%5 = OpString "block/block.hlsl"
+%6 = OpString "#line 1 \"block/block.hlsl\"
+float4 func2(float arg2) {
+  return float4(arg2, 0, 0, 0);
+}
+
+float4 func1(float arg1) {
+  if (arg1 > 1) {
+    return float4(0, 0, 0, 0);
+  }
+  return func2(arg1);
+}
+
+float4 main(float pos : POSITION) : SV_POSITION {
+  return func1(pos);
+}
+"
+OpSource HLSL 600 %5 "#line 1 \"block/block.hlsl\"
+float4 func2(float arg2) {
+  return float4(arg2, 0, 0, 0);
+}
+
+float4 func1(float arg1) {
+  if (arg1 > 1) {
+    return float4(0, 0, 0, 0);
+  }
+  return func2(arg1);
+}
+
+float4 main(float pos : POSITION) : SV_POSITION {
+  return func1(pos);
+}
+"
+%7 = OpString "float"
+%8 = OpString "main"
+%9 = OpString "v4f_main_f"
+%10 = OpString "v4f_func1_f"
+%11 = OpString "v4f_func2_f"
+%12 = OpString "pos : POSITION"
+%13 = OpString "func1"
+%14 = OpString "func2"
+OpName %main "main"
+OpName %pos "pos"
+OpName %bb_entry "bb.entry"
+OpName %param_var_arg1 "param.var.arg1"
+OpName %func1 "func1"
+OpName %arg1 "arg1"
+OpName %bb_entry_0 "bb.entry"
+OpName %param_var_arg2 "param.var.arg2"
+OpName %if_true "if.true"
+OpName %if_merge "if.merge"
+OpName %func2 "func2"
+OpName %arg2 "arg2"
+OpName %bb_entry_1 "bb.entry"
+OpDecorate %gl_Position BuiltIn Position
+OpDecorate %pos Location 0
+%float = OpTypeFloat 32
+%int = OpTypeInt 32 1
+%float_1 = OpConstant %float 1
+%float_0 = OpConstant %float 0
+%int_32 = OpConstant %int 32
+%v4float = OpTypeVector %float 4
+%32 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
+%_ptr_Input_float = OpTypePointer Input %float
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%void = OpTypeVoid
+%36 = OpTypeFunction %void
+%_ptr_Function_float = OpTypePointer Function %float
+%38 = OpTypeFunction %v4float %_ptr_Function_float
+%bool = OpTypeBool
+OpLine %5 12 25
+%pos = OpVariable %_ptr_Input_float Input
+OpLine %5 12 37
+%gl_Position = OpVariable %_ptr_Output_v4float Output
+%40 = OpExtInst %void %1 DebugSource %5 %6
+%41 = OpExtInst %void %1 DebugCompilationUnit 2 4 %40 HLSL
+%42 = OpExtInst %void %1 DebugTypeBasic %7 %int_32 Float
+%43 = OpExtInst %void %1 DebugTypeVector %42 4
+%44 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %43 %42
+%45 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %43 %42
+%46 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %43 %42
+%47 = OpExtInst %void %1 DebugFunction %8 %44 %40 12 1 %41 %9 FlagIsProtected|FlagIsPrivate 13 %main
+%48 = OpExtInst %void %1 DebugFunction %13 %45 %40 5 1 %41 %10 FlagIsProtected|FlagIsPrivate 13 %func1
+%49 = OpExtInst %void %1 DebugFunction %14 %46 %40 1 1 %41 %11 FlagIsProtected|FlagIsPrivate 13 %func2
+%50 = OpExtInst %void %1 DebugLexicalBlock %40 6 17 %48
+%51 = OpExtInst %void %1 DebugLexicalBlock %40 9 3 %48
+OpLine %5 12 1
+%main = OpFunction %void None %36
+%52 = OpExtInst %void %1 DebugScope %47
+%bb_entry = OpLabel
+OpLine %5 13 16
+%param_var_arg1 = OpVariable %_ptr_Function_float Function
+%53 = OpLoad %float %pos
+OpStore %param_var_arg1 %53
+OpLine %5 13 10
+%54 = OpFunctionCall %v4float %func1 %param_var_arg1
+OpLine %5 13 3
+OpStore %gl_Position %54
+OpReturn
+OpFunctionEnd
+OpLine %5 5 1
+%func1 = OpFunction %v4float None %38
+OpLine %5 5 20
+%arg1 = OpFunctionParameter %_ptr_Function_float
+%bb_entry_0 = OpLabel
+%55 = OpExtInst %void %1 DebugScope %48
+OpLine %5 9 16
+%param_var_arg2 = OpVariable %_ptr_Function_float Function
+OpLine %5 6 7
+%56 = OpLoad %float %arg1
+OpLine %5 6 12
+%57 = OpFOrdGreaterThan %bool %56 %float_1
+OpLine %5 6 17
+OpSelectionMerge %if_merge None
+OpBranchConditional %57 %if_true %if_merge
+%if_true = OpLabel
+%58 = OpExtInst %void %1 DebugScope %50
+OpLine %5 7 5
+OpReturnValue %32
+%if_merge = OpLabel
+%59 = OpExtInst %void %1 DebugScope %51
+OpLine %5 9 16
+%60 = OpLoad %float %arg1
+OpStore %param_var_arg2 %60
+OpLine %5 9 10
+%61 = OpFunctionCall %v4float %func2 %param_var_arg2
+OpLine %5 9 3
+OpReturnValue %61
+OpFunctionEnd
+OpLine %5 1 1
+%func2 = OpFunction %v4float None %38
+OpLine %5 1 20
+%arg2 = OpFunctionParameter %_ptr_Function_float
+%bb_entry_1 = OpLabel
+%62 = OpExtInst %void %1 DebugScope %49
+OpLine %5 2 17
+%63 = OpLoad %float %arg2
+%64 = OpCompositeConstruct %v4float %63 %float_0 %float_0 %float_0
+OpLine %5 2 3
+OpReturnValue %64
+OpFunctionEnd
+)";
+
+  SpirvTools t(SPV_ENV_UNIVERSAL_1_1);
+  std::unique_ptr<IRContext> context =
+      BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text);
+  ASSERT_EQ(nullptr, context);
+}
+
 TEST(IrBuilder, LocalGlobalVariables) {
   // #version 310 es
   //
diff --git a/test/val/val_ext_inst_test.cpp b/test/val/val_ext_inst_test.cpp
index 67df43d..01df796 100644
--- a/test/val/val_ext_inst_test.cpp
+++ b/test/val/val_ext_inst_test.cpp
@@ -33,6 +33,9 @@
 using ::testing::Not;
 
 using ValidateExtInst = spvtest::ValidateBase<bool>;
+using ValidateOldDebugInfo = spvtest::ValidateBase<std::string>;
+using ValidateOpenCL100DebugInfo = spvtest::ValidateBase<std::string>;
+using ValidateLocalDebugInfoOutOfFunction = spvtest::ValidateBase<std::string>;
 using ValidateGlslStd450SqrtLike = spvtest::ValidateBase<std::string>;
 using ValidateGlslStd450FMinLike = spvtest::ValidateBase<std::string>;
 using ValidateGlslStd450FClampLike = spvtest::ValidateBase<std::string>;
@@ -460,6 +463,462 @@
   return ss.str();
 }
 
+std::string GenerateShaderCodeForDebugInfo(
+    const std::string& op_string_instructions,
+    const std::string& op_const_instructions,
+    const std::string& debug_instructions_before_main, const std::string& body,
+    const std::string& capabilities_and_extensions = "",
+    const std::string& execution_model = "Fragment") {
+  std::ostringstream ss;
+  ss << R"(
+OpCapability Shader
+OpCapability Float16
+OpCapability Float64
+OpCapability Int16
+OpCapability Int64
+)";
+
+  ss << capabilities_and_extensions;
+  ss << "%extinst = OpExtInstImport \"GLSL.std.450\"\n";
+  ss << "OpMemoryModel Logical GLSL450\n";
+  ss << "OpEntryPoint " << execution_model << " %main \"main\""
+     << " %f32_output"
+     << " %f32vec2_output"
+     << " %u32_output"
+     << " %u32vec2_output"
+     << " %u64_output"
+     << " %f32_input"
+     << " %f32vec2_input"
+     << " %u32_input"
+     << " %u32vec2_input"
+     << " %u64_input"
+     << "\n";
+  if (execution_model == "Fragment") {
+    ss << "OpExecutionMode %main OriginUpperLeft\n";
+  }
+
+  ss << op_string_instructions;
+
+  ss << R"(
+%void = OpTypeVoid
+%func = OpTypeFunction %void
+%bool = OpTypeBool
+%f16 = OpTypeFloat 16
+%f32 = OpTypeFloat 32
+%f64 = OpTypeFloat 64
+%u32 = OpTypeInt 32 0
+%s32 = OpTypeInt 32 1
+%u64 = OpTypeInt 64 0
+%s64 = OpTypeInt 64 1
+%u16 = OpTypeInt 16 0
+%s16 = OpTypeInt 16 1
+%f32vec2 = OpTypeVector %f32 2
+%f32vec3 = OpTypeVector %f32 3
+%f32vec4 = OpTypeVector %f32 4
+%f64vec2 = OpTypeVector %f64 2
+%f64vec3 = OpTypeVector %f64 3
+%f64vec4 = OpTypeVector %f64 4
+%u32vec2 = OpTypeVector %u32 2
+%u32vec3 = OpTypeVector %u32 3
+%s32vec2 = OpTypeVector %s32 2
+%u32vec4 = OpTypeVector %u32 4
+%s32vec4 = OpTypeVector %s32 4
+%u64vec2 = OpTypeVector %u64 2
+%s64vec2 = OpTypeVector %s64 2
+%f64mat22 = OpTypeMatrix %f64vec2 2
+%f32mat22 = OpTypeMatrix %f32vec2 2
+%f32mat23 = OpTypeMatrix %f32vec2 3
+%f32mat32 = OpTypeMatrix %f32vec3 2
+%f32mat33 = OpTypeMatrix %f32vec3 3
+
+%f32_0 = OpConstant %f32 0
+%f32_1 = OpConstant %f32 1
+%f32_2 = OpConstant %f32 2
+%f32_3 = OpConstant %f32 3
+%f32_4 = OpConstant %f32 4
+%f32_h = OpConstant %f32 0.5
+%f32vec2_01 = OpConstantComposite %f32vec2 %f32_0 %f32_1
+%f32vec2_12 = OpConstantComposite %f32vec2 %f32_1 %f32_2
+%f32vec3_012 = OpConstantComposite %f32vec3 %f32_0 %f32_1 %f32_2
+%f32vec3_123 = OpConstantComposite %f32vec3 %f32_1 %f32_2 %f32_3
+%f32vec4_0123 = OpConstantComposite %f32vec4 %f32_0 %f32_1 %f32_2 %f32_3
+%f32vec4_1234 = OpConstantComposite %f32vec4 %f32_1 %f32_2 %f32_3 %f32_4
+
+%f64_0 = OpConstant %f64 0
+%f64_1 = OpConstant %f64 1
+%f64_2 = OpConstant %f64 2
+%f64_3 = OpConstant %f64 3
+%f64vec2_01 = OpConstantComposite %f64vec2 %f64_0 %f64_1
+%f64vec3_012 = OpConstantComposite %f64vec3 %f64_0 %f64_1 %f64_2
+%f64vec4_0123 = OpConstantComposite %f64vec4 %f64_0 %f64_1 %f64_2 %f64_3
+
+%f16_0 = OpConstant %f16 0
+%f16_1 = OpConstant %f16 1
+%f16_h = OpConstant %f16 0.5
+
+%u32_0 = OpConstant %u32 0
+%u32_1 = OpConstant %u32 1
+%u32_2 = OpConstant %u32 2
+%u32_3 = OpConstant %u32 3
+
+%s32_0 = OpConstant %s32 0
+%s32_1 = OpConstant %s32 1
+%s32_2 = OpConstant %s32 2
+%s32_3 = OpConstant %s32 3
+
+%u64_0 = OpConstant %u64 0
+%u64_1 = OpConstant %u64 1
+%u64_2 = OpConstant %u64 2
+%u64_3 = OpConstant %u64 3
+
+%s64_0 = OpConstant %s64 0
+%s64_1 = OpConstant %s64 1
+%s64_2 = OpConstant %s64 2
+%s64_3 = OpConstant %s64 3
+)";
+
+  ss << op_const_instructions;
+
+  ss << R"(
+%s32vec2_01 = OpConstantComposite %s32vec2 %s32_0 %s32_1
+%u32vec2_01 = OpConstantComposite %u32vec2 %u32_0 %u32_1
+
+%s32vec2_12 = OpConstantComposite %s32vec2 %s32_1 %s32_2
+%u32vec2_12 = OpConstantComposite %u32vec2 %u32_1 %u32_2
+
+%s32vec4_0123 = OpConstantComposite %s32vec4 %s32_0 %s32_1 %s32_2 %s32_3
+%u32vec4_0123 = OpConstantComposite %u32vec4 %u32_0 %u32_1 %u32_2 %u32_3
+
+%s64vec2_01 = OpConstantComposite %s64vec2 %s64_0 %s64_1
+%u64vec2_01 = OpConstantComposite %u64vec2 %u64_0 %u64_1
+
+%f32mat22_1212 = OpConstantComposite %f32mat22 %f32vec2_12 %f32vec2_12
+%f32mat23_121212 = OpConstantComposite %f32mat23 %f32vec2_12 %f32vec2_12 %f32vec2_12
+
+%f32_ptr_output = OpTypePointer Output %f32
+%f32vec2_ptr_output = OpTypePointer Output %f32vec2
+
+%u32_ptr_output = OpTypePointer Output %u32
+%u32vec2_ptr_output = OpTypePointer Output %u32vec2
+
+%u64_ptr_output = OpTypePointer Output %u64
+
+%f32_output = OpVariable %f32_ptr_output Output
+%f32vec2_output = OpVariable %f32vec2_ptr_output Output
+
+%u32_output = OpVariable %u32_ptr_output Output
+%u32vec2_output = OpVariable %u32vec2_ptr_output Output
+
+%u64_output = OpVariable %u64_ptr_output Output
+
+%f32_ptr_input = OpTypePointer Input %f32
+%f32vec2_ptr_input = OpTypePointer Input %f32vec2
+
+%u32_ptr_input = OpTypePointer Input %u32
+%u32vec2_ptr_input = OpTypePointer Input %u32vec2
+
+%u64_ptr_input = OpTypePointer Input %u64
+
+%f32_ptr_function = OpTypePointer Function %f32
+
+%f32_input = OpVariable %f32_ptr_input Input
+%f32vec2_input = OpVariable %f32vec2_ptr_input Input
+
+%u32_input = OpVariable %u32_ptr_input Input
+%u32vec2_input = OpVariable %u32vec2_ptr_input Input
+
+%u64_input = OpVariable %u64_ptr_input Input
+
+%u32_ptr_function = OpTypePointer Function %u32
+
+%struct_f16_u16 = OpTypeStruct %f16 %u16
+%struct_f32_f32 = OpTypeStruct %f32 %f32
+%struct_f32_f32_f32 = OpTypeStruct %f32 %f32 %f32
+%struct_f32_u32 = OpTypeStruct %f32 %u32
+%struct_f32_u32_f32 = OpTypeStruct %f32 %u32 %f32
+%struct_u32_f32 = OpTypeStruct %u32 %f32
+%struct_u32_u32 = OpTypeStruct %u32 %u32
+%struct_f32_f64 = OpTypeStruct %f32 %f64
+%struct_f32vec2_f32vec2 = OpTypeStruct %f32vec2 %f32vec2
+%struct_f32vec2_u32vec2 = OpTypeStruct %f32vec2 %u32vec2
+)";
+
+  ss << debug_instructions_before_main;
+
+  ss << R"(
+%main = OpFunction %void None %func
+%main_entry = OpLabel
+)";
+
+  ss << body;
+
+  ss << R"(
+OpReturn
+OpFunctionEnd)";
+
+  return ss.str();
+}
+
+TEST_F(ValidateOldDebugInfo, UseDebugInstructionOutOfFunction) {
+  const std::string src = R"(
+%code = OpString "main() {}"
+)";
+
+  const std::string dbg_inst = R"(
+%cu = OpExtInst %void %DbgExt DebugCompilationUnit %code 1 1
+)";
+
+  const std::string extension = R"(
+%DbgExt = OpExtInstImport "DebugInfo"
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "",
+                                                     extension, "Vertex"));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, UseDebugInstructionOutOfFunction) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+)";
+
+  const std::string dbg_inst = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+)";
+
+  const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "",
+                                                     extension, "Vertex"));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugSourceInFunction) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "main() {}"
+)";
+
+  const std::string dbg_inst = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+)";
+
+  const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", "", dbg_inst,
+                                                     extension, "Vertex"));
+  ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions());
+  EXPECT_THAT(
+      getDiagnosticString(),
+      HasSubstr("Debug info extension instructions other than DebugScope, "
+                "DebugNoScope, DebugDeclare, DebugValue must appear between "
+                "section 9 (types, constants, global variables) and section 10 "
+                "(function declarations)"));
+}
+
+TEST_P(ValidateLocalDebugInfoOutOfFunction, OpenCLDebugInfo100DebugScope) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "void main() {}"
+%void_name = OpString "void"
+%main_name = OpString "main"
+%main_linkage_name = OpString "v_main"
+%int_name = OpString "int"
+%foo_name = OpString "foo"
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%void_info = OpExtInst %void %DbgExt DebugTypeBasic %void_name %u32_0 Unspecified
+%int_info = OpExtInst %void %DbgExt DebugTypeBasic %int_name %u32_0 Signed
+%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void_info %void_info
+%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_linkage_name FlagIsPublic 1 %main
+%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %int_info %dbg_src 1 1 %main_info FlagIsLocal
+%expr = OpExtInst %void %DbgExt DebugExpression
+)";
+
+  const std::string body = R"(
+%foo = OpVariable %u32_ptr_function Function
+%foo_val = OpLoad %u32 %foo
+)";
+
+  const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, "", dbg_inst_header + GetParam(), body, extension, "Vertex"));
+  ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("DebugScope, DebugNoScope, DebugDeclare, DebugValue "
+                        "of debug info extension must appear in a function "
+                        "body"));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    AllLocalDebugInfo, ValidateLocalDebugInfoOutOfFunction,
+    ::testing::ValuesIn(std::vector<std::string>{
+        "%main_scope = OpExtInst %void %DbgExt DebugScope %main_info",
+        "%no_scope = OpExtInst %void %DbgExt DebugNoScope",
+    }));
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugFunctionForwardReference) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "void main() {}"
+%void_name = OpString "void"
+%main_name = OpString "main"
+%main_linkage_name = OpString "v_main"
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%void_info = OpExtInst %void %DbgExt DebugTypeBasic %void_name %u32_0 Unspecified
+%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void_info %void_info
+%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_linkage_name FlagIsPublic 1 %main
+)";
+
+  const std::string body = R"(
+%main_scope = OpExtInst %void %DbgExt DebugScope %main_info
+)";
+
+  const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, "", dbg_inst_header, body, extension, "Vertex"));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugScopeBeforeOpVariableInFunction) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "float4 main(float arg) {
+  float foo;
+  return float4(0, 0, 0, 0);
+}
+"
+%float_name = OpString "float"
+%main_name = OpString "main"
+%main_linkage_name = OpString "v4f_main_f"
+)";
+
+  const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4
+%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %v4float_info %float_info
+%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main
+)";
+
+  const std::string body = R"(
+%main_scope = OpExtInst %void %DbgExt DebugScope %main_info
+%foo = OpVariable %f32_ptr_function Function
+)";
+
+  const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, size_const, dbg_inst_header, body, extension, "Vertex"));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypeCompositeForwardReference) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "struct VS_OUTPUT {
+  float4 pos : SV_POSITION;
+  float4 color : COLOR;
+};
+main() {}
+"
+%VS_OUTPUT_name = OpString "struct VS_OUTPUT"
+%float_name = OpString "float"
+%VS_OUTPUT_pos_name = OpString "pos : SV_POSITION"
+%VS_OUTPUT_color_name = OpString "color : COLOR"
+%VS_OUTPUT_linkage_name = OpString "VS_OUTPUT"
+)";
+
+  const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+%int_128 = OpConstant %u32 128
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%VS_OUTPUT_info = OpExtInst %void %DbgExt DebugTypeComposite %VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %VS_OUTPUT_color_info
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4
+%VS_OUTPUT_pos_info = OpExtInst %void %DbgExt DebugTypeMember %VS_OUTPUT_pos_name %v4float_info %dbg_src 2 3 %VS_OUTPUT_info %u32_0 %int_128 FlagIsPublic
+%VS_OUTPUT_color_info = OpExtInst %void %DbgExt DebugTypeMember %VS_OUTPUT_color_name %v4float_info %dbg_src 3 3 %VS_OUTPUT_info %int_128 %int_128 FlagIsPublic
+)";
+
+  const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, size_const, dbg_inst_header, "", extension, "Vertex"));
+  ASSERT_EQ(SPV_SUCCESS, ValidateInstructions());
+}
+
+TEST_F(ValidateOpenCL100DebugInfo, DebugTypeCompositeMissingReference) {
+  const std::string src = R"(
+%src = OpString "simple.hlsl"
+%code = OpString "struct VS_OUTPUT {
+  float4 pos : SV_POSITION;
+  float4 color : COLOR;
+};
+main() {}
+"
+%VS_OUTPUT_name = OpString "struct VS_OUTPUT"
+%float_name = OpString "float"
+%VS_OUTPUT_pos_name = OpString "pos : SV_POSITION"
+%VS_OUTPUT_color_name = OpString "color : COLOR"
+%VS_OUTPUT_linkage_name = OpString "VS_OUTPUT"
+)";
+
+  const std::string size_const = R"(
+%int_32 = OpConstant %u32 32
+%int_128 = OpConstant %u32 128
+)";
+
+  const std::string dbg_inst_header = R"(
+%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code
+%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL
+%VS_OUTPUT_info = OpExtInst %void %DbgExt DebugTypeComposite %VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %VS_OUTPUT_color_info
+%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float
+%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4
+%VS_OUTPUT_pos_info = OpExtInst %void %DbgExt DebugTypeMember %VS_OUTPUT_pos_name %v4float_info %dbg_src 2 3 %VS_OUTPUT_info %u32_0 %int_128 FlagIsPublic
+)";
+
+  const std::string extension = R"(
+%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100"
+)";
+
+  CompileSuccessfully(GenerateShaderCodeForDebugInfo(
+      src, size_const, dbg_inst_header, "", extension, "Vertex"));
+  ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions());
+  EXPECT_THAT(getDiagnosticString(),
+              HasSubstr("forward referenced IDs have not been defined"));
+}
+
 TEST_P(ValidateGlslStd450SqrtLike, Success) {
   const std::string ext_inst_name = GetParam();
   std::ostringstream ss;