|  | diff --git a/tools/spirv/CMakeLists.txt b/tools/spirv/CMakeLists.txt | 
|  | index af51f86..5775510 100644 | 
|  | --- a/tools/spirv/CMakeLists.txt | 
|  | +++ b/tools/spirv/CMakeLists.txt | 
|  | @@ -12,6 +12,7 @@ include_directories(../..) | 
|  |  | 
|  | set(SOURCES | 
|  | main.cpp | 
|  | +    assembler_table.cpp | 
|  | 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..85bca89 | 
|  | --- /dev/null | 
|  | +++ b/tools/spirv/assembler_table.cpp | 
|  | @@ -0,0 +1,214 @@ | 
|  | +// 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 "assembler_table.h" | 
|  | + | 
|  | +#include <cassert> | 
|  | +#include <cstring> | 
|  | +#include <sstream> | 
|  | + | 
|  | +#include "doc.h" | 
|  | + | 
|  | +namespace spv { | 
|  | + | 
|  | +namespace { | 
|  | + | 
|  | +// Returns true if the given instruction can vary in width. | 
|  | +bool IsVariableLength(const InstructionParameters& inst) { | 
|  | +  const OperandParameters& operands = inst.operands; | 
|  | +  for (int i = 0; i < operands.getNum() ; i++) { | 
|  | +    switch (operands.getClass(i)) { | 
|  | +      case spv::OperandOptionalId: | 
|  | +      case spv::OperandOptionalImage: | 
|  | +      case spv::OperandVariableIds: | 
|  | +      case spv::OperandOptionalLiteral: | 
|  | +      case spv::OperandOptionalLiteralString: | 
|  | +      case spv::OperandVariableLiterals: | 
|  | +      case spv::OperandVariableIdLiteral: | 
|  | +      case spv::OperandVariableLiteralId: | 
|  | +      case spv::OperandLiteralString: | 
|  | +        return true; | 
|  | +      default: | 
|  | +        break; | 
|  | +    } | 
|  | +  } | 
|  | +  return false; | 
|  | +} | 
|  | + | 
|  | +// 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) | 
|  | +    CASE(OperandId) | 
|  | +    CASE(OperandOptionalId) | 
|  | +    CASE(OperandOptionalImage) | 
|  | +    CASE(OperandVariableIds) | 
|  | +    CASE(OperandOptionalLiteral) | 
|  | +    CASE(OperandOptionalLiteralString) | 
|  | +    CASE(OperandVariableLiterals) | 
|  | +    CASE(OperandVariableIdLiteral) | 
|  | +    CASE(OperandVariableLiteralId) | 
|  | +    CASE(OperandLiteralNumber) | 
|  | +    CASE(OperandLiteralString) | 
|  | +    CASE(OperandSource) | 
|  | +    CASE(OperandExecutionModel) | 
|  | +    CASE(OperandAddressing) | 
|  | +    CASE(OperandMemory) | 
|  | +    CASE(OperandExecutionMode) | 
|  | +    CASE(OperandStorage) | 
|  | +    CASE(OperandDimensionality) | 
|  | +    CASE(OperandSamplerAddressingMode) | 
|  | +    CASE(OperandSamplerFilterMode) | 
|  | +    CASE(OperandSamplerImageFormat) | 
|  | +    CASE(OperandImageChannelOrder) | 
|  | +    CASE(OperandImageChannelDataType) | 
|  | +    CASE(OperandImageOperands) | 
|  | +    CASE(OperandFPFastMath) | 
|  | +    CASE(OperandFPRoundingMode) | 
|  | +    CASE(OperandLinkageType) | 
|  | +    CASE(OperandAccessQualifier) | 
|  | +    CASE(OperandFuncParamAttr) | 
|  | +    CASE(OperandDecoration) | 
|  | +    CASE(OperandBuiltIn) | 
|  | +    CASE(OperandSelect) | 
|  | +    CASE(OperandLoop) | 
|  | +    CASE(OperandFunction) | 
|  | +    CASE(OperandMemorySemantics) | 
|  | +    CASE(OperandMemoryAccess) | 
|  | +    CASE(OperandScope) | 
|  | +    CASE(OperandGroupOperation) | 
|  | +    CASE(OperandKernelEnqueueFlags) | 
|  | +    CASE(OperandKernelProfilingInfo) | 
|  | +    CASE(OperandCapability) | 
|  | +    CASE(OperandOpcode) | 
|  | +#undef CASE | 
|  | + | 
|  | +    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 std::vector<OperandClass>& classes, std::ostream& out) { | 
|  | +  std::stringstream contents; | 
|  | +  int numPrinted = 0; | 
|  | +  for (auto operandClass : classes) { | 
|  | +    if (const char* name = GetOperandClassString(operandClass)) { | 
|  | +      if (numPrinted) contents << ", "; | 
|  | +      contents << name; | 
|  | +      numPrinted++; | 
|  | +    } | 
|  | +  } | 
|  | + | 
|  | +  if (numPrinted) | 
|  | +    out << "List(" << contents.str() << ")"; | 
|  | +  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) { | 
|  | +  const char* name = OpcodeString(opcode); | 
|  | +  // There can be gaps in the listing. | 
|  | +  // All valid operations have a name beginning with "Op". | 
|  | +  if (strlen(name) > 2 && name[0] == 'O' && name[1] == 'p') { | 
|  | +    out << "Instruction(" | 
|  | +        << name + 2 << ", "; // Skip the "Op" part. | 
|  | +    out << (inst.hasResult() ? 1 : 0) << ", "; | 
|  | +    out << (inst.hasType() ? 1 : 0) << ", "; | 
|  | +    out << inst.operands.getNum() << ", "; | 
|  | + | 
|  | +    // Emit the capability, if any. | 
|  | +    // The SPIR-V tools doesn't handle the concept of depending on more than | 
|  | +    // one capability.  So call it out separately.  Currently the biggest case | 
|  | +    // is 2.  This is a big hack. | 
|  | +    out << inst.capabilities.size() << ", "; | 
|  | +    assert(inst.capabilities.size() < 3); | 
|  | +    if (inst.capabilities.size() == 1) { | 
|  | +      out << "Capability(" << CapabilityString(inst.capabilities[0]) << "), "; | 
|  | +    } else if (inst.capabilities.size() == 2) { | 
|  | +      out << "Capability2(" << CapabilityString(inst.capabilities[0]) << "," | 
|  | +          << CapabilityString(inst.capabilities[1]) << "), "; | 
|  | +    } else { | 
|  | +      out << "Capability(None), "; | 
|  | +    } | 
|  | + | 
|  | +    out << (IsVariableLength(inst) ? 1 : 0) << ", "; | 
|  | +    PrintOperandClassesForInstruction(inst, out); | 
|  | +    out << ")" << std::endl; | 
|  | +  } | 
|  | +} | 
|  | + | 
|  | +} // anonymous namespace | 
|  | + | 
|  | +void PrintAssemblerTable(std::ostream& out) { | 
|  | +  out << "// Instruction fields are:\n" | 
|  | +      << "//    name - skips the \"Op\" prefix\n" | 
|  | +      << "//    {0|1} - whether the instruction generates a result Id\n" | 
|  | +      << "//    {0|1} - whether the instruction encodes the type of the result Id\n" | 
|  | +      << "//    numLogicalOperands - does not include result id or type id\n" | 
|  | +      << "//    numCapabilities - we only handle 0 or 1 required capabilities\n" | 
|  | +      << "//    Capability(<capability-name>) - capability required to use this instruction. Might be None.\n" | 
|  | +      << "//       There can be Capability2(a,b) for dependence on two capabilities.\n" | 
|  | +      << "//    {0|1} - whether the instruction is variable number of words\n" | 
|  | +      << "//    EmptyList or List(...) - list of classes of logical operands\n" | 
|  | +      << "// Example use:\n" | 
|  | +      << "// #define EmptyList {}\n" | 
|  | +      << "// #define List(...) {__VA_ARGS__}\n" | 
|  | +      << "// #define Capability(C) Capability##C\n" | 
|  | +      << "// #define CapabilityNone -1\n" | 
|  | +      << "// #define Instruction(Name,HasResult,HasType,NumLogicalOperands,CapabiltyRequired,IsVariable,LogicalArgsList)\n"; | 
|  | + | 
|  | +  for (int i = 0; i < spv::OpcodeCeiling ; i++ ) { | 
|  | +    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..b98303a | 
|  | --- /dev/null | 
|  | +++ b/tools/spirv/assembler_table.h | 
|  | @@ -0,0 +1,49 @@ | 
|  | +// 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. | 
|  | + | 
|  | +#pragma once | 
|  | +#ifndef ASSEMBLER_TABLE_H | 
|  | +#define ASSEMBLER_TABLE_H | 
|  | + | 
|  | +#include <iostream> | 
|  | +#include <vector> | 
|  | + | 
|  | +#include "doc.h" | 
|  | + | 
|  | +namespace spv { | 
|  | + | 
|  | +    // Prints the tables used to define the structure of instructions. | 
|  | +    // 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..a182509 100644 | 
|  | --- a/tools/spirv/doc.h | 
|  | +++ b/tools/spirv/doc.h | 
|  | @@ -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); | 
|  | +const char* OperandClassString(int); | 
|  |  | 
|  | // For grouping opcodes into subsections | 
|  | enum OpcodeClass { | 
|  | @@ -159,6 +163,10 @@ enum OperandClass { | 
|  |  | 
|  | OperandOpcode, | 
|  |  | 
|  | +    // The operand class enum is not part of the spec, so | 
|  | +    // it should come after OperandOpcode. | 
|  | +    OperandOperandClass, | 
|  | + | 
|  | 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..e1413bb 100644 | 
|  | --- a/tools/spirv/main.cpp | 
|  | +++ b/tools/spirv/main.cpp | 
|  | @@ -46,6 +46,7 @@ namespace spv { | 
|  | #include "headers/OpenCL.std.h" | 
|  |  | 
|  | // This tool's includes | 
|  | +#include "assembler_table.h" | 
|  | #include "disassemble.h" | 
|  | #include "header.h" | 
|  | #include "doc.h" | 
|  | @@ -65,6 +66,8 @@ enum TOptions { | 
|  | EOptionDisassemble                = 0x004, | 
|  | EOptionPrintHeader                = 0x008, | 
|  | EOptionPrintOclBuiltinsAsciidoc   = 0x010, | 
|  | +    EOptionPrintAssemblerTable        = 0x020, | 
|  | +    EOptionPrintOclInstructionsTable  = 0x040, | 
|  | }; | 
|  |  | 
|  | std::string Filename; | 
|  | @@ -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 +160,12 @@ bool ProcessArguments(int argc, char* argv[]) | 
|  | } | 
|  | return true; | 
|  | } | 
|  | +            case 'a': | 
|  | +                Options |= EOptionPrintAssemblerTable; | 
|  | +                break; | 
|  | +            case 'C': | 
|  | +                Options |= EOptionPrintOclInstructionsTable; | 
|  | +                break; | 
|  | default: | 
|  | return false; | 
|  | } | 
|  | @@ -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; | 
|  | } |