clover: add support for passing kernels as nir to the driver
authorKarol Herbst <kherbst@redhat.com>
Tue, 6 Aug 2019 18:35:48 +0000 (20:35 +0200)
committerKarol Herbst <karolherbst@gmail.com>
Sat, 21 Sep 2019 08:28:32 +0000 (08:28 +0000)
v2: minor formatting fixes
v3: call glsl_type_singleton_init_or_ref and glsl_type_singleton_decref
v4: capitalize and punctuate comments
    fix text_executable -> text_intermediate in TODO
    make glsl_type_singleton wrapper static
v5: rewrite how we run the nir passes
v6: fix unhandled case switch warning in st/mesa

Signed-off-by: Karol Herbst <kherbst@redhat.com>
Reviewed-by: Francisco Jerez <currojerez@riseup.net> (v4)
src/gallium/include/pipe/p_defines.h
src/gallium/state_trackers/clover/core/compiler.hpp
src/gallium/state_trackers/clover/core/device.cpp
src/gallium/state_trackers/clover/core/device.hpp
src/gallium/state_trackers/clover/meson.build
src/gallium/state_trackers/clover/nir/invocation.cpp [new file with mode: 0644]
src/gallium/state_trackers/clover/nir/invocation.hpp [new file with mode: 0644]
src/mesa/state_tracker/st_program.c

index de0d88f6548b7e160cc9df02d60a73d2a3d4cf4f..0d4adcf6f4b66278db8307e903b104534e6ed5fe 100644 (file)
@@ -1004,6 +1004,7 @@ enum pipe_shader_ir
    PIPE_SHADER_IR_TGSI = 0,
    PIPE_SHADER_IR_NATIVE,
    PIPE_SHADER_IR_NIR,
+   PIPE_SHADER_IR_NIR_SERIALIZED,
 };
 
 /**
index 4ed20061627b4c90575cd6cb8578966e9d53d259..6ef30df9b7fcdc758c99d9241e7620e49f3d68ee 100644 (file)
@@ -26,6 +26,8 @@
 #include "core/device.hpp"
 #include "core/module.hpp"
 #include "llvm/invocation.hpp"
+#include "nir/invocation.hpp"
+#include "spirv/invocation.hpp"
 
 namespace clover {
    namespace compiler {
@@ -34,6 +36,10 @@ namespace clover {
                       const device &dev, const std::string &opts,
                       std::string &log) {
          switch (dev.ir_format()) {
+#ifdef HAVE_CLOVER_SPIRV
+         case PIPE_SHADER_IR_NIR_SERIALIZED:
+            return llvm::compile_to_spirv(source, headers, dev, opts, log);
+#endif
          case PIPE_SHADER_IR_NATIVE:
             return llvm::compile_program(source, headers, dev, opts, log);
          default:
@@ -46,6 +52,9 @@ namespace clover {
       link_program(const std::vector<module> &ms, const device &dev,
                    const std::string &opts, std::string &log) {
          switch (dev.ir_format()) {
+         case PIPE_SHADER_IR_NIR_SERIALIZED:
+            return nir::spirv_to_nir(spirv::link_program(ms, dev, opts, log),
+                                     dev, log);
          case PIPE_SHADER_IR_NATIVE:
             return llvm::link_program(ms, dev, opts, log);
          default:
index c7aea827938f03785a2d0f18c7edfcf7dcac48c5..298cde278b4717f1c850dcdfe383540f658de6b0 100644 (file)
@@ -46,12 +46,17 @@ namespace {
 device::device(clover::platform &platform, pipe_loader_device *ldev) :
    platform(platform), ldev(ldev) {
    pipe = pipe_loader_create_screen(ldev);
-   if (!pipe || !pipe->get_param(pipe, PIPE_CAP_COMPUTE) ||
-       !supports_ir(PIPE_SHADER_IR_NATIVE)) {
-      if (pipe)
-         pipe->destroy(pipe);
-      throw error(CL_INVALID_DEVICE);
+   if (pipe && pipe->get_param(pipe, PIPE_CAP_COMPUTE)) {
+      if (supports_ir(PIPE_SHADER_IR_NATIVE))
+         return;
+#ifdef HAVE_CLOVER_SPIRV
+      if (supports_ir(PIPE_SHADER_IR_NIR_SERIALIZED))
+         return;
+#endif
    }
+   if (pipe)
+      pipe->destroy(pipe);
+   throw error(CL_INVALID_DEVICE);
 }
 
 device::~device() {
@@ -246,7 +251,11 @@ device::vendor_name() const {
 
 enum pipe_shader_ir
 device::ir_format() const {
-   return PIPE_SHADER_IR_NATIVE;
+   if (supports_ir(PIPE_SHADER_IR_NATIVE))
+      return PIPE_SHADER_IR_NATIVE;
+
+   assert(supports_ir(PIPE_SHADER_IR_NIR_SERIALIZED));
+   return PIPE_SHADER_IR_NIR_SERIALIZED;
 }
 
 std::string
@@ -294,3 +303,8 @@ device::supported_extensions() const {
       + std::string(has_doubles() ? " cl_khr_fp64" : "")
       + std::string(has_halves() ? " cl_khr_fp16" : "");
 }
+
+const void *
+device::get_compiler_options(enum pipe_shader_ir ir) const {
+   return pipe->get_compiler_options(pipe, ir, PIPE_SHADER_COMPUTE);
+}
index a7084e863f49b2ce3f9d734cb43f1b3c3607dd29..828dfaa65e6f89584541b0698ac548fbdd135ca3 100644 (file)
@@ -90,6 +90,7 @@ namespace clover {
       friend class hard_event;
       friend std::set<cl_image_format>
       supported_formats(const context &, cl_mem_object_type);
+      const void *get_compiler_options(enum pipe_shader_ir ir) const;
 
       clover::platform &platform;
 
index 13d554c3fbfe16137e5962b7ed79ba2426d51c07..04c6f1ea0ebe7f0c42a503d6b45973d8319c153d 100644 (file)
@@ -66,6 +66,15 @@ libclspirv = static_library(
   override_options : clover_cpp_std,
 )
 
+libclnir = static_library(
+  'clnir',
+  [files('nir/invocation.cpp', 'nir/invocation.hpp'), nir_opcodes_h],
+  include_directories : [clover_incs, inc_mesa],
+  cpp_args : [clover_spirv_cpp_args, cpp_vis_args],
+  link_with : [libnir],
+  override_options : clover_cpp_std,
+)
+
 clover_files = files(
   'api/context.cpp',
   'api/device.cpp',
@@ -127,6 +136,6 @@ libclover = static_library(
   [clover_files, sha1_h],
   include_directories : clover_incs,
   cpp_args : [clover_spirv_cpp_args, clover_cpp_args, cpp_vis_args],
-  link_with : [libclllvm, libclspirv],
+  link_with : [libclllvm, libclspirv, libclnir],
   override_options : clover_cpp_std,
 )
diff --git a/src/gallium/state_trackers/clover/nir/invocation.cpp b/src/gallium/state_trackers/clover/nir/invocation.cpp
new file mode 100644 (file)
index 0000000..0ec1c9a
--- /dev/null
@@ -0,0 +1,169 @@
+//
+// Copyright 2019 Karol Herbst
+//
+// 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 "invocation.hpp"
+
+#include <tuple>
+
+#include "core/device.hpp"
+#include "core/error.hpp"
+#include "pipe/p_state.h"
+#include "util/algorithm.hpp"
+#include "util/functional.hpp"
+
+#include <compiler/glsl_types.h>
+#include <compiler/nir/nir_serialize.h>
+#include <compiler/spirv/nir_spirv.h>
+#include <util/u_math.h>
+
+using namespace clover;
+
+#ifdef HAVE_CLOVER_SPIRV
+
+// Refs and unrefs the glsl_type_singleton.
+static class glsl_type_ref {
+public:
+   glsl_type_ref() {
+      glsl_type_singleton_init_or_ref();
+   }
+
+   ~glsl_type_ref() {
+      glsl_type_singleton_decref();
+   }
+} glsl_type_ref;
+
+static const nir_shader_compiler_options *
+dev_get_nir_compiler_options(const device &dev)
+{
+   const void *co = dev.get_compiler_options(PIPE_SHADER_IR_NIR);
+   return static_cast<const nir_shader_compiler_options*>(co);
+}
+
+module clover::nir::spirv_to_nir(const module &mod, const device &dev,
+                                 std::string &r_log)
+{
+   const struct spirv_to_nir_options spirv_options = {
+      .caps = {
+         .address = true,
+         .float64 = true,
+         .int8 = true,
+         .int16 = true,
+         .int64 = true,
+         .kernel = true,
+      },
+   };
+
+   module m;
+   // We only insert one section.
+   assert(mod.secs.size() == 1);
+   auto &section = mod.secs[0];
+
+   module::resource_id section_id = 0;
+   for (const auto &sym : mod.syms) {
+      assert(sym.section == 0);
+
+      const auto *binary =
+         reinterpret_cast<const pipe_binary_program_header *>(section.data.data());
+      const uint32_t *data = reinterpret_cast<const uint32_t *>(binary->blob);
+      const size_t num_words = binary->num_bytes / 4;
+      const char *name = sym.name.c_str();
+      auto *compiler_options = dev_get_nir_compiler_options(dev);
+
+      nir_shader *nir = spirv_to_nir(data, num_words, nullptr, 0,
+                                     MESA_SHADER_KERNEL, name,
+                                     &spirv_options, compiler_options);
+
+      nir->info.cs.local_size_variable = true;
+      nir_validate_shader(nir, "clover");
+
+      // Calculate input offsets.
+      unsigned offset = 0;
+      nir_foreach_variable_safe(var, &nir->inputs) {
+         offset = align(offset, glsl_get_cl_alignment(var->type));
+         var->data.driver_location = offset;
+         offset += glsl_get_cl_size(var->type);
+      }
+
+      // Inline all functions first.
+      // according to the comment on nir_inline_functions
+      NIR_PASS_V(nir, nir_lower_constant_initializers, nir_var_function_temp);
+      NIR_PASS_V(nir, nir_lower_returns);
+      NIR_PASS_V(nir, nir_inline_functions);
+      NIR_PASS_V(nir, nir_opt_deref);
+
+      // Pick off the single entrypoint that we want.
+      foreach_list_typed_safe(nir_function, func, node, &nir->functions) {
+         if (!func->is_entrypoint)
+            exec_node_remove(&func->node);
+      }
+      assert(exec_list_length(&nir->functions) == 1);
+
+      nir_validate_shader(nir, "clover after function inlining");
+
+      NIR_PASS_V(nir, nir_lower_constant_initializers,
+                 static_cast<nir_variable_mode>(~nir_var_function_temp));
+
+      // copy propagate to prepare for lower_explicit_io
+      NIR_PASS_V(nir, nir_split_var_copies);
+      NIR_PASS_V(nir, nir_opt_copy_prop_vars);
+      NIR_PASS_V(nir, nir_lower_var_copies);
+      NIR_PASS_V(nir, nir_lower_vars_to_ssa);
+      NIR_PASS_V(nir, nir_opt_dce);
+
+      nir_variable_mode modes = (nir_variable_mode)(
+         nir_var_shader_in |
+         nir_var_mem_global |
+         nir_var_mem_shared);
+      nir_address_format format = nir->info.cs.ptr_size == 64 ?
+         nir_address_format_64bit_global : nir_address_format_32bit_global;
+      NIR_PASS_V(nir, nir_lower_explicit_io, modes, format);
+
+      NIR_PASS_V(nir, nir_lower_system_values);
+      if (compiler_options->lower_int64_options)
+         NIR_PASS_V(nir, nir_lower_int64,
+                    compiler_options->lower_int64_options);
+
+      NIR_PASS_V(nir, nir_opt_dce);
+
+      struct blob blob;
+      blob_init(&blob);
+      nir_serialize(&blob, nir);
+
+      const pipe_binary_program_header header { uint32_t(blob.size) };
+      module::section text { section_id, module::section::text_executable, 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(), blob.data, blob.data + blob.size);
+
+      m.syms.emplace_back(sym.name, section_id, 0, sym.args);
+      m.secs.push_back(text);
+      section_id++;
+   }
+   return m;
+}
+#else
+module clover::nir::spirv_to_nir(const module &mod, const device &dev, std::string &r_log)
+{
+   r_log += "SPIR-V support in clover is not enabled.\n";
+   throw error(CL_LINKER_NOT_AVAILABLE);
+}
+#endif
diff --git a/src/gallium/state_trackers/clover/nir/invocation.hpp b/src/gallium/state_trackers/clover/nir/invocation.hpp
new file mode 100644 (file)
index 0000000..41407a7
--- /dev/null
@@ -0,0 +1,36 @@
+//
+// Copyright 2019 Karol Herbst
+//
+// 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_NIR_INVOCATION_HPP
+#define CLOVER_NIR_INVOCATION_HPP
+
+#include "core/module.hpp"
+
+namespace clover {
+   class device;
+   namespace nir {
+      // converts a given spirv module to nir
+      module spirv_to_nir(const module &mod, const device &dev, std::string &r_log);
+   }
+}
+
+#endif
index df6c8e81f558c52cc8accf2d9c452be385aeb863..f3143cef95c14f5d1f984a14cec426cdd1ce1c7a 100644 (file)
@@ -418,6 +418,9 @@ st_release_cp_variants(struct st_context *st, struct st_compute_program *stcp)
          /* ??? */
          stcp->tgsi.prog = NULL;
          break;
+      case PIPE_SHADER_IR_NIR_SERIALIZED:
+         unreachable("serialized nirs aren't passed through st/mesa");
+         break;
       }
    }
 }