Signed-off-by: Pierre Moreau <pierre.mor...@free.fr>
---

Resending to the ML, as gabe ran out of memory.

 src/gallium/state_trackers/clover/Makefile.am      |  15 +-
 src/gallium/state_trackers/clover/Makefile.sources |   4 +
 src/gallium/state_trackers/clover/api/program.cpp  |   1 -
 src/gallium/state_trackers/clover/meson.build      |  10 +-
 .../state_trackers/clover/spirv/invocation.cpp     | 668 +++++++++++++++++++++
 .../state_trackers/clover/spirv/invocation.hpp     |  54 ++
 6 files changed, 748 insertions(+), 4 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 a7befb4605..9bbe8b6d9c 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 libclspirv.la
 
 libcltgsi_la_CXXFLAGS = \
        $(CXX11_CXXFLAGS) \
@@ -50,13 +50,24 @@ libclllvm_la_CXXFLAGS = \
 
 libclllvm_la_SOURCES = $(LLVM_SOURCES)
 
+libclspirv_la_CXXFLAGS = \
+       $(CXX11_CXXFLAGS) \
+       $(CLOVER_STD_OVERRIDE) \
+       $(VISIBILITY_CXXFLAGS) \
+       $(SPIRV_TOOLS_CFLAGS)
+
+libclspirv_la_SOURCES = $(SPIRV_SOURCES)
+
+libclspirv_la_LDFLAGS = \
+       $(SPIRV_TOOLS_LIBS)
+
 libclover_la_CXXFLAGS = \
        $(CXX11_CXXFLAGS) \
        $(CLOVER_STD_OVERRIDE) \
        $(VISIBILITY_CXXFLAGS)
 
 libclover_la_LIBADD = \
-       libcltgsi.la libclllvm.la
+       libcltgsi.la libclllvm.la libclspirv.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/api/program.cpp 
b/src/gallium/state_trackers/clover/api/program.cpp
index 8f0b103a4d..6ec87ad128 100644
--- a/src/gallium/state_trackers/clover/api/program.cpp
+++ b/src/gallium/state_trackers/clover/api/program.cpp
@@ -255,7 +255,6 @@ namespace {
                          const std::string &opts) {
       std::vector<device *> devs;
 
-      const std::string flag = "-create-library";
       const bool create_library = opts.find("-create-library") != 
std::string::npos;
 
       for (auto &dev : all_devs) {
diff --git a/src/gallium/state_trackers/clover/meson.build 
b/src/gallium/state_trackers/clover/meson.build
index d1497e657e..cc3c7253de 100644
--- a/src/gallium/state_trackers/clover/meson.build
+++ b/src/gallium/state_trackers/clover/meson.build
@@ -58,6 +58,14 @@ libclllvm = static_library(
   dependencies : [dep_llvm, dep_elf],
 )
 
+libclspirv = static_library(
+  'clspirv',
+  files('spirv/invocation.cpp', 'spirv/invocation.hpp'),
+  include_directories : clover_incs,
+  cpp_args : [cpp_vis_args],
+  dependencies : [dep_spirv_tools],
+)
+
 clover_files = files(
   'api/context.cpp',
   'api/device.cpp',
@@ -118,5 +126,5 @@ libclover = static_library(
   clover_files,
   include_directories : clover_incs,
   cpp_args : [clover_cpp_args, cpp_vis_args],
-  link_with : [libcltgsi, libclllvm],
+  link_with : [libcltgsi, libclllvm, libclspirv],
 )
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..3087947948
--- /dev/null
+++ b/src/gallium/state_trackers/clover/spirv/invocation.cpp
@@ -0,0 +1,668 @@
+//
+// 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 <cstring>
+
+#include <stack>
+#include <tuple>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+
+#include <spirv-tools/libspirv.hpp>
+#include <spirv-tools/linker.hpp>
+
+#include "core/error.hpp"
+#include "core/platform.hpp"
+#include "invocation.hpp"
+#include "llvm/util.hpp"
+#include "pipe/p_state.h"
+#include "util/algorithm.hpp"
+#include "util/functional.hpp"
+#include "util/u_debug.h"
+#include "util/u_math.h"
+
+#include "spirv.hpp11"
+
+using namespace clover;
+
+namespace {
+
+   template<typename T>
+   T get(const char *source, size_t index) {
+      const uint32_t *word_ptr = reinterpret_cast<const uint32_t *>(source);
+      return static_cast<T>(word_ptr[index]);
+   }
+
+   enum module::argument::type
+   convertStorageClass(spv::StorageClass storage_class, std::string &err) {
+      switch (storage_class) {
+      case spv::StorageClass::Function:
+         return module::argument::scalar;
+      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:
+         err += "Invalid storage type " +
+                std::to_string(static_cast<int>(storage_class)) + "\n";
+         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 += "Unsupported 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 += "Unsupported dimension " +
+                std::to_string(static_cast<int>(dim)) + " for image " +
+                std::to_string(static_cast<int>(id));
+         throw build_error();
+      }
+
+#undef APPEND_DIM
+   }
+
+   void
+   extract_kernels_arguments(module &m, std::string &err,
+                             const std::vector<char> &source) {
+      const size_t length = source.size() / sizeof(uint32_t);
+      size_t i = 5u; // Skip header
+
+      std::string kernel_name;
+      uint32_t kernel_nb = 0u;
+      std::vector<module::argument> args;
+      uint32_t pointer_byte_size = 0u;
+
+      std::unordered_map<spv::Id, std::string> kernels;
+      std::unordered_map<spv::Id, module::argument> types;
+      std::unordered_map<spv::Id, spv::Id> pointer_types;
+      std::unordered_map<spv::Id, unsigned int> constants;
+      std::unordered_set<spv::Id> packed_structures;
+      std::unordered_map<spv::Id, std::vector<spv::FunctionParameterAttribute>>
+         func_param_attr_map;
+
+#define GET_OPERAND(type, operand_id) get<type>(source.data(), i + operand_id)
+
+      while (i < length) {
+         const auto desc_word = get<uint32_t>(source.data(), 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.data() + (i + 3u) 
* sizeof(uint32_t));
+            break;
+
+         case spv::Op::OpMemoryModel:
+            switch (GET_OPERAND(spv::AddressingModel, 1)) {
+            case spv::AddressingModel::Physical32:
+               pointer_byte_size = 4u;
+               break;
+            case spv::AddressingModel::Physical64:
+               pointer_byte_size = 8u;
+               break;
+            }
+            break;
+
+         case spv::Op::OpDecorate: {
+            const spv::Id id = GET_OPERAND(spv::Id, 1);
+            const auto decoration = GET_OPERAND(spv::Decoration, 2);
+            if (decoration == spv::Decoration::CPacked)
+               packed_structures.emplace(id);
+            else if (decoration == spv::Decoration::FuncParamAttr)
+               
func_param_attr_map[id].push_back(GET_OPERAND(spv::FunctionParameterAttribute, 
3u));
+            break;
+         }
+
+         case spv::Op::OpGroupDecorate: {
+            const spv::Id group_id = GET_OPERAND(spv::Id, 1);
+            if (packed_structures.count(group_id)) {
+               for (unsigned int i = 2u; i < num_operands; ++i)
+                  packed_structures.emplace(GET_OPERAND(spv::Id, i));
+            }
+            const auto func_param_attr_iter = 
func_param_attr_map.find(group_id);
+            if (func_param_attr_iter != func_param_attr_map.end()) {
+               for (unsigned int i = 2u; i < num_operands; ++i)
+                  func_param_attr_map.emplace(GET_OPERAND(spv::Id, i),
+                                              func_param_attr_iter->second);
+            }
+            break;
+         }
+
+         case spv::Op::OpConstant:
+            // We only care about constants that represents the size of arrays.
+            // If they are passed as argument, they will never be more than
+            // 4GB-wide, and even if they did, a clover::module::argument size
+            // is represented by an int.
+            constants[GET_OPERAND(spv::Id, 2)] = GET_OPERAND(unsigned int, 3u);
+            break;
+
+         case spv::Op::OpTypeInt: // FALLTHROUGH
+         case spv::Op::OpTypeFloat: {
+            const auto size = GET_OPERAND(uint32_t, 2) / 8u;
+            types[GET_OPERAND(spv::Id, 1)] = { module::argument::scalar,
+                                               size, size, size,
+                                               module::argument::zero_ext };
+            break;
+         }
+
+         case spv::Op::OpTypeArray: {
+            const auto id = GET_OPERAND(spv::Id, 1);
+            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();
+            }
+            const auto constant_id = GET_OPERAND(spv::Id, 3);
+            const auto constants_iter = constants.find(constant_id);
+            if (constants_iter == constants.end()) {
+               err += "Constant " + std::to_string(constant_id) + " is 
missing\n";
+               throw build_error();
+            }
+            const auto elem_size = types_iter->second.size;
+            const auto elem_nbs = constants_iter->second;
+            const auto size = elem_size * elem_nbs;
+            types[id] = { module::argument::scalar, size, size,
+                          types_iter->second.target_align,
+                          module::argument::zero_ext };
+            break;
+         }
+
+         case spv::Op::OpTypeStruct: {
+            const auto id = GET_OPERAND(spv::Id, 1);
+            const bool is_packed = packed_structures.count(id);
+
+            unsigned struct_size = 0u;
+            unsigned max_elem_size = 0u;
+            for (unsigned j = 2u; j < num_operands; ++j) {
+               const auto type_id = GET_OPERAND(spv::Id, j);
+               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();
+               }
+               const auto alignment = is_packed ? 1u
+                                                : 
types_iter->second.target_align;
+               const auto padding = (-struct_size) & (alignment - 1u);
+               struct_size += padding + types_iter->second.target_size;
+               max_elem_size = std::max(max_elem_size,
+                                        types_iter->second.target_align);
+            }
+            if (!is_packed)
+               struct_size += (-struct_size) & (max_elem_size - 1u);
+
+            types[id] = { module::argument::scalar, struct_size, struct_size,
+                          is_packed ? 1u
+                                    : max_elem_size, module::argument::zero_ext
+            };
+            break;
+         }
+
+         case spv::Op::OpTypeVector: {
+            const auto id = GET_OPERAND(spv::Id, 1);
+            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();
+            }
+            const auto elem_size = types_iter->second.size;
+            const auto elem_nbs = GET_OPERAND(uint32_t, 3);
+            const auto size = elem_size * elem_nbs;
+            types[id] = { module::argument::scalar,
+                                               size, size, elem_size,
+                                               module::argument::zero_ext };
+            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, err),
+                          sizeof(cl_mem), pointer_byte_size, pointer_byte_size,
+                          module::argument::zero_ext
+            };
+            pointer_types[id] = GET_OPERAND(spv::Id, 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),
+                          sizeof(cl_mem), sizeof(cl_mem), sizeof(cl_mem),
+                          module::argument::zero_ext
+            };
+            break;
+         }
+
+         case spv::Op::OpTypePipe: // FALLTHROUGH
+         case spv::Op::OpTypeQueue: {
+            err += "TypePipe and TypeQueue are valid SPIR-V 1.0 types, but are 
"
+                   "not available in the currently supported OpenCL C version."
+                   "\n";
+            throw build_error();
+         }
+
+         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);
+            auto arg = types.find(type_id)->second;
+            const auto &func_param_attr_iter =
+               func_param_attr_map.find(GET_OPERAND(spv::Id, 2));
+            if (func_param_attr_iter != func_param_attr_map.end()) {
+               for (auto &i : func_param_attr_iter->second) {
+                  switch (i) {
+                  case spv::FunctionParameterAttribute::Sext:
+                     arg.ext_type = module::argument::sign_ext;
+                     break;
+                  case spv::FunctionParameterAttribute::Zext:
+                     arg.ext_type = module::argument::zero_ext;
+                     break;
+                  case spv::FunctionParameterAttribute::ByVal: {
+                     const spv::Id ptr_type_id =
+                        pointer_types.find(type_id)->second;
+                     arg = types.find(ptr_type_id)->second;
+                     break;
+                  }
+                  }
+               }
+            }
+            args.emplace_back(arg);
+            break;
+         }
+
+         case spv::Op::OpFunctionEnd:
+            if (kernel_name.empty())
+               break;
+            m.syms.emplace_back(kernel_name, 0, kernel_nb, args);
+            ++kernel_nb;
+            kernel_name.clear();
+            args.clear();
+            break;
+
+         default:
+            break;
+         }
+
+         i += num_operands;
+      }
+
+#undef GET_OPERAND
+
+   }
+
+   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 = get<uint32_t>(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);
+      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(WARNING);
+            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::section
+   make_text_section(const std::vector<char> &code,
+                     enum module::section::type section_type,
+                     module::section::flags_t section_flags) {
+      const pipe_llvm_program_header header { uint32_t(code.size()) };
+      module::section text { 0, section_type, section_flags, header.num_bytes,
+                             {}
+      };
+
+      text.data.insert(text.data.end(), reinterpret_cast<const char 
*>(&header),
+                       reinterpret_cast<const char *>(&header) + 
sizeof(header));
+      text.data.insert(text.data.end(), code.begin(), code.end());
+
+      return text;
+   }
+
+   bool
+   check_capabilities(const device &dev, const std::vector<char> &source,
+                      std::string &r_log) {
+      const size_t length = source.size() / sizeof(uint32_t);
+      size_t i = 5u; // Skip header
+
+      while (i < length) {
+         const auto desc_word = get<uint32_t>(source.data(), i);
+         const auto opcode = static_cast<spv::Op>(desc_word & spv::OpCodeMask);
+         const unsigned int num_operands = desc_word >> spv::WordCountShift;
+
+         if (opcode != spv::Op::OpCapability)
+            break;
+
+         spv::Capability capability = get<spv::Capability>(source.data(), i + 
1u);
+         switch (capability) {
+         // Mandatory capabilities
+         case spv::Capability::Addresses:
+         case spv::Capability::Float16Buffer:
+         case spv::Capability::Groups:
+         case spv::Capability::Int64:
+         case spv::Capability::Int16:
+         case spv::Capability::Int8:
+         case spv::Capability::Kernel:
+         case spv::Capability::Linkage:
+         case spv::Capability::Vector16:
+            break;
+         // Optional capabilities
+         case spv::Capability::ImageBasic:
+         case spv::Capability::LiteralSampler:
+         case spv::Capability::Sampled1D:
+         case spv::Capability::Image1D:
+         case spv::Capability::SampledBuffer:
+         case spv::Capability::ImageBuffer:
+            if (!dev.image_support()) {
+               r_log += "Capability 'ImageBasic' is not supported.\n";
+               return false;
+            }
+            break;
+         case spv::Capability::Float64:
+            if (!dev.has_doubles()) {
+               r_log += "Capability 'Float64' is not supported.\n";
+               return false;
+            }
+            break;
+         // Enabled through extensions
+         case spv::Capability::Float16:
+            if (!dev.has_halves()) {
+               r_log += "Capability 'Float16' is not supported.\n";
+               return false;
+            }
+            break;
+         default:
+            r_log += "Capability '" +
+                     std::to_string(static_cast<uint32_t>(capability)) +
+                     "' is not supported.\n";
+            return false;
+         }
+
+         i += num_operands;
+      }
+
+      return true;
+   }
+
+   bool
+   check_extensions(const device &dev, const std::vector<char> &source,
+                    std::string &r_log) {
+      const size_t length = source.size() / sizeof(uint32_t);
+      size_t i = 5u; // Skip header
+
+      while (i < length) {
+         const auto desc_word = get<uint32_t>(source.data(), i);
+         const auto opcode = static_cast<spv::Op>(desc_word & spv::OpCodeMask);
+         const unsigned int num_operands = desc_word >> spv::WordCountShift;
+
+         if (opcode == spv::Op::OpCapability) {
+            i += num_operands;
+            continue;
+         }
+         if (opcode != spv::Op::OpExtension)
+            break;
+
+         const char *extension = source.data() + (i + 1u) * sizeof(uint32_t);
+         const std::string device_extensions = dev.supported_extensions();
+         const std::string platform_extensions =
+            dev.platform.supported_extensions();
+         if (device_extensions.find(extension) == std::string::npos &&
+             platform_extensions.find(extension) == std::string::npos) {
+            r_log += "Extension '" + std::string(extension) +
+                     "' is not supported.\n";
+            return false;
+         }
+
+         i += num_operands;
+      }
+
+      return true;
+   }
+}
+
+bool
+clover::spirv::is_binary_spirv(const char *binary)
+{
+   const uint32_t first_word = get<uint32_t>(binary, 0u);
+   return (first_word == spv::MagicNumber) ||
+          (util_bswap32(first_word) == spv::MagicNumber);
+}
+
+char *
+clover::spirv::spirv_to_cpu(const char *binary, size_t length)
+{
+   uint32_t word = get<uint32_t>(binary, 0u);
+   size_t i = 0;
+   char *cpu_endianness_binary = new char[length];
+   if (word == spv::MagicNumber)
+      return reinterpret_cast<char *>(std::memcpy(cpu_endianness_binary, 
binary,
+                                                  length));
+
+   for (i = 0; i < (length / 4u); ++i) {
+      word = get<uint32_t>(binary, i);
+      reinterpret_cast<uint32_t *>(cpu_endianness_binary)[i] = 
util_bswap32(word);
+   }
+
+   return cpu_endianness_binary;
+}
+
+module
+clover::spirv::process_program(const void *il, const size_t length,
+                               const device &dev, std::string &r_log) {
+   auto c_il = reinterpret_cast<const char *>(il);
+   char *cpu_endianness_binary = clover::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_1_2);
+   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.
+   delete[] cpu_endianness_binary;
+
+   if (!check_capabilities(dev, source, r_log))
+      throw build_error();
+   if (!check_extensions(dev, source, r_log))
+      throw build_error();
+
+   module m;
+   extract_kernels_arguments(m, r_log, source);
+   m.secs.push_back(make_text_section(source,
+                                      module::section::text_intermediate,
+                                      module::section::flags_t::none));
+
+   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);
+
+   spvtools::LinkerOptions linker_options;
+   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 && !linker_options.GetCreateLibrary()) {
+      r_log += "SPIR-V linker: '-enable-link-options' cannot be used without 
'-create-library'\n";
+      throw invalid_build_options_error();
+   }
+
+   if (!options.empty()) {
+      r_log += "SPIR-V linker: Ignoring the following link options: ";
+      for (const auto &opt : options)
+         r_log += "'" + opt + "' ";
+   }
+
+   linker_options.SetCreateLibrary(create_library);
+
+   module m;
+
+   const auto section_type = create_library ? module::section::text_library :
+                                              module::section::text_executable;
+
+   std::vector<const uint32_t *> sections;
+   sections.reserve(modules.size());
+   std::vector<size_t> 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_1_2);
+   spvTool.SetMessageConsumer(validator_consumer);
+
+   for (const auto &mod : modules) {
+      const module::section *msec = nullptr;
+      try {
+         msec = &find([&](const module::section &sec) {
+                  return sec.type == module::section::text_intermediate ||
+                         sec.type == module::section::text_library;
+               }, mod.secs);
+      } catch (const std::out_of_range &e) {
+         // We should never reach this as validate_link_devices already checked
+         // that a binary was present.
+         assert(false);
+      }
+
+      const auto c_il = msec->data.data() +
+                        sizeof(struct pipe_llvm_program_header);
+      const auto length = msec->size;
+
+      check_spirv_version(c_il, r_log);
+
+      sections.push_back(reinterpret_cast<const uint32_t *>(c_il));
+      lengths.push_back(length / sizeof(uint32_t));
+   }
+
+   std::vector<uint32_t> linked_binary;
+
+
+   const spvtools::MessageConsumer consumer = validator_consumer;
+   spvtools::Context context(SPV_ENV_OPENCL_1_2);
+   context.SetMessageConsumer(std::move(consumer));
+
+   if (Link(context, sections.data(), lengths.data(), sections.size(),
+            &linked_binary, linker_options) != SPV_SUCCESS)
+      throw build_error();
+
+   if (!spvTool.Validate(linked_binary.data(), linked_binary.size()))
+      throw build_error();
+
+   for (const auto &mod : modules)
+      m.syms.insert(m.syms.end(), mod.syms.begin(), mod.syms.end());
+
+   module::section::flags_t flags = module::section::flags_t::none;
+   if (enable_link)
+      flags = module::section::flags_t::allow_link_options;
+
+   m.secs.emplace_back(make_text_section({
+            reinterpret_cast<char *>(linked_binary.data()),
+            reinterpret_cast<char *>(linked_binary.data()) +
+               linked_binary.size() * sizeof(uint32_t) }, section_type, 
flags));
+
+   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..a8125caea9
--- /dev/null
+++ b/src/gallium/state_trackers/clover/spirv/invocation.hpp
@@ -0,0 +1,54 @@
+//
+// 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 {
+      // Returns true if the binary starts with the SPIR-V magic word, false
+      // otherwise.
+      //
+      // The first word is interpreted as little endian and big endian, but
+      // only one of them has to match.
+      bool is_binary_spirv(const char *binary);
+
+      // Copies the input binary and convert it to the endianness of the host
+      // CPU.
+      char * spirv_to_cpu(const char *binary, size_t length);
+
+      // Creates a clover module out of the given SPIR-V binary.
+      module process_program(const void *il,
+                             const size_t length,
+                             const device &dev,
+                             std::string &r_log);
+
+      // Combines multiple clover modules into a single one, resolving
+      // link dependencies between them.
+      module link_program(const std::vector<module> &modules,
+                          const std::string &opts, std::string &r_log);
+   }
+}
+
+#endif
-- 
2.16.0

_______________________________________________
mesa-dev mailing list
mesa-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/mesa-dev

Reply via email to