diff --git a/readme.md b/readme.md index 71532f8e..85dc3d14 100644 --- a/readme.md +++ b/readme.md @@ -144,7 +144,15 @@ There are three main entry points into the library. In addition to the interface header `/include/libspirv/libspirv.h` the implementation source files reside in `/source/*`. -## Tools +The parsers for the assembler and disassembler use a table describing the +syntax of each core instruction. This table can be generated from the SPIR-V +document generator: + +1. Apply the patch in `source/core_syntax_table.patch` to the document generator. +2. Run the document generator with the `-a` option and place the results in +the `opcode.inc` file in the SPIR-V Tools `source` directory. +3. Be aware of version skew: The SPIR-V document generator might target a newer +verison of the spec than targeted by the SPIR-V tools. ### Assembler diff --git a/source/core_syntax_table.patch b/source/core_syntax_table.patch new file mode 100644 index 00000000..9af15f69 --- /dev/null +++ b/source/core_syntax_table.patch @@ -0,0 +1,329 @@ +diff --git a/tools/spirv/CMakeLists.txt b/tools/spirv/CMakeLists.txt +index 6b47799..7b687e4 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/assembler_table.cpp b/tools/spirv/assembler_table.cpp +new file mode 100644 +index 0000000..63ea9a5 +--- /dev/null ++++ b/tools/spirv/assembler_table.cpp +@@ -0,0 +1,194 @@ ++// 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 ++#include ++#include ++ ++#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 OperandNone: ++ case OperandCount: ++ default: ++ break; ++ } ++ return nullptr; ++} ++ ++// 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) { ++ 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 << ", "; ++ contents << name; ++ numPrinted++; ++ } ++ } ++ ++ if (numPrinted) ++ out << "List(" << contents.str() << ")"; ++ else ++ out << "EmptyList"; ++} ++ ++// 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. ++ // We only handle 0 or 1 capabiliites. ++ out << inst.capabilities.size() << ", "; ++ assert(inst.capabilities.size() < 2); ++ if (inst.capabilities.size()) { ++ out << "Capability(" << CapabilityString(inst.capabilities[0]) << "), "; ++ } else { ++ out << "Capability(None), "; ++ } ++ ++ out << (isVariableLength(inst) ? 1 : 0) << ", "; ++ printOperandClasses(inst, out); ++ out << ")" << std::endl; ++ } ++} ++ ++} ++ ++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 required to use this instruction. Might be None.\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..03ab769 +--- /dev/null ++++ b/tools/spirv/assembler_table.h +@@ -0,0 +1,41 @@ ++// 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 ++ ++namespace spv { ++ ++ // Prints the tables used to define the structure of instructions. ++ // Assumes that parameterization has already occurred ++ void PrintAssemblerTable(std::ostream& out); ++ ++}; // end namespace spv ++ ++#endif // ASSEMBLER_TABLE_H +diff --git a/tools/spirv/doc.h b/tools/spirv/doc.h +index 2b326e9..d16febb 100644 +--- a/tools/spirv/doc.h ++++ b/tools/spirv/doc.h +@@ -81,6 +81,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 +160,10 @@ enum OperandClass { + + OperandOpcode, + ++ // The operand class enum is not part of the spec, so ++ // it should come after OperandOpcode. ++ OperandOperandClass, ++ + OperandCount + }; + +diff --git a/tools/spirv/main.cpp b/tools/spirv/main.cpp +index d10bd2a..b825974 100644 +--- a/tools/spirv/main.cpp ++++ b/tools/spirv/main.cpp +@@ -46,6 +46,7 @@ namespace spv { + #include "headers/OpenCLLib.h" + + // This tool's includes ++#include "assembler_table.h" + #include "disassemble.h" + #include "header.h" + #include "doc.h" +@@ -65,6 +66,7 @@ enum TOptions { + EOptionDisassemble = 0x004, + EOptionPrintHeader = 0x008, + EOptionPrintOclBuiltinsAsciidoc = 0x010, ++ EOptionPrintAssemblerTable = 0x020, + }; + + 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" ++ " -a print table for the assembler\n" + ); + } + +@@ -155,6 +158,9 @@ bool ProcessArguments(int argc, char* argv[]) + } + return true; + } ++ case 'a': ++ Options |= EOptionPrintAssemblerTable; ++ break; + default: + return false; + } +@@ -220,5 +226,8 @@ int main(int argc, char* argv[]) + if (Options & EOptionPrintHeader) + spv::PrintHeader(Language, std::cout); + ++ if (Options & EOptionPrintAssemblerTable) ++ spv::PrintAssemblerTable(std::cout); ++ + return 0; + }