[Mesa-dev] [RFC PATCH 02/17] auxiliary: Implement a linker for SPIR-V binaries

Pierre Moreau pierre.morrow at free.fr
Wed May 3 21:56:50 UTC 2017


Signed-off-by: Pierre Moreau <pierre.morrow at 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



More information about the mesa-dev mailing list