Add OpenCL extended instructions.
authorDavid Neto <dneto@google.com>
Wed, 14 Oct 2015 21:02:11 +0000 (17:02 -0400)
committerDavid Neto <dneto@google.com>
Mon, 26 Oct 2015 16:55:33 +0000 (12:55 -0400)
Versions 1.2, 2.0, and 2.1 all use the same
extended instruction list.

Updated the source code patch for the SPIR-V doc generator,
so it can both generate the core syntax table, and also the
OpenCL extended instructions table.

Tested the Math and Common functions.
TODO: test the remaining entries.

CMakeLists.txt
include/libspirv/libspirv.h
source/ext_inst.cpp
source/opencl_std_ext_inst.inc [new file with mode: 0644]
source/syntax_tables.patch [moved from source/core_syntax_table.patch with 64% similarity]
test/ExtInst.OpenCL.std.cpp [new file with mode: 0644]

index 9f47a14..0faa161 100644 (file)
@@ -182,6 +182,7 @@ if (NOT ${SPIRV_SKIP_EXECUTABLES})
       ${CMAKE_CURRENT_SOURCE_DIR}/test/Comment.cpp
       ${CMAKE_CURRENT_SOURCE_DIR}/test/DiagnosticPrint.cpp
       ${CMAKE_CURRENT_SOURCE_DIR}/test/DiagnosticStream.cpp
+      ${CMAKE_CURRENT_SOURCE_DIR}/test/ExtInst.OpenCL.std.cpp
       ${CMAKE_CURRENT_SOURCE_DIR}/test/ExtInstGLSLstd450.cpp
       ${CMAKE_CURRENT_SOURCE_DIR}/test/FixWord.cpp
       ${CMAKE_CURRENT_SOURCE_DIR}/test/ImmediateInt.cpp
index e4780b6..6dea226 100644 (file)
@@ -246,9 +246,7 @@ typedef enum spv_operand_type_t {
 typedef enum spv_ext_inst_type_t {
   SPV_EXT_INST_TYPE_NONE,
   SPV_EXT_INST_TYPE_GLSL_STD_450,
-  SPV_EXT_INST_TYPE_OPENCL_STD_12,
-  SPV_EXT_INST_TYPE_OPENCL_STD_20,
-  SPV_EXT_INST_TYPE_OPENCL_STD_21,
+  SPV_EXT_INST_TYPE_OPENCL_STD,
 
   SPV_FORCE_32_BIT_ENUM(spv_ext_inst_type_t)
 } spv_ext_inst_type_t;
index 43b80ea..dbceef1 100644 (file)
@@ -96,19 +96,24 @@ static const spv_ext_inst_desc_t glslStd450Entries[] = {
     {GLSL450Inst2(InterpolateAtOffset)},
 };
 
-static const spv_ext_inst_desc_t openclStd12Entries[] = {
-    {"placeholder", 0, {}},
-    // TODO: Add remaining OpenCL.std.12 instructions
-};
-
-static const spv_ext_inst_desc_t openclStd20Entries[] = {
-    {"placeholder", 0, {}},
-    // TODO: Add remaining OpenCL.std.20 instructions
-};
-
-static const spv_ext_inst_desc_t openclStd21Entries[] = {
-    {"placeholder", 0, {}},
-    // TODO: Add remaining OpenCL.std.21 instructions
+static const spv_ext_inst_desc_t openclEntries[] = {
+#define ExtInst(Name, Opcode, OperandList) {#Name, Opcode, OperandList},
+#define EmptyList \
+  {}
+#define List(...) \
+  { __VA_ARGS__ }
+#define OperandId SPV_OPERAND_TYPE_ID
+#define OperandLiteralNumber SPV_OPERAND_TYPE_LITERAL_NUMBER
+#define OperandFPRoundingMode SPV_OPERAND_TYPE_FP_ROUNDING_MODE
+#define OperandVariableIds SPV_OPERAND_TYPE_VARIABLE_ID
+#include "opencl_std_ext_inst.inc"
+#undef ExtList
+#undef EmptyList
+#undef List
+#undef OperandId
+#undef OperandLiteralNumber
+#undef OperandFPRoundingMode
+#undef OperandVariableIds
 };
 
 spv_result_t spvExtInstTableGet(spv_ext_inst_table *pExtInstTable) {
@@ -118,15 +123,9 @@ spv_result_t spvExtInstTableGet(spv_ext_inst_table *pExtInstTable) {
       {SPV_EXT_INST_TYPE_GLSL_STD_450,
        sizeof(glslStd450Entries) / sizeof(spv_ext_inst_desc_t),
        glslStd450Entries},
-      {SPV_EXT_INST_TYPE_OPENCL_STD_12,
-       sizeof(openclStd12Entries) / sizeof(spv_ext_inst_desc_t),
-       openclStd12Entries},
-      {SPV_EXT_INST_TYPE_OPENCL_STD_20,
-       sizeof(openclStd20Entries) / sizeof(spv_ext_inst_desc_t),
-       openclStd20Entries},
-      {SPV_EXT_INST_TYPE_OPENCL_STD_21,
-       sizeof(openclStd21Entries) / sizeof(spv_ext_inst_desc_t),
-       openclStd21Entries},
+      {SPV_EXT_INST_TYPE_OPENCL_STD,
+       sizeof(openclEntries) / sizeof(spv_ext_inst_desc_t),
+       openclEntries},
   };
 
   static const spv_ext_inst_table_t table = {
@@ -138,17 +137,13 @@ spv_result_t spvExtInstTableGet(spv_ext_inst_table *pExtInstTable) {
 }
 
 spv_ext_inst_type_t spvExtInstImportTypeGet(const char *name) {
+  // The names are specified by the respective extension instruction
+  // specifications.
   if (!strcmp("GLSL.std.450", name)) {
     return SPV_EXT_INST_TYPE_GLSL_STD_450;
   }
-  if (!strcmp("OpenCL.std.12", name)) {
-    return SPV_EXT_INST_TYPE_OPENCL_STD_12;
-  }
-  if (!strcmp("OpenCL.std.20", name)) {
-    return SPV_EXT_INST_TYPE_OPENCL_STD_20;
-  }
-  if (!strcmp("OpenCL.std.21", name)) {
-    return SPV_EXT_INST_TYPE_OPENCL_STD_21;
+  if (!strcmp("OpenCL.std", name)) {
+    return SPV_EXT_INST_TYPE_OPENCL_STD;
   }
   return SPV_EXT_INST_TYPE_NONE;
 }
diff --git a/source/opencl_std_ext_inst.inc b/source/opencl_std_ext_inst.inc
new file mode 100644 (file)
index 0000000..10f55be
--- /dev/null
@@ -0,0 +1,212 @@
+
+// OpenCL extended instructions table, one instruction per line.
+// All instructions have a result type and a result ID.
+// Fields in this file are:
+//  - name
+//  - extended instruction index
+//  - EmptyList, or List of operand classes.
+ExtInst(acos, 0, List(OperandId))
+ExtInst(acosh, 1, List(OperandId))
+ExtInst(acospi, 2, List(OperandId))
+ExtInst(asin, 3, List(OperandId))
+ExtInst(asinh, 4, List(OperandId))
+ExtInst(asinpi, 5, List(OperandId))
+ExtInst(atan, 6, List(OperandId))
+ExtInst(atan2, 7, List(OperandId, OperandId))
+ExtInst(atanh, 8, List(OperandId))
+ExtInst(atanpi, 9, List(OperandId))
+ExtInst(atan2pi, 10, List(OperandId, OperandId))
+ExtInst(cbrt, 11, List(OperandId))
+ExtInst(ceil, 12, List(OperandId))
+ExtInst(copysign, 13, List(OperandId, OperandId))
+ExtInst(cos, 14, List(OperandId))
+ExtInst(cosh, 15, List(OperandId))
+ExtInst(cospi, 16, List(OperandId))
+ExtInst(erfc, 17, List(OperandId))
+ExtInst(erf, 18, List(OperandId))
+ExtInst(exp, 19, List(OperandId))
+ExtInst(exp2, 20, List(OperandId))
+ExtInst(exp10, 21, List(OperandId))
+ExtInst(expm1, 22, List(OperandId))
+ExtInst(fabs, 23, List(OperandId))
+ExtInst(fdim, 24, List(OperandId, OperandId))
+ExtInst(floor, 25, List(OperandId))
+ExtInst(fma, 26, List(OperandId, OperandId, OperandId))
+ExtInst(fmax, 27, List(OperandId, OperandId))
+ExtInst(fmin, 28, List(OperandId, OperandId))
+ExtInst(fmod, 29, List(OperandId, OperandId))
+ExtInst(fract, 30, List(OperandId, OperandId))
+ExtInst(frexp, 31, List(OperandId, OperandId))
+ExtInst(hypot, 32, List(OperandId, OperandId))
+ExtInst(ilogb, 33, List(OperandId))
+ExtInst(ldexp, 34, List(OperandId, OperandId))
+ExtInst(lgamma, 35, List(OperandId))
+ExtInst(lgamma_r, 36, List(OperandId, OperandId))
+ExtInst(log, 37, List(OperandId))
+ExtInst(log2, 38, List(OperandId))
+ExtInst(log10, 39, List(OperandId))
+ExtInst(log1p, 40, List(OperandId))
+ExtInst(logb, 41, List(OperandId))
+ExtInst(mad, 42, List(OperandId, OperandId, OperandId))
+ExtInst(maxmag, 43, List(OperandId, OperandId))
+ExtInst(minmag, 44, List(OperandId, OperandId))
+ExtInst(modf, 45, List(OperandId, OperandId))
+ExtInst(nan, 46, List(OperandId))
+ExtInst(nextafter, 47, List(OperandId, OperandId))
+ExtInst(pow, 48, List(OperandId, OperandId, OperandId))
+ExtInst(pown, 49, List(OperandId))
+ExtInst(powr, 50, List(OperandId, OperandId))
+ExtInst(remainder, 51, List(OperandId, OperandId))
+ExtInst(remquo, 52, List(OperandId, OperandId, OperandId))
+ExtInst(rint, 53, List(OperandId))
+ExtInst(rootn, 54, List(OperandId, OperandId))
+ExtInst(round, 55, List(OperandId))
+ExtInst(rsqrt, 56, List(OperandId))
+ExtInst(sin, 57, List(OperandId))
+ExtInst(sincos, 58, List(OperandId, OperandId))
+ExtInst(sinh, 59, List(OperandId))
+ExtInst(sinpi, 60, List(OperandId))
+ExtInst(sqrt, 61, List(OperandId))
+ExtInst(tan, 62, List(OperandId))
+ExtInst(tanh, 63, List(OperandId))
+ExtInst(tanpi, 64, List(OperandId))
+ExtInst(tgamma, 65, List(OperandId))
+ExtInst(trunc, 66, List(OperandId))
+ExtInst(half_cos, 67, List(OperandId))
+ExtInst(half_divide, 68, List(OperandId, OperandId))
+ExtInst(half_exp, 69, List(OperandId))
+ExtInst(half_exp2, 70, List(OperandId))
+ExtInst(half_exp10, 71, List(OperandId))
+ExtInst(half_log, 72, List(OperandId))
+ExtInst(half_log2, 73, List(OperandId))
+ExtInst(half_log10, 74, List(OperandId))
+ExtInst(half_powr, 75, List(OperandId, OperandId))
+ExtInst(half_recip, 76, List(OperandId))
+ExtInst(half_rsqrt, 77, List(OperandId))
+ExtInst(half_sin, 78, List(OperandId))
+ExtInst(half_sqrt, 79, List(OperandId))
+ExtInst(half_tan, 80, List(OperandId))
+ExtInst(native_cos, 81, List(OperandId))
+ExtInst(native_divide, 82, List(OperandId, OperandId))
+ExtInst(native_exp, 83, List(OperandId))
+ExtInst(native_exp2, 84, List(OperandId))
+ExtInst(native_exp10, 85, List(OperandId))
+ExtInst(native_log, 86, List(OperandId))
+ExtInst(native_log2, 87, List(OperandId))
+ExtInst(native_log10, 88, List(OperandId))
+ExtInst(native_powr, 89, List(OperandId, OperandId))
+ExtInst(native_recip, 90, List(OperandId))
+ExtInst(native_rsqrt, 91, List(OperandId))
+ExtInst(native_sin, 92, List(OperandId))
+ExtInst(native_sqrt, 93, List(OperandId))
+ExtInst(native_tan, 94, List(OperandId))
+ExtInst(fclamp, 95, List(OperandId, OperandId, OperandId))
+ExtInst(degrees, 96, List(OperandId))
+ExtInst(fmax_common, 97, List(OperandId, OperandId))
+ExtInst(fmin_common, 98, List(OperandId, OperandId))
+ExtInst(mix, 99, List(OperandId, OperandId, OperandId))
+ExtInst(radians, 100, List(OperandId))
+ExtInst(step, 101, List(OperandId, OperandId))
+ExtInst(smoothstep, 102, List(OperandId, OperandId, OperandId))
+ExtInst(sign, 103, List(OperandId))
+ExtInst(cross, 104, List(OperandId, OperandId))
+ExtInst(distance, 105, List(OperandId, OperandId))
+ExtInst(length, 106, List(OperandId))
+ExtInst(normalize, 107, List(OperandId))
+ExtInst(fast_distance, 108, List(OperandId, OperandId))
+ExtInst(fast_length, 109, List(OperandId))
+ExtInst(fast_normalize, 110, List(OperandId))
+ExtInst(read_imagef, 111, EmptyList)
+ExtInst(read_imagei, 112, EmptyList)
+ExtInst(read_imageui, 113, EmptyList)
+ExtInst(read_imageh, 114, EmptyList)
+ExtInst(read_imagef_samplerless, 115, EmptyList)
+ExtInst(read_imagei_samplerless, 116, EmptyList)
+ExtInst(read_imageui_samplerless, 117, EmptyList)
+ExtInst(read_imageh_samplerless, 118, EmptyList)
+ExtInst(write_imagef, 119, EmptyList)
+ExtInst(write_imagei, 120, EmptyList)
+ExtInst(write_imageui, 121, EmptyList)
+ExtInst(write_imageh, 122, EmptyList)
+ExtInst(read_imagef_mipmap_lod, 123, EmptyList)
+ExtInst(read_imagei_mipmap_lod, 124, EmptyList)
+ExtInst(read_imageui_mipmap_lod, 125, EmptyList)
+ExtInst(read_imagef_mipmap_gradient, 126, EmptyList)
+ExtInst(read_imagei_mipmap_gradient, 127, EmptyList)
+ExtInst(read_imageui_mipmap_gradient, 128, EmptyList)
+ExtInst(write_imagef_mipmap_lod, 129, List(OperandId, OperandId, OperandId, OperandId))
+ExtInst(write_imagei_mipmap_lod, 130, List(OperandId, OperandId, OperandId, OperandId))
+ExtInst(write_imageui_mipmap_lod, 131, List(OperandId, OperandId, OperandId, OperandId))
+ExtInst(get_image_width, 132, EmptyList)
+ExtInst(get_image_height, 133, EmptyList)
+ExtInst(get_image_depth, 134, EmptyList)
+ExtInst(get_image_channel_data_type, 135, EmptyList)
+ExtInst(get_image_channel_order, 136, EmptyList)
+ExtInst(get_image_dim, 137, EmptyList)
+ExtInst(get_image_array_size, 138, EmptyList)
+ExtInst(get_image_num_samples, 139, EmptyList)
+ExtInst(get_image_num_mip_levels, 140, EmptyList)
+ExtInst(s_abs, 141, List(OperandId))
+ExtInst(s_abs_diff, 142, List(OperandId, OperandId))
+ExtInst(s_add_sat, 143, List(OperandId, OperandId))
+ExtInst(u_add_sat, 144, List(OperandId, OperandId))
+ExtInst(s_hadd, 145, List(OperandId, OperandId))
+ExtInst(u_hadd, 146, List(OperandId, OperandId))
+ExtInst(s_rhadd, 147, List(OperandId, OperandId))
+ExtInst(u_rhadd, 148, List(OperandId, OperandId))
+ExtInst(s_clamp, 149, List(OperandId, OperandId, OperandId))
+ExtInst(u_clamp, 150, List(OperandId, OperandId, OperandId))
+ExtInst(clz, 151, List(OperandId))
+ExtInst(ctz, 152, List(OperandId))
+ExtInst(s_mad_hi, 153, List(OperandId, OperandId, OperandId))
+ExtInst(u_mad_sat, 154, List(OperandId, OperandId, OperandId))
+ExtInst(s_mad_sat, 155, List(OperandId, OperandId, OperandId))
+ExtInst(s_max, 156, List(OperandId, OperandId))
+ExtInst(u_max, 157, List(OperandId, OperandId))
+ExtInst(s_min, 158, List(OperandId, OperandId))
+ExtInst(u_min, 159, List(OperandId, OperandId))
+ExtInst(s_mul_hi, 160, List(OperandId, OperandId))
+ExtInst(rotate, 161, List(OperandId, OperandId))
+ExtInst(s_sub_sat, 162, List(OperandId, OperandId))
+ExtInst(u_sub_sat, 163, List(OperandId, OperandId))
+ExtInst(u_upsample, 164, List(OperandId, OperandId))
+ExtInst(s_upsample, 165, List(OperandId, OperandId))
+ExtInst(popcount, 166, List(OperandId))
+ExtInst(s_mad24, 167, List(OperandId, OperandId, OperandId))
+ExtInst(u_mad24, 168, List(OperandId, OperandId, OperandId))
+ExtInst(s_mul24, 169, List(OperandId, OperandId))
+ExtInst(u_mul24, 170, List(OperandId, OperandId))
+ExtInst(vloadn, 171, List(OperandId, OperandId, OperandLiteralNumber))
+ExtInst(vstoren, 172, List(OperandId, OperandId, OperandId))
+ExtInst(vload_half, 173, List(OperandId, OperandId))
+ExtInst(vload_halfn, 174, List(OperandId, OperandId, OperandLiteralNumber))
+ExtInst(vstore_half, 175, List(OperandId, OperandId, OperandId))
+ExtInst(vstore_half_r, 176, List(OperandId, OperandId, OperandId, OperandFPRoundingMode))
+ExtInst(vstore_halfn, 177, List(OperandId, OperandId, OperandId))
+ExtInst(vstore_halfn_r, 178, List(OperandId, OperandId, OperandId, OperandFPRoundingMode))
+ExtInst(vloada_halfn, 179, List(OperandId, OperandId, OperandLiteralNumber))
+ExtInst(vstorea_halfn, 180, List(OperandId, OperandId, OperandId))
+ExtInst(vstorea_halfn_r, 181, List(OperandId, OperandId, OperandId, OperandFPRoundingMode))
+ExtInst(shuffle, 182, List(OperandId, OperandId))
+ExtInst(shuffle2, 183, List(OperandId, OperandId, OperandId))
+ExtInst(printf, 184, List(OperandId, OperandVariableIds))
+ExtInst(prefetch, 185, List(OperandId, OperandId))
+ExtInst(bitselect, 186, List(OperandId, OperandId, OperandId))
+ExtInst(select, 187, List(OperandId, OperandId, OperandId))
+ExtInst(read_pipe, 188, EmptyList)
+ExtInst(write_pipe, 189, EmptyList)
+ExtInst(reserve_read_pipe, 190, EmptyList)
+ExtInst(reserve_write_pipe, 191, EmptyList)
+ExtInst(commit_read_pipe, 192, EmptyList)
+ExtInst(commit_write_pipe, 193, EmptyList)
+ExtInst(is_valid_reserve_id, 194, EmptyList)
+ExtInst(work_group_reserve_read_pipe, 195, EmptyList)
+ExtInst(work_group_reserve_write_pipe, 196, EmptyList)
+ExtInst(work_group_commit_read_pipe, 197, EmptyList)
+ExtInst(work_group_commit_write_pipe, 198, EmptyList)
+ExtInst(get_pipe_num_packets, 199, EmptyList)
+ExtInst(get_pipe_max_packets, 200, EmptyList)
+ExtInst(u_abs, 201, List(OperandId))
+ExtInst(u_abs_diff, 202, List(OperandId, OperandId))
+ExtInst(u_mul_hi, 203, List(OperandId, OperandId))
+ExtInst(u_mad_hi, 204, List(OperandId, OperandId, OperandId))
similarity index 64%
rename from source/core_syntax_table.patch
rename to source/syntax_tables.patch
index 8d90969..c8e47f6 100644 (file)
@@ -10,12 +10,90 @@ index af51f86..5775510 100644
      disassemble.cpp
      header.cpp
      doc.cpp
+diff --git a/tools/spirv/OclDoc.cpp b/tools/spirv/OclDoc.cpp
+index c94eae2..ea47d42 100644
+--- a/tools/spirv/OclDoc.cpp
++++ b/tools/spirv/OclDoc.cpp
+@@ -28,11 +28,14 @@
+ #include "headers/spirv.hpp"
+ #include "headers/OpenCL.std.h"
+-#include "doc.h"
+ #include "OclDoc.h"
++#include "assembler_table.h"
++#include "doc.h"
++
+ #include <stdio.h>
+ #include <string.h>
++#include <cassert>
+ #include <algorithm>
+ #include <map>
+@@ -2358,4 +2361,27 @@ void PrintOclCommonDoc()
+     PrintOclDoc(SPIROpenCLCommonVersion);
+ }
++void PrintOclInstructionsTable(std::ostream& out) {
++  ParameterizeBuiltins(SPIROpenCLCommonVersion);
++
++  out << R"(
++// OpenCL extended instructions table, one instruction per line.
++// All instructions have a result type and a result ID.
++// Fields in this file are:
++//  - name
++//  - extended instruction index
++//  - EmptyList, or List of operand classes.
++)";
++  for (int i = 0; i < OclExtInstCeiling; ++i) {
++    const BuiltInFunctionParameters& inst = BuiltInDesc[i];
++    // Skip gaps in the enum space, if any.
++    if (0 == strcmp("unknown", inst.opName)) continue;
++    assert(inst.hasType());
++    assert(inst.hasResult());
++    out << "ExtInst(" << inst.opName << ", " << i << ", ";
++    PrintOperandClasses(inst.operands.classes(), out);
++    out << ")" << std::endl;
++  }
++}
++
+ };  // end namespace spv
+diff --git a/tools/spirv/OclDoc.h b/tools/spirv/OclDoc.h
+index 8568880..7cf8a31 100644
+--- a/tools/spirv/OclDoc.h
++++ b/tools/spirv/OclDoc.h
+@@ -35,6 +35,8 @@
+ #include <map>
+ #include "headers/spirv.hpp"
++#include "doc.h"
++
+ namespace spv {
+ void OclGetDebugNames(const char** names);
+@@ -189,6 +191,8 @@ public:
+     int getImageType(int op) { return Type[op]; }
+     AccessQualifier getAccessQualifier(int op) { return accessQualifier[op]; }
++    const std::vector<OperandClass>& classes() const  { return opClass; }
++
+ protected:
+     std::vector<OperandClass> opClass;
+     std::vector<const char*> desc;
+@@ -307,4 +311,7 @@ protected:
+ // Print out the OpenCL common (all spec revisions) documentation.
+ void PrintOclCommonDoc();
++// Prints the OpenCL instructions table, for consumption by SPIR-V Tools.
++void PrintOclInstructionsTable(std::ostream& out);
++
+ };  // end namespace spv
 diff --git a/tools/spirv/assembler_table.cpp b/tools/spirv/assembler_table.cpp
 new file mode 100644
-index 0000000..5a68ade
+index 0000000..85bca89
 --- /dev/null
 +++ b/tools/spirv/assembler_table.cpp
-@@ -0,0 +1,200 @@
+@@ -0,0 +1,214 @@
 +// Copyright (c) 2015 The Khronos Group Inc.
 +//
 +// Permission is hereby granted, free of charge, to any person obtaining a
@@ -55,7 +133,7 @@ index 0000000..5a68ade
 +namespace {
 +
 +// Returns true if the given instruction can vary in width.
-+bool isVariableLength(const InstructionParameters& inst) {
++bool IsVariableLength(const InstructionParameters& inst) {
 +  const OperandParameters& operands = inst.operands;
 +  for (int i = 0; i < operands.getNum() ; i++) {
 +    switch (operands.getClass(i)) {
@@ -76,9 +154,8 @@ index 0000000..5a68ade
 +  return false;
 +}
 +
-+// Returns a string for the given operand class, or nullptr if
-+// it's invalid.
-+const char* getOperandClassString(OperandClass operandClass) {
++// Returns a string for the given operand class, or nullptr if it's invalid.
++const char* GetOperandClassString(OperandClass operandClass) {
 +  switch (operandClass) {
 +#define CASE(X) case X: return #X;
 +    CASE(OperandNone)
@@ -126,26 +203,23 @@ index 0000000..5a68ade
 +    CASE(OperandOpcode)
 +#undef CASE
 +
-+//    case OperandNone:
 +    case OperandCount:
 +    default:
 +      break;
 +  }
 +  return nullptr;
 +}
++} // anonymous namespace
 +
 +// Prints a listing of the operand kinds for the given instruction.
 +// If the list is empty, then emit just "EmptyList",
 +// otherwise the output looks like a call to the "List" macro.
-+void printOperandClasses(const InstructionParameters& inst, std::ostream& out) {
++void PrintOperandClasses(const std::vector<OperandClass>& classes, std::ostream& out) {
 +  std::stringstream contents;
-+
-+  const OperandParameters& operands = inst.operands;
 +  int numPrinted = 0;
-+  for (int i = 0; i < operands.getNum() ; i++) {
-+    if (const char* name = getOperandClassString(operands.getClass(i))) {
-+      if (numPrinted)
-+        contents << ", ";
++  for (auto operandClass : classes) {
++    if (const char* name = GetOperandClassString(operandClass)) {
++      if (numPrinted) contents << ", ";
 +      contents << name;
 +      numPrinted++;
 +    }
@@ -156,9 +230,27 @@ index 0000000..5a68ade
 +  else
 +    out << "EmptyList";
 +}
++}  // namespace spv
++
++namespace spv {
++namespace {
++
++// Prints a listing of the operand kinds for the given instruction.
++// If the list is empty, then emit just "EmptyList",
++// otherwise the output looks like a call to the "List" macro.
++void PrintOperandClassesForInstruction(const InstructionParameters& inst,
++                                       std::ostream& out) {
++  std::vector<OperandClass> result;
++
++  const OperandParameters& operands = inst.operands;
++  for (int i = 0; i < operands.getNum(); i++) {
++    result.push_back(operands.getClass(i));
++  }
++  PrintOperandClasses(result, out);
++}
 +
 +// Prints the table entry for the given instruction with the given opcode.
-+void printInstructionDesc(int opcode, const InstructionParameters& inst, std::ostream& out) {
++void PrintInstructionDesc(int opcode, const InstructionParameters& inst, std::ostream& out) {
 +  const char* name = OpcodeString(opcode);
 +  // There can be gaps in the listing.
 +  // All valid operations have a name beginning with "Op".
@@ -184,13 +276,13 @@ index 0000000..5a68ade
 +      out << "Capability(None), ";
 +    }
 +
-+    out << (isVariableLength(inst) ? 1 : 0) << ", ";
-+    printOperandClasses(inst, out);
++    out << (IsVariableLength(inst) ? 1 : 0) << ", ";
++    PrintOperandClassesForInstruction(inst, out);
 +    out << ")" << std::endl;
 +  }
 +}
 +
-+}
++} // anonymous namespace
 +
 +void PrintAssemblerTable(std::ostream& out) {
 +  out << "// Instruction fields are:\n"
@@ -211,17 +303,17 @@ index 0000000..5a68ade
 +      << "// #define Instruction(Name,HasResult,HasType,NumLogicalOperands,CapabiltyRequired,IsVariable,LogicalArgsList)\n";
 +
 +  for (int i = 0; i < spv::OpcodeCeiling ; i++ ) {
-+    printInstructionDesc(i, InstructionDesc[i], out);
++    PrintInstructionDesc(i, InstructionDesc[i], out);
 +  }
 +}
 +
 +}
 diff --git a/tools/spirv/assembler_table.h b/tools/spirv/assembler_table.h
 new file mode 100644
-index 0000000..03ab769
+index 0000000..b98303a
 --- /dev/null
 +++ b/tools/spirv/assembler_table.h
-@@ -0,0 +1,41 @@
+@@ -0,0 +1,49 @@
 +// Copyright (c) 2015 The Khronos Group Inc.
 +//
 +// Permission is hereby granted, free of charge, to any person obtaining a
@@ -253,6 +345,9 @@ index 0000000..03ab769
 +#define ASSEMBLER_TABLE_H
 +
 +#include <iostream>
++#include <vector>
++
++#include "doc.h"
 +
 +namespace spv {
 +
@@ -260,14 +355,29 @@ index 0000000..03ab769
 +    // Assumes that parameterization has already occurred
 +    void PrintAssemblerTable(std::ostream& out);
 +
++    // Prints a listing of the operand kinds.
++    // If the list is empty, then emit just "EmptyList",
++    // otherwise the output looks like a call to the "List" macro.
++    void PrintOperandClasses(const std::vector<OperandClass>& classes, std::ostream& out);
++
 +};  // end namespace spv
 +
 +#endif // ASSEMBLER_TABLE_H
 diff --git a/tools/spirv/doc.h b/tools/spirv/doc.h
-index 78b1031..5984835 100644
+index 78b1031..a182509 100644
 --- a/tools/spirv/doc.h
 +++ b/tools/spirv/doc.h
-@@ -81,6 +81,7 @@ const char* KernelEnqueueFlagsString(int);
+@@ -32,6 +32,9 @@
+ //ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ //POSSIBILITY OF SUCH DAMAGE.
++#ifndef DOC_H_
++#define DOC_H_
++
+ //
+ // Author: John Kessenich, LunarG
+ //
+@@ -81,6 +84,7 @@ const char* KernelEnqueueFlagsString(int);
  const char* KernelProfilingInfoString(int);
  const char* CapabilityString(int);
  const char* OpcodeString(int);
@@ -275,7 +385,7 @@ index 78b1031..5984835 100644
  
  // For grouping opcodes into subsections
  enum OpcodeClass {
-@@ -159,6 +160,10 @@ enum OperandClass {
+@@ -159,6 +163,10 @@ enum OperandClass {
  
      OperandOpcode,
  
@@ -286,8 +396,14 @@ index 78b1031..5984835 100644
      OperandCount
  };
  
+@@ -257,3 +265,5 @@ const char* AccessQualifierString(int attr);
+ void PrintOperands(const OperandParameters& operands, int reservedOperands);
+ };  // end namespace spv
++
++#endif // DOC_H_
 diff --git a/tools/spirv/main.cpp b/tools/spirv/main.cpp
-index d7312f9..c856271 100644
+index d7312f9..e1413bb 100644
 --- a/tools/spirv/main.cpp
 +++ b/tools/spirv/main.cpp
 @@ -46,6 +46,7 @@ namespace spv {
@@ -298,38 +414,62 @@ index d7312f9..c856271 100644
  #include "disassemble.h"
  #include "header.h"
  #include "doc.h"
-@@ -65,6 +66,7 @@ enum TOptions {
+@@ -65,6 +66,8 @@ enum TOptions {
      EOptionDisassemble                = 0x004,
      EOptionPrintHeader                = 0x008,
      EOptionPrintOclBuiltinsAsciidoc   = 0x010,
 +    EOptionPrintAssemblerTable        = 0x020,
++    EOptionPrintOclInstructionsTable  = 0x040,
  };
  
  std::string Filename;
-@@ -89,6 +91,7 @@ void Usage()
-            "      'CL12': OpenCL 1.2 extended instructions documentation\n"
-            "      'CL20': OpenCL 2.0 extended instructions documentation\n"
-            "      'CL21': OpenCL 2.1 extended instructions documentation\n"
+@@ -86,9 +89,11 @@ void Usage()
+            "  -H print header in all supported languages to files in current directory\n"
+            "  -p print documentation\n"
+            "  -s [version] prints the SPIR-V extended instructions documentation\n"
+-           "      'CL12': OpenCL 1.2 extended instructions documentation\n"
+-           "      'CL20': OpenCL 2.0 extended instructions documentation\n"
+-           "      'CL21': OpenCL 2.1 extended instructions documentation\n"
++           "      'OpenCL': OpenCL 1.2, 2.0, 2.1 extended instructions documentation\n"
++           "      'GLSL': GLSL extended instructions documentation\n"
 +           "  -a print table for the assembler\n"
++           "  -C print OpenCL instructions for the assembler\n"
++           "     This is incompatibile with -s OpenCL\n"
             );
  }
  
-@@ -155,6 +158,9 @@ bool ProcessArguments(int argc, char* argv[])
+@@ -155,6 +160,12 @@ bool ProcessArguments(int argc, char* argv[])
                  }
                  return true;
              }
 +            case 'a':
 +                Options |= EOptionPrintAssemblerTable;
 +                break;
++            case 'C':
++                Options |= EOptionPrintOclInstructionsTable;
++                break;
              default:
                  return false;
              }
-@@ -220,5 +226,8 @@ int main(int argc, char* argv[])
+@@ -162,6 +173,9 @@ bool ProcessArguments(int argc, char* argv[])
+             Filename = std::string(argv[0]);
+         }
+     }
++    if ((Options & EOptionPrintOclBuiltinsAsciidoc) &&
++        (Options & EOptionPrintOclInstructionsTable))
++      return false;
+     return true;
+ }
+@@ -220,5 +234,11 @@ int main(int argc, char* argv[])
      if (Options & EOptionPrintHeader)
          spv::PrintHeader(Language, std::cout);
  
 +    if (Options & EOptionPrintAssemblerTable)
 +        spv::PrintAssemblerTable(std::cout);
 +
++    if (Options & EOptionPrintOclInstructionsTable)
++        spv::PrintOclInstructionsTable(std::cout);
++
      return 0;
  }
diff --git a/test/ExtInst.OpenCL.std.cpp b/test/ExtInst.OpenCL.std.cpp
new file mode 100644 (file)
index 0000000..edfd0a4
--- /dev/null
@@ -0,0 +1,217 @@
+// Copyright (c) 2015 The Khronos Group Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and/or associated documentation files (the
+// "Materials"), to deal in the Materials without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Materials, and to
+// permit persons to whom the Materials are furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Materials.
+//
+// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS
+// KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS
+// SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT
+//    https://www.khronos.org/registry/
+//
+// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+
+#include "UnitSPIRV.h"
+
+#include <gmock/gmock.h>
+#include "TestFixture.h"
+
+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 ExtInstOpenCLStdRoundTripTest =
+    spvtest::TextToBinaryTestBase<::testing::TestWithParam<InstructionCase>>;
+
+TEST_P(ExtInstOpenCLStdRoundTripTest, ParameterizedExtInst) {
+  // This example should not validate.
+  const std::string input =
+      "%1 = OpExtInstImport \"OpenCL.std\"\n"
+      "%3 = OpExtInst %2 %1 " +
+      GetParam().name + " " + GetParam().operands + "\n";
+  // First make sure it assembles correctly.
+  EXPECT_THAT(
+      CompiledInstructions(input),
+      Eq(Concatenate(
+          {MakeInstruction(spv::OpExtInstImport, {1}, MakeVector("OpenCL.std")),
+           MakeInstruction(spv::OpExtInst, {2, 3, 1, GetParam().opcode},
+                           GetParam().expected_operands)})))
+      << input;
+  // Now check the round trip through the disassembler.
+  EXPECT_THAT(EncodeAndDecodeSuccessfully(input), input) << input;
+}
+
+#define CASE1(Enum, Name)                                      \
+  {                                                            \
+    uint32_t(OpenCLLIB::Entrypoints::Enum), #Name, "%4", { 4 } \
+  }
+#define CASE2(Enum, Name)                                            \
+  {                                                                  \
+    uint32_t(OpenCLLIB::Entrypoints::Enum), #Name, "%4 %5", { 4, 5 } \
+  }
+#define CASE3(Enum, Name)                                                  \
+  {                                                                        \
+    uint32_t(OpenCLLIB::Entrypoints::Enum), #Name, "%4 %5 %6", { 4, 5, 6 } \
+  }
+
+// clang-format off
+// OpenCL.std: 2.1 Math extended instructions
+INSTANTIATE_TEST_CASE_P(
+    OpenCLMath, ExtInstOpenCLStdRoundTripTest,
+    ::testing::ValuesIn(std::vector<InstructionCase>({
+        // We are only testing the correctness of encoding and decoding here.
+        // Semantic correctness should be the responsibility of validator.
+        CASE1(Acos, acos), // enum value 0
+        CASE1(Acosh, acosh),
+        CASE1(Acospi, acospi),
+        CASE1(Asin, asin),
+        CASE1(Asinh, asinh),
+        CASE1(Asinh, asinh),
+        CASE1(Asinpi, asinpi),
+        CASE1(Atan, atan),
+        CASE2(Atan2, atan2),
+        CASE1(Atanh, atanh),
+        CASE1(Atanpi, atanpi),
+        CASE2(Atan2pi, atan2pi),
+        CASE1(Cbrt, cbrt),
+        CASE1(Ceil, ceil),
+        CASE1(Ceil, ceil),
+        CASE2(Copysign, copysign),
+        CASE1(Cos, cos),
+        CASE1(Cosh, cosh),
+        CASE1(Cospi, cospi),
+        CASE1(Erfc, erfc),
+        CASE1(Erf, erf),
+        CASE1(Exp, exp),
+        CASE1(Exp2, exp2),
+        CASE1(Exp10, exp10),
+        CASE1(Expm1, expm1),
+        CASE1(Fabs, fabs),
+        CASE2(Fdim, fdim),
+        CASE1(Floor, floor),
+        CASE3(Fma, fma),
+        CASE2(Fmax, fmax),
+        CASE2(Fmin, fmin),
+        CASE2(Fmod, fmod),
+        CASE2(Fract, fract),
+        CASE2(Frexp, frexp),
+        CASE2(Hypot, hypot),
+        CASE1(Ilogb, ilogb),
+        CASE2(Ldexp, ldexp),
+        CASE1(Lgamma, lgamma),
+        CASE2(Lgamma_r, lgamma_r),
+        CASE1(Log, log),
+        CASE1(Log2, log2),
+        CASE1(Log10, log10),
+        CASE1(Log1p, log1p),
+        CASE3(Mad, mad),
+        CASE2(Maxmag, maxmag),
+        CASE2(Minmag, minmag),
+        CASE2(Modf, modf),
+        CASE1(Nan, nan),
+        CASE2(Nextafter, nextafter),
+        CASE3(Pow, pow),
+        CASE1(Pown, pown),
+        CASE2(Powr, powr),
+        CASE2(Remainder, remainder),
+        CASE3(Remquo, remquo),
+        CASE1(Rint, rint),
+        CASE2(Rootn, rootn),
+        CASE1(Round, round),
+        CASE1(Rsqrt, rsqrt),
+        CASE1(Sin, sin),
+        CASE2(Sincos, sincos),
+        CASE1(Sinh, sinh),
+        CASE1(Sinpi, sinpi),
+        CASE1(Sqrt, sqrt),
+        CASE1(Tan, tan),
+        CASE1(Tanh, tanh),
+        CASE1(Tanpi, tanpi),
+        CASE1(Tgamma, tgamma),
+        CASE1(Trunc, trunc),
+        CASE1(Half_cos, half_cos),
+        CASE2(Half_divide, half_divide),
+        CASE1(Half_exp, half_exp),
+        CASE1(Half_exp2, half_exp2),
+        CASE1(Half_exp10, half_exp10),
+        CASE1(Half_log, half_log),
+        CASE1(Half_log2, half_log2),
+        CASE1(Half_log10, half_log10),
+        CASE2(Half_powr, half_powr),
+        CASE1(Half_recip, half_recip),
+        CASE1(Half_rsqrt, half_rsqrt),
+        CASE1(Half_sin, half_sin),
+        CASE1(Half_sqrt, half_sqrt),
+        CASE1(Half_tan, half_tan),
+        CASE1(Native_cos, native_cos),
+        CASE2(Native_divide, native_divide),
+        CASE1(Native_exp, native_exp),
+        CASE1(Native_exp2, native_exp2),
+        CASE1(Native_exp10, native_exp10),
+        CASE1(Native_log, native_log),
+        CASE1(Native_log10, native_log10),
+        CASE2(Native_powr, native_powr),
+        CASE1(Native_recip, native_recip),
+        CASE1(Native_rsqrt, native_rsqrt),
+        CASE1(Native_sin, native_sin),
+        CASE1(Native_sqrt, native_sqrt),
+        CASE1(Native_tan, native_tan), // enum value 94
+    })));
+
+// TODO(dneto): OpenCL.std: 2.1 Integer instructions
+
+// OpenCL.std: 2.3 Common instrucitons
+INSTANTIATE_TEST_CASE_P(
+    OpenCLCommon, ExtInstOpenCLStdRoundTripTest,
+    ::testing::ValuesIn(std::vector<InstructionCase>({
+        CASE3(FClamp, fclamp), // enum value 95
+        CASE1(Degrees, degrees),
+        CASE2(FMax_common, fmax_common),
+        CASE2(FMin_common, fmin_common),
+        CASE3(Mix, mix),
+        CASE1(Radians, radians),
+        CASE2(Step, step),
+        CASE3(Smoothstep, smoothstep),
+        CASE1(Sign, sign), // enum value 103
+    })));
+
+// TODO(dneto): OpenCL.std: 2.4 Geometric instructions
+// TODO(dneto): OpenCL.std: 2.5 Relational instructions
+// TODO(dneto): OpenCL.std: 2.6 Vector data load and store instructions
+// TODO(dneto): OpenCL.std: 2.7 Miscellaneous vector instructions
+// TODO(dneto): OpenCL.std: 2.8 Miscellaneous instructions
+// TODO(dneto): OpenCL.std: 2.9.1 Image encoding
+// TODO(dneto): OpenCL.std: 2.9.2 Sampler encoding
+// TODO(dneto): OpenCL.std: 2.9.3 Image read
+// TODO(dneto): OpenCL.std: 2.9.4 Image write
+
+// clang-format on
+
+#undef CASE1
+#undef CASE2
+#undef CASE3
+
+}  // anonymous namespace