Add a header generator project.

This commit is contained in:
John Kessenich 2018-01-23 02:46:13 -07:00
parent 6205884fb3
commit 7747a02cce
11 changed files with 9047 additions and 35 deletions

View File

@ -1,40 +1,33 @@
# SPIR-V Headers
This repository contains machine-readable files from the
This repository contains machine-readable files for the
[SPIR-V Registry](https://www.khronos.org/registry/spir-v/).
This includes:
* Header files for various languages.
* JSON files describing the grammar for the SPIR-V core instruction set,
and for the GLSL.std.450 extended instruction set.
* JSON files describing the grammar for the SPIR-V core instruction set
and the extended instruction sets.
* The XML registry file.
* A tool to build the headers from the JSON grammar.
Under the [include](include) directory, header files are provided according to
their own version. Only major and minor version numbers count.
For example, the headers for SPIR-V 1.1 are in
[include/spirv/1.1](include/spirv/1.1). Also, the headers for the 1.0 versions
of the GLSL.std.450 and OpenCL extended instruction sets are in
[include/spirv/1.0](include/spirv/1.0).
Headers are provided in the [include](include) directory, with up-to-date
headers in the `unified1` subdirectory. Older headers are provided according to
their version.
In contrast, the XML registry file has a linear history, so it is
not tied to SPIR-V specification versions.
## How is this repository updated?
When a new version or revision of the SPIR-V header files are published,
the SPIR Working Group will push new commits onto master, updating
When a new version or revision of the SPIR-V specification is published,
the SPIR-V Working Group will push new commits onto master, updating
the files under [include](include).
A newer revision of a header file always replaces an older revision of
the same version. For example, verison 1.0 Rev 4 of `spirv.h` is placed in
`include/spirv/1.0/spirv.h` and if there is a Rev 5, then it will be placed
in the same location.
The SPIR-V XML registry file is updated by the Khronos registrar whenever
a new enum range is allocated.
The SPIR-V XML registry file is updated by Khronos whenever a new enum range is allocated.
In particular, pull requests that update header files will not be accepted.
Issues with the header files should be filed in the [issue
tracker](https://github.com/KhronosGroup/SPIRV-Headers/issues).
Pull requests can be made to
- request allocation of new enum ranges in the XML registry file
- reserve specific tokens in the JSON grammar
## How to install the headers
@ -48,7 +41,7 @@ cmake --build . --target install-headers
cmake --build . --config Debug --target install-headers
```
Then, for example, you will have `/usr/local/include/spirv/1.0/spirv.h`
Then, for example, you will have `/usr/local/include/spirv/unified1/spirv.h`
If you want to install them somewhere else, then use
`-DCMAKE_INSTALL_PREFIX=/other/path` on the first `cmake` command.
@ -61,30 +54,40 @@ A CMake-based project can use the headers without installing, as follows:
2. Use `${SPIRV-Headers_SOURCE_DIR}/include}` in a `target_include_directories`
directive.
3. In your C or C++ source code use `#include` directives that explicitly mention
the `spirv` path component. For example the following uses SPIR-V 1.1
core instructions, and the 1.0 versions of the GLSL.std.450 and OpenCL
extended instructions.
the `spirv` path component.
```
#include "spirv/1.0/GLSL.std.450.h"
#include "spirv/1.0/OpenCL.std.h"
#include "spirv/1.1/spirv.hpp"
#include "spirv/unified1/GLSL.std.450.h"
#include "spirv/unified1/OpenCL.std.h"
#include "spirv/unified1/spirv.hpp"
```
See also the [example](example/) subdirectory. But since that example is
*inside* this repostory, it doesn't use and `add_subdirectory` directive.
## Generating the headers from the JSON grammar
This will generally be done by Khronos, for a change to the JSON grammar.
However, the project for the tool to do this is included in this repository,
and can be used to test a PR, or even to include the results in the PR.
This is not required though.
The header-generation project is under the `buildHeaders` directory.
Use CMake to build the project, in a `buildHeaders/build` subdirectory.
There is a bash script at `buildHeaders/bin/makeHeaders` that shows how to use the built
header-generator binary to generate the headers from the JSON grammar.
Notes:
- this generator is used in a broader context within Khronos to generate the specification,
and that influences the languages used, for legacy reasons
- the C++ structures built may similarly include more than strictly necessary, for the same reason
## FAQ
* *How are different versions published?*
All versions are present in the master branch of the repository.
They are located in different subdirectories under
[include/spirv](include/spirv), where the subdirectory at that
level encodes the major and minor version number of the relevant spec.
An application should consciously select the targeted spec version
number, by naming the specific version in its `#include` directives,
as above and in the examples.
The multiple versions of the headers have been simplified into a
single `unified1` view. The JSON grammar has a "version" field saying
what version things first showed up in.
* *How do you handle the evolution of extended instruction sets?*

View File

@ -0,0 +1,26 @@
cmake_minimum_required(VERSION 2.8)
set(CMAKE_INSTALL_PREFIX "install" CACHE STRING "prefix" FORCE)
project(buildSpvHeaders)
set(SOURCES
main.cpp
jsonToSpirv.cpp
header.cpp
jsoncpp/dist/jsoncpp.cpp)
set(HEADERS
jsonToSpirv.h
header.h
jsoncpp/dist/json/json.h)
if(CMAKE_COMPILER_IS_GNUCXX)
add_definitions(-std=c++11)
elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_definitions(-std=c++11)
endif()
add_executable(buildSpvHeaders ${SOURCES} ${HEADERS})
install(TARGETS buildSpvHeaders RUNTIME DESTINATION bin)

View File

@ -0,0 +1,5 @@
#!/usr/bin/bash
cd ../../include/spirv/unified1
../../../tools/buildHeaders/build/install/bin/buildSpvHeaders -H spirv.core.grammar.json
dos2unix spirv.*

713
tools/buildHeaders/header.cpp Executable file
View File

@ -0,0 +1,713 @@
// Copyright (c) 2014-2018 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.
//
// Print headers for SPIR-V in several languages.
//
// To change the header information, change the C++-built database in doc.*.
//
// Then, use "spriv -h <language>" - e.g, spriv.{h,hpp,lua,py,etc}:
// replace the auto-generated header, or "spirv -H" to generate all
// supported language headers to predefined names in the current directory.
//
#include <string>
#include <sstream>
#include <fstream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <memory>
#include <cctype>
#include <vector>
#include <utility>
#include "jsoncpp/dist/json/json.h"
#include "header.h"
#include "jsonToSpirv.h"
// snprintf and _snprintf are not quite the same, but close enough
// for our use.
#ifdef _MSC_VER
#pragma warning(disable:4996)
#define snprintf _snprintf
#endif
// This file converts SPIR-V definitions to an internal JSON
// representation, and then generates language specific
// data from that single internal form.
// Initially, the internal form is created from C++ data,
// though this can be changed to a JSON master in time.
namespace {
class TPrinter {
protected:
TPrinter();
static const int DocMagicNumber = 0x07230203;
static const int DocVersion = 0x00010200;
static const int DocRevision = 3;
#define DocRevisionString "3"
static const std::string DocCopyright;
static const std::string DocComment1;
static const std::string DocComment2;
enum enumStyle_t {
enumNoMask,
enumCount,
enumShift,
enumMask,
enumHex,
};
static std::string styleStr(enumStyle_t s) {
return s == enumShift ? "Shift" :
s == enumMask ? "Mask" : "";
}
friend std::ostream& operator<<(std::ostream&, const TPrinter&);
virtual void printAll(std::ostream&) const;
virtual void printComments(std::ostream&) const;
virtual void printPrologue(std::ostream&) const { }
virtual void printDefs(std::ostream&) const;
virtual void printEpilogue(std::ostream&) const { }
virtual void printMeta(std::ostream&) const;
virtual void printTypes(std::ostream&) const { }
virtual std::string escapeComment(const std::string& s) const;
// Default printComments() uses these comment strings
virtual std::string commentBeg() const { return ""; }
virtual std::string commentEnd(bool isLast) const { return ""; }
virtual std::string commentBOL() const { return ""; }
virtual std::string commentEOL(bool isLast) const { return ""; }
typedef std::pair<unsigned, std::string> valpair_t;
// for printing enum values
virtual std::string enumBeg(const std::string&, enumStyle_t) const { return ""; }
virtual std::string enumEnd(const std::string&, enumStyle_t, bool isLast = false) const {
return "";
}
virtual std::string enumFmt(const std::string&, const valpair_t&,
enumStyle_t, bool isLast = false) const {
return "";
}
virtual std::string maxEnumFmt(const std::string&, const valpair_t&,
enumStyle_t) const {
return "";
}
virtual std::string fmtConstInt(unsigned val, const std::string& name,
const char* fmt, bool isLast = false) const {
return "";
}
std::vector<valpair_t> getSortedVals(const Json::Value&) const;
virtual std::string indent(int count = 1) const {
return std::string(count * 4, ' '); // default indent level = 4
}
static std::string fmtNum(const char* fmt, unsigned val) {
char buff[16]; // ample for 8 hex digits + 0x
snprintf(buff, sizeof(buff), fmt, val);
buff[sizeof(buff)-1] = '\0'; // MSVC doesn't promise null termination
return buff;
}
static std::string fmtStyleVal(unsigned v, enumStyle_t style);
// If the enum value name would start with a sigit, prepend the enum name.
// E.g, "3D" -> "Dim3D".
static std::string prependIfDigit(const std::string& ename, const std::string& vname) {
return (std::isdigit(vname[0]) ? ename : std::string("")) + vname;
}
void addComment(Json::Value& node, const std::string& str);
Json::Value spvRoot; // JSON SPIR-V data
};
// Format value as mask or value
std::string TPrinter::fmtStyleVal(unsigned v, enumStyle_t style)
{
switch (style) {
case enumMask:
return fmtNum("0x%08x", 1<<v);
case enumHex:
return fmtNum("0x%08x", v);
default:
return std::to_string(v);
}
}
const std::string TPrinter::DocCopyright =
"Copyright (c) 2014-2018 The Khronos Group Inc.\n"
"\n"
"Permission is hereby granted, free of charge, to any person obtaining a copy\n"
"of this software and/or associated documentation files (the \"Materials\"),\n"
"to deal in the Materials without restriction, including without limitation\n"
"the rights to use, copy, modify, merge, publish, distribute, sublicense,\n"
"and/or sell copies of the Materials, and to permit persons to whom the\n"
"Materials are furnished to do so, subject to the following conditions:\n"
"\n"
"The above copyright notice and this permission notice shall be included in\n"
"all copies or substantial portions of the Materials.\n"
"\n"
"MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS\n"
"STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND\n"
"HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ \n"
"\n"
"THE MATERIALS ARE PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\n"
"OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n"
"FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n"
"THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n"
"LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n"
"FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS\n"
"IN THE MATERIALS.\n";
const std::string TPrinter::DocComment1 =
"This header is automatically generated by the same tool that creates\n"
"the Binary Section of the SPIR-V specification.\n";
const std::string TPrinter::DocComment2 =
"Enumeration tokens for SPIR-V, in various styles:\n"
" C, C++, C++11, JSON, Lua, Python\n"
"\n"
"- C will have tokens with a \"Spv\" prefix, e.g.: SpvSourceLanguageGLSL\n"
"- C++ will have tokens in the \"spv\" name space, e.g.: spv::SourceLanguageGLSL\n"
"- C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL\n"
"- Lua will use tables, e.g.: spv.SourceLanguage.GLSL\n"
"- Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL']\n"
"\n"
"Some tokens act like mask values, which can be OR'd together,\n"
"while others are mutually exclusive. The mask-like ones have\n"
"\"Mask\" in their name, and a parallel enum that has the shift\n"
"amount (1 << x) for each corresponding enumerant.\n";
// Construct
TPrinter::TPrinter()
{
Json::Value& meta = spvRoot["spv"]["meta"];
Json::Value& enums = spvRoot["spv"]["enum"];
meta["MagicNumber"] = DocMagicNumber;
meta["Version"] = DocVersion;
meta["Revision"] = DocRevision;
meta["OpCodeMask"] = 0xffff;
meta["WordCountShift"] = 16;
int commentId = 0;
addComment(meta["Comment"][commentId++], DocCopyright);
addComment(meta["Comment"][commentId++], DocComment1);
addComment(meta["Comment"][commentId++], DocComment2);
for (int e = spv::OperandSource; e < spv::OperandOpcode; ++e) {
auto& enumSet = spv::OperandClassParams[e];
const bool mask = enumSet.bitmask;
const std::string enumName = enumSet.codeName;
for (auto& enumRow : enumSet) {
std::string name = enumRow.name;
enums[e - spv::OperandSource]["Values"][name] = enumRow.value;
}
enums[e - spv::OperandSource]["Type"] = mask ? "Bit" : "Value";
enums[e - spv::OperandSource]["Name"] = enumName;
}
// Instructions are in their own different table
{
auto& entry = enums[spv::OperandOpcode - spv::OperandSource];
for (auto& enumRow : spv::InstructionDesc) {
std::string name = enumRow.name;
entry["Values"][name] = enumRow.value;
}
entry["Type"] = "Value";
entry["Name"] = "Op";
}
}
// Create comment
void TPrinter::addComment(Json::Value& node, const std::string& str)
{
std::istringstream cstream(str);
std::string cline;
int line = 0;
while (std::getline(cstream, cline)) // fmt each line
node[line++] = cline;
}
// Return a list of values sorted by enum value. The std::vector
// returned by value is okay in c++11 due to move semantics.
std::vector<TPrinter::valpair_t>
TPrinter::getSortedVals(const Json::Value& p) const
{
std::vector<valpair_t> values;
for (auto e = p.begin(); e != p.end(); ++e)
values.push_back(valpair_t(e->asUInt(), e.name()));
// Use a stable sort because we might have aliases, e.g.
// SubgropuBallot (might be in future core) vs. SubgroupBallotKHR.
std::stable_sort(values.begin(), values.end());
return values;
}
// Escape comment characters if needed
std::string TPrinter::escapeComment(const std::string& s) const { return s; }
// Format comments in language specific way
void TPrinter::printComments(std::ostream& out) const
{
const int commentCount = spvRoot["spv"]["meta"]["Comment"].size();
int commentNum = 0;
for (const auto& comment : spvRoot["spv"]["meta"]["Comment"]) {
out << commentBeg();
for (int line = 0; line < int(comment.size()); ++line)
out << commentBOL() << escapeComment(comment[line].asString()) <<
commentEOL((line+1) == comment.size()) << std::endl;
out << commentEnd(++commentNum == commentCount) << std::endl;
}
}
// Format header metadata
void TPrinter::printMeta(std::ostream& out) const
{
const Json::Value& meta = spvRoot["spv"]["meta"];
const auto print = [&](const char* name, const char* fmt, bool isLast) {
out << fmtConstInt(meta[name].asUInt(), name, fmt, isLast);
};
print("MagicNumber", "0x%08lx", false);
print("Version", "0x%08lx", false);
print("Revision", "%d", false);
print("OpCodeMask", "0x%04x", false);
print("WordCountShift", "%d", true);
}
// Format value definitions in language specific way
void TPrinter::printDefs(std::ostream& out) const
{
const Json::Value& enums = spvRoot["spv"]["enum"];
for (auto opClass = enums.begin(); opClass != enums.end(); ++opClass) {
const bool isMask = (*opClass)["Type"].asString() == "Bit";
const auto opName = (*opClass)["Name"].asString();
const auto opPrefix = opName == "Op" ? "" : opName;
for (enumStyle_t style = (isMask ? enumShift : enumCount);
style <= (isMask ? enumMask : enumCount); style = enumStyle_t(int(style)+1)) {
out << enumBeg(opName, style);
if (style == enumMask)
out << enumFmt(opPrefix, valpair_t(0, "MaskNone"), enumNoMask);
const auto sorted = getSortedVals((*opClass)["Values"]);
std::string maxEnum = maxEnumFmt(opName, valpair_t(0x7FFFFFFF, "Max"), enumHex);
bool printMax = (style != enumMask && maxEnum.size() > 0);
for (const auto& v : sorted)
out << enumFmt(opPrefix, v, style, !printMax && v.first == sorted.back().first);
if (printMax)
out << maxEnum;
auto nextOpClass = opClass;
out << enumEnd(opName, style, ++nextOpClass == enums.end());
}
}
}
void TPrinter::printAll(std::ostream& out) const
{
printComments(out);
printPrologue(out);
printTypes(out);
printMeta(out);
printDefs(out);
printEpilogue(out);
}
// Stream entire header to output
std::ostream& operator<<(std::ostream& out, const TPrinter &p)
{
p.printAll(out);
return out;
}
// JSON printer. Rather than use the default printer, we supply our own so
// we can control the printing order within various containers.
class TPrinterJSON final : public TPrinter {
private:
void printPrologue(std::ostream& out) const override { out << "{\n" + indent() + "\"spv\":\n" + indent() + "{\n"; }
void printEpilogue(std::ostream& out) const override { out << indent() + "}\n}\n"; }
std::string escapeComment(const std::string& s) const override {
std::string newStr;
for (auto c : s) {
if (c == '"') {
newStr += '\\';
newStr += c;
} else {
newStr += c;
}
}
return newStr;
}
std::string fmtConstInt(unsigned val, const std::string& name,
const char* fmt, bool isLast) const override {
return indent(3) + '"' + name + "\": " + fmtNum("%d", val) + (isLast ? "\n" : ",\n");
}
void printMeta(std::ostream& out) const override
{
out << indent(2) + "\"meta\":\n" + indent(2) + "{\n";
printComments(out);
TPrinter::printMeta(out);
out << indent(2) + "},\n";
}
std::string commentBeg() const override { return indent(4) + "[\n"; }
std::string commentEnd(bool isLast) const override { return indent(4) + (isLast ? "]" : "],"); }
std::string commentBOL() const override { return indent(5) + '"'; }
std::string commentEOL(bool isLast) const override { return (isLast ? "\"" : "\","); }
void printComments(std::ostream& out) const override
{
out << indent(3) + "\"Comment\":\n" + indent(3) + "[\n";
TPrinter::printComments(out);
out << indent(3) + "],\n";
}
void printDefs(std::ostream& out) const override
{
out << indent(2) + "\"enum\":\n" + indent(2) + "[\n";
TPrinter::printDefs(out);
out << indent(2) + "]\n";
}
void printAll(std::ostream& out) const override
{
printPrologue(out);
printMeta(out);
printDefs(out);
printEpilogue(out);
}
std::string enumBeg(const std::string& s, enumStyle_t style) const override {
if (style == enumMask)
return "";
return indent(3) + "{\n" +
indent(4) + "\"Name\": \"" + s + "\",\n" +
indent(4) + "\"Type\": " + (style == enumShift ? "\"Bit\"" : "\"Value\"") + ",\n" +
indent(4) + "\"Values\":\n" +
indent(4) + "{\n";
}
std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override {
if (style == enumMask)
return "";
return indent(4) + "}\n" +
indent(3) + "}" + (isLast ? "" : ",") + "\n";
}
std::string enumFmt(const std::string& s, const valpair_t& v,
enumStyle_t style, bool isLast) const override {
if (style == enumMask || style == enumNoMask)
return "";
return indent(5) + '"' + prependIfDigit(s, v.second) + "\": " + fmtNum("%d", v.first) +
(isLast ? "\n" : ",\n");
}
};
// base for C and C++
class TPrinterCBase : public TPrinter {
protected:
virtual void printPrologue(std::ostream& out) const override {
out << "#ifndef spirv_" << headerGuardSuffix() << std::endl
<< "#define spirv_" << headerGuardSuffix() << std::endl
<< std::endl;
}
void printMeta(std::ostream& out) const override {
out << "#define SPV_VERSION 0x" << std::hex << DocVersion << std::dec << "\n";
out << "#define SPV_REVISION " << DocRevision << "\n";
out << "\n";
return TPrinter::printMeta(out);
}
virtual void printEpilogue(std::ostream& out) const override {
out << "#endif // #ifndef spirv_" << headerGuardSuffix() << std::endl;
}
virtual void printTypes(std::ostream& out) const override {
out << "typedef unsigned int " << pre() << "Id;\n\n";
}
virtual std::string fmtConstInt(unsigned val, const std::string& name,
const char* fmt, bool isLast) const override
{
return std::string("static const unsigned int ") + pre() + name +
" = " + fmtNum(fmt, val) + (isLast ? ";\n\n" : ";\n");
}
virtual std::string pre() const { return ""; } // C name prefix
virtual std::string headerGuardSuffix() const = 0;
};
// C printer
class TPrinterC final : public TPrinterCBase {
private:
std::string commentBeg() const override { return "/*\n"; }
std::string commentEnd(bool isLast) const override { return "*/\n"; }
std::string commentBOL() const override { return "** "; }
std::string enumBeg(const std::string& s, enumStyle_t style) const override {
return std::string("typedef enum ") + pre() + s + styleStr(style) + "_ {\n";
}
std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override {
return "} " + pre() + s + styleStr(style) + ";\n\n";
}
std::string enumFmt(const std::string& s, const valpair_t& v,
enumStyle_t style, bool isLast) const override {
return indent() + pre() + s + v.second + styleStr(style) + " = " + fmtStyleVal(v.first, style) + ",\n";
}
std::string maxEnumFmt(const std::string& s, const valpair_t& v,
enumStyle_t style) const override {
return enumFmt(s, v, style, true);
}
std::string pre() const override { return "Spv"; } // C name prefix
std::string headerGuardSuffix() const override { return "H"; }
};
// C++ printer
class TPrinterCPP : public TPrinterCBase {
private:
void printPrologue(std::ostream& out) const override {
TPrinterCBase::printPrologue(out);
out << "namespace spv {\n\n";
}
void printEpilogue(std::ostream& out) const override {
const Json::Value& enums = spvRoot["spv"]["enum"];
// Create overloaded operator| for mask types
out << "// Overload operator| for mask bit combining\n\n";
for (auto opClass = enums.begin(); opClass != enums.end(); ++opClass) {
const bool isMask = (*opClass)["Type"].asString() == "Bit";
const auto opName = (*opClass)["Name"].asString();
if (isMask) {
const auto typeName = opName + styleStr(enumMask);
out << "inline " + typeName + " operator|(" + typeName + " a, " + typeName + " b) { return " +
typeName + "(unsigned(a) | unsigned(b)); }\n";
}
}
out << "\n} // end namespace spv\n\n";
TPrinterCBase::printEpilogue(out);
}
std::string commentBOL() const override { return "// "; }
virtual std::string enumBeg(const std::string& s, enumStyle_t style) const override {
return std::string("enum ") + s + styleStr(style) + " {\n";
}
std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override {
return "};\n\n";
}
virtual std::string enumFmt(const std::string& s, const valpair_t& v,
enumStyle_t style, bool isLast) const override {
return indent() + s + v.second + styleStr(style) + " = " + fmtStyleVal(v.first, style) + ",\n";
}
virtual std::string maxEnumFmt(const std::string& s, const valpair_t& v,
enumStyle_t style) const override {
return enumFmt(s, v, style, true);
}
// The C++ and C++11 headers define types with the same name. So they
// should use the same header guard.
std::string headerGuardSuffix() const override { return "HPP"; }
std::string operators;
};
// C++11 printer (uses enum classes)
class TPrinterCPP11 final : public TPrinterCPP {
private:
std::string enumBeg(const std::string& s, enumStyle_t style) const override {
return std::string("enum class ") + s + styleStr(style) + " : unsigned {\n";
}
std::string enumFmt(const std::string& s, const valpair_t& v,
enumStyle_t style, bool isLast) const override {
return indent() + prependIfDigit(s, v.second) + " = " + fmtStyleVal(v.first, style) + ",\n";
}
std::string maxEnumFmt(const std::string& s, const valpair_t& v,
enumStyle_t style) const override {
return enumFmt(s, v, style, true);
}
std::string headerGuardSuffix() const override { return "HPP"; }
};
// LUA printer
class TPrinterLua final : public TPrinter {
private:
void printPrologue(std::ostream& out) const override { out << "spv = {\n"; }
void printEpilogue(std::ostream& out) const override { out << "}\n"; }
std::string commentBOL() const override { return "-- "; }
std::string enumBeg(const std::string& s, enumStyle_t style) const override {
return indent() + s + styleStr(style) + " = {\n";
}
std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override {
return indent() + "},\n\n";
}
std::string enumFmt(const std::string& s, const valpair_t& v,
enumStyle_t style, bool isLast) const override {
return indent(2) + prependIfDigit(s, v.second) + " = " + fmtStyleVal(v.first, style) + ",\n";
}
virtual std::string fmtConstInt(unsigned val, const std::string& name,
const char* fmt, bool isLast) const override
{
return indent() + name + " = " + fmtNum(fmt, val) + (isLast ? ",\n\n" : ",\n");
}
};
// Python printer
class TPrinterPython final : public TPrinter {
private:
void printPrologue(std::ostream& out) const override { out << "spv = {\n"; }
void printEpilogue(std::ostream& out) const override { out << "}\n"; }
std::string commentBOL() const override { return "# "; }
std::string enumBeg(const std::string& s, enumStyle_t style) const override {
return indent() + "'" + s + styleStr(style) + "'" + " : {\n";
}
std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override {
return indent() + "},\n\n";
}
std::string enumFmt(const std::string& s, const valpair_t& v,
enumStyle_t style, bool isLast) const override {
return indent(2) + "'" + prependIfDigit(s, v.second) + "'" + " : " + fmtStyleVal(v.first, style) + ",\n";
}
std::string fmtConstInt(unsigned val, const std::string& name,
const char* fmt, bool isLast) const override
{
return indent() + "'" + name + "'" + " : " + fmtNum(fmt, val) + (isLast ? ",\n\n" : ",\n");
}
};
} // namespace
namespace spv {
void PrintAllHeaders()
{
// TODO: Once MSVC 2012 is no longer a factor, use brace initializers here
std::vector<std::pair<TLanguage, std::string>> langInfo;
langInfo.push_back(std::make_pair(ELangC, "spirv.h"));
langInfo.push_back(std::make_pair(ELangCPP, "spirv.hpp"));
langInfo.push_back(std::make_pair(ELangCPP11, "spirv.hpp11"));
langInfo.push_back(std::make_pair(ELangJSON, "spirv.json"));
langInfo.push_back(std::make_pair(ELangLua, "spirv.lua"));
langInfo.push_back(std::make_pair(ELangPython, "spirv.py"));
for (const auto& lang : langInfo) {
std::ofstream out(lang.second, std::ios::out);
if ((out.rdstate() & std::ifstream::failbit)) {
std::cerr << "Unable to open file: " << lang.second << std::endl;
} else {
PrintHeader(lang.first, out);
}
}
}
// Print header for given language to given output stream
void PrintHeader(TLanguage lang, std::ostream& out)
{
typedef std::unique_ptr<TPrinter> TPrinterPtr;
TPrinterPtr p;
switch (lang) {
case ELangC: p = TPrinterPtr(new TPrinterC); break;
case ELangCPP: p = TPrinterPtr(new TPrinterCPP); break;
case ELangCPP11: p = TPrinterPtr(new TPrinterCPP11); break;
case ELangJSON: p = TPrinterPtr(new TPrinterJSON); break;
case ELangLua: p = TPrinterPtr(new TPrinterLua); break;
case ELangPython: p = TPrinterPtr(new TPrinterPython); break;
case ELangAll: PrintAllHeaders(); break;
default:
std::cerr << "Unknown language." << std::endl;
return;
}
// Print the data in the requested format
if (p)
out << *p << std::endl;
// object is auto-deleted
}
} // namespace spv

52
tools/buildHeaders/header.h Executable file
View File

@ -0,0 +1,52 @@
// Copyright (c) 2014-2018 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.
//
// Print headers for SPIR-V in several languages.
//
#pragma once
#ifndef header
#define header
#include <iostream>
namespace spv {
// Languages supported
enum TLanguage {
ELangC, // C
ELangCPP, // C++03
ELangCPP11, // C++11
ELangJSON, // JSON
ELangLua, // Lua
ELangPython, // Python
ELangAll, // print headers in all languages to files
};
// Generate header for requested language
void PrintHeader(TLanguage, std::ostream&);
} // namespace spv
#endif // header

View File

@ -0,0 +1,437 @@
// Copyright (c) 2014-2018 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 <assert.h>
#include <string.h>
#include <algorithm>
#include <iostream>
#include <unordered_map>
#include <utility>
#include <fstream>
#include "jsoncpp/dist/json/json.h"
#include "jsonToSpirv.h"
namespace spv {
// The set of objects that hold all the instruction/operand
// parameterization information.
InstructionValues InstructionDesc;
// Note: There is no entry for OperandOpcode. Use InstructionDesc instead.
EnumDefinition OperandClassParams[OperandOpcode];
EnumValues SourceLanguageParams;
EnumValues ExecutionModelParams;
EnumValues AddressingParams;
EnumValues MemoryParams;
EnumValues ExecutionModeParams;
EnumValues StorageParams;
EnumValues SamplerAddressingModeParams;
EnumValues SamplerFilterModeParams;
EnumValues ImageFormatParams;
EnumValues ImageChannelOrderParams;
EnumValues ImageChannelDataTypeParams;
EnumValues ImageOperandsParams;
EnumValues FPFastMathParams;
EnumValues FPRoundingModeParams;
EnumValues LinkageTypeParams;
EnumValues DecorationParams;
EnumValues BuiltInParams;
EnumValues DimensionalityParams;
EnumValues FuncParamAttrParams;
EnumValues AccessQualifierParams;
EnumValues GroupOperationParams;
EnumValues LoopControlParams;
EnumValues SelectionControlParams;
EnumValues FunctionControlParams;
EnumValues MemorySemanticsParams;
EnumValues MemoryAccessParams;
EnumValues ScopeParams;
EnumValues KernelEnqueueFlagsParams;
EnumValues KernelProfilingInfoParams;
EnumValues CapabilityParams;
std::pair<bool, std::string> ReadFile(const std::string& path)
{
std::ifstream fstream(path, std::ios::in);
if (fstream) {
std::string contents;
fstream.seekg(0, std::ios::end);
contents.reserve((unsigned int)fstream.tellg());
fstream.seekg(0, std::ios::beg);
contents.assign((std::istreambuf_iterator<char>(fstream)),
std::istreambuf_iterator<char>());
return std::make_pair(true, contents);
}
return std::make_pair(false, "");
}
struct ClassOptionality {
OperandClass type;
bool optional;
};
// Converts the |operandKind| and |quantifier| pair used to describe operands
// in the JSON grammar to OperandClass and optionality used in this repo.
ClassOptionality ToOperandClassAndOptionality(const std::string& operandKind, const std::string& quantifier)
{
assert(quantifier.empty() || quantifier == "?" || quantifier == "*");
if (operandKind == "IdRef") {
if (quantifier.empty())
return {OperandId, false};
else if (quantifier == "?")
return {OperandId, true};
else
return {OperandVariableIds, false};
} else if (operandKind == "LiteralInteger") {
if (quantifier.empty())
return {OperandLiteralNumber, false};
if (quantifier == "?")
return {OperandOptionalLiteral, true};
else
return {OperandVariableLiterals, false};
} else if (operandKind == "LiteralString") {
if (quantifier.empty())
return {OperandLiteralString, false};
else if (quantifier == "?")
return {OperandLiteralString, true};
else {
assert(0 && "this case should not exist");
return {OperandNone, false};
}
} else if (operandKind == "PairLiteralIntegerIdRef") {
// Used by OpSwitch in the grammar
return {OperandVariableLiteralId, false};
} else if (operandKind == "PairIdRefLiteralInteger") {
// Used by OpGroupMemberDecorate in the grammar
return {OperandVariableIdLiteral, false};
} else if (operandKind == "PairIdRefIdRef") {
// Used by OpPhi in the grammar
return {OperandVariableIds, false};
} else {
OperandClass type = OperandNone;
if (operandKind == "IdMemorySemantics" || operandKind == "MemorySemantics") {
type = OperandMemorySemantics;
} else if (operandKind == "IdScope" || operandKind == "Scope") {
type = OperandScope;
} else if (operandKind == "LiteralExtInstInteger") {
type = OperandLiteralNumber;
} else if (operandKind == "LiteralSpecConstantOpInteger") {
type = OperandLiteralNumber;
} else if (operandKind == "LiteralContextDependentNumber") {
type = OperandVariableLiterals;
} else if (operandKind == "SourceLanguage") {
type = OperandSource;
} else if (operandKind == "ExecutionModel") {
type = OperandExecutionModel;
} else if (operandKind == "AddressingModel") {
type = OperandAddressing;
} else if (operandKind == "MemoryModel") {
type = OperandMemory;
} else if (operandKind == "ExecutionMode") {
type = OperandExecutionMode;
} else if (operandKind == "StorageClass") {
type = OperandStorage;
} else if (operandKind == "Dim") {
type = OperandDimensionality;
} else if (operandKind == "SamplerAddressingMode") {
type = OperandSamplerAddressingMode;
} else if (operandKind == "SamplerFilterMode") {
type = OperandSamplerFilterMode;
} else if (operandKind == "ImageFormat") {
type = OperandSamplerImageFormat;
} else if (operandKind == "ImageChannelOrder") {
type = OperandImageChannelOrder;
} else if (operandKind == "ImageChannelDataType") {
type = OperandImageChannelDataType;
} else if (operandKind == "FPRoundingMode") {
type = OperandFPRoundingMode;
} else if (operandKind == "LinkageType") {
type = OperandLinkageType;
} else if (operandKind == "AccessQualifier") {
type = OperandAccessQualifier;
} else if (operandKind == "FunctionParameterAttribute") {
type = OperandFuncParamAttr;
} else if (operandKind == "Decoration") {
type = OperandDecoration;
} else if (operandKind == "BuiltIn") {
type = OperandBuiltIn;
} else if (operandKind == "GroupOperation") {
type = OperandGroupOperation;
} else if (operandKind == "KernelEnqueueFlags") {
type = OperandKernelEnqueueFlags;
} else if (operandKind == "KernelProfilingInfo") {
type = OperandKernelProfilingInfo;
} else if (operandKind == "Capability") {
type = OperandCapability;
} else if (operandKind == "ImageOperands") {
type = OperandImageOperands;
} else if (operandKind == "FPFastMathMode") {
type = OperandFPFastMath;
} else if (operandKind == "SelectionControl") {
type = OperandSelect;
} else if (operandKind == "LoopControl") {
type = OperandLoop;
} else if (operandKind == "FunctionControl") {
type = OperandFunction;
} else if (operandKind == "MemoryAccess") {
type = OperandMemoryAccess;
}
if (type == OperandNone) {
std::cerr << "Unhandled operand kind found: " << operandKind << std::endl;
exit(1);
}
return {type, !quantifier.empty()};
}
}
bool IsTypeOrResultId(const std::string& str, bool* isType, bool* isResult)
{
if (str == "IdResultType")
return *isType = true;
if (str == "IdResult")
return *isResult = true;
return false;
}
// Given a number string, returns the position of the only bits set in the number.
// So it requires the number is a power of two.
unsigned int NumberStringToBit(const std::string& str)
{
char* parseEnd;
unsigned int value = (unsigned int)std::strtol(str.c_str(), &parseEnd, 16);
assert(!(value & (value - 1)) && "input number is not a power of 2");
unsigned int bit = 0;
for (; value; value >>= 1) ++bit;
return bit;
}
void jsonToSpirv(const std::string& jsonPath)
{
// only do this once.
static bool initialized = false;
if (initialized)
return;
initialized = true;
// Read the JSON grammar file.
bool fileReadOk = false;
std::string content;
std::tie(fileReadOk, content) = ReadFile(jsonPath);
if (!fileReadOk) {
std::cerr << "Failed to read JSON grammar file: "
<< jsonPath << std::endl;
exit(1);
}
// Decode the JSON grammar file.
Json::Reader reader;
Json::Value root;
if (!reader.parse(content, root)) {
std::cerr << "Failed to parse JSON grammar:\n"
<< reader.getFormattedErrorMessages();
exit(1);
}
// Layouts for all instructions.
// A lambda for returning capabilities from a JSON object as strings.
const auto getCaps = [](const Json::Value& object) {
EnumCaps result;
const auto& caps = object["capabilities"];
if (!caps.empty()) {
assert(caps.isArray());
for (const auto& cap : caps) {
result.emplace_back(cap.asString());
}
}
return result;
};
// A lambda for returning extensions from a JSON object as strings.
const auto getExts = [](const Json::Value& object) {
Extensions result;
const auto& exts = object["extensions"];
if (!exts.empty()) {
assert(exts.isArray());
for (const auto& ext : exts) {
result.emplace_back(ext.asString());
}
}
return result;
};
const Json::Value insts = root["instructions"];
for (const auto& inst : insts) {
const unsigned int opcode = inst["opcode"].asUInt();
const std::string name = inst["opname"].asString();
EnumCaps caps = getCaps(inst);
std::string version = inst["version"].asString();
Extensions exts = getExts(inst);
OperandParameters operands;
bool defResultId = false;
bool defTypeId = false;
for (const auto& operand : inst["operands"]) {
const std::string kind = operand["kind"].asString();
const std::string quantifier = operand.get("quantifier", "").asString();
const std::string doc = operand.get("name", "").asString();
if (!IsTypeOrResultId(kind, &defTypeId, &defResultId)) {
const auto p = ToOperandClassAndOptionality(kind, quantifier);
operands.push(p.type, doc, p.optional);
}
}
InstructionDesc.emplace_back(
std::move(EnumValue(opcode, name,
std::move(caps), std::move(version), std::move(exts),
std::move(operands))),
defTypeId, defResultId);
}
// Specific additional context-dependent operands
// Populate dest with EnumValue objects constructed from source.
const auto populateEnumValues = [&getCaps,&getExts](EnumValues* dest, const Json::Value& source, bool bitEnum) {
// A lambda for determining the numeric value to be used for a given
// enumerant in JSON form, and whether that value is a 0 in a bitfield.
auto getValue = [&bitEnum](const Json::Value& enumerant) {
std::pair<unsigned, bool> result{0u,false};
if (!bitEnum) {
result.first = enumerant["value"].asUInt();
} else {
const unsigned int bit = NumberStringToBit(enumerant["value"].asString());
if (bit == 0)
result.second = true;
else
result.first = bit - 1; // This is the *shift* amount.
}
return result;
};
for (const auto& enumerant : source["enumerants"]) {
unsigned value;
bool skip_zero_in_bitfield;
std::tie(value, skip_zero_in_bitfield) = getValue(enumerant);
if (skip_zero_in_bitfield)
continue;
EnumCaps caps(getCaps(enumerant));
std::string version = enumerant["version"].asString();
Extensions exts(getExts(enumerant));
OperandParameters params;
const Json::Value& paramsJson = enumerant["parameters"];
if (!paramsJson.empty()) { // This enumerant has parameters.
assert(paramsJson.isArray());
for (const auto& param : paramsJson) {
const std::string kind = param["kind"].asString();
const std::string doc = param.get("name", "").asString();
const auto p = ToOperandClassAndOptionality(kind, ""); // All parameters are required!
params.push(p.type, doc);
}
}
dest->emplace_back(
value, enumerant["enumerant"].asString(),
std::move(caps), std::move(version), std::move(exts), std::move(params));
}
};
const auto establishOperandClass = [&populateEnumValues](
const std::string& enumName, spv::OperandClass operandClass,
spv::EnumValues* enumValues, const Json::Value& operandEnum, const std::string& category) {
assert(category == "BitEnum" || category == "ValueEnum");
bool bitEnum = (category == "BitEnum");
populateEnumValues(enumValues, operandEnum, bitEnum);
OperandClassParams[operandClass].set(enumName, enumValues, bitEnum);
};
const Json::Value operandEnums = root["operand_kinds"];
for (const auto& operandEnum : operandEnums) {
const std::string enumName = operandEnum["kind"].asString();
const std::string category = operandEnum["category"].asString();
if (enumName == "SourceLanguage") {
establishOperandClass(enumName, OperandSource, &SourceLanguageParams, operandEnum, category);
} else if (enumName == "Decoration") {
establishOperandClass(enumName, OperandDecoration, &DecorationParams, operandEnum, category);
} else if (enumName == "ExecutionMode") {
establishOperandClass(enumName, OperandExecutionMode, &ExecutionModeParams, operandEnum, category);
} else if (enumName == "Capability") {
establishOperandClass(enumName, OperandCapability, &CapabilityParams, operandEnum, category);
} else if (enumName == "AddressingModel") {
establishOperandClass(enumName, OperandAddressing, &AddressingParams, operandEnum, category);
} else if (enumName == "MemoryModel") {
establishOperandClass(enumName, OperandMemory, &MemoryParams, operandEnum, category);
} else if (enumName == "MemorySemantics") {
establishOperandClass(enumName, OperandMemorySemantics, &MemorySemanticsParams, operandEnum, category);
} else if (enumName == "ExecutionModel") {
establishOperandClass(enumName, OperandExecutionModel, &ExecutionModelParams, operandEnum, category);
} else if (enumName == "StorageClass") {
establishOperandClass(enumName, OperandStorage, &StorageParams, operandEnum, category);
} else if (enumName == "SamplerAddressingMode") {
establishOperandClass(enumName, OperandSamplerAddressingMode, &SamplerAddressingModeParams, operandEnum, category);
} else if (enumName == "SamplerFilterMode") {
establishOperandClass(enumName, OperandSamplerFilterMode, &SamplerFilterModeParams, operandEnum, category);
} else if (enumName == "ImageFormat") {
establishOperandClass(enumName, OperandSamplerImageFormat, &ImageFormatParams, operandEnum, category);
} else if (enumName == "ImageChannelOrder") {
establishOperandClass(enumName, OperandImageChannelOrder, &ImageChannelOrderParams, operandEnum, category);
} else if (enumName == "ImageChannelDataType") {
establishOperandClass(enumName, OperandImageChannelDataType, &ImageChannelDataTypeParams, operandEnum, category);
} else if (enumName == "ImageOperands") {
establishOperandClass(enumName, OperandImageOperands, &ImageOperandsParams, operandEnum, category);
} else if (enumName == "FPFastMathMode") {
establishOperandClass(enumName, OperandFPFastMath, &FPFastMathParams, operandEnum, category);
} else if (enumName == "FPRoundingMode") {
establishOperandClass(enumName, OperandFPRoundingMode, &FPRoundingModeParams, operandEnum, category);
} else if (enumName == "LinkageType") {
establishOperandClass(enumName, OperandLinkageType, &LinkageTypeParams, operandEnum, category);
} else if (enumName == "FunctionParameterAttribute") {
establishOperandClass(enumName, OperandFuncParamAttr, &FuncParamAttrParams, operandEnum, category);
} else if (enumName == "AccessQualifier") {
establishOperandClass(enumName, OperandAccessQualifier, &AccessQualifierParams, operandEnum, category);
} else if (enumName == "BuiltIn") {
establishOperandClass(enumName, OperandBuiltIn, &BuiltInParams, operandEnum, category);
} else if (enumName == "SelectionControl") {
establishOperandClass(enumName, OperandSelect, &SelectionControlParams, operandEnum, category);
} else if (enumName == "LoopControl") {
establishOperandClass(enumName, OperandLoop, &LoopControlParams, operandEnum, category);
} else if (enumName == "FunctionControl") {
establishOperandClass(enumName, OperandFunction, &FunctionControlParams, operandEnum, category);
} else if (enumName == "Dim") {
establishOperandClass(enumName, OperandDimensionality, &DimensionalityParams, operandEnum, category);
} else if (enumName == "MemoryAccess") {
establishOperandClass(enumName, OperandMemoryAccess, &MemoryAccessParams, operandEnum, category);
} else if (enumName == "Scope") {
establishOperandClass(enumName, OperandScope, &ScopeParams, operandEnum, category);
} else if (enumName == "GroupOperation") {
establishOperandClass(enumName, OperandGroupOperation, &GroupOperationParams, operandEnum, category);
} else if (enumName == "KernelEnqueueFlags") {
establishOperandClass(enumName, OperandKernelEnqueueFlags, &KernelEnqueueFlagsParams, operandEnum, category);
} else if (enumName == "KernelProfilingInfo") {
establishOperandClass(enumName, OperandKernelProfilingInfo, &KernelProfilingInfoParams, operandEnum, category);
}
}
}
}; // end namespace spv

259
tools/buildHeaders/jsonToSpirv.h Executable file
View File

@ -0,0 +1,259 @@
// Copyright (c) 2014-2018 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 JSON_TO_SPIRV
#define JSON_TO_SPIRV
#include <string>
#include <vector>
#include <assert.h>
namespace spv {
// Reads the file in the given |path|. Returns true and the contents of the
// file on success; otherwise, returns false and an empty string.
std::pair<bool, std::string> ReadFile(const std::string& path);
// Fill in all the parameters
void jsonToSpirv(const std::string& jsonPath);
// For parameterizing operands.
enum OperandClass {
OperandNone,
OperandId,
OperandVariableIds,
OperandOptionalLiteral,
OperandOptionalLiteralString,
OperandVariableLiterals,
OperandVariableIdLiteral,
OperandVariableLiteralId,
OperandLiteralNumber,
OperandLiteralString,
OperandSource,
OperandExecutionModel,
OperandAddressing,
OperandMemory,
OperandExecutionMode,
OperandStorage,
OperandDimensionality,
OperandSamplerAddressingMode,
OperandSamplerFilterMode,
OperandSamplerImageFormat,
OperandImageChannelOrder,
OperandImageChannelDataType,
OperandImageOperands,
OperandFPFastMath,
OperandFPRoundingMode,
OperandLinkageType,
OperandAccessQualifier,
OperandFuncParamAttr,
OperandDecoration,
OperandBuiltIn,
OperandSelect,
OperandLoop,
OperandFunction,
OperandMemorySemantics,
OperandMemoryAccess,
OperandScope,
OperandGroupOperation,
OperandKernelEnqueueFlags,
OperandKernelProfilingInfo,
OperandCapability,
OperandOpcode,
OperandCount
};
// Any specific enum can have a set of capabilities that allow it:
typedef std::vector<std::string> EnumCaps;
// A set of extensions.
typedef std::vector<std::string> Extensions;
// Parameterize a set of operands with their OperandClass(es) and descriptions.
class OperandParameters {
public:
OperandParameters() { }
void push(OperandClass oc, const std::string& d, bool opt = false)
{
opClass.push_back(oc);
desc.push_back(d);
optional.push_back(opt);
}
void setOptional();
OperandClass getClass(int op) const { return opClass[op]; }
const char* getDesc(int op) const { return desc[op].c_str(); }
bool isOptional(int op) const { return optional[op]; }
int getNum() const { return (int)opClass.size(); }
protected:
std::vector<OperandClass> opClass;
std::vector<std::string> desc;
std::vector<bool> optional;
};
// An ordered sequence of EValue. We'll preserve the order found in the
// JSON file. You can look up a value by enum or by name. If there are
// duplicate values, then take the first. We assume names are unique.
// The EValue must have an unsigned |value| field and a string |name| field.
template <typename EValue>
class EnumValuesContainer {
public:
using ContainerType = std::vector<EValue>;
using iterator = typename ContainerType::iterator;
using const_iterator = typename ContainerType::const_iterator;
EnumValuesContainer() {}
// Constructs an EValue in place as a new element at the end of the
// sequence.
template <typename... Args>
void emplace_back(Args&&... args) {
values.emplace_back(std::forward<Args>(args)...);
}
// Returns the first EValue in the sequence with the given value.
// More than one EValue might have the same value.
EValue& operator[](unsigned value) {
auto where = std::find_if(begin(), end(), [&value](const EValue& e) {
return value == e.value;
});
assert((where != end()) && "Could not find enum in the enum list");
return *where;
}
// Returns the EValue with the given name. We assume uniqueness
// by name.
EValue& at(std::string name) {
auto where = std::find_if(begin(), end(), [&name](const EValue& e) {
return name == e.name;
});
assert((where != end()) && "Could not find name in the enum list");
return *where;
}
iterator begin() { return values.begin(); }
iterator end() { return values.end(); }
private:
ContainerType values;
};
// A single enumerant value. Corresponds to a row in an enumeration table
// in the spec.
class EnumValue {
public:
EnumValue() : value(0), desc(nullptr) {}
EnumValue(unsigned int the_value, const std::string& the_name, EnumCaps&& the_caps, std::string& the_version,
Extensions&& the_extensions, OperandParameters&& the_operands) :
value(the_value), name(the_name), capabilities(std::move(the_caps)), version(std::move(the_version)),
extensions(std::move(the_extensions)), operands(std::move(the_operands)), desc(nullptr) { }
// For ValueEnum, the value from the JSON file.
// For BitEnum, the index of the bit position represented by this mask.
// (That is, what you shift 1 by to get the mask.)
unsigned value;
std::string name;
EnumCaps capabilities;
std::string version;
// A feature only be enabled by certain extensions.
// An empty list means the feature does not require an extension.
// Normally, only Capability enums are enabled by extension. In turn,
// other enums and instructions are enabled by those capabilities.
Extensions extensions;
OperandParameters operands;
const char* desc;
};
using EnumValues = EnumValuesContainer<EnumValue>;
// Parameterize a set of enumerants that form an enum
class EnumDefinition {
public:
EnumDefinition() :
desc(0), bitmask(false), enumValues(nullptr) { }
void set(const std::string& enumName, EnumValues* enumValuesArg, bool mask = false)
{
codeName = enumName;
bitmask = mask;
enumValues = enumValuesArg;
}
// Returns the first EnumValue in the sequence with the given value.
// More than one EnumValue might have the same value. Only valid
// if enumValues has been populated.
EnumValue& operator[](unsigned value) {
assert(enumValues != nullptr);
return (*enumValues)[value];
}
// Returns the name of the first EnumValue with the given value.
// Assumes enumValues has been populated.
const char* getName(unsigned value) {
return (*this)[value].name.c_str();
}
using iterator = EnumValues::iterator;
iterator begin() { return enumValues->begin(); }
iterator end() { return enumValues->end(); }
std::string codeName; // name to use when declaring headers for code
const char* desc;
bool bitmask; // true if these enumerants combine into a bitmask
EnumValues* enumValues; // parameters for each individual enumerant
};
// Parameterize an instruction's logical format, including its known set of operands,
// per OperandParameters above.
class InstructionValue : public EnumValue {
public:
InstructionValue(EnumValue&& e, bool has_type, bool has_result)
: EnumValue(std::move(e)),
opDesc("TBD"),
opClass(0),
typePresent(has_type),
resultPresent(has_result) {}
bool hasResult() const { return resultPresent != 0; }
bool hasType() const { return typePresent != 0; }
const char* opDesc;
int opClass;
protected:
int typePresent : 1;
int resultPresent : 1;
};
using InstructionValues = EnumValuesContainer<InstructionValue>;
// Parameterization info for all instructions.
extern InstructionValues InstructionDesc;
// These hold definitions of the enumerants used for operands.
// This is indexed by OperandClass, but not including OperandOpcode.
extern EnumDefinition OperandClassParams[];
}; // end namespace spv
#endif // JSON_TO_SPIRV

View File

@ -0,0 +1,255 @@
/// Json-cpp amalgated forward header (http://jsoncpp.sourceforge.net/).
/// It is intended to be used with #include "json/json-forwards.h"
/// This header provides forward declaration for all JsonCpp types.
// //////////////////////////////////////////////////////////////////////
// Beginning of content of file: LICENSE
// //////////////////////////////////////////////////////////////////////
/*
The JsonCpp library's source code, including accompanying documentation,
tests and demonstration applications, are licensed under the following
conditions...
The author (Baptiste Lepilleur) explicitly disclaims copyright in all
jurisdictions which recognize such a disclaimer. In such jurisdictions,
this software is released into the Public Domain.
In jurisdictions which do not recognize Public Domain property (e.g. Germany as of
2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur, and is
released under the terms of the MIT License (see below).
In jurisdictions which recognize Public Domain property, the user of this
software may choose to accept it either as 1) Public Domain, 2) under the
conditions of the MIT License (see below), or 3) under the terms of dual
Public Domain/MIT License conditions described here, as they choose.
The MIT License is about as close to Public Domain as a license can get, and is
described in clear, concise terms at:
http://en.wikipedia.org/wiki/MIT_License
The full text of the MIT License follows:
========================================================================
Copyright (c) 2007-2010 Baptiste Lepilleur
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy,
modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is
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 Software.
THE SOFTWARE IS 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 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
========================================================================
(END LICENSE TEXT)
The MIT license is compatible with both the GPL and commercial
software, affording one all of the rights of Public Domain with the
minor nuisance of being required to keep the above copyright notice
and license text in the source code. Note also that by accepting the
Public Domain "license" you can re-license your copy using whatever
license you like.
*/
// //////////////////////////////////////////////////////////////////////
// End of content of file: LICENSE
// //////////////////////////////////////////////////////////////////////
#ifndef JSON_FORWARD_AMALGATED_H_INCLUDED
# define JSON_FORWARD_AMALGATED_H_INCLUDED
/// If defined, indicates that the source file is amalgated
/// to prevent private header inclusion.
#define JSON_IS_AMALGAMATION
// //////////////////////////////////////////////////////////////////////
// Beginning of content of file: include/json/config.h
// //////////////////////////////////////////////////////////////////////
// Copyright 2007-2010 Baptiste Lepilleur
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef JSON_CONFIG_H_INCLUDED
#define JSON_CONFIG_H_INCLUDED
/// If defined, indicates that json library is embedded in CppTL library.
//# define JSON_IN_CPPTL 1
/// If defined, indicates that json may leverage CppTL library
//# define JSON_USE_CPPTL 1
/// If defined, indicates that cpptl vector based map should be used instead of
/// std::map
/// as Value container.
//# define JSON_USE_CPPTL_SMALLMAP 1
// If non-zero, the library uses exceptions to report bad input instead of C
// assertion macros. The default is to use exceptions.
#ifndef JSON_USE_EXCEPTION
#define JSON_USE_EXCEPTION 1
#endif
/// If defined, indicates that the source file is amalgated
/// to prevent private header inclusion.
/// Remarks: it is automatically defined in the generated amalgated header.
// #define JSON_IS_AMALGAMATION
#ifdef JSON_IN_CPPTL
#include <cpptl/config.h>
#ifndef JSON_USE_CPPTL
#define JSON_USE_CPPTL 1
#endif
#endif
#ifdef JSON_IN_CPPTL
#define JSON_API CPPTL_API
#elif defined(JSON_DLL_BUILD)
#if defined(_MSC_VER)
#define JSON_API __declspec(dllexport)
#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING
#endif // if defined(_MSC_VER)
#elif defined(JSON_DLL)
#if defined(_MSC_VER)
#define JSON_API __declspec(dllimport)
#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING
#endif // if defined(_MSC_VER)
#endif // ifdef JSON_IN_CPPTL
#if !defined(JSON_API)
#define JSON_API
#endif
// If JSON_NO_INT64 is defined, then Json only support C++ "int" type for
// integer
// Storages, and 64 bits integer support is disabled.
// #define JSON_NO_INT64 1
#if defined(_MSC_VER) && _MSC_VER <= 1200 // MSVC 6
// Microsoft Visual Studio 6 only support conversion from __int64 to double
// (no conversion from unsigned __int64).
#define JSON_USE_INT64_DOUBLE_CONVERSION 1
// Disable warning 4786 for VS6 caused by STL (identifier was truncated to '255'
// characters in the debug information)
// All projects I've ever seen with VS6 were using this globally (not bothering
// with pragma push/pop).
#pragma warning(disable : 4786)
#endif // if defined(_MSC_VER) && _MSC_VER < 1200 // MSVC 6
#if defined(_MSC_VER) && _MSC_VER >= 1500 // MSVC 2008
/// Indicates that the following function is deprecated.
#define JSONCPP_DEPRECATED(message) __declspec(deprecated(message))
#elif defined(__clang__) && defined(__has_feature)
#if __has_feature(attribute_deprecated_with_message)
#define JSONCPP_DEPRECATED(message) __attribute__ ((deprecated(message)))
#endif
#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5))
#define JSONCPP_DEPRECATED(message) __attribute__ ((deprecated(message)))
#elif defined(__GNUC__) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))
#define JSONCPP_DEPRECATED(message) __attribute__((__deprecated__))
#endif
#if !defined(JSONCPP_DEPRECATED)
#define JSONCPP_DEPRECATED(message)
#endif // if !defined(JSONCPP_DEPRECATED)
namespace Json {
typedef int Int;
typedef unsigned int UInt;
#if defined(JSON_NO_INT64)
typedef int LargestInt;
typedef unsigned int LargestUInt;
#undef JSON_HAS_INT64
#else // if defined(JSON_NO_INT64)
// For Microsoft Visual use specific types as long long is not supported
#if defined(_MSC_VER) // Microsoft Visual Studio
typedef __int64 Int64;
typedef unsigned __int64 UInt64;
#else // if defined(_MSC_VER) // Other platforms, use long long
typedef long long int Int64;
typedef unsigned long long int UInt64;
#endif // if defined(_MSC_VER)
typedef Int64 LargestInt;
typedef UInt64 LargestUInt;
#define JSON_HAS_INT64
#endif // if defined(JSON_NO_INT64)
} // end namespace Json
#endif // JSON_CONFIG_H_INCLUDED
// //////////////////////////////////////////////////////////////////////
// End of content of file: include/json/config.h
// //////////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////////
// Beginning of content of file: include/json/forwards.h
// //////////////////////////////////////////////////////////////////////
// Copyright 2007-2010 Baptiste Lepilleur
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef JSON_FORWARDS_H_INCLUDED
#define JSON_FORWARDS_H_INCLUDED
#if !defined(JSON_IS_AMALGAMATION)
#include "config.h"
#endif // if !defined(JSON_IS_AMALGAMATION)
namespace Json {
// writer.h
class FastWriter;
class StyledWriter;
// reader.h
class Reader;
// features.h
class Features;
// value.h
typedef unsigned int ArrayIndex;
class StaticString;
class Path;
class PathArgument;
class Value;
class ValueIteratorBase;
class ValueIterator;
class ValueConstIterator;
} // namespace Json
#endif // JSON_FORWARDS_H_INCLUDED
// //////////////////////////////////////////////////////////////////////
// End of content of file: include/json/forwards.h
// //////////////////////////////////////////////////////////////////////
#endif //ifndef JSON_FORWARD_AMALGATED_H_INCLUDED

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

121
tools/buildHeaders/main.cpp Executable file
View File

@ -0,0 +1,121 @@
// Copyright (c) 2014-2018 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 <fstream>
#include <string>
#include <algorithm>
#include "jsonToSpirv.h"
#include "header.h"
// Command-line options
enum TOptions {
EOptionNone = 0x000,
EOptionPrintHeader = 0x008,
};
std::string jsonPath;
int Options;
spv::TLanguage Language;
void Usage()
{
printf("Usage: spirv option [file]\n"
"\n"
" -h <language> print header for given language to stdout, from one of:\n"
" C - C99 header\n"
" C++ - C++03 or greater header (also accepts C++03)\n"
" C++11 - C++11 or greater header\n"
" JSON - JSON format data\n"
" Lua - Lua module\n"
" Python - Python module (also accepts Py)\n"
" -H print header in all supported languages to files in current directory\n"
);
}
std::string tolower_s(std::string s)
{
std::transform(s.begin(), s.end(), s.begin(), ::tolower);
return s;
}
bool ProcessArguments(int argc, char* argv[])
{
argc--;
argv++;
for (; argc >= 1; argc--, argv++) {
if (argv[0][0] == '-') {
switch (argv[0][1]) {
case 'H':
Options |= EOptionPrintHeader;
Language = spv::ELangAll;
break;
case 'h': {
if (argc < 2)
return false;
Options |= EOptionPrintHeader;
const std::string language(tolower_s(argv[1]));
if (language == "c") {
Language = spv::ELangC;
} else if (language == "c++" || language == "c++03") {
Language = spv::ELangCPP;
} else if (language == "c++11") {
Language = spv::ELangCPP11;
} else if (language == "json") {
Language = spv::ELangJSON;
} else if (language == "lua") {
Language = spv::ELangLua;
} else if (language == "python" || language == "py") {
Language = spv::ELangPython;
} else
return false;
return true;
}
default:
return false;
}
} else {
jsonPath = std::string(argv[0]);
}
}
return true;
}
int main(int argc, char* argv[])
{
if (argc < 2 || ! ProcessArguments(argc, argv)) {
Usage();
return 1;
}
spv::jsonToSpirv(jsonPath);
if (Options & EOptionPrintHeader)
spv::PrintHeader(Language, std::cout);
return 0;
}