Add asm, dis support for DebugInfo extended instruction set
authorDavid Neto <dneto@google.com>
Sun, 3 Dec 2017 17:30:08 +0000 (12:30 -0500)
committerDavid Neto <dneto@google.com>
Fri, 22 Dec 2017 14:39:36 +0000 (09:39 -0500)
Add grammar file for DebugInfo extended instruction set
- Each new operand enum kind in extinst.debuginfo.grammar.json maps
  to a new value in spv_operand_type_t.
- Add new concrete enum operand types for DebugInfo

Generate a C header for the DebugInfo extended instruction set

Add table lookup of DebugInfo extended instrutions

Handle the debug info operand types in binary parser,
disassembler, and assembler.

Add DebugInfo round trip tests for assembler, disassembler

Android.mk: Support DebugInfo extended instruction set

The extinst.debuginfo.grammar.json file is currently part of
SPIRV-Tools source.

It contributes operand type enums, so it has to be processed
along with the core grammar files.

We also generate a C header DebugInfo.h.

Add necessary grammar file processing to Android.mk.

13 files changed:
Android.mk
include/spirv-tools/libspirv.h
source/CMakeLists.txt
source/binary.cpp
source/disassemble.cpp
source/ext_inst.cpp
source/extinst.debuginfo.grammar.json [new file with mode: 0644]
source/operand.cpp
source/text.cpp
test/CMakeLists.txt
test/ext_inst.debuginfo_test.cpp [new file with mode: 0644]
utils/generate_grammar_tables.py
utils/generate_language_headers.py [new file with mode: 0755]

index 08b9080..47a3004 100644 (file)
@@ -112,8 +112,11 @@ SPVTOOLS_OPT_SRC_FILES := \
 SPV_CORE10_GRAMMAR=$(SPVHEADERS_LOCAL_PATH)/include/spirv/1.0/spirv.core.grammar.json
 SPV_CORE11_GRAMMAR=$(SPVHEADERS_LOCAL_PATH)/include/spirv/1.1/spirv.core.grammar.json
 SPV_CORE12_GRAMMAR=$(SPVHEADERS_LOCAL_PATH)/include/spirv/1.2/spirv.core.grammar.json
+SPV_CORELATEST_GRAMMAR=$(SPV_CORE12_GRAMMAR)
 SPV_GLSL_GRAMMAR=$(SPVHEADERS_LOCAL_PATH)/include/spirv/1.2/extinst.glsl.std.450.grammar.json
 SPV_OPENCL_GRAMMAR=$(SPVHEADERS_LOCAL_PATH)/include/spirv/1.2/extinst.opencl.std.100.grammar.json
+# TODO(dneto): I expect the DebugInfo grammar file to eventually migrate to SPIRV-Headers
+SPV_DEBUGINFO_GRAMMAR=$(LOCAL_PATH)/source/extinst.debuginfo.grammar.json
 
 define gen_spvtools_grammar_tables
 $(call generate-file-dir,$(1)/core.insts-1.0.inc)
@@ -121,11 +124,13 @@ $(1)/core.insts-1.0.inc $(1)/operand.kinds-1.0.inc $(1)/glsl.std.450.insts.inc $
         $(LOCAL_PATH)/utils/generate_grammar_tables.py \
         $(SPV_CORE10_GRAMMAR) \
         $(SPV_GLSL_GRAMMAR) \
-        $(SPV_OPENCL_GRAMMAR)
+        $(SPV_OPENCL_GRAMMAR) \
+        $(SPV_DEBUGINFO_GRAMMAR)
                @$(HOST_PYTHON) $(LOCAL_PATH)/utils/generate_grammar_tables.py \
                                --spirv-core-grammar=$(SPV_CORE10_GRAMMAR) \
                                --extinst-glsl-grammar=$(SPV_GLSL_GRAMMAR) \
                                --extinst-opencl-grammar=$(SPV_OPENCL_GRAMMAR) \
+                               --extinst-debuginfo-grammar=$(SPV_DEBUGINFO_GRAMMAR) \
                                --core-insts-output=$(1)/core.insts-1.0.inc \
                                --glsl-insts-output=$(1)/glsl.std.450.insts.inc \
                                --opencl-insts-output=$(1)/opencl.std.insts.inc \
@@ -133,17 +138,21 @@ $(1)/core.insts-1.0.inc $(1)/operand.kinds-1.0.inc $(1)/glsl.std.450.insts.inc $
                @echo "[$(TARGET_ARCH_ABI)] Grammar v1.0   : instructions & operands <= grammar JSON files"
 $(1)/core.insts-1.1.inc $(1)/operand.kinds-1.1.inc: \
         $(LOCAL_PATH)/utils/generate_grammar_tables.py \
-        $(SPV_CORE11_GRAMMAR)
+        $(SPV_CORE11_GRAMMAR) \
+        $(SPV_DEBUGINFO_GRAMMAR)
                @$(HOST_PYTHON) $(LOCAL_PATH)/utils/generate_grammar_tables.py \
                                --spirv-core-grammar=$(SPV_CORE11_GRAMMAR) \
+                               --extinst-debuginfo-grammar=$(SPV_DEBUGINFO_GRAMMAR) \
                                --core-insts-output=$(1)/core.insts-1.1.inc \
                                --operand-kinds-output=$(1)/operand.kinds-1.1.inc
                @echo "[$(TARGET_ARCH_ABI)] Grammar v1.1   : instructions & operands <= grammar JSON files"
 $(1)/core.insts-1.2.inc $(1)/operand.kinds-1.2.inc: \
         $(LOCAL_PATH)/utils/generate_grammar_tables.py \
-        $(SPV_CORE12_GRAMMAR)
+        $(SPV_CORE12_GRAMMAR) \
+        $(SPV_DEBUGINFO_GRAMMAR)
                @$(HOST_PYTHON) $(LOCAL_PATH)/utils/generate_grammar_tables.py \
                                --spirv-core-grammar=$(SPV_CORE12_GRAMMAR) \
+                               --extinst-debuginfo-grammar=$(SPV_DEBUGINFO_GRAMMAR) \
                                --core-insts-output=$(1)/core.insts-1.2.inc \
                                --operand-kinds-output=$(1)/operand.kinds-1.2.inc
                @echo "[$(TARGET_ARCH_ABI)] Grammar v1.2   : instructions & operands <= grammar JSON files"
@@ -153,6 +162,27 @@ $(LOCAL_PATH)/source/ext_inst.cpp: $(1)/glsl.std.450.insts.inc $(1)/opencl.std.i
 endef
 $(eval $(call gen_spvtools_grammar_tables,$(SPVTOOLS_OUT_PATH)))
 
+
+define gen_spvtools_lang_headers
+# Generate language-specific headers.  So far we only generate C headers
+# $1 is the output directory.
+# $2 is the base name of the header file, e.g. "DebugInfo".
+# $3 is the grammar file containing token definitions.
+$(call generate-file-dir,$(1)/$(2).h)
+$(1)/$(2).h : \
+        $(LOCAL_PATH)/utils/generate_language_headers.py \
+        $(3)
+               @$(HOST_PYTHON) $(LOCAL_PATH)/utils/generate_language_headers.py \
+                   --extinst-name=$(2) \
+                   --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
+endef
+# We generate language-specific headers for DebugInfo
+$(eval $(call gen_spvtools_lang_headers,$(SPVTOOLS_OUT_PATH),DebugInfo,$(SPV_DEBUGINFO_GRAMMAR)))
+
+
 define gen_spvtools_vendor_tables
 $(call generate-file-dir,$(1)/$(2).insts.inc)
 $(1)/$(2).insts.inc : \
@@ -172,9 +202,10 @@ define gen_spvtools_enum_string_mapping
 $(call generate-file-dir,$(1)/extension_enum.inc.inc)
 $(1)/extension_enum.inc $(1)/enum_string_mapping.inc: \
         $(LOCAL_PATH)/utils/generate_grammar_tables.py \
-        $(SPV_CORE11_GRAMMAR)
+        $(SPV_CORELATEST_GRAMMAR)
                @$(HOST_PYTHON) $(LOCAL_PATH)/utils/generate_grammar_tables.py \
-                               --spirv-core-grammar=$(SPV_CORE11_GRAMMAR) \
+                               --spirv-core-grammar=$(SPV_CORELATEST_GRAMMAR) \
+                               --extinst-debuginfo-grammar=$(SPV_DEBUGINFO_GRAMMAR) \
                                --extension-enum-output=$(1)/extension_enum.inc \
                                --enum-string-mapping-output=$(1)/enum_string_mapping.inc
                @echo "[$(TARGET_ARCH_ABI)] Generate enum<->string mapping <= grammar JSON files"
index 36d9c19..ed7b820 100644 (file)
@@ -206,6 +206,13 @@ typedef enum spv_operand_type_t {
   // A sequence of zero or more pairs of (Id, Literal integer)
   LAST_VARIABLE(SPV_OPERAND_TYPE_VARIABLE_ID_LITERAL_INTEGER),
 
+  // The following are concrete enum types.
+  SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS,  // DebugInfo Sec 3.2.  A mask.
+  SPV_OPERAND_TYPE_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING,  // DebugInfo Sec 3.3
+  SPV_OPERAND_TYPE_DEBUG_COMPOSITE_TYPE,                // DebugInfo Sec 3.4
+  SPV_OPERAND_TYPE_DEBUG_TYPE_QUALIFIER,                // DebugInfo Sec 3.5
+  SPV_OPERAND_TYPE_DEBUG_OPERATION,                     // DebugInfo Sec 3.6
+
   // This is a sentinel value, and does not represent an operand type.
   // It should come last.
   SPV_OPERAND_TYPE_NUM_OPERAND_TYPES,
@@ -221,6 +228,7 @@ typedef enum spv_ext_inst_type_t {
   SPV_EXT_INST_TYPE_SPV_AMD_SHADER_TRINARY_MINMAX,
   SPV_EXT_INST_TYPE_SPV_AMD_GCN_SHADER,
   SPV_EXT_INST_TYPE_SPV_AMD_SHADER_BALLOT,
+  SPV_EXT_INST_TYPE_DEBUGINFO,
 
   SPV_FORCE_32_BIT_ENUM(spv_ext_inst_type_t)
 } spv_ext_inst_type_t;
index 7de6640..ce0bf7b 100644 (file)
 set(GRAMMAR_PROCESSING_SCRIPT "${spirv-tools_SOURCE_DIR}/utils/generate_grammar_tables.py")
 set(VIMSYNTAX_PROCESSING_SCRIPT "${spirv-tools_SOURCE_DIR}/utils/generate_vim_syntax.py")
 set(XML_REGISTRY_PROCESSING_SCRIPT "${spirv-tools_SOURCE_DIR}/utils/generate_registry_tables.py")
+set(LANG_HEADER_PROCESSING_SCRIPT "${spirv-tools_SOURCE_DIR}/utils/generate_language_headers.py")
+
+# For now, assume the DebugInfo grammar file is in the current directory.
+# It might migrate to SPIRV-Headers.
+set(DEBUGINFO_GRAMMAR_JSON_FILE "${CMAKE_CURRENT_SOURCE_DIR}/extinst.debuginfo.grammar.json")
 
 # macro() definitions are used in the following because we need to append .inc
 # file paths into some global lists (*_CPP_DEPENDS). And those global lists are
@@ -27,9 +32,10 @@ macro(spvtools_core_tables VERSION)
   add_custom_command(OUTPUT ${GRAMMAR_INSTS_INC_FILE} ${GRAMMAR_KINDS_INC_FILE}
     COMMAND ${PYTHON_EXECUTABLE} ${GRAMMAR_PROCESSING_SCRIPT}
       --spirv-core-grammar=${GRAMMAR_JSON_FILE}
+      --extinst-debuginfo-grammar=${DEBUGINFO_GRAMMAR_JSON_FILE}
       --core-insts-output=${GRAMMAR_INSTS_INC_FILE}
       --operand-kinds-output=${GRAMMAR_KINDS_INC_FILE}
-    DEPENDS ${GRAMMAR_PROCESSING_SCRIPT} ${GRAMMAR_JSON_FILE}
+    DEPENDS ${GRAMMAR_PROCESSING_SCRIPT} ${GRAMMAR_JSON_FILE} ${DEBUGINFO_GRAMMAR_JSON_FILE}
     COMMENT "Generate info tables for SPIR-V v${VERSION} core instructions and operands.")
   list(APPEND OPCODE_CPP_DEPENDS ${GRAMMAR_INSTS_INC_FILE})
   list(APPEND OPERAND_CPP_DEPENDS ${GRAMMAR_KINDS_INC_FILE})
@@ -43,9 +49,10 @@ macro(spvtools_enum_string_mapping VERSION)
      ${GRAMMAR_ENUM_STRING_MAPPING_INC_FILE}
     COMMAND ${PYTHON_EXECUTABLE} ${GRAMMAR_PROCESSING_SCRIPT}
       --spirv-core-grammar=${GRAMMAR_JSON_FILE}
+      --extinst-debuginfo-grammar=${DEBUGINFO_GRAMMAR_JSON_FILE}
       --extension-enum-output=${GRAMMAR_EXTENSION_ENUM_INC_FILE}
       --enum-string-mapping-output=${GRAMMAR_ENUM_STRING_MAPPING_INC_FILE}
-    DEPENDS ${GRAMMAR_PROCESSING_SCRIPT} ${GRAMMAR_JSON_FILE}
+    DEPENDS ${GRAMMAR_PROCESSING_SCRIPT} ${GRAMMAR_JSON_FILE} ${DEBUGINFO_GRAMMAR_JSON_FILE}
     COMMENT "Generate enum-string mapping for SPIR-V v${VERSION}.")
   list(APPEND EXTENSION_H_DEPENDS ${GRAMMAR_EXTENSION_ENUM_INC_FILE})
   list(APPEND ENUM_STRING_MAPPING_CPP_DEPENDS ${GRAMMAR_ENUM_STRING_MAPPING_INC_FILE})
@@ -59,11 +66,12 @@ macro(spvtools_vimsyntax VERSION CLVERSION)
   add_custom_command(OUTPUT ${VIMSYNTAX_FILE}
       COMMAND ${PYTHON_EXECUTABLE} ${VIMSYNTAX_PROCESSING_SCRIPT}
       --spirv-core-grammar=${GRAMMAR_JSON_FILE}
+      --exinst-debuginfo-grammar=${DEBUGINFO_GRAMMAR_JSON_FILE}
       --extinst-glsl-grammar=${GLSL_GRAMMAR_JSON_FILE}
       --extinst-opencl-grammar=${OPENCL_GRAMMAR_JSON_FILE}
       >${VIMSYNTAX_FILE}
     DEPENDS ${VIMSYNTAX_PROCESSING_SCRIPT} ${GRAMMAR_JSON_FILE}
-            ${GLSL_GRAMMAR_JSON_FILE} ${OPENCL_GRAMMAR_JSON_FILE}
+            ${GLSL_GRAMMAR_JSON_FILE} ${OPENCL_GRAMMAR_JSON_FILE} ${DEBUGINFO_GRAMMAR_JSON_FILE}
     COMMENT "Generate spvasm.vim: Vim syntax file for SPIR-V assembly.")
 endmacro(spvtools_vimsyntax)
 
@@ -73,7 +81,6 @@ macro(spvtools_glsl_tables VERSION)
   set(GRAMMAR_INC_FILE "${spirv-tools_BINARY_DIR}/glsl.std.450.insts.inc")
   add_custom_command(OUTPUT ${GRAMMAR_INC_FILE}
     COMMAND ${PYTHON_EXECUTABLE} ${GRAMMAR_PROCESSING_SCRIPT}
-      --spirv-core-grammar=${CORE_GRAMMAR_JSON_FILE}
       --extinst-glsl-grammar=${GLSL_GRAMMAR_JSON_FILE}
       --glsl-insts-output=${GRAMMAR_INC_FILE}
     DEPENDS ${GRAMMAR_PROCESSING_SCRIPT} ${CORE_GRAMMAR_JSON_FILE} ${GLSL_GRAMMAR_JSON_FILE}
@@ -87,7 +94,6 @@ macro(spvtools_opencl_tables VERSION)
   set(GRAMMAR_INC_FILE "${spirv-tools_BINARY_DIR}/opencl.std.insts.inc")
   add_custom_command(OUTPUT ${GRAMMAR_INC_FILE}
     COMMAND ${PYTHON_EXECUTABLE} ${GRAMMAR_PROCESSING_SCRIPT}
-      --spirv-core-grammar=${CORE_GRAMMAR_JSON_FILE}
       --extinst-opencl-grammar=${OPENCL_GRAMMAR_JSON_FILE}
       --opencl-insts-output=${GRAMMAR_INC_FILE}
     DEPENDS ${GRAMMAR_PROCESSING_SCRIPT} ${CORE_GRAMMAR_JSON_FILE} ${OPENCL_GRAMMAR_JSON_FILE}
@@ -109,6 +115,22 @@ macro(spvtools_vendor_tables VENDOR_TABLE)
   set_property(TARGET spirv-tools-${VENDOR_TABLE} PROPERTY FOLDER "SPIRV-Tools build")
 endmacro(spvtools_vendor_tables)
 
+macro(spvtools_extinst_lang_headers NAME GRAMMAR_FILE)
+  set(OUTBASE ${spirv-tools_BINARY_DIR}/${NAME})
+  set(OUT_H ${OUTBASE}.h)
+  add_custom_command(OUTPUT ${OUT_H}
+    COMMAND ${PYTHON_EXECUTABLE} ${LANG_HEADER_PROCESSING_SCRIPT}
+      --extinst-name=${NAME}
+      --extinst-grammar=${GRAMMAR_FILE}
+      --extinst-output-base=${OUTBASE}
+    DEPENDS ${LANG_HEADER_PROCESSING_SCRIPT} ${GRAMMAR_FILE}
+    COMMENT "Generate language specific header for ${NAME}.")
+  list(APPEND EXTINST_CPP_DEPENDS ${OUT_H})
+  add_custom_target(spirv-tools-header-${NAME} DEPENDS ${OUT_H})
+  set_property(TARGET spirv-tools-header-${NAME} PROPERTY FOLDER "SPIRV-Tools build")
+endmacro(spvtools_extinst_lang_headers)
+
+
 spvtools_core_tables("1.0")
 spvtools_core_tables("1.1")
 spvtools_core_tables("1.2")
@@ -119,6 +141,8 @@ spvtools_vendor_tables("spv-amd-shader-explicit-vertex-parameter")
 spvtools_vendor_tables("spv-amd-shader-trinary-minmax")
 spvtools_vendor_tables("spv-amd-gcn-shader")
 spvtools_vendor_tables("spv-amd-shader-ballot")
+spvtools_vendor_tables("debuginfo")
+spvtools_extinst_lang_headers("DebugInfo" ${DEBUGINFO_GRAMMAR_JSON_FILE})
 
 spvtools_vimsyntax("1.2" "1.0")
 add_custom_target(spirv-tools-vimsyntax DEPENDS ${VIMSYNTAX_FILE})
index d6d28b6..10523e7 100644 (file)
@@ -613,7 +613,11 @@ spv_result_t Parser::parseOperand(size_t inst_offset,
     case SPV_OPERAND_TYPE_BUILT_IN:
     case SPV_OPERAND_TYPE_GROUP_OPERATION:
     case SPV_OPERAND_TYPE_KERNEL_ENQ_FLAGS:
-    case SPV_OPERAND_TYPE_KERNEL_PROFILING_INFO: {
+    case SPV_OPERAND_TYPE_KERNEL_PROFILING_INFO:
+    case SPV_OPERAND_TYPE_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING:
+    case SPV_OPERAND_TYPE_DEBUG_COMPOSITE_TYPE:
+    case SPV_OPERAND_TYPE_DEBUG_TYPE_QUALIFIER:
+    case SPV_OPERAND_TYPE_DEBUG_OPERATION: {
       // A single word that is a plain enum value.
 
       // Map an optional operand type to its corresponding concrete type.
@@ -636,7 +640,8 @@ spv_result_t Parser::parseOperand(size_t inst_offset,
     case SPV_OPERAND_TYPE_IMAGE:
     case SPV_OPERAND_TYPE_OPTIONAL_IMAGE:
     case SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS:
-    case SPV_OPERAND_TYPE_SELECTION_CONTROL: {
+    case SPV_OPERAND_TYPE_SELECTION_CONTROL:
+    case SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS: {
       // This operand is a mask.
 
       // Map an optional operand type to its corresponding concrete type.
index af8cae1..5230a50 100644 (file)
@@ -265,7 +265,11 @@ void Disassembler::EmitOperand(const spv_parsed_instruction_t& inst,
     case SPV_OPERAND_TYPE_BUILT_IN:
     case SPV_OPERAND_TYPE_GROUP_OPERATION:
     case SPV_OPERAND_TYPE_KERNEL_ENQ_FLAGS:
-    case SPV_OPERAND_TYPE_KERNEL_PROFILING_INFO: {
+    case SPV_OPERAND_TYPE_KERNEL_PROFILING_INFO:
+    case SPV_OPERAND_TYPE_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING:
+    case SPV_OPERAND_TYPE_DEBUG_COMPOSITE_TYPE:
+    case SPV_OPERAND_TYPE_DEBUG_TYPE_QUALIFIER:
+    case SPV_OPERAND_TYPE_DEBUG_OPERATION: {
       spv_operand_desc entry;
       if (grammar_.lookupOperand(operand.type, word, &entry))
         assert(false && "should have caught this earlier");
@@ -277,6 +281,7 @@ void Disassembler::EmitOperand(const spv_parsed_instruction_t& inst,
     case SPV_OPERAND_TYPE_IMAGE:
     case SPV_OPERAND_TYPE_MEMORY_ACCESS:
     case SPV_OPERAND_TYPE_SELECTION_CONTROL:
+    case SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS:
       EmitMaskOperand(operand.type, word);
       break;
     default:
index fa2c9b6..19122a2 100644 (file)
 #include <cassert>
 #include <cstring>
 
+// DebugInfo extended instruction set.
+// See https://www.khronos.org/registry/spir-v/specs/1.0/DebugInfo.html
+// TODO(dneto): DebugInfo.h should probably move to SPIRV-Headers.
+#include "DebugInfo.h"
+
 #include "latest_version_glsl_std_450_header.h"
 #include "latest_version_opencl_std_header.h"
-#include "spirv_definition.h"
-
 #include "macro.h"
+#include "spirv_definition.h"
 
+#include "debuginfo.insts.inc"     // defines opencl_entries
 #include "glsl.std.450.insts.inc"  // defines glsl_entries
 #include "opencl.std.insts.inc"    // defines opencl_entries
 
@@ -44,6 +49,8 @@ static const spv_ext_inst_group_t kGroups_1_0[] = {
      ARRAY_SIZE(spv_amd_gcn_shader_entries), spv_amd_gcn_shader_entries},
     {SPV_EXT_INST_TYPE_SPV_AMD_SHADER_BALLOT,
      ARRAY_SIZE(spv_amd_shader_ballot_entries), spv_amd_shader_ballot_entries},
+    {SPV_EXT_INST_TYPE_DEBUGINFO, ARRAY_SIZE(debuginfo_entries),
+     debuginfo_entries},
 };
 
 static const spv_ext_inst_table_t kTable_1_0 = {ARRAY_SIZE(kGroups_1_0),
@@ -101,6 +108,9 @@ spv_ext_inst_type_t spvExtInstImportTypeGet(const char* name) {
   if (!strcmp("SPV_AMD_shader_ballot", name)) {
     return SPV_EXT_INST_TYPE_SPV_AMD_SHADER_BALLOT;
   }
+  if (!strcmp("DebugInfo", name)) {
+    return SPV_EXT_INST_TYPE_DEBUGINFO;
+  }
   return SPV_EXT_INST_TYPE_NONE;
 }
 
diff --git a/source/extinst.debuginfo.grammar.json b/source/extinst.debuginfo.grammar.json
new file mode 100644 (file)
index 0000000..9212f6f
--- /dev/null
@@ -0,0 +1,568 @@
+{
+  "copyright" : [
+    "Copyright (c) 2017 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."
+  ],
+  "version" : 100,
+  "revision" : 1,
+  "instructions" : [
+    {
+      "opname" : "DebugInfoNone",
+      "opcode" : 0
+    },
+    {
+      "opname" : "DebugCompilationUnit",
+      "opcode" : 1,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Source'" },
+        { "kind" : "LiteralInteger", "name" : "'Version'" },
+        { "kind" : "LiteralInteger", "name" : "'DWARF Version'" }
+      ]
+    },
+    {
+      "opname" : "DebugTypeBasic",
+      "opcode" : 2,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Name'" },
+        { "kind" : "IdRef", "name" : "'Size'" },
+        { "kind" : "DebugBaseTypeAttributeEncoding", "name" : "'Encoding'" }
+      ]
+    },
+    {
+      "opname" : "DebugTypePointer",
+      "opcode" : 3,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Base Type'" },
+        { "kind" : "StorageClass", "name" : "'Storage Class'" },
+        { "kind" : "DebugInfoFlags", "name" : "'Literal Flags'" }
+      ]
+    },
+    {
+      "opname" : "DebugTypeQualifier",
+      "opcode" : 4,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Base Type'" },
+        { "kind" : "DebugTypeQualifier", "name" : "'Type Qualifier'" }
+      ]
+    },
+    {
+      "opname" : "DebugTypeArray",
+      "opcode" : 5,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Base Type'" },
+        { "kind" : "IdRef", "name" : "'Component Counts'", "quantifier" : "*" }
+      ]
+    },
+    {
+      "opname" : "DebugTypeVector",
+      "opcode" : 6,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Base Type'" },
+        { "kind" : "LiteralInteger", "name" : "'Component Count'" }
+      ]
+    },
+    {
+      "opname" : "DebugTypedef",
+      "opcode" : 7,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Name'" },
+        { "kind" : "IdRef", "name" : "'Base Type'" },
+        { "kind" : "IdRef", "name" : "'Source'" },
+        { "kind" : "LiteralInteger", "name" : "'Line'" },
+        { "kind" : "LiteralInteger", "name" : "'Column'" },
+        { "kind" : "IdRef", "name" : "'Parent'" }
+      ]
+    },
+    {
+      "opname" : "DebugTypeFunction",
+      "opcode" : 8,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Return Type'" },
+        { "kind" : "IdRef", "name" : "'Paramter Types'", "quantifier" : "*" }
+      ]
+    },
+    {
+      "opname" : "DebugTypeEnum",
+      "opcode" : 9,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Name'" },
+        { "kind" : "IdRef", "name" : "'Underlying Type'" },
+        { "kind" : "IdRef", "name" : "'Source'" },
+        { "kind" : "LiteralInteger", "name" : "'Line'" },
+        { "kind" : "LiteralInteger", "name" : "'Column'" },
+        { "kind" : "IdRef", "name" : "'Parent'" },
+        { "kind" : "IdRef", "name" : "'Size'" },
+        { "kind" : "DebugInfoFlags", "name" : "'Flags'" },
+        { "kind" : "PairIdRefIdRef", "name" : "'Value, Name, Value, Name, ...'", "quantifier" : "*" }
+      ]
+    },
+    {
+      "opname" : "DebugTypeComposite",
+      "opcode" : 10,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Name'" },
+        { "kind" : "DebugCompositeType", "name" : "'Tag'" },
+        { "kind" : "IdRef", "name" : "'Source'" },
+        { "kind" : "LiteralInteger", "name" : "'Line'" },
+        { "kind" : "LiteralInteger", "name" : "'Column'" },
+        { "kind" : "IdRef", "name" : "'Parent'" },
+        { "kind" : "IdRef", "name" : "'Size'" },
+        { "kind" : "DebugInfoFlags", "name" : "'Flags'" },
+        { "kind" : "IdRef", "name" : "'Members'", "quantifier" : "*" }
+      ]
+    },
+    {
+      "opname" : "DebugTypeMember",
+      "opcode" : 11,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Name'" },
+        { "kind" : "IdRef", "name" : "'Type'" },
+        { "kind" : "IdRef", "name" : "'Source'" },
+        { "kind" : "LiteralInteger", "name" : "'Line'" },
+        { "kind" : "LiteralInteger", "name" : "'Column'" },
+        { "kind" : "IdRef", "name" : "'Parent'" },
+        { "kind" : "IdRef", "name" : "'Offset'" },
+        { "kind" : "IdRef", "name" : "'Size'" },
+        { "kind" : "DebugInfoFlags", "name" : "'Flags'" },
+        { "kind" : "IdRef", "name" : "'Value'", "quantifier" : "?" }
+      ]
+    },
+    {
+      "opname" : "DebugTypeInheritance",
+      "opcode" : 12,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Child'" },
+        { "kind" : "IdRef", "name" : "'Parent'" },
+        { "kind" : "IdRef", "name" : "'Offset'" },
+        { "kind" : "IdRef", "name" : "'Size'" },
+        { "kind" : "DebugInfoFlags", "name" : "'Flags'" }
+      ]
+    },
+    {
+      "opname" : "DebugTypePtrToMember",
+      "opcode" : 13,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Member Type'" },
+        { "kind" : "IdRef", "name" : "'Parent'" }
+      ]
+    },
+    {
+      "opname" : "DebugTypeTemplate",
+      "opcode" : 14,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Target'" },
+        { "kind" : "IdRef", "name" : "'Parameters'", "quantifier" : "*" }
+      ]
+    },
+    {
+      "opname" : "DebugTypeTemplateParameter",
+      "opcode" : 15,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Name'" },
+        { "kind" : "IdRef", "name" : "'Actual Type'" },
+        { "kind" : "IdRef", "name" : "'Value'" },
+        { "kind" : "IdRef", "name" : "'Source'" },
+        { "kind" : "LiteralInteger", "name" : "'Line'" },
+        { "kind" : "LiteralInteger", "name" : "'Column'" }
+      ]
+    },
+    {
+      "opname" : "DebugTypeTemplateTemplateParameter",
+      "opcode" : 16,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Name'" },
+        { "kind" : "IdRef", "name" : "'Template Name'" },
+        { "kind" : "IdRef", "name" : "'Source'" },
+        { "kind" : "LiteralInteger", "name" : "'Line'" },
+        { "kind" : "LiteralInteger", "name" : "'Column'" }
+      ]
+    },
+    {
+      "opname" : "DebugTypeTemplateParameterPack",
+      "opcode" : 17,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Name'" },
+        { "kind" : "IdRef", "name" : "'Source'" },
+        { "kind" : "LiteralInteger", "name" : "'Line'" },
+        { "kind" : "LiteralInteger", "name" : "'Column'" },
+        { "kind" : "IdRef", "name" : "'Template Parameters'", "quantifier" : "*" }
+      ]
+    },
+    {
+      "opname" : "DebugGlobalVariable",
+      "opcode" : 18,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Name'" },
+        { "kind" : "IdRef", "name" : "'Type'" },
+        { "kind" : "IdRef", "name" : "'Source'" },
+        { "kind" : "LiteralInteger", "name" : "'Line'" },
+        { "kind" : "LiteralInteger", "name" : "'Column'" },
+        { "kind" : "IdRef", "name" : "'Parent'" },
+        { "kind" : "IdRef", "name" : "'Linkage Name'" },
+        { "kind" : "IdRef", "name" : "'Variable'" },
+        { "kind" : "DebugInfoFlags", "name" : "'Flags'" },
+        { "kind" : "IdRef", "name" : "'Static Member Declaration'", "quantifier" : "?" }
+      ]
+    },
+    {
+      "opname" : "DebugFunctionDeclaration",
+      "opcode" : 19,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Name'" },
+        { "kind" : "IdRef", "name" : "'Type'" },
+        { "kind" : "IdRef", "name" : "'Source'" },
+        { "kind" : "LiteralInteger", "name" : "'Line'" },
+        { "kind" : "LiteralInteger", "name" : "'Column'" },
+        { "kind" : "IdRef", "name" : "'Parent'" },
+        { "kind" : "IdRef", "name" : "'Linkage Name'" },
+        { "kind" : "DebugInfoFlags", "name" : "'Flags'" }
+      ]
+    },
+    {
+      "opname" : "DebugFunction",
+      "opcode" : 20,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Name'" },
+        { "kind" : "IdRef", "name" : "'Type'" },
+        { "kind" : "IdRef", "name" : "'Source'" },
+        { "kind" : "LiteralInteger", "name" : "'Line'" },
+        { "kind" : "LiteralInteger", "name" : "'Column'" },
+        { "kind" : "IdRef", "name" : "'Parent'" },
+        { "kind" : "IdRef", "name" : "'Linkage Name'" },
+        { "kind" : "DebugInfoFlags", "name" : "'Flags'" },
+        { "kind" : "LiteralInteger", "name" : "'Scope Line'" },
+        { "kind" : "IdRef", "name" : "'Function'" },
+        { "kind" : "IdRef", "name" : "'Declaration'", "quantifier" : "?" }
+      ]
+    },
+    {
+      "opname" : "DebugLexicalBlock",
+      "opcode" : 21,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Source'" },
+        { "kind" : "LiteralInteger", "name" : "'Line'" },
+        { "kind" : "LiteralInteger", "name" : "'Column'" },
+        { "kind" : "IdRef", "name" : "'Parent'" },
+        { "kind" : "IdRef", "name" : "'Name'", "quantifier" : "?" }
+      ]
+    },
+    {
+      "opname" : "DebugLexicalBlockDiscriminator",
+      "opcode" : 22,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Scope'" },
+        { "kind" : "LiteralInteger", "name" : "'Discriminator'" },
+        { "kind" : "IdRef", "name" : "'Parent'" }
+      ]
+    },
+    {
+      "opname" : "DebugScope",
+      "opcode" : 23,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Scope'" },
+        { "kind" : "IdRef", "name" : "'Inlined At'", "quantifier" : "?" }
+      ]
+    },
+    {
+      "opname" : "DebugNoScope",
+      "opcode" : 24
+    },
+    {
+      "opname" : "DebugInlinedAt",
+      "opcode" : 25,
+      "operands" : [
+        { "kind" : "LiteralInteger", "name" : "'Line'" },
+        { "kind" : "IdRef", "name" : "'Scope'" },
+        { "kind" : "IdRef", "name" : "'Inlined'", "quantifier" : "?" }
+      ]
+    },
+    {
+      "opname" : "DebugLocalVariable",
+      "opcode" : 26,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Name'" },
+        { "kind" : "IdRef", "name" : "'Type'" },
+        { "kind" : "IdRef", "name" : "'Source'" },
+        { "kind" : "LiteralInteger", "name" : "'Line'" },
+        { "kind" : "LiteralInteger", "name" : "'Column'" },
+        { "kind" : "IdRef", "name" : "'Parent'" },
+        { "kind" : "LiteralInteger", "name" : "'Arg Number'", "quantifier" : "?" }
+      ]
+    },
+    {
+      "opname" : "DebugInlinedVariable",
+      "opcode" : 27,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Variable'" },
+        { "kind" : "IdRef", "name" : "'Inlined'" }
+      ]
+    },
+    {
+      "opname" : "DebugDeclare",
+      "opcode" : 28,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Local Variable'" },
+        { "kind" : "IdRef", "name" : "'Variable'" },
+        { "kind" : "IdRef", "name" : "'Expression'" }
+      ]
+    },
+    {
+      "opname" : "DebugValue",
+      "opcode" : 29,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Value'" },
+        { "kind" : "IdRef", "name" : "'Expression'" },
+        { "kind" : "IdRef", "name" : "'Indexes'", "quantifier" : "*" }
+      ]
+    },
+    {
+      "opname" : "DebugOperation",
+      "opcode" : 30,
+      "operands" : [
+        { "kind" : "DebugOperation", "name" : "'OpCode'" },
+        { "kind" : "LiteralInteger", "name" : "'Operands ...'", "quantifier" : "*" }
+      ]
+    },
+    {
+      "opname" : "DebugExpression",
+      "opcode" : 31,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Operands ...'", "quantifier" : "*" }
+      ]
+    },
+    {
+      "opname" : "DebugMacroDef",
+      "opcode" : 32,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Source'" },
+        { "kind" : "LiteralInteger", "name" : "'Line'" },
+        { "kind" : "IdRef", "name" : "'Name'" },
+        { "kind" : "IdRef", "name" : "'Value'", "quantifier" : "?" }
+      ]
+    },
+    {
+      "opname" : "DebugMacroUndef",
+      "opcode" : 33,
+      "operands" : [
+        { "kind" : "IdRef", "name" : "'Source'" },
+        { "kind" : "LiteralInteger", "name" : "'Line'" },
+        { "kind" : "IdRef", "name" : "'Macro'" }
+      ]
+    }
+  ],
+  "operand_kinds" : [
+    {
+      "category" : "BitEnum",
+      "kind" : "DebugInfoFlags",
+      "enumerants" : [
+        {
+          "enumerant" : "FlagIsProtected",
+          "value" : "0x01"
+        },
+        {
+          "enumerant" : "FlagIsPrivate",
+          "value" : "0x02"
+        },
+        {
+          "enumerant" : "FlagIsPublic",
+          "value" : "0x03"
+        },
+        {
+          "enumerant" : "FlagIsLocal",
+          "value" : "0x04"
+        },
+        {
+          "enumerant" : "FlagIsDefinition",
+          "value" : "0x08"
+        },
+        {
+          "enumerant" : "FlagFwdDecl",
+          "value" : "0x10"
+        },
+        {
+          "enumerant" : "FlagArtificial",
+          "value" : "0x20"
+        },
+        {
+          "enumerant" : "FlagExplicit",
+          "value" : "0x40"
+        },
+        {
+          "enumerant" : "FlagPrototyped",
+          "value" : "0x80"
+        },
+        {
+          "enumerant" : "FlagObjectPointer",
+          "value" : "0x100"
+        },
+        {
+          "enumerant" : "FlagStaticMember",
+          "value" : "0x200"
+        },
+        {
+          "enumerant" : "FlagIndirectVariable",
+          "value" : "0x400"
+        },
+        {
+          "enumerant" : "FlagLValueReference",
+          "value" : "0x800"
+        },
+        {
+          "enumerant" : "FlagRValueReference",
+          "value" : "0x1000"
+        },
+        {
+          "enumerant" : "FlagIsOptimized",
+          "value" : "0x2000"
+        }
+      ]
+    },
+    {
+      "category" : "ValueEnum",
+      "kind" : "DebugBaseTypeAttributeEncoding",
+      "enumerants" : [
+        {
+          "enumerant" : "Unspecified",
+          "value" : "0"
+        },
+        {
+          "enumerant" : "Address",
+          "value" : "1"
+        },
+        {
+          "enumerant" : "Boolean",
+          "value" : "2"
+        },
+        {
+          "enumerant" : "Float",
+          "value" : "4"
+        },
+        {
+          "enumerant" : "Signed",
+          "value" : "5"
+        },
+        {
+          "enumerant" : "SignedChar",
+          "value" : "6"
+        },
+        {
+          "enumerant" : "Unsigned",
+          "value" : "7"
+        },
+        {
+          "enumerant" : "UnsignedChar",
+          "value" : "8"
+        }
+      ]
+    },
+    {
+      "category" : "ValueEnum",
+      "kind" : "DebugCompositeType",
+      "enumerants" : [
+        {
+          "enumerant" : "Class",
+          "value" : "0"
+        },
+        {
+          "enumerant" : "Structure",
+          "value" : "1"
+        },
+        {
+          "enumerant" : "Union",
+          "value" : "2"
+        }
+      ]
+    },
+    {
+      "category" : "ValueEnum",
+      "kind" : "DebugTypeQualifier",
+      "enumerants" : [
+        {
+          "enumerant" : "ConstType",
+          "value" : "0"
+        },
+        {
+          "enumerant" : "VolatileType",
+          "value" : "1"
+        },
+        {
+          "enumerant" : "RestrictType",
+          "value" : "2"
+        }
+      ]
+    },
+    {
+      "category" : "ValueEnum",
+      "kind" : "DebugOperation",
+      "enumerants" : [
+        {
+          "enumerant" : "Deref",
+          "value" : "0"
+        },
+        {
+          "enumerant" : "Plus",
+          "value" : "1"
+        },
+        {
+          "enumerant" : "Minus",
+          "value" : "2"
+        },
+        {
+          "enumerant" : "PlusUconst",
+          "value" : "3",
+          "parameters" : [
+             { "kind" : "LiteralInteger" }
+          ]
+        },
+        {
+          "enumerant" : "BitPiece",
+          "value" : "4",
+          "parameters" : [
+             { "kind" : "LiteralInteger" },
+             { "kind" : "LiteralInteger" }
+          ]
+        },
+        {
+          "enumerant" : "Swap",
+          "value" : "5"
+        },
+        {
+          "enumerant" : "Xderef",
+          "value" : "6"
+        },
+        {
+          "enumerant" : "StackValue",
+          "value" : "7"
+        },
+        {
+          "enumerant" : "Constu",
+          "value" : "8",
+          "parameters" : [
+             { "kind" : "LiteralInteger" }
+          ]
+        }
+      ]
+    }
+  ]
+}
index 96c2223..8959f5b 100644 (file)
@@ -200,6 +200,16 @@ const char* spvOperandTypeStr(spv_operand_type_t type) {
       return "image";
     case SPV_OPERAND_TYPE_OPTIONAL_CIV:
       return "context-insensitive value";
+    case SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS:
+      return "debug info flags";
+    case SPV_OPERAND_TYPE_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING:
+      return "debug base type encoding";
+    case SPV_OPERAND_TYPE_DEBUG_COMPOSITE_TYPE:
+      return "debug composite type";
+    case SPV_OPERAND_TYPE_DEBUG_TYPE_QUALIFIER:
+      return "debug type qualifier";
+    case SPV_OPERAND_TYPE_DEBUG_OPERATION:
+      return "debug operation";
 
     // The next values are for values returned from an instruction, not actually
     // an operand.  So the specific strings don't matter.  But let's add them
@@ -278,6 +288,10 @@ bool spvOperandIsConcrete(spv_operand_type_t type) {
     case SPV_OPERAND_TYPE_KERNEL_ENQ_FLAGS:
     case SPV_OPERAND_TYPE_KERNEL_PROFILING_INFO:
     case SPV_OPERAND_TYPE_CAPABILITY:
+    case SPV_OPERAND_TYPE_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING:
+    case SPV_OPERAND_TYPE_DEBUG_COMPOSITE_TYPE:
+    case SPV_OPERAND_TYPE_DEBUG_TYPE_QUALIFIER:
+    case SPV_OPERAND_TYPE_DEBUG_OPERATION:
       return true;
     default:
       break;
@@ -293,6 +307,7 @@ bool spvOperandIsConcreteMask(spv_operand_type_t type) {
     case SPV_OPERAND_TYPE_LOOP_CONTROL:
     case SPV_OPERAND_TYPE_FUNCTION_CONTROL:
     case SPV_OPERAND_TYPE_MEMORY_ACCESS:
+    case SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS:
       return true;
     default:
       break;
index 78a3e58..bf18bcc 100644 (file)
@@ -366,13 +366,16 @@ spv_result_t spvTextEncodeOperand(const libspirv::AssemblyGrammar& grammar,
       if (context->binaryEncodeString(literal.str.c_str(), pInst))
         return SPV_ERROR_INVALID_TEXT;
     } break;
+
+    // Masks.
     case SPV_OPERAND_TYPE_FP_FAST_MATH_MODE:
     case SPV_OPERAND_TYPE_FUNCTION_CONTROL:
     case SPV_OPERAND_TYPE_LOOP_CONTROL:
     case SPV_OPERAND_TYPE_IMAGE:
     case SPV_OPERAND_TYPE_OPTIONAL_IMAGE:
     case SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS:
-    case SPV_OPERAND_TYPE_SELECTION_CONTROL: {
+    case SPV_OPERAND_TYPE_SELECTION_CONTROL:
+    case SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS: {
       uint32_t value;
       if (grammar.parseMaskOperand(type, textValue, &value)) {
         return context->diagnostic() << "Invalid " << spvOperandTypeStr(type)
index fb8c4b5..4ede989 100644 (file)
@@ -92,6 +92,7 @@ set(TEST_SOURCES
   comment_test.cpp
   enum_string_mapping_test.cpp
   enum_set_test.cpp
+  ext_inst.debuginfo_test.cpp
   ext_inst.glsl_test.cpp
   ext_inst.opencl_test.cpp
   fix_word_test.cpp
diff --git a/test/ext_inst.debuginfo_test.cpp b/test/ext_inst.debuginfo_test.cpp
new file mode 100644 (file)
index 0000000..608d119
--- /dev/null
@@ -0,0 +1,808 @@
+// Copyright (c) 2017 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "unit_spirv.h"
+
+#include <gmock/gmock.h>
+#include "DebugInfo.h"
+#include "test_fixture.h"
+
+// This file tests the correctness of encoding and decoding of instructions
+// involving the DebugInfo extended instruction set.
+// Semantic correctness should be the responsibility of validator.
+//
+// See https://www.khronos.org/registry/spir-v/specs/1.0/DebugInfo.html
+
+namespace {
+
+using spvtest::Concatenate;
+using spvtest::MakeInstruction;
+using spvtest::MakeVector;
+using testing::Eq;
+
+struct InstructionCase {
+  uint32_t opcode;
+  std::string name;
+  std::string operands;
+  std::vector<uint32_t> expected_operands;
+};
+
+using ExtInstDebugInfoRoundTripTest =
+    spvtest::TextToBinaryTestBase<::testing::TestWithParam<InstructionCase>>;
+using ExtInstDebugInfoRoundTripTestExplicit = spvtest::TextToBinaryTest;
+
+TEST_P(ExtInstDebugInfoRoundTripTest, ParameterizedExtInst) {
+  const std::string input =
+      "%1 = OpExtInstImport \"DebugInfo\"\n"
+      "%3 = OpExtInst %2 %1 " +
+      GetParam().name + GetParam().operands + "\n";
+  // First make sure it assembles correctly.
+  EXPECT_THAT(
+      CompiledInstructions(input),
+      Eq(Concatenate(
+          {MakeInstruction(SpvOpExtInstImport, {1}, MakeVector("DebugInfo")),
+           MakeInstruction(SpvOpExtInst, {2, 3, 1, GetParam().opcode},
+                           GetParam().expected_operands)})))
+      << input;
+  // Now check the round trip through the disassembler.
+  EXPECT_THAT(EncodeAndDecodeSuccessfully(input), input) << input;
+}
+
+#define CASE_0(Enum)                                      \
+  {                                                       \
+    uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, "", {} \
+  }
+
+#define CASE_ILL(Enum, L0, L1)                                           \
+  {                                                                      \
+    uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, " %4 " #L0 " " #L1, { \
+      4, L0, L1                                                          \
+    }                                                                    \
+  }
+
+#define CASE_IL(Enum, L0)                                                \
+  {                                                                      \
+    uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, " %4 " #L0, { 4, L0 } \
+  }
+
+#define CASE_I(Enum)                                            \
+  {                                                             \
+    uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, " %4", { 4 } \
+  }
+
+#define CASE_II(Enum)                                                 \
+  {                                                                   \
+    uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, " %4 %5", { 4, 5 } \
+  }
+
+#define CASE_III(Enum)                                                      \
+  {                                                                         \
+    uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, " %4 %5 %6", { 4, 5, 6 } \
+  }
+
+#define CASE_IIII(Enum)                                              \
+  {                                                                  \
+    uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, " %4 %5 %6 %7", { \
+      4, 5, 6, 7                                                     \
+    }                                                                \
+  }
+
+#define CASE_IIIII(Enum)                                                \
+  {                                                                     \
+    uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, " %4 %5 %6 %7 %8", { \
+      4, 5, 6, 7, 8                                                     \
+    }                                                                   \
+  }
+
+#define CASE_IIIIII(Enum)                                                  \
+  {                                                                        \
+    uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, " %4 %5 %6 %7 %8 %9", { \
+      4, 5, 6, 7, 8, 9                                                     \
+    }                                                                      \
+  }
+
+#define CASE_IIIIIII(Enum)                                                     \
+  {                                                                            \
+    uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, " %4 %5 %6 %7 %8 %9 %10", { \
+      4, 5, 6, 7, 8, 9, 10                                                     \
+    }                                                                          \
+  }
+
+#define CASE_IIILLI(Enum, L0, L1)                  \
+  {                                                \
+    uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, \
+        " %4 %5 %6 " #L0 " " #L1 " %7", {          \
+      4, 5, 6, L0, L1, 7                           \
+    }                                              \
+  }
+
+#define CASE_IIILLIL(Enum, L0, L1, L2)             \
+  {                                                \
+    uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, \
+        " %4 %5 %6 " #L0 " " #L1 " %7 " #L2, {     \
+      4, 5, 6, L0, L1, 7, L2                       \
+    }                                              \
+  }
+
+#define CASE_IE(Enum, E0)                                        \
+  {                                                              \
+    uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, " %4 " #E0, { \
+      4, uint32_t(DebugInfo##E0)                                 \
+    }                                                            \
+  }
+
+#define CASE_IIE(Enum, E0)                                          \
+  {                                                                 \
+    uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, " %4 %5 " #E0, { \
+      4, 5, uint32_t(DebugInfo##E0)                                 \
+    }                                                               \
+  }
+
+#define CASE_ISF(Enum, S0, Fstr, Fnum)                                    \
+  {                                                                       \
+    uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, " %4 " #S0 " " Fstr, { \
+      4, uint32_t(SpvStorageClass##S0), Fnum                              \
+    }                                                                     \
+  }
+
+#define CASE_LII(Enum, L0)                                             \
+  {                                                                    \
+    uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, " " #L0 " %4 %5", { \
+      L0, 4, 5                                                         \
+    }                                                                  \
+  }
+
+#define CASE_ILI(Enum, L0)                                             \
+  {                                                                    \
+    uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, " %4 " #L0 " %5", { \
+      4, L0, 5                                                         \
+    }                                                                  \
+  }
+
+#define CASE_ILII(Enum, L0)                                               \
+  {                                                                       \
+    uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, " %4 " #L0 " %5 %6", { \
+      4, L0, 5, 6                                                         \
+    }                                                                     \
+  }
+
+#define CASE_ILLII(Enum, L0, L1)                   \
+  {                                                \
+    uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, \
+        " %4 " #L0 " " #L1 " %5 %6", {             \
+      4, L0, L1, 5, 6                              \
+    }                                              \
+  }
+
+#define CASE_IIILLIIF(Enum, L0, L1, Fstr, Fnum)    \
+  {                                                \
+    uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, \
+        " %4 %5 %6 " #L0 " " #L1 " %7 %8 " Fstr, { \
+      4, 5, 6, L0, L1, 7, 8, Fnum                  \
+    }                                              \
+  }
+
+#define CASE_IIILLIIFII(Enum, L0, L1, Fstr, Fnum)            \
+  {                                                          \
+    uint32_t(DebugInfoDebug##Enum), "Debug" #Enum,           \
+        " %4 %5 %6 " #L0 " " #L1 " %7 %8 " Fstr " %9 %10", { \
+      4, 5, 6, L0, L1, 7, 8, Fnum, 9, 10                     \
+    }                                                        \
+  }
+
+#define CASE_IIILLIIFIIII(Enum, L0, L1, Fstr, Fnum)                  \
+  {                                                                  \
+    uint32_t(DebugInfoDebug##Enum), "Debug" #Enum,                   \
+        " %4 %5 %6 " #L0 " " #L1 " %7 %8 " Fstr " %9 %10 %11 %12", { \
+      4, 5, 6, L0, L1, 7, 8, Fnum, 9, 10, 11, 12                     \
+    }                                                                \
+  }
+
+#define CASE_IIILLIIFIIIIII(Enum, L0, L1, Fstr, Fnum)                        \
+  {                                                                          \
+    uint32_t(DebugInfoDebug##Enum), "Debug" #Enum,                           \
+        " %4 %5 %6 " #L0 " " #L1 " %7 %8 " Fstr " %9 %10 %11 %12 %13 %14", { \
+      4, 5, 6, L0, L1, 7, 8, Fnum, 9, 10, 11, 12, 13, 14                     \
+    }                                                                        \
+  }
+
+#define CASE_IEILLIIF(Enum, E0, L0, L1, Fstr, Fnum)     \
+  {                                                     \
+    uint32_t(DebugInfoDebug##Enum), "Debug" #Enum,      \
+        " %4 " #E0 " %5 " #L0 " " #L1 " %6 %7 " Fstr, { \
+      4, uint32_t(DebugInfo##E0), 5, L0, L1, 6, 7, Fnum \
+    }                                                   \
+  }
+
+#define CASE_IEILLIIFI(Enum, E0, L0, L1, Fstr, Fnum)          \
+  {                                                           \
+    uint32_t(DebugInfoDebug##Enum), "Debug" #Enum,            \
+        " %4 " #E0 " %5 " #L0 " " #L1 " %6 %7 " Fstr " %8", { \
+      4, uint32_t(DebugInfo##E0), 5, L0, L1, 6, 7, Fnum, 8    \
+    }                                                         \
+  }
+
+#define CASE_IEILLIIFII(Enum, E0, L0, L1, Fstr, Fnum)            \
+  {                                                              \
+    uint32_t(DebugInfoDebug##Enum), "Debug" #Enum,               \
+        " %4 " #E0 " %5 " #L0 " " #L1 " %6 %7 " Fstr " %8 %9", { \
+      4, uint32_t(DebugInfo##E0), 5, L0, L1, 6, 7, Fnum, 8, 9    \
+    }                                                            \
+  }
+
+#define CASE_IEILLIIFIII(Enum, E0, L0, L1, Fstr, Fnum)               \
+  {                                                                  \
+    uint32_t(DebugInfoDebug##Enum), "Debug" #Enum,                   \
+        " %4 " #E0 " %5 " #L0 " " #L1 " %6 %7 " Fstr " %8 %9 %10", { \
+      4, uint32_t(DebugInfo##E0), 5, L0, L1, 6, 7, Fnum, 8, 9, 10    \
+    }                                                                \
+  }
+
+#define CASE_IEILLIIFIIII(Enum, E0, L0, L1, Fstr, Fnum)                  \
+  {                                                                      \
+    uint32_t(DebugInfoDebug##Enum), "Debug" #Enum,                       \
+        " %4 " #E0 " %5 " #L0 " " #L1 " %6 %7 " Fstr " %8 %9 %10 %11", { \
+      4, uint32_t(DebugInfo##E0), 5, L0, L1, 6, 7, Fnum, 8, 9, 10, 11    \
+    }                                                                    \
+  }
+
+#define CASE_IIILLIIIF(Enum, L0, L1, Fstr, Fnum)      \
+  {                                                   \
+    uint32_t(DebugInfoDebug##Enum), "Debug" #Enum,    \
+        " %4 %5 %6 " #L0 " " #L1 " %7 %8 %9 " Fstr, { \
+      4, 5, 6, L0, L1, 7, 8, 9, Fnum                  \
+    }                                                 \
+  }
+
+#define CASE_IIILLIIIFI(Enum, L0, L1, Fstr, Fnum)            \
+  {                                                          \
+    uint32_t(DebugInfoDebug##Enum), "Debug" #Enum,           \
+        " %4 %5 %6 " #L0 " " #L1 " %7 %8 %9 " Fstr " %10", { \
+      4, 5, 6, L0, L1, 7, 8, 9, Fnum, 10                     \
+    }                                                        \
+  }
+
+#define CASE_IIIIF(Enum, Fstr, Fnum)                                       \
+  {                                                                        \
+    uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, " %4 %5 %6 %7 " Fstr, { \
+      4, 5, 6, 7, Fnum                                                     \
+    }                                                                      \
+  }
+
+#define CASE_IIILL(Enum, L0, L1)                                               \
+  {                                                                            \
+    uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, " %4 %5 %6 " #L0 " " #L1, { \
+      4, 5, 6, L0, L1                                                          \
+    }                                                                          \
+  }
+
+#define CASE_IIIILL(Enum, L0, L1)                  \
+  {                                                \
+    uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, \
+        " %4 %5 %6 %7 " #L0 " " #L1, {             \
+      4, 5, 6, 7, L0, L1                           \
+    }                                              \
+  }
+
+#define CASE_IILLI(Enum, L0, L1)                   \
+  {                                                \
+    uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, \
+        " %4 %5 " #L0 " " #L1 " %6", {             \
+      4, 5, L0, L1, 6                              \
+    }                                              \
+  }
+
+#define CASE_IILLII(Enum, L0, L1)                  \
+  {                                                \
+    uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, \
+        " %4 %5 " #L0 " " #L1 " %6 %7", {          \
+      4, 5, L0, L1, 6, 7                           \
+    }                                              \
+  }
+
+#define CASE_IILLIII(Enum, L0, L1)                 \
+  {                                                \
+    uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, \
+        " %4 %5 " #L0 " " #L1 " %6 %7 %8", {       \
+      4, 5, L0, L1, 6, 7, 8                        \
+    }                                              \
+  }
+
+#define CASE_IILLIIII(Enum, L0, L1)                \
+  {                                                \
+    uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, \
+        " %4 %5 " #L0 " " #L1 " %6 %7 %8 %9", {    \
+      4, 5, L0, L1, 6, 7, 8, 9                     \
+    }                                              \
+  }
+
+#define CASE_IIILLIIFLI(Enum, L0, L1, Fstr, Fnum, L2)            \
+  {                                                              \
+    uint32_t(DebugInfoDebug##Enum), "Debug" #Enum,               \
+        " %4 %5 %6 " #L0 " " #L1 " %7 %8 " Fstr " " #L2 " %9", { \
+      4, 5, 6, L0, L1, 7, 8, Fnum, L2, 9                         \
+    }                                                            \
+  }
+
+#define CASE_IIILLIIFLII(Enum, L0, L1, Fstr, Fnum, L2)               \
+  {                                                                  \
+    uint32_t(DebugInfoDebug##Enum), "Debug" #Enum,                   \
+        " %4 %5 %6 " #L0 " " #L1 " %7 %8 " Fstr " " #L2 " %9 %10", { \
+      4, 5, 6, L0, L1, 7, 8, Fnum, L2, 9, 10                         \
+    }                                                                \
+  }
+
+#define CASE_E(Enum, E0)                                      \
+  {                                                           \
+    uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, " " #E0, { \
+      uint32_t(DebugInfo##E0)                                 \
+    }                                                         \
+  }
+
+#define CASE_EL(Enum, E0, L0)                                         \
+  {                                                                   \
+    uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, " " #E0 " " #L0, { \
+      uint32_t(DebugInfo##E0), L0                                     \
+    }                                                                 \
+  }
+
+#define CASE_ELL(Enum, E0, L0, L1)                                            \
+  {                                                                           \
+    uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, " " #E0 " " #L0 " " #L1, { \
+      uint32_t(DebugInfo##E0), L0, L1                                         \
+    }                                                                         \
+  }
+
+// DebugInfo 4.1 Absent Debugging Information
+INSTANTIATE_TEST_CASE_P(DebugInfoDebugInfoNone, ExtInstDebugInfoRoundTripTest,
+                        ::testing::ValuesIn(std::vector<InstructionCase>({
+                            CASE_0(InfoNone),  // enum value 0
+                        })), );
+
+// DebugInfo 4.2 Compilation Unit
+INSTANTIATE_TEST_CASE_P(DebugInfoDebugCompilationUnit,
+                        ExtInstDebugInfoRoundTripTest,
+                        ::testing::ValuesIn(std::vector<InstructionCase>({
+                            CASE_ILL(CompilationUnit, 100, 42),
+                        })), );
+
+// DebugInfo 4.3 Type instructions
+INSTANTIATE_TEST_CASE_P(DebugInfoDebugTypeBasic, ExtInstDebugInfoRoundTripTest,
+                        ::testing::ValuesIn(std::vector<InstructionCase>({
+                            CASE_IIE(TypeBasic, Unspecified),
+                            CASE_IIE(TypeBasic, Address),
+                            CASE_IIE(TypeBasic, Boolean),
+                            CASE_IIE(TypeBasic, Float),
+                            CASE_IIE(TypeBasic, Signed),
+                            CASE_IIE(TypeBasic, SignedChar),
+                            CASE_IIE(TypeBasic, Unsigned),
+                            CASE_IIE(TypeBasic, UnsignedChar),
+                        })), );
+
+// The FlagIsPublic is value is (1 << 0) | (1 << 2) which is the same
+// as the bitwise-OR of FlagIsProtected and FlagIsPrivate.
+// The disassembler will emit the compound expression instead.
+// There is no simple fix for this.  This enum is not really a mask
+// for the bottom two bits.
+TEST_F(ExtInstDebugInfoRoundTripTestExplicit, FlagIsPublic) {
+  const std::string prefix =
+      "%1 = OpExtInstImport \"DebugInfo\"\n"
+      "%3 = OpExtInst %2 %1 DebugTypePointer %4 Private ";
+  const std::string input = prefix + "FlagIsPublic\n";
+  const std::string expected = prefix + "FlagIsProtected|FlagIsPrivate\n";
+  // First make sure it assembles correctly.
+  EXPECT_THAT(
+      CompiledInstructions(input),
+      Eq(Concatenate(
+          {MakeInstruction(SpvOpExtInstImport, {1}, MakeVector("DebugInfo")),
+           MakeInstruction(SpvOpExtInst, {2, 3, 1, DebugInfoDebugTypePointer, 4,
+                                          uint32_t(SpvStorageClassPrivate),
+                                          DebugInfoFlagIsPublic})})))
+      << input;
+  // Now check the round trip through the disassembler.
+  EXPECT_THAT(EncodeAndDecodeSuccessfully(input), Eq(expected)) << input;
+}
+
+INSTANTIATE_TEST_CASE_P(
+    DebugInfoDebugTypePointer, ExtInstDebugInfoRoundTripTest,
+    ::testing::ValuesIn(std::vector<InstructionCase>({
+
+        //// Use each flag independently.
+        CASE_ISF(TypePointer, Private, "FlagIsProtected",
+                 uint32_t(DebugInfoFlagIsProtected)),
+        CASE_ISF(TypePointer, Private, "FlagIsPrivate",
+                 uint32_t(DebugInfoFlagIsPrivate)),
+
+        // FlagIsPublic is tested above.
+
+        CASE_ISF(TypePointer, Private, "FlagIsLocal",
+                 uint32_t(DebugInfoFlagIsLocal)),
+        CASE_ISF(TypePointer, Private, "FlagIsDefinition",
+                 uint32_t(DebugInfoFlagIsDefinition)),
+        CASE_ISF(TypePointer, Private, "FlagFwdDecl",
+                 uint32_t(DebugInfoFlagFwdDecl)),
+        CASE_ISF(TypePointer, Private, "FlagArtificial",
+                 uint32_t(DebugInfoFlagArtificial)),
+        CASE_ISF(TypePointer, Private, "FlagExplicit",
+                 uint32_t(DebugInfoFlagExplicit)),
+        CASE_ISF(TypePointer, Private, "FlagPrototyped",
+                 uint32_t(DebugInfoFlagPrototyped)),
+        CASE_ISF(TypePointer, Private, "FlagObjectPointer",
+                 uint32_t(DebugInfoFlagObjectPointer)),
+        CASE_ISF(TypePointer, Private, "FlagStaticMember",
+                 uint32_t(DebugInfoFlagStaticMember)),
+        CASE_ISF(TypePointer, Private, "FlagIndirectVariable",
+                 uint32_t(DebugInfoFlagIndirectVariable)),
+        CASE_ISF(TypePointer, Private, "FlagLValueReference",
+                 uint32_t(DebugInfoFlagLValueReference)),
+        CASE_ISF(TypePointer, Private, "FlagIsOptimized",
+                 uint32_t(DebugInfoFlagIsOptimized)),
+
+        //// Use flags in combination, and try different storage classes.
+        CASE_ISF(TypePointer, Function, "FlagIsProtected|FlagIsPrivate",
+                 uint32_t(DebugInfoFlagIsProtected) |
+                     uint32_t(DebugInfoFlagIsPrivate)),
+        CASE_ISF(
+            TypePointer, Workgroup,
+            "FlagIsPrivate|FlagFwdDecl|FlagIndirectVariable|FlagIsOptimized",
+            uint32_t(DebugInfoFlagIsPrivate) | uint32_t(DebugInfoFlagFwdDecl) |
+                uint32_t(DebugInfoFlagIndirectVariable) |
+                uint32_t(DebugInfoFlagIsOptimized)),
+
+    })), );
+
+INSTANTIATE_TEST_CASE_P(DebugInfoDebugTypeQualifier,
+                        ExtInstDebugInfoRoundTripTest,
+                        ::testing::ValuesIn(std::vector<InstructionCase>({
+                            CASE_IE(TypeQualifier, ConstType),
+                            CASE_IE(TypeQualifier, VolatileType),
+                            CASE_IE(TypeQualifier, RestrictType),
+                        })), );
+
+INSTANTIATE_TEST_CASE_P(DebugInfoDebugTypeArray, ExtInstDebugInfoRoundTripTest,
+                        ::testing::ValuesIn(std::vector<InstructionCase>({
+                            CASE_II(TypeArray),
+                            CASE_III(TypeArray),
+                            CASE_IIII(TypeArray),
+                            CASE_IIIII(TypeArray),
+                        })), );
+
+INSTANTIATE_TEST_CASE_P(DebugInfoDebugTypeVector, ExtInstDebugInfoRoundTripTest,
+                        ::testing::ValuesIn(std::vector<InstructionCase>({
+                            CASE_IL(TypeVector, 2),
+                            CASE_IL(TypeVector, 3),
+                            CASE_IL(TypeVector, 4),
+                            CASE_IL(TypeVector, 16),
+                        })), );
+
+INSTANTIATE_TEST_CASE_P(DebugInfoDebugTypedef, ExtInstDebugInfoRoundTripTest,
+                        ::testing::ValuesIn(std::vector<InstructionCase>({
+                            CASE_IIILLI(Typedef, 12, 13),
+                            CASE_IIILLI(Typedef, 14, 99),
+                        })), );
+
+INSTANTIATE_TEST_CASE_P(DebugInfoDebugTypeFunction,
+                        ExtInstDebugInfoRoundTripTest,
+                        ::testing::ValuesIn(std::vector<InstructionCase>({
+                            CASE_I(TypeFunction),
+                            CASE_II(TypeFunction),
+                            CASE_III(TypeFunction),
+                            CASE_IIII(TypeFunction),
+                            CASE_IIIII(TypeFunction),
+                        })), );
+
+INSTANTIATE_TEST_CASE_P(
+    DebugInfoDebugTypeEnum, ExtInstDebugInfoRoundTripTest,
+    ::testing::ValuesIn(std::vector<InstructionCase>({
+        CASE_IIILLIIFII(
+            TypeEnum, 12, 13,
+            "FlagIsPrivate|FlagFwdDecl|FlagIndirectVariable|FlagIsOptimized",
+            uint32_t(DebugInfoFlagIsPrivate) | uint32_t(DebugInfoFlagFwdDecl) |
+                uint32_t(DebugInfoFlagIndirectVariable) |
+                uint32_t(DebugInfoFlagIsOptimized)),
+        CASE_IIILLIIFIIII(TypeEnum, 17, 18, "FlagStaticMember",
+                          uint32_t(DebugInfoFlagStaticMember)),
+        CASE_IIILLIIFIIIIII(TypeEnum, 99, 1, "FlagStaticMember",
+                            uint32_t(DebugInfoFlagStaticMember)),
+    })), );
+
+INSTANTIATE_TEST_CASE_P(
+    DebugInfoDebugTypeComposite, ExtInstDebugInfoRoundTripTest,
+    ::testing::ValuesIn(std::vector<InstructionCase>({
+        CASE_IEILLIIF(
+            TypeComposite, Class, 12, 13,
+            "FlagIsPrivate|FlagFwdDecl|FlagIndirectVariable|FlagIsOptimized",
+            uint32_t(DebugInfoFlagIsPrivate) | uint32_t(DebugInfoFlagFwdDecl) |
+                uint32_t(DebugInfoFlagIndirectVariable) |
+                uint32_t(DebugInfoFlagIsOptimized)),
+        // Cover all tag values: Class, Structure, Union
+        CASE_IEILLIIF(TypeComposite, Class, 12, 13, "FlagIsPrivate",
+                      uint32_t(DebugInfoFlagIsPrivate)),
+        CASE_IEILLIIF(TypeComposite, Structure, 12, 13, "FlagIsPrivate",
+                      uint32_t(DebugInfoFlagIsPrivate)),
+        CASE_IEILLIIF(TypeComposite, Union, 12, 13, "FlagIsPrivate",
+                      uint32_t(DebugInfoFlagIsPrivate)),
+        // Now add members
+        CASE_IEILLIIFI(TypeComposite, Class, 9, 10, "FlagIsPrivate",
+                       uint32_t(DebugInfoFlagIsPrivate)),
+        CASE_IEILLIIFII(TypeComposite, Class, 9, 10, "FlagIsPrivate",
+                        uint32_t(DebugInfoFlagIsPrivate)),
+        CASE_IEILLIIFIII(TypeComposite, Class, 9, 10, "FlagIsPrivate",
+                         uint32_t(DebugInfoFlagIsPrivate)),
+        CASE_IEILLIIFIIII(TypeComposite, Class, 9, 10, "FlagIsPrivate",
+                          uint32_t(DebugInfoFlagIsPrivate)),
+    })), );
+
+INSTANTIATE_TEST_CASE_P(DebugInfoDebugTypeMember, ExtInstDebugInfoRoundTripTest,
+                        ::testing::ValuesIn(std::vector<InstructionCase>({
+                            CASE_IIILLIIIF(TypeMember, 12, 13, "FlagIsPrivate",
+                                           uint32_t(DebugInfoFlagIsPrivate)),
+                            CASE_IIILLIIIF(TypeMember, 99, 100,
+                                           "FlagIsPrivate|FlagFwdDecl",
+                                           uint32_t(DebugInfoFlagIsPrivate) |
+                                               uint32_t(DebugInfoFlagFwdDecl)),
+                            // Add the optional Id argument.
+                            CASE_IIILLIIIFI(TypeMember, 12, 13, "FlagIsPrivate",
+                                            uint32_t(DebugInfoFlagIsPrivate)),
+                        })), );
+
+INSTANTIATE_TEST_CASE_P(
+    DebugInfoDebugTypeInheritance, ExtInstDebugInfoRoundTripTest,
+    ::testing::ValuesIn(std::vector<InstructionCase>({
+        CASE_IIIIF(TypeInheritance, "FlagIsPrivate",
+                   uint32_t(DebugInfoFlagIsPrivate)),
+        CASE_IIIIF(TypeInheritance, "FlagIsPrivate|FlagFwdDecl",
+                   uint32_t(DebugInfoFlagIsPrivate) |
+                       uint32_t(DebugInfoFlagFwdDecl)),
+    })), );
+
+INSTANTIATE_TEST_CASE_P(DebugInfoDebugTypePtrToMember,
+                        ExtInstDebugInfoRoundTripTest,
+                        ::testing::ValuesIn(std::vector<InstructionCase>({
+                            CASE_II(TypePtrToMember),
+                        })), );
+
+// DebugInfo 4.4 Templates
+
+INSTANTIATE_TEST_CASE_P(DebugInfoDebugTypeTemplate,
+                        ExtInstDebugInfoRoundTripTest,
+                        ::testing::ValuesIn(std::vector<InstructionCase>({
+                            CASE_II(TypeTemplate),
+                            CASE_III(TypeTemplate),
+                            CASE_IIII(TypeTemplate),
+                            CASE_IIIII(TypeTemplate),
+                        })), );
+
+INSTANTIATE_TEST_CASE_P(DebugInfoDebugTypeTemplateParameter,
+                        ExtInstDebugInfoRoundTripTest,
+                        ::testing::ValuesIn(std::vector<InstructionCase>({
+                            CASE_IIIILL(TypeTemplateParameter, 1, 2),
+                            CASE_IIIILL(TypeTemplateParameter, 99, 102),
+                            CASE_IIIILL(TypeTemplateParameter, 10, 7),
+                        })), );
+
+INSTANTIATE_TEST_CASE_P(DebugInfoDebugTypeTemplateTemplateParameter,
+                        ExtInstDebugInfoRoundTripTest,
+                        ::testing::ValuesIn(std::vector<InstructionCase>({
+                            CASE_IIILL(TypeTemplateTemplateParameter, 1, 2),
+                            CASE_IIILL(TypeTemplateTemplateParameter, 99, 102),
+                            CASE_IIILL(TypeTemplateTemplateParameter, 10, 7),
+                        })), );
+
+INSTANTIATE_TEST_CASE_P(DebugInfoDebugTypeTemplateParameterPack,
+                        ExtInstDebugInfoRoundTripTest,
+                        ::testing::ValuesIn(std::vector<InstructionCase>({
+                            CASE_IILLI(TypeTemplateParameterPack, 1, 2),
+                            CASE_IILLII(TypeTemplateParameterPack, 99, 102),
+                            CASE_IILLIII(TypeTemplateParameterPack, 10, 7),
+                            CASE_IILLIIII(TypeTemplateParameterPack, 10, 7),
+                        })), );
+
+// DebugInfo 4.5 Global Variables
+
+INSTANTIATE_TEST_CASE_P(
+    DebugInfoDebugGlobalVariable, ExtInstDebugInfoRoundTripTest,
+    ::testing::ValuesIn(std::vector<InstructionCase>({
+        CASE_IIILLIIIF(GlobalVariable, 1, 2, "FlagIsOptimized",
+                       uint32_t(DebugInfoFlagIsOptimized)),
+        CASE_IIILLIIIF(GlobalVariable, 42, 43, "FlagIsOptimized",
+                       uint32_t(DebugInfoFlagIsOptimized)),
+        CASE_IIILLIIIFI(GlobalVariable, 1, 2, "FlagIsOptimized",
+                        uint32_t(DebugInfoFlagIsOptimized)),
+        CASE_IIILLIIIFI(GlobalVariable, 42, 43, "FlagIsOptimized",
+                        uint32_t(DebugInfoFlagIsOptimized)),
+    })), );
+
+// DebugInfo 4.6 Functions
+
+INSTANTIATE_TEST_CASE_P(
+    DebugInfoDebugFunctionDeclaration, ExtInstDebugInfoRoundTripTest,
+    ::testing::ValuesIn(std::vector<InstructionCase>({
+        CASE_IIILLIIF(FunctionDeclaration, 1, 2, "FlagIsOptimized",
+                      uint32_t(DebugInfoFlagIsOptimized)),
+        CASE_IIILLIIF(FunctionDeclaration, 42, 43, "FlagFwdDecl",
+                      uint32_t(DebugInfoFlagFwdDecl)),
+    })), );
+
+INSTANTIATE_TEST_CASE_P(
+    DebugInfoDebugFunction, ExtInstDebugInfoRoundTripTest,
+    ::testing::ValuesIn(std::vector<InstructionCase>({
+        CASE_IIILLIIFLI(Function, 1, 2, "FlagIsOptimized",
+                        uint32_t(DebugInfoFlagIsOptimized), 3),
+        CASE_IIILLIIFLI(Function, 42, 43, "FlagFwdDecl",
+                        uint32_t(DebugInfoFlagFwdDecl), 44),
+        // Add the optional declaration Id.
+        CASE_IIILLIIFLII(Function, 1, 2, "FlagIsOptimized",
+                         uint32_t(DebugInfoFlagIsOptimized), 3),
+        CASE_IIILLIIFLII(Function, 42, 43, "FlagFwdDecl",
+                         uint32_t(DebugInfoFlagFwdDecl), 44),
+    })), );
+
+// DebugInfo 4.7 Local Information
+
+INSTANTIATE_TEST_CASE_P(DebugInfoDebugLexicalBlock,
+                        ExtInstDebugInfoRoundTripTest,
+                        ::testing::ValuesIn(std::vector<InstructionCase>({
+                            CASE_ILLII(LexicalBlock, 1, 2),
+                            CASE_ILLII(LexicalBlock, 42, 43),
+                        })), );
+
+INSTANTIATE_TEST_CASE_P(DebugInfoDebugLexicalBlockDiscriminator,
+                        ExtInstDebugInfoRoundTripTest,
+                        ::testing::ValuesIn(std::vector<InstructionCase>({
+                            CASE_ILI(LexicalBlockDiscriminator, 1),
+                            CASE_ILI(LexicalBlockDiscriminator, 42),
+                        })), );
+
+INSTANTIATE_TEST_CASE_P(DebugInfoDebugScope, ExtInstDebugInfoRoundTripTest,
+                        ::testing::ValuesIn(std::vector<InstructionCase>({
+                            CASE_I(Scope),
+                            CASE_II(Scope),
+                        })), );
+
+INSTANTIATE_TEST_CASE_P(DebugInfoDebugNoScope, ExtInstDebugInfoRoundTripTest,
+                        ::testing::ValuesIn(std::vector<InstructionCase>({
+                            CASE_0(NoScope),
+                        })), );
+
+INSTANTIATE_TEST_CASE_P(DebugInfoDebugInlinedAt, ExtInstDebugInfoRoundTripTest,
+                        ::testing::ValuesIn(std::vector<InstructionCase>({
+                            CASE_LII(InlinedAt, 1),
+                            CASE_LII(InlinedAt, 42),
+                        })), );
+
+// DebugInfo 4.8 Local Variables
+
+INSTANTIATE_TEST_CASE_P(DebugInfoDebugLocalVariable,
+                        ExtInstDebugInfoRoundTripTest,
+                        ::testing::ValuesIn(std::vector<InstructionCase>({
+                            CASE_IIILLI(LocalVariable, 1, 2),
+                            CASE_IIILLI(LocalVariable, 42, 43),
+                            CASE_IIILLIL(LocalVariable, 1, 2, 3),
+                            CASE_IIILLIL(LocalVariable, 42, 43, 44),
+                        })), );
+
+INSTANTIATE_TEST_CASE_P(DebugInfoDebugInlinedVariable,
+                        ExtInstDebugInfoRoundTripTest,
+                        ::testing::ValuesIn(std::vector<InstructionCase>({
+                            CASE_II(InlinedVariable),
+                        })), );
+
+INSTANTIATE_TEST_CASE_P(DebugInfoDebugDebugDeclare,
+                        ExtInstDebugInfoRoundTripTest,
+                        ::testing::ValuesIn(std::vector<InstructionCase>({
+                            CASE_III(Declare),
+                        })), );
+
+INSTANTIATE_TEST_CASE_P(
+    DebugInfoDebugDebugValue, ExtInstDebugInfoRoundTripTest,
+    ::testing::ValuesIn(std::vector<InstructionCase>({
+        CASE_III(Value),
+        CASE_IIII(Value),
+        CASE_IIIII(Value),
+        CASE_IIIIII(Value),
+        // Test up to 4 id parameters. We can always try more.
+        CASE_IIIIIII(Value),
+    })), );
+
+INSTANTIATE_TEST_CASE_P(DebugInfoDebugDebugOperation,
+                        ExtInstDebugInfoRoundTripTest,
+                        ::testing::ValuesIn(std::vector<InstructionCase>({
+                            CASE_E(Operation, Deref),
+                            CASE_E(Operation, Plus),
+                            CASE_E(Operation, Minus),
+                            CASE_EL(Operation, PlusUconst, 1),
+                            CASE_EL(Operation, PlusUconst, 42),
+                            CASE_ELL(Operation, BitPiece, 1, 2),
+                            CASE_ELL(Operation, BitPiece, 4, 5),
+                            CASE_E(Operation, Swap),
+                            CASE_E(Operation, Xderef),
+                            CASE_E(Operation, StackValue),
+                            CASE_EL(Operation, Constu, 1),
+                            CASE_EL(Operation, Constu, 42),
+                        })), );
+
+INSTANTIATE_TEST_CASE_P(DebugInfoDebugDebugExpression,
+                        ExtInstDebugInfoRoundTripTest,
+                        ::testing::ValuesIn(std::vector<InstructionCase>({
+                            CASE_0(Expression),
+                            CASE_I(Expression),
+                            CASE_II(Expression),
+                            CASE_III(Expression),
+                            CASE_IIII(Expression),
+                            CASE_IIIII(Expression),
+                            CASE_IIIIII(Expression),
+                            CASE_IIIIIII(Expression),
+                        })), );
+
+// DebugInfo 4.9 Macros
+
+INSTANTIATE_TEST_CASE_P(DebugInfoDebugMacroDef, ExtInstDebugInfoRoundTripTest,
+                        ::testing::ValuesIn(std::vector<InstructionCase>({
+                            CASE_ILI(MacroDef, 1),
+                            CASE_ILI(MacroDef, 42),
+                            CASE_ILII(MacroDef, 1),
+                            CASE_ILII(MacroDef, 42),
+                        })), );
+
+INSTANTIATE_TEST_CASE_P(DebugInfoDebugMacroUndef, ExtInstDebugInfoRoundTripTest,
+                        ::testing::ValuesIn(std::vector<InstructionCase>({
+                            CASE_ILI(MacroUndef, 1),
+                            CASE_ILI(MacroUndef, 42),
+                        })), );
+
+#undef CASE_0
+#undef CASE_ILL
+#undef CASE_IL
+#undef CASE_I
+#undef CASE_II
+#undef CASE_III
+#undef CASE_IIII
+#undef CASE_IIIII
+#undef CASE_IIIIII
+#undef CASE_IIIIIII
+#undef CASE_IIILLI
+#undef CASE_IIILLIL
+#undef CASE_IE
+#undef CASE_IIE
+#undef CASE_ISF
+#undef CASE_LII
+#undef CASE_ILI
+#undef CASE_ILII
+#undef CASE_ILLII
+#undef CASE_IIILLIIF
+#undef CASE_IIILLIIFII
+#undef CASE_IIILLIIFIIII
+#undef CASE_IIILLIIFIIIIII
+#undef CASE_IEILLIIF
+#undef CASE_IEILLIIFI
+#undef CASE_IEILLIIFII
+#undef CASE_IEILLIIFIII
+#undef CASE_IEILLIIFIIII
+#undef CASE_IIILLIIIF
+#undef CASE_IIILLIIIFI
+#undef CASE_IIIIF
+#undef CASE_IIILL
+#undef CASE_IIIILL
+#undef CASE_IILLI
+#undef CASE_IILLII
+#undef CASE_IILLIII
+#undef CASE_IILLIIII
+#undef CASE_IIILLIIFLI
+#undef CASE_IIILLIIFLII
+#undef CASE_E
+#undef CASE_EL
+#undef CASE_ELL
+
+}  // anonymous namespace
index d1ad0bd..a6faf37 100755 (executable)
@@ -612,6 +612,10 @@ def main():
                         type=str, required=False,
                         help='input JSON grammar file for core SPIR-V '
                         'instructions')
+    parser.add_argument('--extinst-debuginfo-grammar', metavar='<path>',
+                        type=str, required=False, default=None,
+                        help='input JSON grammar file for DebugInfo extended '
+                        'instruction set')
     parser.add_argument('--extinst-glsl-grammar', metavar='<path>',
                         type=str, required=False, default=None,
                         help='input JSON grammar file for GLSL extended '
@@ -650,9 +654,13 @@ def main():
 
     if (args.core_insts_output is None) != \
             (args.operand_kinds_output is None):
-        print('error: --core-insts-output and --operand_kinds_output '
+        print('error: --core-insts-output and --operand-kinds-output '
               'should be specified together.')
         exit(1)
+    if args.operand_kinds_output and not (args.spirv_core_grammar and args.extinst_debuginfo_grammar):
+        print('error: --operand-kinds-output requires --spirv-core-grammar '
+              'and --exinst-debuginfo-grammar')
+        exit(1)
     if (args.glsl_insts_output is None) != \
             (args.extinst_glsl_grammar is None):
         print('error: --glsl-insts-output and --extinst-glsl-grammar '
@@ -680,26 +688,28 @@ def main():
     if args.spirv_core_grammar is not None:
         with open(args.spirv_core_grammar) as json_file:
             grammar = json.loads(json_file.read())
-            if args.core_insts_output is not None:
-                make_path_to_file(args.core_insts_output)
-                make_path_to_file(args.operand_kinds_output)
-                version = '{}_{}'.format(grammar['major_version'],
-                                         grammar['minor_version'])
-                print(generate_instruction_table(
-                        grammar['instructions'], version),
-                      file=open(args.core_insts_output, 'w'))
-                print(generate_operand_kind_table(
-                        grammar['operand_kinds'], version),
-                      file=open(args.operand_kinds_output, 'w'))
-            if args.extension_enum_output is not None:
-                make_path_to_file(args.extension_enum_output)
-                print(generate_extension_enum(grammar['operand_kinds']),
-                      file=open(args.extension_enum_output, 'w'))
-            if args.enum_string_mapping_output is not None:
-                make_path_to_file(args.enum_string_mapping_output)
-                print(generate_all_string_enum_mappings(
-                          grammar['operand_kinds']),
-                      file=open(args.enum_string_mapping_output, 'w'))
+            with open(args.extinst_debuginfo_grammar) as debuginfo_json_file:
+                debuginfo_grammar = json.loads(debuginfo_json_file.read())
+                operand_kinds = grammar['operand_kinds']
+                operand_kinds.extend(debuginfo_grammar['operand_kinds'])
+               if args.core_insts_output is not None:
+                   make_path_to_file(args.core_insts_output)
+                   make_path_to_file(args.operand_kinds_output)
+                   version = '{}_{}'.format(grammar['major_version'],
+                                            grammar['minor_version'])
+                   print(generate_instruction_table(
+                           grammar['instructions'], version),
+                         file=open(args.core_insts_output, 'w'))
+                   print(generate_operand_kind_table(operand_kinds, version),
+                         file=open(args.operand_kinds_output, 'w'))
+               if args.extension_enum_output is not None:
+                   make_path_to_file(args.extension_enum_output)
+                   print(generate_extension_enum(grammar['operand_kinds']),
+                         file=open(args.extension_enum_output, 'w'))
+               if args.enum_string_mapping_output is not None:
+                   make_path_to_file(args.enum_string_mapping_output)
+                   print(generate_all_string_enum_mappings(operand_kinds),
+                         file=open(args.enum_string_mapping_output, 'w'))
 
     if args.extinst_glsl_grammar is not None:
         with open(args.extinst_glsl_grammar) as json_file:
diff --git a/utils/generate_language_headers.py b/utils/generate_language_headers.py
new file mode 100755 (executable)
index 0000000..1886bf4
--- /dev/null
@@ -0,0 +1,188 @@
+#!/usr/bin/env python
+# Copyright (c) 2017 Google Inc.
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Generates language headers from a JSON grammar file"""
+
+from __future__ import print_function
+
+import errno
+import json
+import os.path
+import re
+
+
+def make_path_to_file(f):
+    """Makes all ancestor directories to the given file, if they
+    don't yet exist.
+
+    Arguments:
+        f: The file whose ancestor directories are to be created.
+    """
+    dir = os.path.dirname(os.path.abspath(f))
+    try:
+        os.makedirs(dir)
+    except OSError as e:
+        if e.errno == errno.EEXIST and os.path.isdir(dir):
+            pass
+        else:
+            raise
+
+class ExtInstGrammar:
+    """The grammar for an extended instruction set"""
+
+    def __init__(self, name, copyright, instructions, operand_kinds, version = None, revision = None):
+       self.name = name
+       self.copyright = copyright
+       self.instructions = instructions
+       self.operand_kinds = operand_kinds
+       self.version = version
+       self.revision = revision
+
+
+class LangGenerator:
+    """A language-specific generator"""
+
+    def __init__(self):
+        self.upper_case_initial = re.compile('^[A-Z]')
+        pass
+
+    def comment_prefix(self):
+        return ""
+
+    def namespace_prefix(self):
+        return ""
+
+    def uses_guards(self):
+        return False
+
+    def cpp_guard_preamble(self):
+        return ""
+
+    def cpp_guard_postamble(self):
+        return ""
+
+    def enum_value(self, prefix, name, value):
+        if self.upper_case_initial.match(name):
+            use_name = name
+        else:
+            use_name = '_' + name
+
+        return "    {}{} = {},".format(prefix, use_name, value)
+
+    def generate(self, grammar):
+        """Returns a string that is the language-specific header for the given grammar"""
+
+        parts = []
+        if grammar.copyright:
+            parts.extend(["{}{}".format(self.comment_prefix(), f) for f in grammar.copyright])
+        parts.append('')
+
+        guard = 'SPIRV_EXTINST_{}_H_'.format(grammar.name)
+        if self.uses_guards:
+            parts.append('#ifndef {}'.format(guard))
+            parts.append('#define {}'.format(guard))
+        parts.append('')
+
+        parts.append(self.cpp_guard_preamble())
+
+        if grammar.version:
+            parts.append(self.const_definition(grammar.name, 'Version', grammar.version))
+
+        if grammar.revision is not None:
+            parts.append(self.const_definition(grammar.name, 'Revision', grammar.revision))
+
+        parts.append('')
+
+        if grammar.instructions:
+            parts.append(self.enum_prefix(grammar.name, 'Instructions'))
+            for inst in grammar.instructions:
+                parts.append(self.enum_value(grammar.name, inst['opname'], inst['opcode']))
+            parts.append(self.enum_end(grammar.name, 'Instructions'))
+            parts.append('')
+
+        if grammar.operand_kinds:
+            for kind in grammar.operand_kinds:
+                parts.append(self.enum_prefix(grammar.name, kind['kind']))
+                for e in kind['enumerants']:
+                    parts.append(self.enum_value(grammar.name, e['enumerant'], e['value']))
+                parts.append(self.enum_end(grammar.name, kind['kind']))
+            parts.append('')
+
+        parts.append(self.cpp_guard_postamble())
+
+        if self.uses_guards:
+            parts.append('#endif // {}'.format(guard))
+
+        return '\n'.join(parts)
+
+
+class CLikeGenerator(LangGenerator):
+    def uses_guards(self):
+        return True
+
+    def comment_prefix(self):
+        return "// "
+
+    def const_definition(self, prefix, var, value):
+        # Use an anonymous enum.  Don't use a static const int variable because
+        # that can bloat binary size.
+        return 'enum {0} {1}{2} = {3}, {1}{2}_BitWidthPadding = 0x7fffffff {4};'.format(
+               '{', prefix, var, value, '}')
+
+    def enum_prefix(self, prefix, name):
+        return 'enum {}{} {}'.format(prefix, name, '{')
+
+    def enum_end(self, prefix, enum):
+        return '    {}{}Max = 0x7ffffff\n{};\n'.format(prefix, enum, '}')
+
+    def cpp_guard_preamble(self):
+        return '#ifdef __cplusplus\nextern "C" {\n#endif\n'
+
+    def cpp_guard_postamble(self):
+        return '#ifdef __cplusplus\n}\n#endif\n'
+
+
+class CGenerator(CLikeGenerator):
+    pass
+
+
+def main():
+    import argparse
+    parser = argparse.ArgumentParser(description='Generate language headers from a JSON grammar')
+
+    parser.add_argument('--extinst-name',
+                        type=str, required=True,
+                        help='The name to use in tokens')
+    parser.add_argument('--extinst-grammar', metavar='<path>',
+                        type=str, required=True,
+                        help='input JSON grammar file for extended instruction set')
+    parser.add_argument('--extinst-output-base', metavar='<path>',
+                        type=str, required=True,
+                        help='Basename of the language-specific output file.')
+    args = parser.parse_args()
+
+    with open(args.extinst_grammar) as json_file:
+        grammar_json = json.loads(json_file.read())
+        grammar = ExtInstGrammar(name = args.extinst_name,
+                                 copyright = grammar_json['copyright'],
+                                 instructions = grammar_json['instructions'],
+                                 operand_kinds = grammar_json['operand_kinds'],
+                                 version = grammar_json['version'],
+                                 revision = grammar_json['revision'])
+        make_path_to_file(args.extinst_output_base)
+        print(CGenerator().generate(grammar), file=open(args.extinst_output_base + '.h', 'w'))
+
+
+if __name__ == '__main__':
+    main()