Discussion:
[RFC PATCH 00/17] Introducing SPIR-V support to clover
(too old to reply)
Pierre Moreau
2017-05-03 21:56:48 UTC
Permalink
Raw Message
Hello everyone,

I have been working on converting SPIR-V to NVIR in order to run OpenCL kernels
on Nouveau, and I would like to submit the first part of that work for review.
Pieces from the SPIR-V to NVIR conversion work will be submitted once I have
cleaned it up and this series has progressed through the reviewing process.


What’s in this Series?
----------------------

The focus of this series is to let clover accept SPIR-V binaries, either
through `clCreateProgramWithBinary()`, or through `clCreateProgramWithIL()`.
The latter function is the proper way to feed SPIR-V binaries using the OpenCL
API, however it was only introduced in OpenCL 2.1 (more on “why supporting
SPIR-V through `clCreateProgramWithBinary()` can be interesting” further down).

As several SPIR-V binaries can be linked together using the OpenCL API, I
implemented a SPIR-V linker, which is not perfect, but does the job. I tested
linking against a variable, a function, a library, and a function containing a
switch statement; switch-statements require you to keep some extra stuff around
to be properly parsed.
I also added a few “utilities” functions for retrieving and setting a word /
retrieving a string from a SPIR-V binary, and converting a SPIR-V binary to the
same endianness as the host CPU.

For validating SPIR-V binaries, I use an external tool, SPIRV-Tools [1]. It
could also be used in anv, and possibly radv if there is no validation done
already, but I haven’t looked into that.

A few modifications have been made to the pipe interface, to add a define for
the SPIR-V IR, and store the program’s byte-size along the program in
`struct pipe_compute_state`. The latter will only be needed by the consumer of
the SPIR-V, which is not part of this series. However, since clover needs to
fill that information in and I was modifying clover already, I decided to add
the new attribute in this series.


Missing
-------

* As there is no upstream version of LLVM which can produce SPIR-V out of
OpenCL code, clCreateProgramWithSource will refuse to work if the target IR
is SPIR-V, for now.

* Optimisation linking options are parsed by the SPIR-V code in clover but
are not passed along to the linker as it does not support them.


To Improve
----------

The SPIR-V binary resulting from the linking of multiple SPIR-V binaries could
be cleaned up:

* As capabilities are simply copied from all the involved binaries, you can end
up with multiple times the same capabilities in the resulting binary; this
shouldn’t have any impact though.

* Similarly, types can end up being duplicated under different IDs, which
should have no other impact than making SPIR-V validators unhappy.


Misc.
-----

Being able to feed SPIR-V binaries through `clCreateProgramWithBinary()` is not
really useful at the moment: the same can be achieved using
`clCreateProgramWithIL()`. However it will be interesting once there is an
upstream version of LLVM which can generate SPIR-V binaries, as the application
could query the binary created by `clCreateProgramWithSource()` on the first
run, and give it to `clCreateProgramWithBinary()`on later runs.

Once NIR supports pointers, and anything else that could be missing to support
OpenCL kernels, it should be possible and easy to convert input SPIR-V
binaries to NIR, for drivers that do not accept SPIR-V as IR.


I have sent patches to Mesa in the past, but never series, so the splitting of
the patches in the series could be completely wrong, and I apologise for that
in advance.
Also, I am sure I abused of macros, gotos and manual memory managements, as I
am not that comfortable at writing too much C code: I’ll try to learn from
your comments.


Thank you in advance for reviewing/commenting,
Pierre


[1]: https://github.com/KhronosGroup/SPIRV-Tools/


Pierre Moreau (17):
auxiliary: Introduce utilities for SPIR-V binaries
auxiliary: Implement a linker for SPIR-V binaries
include/pipe: Define SPIRV as an IR
include/pipe: Store the byte-size of a SPIR-V binary
include/CL: Add clCreateProgramWithIL from OpenCL 2.1
include/CL: Add new option to clGetProgramInfo from OpenCL 2.1
configure.ac: Check for SPIRV-Tools header and library
clover: Fill in the program byte-size in pipe_compute_state
clover: Add additional functions to query supported IRs
clover/spirv: Import spirv.hpp11 version 1.0 (rev 10)
clover/spirv: Add functions for parsing arguments, linking programs,
etc.
clover: Refuse to compile source code to SPIR-V
clover: Handle the case when linking SPIR-V binaries together
clover: Accept SPIR-V binaries in clCreateProgramWithBinary
clover: Implement clCreateProgramWithIL from OpenCL 2.1
clover: Add a pointer property to return ILs
clover: Handle CL_PROGRAM_IL in clGetProgramInfo

configure.ac | 16 +
include/CL/cl.h | 7 +
include/CL/cl_platform.h | 1 +
src/gallium/auxiliary/Makefile.am | 1 +
src/gallium/auxiliary/Makefile.sources | 6 +
src/gallium/auxiliary/spirv/spirv_linker.c | 1324 ++++++++++++++++++++
src/gallium/auxiliary/spirv/spirv_linker.h | 67 +
src/gallium/auxiliary/spirv/spirv_utils.c | 75 ++
src/gallium/auxiliary/spirv/spirv_utils.h | 86 ++
src/gallium/include/pipe/p_defines.h | 1 +
src/gallium/include/pipe/p_state.h | 1 +
src/gallium/state_trackers/clover/Makefile.am | 10 +-
src/gallium/state_trackers/clover/Makefile.sources | 4 +
src/gallium/state_trackers/clover/api/program.cpp | 74 +-
src/gallium/state_trackers/clover/core/device.cpp | 11 +
src/gallium/state_trackers/clover/core/device.hpp | 3 +
src/gallium/state_trackers/clover/core/kernel.cpp | 1 +
src/gallium/state_trackers/clover/core/program.cpp | 80 +-
src/gallium/state_trackers/clover/core/program.hpp | 14 +
.../state_trackers/clover/core/property.hpp | 39 +
.../state_trackers/clover/spirv/invocation.cpp | 481 +++++++
.../state_trackers/clover/spirv/invocation.hpp | 40 +
.../state_trackers/clover/spirv/spirv.hpp11 | 952 ++++++++++++++
23 files changed, 3272 insertions(+), 22 deletions(-)
create mode 100644 src/gallium/auxiliary/spirv/spirv_linker.c
create mode 100644 src/gallium/auxiliary/spirv/spirv_linker.h
create mode 100644 src/gallium/auxiliary/spirv/spirv_utils.c
create mode 100644 src/gallium/auxiliary/spirv/spirv_utils.h
create mode 100644 src/gallium/state_trackers/clover/spirv/invocation.cpp
create mode 100644 src/gallium/state_trackers/clover/spirv/invocation.hpp
create mode 100644 src/gallium/state_trackers/clover/spirv/spirv.hpp11
--
2.12.2
Pierre Moreau
2017-05-03 21:56:49 UTC
Permalink
Raw Message
Signed-off-by: Pierre Moreau <***@free.fr>
---
src/gallium/auxiliary/Makefile.am | 1 +
src/gallium/auxiliary/Makefile.sources | 4 ++
src/gallium/auxiliary/spirv/spirv_utils.c | 75 +++++++++++++++++++++++++++
src/gallium/auxiliary/spirv/spirv_utils.h | 86 +++++++++++++++++++++++++++++++
4 files changed, 166 insertions(+)
create mode 100644 src/gallium/auxiliary/spirv/spirv_utils.c
create mode 100644 src/gallium/auxiliary/spirv/spirv_utils.h

diff --git a/src/gallium/auxiliary/Makefile.am b/src/gallium/auxiliary/Makefile.am
index dc4bd4a40c..d2530a1f90 100644
--- a/src/gallium/auxiliary/Makefile.am
+++ b/src/gallium/auxiliary/Makefile.am
@@ -19,6 +19,7 @@ AM_CXXFLAGS = \
libgallium_la_SOURCES = \
$(C_SOURCES) \
$(NIR_SOURCES) \
+ $(SPIRV_SOURCES) \
$(GENERATED_SOURCES)

if HAVE_LIBDRM
diff --git a/src/gallium/auxiliary/Makefile.sources b/src/gallium/auxiliary/Makefile.sources
index dbdb3ca815..f4817742ff 100644
--- a/src/gallium/auxiliary/Makefile.sources
+++ b/src/gallium/auxiliary/Makefile.sources
@@ -312,6 +312,10 @@ NIR_SOURCES := \
nir/tgsi_to_nir.c \
nir/tgsi_to_nir.h

+SPIRV_SOURCES := \
+ spirv/spirv_utils.c \
+ spirv/spirv_utils.h
+
VL_SOURCES := \
vl/vl_bicubic_filter.c \
vl/vl_bicubic_filter.h \
diff --git a/src/gallium/auxiliary/spirv/spirv_utils.c b/src/gallium/auxiliary/spirv/spirv_utils.c
new file mode 100644
index 0000000000..a2334d6909
--- /dev/null
+++ b/src/gallium/auxiliary/spirv/spirv_utils.c
@@ -0,0 +1,75 @@
+/**************************************************************************
+ *
+ * Copyright 2017 Pierre Moreau
+ * All Rights Reserved.
+ *
+ * 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, sub license, 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 (including the
+ * next paragraph) 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 NON-INFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS AND/OR ITS SUPPLIERS 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.
+ *
+ **************************************************************************/
+
+#include "spirv_utils.h"
+
+#include "compiler/spirv/spirv.h"
+#include "util/u_math.h"
+
+spirv_word
+spirv_get_word(const char *binary, unsigned word_offset)
+{
+ return ((spirv_word *) binary)[word_offset];
+}
+
+void
+spirv_set_word(char *binary, unsigned word_offset, spirv_word word)
+{
+ ((spirv_word *) binary)[word_offset] = word;
+}
+
+const char *
+spirv_get_string(const char *binary, unsigned word_offset)
+{
+ return binary + word_offset * sizeof(spirv_word);
+}
+
+bool
+spirv_is_binary_spirv(const char *binary)
+{
+ const spirv_word first_word = spirv_get_word(binary, 0u);
+ const bool ret = (first_word == SpvMagicNumber) ||
+ (util_bswap32(first_word) == SpvMagicNumber);
+ return ret;
+}
+
+char *
+spirv_spirv_to_cpu(const char *binary, size_t length)
+{
+ spirv_word word = spirv_get_word(binary, 0u);
+ size_t i = 0;
+ char *cpu_endianness_binary = malloc(length);
+ if (word == SpvMagicNumber)
+ return memcpy(cpu_endianness_binary, binary, length);
+
+ for (i = 0; i < length; i += 4) {
+ word = spirv_get_word(binary, i);
+ spirv_set_word(cpu_endianness_binary, i, util_bswap32(word));
+ }
+
+ return cpu_endianness_binary;
+}
diff --git a/src/gallium/auxiliary/spirv/spirv_utils.h b/src/gallium/auxiliary/spirv/spirv_utils.h
new file mode 100644
index 0000000000..2db7f3b9dd
--- /dev/null
+++ b/src/gallium/auxiliary/spirv/spirv_utils.h
@@ -0,0 +1,86 @@
+/**************************************************************************
+ *
+ * Copyright 2017 Pierre Moreau
+ * All Rights Reserved.
+ *
+ * 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, sub license, 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 (including the
+ * next paragraph) 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 NON-INFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS AND/OR ITS SUPPLIERS 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.
+ *
+ **************************************************************************/
+
+#ifndef SPIRV_UTILS_H
+#define SPIRV_UTILS_H
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#if defined __cplusplus
+extern "C" {
+#endif
+
+typedef uint32_t spirv_word;
+
+/**
+ * Return the word found at a given offset in a SPIR-V binary.
+ *
+ * The binary is assumed to have the same endianness as the CPU. Be sure to
+ * call `spirv_to_cpu()` to ensure that is the case.
+ */
+spirv_word
+spirv_get_word(const char *binary, unsigned word_offset);
+
+/**
+ * Set the word found at a given offset in a SPIR-V binary.
+ *
+ * The binary is assumed to have the same endianness as the CPU. Be sure to
+ * call `spirv_to_cpu()` to ensure that is the case.
+ */
+void
+spirv_set_word(char *binary, unsigned word_offset, spirv_word word);
+
+/**
+ * Return the string found at a given offset in a SPIR-V binary.
+ *
+ * The binary is assumed to have the same endianness as the CPU. Be sure to
+ * call `spirv_to_cpu()` to ensure that is the case.
+ */
+const char *
+spirv_get_string(const char *binary, unsigned word_offset);
+
+/**
+ * Return true whether the given binary starts with the SPIR-V magic number.
+ */
+bool
+spirv_is_binary_spirv(const char *binary);
+
+/**
+ * Convert if needed the binary to the endianness of the CPU.
+ *
+ * It is the responsability of the caller to free the returned pointer.
+ */
+char *
+spirv_spirv_to_cpu(const char *binary, size_t length);
+
+#if defined __cplusplus
+}
+#endif
+
+#endif /* SPIRV_UTILS_H */
--
2.12.2
Pierre Moreau
2017-05-03 21:56:50 UTC
Permalink
Raw Message
Signed-off-by: Pierre Moreau <***@free.fr>
---
src/gallium/auxiliary/Makefile.sources | 4 +-
src/gallium/auxiliary/spirv/spirv_linker.c | 1324 ++++++++++++++++++++++++++++
src/gallium/auxiliary/spirv/spirv_linker.h | 67 ++
3 files changed, 1394 insertions(+), 1 deletion(-)
create mode 100644 src/gallium/auxiliary/spirv/spirv_linker.c
create mode 100644 src/gallium/auxiliary/spirv/spirv_linker.h

diff --git a/src/gallium/auxiliary/Makefile.sources b/src/gallium/auxiliary/Makefile.sources
index f4817742ff..91aac49dfb 100644
--- a/src/gallium/auxiliary/Makefile.sources
+++ b/src/gallium/auxiliary/Makefile.sources
@@ -314,7 +314,9 @@ NIR_SOURCES := \

SPIRV_SOURCES := \
spirv/spirv_utils.c \
- spirv/spirv_utils.h
+ spirv/spirv_utils.h \
+ spirv/spirv_linker.c \
+ spirv/spirv_linker.h

VL_SOURCES := \
vl/vl_bicubic_filter.c \
diff --git a/src/gallium/auxiliary/spirv/spirv_linker.c b/src/gallium/auxiliary/spirv/spirv_linker.c
new file mode 100644
index 0000000000..9d060be0cc
--- /dev/null
+++ b/src/gallium/auxiliary/spirv/spirv_linker.c
@@ -0,0 +1,1324 @@
+/**************************************************************************
+ *
+ * Copyright 2017 Pierre Moreau
+ * All Rights Reserved.
+ *
+ * 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, sub license, 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 (including the
+ * next paragraph) 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 NON-INFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS AND/OR ITS SUPPLIERS 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.
+ *
+ **************************************************************************/
+
+#include "spirv_linker.h"
+#include "spirv_utils.h"
+
+#include "compiler/spirv/spirv.h"
+#include "util/u_debug.h"
+#include "util/u_hash_table.h"
+#include "util/u_pointer.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#define PTR_TO_UINT(x) ((unsigned)pointer_to_uintptr(x))
+#define UINT_TO_PTR(x) (uintptr_to_pointer((uintptr_t)(x)))
+
+/**
+ * Extracts the opcode and the number of words making up this instruction.
+ *
+ * @param binary binary to extract the information from
+ * @param word_id index of the word to extract
+ * @param word_count if not null, will be set to the number of words making up
+ * the instruction, otherwise will be left untouched
+ * @return the opcode
+ */
+static SpvOp
+spirv_get_opcode(const char *binary, size_t word_offset, unsigned *word_count)
+{
+ const unsigned desc_word = spirv_get_word(binary, word_offset);
+ if (word_count)
+ *word_count = desc_word >> SpvWordCountShift;
+ return (SpvOp) (desc_word & SpvOpCodeMask);
+}
+
+static unsigned
+spirv_spvid_hash(void *id)
+{
+ return PTR_TO_UINT(id);
+}
+
+static int
+spirv_spvid_compare(void *id1, void *id2)
+{
+ return PTR_TO_UINT(id1) != PTR_TO_UINT(id2);
+}
+
+/**
+ * Adds a specified base ID to the ID found at a specified position in the
+ * binary.
+ */
+static void
+spirv_bump_id(char *binary, unsigned word_offset, void *base_id)
+{
+ SpvId old_id = spirv_get_word(binary, word_offset);
+ spirv_set_word(binary, word_offset, PTR_TO_UINT(base_id) + old_id);
+}
+
+/**
+ * Replaces an ID with another one, if found in the link table.
+ */
+static void
+spirv_link_ids(char *binary, unsigned word_offset, void *link_table)
+{
+ SpvId old_id = spirv_get_word(binary, word_offset);
+ void *new_id_ptr = util_hash_table_get((struct util_hash_table *) link_table,
+ UINT_TO_PTR(old_id));
+ SpvId new_id = PTR_TO_UINT(new_id_ptr);
+ if (new_id_ptr != NULL)
+ spirv_set_word(binary, word_offset, new_id);
+}
+
+/**
+ * Associates the given variable to its width, if found.
+ */
+static void
+spirv_register_variable(char *binary, unsigned type_offset,
+ unsigned variable_offset, struct util_hash_table *types,
+ struct util_hash_table *variables)
+{
+ SpvId type_id = spirv_get_word(binary, type_offset);
+ SpvId var_id = spirv_get_word(binary, variable_offset);
+ void *width_ptr = util_hash_table_get(types, UINT_TO_PTR(type_id));
+ if (width_ptr != NULL)
+ util_hash_table_set(variables, UINT_TO_PTR(var_id), width_ptr);
+}
+
+/**
+ * Applies the given function onto the specified IDs.
+ */
+static void
+spirv_transform_ids(void (*transform_id)(char *, unsigned, void *),
+ char *binary, unsigned offset,
+ void *data, int ids_count, ...)
+{
+ va_list ids;
+ va_start(ids, ids_count);
+ for (int i = 0; i < ids_count; ++i)
+ transform_id(binary, offset + va_arg(ids, SpvId), data);
+ va_end(ids);
+}
+
+/**
+ * Applies the given function to all IDs found in the given binary.
+ */
+static int
+spirv_transform_binary(char *binary, unsigned binary_word_count,
+ void (*transform_id)(char *, unsigned, void *),
+ void *data)
+{
+ unsigned i = 5u; // Skip header
+ unsigned j = 0u, k = 0u;
+ int ret = 0;
+ unsigned opcode = 0u, insn_word_count = 0u;
+
+ struct util_hash_table *int_types = util_hash_table_create(&spirv_spvid_hash,
+ &spirv_spvid_compare);
+ struct util_hash_table *int_variables = util_hash_table_create(&spirv_spvid_hash,
+ &spirv_spvid_compare);
+
+ while (i < binary_word_count) {
+ opcode = spirv_get_opcode(binary, i, &insn_word_count);
+
+ switch (opcode) {
+ case SpvOpNop:
+ break;
+ case SpvOpUndef:
+ for (j = 1u; j < insn_word_count; ++j)
+ transform_id(binary, i + j, data);
+ break;
+ case SpvOpSourceContinued:
+ break;
+ case SpvOpSource:
+ if (insn_word_count > 3u)
+ transform_id(binary, i + 3u, data);
+ break;
+ case SpvOpSourceExtension:
+ break;
+ case SpvOpName:
+ case SpvOpMemberName:
+ case SpvOpString:
+ case SpvOpLine:
+ transform_id(binary, i + 1u, data);
+ break;
+ case SpvOpNoLine:
+ break;
+ case SpvOpDecorate:
+ case SpvOpMemberDecorate:
+ case SpvOpDecorationGroup:
+ transform_id(binary, i + 1u, data);
+ break;
+ case SpvOpGroupDecorate:
+ for (j = 1u; j < insn_word_count; ++j)
+ transform_id(binary, i + j, data);
+ break;
+ case SpvOpGroupMemberDecorate:
+ transform_id(binary, i + 1u, data);
+ for (j = 2u; j < insn_word_count; j += 2u)
+ transform_id(binary, i + j, data);
+ break;
+ case SpvOpExtension:
+ break;
+ case SpvOpExtInstImport:
+ transform_id(binary, i + 1u, data);
+ break;
+ case SpvOpExtInst:
+ spirv_transform_ids(transform_id, binary, i, data, 3, 1u, 2u, 3u);
+ for (j = 5u; j < insn_word_count; ++j)
+ transform_id(binary, i + j, data);
+ break;
+ case SpvOpMemoryModel:
+ break;
+ case SpvOpEntryPoint:
+ transform_id(binary, i + 2u, data);
+ j = 3u;
+ k = 0u;
+ /* We have to compute first how long the string is */
+ while (j < insn_word_count &&
+ binary[(i + j) * sizeof(spirv_word) + k] != '\0') {
+ ++k;
+ if (k >= sizeof(spirv_word)) {
+ k = 0u;
+ ++j;
+ }
+ }
+ if (j < insn_word_count)
+ ++k;
+ if (k > 0u) {
+ k = 0u;
+ ++j;
+ }
+ for (; j < insn_word_count; ++j)
+ transform_id(binary, i + j, data);
+ break;
+ case SpvOpExecutionMode:
+ transform_id(binary, i + 1u, data);
+ break;
+ case SpvOpCapability:
+ break;
+ case SpvOpTypeVoid:
+ case SpvOpTypeBool:
+ case SpvOpTypeInt:
+ case SpvOpTypeFloat:
+ transform_id(binary, i + 1u, data);
+ if (opcode == SpvOpTypeInt) {
+ SpvId new_id = spirv_get_word(binary, i + 1u);
+ uint32_t width = spirv_get_word(binary, i + 2u);
+ width = width / 32u + (width % 32u != 0u);
+ util_hash_table_set(int_types, UINT_TO_PTR(new_id),
+ UINT_TO_PTR(width));
+ }
+ break;
+ case SpvOpTypeVector:
+ case SpvOpTypeMatrix:
+ case SpvOpTypeImage:
+ for (j = 1u; j < 3u; ++j)
+ transform_id(binary, i + j, data);
+ break;
+ case SpvOpTypeSampler:
+ transform_id(binary, i + 1u, data);
+ break;
+ case SpvOpTypeSampledImage:
+ case SpvOpTypeArray:
+ case SpvOpTypeRuntimeArray:
+ case SpvOpTypeStruct:
+ for (j = 1u; j < insn_word_count; ++j)
+ transform_id(binary, i + j, data);
+ break;
+ case SpvOpTypeOpaque:
+ transform_id(binary, i + 1u, data);
+ break;
+ case SpvOpTypePointer:
+ spirv_transform_ids(transform_id, binary, i, data, 2, 1u, 3u);
+ break;
+ case SpvOpTypeFunction:
+ for (j = 1u; j < insn_word_count; ++j)
+ transform_id(binary, i + j, data);
+ break;
+ case SpvOpTypeEvent:
+ case SpvOpTypeDeviceEvent:
+ case SpvOpTypeReserveId:
+ case SpvOpTypeQueue:
+ case SpvOpTypePipe:
+ case SpvOpTypeForwardPointer:
+ transform_id(binary, i + 1u, data);
+ break;
+ case SpvOpConstantTrue:
+ case SpvOpConstantFalse:
+ case SpvOpConstant:
+ for (j = 1u; j < 3u; ++j)
+ transform_id(binary, i + j, data);
+ if (opcode == SpvOpConstant)
+ spirv_register_variable(binary, i + 1u, i + 2u, int_types,
+ int_variables);
+ break;
+ case SpvOpConstantComposite:
+ for (j = 1u; j < insn_word_count; ++j)
+ transform_id(binary, i + j, data);
+ break;
+ case SpvOpConstantSampler:
+ case SpvOpConstantNull:
+ case SpvOpSpecConstantTrue:
+ case SpvOpSpecConstantFalse:
+ case SpvOpSpecConstant:
+ for (j = 1u; j < 3u; ++j)
+ transform_id(binary, i + j, data);
+ if (opcode == SpvOpSpecConstant)
+ spirv_register_variable(binary, i + 1u, i + 2u, int_types,
+ int_variables);
+ break;
+ case SpvOpSpecConstantComposite:
+ for (j = 1u; j < insn_word_count; ++j)
+ transform_id(binary, i + j, data);
+ break;
+ case SpvOpSpecConstantOp:
+ for (j = 1u; j < 3u; ++j)
+ transform_id(binary, i + j, data);
+ for (j = 4u; j < insn_word_count; ++j)
+ transform_id(binary, i + j, data);
+ spirv_register_variable(binary, i + 1u, i + 2u, int_types,
+ int_variables);
+ break;
+ case SpvOpVariable:
+ for (j = 1u; j < 3u; ++j)
+ transform_id(binary, i + j, data);
+ if (insn_word_count == 5u)
+ transform_id(binary, i + 4u, data);
+ spirv_register_variable(binary, i + 1u, i + 2u, int_types,
+ int_variables);
+ break;
+ case SpvOpImageTexelPointer:
+ for (j = 1u; j < insn_word_count; ++j)
+ transform_id(binary, i + j, data);
+ break;
+ case SpvOpLoad:
+ for (j = 1u; j < 4u; ++j)
+ transform_id(binary, i + j, data);
+ spirv_register_variable(binary, i + 1u, i + 2u, int_types,
+ int_variables);
+ break;
+ case SpvOpStore:
+ case SpvOpCopyMemory:
+ for (j = 1u; j < 3u; ++j)
+ transform_id(binary, i + j, data);
+ break;
+ case SpvOpCopyMemorySized:
+ for (j = 1u; j < 4u; ++j)
+ transform_id(binary, i + j, data);
+ break;
+ case SpvOpAccessChain:
+ case SpvOpInBoundsAccessChain:
+ case SpvOpPtrAccessChain:
+ for (j = 1u; j < insn_word_count; ++j)
+ transform_id(binary, i + j, data);
+ break;
+ case SpvOpArrayLength:
+ case SpvOpGenericPtrMemSemantics:
+ for (j = 1u; j < 4u; ++j)
+ transform_id(binary, i + j, data);
+ spirv_register_variable(binary, i + 1u, i + 2u, int_types,
+ int_variables);
+ break;
+ case SpvOpInBoundsPtrAccessChain:
+ for (j = 1u; j < insn_word_count; ++j)
+ transform_id(binary, i + j, data);
+ break;
+ case SpvOpFunction:
+ spirv_transform_ids(transform_id, binary, i, data,
+ 3, 1u, 2u, 4u);
+ break;
+ case SpvOpFunctionParameter:
+ for (j = 1u; j < insn_word_count; ++j)
+ transform_id(binary, i + j, data);
+ spirv_register_variable(binary, i + 1u, i + 2u, int_types,
+ int_variables);
+ break;
+ case SpvOpFunctionEnd:
+ break;
+ case SpvOpFunctionCall:
+ for (j = 1u; j < insn_word_count; ++j)
+ transform_id(binary, i + j, data);
+ spirv_register_variable(binary, i + 1u, i + 2u, int_types,
+ int_variables);
+ break;
+ case SpvOpSampledImage:
+ for (j = 1u; j < insn_word_count; ++j)
+ transform_id(binary, i + j, data);
+ break;
+ case SpvOpImageSampleImplicitLod:
+ case SpvOpImageSampleExplicitLod:
+ case SpvOpImageSampleProjImplicitLod:
+ case SpvOpImageSampleProjExplicitLod:
+ case SpvOpImageFetch:
+ case SpvOpImageRead:
+ for (j = 1u; j < 5u; ++j)
+ transform_id(binary, i + j, data);
+ for (j = 6u; j < insn_word_count; ++j)
+ transform_id(binary, i + j, data);
+ if (opcode == SpvOpImageRead)
+ spirv_register_variable(binary, i + 1u, i + 2u, int_types,
+ int_variables);
+ break;
+ case SpvOpImageSampleDrefImplicitLod:
+ case SpvOpImageSampleDrefExplicitLod:
+ case SpvOpImageSampleProjDrefImplicitLod:
+ case SpvOpImageSampleProjDrefExplicitLod:
+ case SpvOpImageGather:
+ case SpvOpImageDrefGather:
+ for (j = 1u; j < 6u; ++j)
+ transform_id(binary, i + j, data);
+ for (j = 7u; j < insn_word_count; ++j)
+ transform_id(binary, i + j, data);
+ if (opcode != SpvOpImageGather)
+ spirv_register_variable(binary, i + 1u, i + 2u, int_types,
+ int_variables);
+ break;
+ case SpvOpImageWrite:
+ for (j = 1u; j < 4u; ++j)
+ transform_id(binary, i + j, data);
+ for (j = 5u; j < insn_word_count; ++j)
+ transform_id(binary, i + j, data);
+ break;
+ case SpvOpImage:
+ case SpvOpImageQueryFormat:
+ case SpvOpImageQueryOrder:
+ case SpvOpImageQuerySizeLod:
+ case SpvOpImageQuerySize:
+ case SpvOpImageQueryLod:
+ case SpvOpImageQueryLevels:
+ case SpvOpImageQuerySamples:
+ for (j = 1u; j < insn_word_count; ++j)
+ transform_id(binary, i + j, data);
+ if (opcode != SpvOpImage && opcode != SpvOpImageQueryLod)
+ spirv_register_variable(binary, i + 1u, i + 2u, int_types,
+ int_variables);
+ break;
+ case SpvOpImageSparseSampleImplicitLod:
+ case SpvOpImageSparseSampleExplicitLod:
+ case SpvOpImageSparseFetch:
+ case SpvOpImageSparseRead:
+ for (j = 1u; j < 5u; ++j)
+ transform_id(binary, i + j, data);
+ for (j = 6u; j < insn_word_count; ++j)
+ transform_id(binary, i + j, data);
+ break;
+ case SpvOpImageSparseSampleDrefImplicitLod:
+ case SpvOpImageSparseSampleDrefExplicitLod:
+ case SpvOpImageSparseGather:
+ case SpvOpImageSparseDrefGather:
+ for (j = 1u; j < 6u; ++j)
+ transform_id(binary, i + j, data);
+ for (j = 7u; j < insn_word_count; ++j)
+ transform_id(binary, i + j, data);
+ break;
+ case SpvOpImageSparseSampleProjImplicitLod:
+ case SpvOpImageSparseSampleProjExplicitLod:
+ case SpvOpImageSparseSampleProjDrefImplicitLod:
+ case SpvOpImageSparseSampleProjDrefExplicitLod:
+ assert(false);
+ return -1;
+ case SpvOpImageSparseTexelsResident:
+ for (j = 1u; j < 4u; ++j)
+ transform_id(binary, i + j, data);
+ break;
+ case SpvOpConvertFToU:
+ case SpvOpConvertFToS:
+ case SpvOpUConvert:
+ case SpvOpSConvert:
+ case SpvOpConvertPtrToU:
+ case SpvOpSatConvertSToU:
+ case SpvOpSatConvertUToS:
+ case SpvOpBitcast:
+ for (j = 1u; j < 4u; ++j)
+ transform_id(binary, i + j, data);
+ spirv_register_variable(binary, i + 1u, i + 2u, int_types,
+ int_variables);
+ break;
+ case SpvOpConvertSToF:
+ case SpvOpConvertUToF:
+ case SpvOpFConvert:
+ case SpvOpQuantizeToF16:
+ case SpvOpConvertUToPtr:
+ case SpvOpPtrCastToGeneric:
+ case SpvOpGenericCastToPtr:
+ case SpvOpGenericCastToPtrExplicit:
+ for (j = 1u; j < 4u; ++j)
+ transform_id(binary, i + j, data);
+ break;
+ case SpvOpVectorExtractDynamic:
+ case SpvOpVectorInsertDynamic:
+ for (j = 1u; j < insn_word_count; ++j)
+ transform_id(binary, i + j, data);
+ if (opcode == SpvOpVectorExtractDynamic)
+ spirv_register_variable(binary, i + 1u, i + 2u, int_types,
+ int_variables);
+ break;
+ case SpvOpVectorShuffle:
+ case SpvOpCompositeInsert:
+ for (j = 1u; j < 5u; ++j)
+ transform_id(binary, i + j, data);
+ break;
+ case SpvOpCompositeConstruct:
+ for (j = 1u; j < insn_word_count; ++j)
+ transform_id(binary, i + j, data);
+ break;
+ case SpvOpCompositeExtract:
+ for (j = 1u; j < 4u; ++j)
+ transform_id(binary, i + j, data);
+ spirv_register_variable(binary, i + 1u, i + 2u, int_types,
+ int_variables);
+ break;
+ case SpvOpCopyObject:
+ case SpvOpTranspose:
+ case SpvOpSNegate:
+ case SpvOpFNegate:
+ case SpvOpIAdd:
+ case SpvOpISub:
+ case SpvOpIMul:
+ case SpvOpUDiv:
+ case SpvOpSDiv:
+ case SpvOpUMod:
+ case SpvOpSRem:
+ case SpvOpSMod:
+ case SpvOpShiftRightLogical:
+ case SpvOpShiftRightArithmetic:
+ case SpvOpShiftLeftLogical:
+ case SpvOpBitwiseOr:
+ case SpvOpBitwiseXor:
+ case SpvOpBitwiseAnd:
+ case SpvOpNot:
+ case SpvOpBitFieldInsert:
+ case SpvOpBitFieldSExtract:
+ case SpvOpBitFieldUExtract:
+ case SpvOpSelect:
+ for (j = 1u; j < insn_word_count; ++j)
+ transform_id(binary, i + j, data);
+ if (opcode != SpvOpTranspose && opcode != SpvOpFNegate)
+ spirv_register_variable(binary, i + 1u, i + 2u, int_types,
+ int_variables);
+ break;
+ case SpvOpFAdd:
+ case SpvOpFSub:
+ case SpvOpFMul:
+ case SpvOpFDiv:
+ case SpvOpFRem:
+ case SpvOpFMod:
+ case SpvOpVectorTimesScalar:
+ case SpvOpMatrixTimesScalar:
+ case SpvOpVectorTimesMatrix:
+ case SpvOpMatrixTimesVector:
+ case SpvOpMatrixTimesMatrix:
+ case SpvOpOuterProduct:
+ case SpvOpDot:
+ case SpvOpIAddCarry:
+ case SpvOpISubBorrow:
+ case SpvOpUMulExtended:
+ case SpvOpSMulExtended:
+ for (j = 1u; j < insn_word_count; ++j)
+ transform_id(binary, i + j, data);
+ break;
+ case SpvOpBitReverse:
+ case SpvOpBitCount:
+ case SpvOpAny:
+ case SpvOpAll:
+ case SpvOpIsNan:
+ case SpvOpIsInf:
+ case SpvOpIsFinite:
+ case SpvOpIsNormal:
+ case SpvOpSignBitSet:
+ case SpvOpPhi:
+ for (j = 1u; j < insn_word_count; ++j)
+ transform_id(binary, i + j, data);
+ if (opcode == SpvOpBitReverse || opcode == SpvOpBitCount ||
+ opcode == SpvOpPhi)
+ spirv_register_variable(binary, i + 1u, i + 2u, int_types,
+ int_variables);
+ break;
+ case SpvOpLessOrGreater:
+ case SpvOpOrdered:
+ case SpvOpUnordered:
+ case SpvOpLogicalEqual:
+ case SpvOpLogicalNotEqual:
+ case SpvOpLogicalOr:
+ case SpvOpLogicalAnd:
+ case SpvOpLogicalNot:
+ case SpvOpIEqual:
+ case SpvOpINotEqual:
+ case SpvOpUGreaterThan:
+ case SpvOpSGreaterThan:
+ case SpvOpUGreaterThanEqual:
+ case SpvOpSGreaterThanEqual:
+ case SpvOpULessThan:
+ case SpvOpSLessThan:
+ case SpvOpULessThanEqual:
+ case SpvOpSLessThanEqual:
+ case SpvOpFOrdEqual:
+ case SpvOpFUnordEqual:
+ case SpvOpFOrdNotEqual:
+ case SpvOpFUnordNotEqual:
+ case SpvOpFOrdLessThan:
+ case SpvOpFUnordLessThan:
+ case SpvOpFOrdGreaterThan:
+ case SpvOpFUnordGreaterThan:
+ case SpvOpFOrdLessThanEqual:
+ case SpvOpFUnordLessThanEqual:
+ case SpvOpFOrdGreaterThanEqual:
+ case SpvOpFUnordGreaterThanEqual:
+ case SpvOpDPdx:
+ case SpvOpDPdy:
+ case SpvOpFwidth:
+ case SpvOpDPdxFine:
+ case SpvOpDPdyFine:
+ case SpvOpFwidthFine:
+ case SpvOpDPdxCoarse:
+ case SpvOpDPdyCoarse:
+ case SpvOpFwidthCoarse:
+ for (j = 1u; j < insn_word_count; ++j)
+ transform_id(binary, i + j, data);
+ break;
+ case SpvOpLoopMerge:
+ for (j = 1u; j < 3u; ++j)
+ transform_id(binary, i + j, data);
+ break;
+ case SpvOpSelectionMerge:
+ case SpvOpLabel:
+ case SpvOpBranch:
+ transform_id(binary, i + 1u, data);
+ break;
+ case SpvOpBranchConditional:
+ spirv_transform_ids(transform_id, binary, i, data, 3, 1u, 2u, 3u);
+ break;
+ case SpvOpSwitch:
+ for (j = 1u; j < 4u; ++j)
+ transform_id(binary, i + j, data);
+ {
+ SpvId selector_id = spirv_get_word(binary, i + 1u);
+ void *width_ptr = util_hash_table_get(int_variables,
+ UINT_TO_PTR(selector_id));
+ assert(width_ptr);
+ unsigned width = PTR_TO_UINT(width_ptr);
+ for (j = 3u + width; j < insn_word_count; j += 1u + width)
+ transform_id(binary, i + j, data);
+ }
+ break;
+ case SpvOpKill:
+ case SpvOpReturn:
+ case SpvOpUnreachable:
+ break;
+ case SpvOpReturnValue:
+ case SpvOpLifetimeStart:
+ case SpvOpLifetimeStop:
+ transform_id(binary, i + 1u, data);
+ break;
+ case SpvOpAtomicLoad:
+ case SpvOpAtomicStore:
+ case SpvOpAtomicExchange:
+ case SpvOpAtomicCompareExchange:
+ case SpvOpAtomicCompareExchangeWeak:
+ case SpvOpAtomicIIncrement:
+ case SpvOpAtomicIDecrement:
+ case SpvOpAtomicIAdd:
+ case SpvOpAtomicISub:
+ case SpvOpAtomicSMin:
+ case SpvOpAtomicUMin:
+ case SpvOpAtomicSMax:
+ case SpvOpAtomicUMax:
+ case SpvOpAtomicAnd:
+ case SpvOpAtomicOr:
+ case SpvOpAtomicXor:
+ case SpvOpAtomicFlagTestAndSet:
+ case SpvOpAtomicFlagClear:
+ for (j = 1u; j < insn_word_count; ++j)
+ transform_id(binary, i + j, data);
+ if (opcode != SpvOpAtomicStore &&
+ opcode != SpvOpAtomicFlagTestAndSet &&
+ opcode != SpvOpAtomicFlagClear)
+ spirv_register_variable(binary, i + 1u, i + 2u, int_types,
+ int_variables);
+ break;
+ case SpvOpEmitVertex:
+ case SpvOpEndPrimitive:
+ break;
+ case SpvOpEmitStreamVertex:
+ case SpvOpEndStreamPrimitive:
+ transform_id(binary, i + 1u, data);
+ break;
+ case SpvOpControlBarrier:
+ case SpvOpMemoryBarrier:
+ case SpvOpGroupAsyncCopy:
+ case SpvOpGroupWaitEvents:
+ case SpvOpGroupAll:
+ case SpvOpGroupAny:
+ for (j = 1u; j < insn_word_count; ++j)
+ transform_id(binary, i + j, data);
+ break;
+ case SpvOpGroupBroadcast:
+ case SpvOpGroupIAdd:
+ case SpvOpGroupUMin:
+ case SpvOpGroupSMin:
+ case SpvOpGroupUMax:
+ case SpvOpGroupSMax:
+ for (j = 1u; j < insn_word_count; ++j)
+ transform_id(binary, i + j, data);
+ spirv_register_variable(binary, i + 1u, i + 2u, int_types,
+ int_variables);
+ break;
+ case SpvOpGroupFAdd:
+ case SpvOpGroupFMin:
+ case SpvOpGroupFMax:
+ case SpvOpSubgroupBallotKHR:
+ for (j = 1u; j < insn_word_count; ++j)
+ transform_id(binary, i + j, data);
+ break;
+ case SpvOpSubgroupFirstInvocationKHR:
+ case SpvOpEnqueueMarker:
+ case SpvOpEnqueueKernel:
+ case SpvOpGetKernelNDrangeSubGroupCount:
+ case SpvOpGetKernelNDrangeMaxSubGroupSize:
+ case SpvOpGetKernelWorkGroupSize:
+ case SpvOpGetKernelPreferredWorkGroupSizeMultiple:
+ for (j = 1u; j < insn_word_count; ++j)
+ transform_id(binary, i + j, data);
+ spirv_register_variable(binary, i + 1u, i + 2u, int_types,
+ int_variables);
+ break;
+ case SpvOpRetainEvent:
+ case SpvOpReleaseEvent:
+ case SpvOpCreateUserEvent:
+ case SpvOpIsValidEvent:
+ case SpvOpSetUserEventStatus:
+ case SpvOpCaptureEventProfilingInfo:
+ case SpvOpGetDefaultQueue:
+ case SpvOpBuildNDRange:
+ for (j = 1u; j < insn_word_count; ++j)
+ transform_id(binary, i + j, data);
+ break;
+ case SpvOpReadPipe:
+ case SpvOpWritePipe:
+ case SpvOpReservedReadPipe:
+ case SpvOpReservedWritePipe:
+ case SpvOpGetNumPipePackets:
+ case SpvOpGetMaxPipePackets:
+ for (j = 1u; j < insn_word_count; ++j)
+ transform_id(binary, i + j, data);
+ spirv_register_variable(binary, i + 1u, i + 2u, int_types,
+ int_variables);
+ break;
+ case SpvOpReserveReadPipePackets:
+ case SpvOpReserveWritePipePackets:
+ case SpvOpCommitReadPipe:
+ case SpvOpCommitWritePipe:
+ case SpvOpIsValidReserveId:
+ case SpvOpGroupReserveReadPipePackets:
+ case SpvOpGroupReserveWritePipePackets:
+ case SpvOpGroupCommitReadPipe:
+ case SpvOpGroupCommitWritePipe:
+ for (j = 1u; j < insn_word_count; ++j)
+ transform_id(binary, i + j, data);
+ break;
+ case SpvOpMax: //FALLTHROUGH
+ default:
+ assert(false);
+ return -1;
+ }
+
+ i += insn_word_count;
+ }
+
+ util_hash_table_destroy(int_types);
+ util_hash_table_destroy(int_variables);
+
+ return ret;
+}
+
+struct spirv_link_data {
+ struct util_hash_table *exports;
+ struct util_hash_table *link_table;
+ const char *linkage_uid;
+ SpvId export_id;
+ char **msg;
+ unsigned *msg_length;
+};
+
+static const char *
+addressing_model_to_string(SpvAddressingModel model)
+{
+#define CVT_ADDR_MODEL(v) case SpvAddressingModel##v: return #v
+ switch (model) {
+ CVT_ADDR_MODEL(Logical);
+ CVT_ADDR_MODEL(Physical32);
+ CVT_ADDR_MODEL(Physical64);
+ default:
+ return "Unsupported";
+ }
+#undef CVT_ADDR_MODEL
+}
+
+static const char *
+memory_model_to_string(SpvAddressingModel model)
+{
+#define CVT_MEMORY_MODEL(v) case SpvMemoryModel##v: return #v
+ switch (model) {
+ CVT_MEMORY_MODEL(Simple);
+ CVT_MEMORY_MODEL(GLSL450);
+ CVT_MEMORY_MODEL(OpenCL);
+ default:
+ return "Unsupported";
+ }
+#undef CVT_MEMORY_MODEL
+}
+
+/* Returns PIPE_ERROR when item found */
+static enum pipe_error
+find_export(void *export_id, void *linkage_uid, void *data)
+{
+ struct spirv_link_data *link_data = (struct spirv_link_data *) data;
+ if (!strcmp(link_data->linkage_uid, (const char *) linkage_uid)) {
+ link_data->export_id = PTR_TO_UINT(export_id);
+ return PIPE_ERROR;
+ }
+ return PIPE_OK;
+}
+
+static enum pipe_error
+generate_link_table(void *import_id, void *linkage_uid, void *data)
+{
+ struct spirv_link_data *link_data = (struct spirv_link_data *) data;
+ link_data->export_id = 0u;
+ link_data->linkage_uid = (const char *) linkage_uid;
+ util_hash_table_foreach(link_data->exports, &find_export, data);
+ if (link_data->export_id == 0u) {
+ *link_data->msg_length = snprintf(NULL, 0,
+ "SPIR-V linker: Missing symbol \"%s\"\n",
+ link_data->linkage_uid) + 1;
+ *link_data->msg = (char *) malloc(*link_data->msg_length);
+ snprintf(*link_data->msg, *link_data->msg_length,
+ "SPIR-V linker: Missing symbol \"%s\"\n",
+ link_data->linkage_uid);
+ return PIPE_ERROR;
+ }
+ util_hash_table_set(link_data->link_table, import_id,
+ UINT_TO_PTR(link_data->export_id));
+ return PIPE_OK;
+}
+
+static bool
+spirv_is_capability_op(SpvId opcode)
+{
+ return opcode == SpvOpCapability;
+}
+
+static bool
+spirv_is_extension_op(SpvId opcode)
+{
+ return opcode == SpvOpExtension;
+}
+
+static bool
+spirv_is_ext_inst_import_op(SpvId opcode)
+{
+ return opcode == SpvOpExtInstImport;
+}
+
+static bool
+spirv_is_entry_point_op(SpvId opcode)
+{
+ return opcode == SpvOpEntryPoint;
+}
+
+static bool
+spirv_is_execution_mode_op(SpvId opcode)
+{
+ return opcode == SpvOpExecutionMode;
+}
+
+static bool
+spirv_is_debug_a_op(SpvId opcode)
+{
+ switch (opcode) {
+ case SpvOpString:
+ case SpvOpSourceExtension:
+ case SpvOpSource:
+ case SpvOpSourceContinued:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool
+spirv_is_debug_b_op(SpvId opcode)
+{
+ return opcode == SpvOpName || opcode == SpvOpMemberName;
+}
+
+static bool
+spirv_is_type_constant_op(SpvId opcode)
+{
+ switch (opcode) {
+ case SpvOpLine:
+ case SpvOpTypeVoid:
+ case SpvOpTypeBool:
+ case SpvOpTypeInt:
+ case SpvOpTypeFloat:
+ case SpvOpTypeVector:
+ case SpvOpTypeMatrix:
+ case SpvOpTypeImage:
+ case SpvOpTypeSampler:
+ case SpvOpTypeSampledImage:
+ case SpvOpTypeArray:
+ case SpvOpTypeRuntimeArray:
+ case SpvOpTypeStruct:
+ case SpvOpTypeOpaque:
+ case SpvOpTypePointer:
+ case SpvOpTypeFunction:
+ case SpvOpTypeEvent:
+ case SpvOpTypeDeviceEvent:
+ case SpvOpTypeReserveId:
+ case SpvOpTypeQueue:
+ case SpvOpTypePipe:
+ case SpvOpTypeForwardPointer:
+ case SpvOpConstantTrue:
+ case SpvOpConstantFalse:
+ case SpvOpConstant:
+ case SpvOpConstantComposite:
+ case SpvOpConstantSampler:
+ case SpvOpConstantNull:
+ case SpvOpSpecConstantTrue:
+ case SpvOpSpecConstantFalse:
+ case SpvOpSpecConstant:
+ case SpvOpSpecConstantComposite:
+ case SpvOpSpecConstantOp:
+ case SpvOpVariable:
+ case SpvOpUndef:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static void
+spirv_copy_sections(char **binaries, const unsigned *lengths,
+ unsigned *indices, unsigned num_binaries,
+ char *merged_binary, unsigned *merged_index,
+ bool (*check_section)(SpvId))
+{
+ unsigned i = 0u, j = 0u;
+ unsigned length = 0u, prev_j = 0u, index = 0u;
+ const char *binary = NULL;
+ unsigned opcode = 0u, word_count = 0u;
+
+ for (i = 0u; i < num_binaries; ++i) {
+ binary = binaries[i];
+ length = lengths[i];
+ index = indices[i];
+ prev_j = j = index;
+ while (j < length) {
+ opcode = spirv_get_opcode(binary, j, &word_count);
+
+ prev_j = j;
+
+ if (!check_section(opcode))
+ break;
+
+ j += word_count;
+ }
+ if (j == length)
+ prev_j = j;
+
+ memcpy(&merged_binary[*merged_index * sizeof(spirv_word)],
+ &binary[index * sizeof(spirv_word)],
+ (prev_j - index) * sizeof(spirv_word));
+ *merged_index += prev_j - index;
+ indices[i] = j;
+ }
+}
+
+static int
+spirv_merge_modules(char **binaries, const unsigned *lengths,
+ unsigned num_binaries, unsigned max_index, unsigned version,
+ char *merged_binary, bool create_library,
+ unsigned *final_length, char **msg,
+ unsigned *msg_length)
+{
+ unsigned *indices = (unsigned *) malloc(num_binaries * sizeof(unsigned));
+ unsigned i = 0u, j = 0u, merged_index = 0u;
+ unsigned length = 0u, prev_j = 0u, index = 0u, function_begin = 0u;
+ int ret = 0;
+ const char *binary = NULL;
+ unsigned opcode = 0u, word_count = 0u;
+ SpvAddressingModel addressing_model, addressing_model_tmp;
+ const char *model_str = NULL, *model_tmp_str = NULL;
+ SpvMemoryModel memory_model, memory_model_tmp;
+
+ struct util_hash_table *exports = util_hash_table_create(&spirv_spvid_hash,
+ &spirv_spvid_compare);
+ struct util_hash_table *imports = util_hash_table_create(&spirv_spvid_hash,
+ &spirv_spvid_compare);
+ struct util_hash_table *link_table = util_hash_table_create(&spirv_spvid_hash,
+ &spirv_spvid_compare);
+
+ /* Generate new header */
+ spirv_set_word(merged_binary, 0u, SpvMagicNumber);
+ spirv_set_word(merged_binary, 1u, version);
+ spirv_set_word(merged_binary, 2u, 0u);
+ spirv_set_word(merged_binary, 3u, max_index);
+ spirv_set_word(merged_binary, 4u, 0u);
+ merged_index += 5u;
+
+ for (i = 0u; i < num_binaries; ++i)
+ indices[i] = 5u;
+
+ /* Copy OpCapabilities */
+ spirv_copy_sections(binaries, lengths, indices, num_binaries, merged_binary,
+ &merged_index, &spirv_is_capability_op);
+
+ /* Copy OpExtensions */
+ spirv_copy_sections(binaries, lengths, indices, num_binaries, merged_binary,
+ &merged_index, &spirv_is_extension_op);
+
+ /* Copy OpExtInstImport */
+ spirv_copy_sections(binaries, lengths, indices, num_binaries, merged_binary,
+ &merged_index, &spirv_is_ext_inst_import_op);
+
+ /* Merge OpMemoryModel */
+ for (i = 0u; i < num_binaries; ++i) {
+ binary = binaries[i];
+ index = indices[i];
+ opcode = spirv_get_opcode(binary, index, &word_count);
+
+ assert(opcode == SpvOpMemoryModel);
+ if (i == 0u) {
+ addressing_model = (SpvAddressingModel) spirv_get_word(binary,
+ index + 1u);
+ memory_model = (SpvMemoryModel) spirv_get_word(binary, index + 2u);
+ } else {
+ addressing_model_tmp = (SpvAddressingModel) spirv_get_word(binary,
+ index + 1u);
+ memory_model_tmp = (SpvMemoryModel) spirv_get_word(binary, index + 2u);
+ if (addressing_model != addressing_model_tmp) {
+ model_str = addressing_model_to_string(addressing_model);
+ model_tmp_str = addressing_model_to_string(addressing_model_tmp);
+ *msg_length = snprintf(NULL, 0,
+ "SPIR-V linker: Inconsistent addressing models: '%s' for binary 0 and '%s' for binary %d\n",
+ model_str, model_tmp_str, i) + 1;
+ *msg = (char *) malloc(*msg_length);
+ snprintf(*msg, *msg_length,
+ "SPIR-V linker: Inconsistent addressing models: '%s' for binary 0 and '%s' for binary %d\n",
+ model_str, model_tmp_str, i);
+ ret = -1;
+ goto end;
+ }
+ if (memory_model != memory_model_tmp) {
+ model_str = memory_model_to_string(memory_model);
+ model_tmp_str = memory_model_to_string(memory_model_tmp);
+ *msg_length = snprintf(NULL, 0,
+ "SPIR-V linker: Inconsistent memory models: '%s' for binary 0 and '%s' for binary %d\n",
+ model_str, model_tmp_str, i) + 1;
+ *msg = (char *) malloc(*msg_length);
+ snprintf(*msg, *msg_length,
+ "SPIR-V linker: Inconsistent memory models: '%s' for binary 0 and '%s' for binary %d\n",
+ model_str, model_tmp_str, i);
+ ret = -1;
+ goto end;
+ }
+ }
+
+ indices[i] = index + 3u;
+ }
+ spirv_set_word(merged_binary, merged_index, (3u << SpvWordCountShift) + 14);
+ spirv_set_word(merged_binary, merged_index + 1u, addressing_model);
+ spirv_set_word(merged_binary, merged_index + 2u, memory_model);
+ merged_index += 3u;
+
+ /* Copy OpEntryPoint */
+ spirv_copy_sections(binaries, lengths, indices, num_binaries, merged_binary,
+ &merged_index, &spirv_is_entry_point_op);
+
+ /* Copy OpExecutionMode */
+ spirv_copy_sections(binaries, lengths, indices, num_binaries, merged_binary,
+ &merged_index, &spirv_is_execution_mode_op);
+
+ /* Copy debug a) */
+ spirv_copy_sections(binaries, lengths, indices, num_binaries, merged_binary,
+ &merged_index, &spirv_is_debug_a_op);
+ /* Copy debug b) */
+ spirv_copy_sections(binaries, lengths, indices, num_binaries, merged_binary,
+ &merged_index, &spirv_is_debug_b_op);
+
+ /* Copy annotations */
+ for (i = 0u; i < num_binaries; ++i) {
+ binary = binaries[i];
+ length = lengths[i];
+ index = indices[i];
+ j = index;
+ prev_j = j;
+ while (j < length) {
+ opcode = spirv_get_opcode(binary, j, &word_count);
+
+ prev_j = j;
+
+ if (opcode != SpvOpDecorate && opcode != SpvOpMemberDecorate &&
+ opcode != SpvOpGroupDecorate && opcode != SpvOpGroupMemberDecorate &&
+ opcode != SpvOpDecorationGroup)
+ break;
+
+ j += word_count;
+
+ if (opcode != SpvOpDecorate)
+ continue;
+ const SpvDecoration decoration = spirv_get_word(binary, prev_j + 2u);
+ if (decoration != SpvDecorationLinkageAttributes)
+ continue;
+
+ const SpvId id = spirv_get_word(binary, prev_j + 1u);
+ const SpvLinkageType linkage_type = spirv_get_word(binary, prev_j + (word_count - 1u));
+ if (linkage_type == SpvLinkageTypeExport)
+ util_hash_table_set(exports, UINT_TO_PTR(id),
+ spirv_get_string(binary, prev_j + 3u));
+ else
+ util_hash_table_set(imports, UINT_TO_PTR(id),
+ spirv_get_string(binary, prev_j + 3u));
+
+ /* Remove exports from SPIR-V if we are not building a library */
+ if (create_library && linkage_type == SpvLinkageTypeExport)
+ continue;
+
+ memcpy(&merged_binary[merged_index * sizeof(spirv_word)],
+ &binary[index * sizeof(spirv_word)],
+ (prev_j - index) * sizeof(spirv_word));
+ merged_index += prev_j - index;
+ index = j;
+ }
+ if (j == length)
+ prev_j = j;
+
+ memcpy(&merged_binary[merged_index * sizeof(spirv_word)],
+ &binary[index * sizeof(spirv_word)],
+ (prev_j - index) * sizeof(spirv_word));
+ merged_index += prev_j - index;
+ indices[i] = j;
+ }
+
+ struct spirv_link_data link_data = {
+ .exports = exports,
+ .link_table = link_table,
+ .linkage_uid = NULL,
+ .export_id = 0u,
+ .msg = msg,
+ .msg_length = msg_length
+ };
+ ret = util_hash_table_foreach(imports, &generate_link_table, &link_data);
+ if (ret != PIPE_OK)
+ goto end;
+
+ /* Copy types/constants/global variables/OpUndef */
+ for (i = 0u; i < num_binaries; ++i) {
+ binary = binaries[i];
+ length = lengths[i];
+ index = indices[i];
+ j = index;
+ prev_j = j;
+ while (j < length) {
+ opcode = spirv_get_opcode(binary, j, &word_count);
+
+ prev_j = j;
+
+ SpvStorageClass storage_type = spirv_get_word(binary, j + 3u);
+ if (!spirv_is_type_constant_op(opcode) ||
+ (opcode == SpvOpVariable &&
+ storage_type == SpvStorageClassFunction))
+ break;
+
+ j += word_count;
+
+ /* Remove imported variables */
+ void *tmp = UINT_TO_PTR(spirv_get_word(binary, prev_j + 2u));
+ if (opcode == SpvOpVariable &&
+ util_hash_table_get(link_table, tmp) != NULL) {
+ memcpy(&merged_binary[merged_index * sizeof(spirv_word)],
+ &binary[index * sizeof(spirv_word)],
+ (prev_j - index) * sizeof(spirv_word));
+ merged_index += prev_j - index;
+ index = j;
+ }
+ }
+ if (j == length)
+ prev_j = j;
+
+ memcpy(&merged_binary[merged_index * sizeof(spirv_word)],
+ &binary[index * sizeof(spirv_word)],
+ (prev_j - index) * sizeof(spirv_word));
+ merged_index += prev_j - index;
+ indices[i] = j;
+ }
+
+ /* Skip function declarations */
+ for (i = 0u; i < num_binaries; ++i) {
+ binary = binaries[i];
+ length = lengths[i];
+ index = indices[i];
+ j = index;
+ function_begin = j;
+ while (j < length) {
+ opcode = spirv_get_opcode(binary, j, &word_count);
+
+ if (opcode == SpvOpFunction)
+ function_begin = j;
+
+ /* This was not a prototype: revert to the beginning of the function */
+ if (opcode != SpvOpFunction && opcode != SpvOpFunctionParameter &&
+ opcode != SpvOpFunctionEnd) {
+ j = function_begin;
+ break;
+ }
+
+ j += word_count;
+ }
+
+ indices[i] = j;
+ }
+
+ /* Copy function defintions */
+ for (i = 0u; i < num_binaries; ++i) {
+ binary = binaries[i];
+ length = lengths[i];
+ index = indices[i];
+ memcpy(&merged_binary[merged_index * sizeof(spirv_word)],
+ &binary[index * sizeof(spirv_word)],
+ (length - index) * sizeof(spirv_word));
+ merged_index += length - index;
+ }
+
+ /* Link everything together */
+ ret = spirv_transform_binary(merged_binary, merged_index, &spirv_link_ids,
+ link_table);
+ if (ret)
+ goto end;
+
+ if (final_length)
+ *final_length = merged_index;
+
+end:
+ free(indices);
+ util_hash_table_destroy(imports);
+ util_hash_table_destroy(exports);
+ util_hash_table_destroy(link_table);
+ return ret;
+}
+
+const char *
+spirv_link_binaries(const char **binaries, const unsigned *binaries_word_count,
+ unsigned num_binaries, bool create_library,
+ unsigned *linked_word_count, char **msg,
+ unsigned *length)
+{
+ char **tmp_binaries = NULL;
+ char *linked_binary = NULL;
+ const char *binary = NULL;
+ char *tmp_binary = NULL;
+ unsigned total_word_count = 0u, binary_max_id = 0u, id_upper_bound = 0u;
+ unsigned word_count = 0u, byte_count = 0u;
+ int i = 0, j = 0, ret = 0;
+ unsigned max_version = 0u, version = 0u;
+
+ /* As we need to bump IDs in each module, so as they do not conflict as we
+ * merge them together, we need to make a copy which we can modify.
+ */
+ tmp_binaries = (char **) malloc(num_binaries * sizeof(char *));
+
+ for (i = 0; i < (int) num_binaries; ++i) {
+ word_count = binaries_word_count[i];
+ binary = binaries[i];
+ byte_count = word_count * sizeof(spirv_word);
+
+ version = spirv_get_word(binary, 1u);
+ if (version > SpvVersion) {
+ *length = snprintf(NULL, 0,
+ "SPIR-V linker: Binary %d uses an unsupported SPIR-V version: %u.%u\n",
+ i, ((version >> 16u) & 0xff),
+ ((version >> 8u) & 0xff)) + 1;
+ *msg = (char *) malloc(*length);
+ snprintf(*msg, *length,
+ "SPIR-V linker: Binary %d uses an unsupported SPIR-V version: %u.%u\n",
+ i, ((version >> 16u) & 0xff), ((version >> 8u) & 0xff));
+ goto error;
+ }
+ if (version > max_version)
+ max_version = version;
+
+ tmp_binary = (char *) malloc(byte_count);
+ if (!tmp_binary)
+ goto error;
+ memcpy(tmp_binary, binary, byte_count);
+ tmp_binaries[i] = tmp_binary;
+
+ binary_max_id = spirv_get_word(binary, 3u) - 1u;
+
+ ret = spirv_transform_binary(tmp_binary, word_count, &spirv_bump_id,
+ UINT_TO_PTR(id_upper_bound));
+ if (ret)
+ goto error2;
+
+ id_upper_bound += binary_max_id;
+ total_word_count += word_count;
+ }
+
+ /* id_upper_bound is currently equal to the highest id being used, so add
+ * one to get the strict upper bound
+ */
+ ++id_upper_bound;
+
+ linked_binary = (char *) malloc(total_word_count * sizeof(spirv_word));
+ ret = spirv_merge_modules(tmp_binaries, binaries_word_count, num_binaries,
+ id_upper_bound, max_version, linked_binary,
+ create_library, &total_word_count, msg, length);
+ if (ret)
+ goto error3;
+
+ if (linked_word_count)
+ *linked_word_count = total_word_count;
+
+ for (i = 0; i < (int) num_binaries; ++i)
+ free(tmp_binaries[i]);
+ free(tmp_binaries);
+
+ return linked_binary;
+
+error3:
+ free(linked_binary);
+error2:
+ if (i < (int) num_binaries)
+ free(tmp_binaries[i]);
+error:
+ for (j = 0; j < i; ++j)
+ free(tmp_binaries[j]);
+ free(tmp_binaries);
+ return NULL;
+}
diff --git a/src/gallium/auxiliary/spirv/spirv_linker.h b/src/gallium/auxiliary/spirv/spirv_linker.h
new file mode 100644
index 0000000000..0090c8071f
--- /dev/null
+++ b/src/gallium/auxiliary/spirv/spirv_linker.h
@@ -0,0 +1,67 @@
+/**************************************************************************
+ *
+ * Copyright 2017 Pierre Moreau
+ * All Rights Reserved.
+ *
+ * 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, sub license, 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 (including the
+ * next paragraph) 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 NON-INFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS AND/OR ITS SUPPLIERS 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.
+ *
+ **************************************************************************/
+
+#ifndef SPIRV_LINKER_H
+#define SPIRV_LINKER_H
+
+#include <stdbool.h>
+
+#if defined __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Link SPIR-V binaries into a SPIR-V library or a SPIR-V executable.
+ *
+ * The given SPIR-V modules are expected to
+ * - have been validated;
+ * - use the same endianness as the CPU.
+ *
+ * @param binaries binaries to be linked together
+ * @param binaries_word_count number of words making of each binary
+ * @param num_binaries number of binaries to be linked
+ * @param create_library whether to create a library (a SPIR-V module with no
+ * import linkage attributes, only export) or an
+ * executable (a SPIR-V module with no linkage attributes)
+ * @param linked_word_count if specified, it will contain the number of words
+ * making up the resulting linked SPIR-V binary
+ * @param msg the error message if something wrong happened during linking
+ * @param length the length of the error message msg
+ * @return if linking was successful, the linked SPIR-V binary, a nul pointer
+ * otherwise
+ */
+const char *
+spirv_link_binaries(const char **binaries, const unsigned *binaries_word_count,
+ unsigned num_binaries, bool create_library,
+ unsigned *linked_word_count, char **msg,
+ unsigned *length);
+
+#if defined __cplusplus
+}
+#endif
+
+#endif /* SPIRV_LINKER_H */
--
2.12.2
Pierre Moreau
2017-05-15 10:14:02 UTC
Permalink
Raw Message
This version tries to link builtins, as they are marked as imported, but nothing
exports them, resulting in the linker failing and throwing an error. As
builtins are target dependent, it is best to let the driver handles those
cases. I changed the code locally to also track which IDs are builtins, and
remove from the import table all builtins, right before generating the linking
table.

Other comments are welcomed (on this patch, and on the others as well). :-)

Pierre
Post by Pierre Moreau
---
src/gallium/auxiliary/Makefile.sources | 4 +-
src/gallium/auxiliary/spirv/spirv_linker.c | 1324 ++++++++++++++++++++++++++++
src/gallium/auxiliary/spirv/spirv_linker.h | 67 ++
3 files changed, 1394 insertions(+), 1 deletion(-)
create mode 100644 src/gallium/auxiliary/spirv/spirv_linker.c
create mode 100644 src/gallium/auxiliary/spirv/spirv_linker.h
diff --git a/src/gallium/auxiliary/Makefile.sources b/src/gallium/auxiliary/Makefile.sources
index f4817742ff..91aac49dfb 100644
--- a/src/gallium/auxiliary/Makefile.sources
+++ b/src/gallium/auxiliary/Makefile.sources
@@ -314,7 +314,9 @@ NIR_SOURCES := \
SPIRV_SOURCES := \
spirv/spirv_utils.c \
- spirv/spirv_utils.h
+ spirv/spirv_utils.h \
+ spirv/spirv_linker.c \
+ spirv/spirv_linker.h
VL_SOURCES := \
vl/vl_bicubic_filter.c \
diff --git a/src/gallium/auxiliary/spirv/spirv_linker.c b/src/gallium/auxiliary/spirv/spirv_linker.c
new file mode 100644
index 0000000000..9d060be0cc
--- /dev/null
+++ b/src/gallium/auxiliary/spirv/spirv_linker.c
@@ -0,0 +1,1324 @@
+/**************************************************************************
+ *
+ * Copyright 2017 Pierre Moreau
+ * All Rights Reserved.
+ *
+ * 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, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) 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 NON-INFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS AND/OR ITS SUPPLIERS 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.
+ *
+ **************************************************************************/
+
+#include "spirv_linker.h"
+#include "spirv_utils.h"
+
+#include "compiler/spirv/spirv.h"
+#include "util/u_debug.h"
+#include "util/u_hash_table.h"
+#include "util/u_pointer.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#define PTR_TO_UINT(x) ((unsigned)pointer_to_uintptr(x))
+#define UINT_TO_PTR(x) (uintptr_to_pointer((uintptr_t)(x)))
+
+/**
+ * Extracts the opcode and the number of words making up this instruction.
+ *
+ * the instruction, otherwise will be left untouched
+ */
+static SpvOp
+spirv_get_opcode(const char *binary, size_t word_offset, unsigned *word_count)
+{
+ const unsigned desc_word = spirv_get_word(binary, word_offset);
+ if (word_count)
+ *word_count = desc_word >> SpvWordCountShift;
+ return (SpvOp) (desc_word & SpvOpCodeMask);
+}
+
+static unsigned
+spirv_spvid_hash(void *id)
+{
+ return PTR_TO_UINT(id);
+}
+
+static int
+spirv_spvid_compare(void *id1, void *id2)
+{
+ return PTR_TO_UINT(id1) != PTR_TO_UINT(id2);
+}
+
+/**
+ * Adds a specified base ID to the ID found at a specified position in the
+ * binary.
+ */
+static void
+spirv_bump_id(char *binary, unsigned word_offset, void *base_id)
+{
+ SpvId old_id = spirv_get_word(binary, word_offset);
+ spirv_set_word(binary, word_offset, PTR_TO_UINT(base_id) + old_id);
+}
+
+/**
+ * Replaces an ID with another one, if found in the link table.
+ */
+static void
+spirv_link_ids(char *binary, unsigned word_offset, void *link_table)
+{
+ SpvId old_id = spirv_get_word(binary, word_offset);
+ void *new_id_ptr = util_hash_table_get((struct util_hash_table *) link_table,
+ UINT_TO_PTR(old_id));
+ SpvId new_id = PTR_TO_UINT(new_id_ptr);
+ if (new_id_ptr != NULL)
+ spirv_set_word(binary, word_offset, new_id);
+}
+
+/**
+ * Associates the given variable to its width, if found.
+ */
+static void
+spirv_register_variable(char *binary, unsigned type_offset,
+ unsigned variable_offset, struct util_hash_table *types,
+ struct util_hash_table *variables)
+{
+ SpvId type_id = spirv_get_word(binary, type_offset);
+ SpvId var_id = spirv_get_word(binary, variable_offset);
+ void *width_ptr = util_hash_table_get(types, UINT_TO_PTR(type_id));
+ if (width_ptr != NULL)
+ util_hash_table_set(variables, UINT_TO_PTR(var_id), width_ptr);
+}
+
+/**
+ * Applies the given function onto the specified IDs.
+ */
+static void
+spirv_transform_ids(void (*transform_id)(char *, unsigned, void *),
+ char *binary, unsigned offset,
+ void *data, int ids_count, ...)
+{
+ va_list ids;
+ va_start(ids, ids_count);
+ for (int i = 0; i < ids_count; ++i)
+ transform_id(binary, offset + va_arg(ids, SpvId), data);
+ va_end(ids);
+}
+
+/**
+ * Applies the given function to all IDs found in the given binary.
+ */
+static int
+spirv_transform_binary(char *binary, unsigned binary_word_count,
+ void (*transform_id)(char *, unsigned, void *),
+ void *data)
+{
+ unsigned i = 5u; // Skip header
+ unsigned j = 0u, k = 0u;
+ int ret = 0;
+ unsigned opcode = 0u, insn_word_count = 0u;
+
+ struct util_hash_table *int_types = util_hash_table_create(&spirv_spvid_hash,
+ &spirv_spvid_compare);
+ struct util_hash_table *int_variables = util_hash_table_create(&spirv_spvid_hash,
+ &spirv_spvid_compare);
+
+ while (i < binary_word_count) {
+ opcode = spirv_get_opcode(binary, i, &insn_word_count);
+
+ switch (opcode) {
+ break;
+ for (j = 1u; j < insn_word_count; ++j)
+ transform_id(binary, i + j, data);
+ break;
+ break;
+ if (insn_word_count > 3u)
+ transform_id(binary, i + 3u, data);
+ break;
+ break;
+ transform_id(binary, i + 1u, data);
+ break;
+ break;
+ transform_id(binary, i + 1u, data);
+ break;
+ for (j = 1u; j < insn_word_count; ++j)
+ transform_id(binary, i + j, data);
+ break;
+ transform_id(binary, i + 1u, data);
+ for (j = 2u; j < insn_word_count; j += 2u)
+ transform_id(binary, i + j, data);
+ break;
+ break;
+ transform_id(binary, i + 1u, data);
+ break;
+ spirv_transform_ids(transform_id, binary, i, data, 3, 1u, 2u, 3u);
+ for (j = 5u; j < insn_word_count; ++j)
+ transform_id(binary, i + j, data);
+ break;
+ break;
+ transform_id(binary, i + 2u, data);
+ j = 3u;
+ k = 0u;
+ /* We have to compute first how long the string is */
+ while (j < insn_word_count &&
+ binary[(i + j) * sizeof(spirv_word) + k] != '\0') {
+ ++k;
+ if (k >= sizeof(spirv_word)) {
+ k = 0u;
+ ++j;
+ }
+ }
+ if (j < insn_word_count)
+ ++k;
+ if (k > 0u) {
+ k = 0u;
+ ++j;
+ }
+ for (; j < insn_word_count; ++j)
+ transform_id(binary, i + j, data);
+ break;
+ transform_id(binary, i + 1u, data);
+ break;
+ break;
+ transform_id(binary, i + 1u, data);
+ if (opcode == SpvOpTypeInt) {
+ SpvId new_id = spirv_get_word(binary, i + 1u);
+ uint32_t width = spirv_get_word(binary, i + 2u);
+ width = width / 32u + (width % 32u != 0u);
+ util_hash_table_set(int_types, UINT_TO_PTR(new_id),
+ UINT_TO_PTR(width));
+ }
+ break;
+ for (j = 1u; j < 3u; ++j)
+ transform_id(binary, i + j, data);
+ break;
+ transform_id(binary, i + 1u, data);
+ break;
+ for (j = 1u; j < insn_word_count; ++j)
+ transform_id(binary, i + j, data);
+ break;
+ transform_id(binary, i + 1u, data);
+ break;
+ spirv_transform_ids(transform_id, binary, i, data, 2, 1u, 3u);
+ break;
+ for (j = 1u; j < insn_word_count; ++j)
+ transform_id(binary, i + j, data);
+ break;
+ transform_id(binary, i + 1u, data);
+ break;
+ for (j = 1u; j < 3u; ++j)
+ transform_id(binary, i + j, data);
+ if (opcode == SpvOpConstant)
+ spirv_register_variable(binary, i + 1u, i + 2u, int_types,
+ int_variables);
+ break;
+ for (j = 1u; j < insn_word_count; ++j)
+ transform_id(binary, i + j, data);
+ break;
+ for (j = 1u; j < 3u; ++j)
+ transform_id(binary, i + j, data);
+ if (opcode == SpvOpSpecConstant)
+ spirv_register_variable(binary, i + 1u, i + 2u, int_types,
+ int_variables);
+ break;
+ for (j = 1u; j < insn_word_count; ++j)
+ transform_id(binary, i + j, data);
+ break;
+ for (j = 1u; j < 3u; ++j)
+ transform_id(binary, i + j, data);
+ for (j = 4u; j < insn_word_count; ++j)
+ transform_id(binary, i + j, data);
+ spirv_register_variable(binary, i + 1u, i + 2u, int_types,
+ int_variables);
+ break;
+ for (j = 1u; j < 3u; ++j)
+ transform_id(binary, i + j, data);
+ if (insn_word_count == 5u)
+ transform_id(binary, i + 4u, data);
+ spirv_register_variable(binary, i + 1u, i + 2u, int_types,
+ int_variables);
+ break;
+ for (j = 1u; j < insn_word_count; ++j)
+ transform_id(binary, i + j, data);
+ break;
+ for (j = 1u; j < 4u; ++j)
+ transform_id(binary, i + j, data);
+ spirv_register_variable(binary, i + 1u, i + 2u, int_types,
+ int_variables);
+ break;
+ for (j = 1u; j < 3u; ++j)
+ transform_id(binary, i + j, data);
+ break;
+ for (j = 1u; j < 4u; ++j)
+ transform_id(binary, i + j, data);
+ break;
+ for (j = 1u; j < insn_word_count; ++j)
+ transform_id(binary, i + j, data);
+ break;
+ for (j = 1u; j < 4u; ++j)
+ transform_id(binary, i + j, data);
+ spirv_register_variable(binary, i + 1u, i + 2u, int_types,
+ int_variables);
+ break;
+ for (j = 1u; j < insn_word_count; ++j)
+ transform_id(binary, i + j, data);
+ break;
+ spirv_transform_ids(transform_id, binary, i, data,
+ 3, 1u, 2u, 4u);
+ break;
+ for (j = 1u; j < insn_word_count; ++j)
+ transform_id(binary, i + j, data);
+ spirv_register_variable(binary, i + 1u, i + 2u, int_types,
+ int_variables);
+ break;
+ break;
+ for (j = 1u; j < insn_word_count; ++j)
+ transform_id(binary, i + j, data);
+ spirv_register_variable(binary, i + 1u, i + 2u, int_types,
+ int_variables);
+ break;
+ for (j = 1u; j < insn_word_count; ++j)
+ transform_id(binary, i + j, data);
+ break;
+ for (j = 1u; j < 5u; ++j)
+ transform_id(binary, i + j, data);
+ for (j = 6u; j < insn_word_count; ++j)
+ transform_id(binary, i + j, data);
+ if (opcode == SpvOpImageRead)
+ spirv_register_variable(binary, i + 1u, i + 2u, int_types,
+ int_variables);
+ break;
+ for (j = 1u; j < 6u; ++j)
+ transform_id(binary, i + j, data);
+ for (j = 7u; j < insn_word_count; ++j)
+ transform_id(binary, i + j, data);
+ if (opcode != SpvOpImageGather)
+ spirv_register_variable(binary, i + 1u, i + 2u, int_types,
+ int_variables);
+ break;
+ for (j = 1u; j < 4u; ++j)
+ transform_id(binary, i + j, data);
+ for (j = 5u; j < insn_word_count; ++j)
+ transform_id(binary, i + j, data);
+ break;
+ for (j = 1u; j < insn_word_count; ++j)
+ transform_id(binary, i + j, data);
+ if (opcode != SpvOpImage && opcode != SpvOpImageQueryLod)
+ spirv_register_variable(binary, i + 1u, i + 2u, int_types,
+ int_variables);
+ break;
+ for (j = 1u; j < 5u; ++j)
+ transform_id(binary, i + j, data);
+ for (j = 6u; j < insn_word_count; ++j)
+ transform_id(binary, i + j, data);
+ break;
+ for (j = 1u; j < 6u; ++j)
+ transform_id(binary, i + j, data);
+ for (j = 7u; j < insn_word_count; ++j)
+ transform_id(binary, i + j, data);
+ break;
+ assert(false);
+ return -1;
+ for (j = 1u; j < 4u; ++j)
+ transform_id(binary, i + j, data);
+ break;
+ for (j = 1u; j < 4u; ++j)
+ transform_id(binary, i + j, data);
+ spirv_register_variable(binary, i + 1u, i + 2u, int_types,
+ int_variables);
+ break;
+ for (j = 1u; j < 4u; ++j)
+ transform_id(binary, i + j, data);
+ break;
+ for (j = 1u; j < insn_word_count; ++j)
+ transform_id(binary, i + j, data);
+ if (opcode == SpvOpVectorExtractDynamic)
+ spirv_register_variable(binary, i + 1u, i + 2u, int_types,
+ int_variables);
+ break;
+ for (j = 1u; j < 5u; ++j)
+ transform_id(binary, i + j, data);
+ break;
+ for (j = 1u; j < insn_word_count; ++j)
+ transform_id(binary, i + j, data);
+ break;
+ for (j = 1u; j < 4u; ++j)
+ transform_id(binary, i + j, data);
+ spirv_register_variable(binary, i + 1u, i + 2u, int_types,
+ int_variables);
+ break;
+ for (j = 1u; j < insn_word_count; ++j)
+ transform_id(binary, i + j, data);
+ if (opcode != SpvOpTranspose && opcode != SpvOpFNegate)
+ spirv_register_variable(binary, i + 1u, i + 2u, int_types,
+ int_variables);
+ break;
+ for (j = 1u; j < insn_word_count; ++j)
+ transform_id(binary, i + j, data);
+ break;
+ for (j = 1u; j < insn_word_count; ++j)
+ transform_id(binary, i + j, data);
+ if (opcode == SpvOpBitReverse || opcode == SpvOpBitCount ||
+ opcode == SpvOpPhi)
+ spirv_register_variable(binary, i + 1u, i + 2u, int_types,
+ int_variables);
+ break;
+ for (j = 1u; j < insn_word_count; ++j)
+ transform_id(binary, i + j, data);
+ break;
+ for (j = 1u; j < 3u; ++j)
+ transform_id(binary, i + j, data);
+ break;
+ transform_id(binary, i + 1u, data);
+ break;
+ spirv_transform_ids(transform_id, binary, i, data, 3, 1u, 2u, 3u);
+ break;
+ for (j = 1u; j < 4u; ++j)
+ transform_id(binary, i + j, data);
+ {
+ SpvId selector_id = spirv_get_word(binary, i + 1u);
+ void *width_ptr = util_hash_table_get(int_variables,
+ UINT_TO_PTR(selector_id));
+ assert(width_ptr);
+ unsigned width = PTR_TO_UINT(width_ptr);
+ for (j = 3u + width; j < insn_word_count; j += 1u + width)
+ transform_id(binary, i + j, data);
+ }
+ break;
+ break;
+ transform_id(binary, i + 1u, data);
+ break;
+ for (j = 1u; j < insn_word_count; ++j)
+ transform_id(binary, i + j, data);
+ if (opcode != SpvOpAtomicStore &&
+ opcode != SpvOpAtomicFlagTestAndSet &&
+ opcode != SpvOpAtomicFlagClear)
+ spirv_register_variable(binary, i + 1u, i + 2u, int_types,
+ int_variables);
+ break;
+ break;
+ transform_id(binary, i + 1u, data);
+ break;
+ for (j = 1u; j < insn_word_count; ++j)
+ transform_id(binary, i + j, data);
+ break;
+ for (j = 1u; j < insn_word_count; ++j)
+ transform_id(binary, i + j, data);
+ spirv_register_variable(binary, i + 1u, i + 2u, int_types,
+ int_variables);
+ break;
+ for (j = 1u; j < insn_word_count; ++j)
+ transform_id(binary, i + j, data);
+ break;
+ for (j = 1u; j < insn_word_count; ++j)
+ transform_id(binary, i + j, data);
+ spirv_register_variable(binary, i + 1u, i + 2u, int_types,
+ int_variables);
+ break;
+ for (j = 1u; j < insn_word_count; ++j)
+ transform_id(binary, i + j, data);
+ break;
+ for (j = 1u; j < insn_word_count; ++j)
+ transform_id(binary, i + j, data);
+ spirv_register_variable(binary, i + 1u, i + 2u, int_types,
+ int_variables);
+ break;
+ for (j = 1u; j < insn_word_count; ++j)
+ transform_id(binary, i + j, data);
+ break;
+ case SpvOpMax: //FALLTHROUGH
+ assert(false);
+ return -1;
+ }
+
+ i += insn_word_count;
+ }
+
+ util_hash_table_destroy(int_types);
+ util_hash_table_destroy(int_variables);
+
+ return ret;
+}
+
+struct spirv_link_data {
+ struct util_hash_table *exports;
+ struct util_hash_table *link_table;
+ const char *linkage_uid;
+ SpvId export_id;
+ char **msg;
+ unsigned *msg_length;
+};
+
+static const char *
+addressing_model_to_string(SpvAddressingModel model)
+{
+#define CVT_ADDR_MODEL(v) case SpvAddressingModel##v: return #v
+ switch (model) {
+ CVT_ADDR_MODEL(Logical);
+ CVT_ADDR_MODEL(Physical32);
+ CVT_ADDR_MODEL(Physical64);
+ return "Unsupported";
+ }
+#undef CVT_ADDR_MODEL
+}
+
+static const char *
+memory_model_to_string(SpvAddressingModel model)
+{
+#define CVT_MEMORY_MODEL(v) case SpvMemoryModel##v: return #v
+ switch (model) {
+ CVT_MEMORY_MODEL(Simple);
+ CVT_MEMORY_MODEL(GLSL450);
+ CVT_MEMORY_MODEL(OpenCL);
+ return "Unsupported";
+ }
+#undef CVT_MEMORY_MODEL
+}
+
+/* Returns PIPE_ERROR when item found */
+static enum pipe_error
+find_export(void *export_id, void *linkage_uid, void *data)
+{
+ struct spirv_link_data *link_data = (struct spirv_link_data *) data;
+ if (!strcmp(link_data->linkage_uid, (const char *) linkage_uid)) {
+ link_data->export_id = PTR_TO_UINT(export_id);
+ return PIPE_ERROR;
+ }
+ return PIPE_OK;
+}
+
+static enum pipe_error
+generate_link_table(void *import_id, void *linkage_uid, void *data)
+{
+ struct spirv_link_data *link_data = (struct spirv_link_data *) data;
+ link_data->export_id = 0u;
+ link_data->linkage_uid = (const char *) linkage_uid;
+ util_hash_table_foreach(link_data->exports, &find_export, data);
+ if (link_data->export_id == 0u) {
+ *link_data->msg_length = snprintf(NULL, 0,
+ "SPIR-V linker: Missing symbol \"%s\"\n",
+ link_data->linkage_uid) + 1;
+ *link_data->msg = (char *) malloc(*link_data->msg_length);
+ snprintf(*link_data->msg, *link_data->msg_length,
+ "SPIR-V linker: Missing symbol \"%s\"\n",
+ link_data->linkage_uid);
+ return PIPE_ERROR;
+ }
+ util_hash_table_set(link_data->link_table, import_id,
+ UINT_TO_PTR(link_data->export_id));
+ return PIPE_OK;
+}
+
+static bool
+spirv_is_capability_op(SpvId opcode)
+{
+ return opcode == SpvOpCapability;
+}
+
+static bool
+spirv_is_extension_op(SpvId opcode)
+{
+ return opcode == SpvOpExtension;
+}
+
+static bool
+spirv_is_ext_inst_import_op(SpvId opcode)
+{
+ return opcode == SpvOpExtInstImport;
+}
+
+static bool
+spirv_is_entry_point_op(SpvId opcode)
+{
+ return opcode == SpvOpEntryPoint;
+}
+
+static bool
+spirv_is_execution_mode_op(SpvId opcode)
+{
+ return opcode == SpvOpExecutionMode;
+}
+
+static bool
+spirv_is_debug_a_op(SpvId opcode)
+{
+ switch (opcode) {
+ return true;
+ return false;
+ }
+}
+
+static bool
+spirv_is_debug_b_op(SpvId opcode)
+{
+ return opcode == SpvOpName || opcode == SpvOpMemberName;
+}
+
+static bool
+spirv_is_type_constant_op(SpvId opcode)
+{
+ switch (opcode) {
+ return true;
+ return false;
+ }
+}
+
+static void
+spirv_copy_sections(char **binaries, const unsigned *lengths,
+ unsigned *indices, unsigned num_binaries,
+ char *merged_binary, unsigned *merged_index,
+ bool (*check_section)(SpvId))
+{
+ unsigned i = 0u, j = 0u;
+ unsigned length = 0u, prev_j = 0u, index = 0u;
+ const char *binary = NULL;
+ unsigned opcode = 0u, word_count = 0u;
+
+ for (i = 0u; i < num_binaries; ++i) {
+ binary = binaries[i];
+ length = lengths[i];
+ index = indices[i];
+ prev_j = j = index;
+ while (j < length) {
+ opcode = spirv_get_opcode(binary, j, &word_count);
+
+ prev_j = j;
+
+ if (!check_section(opcode))
+ break;
+
+ j += word_count;
+ }
+ if (j == length)
+ prev_j = j;
+
+ memcpy(&merged_binary[*merged_index * sizeof(spirv_word)],
+ &binary[index * sizeof(spirv_word)],
+ (prev_j - index) * sizeof(spirv_word));
+ *merged_index += prev_j - index;
+ indices[i] = j;
+ }
+}
+
+static int
+spirv_merge_modules(char **binaries, const unsigned *lengths,
+ unsigned num_binaries, unsigned max_index, unsigned version,
+ char *merged_binary, bool create_library,
+ unsigned *final_length, char **msg,
+ unsigned *msg_length)
+{
+ unsigned *indices = (unsigned *) malloc(num_binaries * sizeof(unsigned));
+ unsigned i = 0u, j = 0u, merged_index = 0u;
+ unsigned length = 0u, prev_j = 0u, index = 0u, function_begin = 0u;
+ int ret = 0;
+ const char *binary = NULL;
+ unsigned opcode = 0u, word_count = 0u;
+ SpvAddressingModel addressing_model, addressing_model_tmp;
+ const char *model_str = NULL, *model_tmp_str = NULL;
+ SpvMemoryModel memory_model, memory_model_tmp;
+
+ struct util_hash_table *exports = util_hash_table_create(&spirv_spvid_hash,
+ &spirv_spvid_compare);
+ struct util_hash_table *imports = util_hash_table_create(&spirv_spvid_hash,
+ &spirv_spvid_compare);
+ struct util_hash_table *link_table = util_hash_table_create(&spirv_spvid_hash,
+ &spirv_spvid_compare);
+
+ /* Generate new header */
+ spirv_set_word(merged_binary, 0u, SpvMagicNumber);
+ spirv_set_word(merged_binary, 1u, version);
+ spirv_set_word(merged_binary, 2u, 0u);
+ spirv_set_word(merged_binary, 3u, max_index);
+ spirv_set_word(merged_binary, 4u, 0u);
+ merged_index += 5u;
+
+ for (i = 0u; i < num_binaries; ++i)
+ indices[i] = 5u;
+
+ /* Copy OpCapabilities */
+ spirv_copy_sections(binaries, lengths, indices, num_binaries, merged_binary,
+ &merged_index, &spirv_is_capability_op);
+
+ /* Copy OpExtensions */
+ spirv_copy_sections(binaries, lengths, indices, num_binaries, merged_binary,
+ &merged_index, &spirv_is_extension_op);
+
+ /* Copy OpExtInstImport */
+ spirv_copy_sections(binaries, lengths, indices, num_binaries, merged_binary,
+ &merged_index, &spirv_is_ext_inst_import_op);
+
+ /* Merge OpMemoryModel */
+ for (i = 0u; i < num_binaries; ++i) {
+ binary = binaries[i];
+ index = indices[i];
+ opcode = spirv_get_opcode(binary, index, &word_count);
+
+ assert(opcode == SpvOpMemoryModel);
+ if (i == 0u) {
+ addressing_model = (SpvAddressingModel) spirv_get_word(binary,
+ index + 1u);
+ memory_model = (SpvMemoryModel) spirv_get_word(binary, index + 2u);
+ } else {
+ addressing_model_tmp = (SpvAddressingModel) spirv_get_word(binary,
+ index + 1u);
+ memory_model_tmp = (SpvMemoryModel) spirv_get_word(binary, index + 2u);
+ if (addressing_model != addressing_model_tmp) {
+ model_str = addressing_model_to_string(addressing_model);
+ model_tmp_str = addressing_model_to_string(addressing_model_tmp);
+ *msg_length = snprintf(NULL, 0,
+ "SPIR-V linker: Inconsistent addressing models: '%s' for binary 0 and '%s' for binary %d\n",
+ model_str, model_tmp_str, i) + 1;
+ *msg = (char *) malloc(*msg_length);
+ snprintf(*msg, *msg_length,
+ "SPIR-V linker: Inconsistent addressing models: '%s' for binary 0 and '%s' for binary %d\n",
+ model_str, model_tmp_str, i);
+ ret = -1;
+ goto end;
+ }
+ if (memory_model != memory_model_tmp) {
+ model_str = memory_model_to_string(memory_model);
+ model_tmp_str = memory_model_to_string(memory_model_tmp);
+ *msg_length = snprintf(NULL, 0,
+ "SPIR-V linker: Inconsistent memory models: '%s' for binary 0 and '%s' for binary %d\n",
+ model_str, model_tmp_str, i) + 1;
+ *msg = (char *) malloc(*msg_length);
+ snprintf(*msg, *msg_length,
+ "SPIR-V linker: Inconsistent memory models: '%s' for binary 0 and '%s' for binary %d\n",
+ model_str, model_tmp_str, i);
+ ret = -1;
+ goto end;
+ }
+ }
+
+ indices[i] = index + 3u;
+ }
+ spirv_set_word(merged_binary, merged_index, (3u << SpvWordCountShift) + 14);
+ spirv_set_word(merged_binary, merged_index + 1u, addressing_model);
+ spirv_set_word(merged_binary, merged_index + 2u, memory_model);
+ merged_index += 3u;
+
+ /* Copy OpEntryPoint */
+ spirv_copy_sections(binaries, lengths, indices, num_binaries, merged_binary,
+ &merged_index, &spirv_is_entry_point_op);
+
+ /* Copy OpExecutionMode */
+ spirv_copy_sections(binaries, lengths, indices, num_binaries, merged_binary,
+ &merged_index, &spirv_is_execution_mode_op);
+
+ /* Copy debug a) */
+ spirv_copy_sections(binaries, lengths, indices, num_binaries, merged_binary,
+ &merged_index, &spirv_is_debug_a_op);
+ /* Copy debug b) */
+ spirv_copy_sections(binaries, lengths, indices, num_binaries, merged_binary,
+ &merged_index, &spirv_is_debug_b_op);
+
+ /* Copy annotations */
+ for (i = 0u; i < num_binaries; ++i) {
+ binary = binaries[i];
+ length = lengths[i];
+ index = indices[i];
+ j = index;
+ prev_j = j;
+ while (j < length) {
+ opcode = spirv_get_opcode(binary, j, &word_count);
+
+ prev_j = j;
+
+ if (opcode != SpvOpDecorate && opcode != SpvOpMemberDecorate &&
+ opcode != SpvOpGroupDecorate && opcode != SpvOpGroupMemberDecorate &&
+ opcode != SpvOpDecorationGroup)
+ break;
+
+ j += word_count;
+
+ if (opcode != SpvOpDecorate)
+ continue;
+ const SpvDecoration decoration = spirv_get_word(binary, prev_j + 2u);
+ if (decoration != SpvDecorationLinkageAttributes)
+ continue;
+
+ const SpvId id = spirv_get_word(binary, prev_j + 1u);
+ const SpvLinkageType linkage_type = spirv_get_word(binary, prev_j + (word_count - 1u));
+ if (linkage_type == SpvLinkageTypeExport)
+ util_hash_table_set(exports, UINT_TO_PTR(id),
+ spirv_get_string(binary, prev_j + 3u));
+ else
+ util_hash_table_set(imports, UINT_TO_PTR(id),
+ spirv_get_string(binary, prev_j + 3u));
+
+ /* Remove exports from SPIR-V if we are not building a library */
+ if (create_library && linkage_type == SpvLinkageTypeExport)
+ continue;
+
+ memcpy(&merged_binary[merged_index * sizeof(spirv_word)],
+ &binary[index * sizeof(spirv_word)],
+ (prev_j - index) * sizeof(spirv_word));
+ merged_index += prev_j - index;
+ index = j;
+ }
+ if (j == length)
+ prev_j = j;
+
+ memcpy(&merged_binary[merged_index * sizeof(spirv_word)],
+ &binary[index * sizeof(spirv_word)],
+ (prev_j - index) * sizeof(spirv_word));
+ merged_index += prev_j - index;
+ indices[i] = j;
+ }
+
+ struct spirv_link_data link_data = {
+ .exports = exports,
+ .link_table = link_table,
+ .linkage_uid = NULL,
+ .export_id = 0u,
+ .msg = msg,
+ .msg_length = msg_length
+ };
+ ret = util_hash_table_foreach(imports, &generate_link_table, &link_data);
+ if (ret != PIPE_OK)
+ goto end;
+
+ /* Copy types/constants/global variables/OpUndef */
+ for (i = 0u; i < num_binaries; ++i) {
+ binary = binaries[i];
+ length = lengths[i];
+ index = indices[i];
+ j = index;
+ prev_j = j;
+ while (j < length) {
+ opcode = spirv_get_opcode(binary, j, &word_count);
+
+ prev_j = j;
+
+ SpvStorageClass storage_type = spirv_get_word(binary, j + 3u);
+ if (!spirv_is_type_constant_op(opcode) ||
+ (opcode == SpvOpVariable &&
+ storage_type == SpvStorageClassFunction))
+ break;
+
+ j += word_count;
+
+ /* Remove imported variables */
+ void *tmp = UINT_TO_PTR(spirv_get_word(binary, prev_j + 2u));
+ if (opcode == SpvOpVariable &&
+ util_hash_table_get(link_table, tmp) != NULL) {
+ memcpy(&merged_binary[merged_index * sizeof(spirv_word)],
+ &binary[index * sizeof(spirv_word)],
+ (prev_j - index) * sizeof(spirv_word));
+ merged_index += prev_j - index;
+ index = j;
+ }
+ }
+ if (j == length)
+ prev_j = j;
+
+ memcpy(&merged_binary[merged_index * sizeof(spirv_word)],
+ &binary[index * sizeof(spirv_word)],
+ (prev_j - index) * sizeof(spirv_word));
+ merged_index += prev_j - index;
+ indices[i] = j;
+ }
+
+ /* Skip function declarations */
+ for (i = 0u; i < num_binaries; ++i) {
+ binary = binaries[i];
+ length = lengths[i];
+ index = indices[i];
+ j = index;
+ function_begin = j;
+ while (j < length) {
+ opcode = spirv_get_opcode(binary, j, &word_count);
+
+ if (opcode == SpvOpFunction)
+ function_begin = j;
+
+ /* This was not a prototype: revert to the beginning of the function */
+ if (opcode != SpvOpFunction && opcode != SpvOpFunctionParameter &&
+ opcode != SpvOpFunctionEnd) {
+ j = function_begin;
+ break;
+ }
+
+ j += word_count;
+ }
+
+ indices[i] = j;
+ }
+
+ /* Copy function defintions */
+ for (i = 0u; i < num_binaries; ++i) {
+ binary = binaries[i];
+ length = lengths[i];
+ index = indices[i];
+ memcpy(&merged_binary[merged_index * sizeof(spirv_word)],
+ &binary[index * sizeof(spirv_word)],
+ (length - index) * sizeof(spirv_word));
+ merged_index += length - index;
+ }
+
+ /* Link everything together */
+ ret = spirv_transform_binary(merged_binary, merged_index, &spirv_link_ids,
+ link_table);
+ if (ret)
+ goto end;
+
+ if (final_length)
+ *final_length = merged_index;
+
+ free(indices);
+ util_hash_table_destroy(imports);
+ util_hash_table_destroy(exports);
+ util_hash_table_destroy(link_table);
+ return ret;
+}
+
+const char *
+spirv_link_binaries(const char **binaries, const unsigned *binaries_word_count,
+ unsigned num_binaries, bool create_library,
+ unsigned *linked_word_count, char **msg,
+ unsigned *length)
+{
+ char **tmp_binaries = NULL;
+ char *linked_binary = NULL;
+ const char *binary = NULL;
+ char *tmp_binary = NULL;
+ unsigned total_word_count = 0u, binary_max_id = 0u, id_upper_bound = 0u;
+ unsigned word_count = 0u, byte_count = 0u;
+ int i = 0, j = 0, ret = 0;
+ unsigned max_version = 0u, version = 0u;
+
+ /* As we need to bump IDs in each module, so as they do not conflict as we
+ * merge them together, we need to make a copy which we can modify.
+ */
+ tmp_binaries = (char **) malloc(num_binaries * sizeof(char *));
+
+ for (i = 0; i < (int) num_binaries; ++i) {
+ word_count = binaries_word_count[i];
+ binary = binaries[i];
+ byte_count = word_count * sizeof(spirv_word);
+
+ version = spirv_get_word(binary, 1u);
+ if (version > SpvVersion) {
+ *length = snprintf(NULL, 0,
+ "SPIR-V linker: Binary %d uses an unsupported SPIR-V version: %u.%u\n",
+ i, ((version >> 16u) & 0xff),
+ ((version >> 8u) & 0xff)) + 1;
+ *msg = (char *) malloc(*length);
+ snprintf(*msg, *length,
+ "SPIR-V linker: Binary %d uses an unsupported SPIR-V version: %u.%u\n",
+ i, ((version >> 16u) & 0xff), ((version >> 8u) & 0xff));
+ goto error;
+ }
+ if (version > max_version)
+ max_version = version;
+
+ tmp_binary = (char *) malloc(byte_count);
+ if (!tmp_binary)
+ goto error;
+ memcpy(tmp_binary, binary, byte_count);
+ tmp_binaries[i] = tmp_binary;
+
+ binary_max_id = spirv_get_word(binary, 3u) - 1u;
+
+ ret = spirv_transform_binary(tmp_binary, word_count, &spirv_bump_id,
+ UINT_TO_PTR(id_upper_bound));
+ if (ret)
+ goto error2;
+
+ id_upper_bound += binary_max_id;
+ total_word_count += word_count;
+ }
+
+ /* id_upper_bound is currently equal to the highest id being used, so add
+ * one to get the strict upper bound
+ */
+ ++id_upper_bound;
+
+ linked_binary = (char *) malloc(total_word_count * sizeof(spirv_word));
+ ret = spirv_merge_modules(tmp_binaries, binaries_word_count, num_binaries,
+ id_upper_bound, max_version, linked_binary,
+ create_library, &total_word_count, msg, length);
+ if (ret)
+ goto error3;
+
+ if (linked_word_count)
+ *linked_word_count = total_word_count;
+
+ for (i = 0; i < (int) num_binaries; ++i)
+ free(tmp_binaries[i]);
+ free(tmp_binaries);
+
+ return linked_binary;
+
+ free(linked_binary);
+ if (i < (int) num_binaries)
+ free(tmp_binaries[i]);
+ for (j = 0; j < i; ++j)
+ free(tmp_binaries[j]);
+ free(tmp_binaries);
+ return NULL;
+}
diff --git a/src/gallium/auxiliary/spirv/spirv_linker.h b/src/gallium/auxiliary/spirv/spirv_linker.h
new file mode 100644
index 0000000000..0090c8071f
--- /dev/null
+++ b/src/gallium/auxiliary/spirv/spirv_linker.h
@@ -0,0 +1,67 @@
+/**************************************************************************
+ *
+ * Copyright 2017 Pierre Moreau
+ * All Rights Reserved.
+ *
+ * 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, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) 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 NON-INFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS AND/OR ITS SUPPLIERS 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.
+ *
+ **************************************************************************/
+
+#ifndef SPIRV_LINKER_H
+#define SPIRV_LINKER_H
+
+#include <stdbool.h>
+
+#if defined __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Link SPIR-V binaries into a SPIR-V library or a SPIR-V executable.
+ *
+ * The given SPIR-V modules are expected to
+ * - have been validated;
+ * - use the same endianness as the CPU.
+ *
+ * import linkage attributes, only export) or an
+ * executable (a SPIR-V module with no linkage attributes)
+ * making up the resulting linked SPIR-V binary
+ * otherwise
+ */
+const char *
+spirv_link_binaries(const char **binaries, const unsigned *binaries_word_count,
+ unsigned num_binaries, bool create_library,
+ unsigned *linked_word_count, char **msg,
+ unsigned *length);
+
+#if defined __cplusplus
+}
+#endif
+
+#endif /* SPIRV_LINKER_H */
--
2.12.2
_______________________________________________
mesa-dev mailing list
https://lists.freedesktop.org/mailman/listinfo/mesa-dev
Pierre Moreau
2017-05-03 21:56:51 UTC
Permalink
Raw Message
Signed-off-by: Pierre Moreau <***@free.fr>
---
src/gallium/include/pipe/p_defines.h | 1 +
1 file changed, 1 insertion(+)

diff --git a/src/gallium/include/pipe/p_defines.h b/src/gallium/include/pipe/p_defines.h
index ce2cfd1d88..71991383c2 100644
--- a/src/gallium/include/pipe/p_defines.h
+++ b/src/gallium/include/pipe/p_defines.h
@@ -850,6 +850,7 @@ enum pipe_shader_ir
PIPE_SHADER_IR_LLVM,
PIPE_SHADER_IR_NATIVE,
PIPE_SHADER_IR_NIR,
+ PIPE_SHADER_IR_SPIRV
};

/**
--
2.12.2
Pierre Moreau
2017-05-03 21:56:53 UTC
Permalink
Raw Message
Signed-off-by: Pierre Moreau <***@free.fr>
---
include/CL/cl.h | 6 ++++++
include/CL/cl_platform.h | 1 +
2 files changed, 7 insertions(+)

diff --git a/include/CL/cl.h b/include/CL/cl.h
index 316565d6e4..44d7aedc3e 100644
--- a/include/CL/cl.h
+++ b/include/CL/cl.h
@@ -757,6 +757,12 @@ clCreateProgramWithBuiltInKernels(cl_context /* context */,
const char * /* kernel_names */,
cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_2;

+extern CL_API_ENTRY cl_program CL_API_CALL
+clCreateProgramWithIL(cl_context /* context */,
+ const void* /* il */,
+ size_t /* length */,
+ cl_int* /* errcode_ret */) CL_API_SUFFIX__VERSION_2_1;
+
extern CL_API_ENTRY cl_int CL_API_CALL
clRetainProgram(cl_program /* program */) CL_API_SUFFIX__VERSION_1_0;

diff --git a/include/CL/cl_platform.h b/include/CL/cl_platform.h
index 7f6f5e8a74..105d3cc1f0 100644
--- a/include/CL/cl_platform.h
+++ b/include/CL/cl_platform.h
@@ -75,6 +75,7 @@ extern "C" {
#define CL_EXT_SUFFIX__VERSION_1_1
#define CL_API_SUFFIX__VERSION_1_2
#define CL_EXT_SUFFIX__VERSION_1_2
+ #define CL_API_SUFFIX__VERSION_2_1

#ifdef __GNUC__
#ifdef CL_USE_DEPRECATED_OPENCL_1_0_APIS
--
2.12.2
Pierre Moreau
2017-05-03 21:56:52 UTC
Permalink
Raw Message
Besides parsing all the opcodes until reaching the EOF character, there
is no way to compute the size of a SPIR-V binary. Therefore, it is
easier to pass it along the SPIR-V binary in pipe_compute_state.

Signed-off-by: Pierre Moreau <***@free.fr>
---
src/gallium/include/pipe/p_state.h | 1 +
1 file changed, 1 insertion(+)

diff --git a/src/gallium/include/pipe/p_state.h b/src/gallium/include/pipe/p_state.h
index ce9ca34d29..1f8fdf530f 100644
--- a/src/gallium/include/pipe/p_state.h
+++ b/src/gallium/include/pipe/p_state.h
@@ -810,6 +810,7 @@ struct pipe_compute_state
{
enum pipe_shader_ir ir_type; /**< IR type contained in prog. */
const void *prog; /**< Compute program to be executed. */
+ unsigned prog_num_bytes; /**< Program size in bytes, used by SPIR-V. */
unsigned req_local_mem; /**< Required size of the LOCAL resource. */
unsigned req_private_mem; /**< Required size of the PRIVATE resource. */
unsigned req_input_mem; /**< Required size of the INPUT resource. */
--
2.12.2
Francisco Jerez
2017-05-05 19:17:46 UTC
Permalink
Raw Message
Post by Pierre Moreau
Besides parsing all the opcodes until reaching the EOF character, there
is no way to compute the size of a SPIR-V binary. Therefore, it is
easier to pass it along the SPIR-V binary in pipe_compute_state.
LLVM IR programs use pipe_llvm_program_header in order to provide the
same information. Not necessarily opposed to doing it this way but I
think it would make sense for the SPIR-V and LLVM paths to use the same
mechanism.
Post by Pierre Moreau
---
src/gallium/include/pipe/p_state.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/gallium/include/pipe/p_state.h b/src/gallium/include/pipe/p_state.h
index ce9ca34d29..1f8fdf530f 100644
--- a/src/gallium/include/pipe/p_state.h
+++ b/src/gallium/include/pipe/p_state.h
@@ -810,6 +810,7 @@ struct pipe_compute_state
{
enum pipe_shader_ir ir_type; /**< IR type contained in prog. */
const void *prog; /**< Compute program to be executed. */
+ unsigned prog_num_bytes; /**< Program size in bytes, used by SPIR-V. */
unsigned req_local_mem; /**< Required size of the LOCAL resource. */
unsigned req_private_mem; /**< Required size of the PRIVATE resource. */
unsigned req_input_mem; /**< Required size of the INPUT resource. */
--
2.12.2
_______________________________________________
mesa-dev mailing list
https://lists.freedesktop.org/mailman/listinfo/mesa-dev
Pierre Moreau
2017-05-06 15:11:26 UTC
Permalink
Raw Message
This seems like a good idea. I changed the code locally to use
`pipe_llvm_program_header` instead; it does not seem worth it to create a
`pipe_spirv_program_header`, since it would only contain a `num_bytes`
attributes for now.
Pierre Moreau
2017-05-03 21:56:54 UTC
Permalink
Raw Message
Signed-off-by: Pierre Moreau <***@free.fr>
---
include/CL/cl.h | 1 +
1 file changed, 1 insertion(+)

diff --git a/include/CL/cl.h b/include/CL/cl.h
index 44d7aedc3e..cc8d7ddf60 100644
--- a/include/CL/cl.h
+++ b/include/CL/cl.h
@@ -455,6 +455,7 @@ typedef struct _cl_buffer_region {
#define CL_PROGRAM_BINARIES 0x1166
#define CL_PROGRAM_NUM_KERNELS 0x1167
#define CL_PROGRAM_KERNEL_NAMES 0x1168
+#define CL_PROGRAM_IL 0x1169

/* cl_program_build_info */
#define CL_PROGRAM_BUILD_STATUS 0x1181
--
2.12.2
Pierre Moreau
2017-05-03 21:56:55 UTC
Permalink
Raw Message
Signed-off-by: Pierre Moreau <***@free.fr>
---
configure.ac | 16 ++++++++++++++++
1 file changed, 16 insertions(+)

diff --git a/configure.ac b/configure.ac
index ba042791ad..602aeb279d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2064,6 +2064,11 @@ AC_ARG_WITH([clang-libdir],

PKG_CHECK_EXISTS([libclc], [have_libclc=yes], [have_libclc=no])

+AC_LANG_PUSH([C++])
+AC_SEARCH_LIBS([_ZNK8spvtools10SpirvTools8ValidateEPKjm], [SPIRV-Tools], [have_spirv_tools=yes], [have_spirv_tools=no])
+AC_CHECK_HEADER([spirv-tools/libspirv.hpp], [have_spirv_tools_headers=yes; break;])
+AC_LANG_POP([C++])
+
if test "x$enable_opencl" = xyes; then
if test -z "$with_gallium_drivers"; then
AC_MSG_ERROR([cannot enable OpenCL without Gallium])
@@ -2123,6 +2128,17 @@ if test "x$enable_opencl" = xyes; then
llvm_add_component "objcarcopts" "opencl"
llvm_add_component "profiledata" "opencl"

+ if test "x$have_spirv_tools_headers" != xyes; then
+ AC_MSG_ERROR([Failed to find spirv-tools/libspirv.hpp, which is
+ required to build clover])
+ fi
+
+ if test "x$have_spirv_tools" != xyes; then
+ AC_MSG_ERROR([Failed to find a library implementing
+ _ZNK8spvtools10SpirvTools8ValidateEPKjm which is required
+ to build clover])
+ fi
+
dnl Check for Clang internal headers
if test -z "$CLANG_LIBDIR"; then
CLANG_LIBDIR=${LLVM_LIBDIR}
--
2.12.2
Pierre Moreau
2017-05-03 21:56:56 UTC
Permalink
Raw Message
Signed-off-by: Pierre Moreau <***@free.fr>
---
src/gallium/state_trackers/clover/core/kernel.cpp | 1 +
1 file changed, 1 insertion(+)

diff --git a/src/gallium/state_trackers/clover/core/kernel.cpp b/src/gallium/state_trackers/clover/core/kernel.cpp
index 4716705323..328323b6b0 100644
--- a/src/gallium/state_trackers/clover/core/kernel.cpp
+++ b/src/gallium/state_trackers/clover/core/kernel.cpp
@@ -228,6 +228,7 @@ kernel::exec_context::bind(intrusive_ptr<command_queue> _q,

cs.ir_type = q->device().ir_format();
cs.prog = &(msec.data[0]);
+ cs.prog_num_bytes = msec.data.size();
cs.req_local_mem = mem_local;
cs.req_input_mem = input.size();
st = q->pipe->create_compute_state(q->pipe, &cs);
--
2.12.2
Pierre Moreau
2017-05-03 21:56:57 UTC
Permalink
Raw Message
Signed-off-by: Pierre Moreau <***@free.fr>
---
src/gallium/state_trackers/clover/core/device.cpp | 11 +++++++++++
src/gallium/state_trackers/clover/core/device.hpp | 3 +++
2 files changed, 14 insertions(+)

diff --git a/src/gallium/state_trackers/clover/core/device.cpp b/src/gallium/state_trackers/clover/core/device.cpp
index 158c9aa696..52ac5229a3 100644
--- a/src/gallium/state_trackers/clover/core/device.cpp
+++ b/src/gallium/state_trackers/clover/core/device.cpp
@@ -224,6 +224,12 @@ device::ir_format() const {
pipe, PIPE_SHADER_COMPUTE, PIPE_SHADER_CAP_PREFERRED_IR);
}

+cl_uint
+device::supported_irs() const {
+ return (enum pipe_shader_ir) pipe->get_shader_param(
+ pipe, PIPE_SHADER_COMPUTE, PIPE_SHADER_CAP_SUPPORTED_IRS);
+}
+
std::string
device::ir_target() const {
std::vector<char> target = get_compute_param<char>(
@@ -235,3 +241,8 @@ enum pipe_endian
device::endianness() const {
return (enum pipe_endian)pipe->get_param(pipe, PIPE_CAP_ENDIANNESS);
}
+
+bool
+device::supports_ir(cl_uint ir) const {
+ return supported_irs() & (1 << ir);
+}
diff --git a/src/gallium/state_trackers/clover/core/device.hpp b/src/gallium/state_trackers/clover/core/device.hpp
index 94a61d1050..065e788fd3 100644
--- a/src/gallium/state_trackers/clover/core/device.hpp
+++ b/src/gallium/state_trackers/clover/core/device.hpp
@@ -74,9 +74,12 @@ namespace clover {
std::string device_name() const;
std::string vendor_name() const;
enum pipe_shader_ir ir_format() const;
+ cl_uint supported_irs() const;
std::string ir_target() const;
enum pipe_endian endianness() const;

+ bool supports_ir(cl_uint ir) const;
+
friend class command_queue;
friend class root_resource;
friend class hard_event;
--
2.12.2
Pierre Moreau
2017-05-03 21:56:58 UTC
Permalink
Raw Message
Signed-off-by: Pierre Moreau <***@free.fr>
---
.../state_trackers/clover/spirv/spirv.hpp11 | 952 +++++++++++++++++++++
1 file changed, 952 insertions(+)
create mode 100644 src/gallium/state_trackers/clover/spirv/spirv.hpp11

diff --git a/src/gallium/state_trackers/clover/spirv/spirv.hpp11 b/src/gallium/state_trackers/clover/spirv/spirv.hpp11
new file mode 100644
index 0000000000..62bb127a8a
--- /dev/null
+++ b/src/gallium/state_trackers/clover/spirv/spirv.hpp11
@@ -0,0 +1,952 @@
+// Copyright (c) 2014-2017 The Khronos Group Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and/or associated documentation files (the "Materials"),
+// to deal in the Materials without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Materials, and to permit persons to whom the
+// Materials are furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Materials.
+//
+// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS
+// STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND
+// HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/
+//
+// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS
+// IN THE MATERIALS.
+
+// This header is automatically generated by the same tool that creates
+// the Binary Section of the SPIR-V specification.
+
+// Enumeration tokens for SPIR-V, in various styles:
+// C, C++, C++11, JSON, Lua, Python
+//
+// - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL
+// - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL
+// - C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL
+// - Lua will use tables, e.g.: spv.SourceLanguage.GLSL
+// - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL']
+//
+// Some tokens act like mask values, which can be OR'd together,
+// while others are mutually exclusive. The mask-like ones have
+// "Mask" in their name, and a parallel enum that has the shift
+// amount (1 << x) for each corresponding enumerant.
+
+#ifndef spirv_HPP
+#define spirv_HPP
+
+namespace spv {
+
+typedef unsigned int Id;
+
+#define SPV_VERSION 0x10000
+#define SPV_REVISION 10
+
+static const unsigned int MagicNumber = 0x07230203;
+static const unsigned int Version = 0x00010000;
+static const unsigned int Revision = 10;
+static const unsigned int OpCodeMask = 0xffff;
+static const unsigned int WordCountShift = 16;
+
+enum class SourceLanguage : unsigned {
+ Unknown = 0,
+ ESSL = 1,
+ GLSL = 2,
+ OpenCL_C = 3,
+ OpenCL_CPP = 4,
+ Max = 0x7fffffff,
+};
+
+enum class ExecutionModel : unsigned {
+ Vertex = 0,
+ TessellationControl = 1,
+ TessellationEvaluation = 2,
+ Geometry = 3,
+ Fragment = 4,
+ GLCompute = 5,
+ Kernel = 6,
+ Max = 0x7fffffff,
+};
+
+enum class AddressingModel : unsigned {
+ Logical = 0,
+ Physical32 = 1,
+ Physical64 = 2,
+ Max = 0x7fffffff,
+};
+
+enum class MemoryModel : unsigned {
+ Simple = 0,
+ GLSL450 = 1,
+ OpenCL = 2,
+ Max = 0x7fffffff,
+};
+
+enum class ExecutionMode : unsigned {
+ Invocations = 0,
+ SpacingEqual = 1,
+ SpacingFractionalEven = 2,
+ SpacingFractionalOdd = 3,
+ VertexOrderCw = 4,
+ VertexOrderCcw = 5,
+ PixelCenterInteger = 6,
+ OriginUpperLeft = 7,
+ OriginLowerLeft = 8,
+ EarlyFragmentTests = 9,
+ PointMode = 10,
+ Xfb = 11,
+ DepthReplacing = 12,
+ DepthGreater = 14,
+ DepthLess = 15,
+ DepthUnchanged = 16,
+ LocalSize = 17,
+ LocalSizeHint = 18,
+ InputPoints = 19,
+ InputLines = 20,
+ InputLinesAdjacency = 21,
+ Triangles = 22,
+ InputTrianglesAdjacency = 23,
+ Quads = 24,
+ Isolines = 25,
+ OutputVertices = 26,
+ OutputPoints = 27,
+ OutputLineStrip = 28,
+ OutputTriangleStrip = 29,
+ VecTypeHint = 30,
+ ContractionOff = 31,
+ Max = 0x7fffffff,
+};
+
+enum class StorageClass : unsigned {
+ UniformConstant = 0,
+ Input = 1,
+ Uniform = 2,
+ Output = 3,
+ Workgroup = 4,
+ CrossWorkgroup = 5,
+ Private = 6,
+ Function = 7,
+ Generic = 8,
+ PushConstant = 9,
+ AtomicCounter = 10,
+ Image = 11,
+ Max = 0x7fffffff,
+};
+
+enum class Dim : unsigned {
+ Dim1D = 0,
+ Dim2D = 1,
+ Dim3D = 2,
+ Cube = 3,
+ Rect = 4,
+ Buffer = 5,
+ SubpassData = 6,
+ Max = 0x7fffffff,
+};
+
+enum class SamplerAddressingMode : unsigned {
+ None = 0,
+ ClampToEdge = 1,
+ Clamp = 2,
+ Repeat = 3,
+ RepeatMirrored = 4,
+ Max = 0x7fffffff,
+};
+
+enum class SamplerFilterMode : unsigned {
+ Nearest = 0,
+ Linear = 1,
+ Max = 0x7fffffff,
+};
+
+enum class ImageFormat : unsigned {
+ Unknown = 0,
+ Rgba32f = 1,
+ Rgba16f = 2,
+ R32f = 3,
+ Rgba8 = 4,
+ Rgba8Snorm = 5,
+ Rg32f = 6,
+ Rg16f = 7,
+ R11fG11fB10f = 8,
+ R16f = 9,
+ Rgba16 = 10,
+ Rgb10A2 = 11,
+ Rg16 = 12,
+ Rg8 = 13,
+ R16 = 14,
+ R8 = 15,
+ Rgba16Snorm = 16,
+ Rg16Snorm = 17,
+ Rg8Snorm = 18,
+ R16Snorm = 19,
+ R8Snorm = 20,
+ Rgba32i = 21,
+ Rgba16i = 22,
+ Rgba8i = 23,
+ R32i = 24,
+ Rg32i = 25,
+ Rg16i = 26,
+ Rg8i = 27,
+ R16i = 28,
+ R8i = 29,
+ Rgba32ui = 30,
+ Rgba16ui = 31,
+ Rgba8ui = 32,
+ R32ui = 33,
+ Rgb10a2ui = 34,
+ Rg32ui = 35,
+ Rg16ui = 36,
+ Rg8ui = 37,
+ R16ui = 38,
+ R8ui = 39,
+ Max = 0x7fffffff,
+};
+
+enum class ImageChannelOrder : unsigned {
+ R = 0,
+ A = 1,
+ RG = 2,
+ RA = 3,
+ RGB = 4,
+ RGBA = 5,
+ BGRA = 6,
+ ARGB = 7,
+ Intensity = 8,
+ Luminance = 9,
+ Rx = 10,
+ RGx = 11,
+ RGBx = 12,
+ Depth = 13,
+ DepthStencil = 14,
+ sRGB = 15,
+ sRGBx = 16,
+ sRGBA = 17,
+ sBGRA = 18,
+ ABGR = 19,
+ Max = 0x7fffffff,
+};
+
+enum class ImageChannelDataType : unsigned {
+ SnormInt8 = 0,
+ SnormInt16 = 1,
+ UnormInt8 = 2,
+ UnormInt16 = 3,
+ UnormShort565 = 4,
+ UnormShort555 = 5,
+ UnormInt101010 = 6,
+ SignedInt8 = 7,
+ SignedInt16 = 8,
+ SignedInt32 = 9,
+ UnsignedInt8 = 10,
+ UnsignedInt16 = 11,
+ UnsignedInt32 = 12,
+ HalfFloat = 13,
+ Float = 14,
+ UnormInt24 = 15,
+ UnormInt101010_2 = 16,
+ Max = 0x7fffffff,
+};
+
+enum class ImageOperandsShift : unsigned {
+ Bias = 0,
+ Lod = 1,
+ Grad = 2,
+ ConstOffset = 3,
+ Offset = 4,
+ ConstOffsets = 5,
+ Sample = 6,
+ MinLod = 7,
+ Max = 0x7fffffff,
+};
+
+enum class ImageOperandsMask : unsigned {
+ MaskNone = 0,
+ Bias = 0x00000001,
+ Lod = 0x00000002,
+ Grad = 0x00000004,
+ ConstOffset = 0x00000008,
+ Offset = 0x00000010,
+ ConstOffsets = 0x00000020,
+ Sample = 0x00000040,
+ MinLod = 0x00000080,
+};
+
+enum class FPFastMathModeShift : unsigned {
+ NotNaN = 0,
+ NotInf = 1,
+ NSZ = 2,
+ AllowRecip = 3,
+ Fast = 4,
+ Max = 0x7fffffff,
+};
+
+enum class FPFastMathModeMask : unsigned {
+ MaskNone = 0,
+ NotNaN = 0x00000001,
+ NotInf = 0x00000002,
+ NSZ = 0x00000004,
+ AllowRecip = 0x00000008,
+ Fast = 0x00000010,
+};
+
+enum class FPRoundingMode : unsigned {
+ RTE = 0,
+ RTZ = 1,
+ RTP = 2,
+ RTN = 3,
+ Max = 0x7fffffff,
+};
+
+enum class LinkageType : unsigned {
+ Export = 0,
+ Import = 1,
+ Max = 0x7fffffff,
+};
+
+enum class AccessQualifier : unsigned {
+ ReadOnly = 0,
+ WriteOnly = 1,
+ ReadWrite = 2,
+ Max = 0x7fffffff,
+};
+
+enum class FunctionParameterAttribute : unsigned {
+ Zext = 0,
+ Sext = 1,
+ ByVal = 2,
+ Sret = 3,
+ NoAlias = 4,
+ NoCapture = 5,
+ NoWrite = 6,
+ NoReadWrite = 7,
+ Max = 0x7fffffff,
+};
+
+enum class Decoration : unsigned {
+ RelaxedPrecision = 0,
+ SpecId = 1,
+ Block = 2,
+ BufferBlock = 3,
+ RowMajor = 4,
+ ColMajor = 5,
+ ArrayStride = 6,
+ MatrixStride = 7,
+ GLSLShared = 8,
+ GLSLPacked = 9,
+ CPacked = 10,
+ BuiltIn = 11,
+ NoPerspective = 13,
+ Flat = 14,
+ Patch = 15,
+ Centroid = 16,
+ Sample = 17,
+ Invariant = 18,
+ Restrict = 19,
+ Aliased = 20,
+ Volatile = 21,
+ Constant = 22,
+ Coherent = 23,
+ NonWritable = 24,
+ NonReadable = 25,
+ Uniform = 26,
+ SaturatedConversion = 28,
+ Stream = 29,
+ Location = 30,
+ Component = 31,
+ Index = 32,
+ Binding = 33,
+ DescriptorSet = 34,
+ Offset = 35,
+ XfbBuffer = 36,
+ XfbStride = 37,
+ FuncParamAttr = 38,
+ FPRoundingMode = 39,
+ FPFastMathMode = 40,
+ LinkageAttributes = 41,
+ NoContraction = 42,
+ InputAttachmentIndex = 43,
+ Alignment = 44,
+ OverrideCoverageNV = 5248,
+ PassthroughNV = 5250,
+ ViewportRelativeNV = 5252,
+ SecondaryViewportRelativeNV = 5256,
+ Max = 0x7fffffff,
+};
+
+enum class BuiltIn : unsigned {
+ Position = 0,
+ PointSize = 1,
+ ClipDistance = 3,
+ CullDistance = 4,
+ VertexId = 5,
+ InstanceId = 6,
+ PrimitiveId = 7,
+ InvocationId = 8,
+ Layer = 9,
+ ViewportIndex = 10,
+ TessLevelOuter = 11,
+ TessLevelInner = 12,
+ TessCoord = 13,
+ PatchVertices = 14,
+ FragCoord = 15,
+ PointCoord = 16,
+ FrontFacing = 17,
+ SampleId = 18,
+ SamplePosition = 19,
+ SampleMask = 20,
+ FragDepth = 22,
+ HelperInvocation = 23,
+ NumWorkgroups = 24,
+ WorkgroupSize = 25,
+ WorkgroupId = 26,
+ LocalInvocationId = 27,
+ GlobalInvocationId = 28,
+ LocalInvocationIndex = 29,
+ WorkDim = 30,
+ GlobalSize = 31,
+ EnqueuedWorkgroupSize = 32,
+ GlobalOffset = 33,
+ GlobalLinearId = 34,
+ SubgroupSize = 36,
+ SubgroupMaxSize = 37,
+ NumSubgroups = 38,
+ NumEnqueuedSubgroups = 39,
+ SubgroupId = 40,
+ SubgroupLocalInvocationId = 41,
+ VertexIndex = 42,
+ InstanceIndex = 43,
+ SubgroupEqMaskKHR = 4416,
+ SubgroupGeMaskKHR = 4417,
+ SubgroupGtMaskKHR = 4418,
+ SubgroupLeMaskKHR = 4419,
+ SubgroupLtMaskKHR = 4420,
+ BaseVertex = 4424,
+ BaseInstance = 4425,
+ DrawIndex = 4426,
+ DeviceIndex = 4438,
+ ViewIndex = 4440,
+ ViewportMaskNV = 5253,
+ SecondaryPositionNV = 5257,
+ SecondaryViewportMaskNV = 5258,
+ PositionPerViewNV = 5261,
+ ViewportMaskPerViewNV = 5262,
+ Max = 0x7fffffff,
+};
+
+enum class SelectionControlShift : unsigned {
+ Flatten = 0,
+ DontFlatten = 1,
+ Max = 0x7fffffff,
+};
+
+enum class SelectionControlMask : unsigned {
+ MaskNone = 0,
+ Flatten = 0x00000001,
+ DontFlatten = 0x00000002,
+};
+
+enum class LoopControlShift : unsigned {
+ Unroll = 0,
+ DontUnroll = 1,
+ Max = 0x7fffffff,
+};
+
+enum class LoopControlMask : unsigned {
+ MaskNone = 0,
+ Unroll = 0x00000001,
+ DontUnroll = 0x00000002,
+};
+
+enum class FunctionControlShift : unsigned {
+ Inline = 0,
+ DontInline = 1,
+ Pure = 2,
+ Const = 3,
+ Max = 0x7fffffff,
+};
+
+enum class FunctionControlMask : unsigned {
+ MaskNone = 0,
+ Inline = 0x00000001,
+ DontInline = 0x00000002,
+ Pure = 0x00000004,
+ Const = 0x00000008,
+};
+
+enum class MemorySemanticsShift : unsigned {
+ Acquire = 1,
+ Release = 2,
+ AcquireRelease = 3,
+ SequentiallyConsistent = 4,
+ UniformMemory = 6,
+ SubgroupMemory = 7,
+ WorkgroupMemory = 8,
+ CrossWorkgroupMemory = 9,
+ AtomicCounterMemory = 10,
+ ImageMemory = 11,
+ Max = 0x7fffffff,
+};
+
+enum class MemorySemanticsMask : unsigned {
+ MaskNone = 0,
+ Acquire = 0x00000002,
+ Release = 0x00000004,
+ AcquireRelease = 0x00000008,
+ SequentiallyConsistent = 0x00000010,
+ UniformMemory = 0x00000040,
+ SubgroupMemory = 0x00000080,
+ WorkgroupMemory = 0x00000100,
+ CrossWorkgroupMemory = 0x00000200,
+ AtomicCounterMemory = 0x00000400,
+ ImageMemory = 0x00000800,
+};
+
+enum class MemoryAccessShift : unsigned {
+ Volatile = 0,
+ Aligned = 1,
+ Nontemporal = 2,
+ Max = 0x7fffffff,
+};
+
+enum class MemoryAccessMask : unsigned {
+ MaskNone = 0,
+ Volatile = 0x00000001,
+ Aligned = 0x00000002,
+ Nontemporal = 0x00000004,
+};
+
+enum class Scope : unsigned {
+ CrossDevice = 0,
+ Device = 1,
+ Workgroup = 2,
+ Subgroup = 3,
+ Invocation = 4,
+ Max = 0x7fffffff,
+};
+
+enum class GroupOperation : unsigned {
+ Reduce = 0,
+ InclusiveScan = 1,
+ ExclusiveScan = 2,
+ Max = 0x7fffffff,
+};
+
+enum class KernelEnqueueFlags : unsigned {
+ NoWait = 0,
+ WaitKernel = 1,
+ WaitWorkGroup = 2,
+ Max = 0x7fffffff,
+};
+
+enum class KernelProfilingInfoShift : unsigned {
+ CmdExecTime = 0,
+ Max = 0x7fffffff,
+};
+
+enum class KernelProfilingInfoMask : unsigned {
+ MaskNone = 0,
+ CmdExecTime = 0x00000001,
+};
+
+enum class Capability : unsigned {
+ Matrix = 0,
+ Shader = 1,
+ Geometry = 2,
+ Tessellation = 3,
+ Addresses = 4,
+ Linkage = 5,
+ Kernel = 6,
+ Vector16 = 7,
+ Float16Buffer = 8,
+ Float16 = 9,
+ Float64 = 10,
+ Int64 = 11,
+ Int64Atomics = 12,
+ ImageBasic = 13,
+ ImageReadWrite = 14,
+ ImageMipmap = 15,
+ Pipes = 17,
+ Groups = 18,
+ DeviceEnqueue = 19,
+ LiteralSampler = 20,
+ AtomicStorage = 21,
+ Int16 = 22,
+ TessellationPointSize = 23,
+ GeometryPointSize = 24,
+ ImageGatherExtended = 25,
+ StorageImageMultisample = 27,
+ UniformBufferArrayDynamicIndexing = 28,
+ SampledImageArrayDynamicIndexing = 29,
+ StorageBufferArrayDynamicIndexing = 30,
+ StorageImageArrayDynamicIndexing = 31,
+ ClipDistance = 32,
+ CullDistance = 33,
+ ImageCubeArray = 34,
+ SampleRateShading = 35,
+ ImageRect = 36,
+ SampledRect = 37,
+ GenericPointer = 38,
+ Int8 = 39,
+ InputAttachment = 40,
+ SparseResidency = 41,
+ MinLod = 42,
+ Sampled1D = 43,
+ Image1D = 44,
+ SampledCubeArray = 45,
+ SampledBuffer = 46,
+ ImageBuffer = 47,
+ ImageMSArray = 48,
+ StorageImageExtendedFormats = 49,
+ ImageQuery = 50,
+ DerivativeControl = 51,
+ InterpolationFunction = 52,
+ TransformFeedback = 53,
+ GeometryStreams = 54,
+ StorageImageReadWithoutFormat = 55,
+ StorageImageWriteWithoutFormat = 56,
+ MultiViewport = 57,
+ SubgroupBallotKHR = 4423,
+ DrawParameters = 4427,
+ SubgroupVoteKHR = 4431,
+ StorageUniformBufferBlock16 = 4433,
+ StorageUniform16 = 4434,
+ StoragePushConstant16 = 4435,
+ StorageInputOutput16 = 4436,
+ DeviceGroup = 4437,
+ MultiView = 4439,
+ SampleMaskOverrideCoverageNV = 5249,
+ GeometryShaderPassthroughNV = 5251,
+ ShaderViewportIndexLayerNV = 5254,
+ ShaderViewportMaskNV = 5255,
+ ShaderStereoViewNV = 5259,
+ PerViewAttributesNV = 5260,
+ Max = 0x7fffffff,
+};
+
+enum class Op : unsigned {
+ OpNop = 0,
+ OpUndef = 1,
+ OpSourceContinued = 2,
+ OpSource = 3,
+ OpSourceExtension = 4,
+ OpName = 5,
+ OpMemberName = 6,
+ OpString = 7,
+ OpLine = 8,
+ OpExtension = 10,
+ OpExtInstImport = 11,
+ OpExtInst = 12,
+ OpMemoryModel = 14,
+ OpEntryPoint = 15,
+ OpExecutionMode = 16,
+ OpCapability = 17,
+ OpTypeVoid = 19,
+ OpTypeBool = 20,
+ OpTypeInt = 21,
+ OpTypeFloat = 22,
+ OpTypeVector = 23,
+ OpTypeMatrix = 24,
+ OpTypeImage = 25,
+ OpTypeSampler = 26,
+ OpTypeSampledImage = 27,
+ OpTypeArray = 28,
+ OpTypeRuntimeArray = 29,
+ OpTypeStruct = 30,
+ OpTypeOpaque = 31,
+ OpTypePointer = 32,
+ OpTypeFunction = 33,
+ OpTypeEvent = 34,
+ OpTypeDeviceEvent = 35,
+ OpTypeReserveId = 36,
+ OpTypeQueue = 37,
+ OpTypePipe = 38,
+ OpTypeForwardPointer = 39,
+ OpConstantTrue = 41,
+ OpConstantFalse = 42,
+ OpConstant = 43,
+ OpConstantComposite = 44,
+ OpConstantSampler = 45,
+ OpConstantNull = 46,
+ OpSpecConstantTrue = 48,
+ OpSpecConstantFalse = 49,
+ OpSpecConstant = 50,
+ OpSpecConstantComposite = 51,
+ OpSpecConstantOp = 52,
+ OpFunction = 54,
+ OpFunctionParameter = 55,
+ OpFunctionEnd = 56,
+ OpFunctionCall = 57,
+ OpVariable = 59,
+ OpImageTexelPointer = 60,
+ OpLoad = 61,
+ OpStore = 62,
+ OpCopyMemory = 63,
+ OpCopyMemorySized = 64,
+ OpAccessChain = 65,
+ OpInBoundsAccessChain = 66,
+ OpPtrAccessChain = 67,
+ OpArrayLength = 68,
+ OpGenericPtrMemSemantics = 69,
+ OpInBoundsPtrAccessChain = 70,
+ OpDecorate = 71,
+ OpMemberDecorate = 72,
+ OpDecorationGroup = 73,
+ OpGroupDecorate = 74,
+ OpGroupMemberDecorate = 75,
+ OpVectorExtractDynamic = 77,
+ OpVectorInsertDynamic = 78,
+ OpVectorShuffle = 79,
+ OpCompositeConstruct = 80,
+ OpCompositeExtract = 81,
+ OpCompositeInsert = 82,
+ OpCopyObject = 83,
+ OpTranspose = 84,
+ OpSampledImage = 86,
+ OpImageSampleImplicitLod = 87,
+ OpImageSampleExplicitLod = 88,
+ OpImageSampleDrefImplicitLod = 89,
+ OpImageSampleDrefExplicitLod = 90,
+ OpImageSampleProjImplicitLod = 91,
+ OpImageSampleProjExplicitLod = 92,
+ OpImageSampleProjDrefImplicitLod = 93,
+ OpImageSampleProjDrefExplicitLod = 94,
+ OpImageFetch = 95,
+ OpImageGather = 96,
+ OpImageDrefGather = 97,
+ OpImageRead = 98,
+ OpImageWrite = 99,
+ OpImage = 100,
+ OpImageQueryFormat = 101,
+ OpImageQueryOrder = 102,
+ OpImageQuerySizeLod = 103,
+ OpImageQuerySize = 104,
+ OpImageQueryLod = 105,
+ OpImageQueryLevels = 106,
+ OpImageQuerySamples = 107,
+ OpConvertFToU = 109,
+ OpConvertFToS = 110,
+ OpConvertSToF = 111,
+ OpConvertUToF = 112,
+ OpUConvert = 113,
+ OpSConvert = 114,
+ OpFConvert = 115,
+ OpQuantizeToF16 = 116,
+ OpConvertPtrToU = 117,
+ OpSatConvertSToU = 118,
+ OpSatConvertUToS = 119,
+ OpConvertUToPtr = 120,
+ OpPtrCastToGeneric = 121,
+ OpGenericCastToPtr = 122,
+ OpGenericCastToPtrExplicit = 123,
+ OpBitcast = 124,
+ OpSNegate = 126,
+ OpFNegate = 127,
+ OpIAdd = 128,
+ OpFAdd = 129,
+ OpISub = 130,
+ OpFSub = 131,
+ OpIMul = 132,
+ OpFMul = 133,
+ OpUDiv = 134,
+ OpSDiv = 135,
+ OpFDiv = 136,
+ OpUMod = 137,
+ OpSRem = 138,
+ OpSMod = 139,
+ OpFRem = 140,
+ OpFMod = 141,
+ OpVectorTimesScalar = 142,
+ OpMatrixTimesScalar = 143,
+ OpVectorTimesMatrix = 144,
+ OpMatrixTimesVector = 145,
+ OpMatrixTimesMatrix = 146,
+ OpOuterProduct = 147,
+ OpDot = 148,
+ OpIAddCarry = 149,
+ OpISubBorrow = 150,
+ OpUMulExtended = 151,
+ OpSMulExtended = 152,
+ OpAny = 154,
+ OpAll = 155,
+ OpIsNan = 156,
+ OpIsInf = 157,
+ OpIsFinite = 158,
+ OpIsNormal = 159,
+ OpSignBitSet = 160,
+ OpLessOrGreater = 161,
+ OpOrdered = 162,
+ OpUnordered = 163,
+ OpLogicalEqual = 164,
+ OpLogicalNotEqual = 165,
+ OpLogicalOr = 166,
+ OpLogicalAnd = 167,
+ OpLogicalNot = 168,
+ OpSelect = 169,
+ OpIEqual = 170,
+ OpINotEqual = 171,
+ OpUGreaterThan = 172,
+ OpSGreaterThan = 173,
+ OpUGreaterThanEqual = 174,
+ OpSGreaterThanEqual = 175,
+ OpULessThan = 176,
+ OpSLessThan = 177,
+ OpULessThanEqual = 178,
+ OpSLessThanEqual = 179,
+ OpFOrdEqual = 180,
+ OpFUnordEqual = 181,
+ OpFOrdNotEqual = 182,
+ OpFUnordNotEqual = 183,
+ OpFOrdLessThan = 184,
+ OpFUnordLessThan = 185,
+ OpFOrdGreaterThan = 186,
+ OpFUnordGreaterThan = 187,
+ OpFOrdLessThanEqual = 188,
+ OpFUnordLessThanEqual = 189,
+ OpFOrdGreaterThanEqual = 190,
+ OpFUnordGreaterThanEqual = 191,
+ OpShiftRightLogical = 194,
+ OpShiftRightArithmetic = 195,
+ OpShiftLeftLogical = 196,
+ OpBitwiseOr = 197,
+ OpBitwiseXor = 198,
+ OpBitwiseAnd = 199,
+ OpNot = 200,
+ OpBitFieldInsert = 201,
+ OpBitFieldSExtract = 202,
+ OpBitFieldUExtract = 203,
+ OpBitReverse = 204,
+ OpBitCount = 205,
+ OpDPdx = 207,
+ OpDPdy = 208,
+ OpFwidth = 209,
+ OpDPdxFine = 210,
+ OpDPdyFine = 211,
+ OpFwidthFine = 212,
+ OpDPdxCoarse = 213,
+ OpDPdyCoarse = 214,
+ OpFwidthCoarse = 215,
+ OpEmitVertex = 218,
+ OpEndPrimitive = 219,
+ OpEmitStreamVertex = 220,
+ OpEndStreamPrimitive = 221,
+ OpControlBarrier = 224,
+ OpMemoryBarrier = 225,
+ OpAtomicLoad = 227,
+ OpAtomicStore = 228,
+ OpAtomicExchange = 229,
+ OpAtomicCompareExchange = 230,
+ OpAtomicCompareExchangeWeak = 231,
+ OpAtomicIIncrement = 232,
+ OpAtomicIDecrement = 233,
+ OpAtomicIAdd = 234,
+ OpAtomicISub = 235,
+ OpAtomicSMin = 236,
+ OpAtomicUMin = 237,
+ OpAtomicSMax = 238,
+ OpAtomicUMax = 239,
+ OpAtomicAnd = 240,
+ OpAtomicOr = 241,
+ OpAtomicXor = 242,
+ OpPhi = 245,
+ OpLoopMerge = 246,
+ OpSelectionMerge = 247,
+ OpLabel = 248,
+ OpBranch = 249,
+ OpBranchConditional = 250,
+ OpSwitch = 251,
+ OpKill = 252,
+ OpReturn = 253,
+ OpReturnValue = 254,
+ OpUnreachable = 255,
+ OpLifetimeStart = 256,
+ OpLifetimeStop = 257,
+ OpGroupAsyncCopy = 259,
+ OpGroupWaitEvents = 260,
+ OpGroupAll = 261,
+ OpGroupAny = 262,
+ OpGroupBroadcast = 263,
+ OpGroupIAdd = 264,
+ OpGroupFAdd = 265,
+ OpGroupFMin = 266,
+ OpGroupUMin = 267,
+ OpGroupSMin = 268,
+ OpGroupFMax = 269,
+ OpGroupUMax = 270,
+ OpGroupSMax = 271,
+ OpReadPipe = 274,
+ OpWritePipe = 275,
+ OpReservedReadPipe = 276,
+ OpReservedWritePipe = 277,
+ OpReserveReadPipePackets = 278,
+ OpReserveWritePipePackets = 279,
+ OpCommitReadPipe = 280,
+ OpCommitWritePipe = 281,
+ OpIsValidReserveId = 282,
+ OpGetNumPipePackets = 283,
+ OpGetMaxPipePackets = 284,
+ OpGroupReserveReadPipePackets = 285,
+ OpGroupReserveWritePipePackets = 286,
+ OpGroupCommitReadPipe = 287,
+ OpGroupCommitWritePipe = 288,
+ OpEnqueueMarker = 291,
+ OpEnqueueKernel = 292,
+ OpGetKernelNDrangeSubGroupCount = 293,
+ OpGetKernelNDrangeMaxSubGroupSize = 294,
+ OpGetKernelWorkGroupSize = 295,
+ OpGetKernelPreferredWorkGroupSizeMultiple = 296,
+ OpRetainEvent = 297,
+ OpReleaseEvent = 298,
+ OpCreateUserEvent = 299,
+ OpIsValidEvent = 300,
+ OpSetUserEventStatus = 301,
+ OpCaptureEventProfilingInfo = 302,
+ OpGetDefaultQueue = 303,
+ OpBuildNDRange = 304,
+ OpImageSparseSampleImplicitLod = 305,
+ OpImageSparseSampleExplicitLod = 306,
+ OpImageSparseSampleDrefImplicitLod = 307,
+ OpImageSparseSampleDrefExplicitLod = 308,
+ OpImageSparseSampleProjImplicitLod = 309,
+ OpImageSparseSampleProjExplicitLod = 310,
+ OpImageSparseSampleProjDrefImplicitLod = 311,
+ OpImageSparseSampleProjDrefExplicitLod = 312,
+ OpImageSparseFetch = 313,
+ OpImageSparseGather = 314,
+ OpImageSparseDrefGather = 315,
+ OpImageSparseTexelsResident = 316,
+ OpNoLine = 317,
+ OpAtomicFlagTestAndSet = 318,
+ OpAtomicFlagClear = 319,
+ OpImageSparseRead = 320,
+ OpSubgroupBallotKHR = 4421,
+ OpSubgroupFirstInvocationKHR = 4422,
+ OpSubgroupAllKHR = 4428,
+ OpSubgroupAnyKHR = 4429,
+ OpSubgroupAllEqualKHR = 4430,
+ OpSubgroupReadInvocationKHR = 4432,
+ Max = 0x7fffffff,
+};
+
+// Overload operator| for mask bit combining
+
+inline ImageOperandsMask operator|(ImageOperandsMask a, ImageOperandsMask b) { return ImageOperandsMask(unsigned(a) | unsigned(b)); }
+inline FPFastMathModeMask operator|(FPFastMathModeMask a, FPFastMathModeMask b) { return FPFastMathModeMask(unsigned(a) | unsigned(b)); }
+inline SelectionControlMask operator|(SelectionControlMask a, SelectionControlMask b) { return SelectionControlMask(unsigned(a) | unsigned(b)); }
+inline LoopControlMask operator|(LoopControlMask a, LoopControlMask b) { return LoopControlMask(unsigned(a) | unsigned(b)); }
+inline FunctionControlMask operator|(FunctionControlMask a, FunctionControlMask b) { return FunctionControlMask(unsigned(a) | unsigned(b)); }
+inline MemorySemanticsMask operator|(MemorySemanticsMask a, MemorySemanticsMask b) { return MemorySemanticsMask(unsigned(a) | unsigned(b)); }
+inline MemoryAccessMask operator|(MemoryAccessMask a, MemoryAccessMask b) { return MemoryAccessMask(unsigned(a) | unsigned(b)); }
+inline KernelProfilingInfoMask operator|(KernelProfilingInfoMask a, KernelProfilingInfoMask b) { return KernelProfilingInfoMask(unsigned(a) | unsigned(b)); }
+
+} // end namespace spv
+
+#endif // #ifndef spirv_HPP
+
--
2.12.2
Pierre Moreau
2017-05-03 21:56:59 UTC
Permalink
Raw Message
Signed-off-by: Pierre Moreau <***@free.fr>
---
src/gallium/state_trackers/clover/Makefile.am | 10 +-
src/gallium/state_trackers/clover/Makefile.sources | 4 +
.../state_trackers/clover/spirv/invocation.cpp | 481 +++++++++++++++++++++
.../state_trackers/clover/spirv/invocation.hpp | 40 ++
4 files changed, 533 insertions(+), 2 deletions(-)
create mode 100644 src/gallium/state_trackers/clover/spirv/invocation.cpp
create mode 100644 src/gallium/state_trackers/clover/spirv/invocation.hpp

diff --git a/src/gallium/state_trackers/clover/Makefile.am b/src/gallium/state_trackers/clover/Makefile.am
index 321393536d..e29457e948 100644
--- a/src/gallium/state_trackers/clover/Makefile.am
+++ b/src/gallium/state_trackers/clover/Makefile.am
@@ -28,7 +28,7 @@ cl_HEADERS = \
$(top_srcdir)/include/CL/opencl.h
endif

-noinst_LTLIBRARIES = libclover.la libcltgsi.la libclllvm.la
+noinst_LTLIBRARIES = libclover.la libcltgsi.la libclllvm.la libspirv.la

libcltgsi_la_CXXFLAGS = \
-std=c++11 \
@@ -50,13 +50,19 @@ libclllvm_la_CXXFLAGS = \

libclllvm_la_SOURCES = $(LLVM_SOURCES)

+libspirv_la_CXXFLAGS = \
+ -std=c++11 \
+ $(VISIBILITY_CXXFLAGS)
+
+libspirv_la_SOURCES = $(SPIRV_SOURCES)
+
libclover_la_CXXFLAGS = \
-std=c++11 \
$(CLOVER_STD_OVERRIDE) \
$(VISIBILITY_CXXFLAGS)

libclover_la_LIBADD = \
- libcltgsi.la libclllvm.la
+ libcltgsi.la libclllvm.la libspirv.la

libclover_la_SOURCES = $(CPP_SOURCES)

diff --git a/src/gallium/state_trackers/clover/Makefile.sources b/src/gallium/state_trackers/clover/Makefile.sources
index e9828b107b..f223bebcd3 100644
--- a/src/gallium/state_trackers/clover/Makefile.sources
+++ b/src/gallium/state_trackers/clover/Makefile.sources
@@ -66,3 +66,7 @@ LLVM_SOURCES := \
TGSI_SOURCES := \
tgsi/compiler.cpp \
tgsi/invocation.hpp
+
+SPIRV_SOURCES := \
+ spirv/invocation.cpp \
+ spirv/invocation.hpp
diff --git a/src/gallium/state_trackers/clover/spirv/invocation.cpp b/src/gallium/state_trackers/clover/spirv/invocation.cpp
new file mode 100644
index 0000000000..3e740eb998
--- /dev/null
+++ b/src/gallium/state_trackers/clover/spirv/invocation.cpp
@@ -0,0 +1,481 @@
+//
+// Copyright 2017 Pierre Moreau
+//
+// 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.
+//
+
+#include <vector>
+#include <unordered_map>
+
+#include <spirv-tools/libspirv.hpp>
+
+#include "core/error.hpp"
+#include "invocation.hpp"
+#include "llvm/util.hpp"
+#include "spirv/spirv_linker.h"
+#include "spirv/spirv_utils.h"
+#include "util/algorithm.hpp"
+#include "util/functional.hpp"
+#include "util/u_debug.h"
+
+#include "spirv.hpp11"
+
+using namespace clover;
+
+namespace {
+
+ template<typename T>
+ T get(const std::vector<char>& source, size_t index) {
+ if (index * sizeof(spirv_word) + 3u > source.size())
+ return static_cast<T>(0);
+ return static_cast<T>(spirv_get_word(source.data(), index));
+ }
+
+ enum module::argument::type
+ convertStorageClass(spv::StorageClass storage_class) {
+ switch (storage_class) {
+ case spv::StorageClass::UniformConstant:
+ return module::argument::constant;
+ case spv::StorageClass::Workgroup:
+ return module::argument::local;
+ case spv::StorageClass::CrossWorkgroup:
+ return module::argument::global;
+ default:
+ throw build_error();
+ }
+ }
+
+ enum module::argument::type
+ convertImageType(spv::Id id, spv::Dim dim, spv::AccessQualifier access,
+ std::string &err) {
+#define APPEND_DIM(d) \
+ switch(access) { \
+ case spv::AccessQualifier::ReadOnly: \
+ return module::argument::image##d##_rd; \
+ case spv::AccessQualifier::WriteOnly: \
+ return module::argument::image##d##_wr; \
+ default: \
+ err += "Invalid access qualifier " #d " for image " + \
+ std::to_string(static_cast<int>(id)); \
+ throw build_error(); \
+ }
+
+ switch (dim) {
+ case spv::Dim::Dim2D:
+ APPEND_DIM(2d)
+ case spv::Dim::Dim3D:
+ APPEND_DIM(3d)
+ default:
+ err += "Invalid dimension " + std::to_string(static_cast<int>(dim)) +
+ " for image " + std::to_string(static_cast<int>(id));
+ throw build_error();
+ }
+
+#undef APPEND_DIM
+ }
+
+#define GET_OPERAND(type, operand_id) get<type>(source, i + operand_id)
+
+ void
+ add_kernels_data(module &m, std::string &err,
+ const std::vector<char> &source) {
+ const unsigned int length = source.size() / sizeof(spirv_word);
+ unsigned int i = 5u; // Skip header
+
+ std::string kernel_name;
+ uint32_t kernel_nb = 0u;
+ std::vector<module::argument> args;
+ uint32_t pointer_size = 0u;
+
+ std::unordered_map<spv::Id, std::string> kernels;
+ std::unordered_map<spv::Id, module::argument> types;
+
+ while (i < length) {
+ const auto desc_word = get<spirv_word>(source, i);
+ const auto opcode = static_cast<spv::Op>(desc_word & spv::OpCodeMask);
+ const unsigned int num_operands = desc_word >> spv::WordCountShift;
+
+ switch (opcode) {
+ case spv::Op::OpEntryPoint:
+ if (GET_OPERAND(spv::ExecutionModel, 1) != spv::ExecutionModel::Kernel)
+ break;
+ kernels.emplace(GET_OPERAND(spv::Id, 2),
+ &source[(i + 3) * sizeof(spirv_word)]);
+ break;
+
+ case spv::Op::OpMemoryModel:
+ switch (GET_OPERAND(spv::AddressingModel, 1)) {
+ case spv::AddressingModel::Physical32:
+ pointer_size = 32u / 8u;
+ break;
+ case spv::AddressingModel::Physical64:
+ pointer_size = 64u / 8u;
+ break;
+ case spv::AddressingModel::Logical:
+ err += "Addressing model 'Logical' is not valid for OpenCL\n";
+ // FALLTHROUGH
+ case spv::AddressingModel::Max:
+ throw build_error();
+ }
+ break;
+
+ case spv::Op::OpTypeInt: // FALLTHROUGH
+ case spv::Op::OpTypeFloat:
+ types[GET_OPERAND(spv::Id, 1)] = { module::argument::scalar,
+ GET_OPERAND(spirv_word, 2) / 8u };
+ break;
+
+ case spv::Op::OpTypeVector: {
+ const auto type_id = GET_OPERAND(spv::Id, 2);
+ const auto types_iter = types.find(type_id);
+ if (types_iter == types.end()) {
+ err += "Type " + std::to_string(type_id) + " is missing\n";
+ throw build_error();
+ }
+ types[GET_OPERAND(spv::Id, 1)] = { module::argument::scalar,
+ types_iter->second.size * GET_OPERAND(spirv_word, 3) };
+ break;
+ }
+
+ case spv::Op::OpTypeSampler:
+ types[GET_OPERAND(spv::Id, 1)] = { module::argument::sampler,
+ sizeof(cl_sampler) };
+ break;
+
+ case spv::Op::OpTypeImage: {
+ const auto id = GET_OPERAND(spv::Id, 1);
+ const auto dim = GET_OPERAND(spv::Dim, 3);
+ const auto access = GET_OPERAND(spv::AccessQualifier, 9);
+ types[id] = { convertImageType(id, dim, access, err), 0 };
+ break;
+ }
+
+
+ case spv::Op::OpTypePointer: {
+ const auto id = GET_OPERAND(spv::Id, 1);
+ const auto storage_class = GET_OPERAND(spv::StorageClass, 2);
+ types[id] = { convertStorageClass(storage_class), pointer_size };
+ break;
+ }
+
+ // case spv::Op::OpTypeBool: not allowed
+ // case spv::Op::OpTypeArray: expressed as pointer to underlying type
+ // case spv::Op::OpTypeMatrix: there is no native matrix type in OpenCL
+ // case spv::Op::OpTypePipe: OpenCL 2.0
+ // case spv::Op::OpTypeQueue: OpenCL 2.0
+
+ case spv::Op::OpFunction: {
+ const auto kernels_iter = kernels.find(GET_OPERAND(spv::Id, 2));
+ if (kernels_iter != kernels.end())
+ kernel_name = kernels_iter->second;
+ break;
+ }
+
+ case spv::Op::OpFunctionParameter: {
+ if (kernel_name.empty())
+ break;
+ const auto type_id = GET_OPERAND(spv::Id, 1);
+ const auto types_iter = types.find(type_id);
+ if (types_iter == types.end()) {
+ err += "Type " + std::to_string(type_id) + " is missing\n";
+ throw build_error();
+ }
+ args.push_back(types_iter->second);
+ break;
+ }
+
+ case spv::Op::OpFunctionEnd:
+ if (kernel_name.empty())
+ break;
+ m.syms.emplace_back(kernel_name, 0, kernel_nb++, args);
+ kernel_name.clear();
+ args.clear();
+ break;
+
+ default:
+ break;
+ }
+
+ i += num_operands;
+ }
+ }
+
+ enum module::section::type
+ guess_binary_type(const std::vector<char> &source, std::string &err) {
+ const unsigned int length = source.size() / sizeof(spirv_word);
+ unsigned int i = 5u; // Skip header
+ bool has_linkage_capability = false;
+ bool processed_decorations = false;
+
+ while (i < length) {
+ const auto desc_word = get<spirv_word>(source, i);
+ const auto opcode = static_cast<spv::Op>(desc_word & spv::OpCodeMask);
+ const unsigned int num_operands = desc_word >> spv::WordCountShift;
+
+ switch (opcode) {
+ case spv::Op::OpCapability:
+ if (GET_OPERAND(spv::Capability, 1) == spv::Capability::Linkage)
+ has_linkage_capability = true;
+ break;
+
+ case spv::Op::OpDecorate: // FALLTHROUGH
+ case spv::Op::OpMemberDecorate: {
+ processed_decorations = true;
+ const auto deco_index = (opcode == spv::Op::OpDecorate) ? 2u : 3u;
+ const auto decoration = GET_OPERAND(spv::Decoration, deco_index);
+ if (decoration != spv::Decoration::LinkageAttributes)
+ break;
+ const auto linkage_type = GET_OPERAND(spv::LinkageType,
+ num_operands - 1);
+ if (linkage_type == spv::LinkageType::Import)
+ return module::section::text_intermediate;
+ break;
+ }
+
+ case spv::Op::OpGroupDecorate:
+ case spv::Op::OpGroupMemberDecorate:
+ case spv::Op::OpDecorationGroup:
+ processed_decorations = true;
+ break;
+
+ default:
+ // OpCapability are the first instructions following the header, so
+ // when reaching the first non-OpCapability, if we have not seen
+ // any Capability::Linkage, the module is an executable (no missing
+ // symbols, and not exporting any symbols so not a library).
+ if (!has_linkage_capability)
+ return module::section::text_executable;
+
+ // All decorations are grouped together, so if we are reaching a
+ // non-decoration instruction but have seen some before, then we
+ // have processed all decoration instructions. And if we are still
+ // here, that means that only export type linkage were found.
+ if (processed_decorations)
+ return module::section::text_library;
+ break;
+ }
+
+ i += num_operands;
+ }
+
+ return module::section::text_intermediate;
+ }
+
+ std::string
+ version_to_string(unsigned version) {
+ return std::to_string((version >> 16u) & 0xff) + "." +
+ std::to_string((version >> 8u) & 0xff);
+ }
+
+ void
+ check_spirv_version(const char *binary, std::string &r_log) {
+ const auto binary_version = spirv_get_word(binary, 1u);
+ if (binary_version <= spv::Version)
+ return;
+
+ r_log += "SPIR-V version " + version_to_string(binary_version) +
+ " is not supported; supported versions <= " +
+ version_to_string(spv::Version);
+ _debug_printf("%s\n", r_log.c_str());
+ throw build_error();
+ }
+
+ std::string
+ format_validator_msg(spv_message_level_t level,
+ const spv_position_t& position, const char* message) {
+ auto const level_to_string = [](spv_message_level_t level){
+#define LVL2STR(lvl) case SPV_MSG_##lvl: return std::string(#lvl)
+ switch (level) {
+ LVL2STR(FATAL);
+ LVL2STR(INTERNAL_ERROR);
+ LVL2STR(ERROR);
+ LVL2STR(WARNINING);
+ LVL2STR(INFO);
+ LVL2STR(DEBUG);
+ }
+#undef LVL2STR
+ return std::string();
+ };
+ return "[" + level_to_string(level) + "] At word No." +
+ std::to_string(position.index) + ": \"" + message + "\"\n";
+ }
+}
+
+module
+clover::spirv::process_program(const void *il, const size_t length,
+ bool guess_section_type, std::string &r_log) {
+ auto c_il = reinterpret_cast<const char*>(il);
+ char *cpu_endianness_binary = spirv_spirv_to_cpu(c_il, length);
+
+ auto const validator_consumer = [&r_log](spv_message_level_t level,
+ const char* /* source */,
+ const spv_position_t& position,
+ const char* message) {
+ r_log += format_validator_msg(level, position, message);
+ };
+ spvtools::SpirvTools spvTool(SPV_ENV_OPENCL_2_1);
+ spvTool.SetMessageConsumer(validator_consumer);
+ if (!spvTool.Validate(reinterpret_cast<const uint32_t*>(cpu_endianness_binary), length / 4u))
+ throw build_error();
+
+ check_spirv_version(cpu_endianness_binary, r_log);
+
+ std::vector<char> source(cpu_endianness_binary, cpu_endianness_binary + length);
+
+ // It is our responsability to free the converted result.
+ free(cpu_endianness_binary);
+
+ module m;
+ add_kernels_data(m, r_log, source);
+ auto section_type = module::section::text_intermediate;
+ if (guess_section_type)
+ section_type = guess_binary_type(source, r_log);
+ m.secs.push_back(module::section(0, section_type, source.size(), source));
+
+ return m;
+}
+
+module
+clover::spirv::link_program(const std::vector<module> &modules,
+ const std::string &opts, std::string &r_log) {
+ std::vector<std::string> options = clover::llvm::tokenize(opts);
+
+ const bool create_library = count("-create-library", options);
+ erase_if(equals("-create-library"), options);
+
+ const bool enable_link = count("-enable-link-options", options);
+ erase_if(equals("-enable-link-options"), options);
+ if (enable_link && !create_library) {
+ r_log += "SPIR-V linker: '-enable-link-options' can't be used without '-create-library'\n";
+ throw invalid_build_options_error();
+ }
+
+ const auto get_opt = [&options,enable_link,&r_log](const std::string &name){
+ bool var = count(name, options);
+ erase_if(equals(name), options);
+ if (var && !enable_link) {
+ r_log += "SPIR-V linker: '" + name + "' can't be used without '-enable-link-options'\n";
+ throw invalid_build_options_error();
+ }
+ return var;
+ };
+
+ auto denorms_are_zero = get_opt("-cl-no-signed-zeroes");
+ auto no_signed_zeroes = get_opt("-cl-denorms_are_zero");
+ auto unsafe_math = get_opt("-cl-unsafe-math-optimizations");
+ auto finite_math = get_opt("-cl-finite-math-only");
+ auto fast_relaxed = get_opt("-cl-fast-relaxed-math");
+
+ if (!options.empty()) {
+ r_log += "SPIR-V linker: Invalid linker options: ";
+ for (const auto &opt : options)
+ r_log += "'" + opt + "' ";
+ throw invalid_build_options_error();
+ }
+
+ unsafe_math |= fast_relaxed;
+ finite_math |= fast_relaxed;
+ no_signed_zeroes |= unsafe_math;
+ bool enable_mad = unsafe_math;
+
+ module m;
+
+ const auto section_type = create_library ? module::section::text_library :
+ module::section::text_executable;
+
+ std::vector<const char *> sections;
+ sections.reserve(modules.size());
+ std::vector<unsigned> lengths;
+ lengths.reserve(modules.size());
+
+ auto const validator_consumer = [&r_log](spv_message_level_t level,
+ const char* /* source */,
+ const spv_position_t& position,
+ const char* message) {
+ r_log += format_validator_msg(level, position, message);
+ };
+ spvtools::SpirvTools spvTool(SPV_ENV_OPENCL_2_1);
+ spvTool.SetMessageConsumer(validator_consumer);
+
+ auto found_binary = false;
+ for (const auto &mod : modules) {
+ const module::section *msec = nullptr;
+ try {
+ msec = &find(type_equals(module::section::text_library), mod.secs);
+ } catch (const std::out_of_range &e) {
+ try {
+ msec = &find(type_equals(module::section::text_intermediate),
+ mod.secs);
+ } catch (const std::out_of_range &e) {
+ }
+ }
+ found_binary |= msec != nullptr;
+
+ // OpenCL 1.2 Specification, Section 5.6.3:
+ // > * All programs specified by input_programs contain a compiled binary
+ // > or library for the device. In this case, a link is performed to
+ // > generate a program executable for this device.
+ // > * None of the programs contain a compiled binary or library for that
+ // > device. In this case, no link is performed and there will be no
+ // > program executable generated for this device.
+ // > * All other cases will return a CL_INVALID_OPERATION error.
+ if (found_binary != (msec != nullptr)) {
+ r_log += "SPIR-V linker: Some programs have binaries/libraries and some have nothing\n";
+ throw error(CL_INVALID_OPERATION);
+ }
+
+ const auto c_il = msec->data.data();
+ const auto length = msec->data.size();
+
+ if (!spvTool.Validate(reinterpret_cast<const uint32_t*>(c_il), length / 4u))
+ throw build_error();
+
+ check_spirv_version(c_il, r_log);
+
+ sections.push_back(c_il);
+ lengths.push_back(length / sizeof(spirv_word));
+ }
+
+ if (!found_binary)
+ return m;
+
+ unsigned final_size;
+ char *error_msg;
+ unsigned error_msg_length;
+ auto final_data = spirv_link_binaries(sections.data(), lengths.data(),
+ modules.size(), create_library,
+ &final_size, &error_msg,
+ &error_msg_length);
+ if (!final_data) {
+ r_log += error_msg;
+ free(error_msg);
+ throw build_error();
+ }
+
+// if (!spvTool.Validate(reinterpret_cast<const uint32_t*>(final_data), final_size))
+// throw build_error();
+
+ for (const auto &mod : modules)
+ m.syms.insert(m.syms.end(), mod.syms.begin(), mod.syms.end());
+
+ m.secs.emplace_back(module::section{ 0, section_type, final_size,
+ { final_data, final_data + final_size * sizeof(spirv_word) } });
+
+ return m;
+}
diff --git a/src/gallium/state_trackers/clover/spirv/invocation.hpp b/src/gallium/state_trackers/clover/spirv/invocation.hpp
new file mode 100644
index 0000000000..06e8f5bda8
--- /dev/null
+++ b/src/gallium/state_trackers/clover/spirv/invocation.hpp
@@ -0,0 +1,40 @@
+//
+// Copyright 2017 Pierre Moreau
+//
+// 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.
+//
+
+#ifndef CLOVER_SPIRV_INVOCATION_HPP
+#define CLOVER_SPIRV_INVOCATION_HPP
+
+#include "core/module.hpp"
+
+namespace clover {
+ namespace spirv {
+ module process_program(const void *il,
+ const size_t length,
+ bool guess_section_type,
+ std::string &r_log);
+
+ module link_program(const std::vector<module> &modules,
+ const std::string &opts, std::string &r_log);
+ }
+}
+
+#endif
--
2.12.2
Pierre Moreau
2017-05-03 21:57:00 UTC
Permalink
Raw Message
Creating a program using clCreateProgramWithSource to SPIR-V requires a
non-upstreamed version of LLVM and clang, therefore it is currently not
supported.

Signed-off-by: Pierre Moreau <***@free.fr>
---
src/gallium/state_trackers/clover/core/program.cpp | 4 ++++
1 file changed, 4 insertions(+)

diff --git a/src/gallium/state_trackers/clover/core/program.cpp b/src/gallium/state_trackers/clover/core/program.cpp
index ae4b50a879..15d559cd93 100644
--- a/src/gallium/state_trackers/clover/core/program.cpp
+++ b/src/gallium/state_trackers/clover/core/program.cpp
@@ -51,6 +51,10 @@ program::compile(const ref_vector<device> &devs, const std::string &opts,
std::string log;

try {
+ if (dev.ir_format() == PIPE_SHADER_IR_SPIRV) {
+ log = "Compiling from source to SPIR-V is not supported yet\n";
+ throw error(CL_INVALID_DEVICE);
+ }
const module m = (dev.ir_format() == PIPE_SHADER_IR_TGSI ?
tgsi::compile_program(_source, log) :
llvm::compile_program(_source, headers,
--
2.12.2
Pierre Moreau
2017-05-03 21:57:01 UTC
Permalink
Raw Message
Signed-off-by: Pierre Moreau <***@free.fr>
---
src/gallium/state_trackers/clover/core/program.cpp | 19 ++++++++++++++-----
1 file changed, 14 insertions(+), 5 deletions(-)

diff --git a/src/gallium/state_trackers/clover/core/program.cpp b/src/gallium/state_trackers/clover/core/program.cpp
index 15d559cd93..6a54500247 100644
--- a/src/gallium/state_trackers/clover/core/program.cpp
+++ b/src/gallium/state_trackers/clover/core/program.cpp
@@ -80,11 +80,20 @@ program::link(const ref_vector<device> &devs, const std::string &opts,
std::string log = _builds[&dev].log;

try {
- const module m = (dev.ir_format() == PIPE_SHADER_IR_TGSI ?
- tgsi::link_program(ms) :
- llvm::link_program(ms, dev.ir_format(),
- dev.ir_target(), opts, log));
- _builds[&dev] = { m, opts, log };
+ switch (dev.ir_format()) {
+ case PIPE_SHADER_IR_TGSI:
+ _builds[&dev] = { tgsi::link_program(ms), opts, log };
+ break;
+ case PIPE_SHADER_IR_LLVM:
+ case PIPE_SHADER_IR_NATIVE:
+ case PIPE_SHADER_IR_NIR:
+ _builds[&dev] = { llvm::link_program(ms, dev.ir_format(),
+ dev.ir_target(), opts, log), opts, log };
+ break;
+ case PIPE_SHADER_IR_SPIRV:
+ _builds[&dev] = { clover::spirv::link_program(ms, opts, log), opts, log };
+ break;
+ }
} catch (...) {
_builds[&dev] = { module(), opts, log };
throw;
--
2.12.2
Pierre Moreau
2017-05-03 21:57:02 UTC
Permalink
Raw Message
Signed-off-by: Pierre Moreau <***@free.fr>
---
src/gallium/state_trackers/clover/api/program.cpp | 35 ++++++++++++++++-------
1 file changed, 25 insertions(+), 10 deletions(-)

diff --git a/src/gallium/state_trackers/clover/api/program.cpp b/src/gallium/state_trackers/clover/api/program.cpp
index 9d59668f8f..5f5971078d 100644
--- a/src/gallium/state_trackers/clover/api/program.cpp
+++ b/src/gallium/state_trackers/clover/api/program.cpp
@@ -22,6 +22,8 @@

#include "api/util.hpp"
#include "core/program.hpp"
+#include "spirv/invocation.hpp"
+#include "spirv/spirv_utils.h"
#include "util/u_debug.h"

#include <sstream>
@@ -92,22 +94,35 @@ clCreateProgramWithBinary(cl_context d_ctx, cl_uint n,

// Deserialize the provided binaries,
std::vector<std::pair<cl_int, module>> result = map(
- [](const unsigned char *p, size_t l) -> std::pair<cl_int, module> {
+ [](const unsigned char *p, size_t l, device &dev) -> std::pair<cl_int, module> {
if (!p || !l)
return { CL_INVALID_VALUE, {} };

- try {
- std::stringbuf bin( { (char*)p, l } );
- std::istream s(&bin);
-
- return { CL_SUCCESS, module::deserialize(s) };
-
- } catch (std::istream::failure &e) {
- return { CL_INVALID_BINARY, {} };
+ if (spirv_is_binary_spirv(reinterpret_cast<const char *>(p))) {
+ if (!dev.supports_ir(PIPE_SHADER_IR_SPIRV))
+ return { CL_INVALID_BINARY, {} };
+
+ try {
+ std::string log;
+ return { CL_SUCCESS, spirv::process_program(p, l, true, log) };
+ } catch (build_error &e) {
+ return { CL_INVALID_BINARY, {} };
+ }
+ } else {
+ try {
+ std::stringbuf bin( { (char*)p, l } );
+ std::istream s(&bin);
+
+ return { CL_SUCCESS, module::deserialize(s) };
+
+ } catch (std::istream::failure &e) {
+ return { CL_INVALID_BINARY, {} };
+ }
}
},
range(binaries, n),
- range(lengths, n));
+ range(lengths, n),
+ devs);

// update the status array,
if (r_status)
--
2.12.2
Pierre Moreau
2017-05-03 21:57:03 UTC
Permalink
Raw Message
Signed-off-by: Pierre Moreau <***@free.fr>
---
src/gallium/state_trackers/clover/api/program.cpp | 29 ++++++++++-
src/gallium/state_trackers/clover/core/program.cpp | 57 ++++++++++++++++++++--
src/gallium/state_trackers/clover/core/program.hpp | 14 ++++++
3 files changed, 95 insertions(+), 5 deletions(-)

diff --git a/src/gallium/state_trackers/clover/api/program.cpp b/src/gallium/state_trackers/clover/api/program.cpp
index 5f5971078d..57b8aedb91 100644
--- a/src/gallium/state_trackers/clover/api/program.cpp
+++ b/src/gallium/state_trackers/clover/api/program.cpp
@@ -144,6 +144,31 @@ clCreateProgramWithBinary(cl_context d_ctx, cl_uint n,
}

CLOVER_API cl_program
+clCreateProgramWithIL(cl_context d_ctx,
+ const void *il,
+ const size_t length,
+ cl_int *r_errcode) try {
+ auto &ctx = obj(d_ctx);
+
+ if (!il || !length)
+ throw error(CL_INVALID_VALUE);
+
+ uint32_t type = 0;
+ // Only SPIR-V is supported for now
+ if (!spirv_is_binary_spirv(reinterpret_cast<const char*>(il)))
+ throw error(CL_INVALID_VALUE);
+ type = PIPE_SHADER_IR_SPIRV;
+
+ // initialize a program object with it.
+ ret_error(r_errcode, CL_SUCCESS);
+ return new program(ctx, il, length, type);
+
+} catch (error &e) {
+ ret_error(r_errcode, e);
+ return NULL;
+}
+
+CLOVER_API cl_program
clCreateProgramWithBuiltInKernels(cl_context d_ctx, cl_uint n,
const cl_device_id *d_devs,
const char *kernel_names,
@@ -198,7 +223,7 @@ clBuildProgram(cl_program d_prog, cl_uint num_devs,

validate_build_common(prog, num_devs, d_devs, pfn_notify, user_data);

- if (prog.has_source) {
+ if (prog.has_source || prog.has_il) {
prog.compile(devs, opts);
prog.link(devs, opts, { prog });
}
@@ -228,7 +253,7 @@ clCompileProgram(cl_program d_prog, cl_uint num_devs,
if (bool(num_headers) != bool(header_names))
throw error(CL_INVALID_VALUE);

- if (!prog.has_source)
+ if (!prog.has_source && !prog.has_il)
throw error(CL_INVALID_OPERATION);

for_each([&](const char *name, const program &header) {
diff --git a/src/gallium/state_trackers/clover/core/program.cpp b/src/gallium/state_trackers/clover/core/program.cpp
index 6a54500247..d9d197fffe 100644
--- a/src/gallium/state_trackers/clover/core/program.cpp
+++ b/src/gallium/state_trackers/clover/core/program.cpp
@@ -23,24 +23,43 @@
#include "core/program.hpp"
#include "llvm/invocation.hpp"
#include "tgsi/invocation.hpp"
+#include "spirv/invocation.hpp"
+
+#include "spirv/spirv_utils.h"
+
+#include <cstring>

using namespace clover;

program::program(clover::context &ctx, const std::string &source) :
- has_source(true), context(ctx), _source(source), _kernel_ref_counter(0) {
+ has_source(true), has_il(false), il_type(0u), context(ctx), _source(source),
+ _kernel_ref_counter(0), _il(nullptr), _length(0) {
}

program::program(clover::context &ctx,
const ref_vector<device> &devs,
const std::vector<module> &binaries) :
- has_source(false), context(ctx),
- _devices(devs), _kernel_ref_counter(0) {
+ has_source(false), has_il(false), il_type(0u), context(ctx),
+ _devices(devs), _kernel_ref_counter(0), _il(nullptr), _length(0) {
for_each([&](device &dev, const module &bin) {
_builds[&dev] = { bin };
},
devs, binaries);
}

+program::program(clover::context &ctx, const void *il, const size_t length,
+ const uint32_t type) :
+ has_source(false), has_il(true), il_type(type), context(ctx),
+ _kernel_ref_counter(0), _il(nullptr), _length(length) {
+ const char *c_il = reinterpret_cast<const char *>(il);
+ _il = spirv_spirv_to_cpu(c_il, length);
+}
+
+program::~program() {
+ if (has_il)
+ delete[] reinterpret_cast<const char *>(_il);
+}
+
void
program::compile(const ref_vector<device> &devs, const std::string &opts,
const header_map &headers) {
@@ -65,6 +84,28 @@ program::compile(const ref_vector<device> &devs, const std::string &opts,
throw;
}
}
+ } else if (has_il) {
+ _devices = devs;
+
+ for (auto &dev : devs) {
+ std::string log;
+
+ try {
+ if (il_type == PIPE_SHADER_IR_SPIRV) {
+ if (!dev.supports_ir(PIPE_SHADER_IR_SPIRV)) {
+ log = "Device does not support SPIR-V as IL\n";
+ throw error(CL_INVALID_BINARY);
+ }
+ _builds[&dev] = { spirv::process_program(_il, _length, false, log), opts, log };
+ } else {
+ log = "Only SPIR-V is supported as IL by clover for now\n";
+ throw error(CL_INVALID_BINARY);
+ }
+ } catch (const error &) {
+ _builds[&dev] = { module(), opts, log };
+ throw;
+ }
+ }
}
}

@@ -101,6 +142,16 @@ program::link(const ref_vector<device> &devs, const std::string &opts,
}
}

+const void *
+program::il() const {
+ return _il;
+}
+
+size_t
+program::length() const {
+ return _length;
+}
+
const std::string &
program::source() const {
return _source;
diff --git a/src/gallium/state_trackers/clover/core/program.hpp b/src/gallium/state_trackers/clover/core/program.hpp
index 05964e78a7..77a79c5d94 100644
--- a/src/gallium/state_trackers/clover/core/program.hpp
+++ b/src/gallium/state_trackers/clover/core/program.hpp
@@ -43,11 +43,17 @@ namespace clover {
program(clover::context &ctx,
const ref_vector<device> &devs = {},
const std::vector<module> &binaries = {});
+ program(clover::context &ctx,
+ const void *il,
+ const size_t length,
+ const uint32_t type);

program(const program &prog) = delete;
program &
operator=(const program &prog) = delete;

+ ~program();
+
void compile(const ref_vector<device> &devs, const std::string &opts,
const header_map &headers = {});
void link(const ref_vector<device> &devs, const std::string &opts,
@@ -56,6 +62,12 @@ namespace clover {
const bool has_source;
const std::string &source() const;

+ const bool has_il;
+ const uint32_t il_type;
+ const void *il() const;
+
+ size_t length() const;
+
device_range devices() const;

struct build {
@@ -85,6 +97,8 @@ namespace clover {
std::map<const device *, struct build> _builds;
std::string _source;
ref_counter _kernel_ref_counter;
+ const void *_il;
+ size_t _length;
};
}
--
2.12.2
Pierre Moreau
2017-05-03 21:57:04 UTC
Permalink
Raw Message
OpenCL 2.1 gives the ability to query for a program’s IL, which is
returned as a pointer.

Signed-off-by: Pierre Moreau <***@free.fr>
---
.../state_trackers/clover/core/property.hpp | 39 ++++++++++++++++++++++
1 file changed, 39 insertions(+)

diff --git a/src/gallium/state_trackers/clover/core/property.hpp b/src/gallium/state_trackers/clover/core/property.hpp
index 7f8e17684d..5beac372e7 100644
--- a/src/gallium/state_trackers/clover/core/property.hpp
+++ b/src/gallium/state_trackers/clover/core/property.hpp
@@ -23,6 +23,7 @@
#ifndef CLOVER_CORE_PROPERTY_HPP
#define CLOVER_CORE_PROPERTY_HPP

+#include <cstring>
#include <map>

#include "util/range.hpp"
@@ -84,6 +85,19 @@ namespace clover {
private:
property_buffer &buf;
};
+
+ template<typename T>
+ class property_pointer {
+ public:
+ property_pointer(property_buffer &buf) : buf(buf) {
+ }
+
+ inline property_pointer &
+ operator=(const std::pair<const T *, size_t> &v);
+
+ private:
+ property_buffer &buf;
+ };
};

///
@@ -119,6 +133,12 @@ namespace clover {
}

template<typename T>
+ detail::property_pointer<T>
+ as_pointer() {
+ return { *this };
+ }
+
+ template<typename T>
iterator_range<T *>
allocate(size_t n) {
if (r_buf && size < n * sizeof(T))
@@ -133,6 +153,17 @@ namespace clover {
return { };
}

+ void
+ allocate_raw(const void *v, size_t n) {
+ if (r_buf && size < n)
+ throw error(CL_INVALID_VALUE);
+
+ if (r_size)
+ *r_size = n;
+
+ std::memcpy(r_buf, v, n);
+ }
+
private:
void *const r_buf;
const size_t size;
@@ -178,6 +209,14 @@ namespace clover {
return *this;
}

+ template<typename T>
+ inline property_pointer<T> &
+ property_pointer<T>::operator=(const std::pair<const T *, size_t> &v) {
+ buf.allocate_raw(v.first, v.second);
+
+ return *this;
+ }
+
inline property_string &
property_string::operator=(const std::string &v) {
auto r = buf.allocate<char>(v.size() + 1);
--
2.12.2
Pierre Moreau
2017-05-03 21:57:05 UTC
Permalink
Raw Message
Signed-off-by: Pierre Moreau <***@free.fr>
---
src/gallium/state_trackers/clover/api/program.cpp | 10 ++++++++++
1 file changed, 10 insertions(+)

diff --git a/src/gallium/state_trackers/clover/api/program.cpp b/src/gallium/state_trackers/clover/api/program.cpp
index 57b8aedb91..5357724939 100644
--- a/src/gallium/state_trackers/clover/api/program.cpp
+++ b/src/gallium/state_trackers/clover/api/program.cpp
@@ -386,6 +386,16 @@ clGetProgramInfo(cl_program d_prog, cl_program_info param,
buf.as_string() = prog.source();
break;

+ // FIXME valid only if OpenCL 2.1 context
+ case CL_PROGRAM_IL:
+// if (prog.context().properties())
+// throw error(CL_INVALID_VALUE);
+ if (prog.has_il)
+ buf.as_pointer<void>() = std::make_pair(prog.il(), prog.length());
+ else if (r_size)
+ *r_size = 0u;
+ break;
+
case CL_PROGRAM_BINARY_SIZES:
buf.as_vector<size_t>() = map([&](const device &dev) {
return prog.build(dev).binary.size();
--
2.12.2
Rob Clark
2017-05-04 02:25:23 UTC
Permalink
Raw Message
Post by Pierre Moreau
* As there is no upstream version of LLVM which can produce SPIR-V out of
OpenCL code, clCreateProgramWithSource will refuse to work if the target IR
is SPIR-V, for now.
hopefully this[1] will eventually happen, which would make this less
of an issue :-)

[1] http://lists.llvm.org/pipermail/llvm-dev/2017-May/112538.html
Pierre Moreau
2017-05-04 08:52:21 UTC
Permalink
Raw Message
Post by Rob Clark
hopefully this[1] will eventually happen, which would make this less
of an issue :-)
[1] http://lists.llvm.org/pipermail/llvm-dev/2017-May/112538.html
Indeed! I have seen that thread and made sure to subscribe to the ML not to
miss anything there.
Pierre Moreau
2017-06-16 19:59:57 UTC
Permalink
Raw Message
Hello,

I am working on an updated version of this series, which will include changes
that have been suggested, as well as support for structures as a kernel
argument. Along those, I was also thinking of doing some other changes to the
SPIR-V linker:

a) Store some metadata about the SPIR-V module, such as the word offset of
each of the different sections: if one wants to run an operation on a single
section of the module, it is no longer needed to iterate over everything in
front of that section.
It could also contain the width of the literals used in the switch
statements, avoiding having to track those every time one iterate over the
module.
Maybe tables for imported and exported symbols, as well?

b) Change the code from having a single loop iterating over the whole module,
into having one loop per section. This will avoid testing for entry points,
decorations, types, etc. when we are already iterating over the function
bodies, possibly improving performance.

c) Remove duplicate decorations, capabilities, names? and types found in the
linked module. Types is the most important one, as it currently prevents us
from validating the linked module.

I am still thinking of leaving out of this series, the linking options support
for the linker.

As for clover, it should check that the devices actually support the
capabilities present in the module, so I will add that as well.

Do you think this would make sense, and should I be implementing them in this
series, or should I leave them be for later? (Point b), if deemed useful,
should be part of this series as it would rewrite a decent chunk of the code.)
If you have any other comments, feel free to share it. :-)

Best regards,
Pierre
Post by Pierre Moreau
Hello everyone,
I have been working on converting SPIR-V to NVIR in order to run OpenCL kernels
on Nouveau, and I would like to submit the first part of that work for review.
Pieces from the SPIR-V to NVIR conversion work will be submitted once I have
cleaned it up and this series has progressed through the reviewing process.
What’s in this Series?
----------------------
The focus of this series is to let clover accept SPIR-V binaries, either
through `clCreateProgramWithBinary()`, or through `clCreateProgramWithIL()`.
The latter function is the proper way to feed SPIR-V binaries using the OpenCL
API, however it was only introduced in OpenCL 2.1 (more on “why supporting
SPIR-V through `clCreateProgramWithBinary()` can be interesting” further down).
As several SPIR-V binaries can be linked together using the OpenCL API, I
implemented a SPIR-V linker, which is not perfect, but does the job. I tested
linking against a variable, a function, a library, and a function containing a
switch statement; switch-statements require you to keep some extra stuff around
to be properly parsed.
I also added a few “utilities” functions for retrieving and setting a word /
retrieving a string from a SPIR-V binary, and converting a SPIR-V binary to the
same endianness as the host CPU.
For validating SPIR-V binaries, I use an external tool, SPIRV-Tools [1]. It
could also be used in anv, and possibly radv if there is no validation done
already, but I haven’t looked into that.
A few modifications have been made to the pipe interface, to add a define for
the SPIR-V IR, and store the program’s byte-size along the program in
`struct pipe_compute_state`. The latter will only be needed by the consumer of
the SPIR-V, which is not part of this series. However, since clover needs to
fill that information in and I was modifying clover already, I decided to add
the new attribute in this series.
Missing
-------
* As there is no upstream version of LLVM which can produce SPIR-V out of
OpenCL code, clCreateProgramWithSource will refuse to work if the target IR
is SPIR-V, for now.
* Optimisation linking options are parsed by the SPIR-V code in clover but
are not passed along to the linker as it does not support them.
To Improve
----------
The SPIR-V binary resulting from the linking of multiple SPIR-V binaries could
* As capabilities are simply copied from all the involved binaries, you can end
up with multiple times the same capabilities in the resulting binary; this
shouldn’t have any impact though.
* Similarly, types can end up being duplicated under different IDs, which
should have no other impact than making SPIR-V validators unhappy.
Misc.
-----
Being able to feed SPIR-V binaries through `clCreateProgramWithBinary()` is not
really useful at the moment: the same can be achieved using
`clCreateProgramWithIL()`. However it will be interesting once there is an
upstream version of LLVM which can generate SPIR-V binaries, as the application
could query the binary created by `clCreateProgramWithSource()` on the first
run, and give it to `clCreateProgramWithBinary()`on later runs.
Once NIR supports pointers, and anything else that could be missing to support
OpenCL kernels, it should be possible and easy to convert input SPIR-V
binaries to NIR, for drivers that do not accept SPIR-V as IR.
I have sent patches to Mesa in the past, but never series, so the splitting of
the patches in the series could be completely wrong, and I apologise for that
in advance.
Also, I am sure I abused of macros, gotos and manual memory managements, as I
am not that comfortable at writing too much C code: I’ll try to learn from
your comments.
Thank you in advance for reviewing/commenting,
Pierre
[1]: https://github.com/KhronosGroup/SPIRV-Tools/
auxiliary: Introduce utilities for SPIR-V binaries
auxiliary: Implement a linker for SPIR-V binaries
include/pipe: Define SPIRV as an IR
include/pipe: Store the byte-size of a SPIR-V binary
include/CL: Add clCreateProgramWithIL from OpenCL 2.1
include/CL: Add new option to clGetProgramInfo from OpenCL 2.1
configure.ac: Check for SPIRV-Tools header and library
clover: Fill in the program byte-size in pipe_compute_state
clover: Add additional functions to query supported IRs
clover/spirv: Import spirv.hpp11 version 1.0 (rev 10)
clover/spirv: Add functions for parsing arguments, linking programs,
etc.
clover: Refuse to compile source code to SPIR-V
clover: Handle the case when linking SPIR-V binaries together
clover: Accept SPIR-V binaries in clCreateProgramWithBinary
clover: Implement clCreateProgramWithIL from OpenCL 2.1
clover: Add a pointer property to return ILs
clover: Handle CL_PROGRAM_IL in clGetProgramInfo
configure.ac | 16 +
include/CL/cl.h | 7 +
include/CL/cl_platform.h | 1 +
src/gallium/auxiliary/Makefile.am | 1 +
src/gallium/auxiliary/Makefile.sources | 6 +
src/gallium/auxiliary/spirv/spirv_linker.c | 1324 ++++++++++++++++++++
src/gallium/auxiliary/spirv/spirv_linker.h | 67 +
src/gallium/auxiliary/spirv/spirv_utils.c | 75 ++
src/gallium/auxiliary/spirv/spirv_utils.h | 86 ++
src/gallium/include/pipe/p_defines.h | 1 +
src/gallium/include/pipe/p_state.h | 1 +
src/gallium/state_trackers/clover/Makefile.am | 10 +-
src/gallium/state_trackers/clover/Makefile.sources | 4 +
src/gallium/state_trackers/clover/api/program.cpp | 74 +-
src/gallium/state_trackers/clover/core/device.cpp | 11 +
src/gallium/state_trackers/clover/core/device.hpp | 3 +
src/gallium/state_trackers/clover/core/kernel.cpp | 1 +
src/gallium/state_trackers/clover/core/program.cpp | 80 +-
src/gallium/state_trackers/clover/core/program.hpp | 14 +
.../state_trackers/clover/core/property.hpp | 39 +
.../state_trackers/clover/spirv/invocation.cpp | 481 +++++++
.../state_trackers/clover/spirv/invocation.hpp | 40 +
.../state_trackers/clover/spirv/spirv.hpp11 | 952 ++++++++++++++
23 files changed, 3272 insertions(+), 22 deletions(-)
create mode 100644 src/gallium/auxiliary/spirv/spirv_linker.c
create mode 100644 src/gallium/auxiliary/spirv/spirv_linker.h
create mode 100644 src/gallium/auxiliary/spirv/spirv_utils.c
create mode 100644 src/gallium/auxiliary/spirv/spirv_utils.h
create mode 100644 src/gallium/state_trackers/clover/spirv/invocation.cpp
create mode 100644 src/gallium/state_trackers/clover/spirv/invocation.hpp
create mode 100644 src/gallium/state_trackers/clover/spirv/spirv.hpp11
--
2.12.2
_______________________________________________
mesa-dev mailing list
https://lists.freedesktop.org/mailman/listinfo/mesa-dev
Loading...