From 2043c5f37cf1f1b64a8f5d9ae04bbf8e7860f616 Mon Sep 17 00:00:00 2001
From: Pierre Moreau <pierre.morrow@free.fr>
Date: Sat, 10 Feb 2018 21:44:45 +0100
Subject: clover/llvm: Add functions for compiling from source to SPIR-V

Reviewed-by: Karol Herbst <kherbst@redhat.com>
Reviewed-by: Francisco Jerez <currojerez@riseup.net>
---
 .../state_trackers/clover/llvm/invocation.cpp      | 99 ++++++++++++++++++----
 .../state_trackers/clover/llvm/invocation.hpp      |  8 ++
 src/gallium/state_trackers/clover/meson.build      |  2 +-
 3 files changed, 92 insertions(+), 17 deletions(-)

diff --git a/src/gallium/state_trackers/clover/llvm/invocation.cpp b/src/gallium/state_trackers/clover/llvm/invocation.cpp
index deaf1cc4b43..d71ddf8b01b 100644
--- a/src/gallium/state_trackers/clover/llvm/invocation.cpp
+++ b/src/gallium/state_trackers/clover/llvm/invocation.cpp
@@ -30,6 +30,9 @@
 #include <llvm/Support/raw_ostream.h>
 #include <llvm/Transforms/IPO/PassManagerBuilder.h>
 #include <llvm-c/Target.h>
+#ifdef HAVE_CLOVER_SPIRV
+#include <LLVMSPIRVLib/LLVMSPIRVLib.h>
+#endif
 
 #include <clang/CodeGen/CodeGenAction.h>
 #include <clang/Lex/PreprocessorOptions.h>
@@ -51,6 +54,9 @@
 #include "llvm/invocation.hpp"
 #include "llvm/metadata.hpp"
 #include "llvm/util.hpp"
+#ifdef HAVE_CLOVER_SPIRV
+#include "spirv/invocation.hpp"
+#endif
 #include "util/algorithm.hpp"
 
 
@@ -182,7 +188,7 @@ namespace {
    }
 
    std::unique_ptr<clang::CompilerInstance>
-   create_compiler_instance(const device &dev,
+   create_compiler_instance(const device &dev, const std::string& ir_target,
                             const std::vector<std::string> &opts,
                             std::string &r_log) {
       std::unique_ptr<clang::CompilerInstance> c { new clang::CompilerInstance };
@@ -196,7 +202,7 @@ namespace {
       const std::vector<const char *> copts =
          map(std::mem_fn(&std::string::c_str), opts);
 
-      const target &target = dev.ir_target();
+      const target &target = ir_target;
       const std::string &device_clc_version = dev.device_clc_version();
 
       if (!compat::create_compiler_invocation_from_args(
@@ -235,19 +241,29 @@ namespace {
    compile(LLVMContext &ctx, clang::CompilerInstance &c,
            const std::string &name, const std::string &source,
            const header_map &headers, const device &dev,
-           const std::string &opts, std::string &r_log) {
+           const std::string &opts, bool use_libclc, std::string &r_log) {
       c.getFrontendOpts().ProgramAction = clang::frontend::EmitLLVMOnly;
       c.getHeaderSearchOpts().UseBuiltinIncludes = true;
       c.getHeaderSearchOpts().UseStandardSystemIncludes = true;
       c.getHeaderSearchOpts().ResourceDir = CLANG_RESOURCE_DIR;
 
-      // Add libclc generic search path
-      c.getHeaderSearchOpts().AddPath(LIBCLC_INCLUDEDIR,
-                                      clang::frontend::Angled,
-                                      false, false);
+      if (use_libclc) {
+         // Add libclc generic search path
+         c.getHeaderSearchOpts().AddPath(LIBCLC_INCLUDEDIR,
+                                         clang::frontend::Angled,
+                                         false, false);
+
+         // Add libclc include
+         c.getPreprocessorOpts().Includes.push_back("clc/clc.h");
+      } else {
+         // Add opencl-c generic search path
+         c.getHeaderSearchOpts().AddPath(CLANG_RESOURCE_DIR,
+                                         clang::frontend::Angled,
+                                         false, false);
 
-      // Add libclc include
-      c.getPreprocessorOpts().Includes.push_back("clc/clc.h");
+         // Add opencl include
+         c.getPreprocessorOpts().Includes.push_back("opencl-c.h");
+      }
 
       // Add definition for the OpenCL version
       c.getPreprocessorOpts().addMacroDef("__OPENCL_VERSION__=" +
@@ -279,8 +295,9 @@ namespace {
       // attribute will prevent Clang from creating illegal uses of
       // barrier() (e.g. Moving barrier() inside a conditional that is
       // no executed by all threads) during its optimizaton passes.
-      compat::add_link_bitcode_file(c.getCodeGenOpts(),
-                                    LIBCLC_LIBEXECDIR + dev.ir_target() + ".bc");
+      if (use_libclc)
+         compat::add_link_bitcode_file(c.getCodeGenOpts(),
+                                       LIBCLC_LIBEXECDIR + dev.ir_target() + ".bc");
 
       // Compile the code
       clang::EmitLLVMOnlyAction act(&ctx);
@@ -301,8 +318,10 @@ clover::llvm::compile_program(const std::string &source,
       debug::log(".cl", "// Options: " + opts + '\n' + source);
 
    auto ctx = create_context(r_log);
-   auto c = create_compiler_instance(dev, tokenize(opts + " input.cl"), r_log);
-   auto mod = compile(*ctx, *c, "input.cl", source, headers, dev, opts, r_log);
+   auto c = create_compiler_instance(dev, dev.ir_target(),
+                                     tokenize(opts + " input.cl"), r_log);
+   auto mod = compile(*ctx, *c, "input.cl", source, headers, dev, opts, true,
+                      r_log);
 
    if (has_flag(debug::llvm))
       debug::log(".ll", print_module_bitcode(*mod));
@@ -363,14 +382,14 @@ namespace {
 
 module
 clover::llvm::link_program(const std::vector<module> &modules,
-                           const device &dev,
-                           const std::string &opts, std::string &r_log) {
+                           const device &dev, const std::string &opts,
+                           std::string &r_log) {
    std::vector<std::string> options = tokenize(opts + " input.cl");
    const bool create_library = count("-create-library", options);
    erase_if(equals("-create-library"), options);
 
    auto ctx = create_context(r_log);
-   auto c = create_compiler_instance(dev, options, r_log);
+   auto c = create_compiler_instance(dev, dev.ir_target(), options, r_log);
    auto mod = link(*ctx, *c, modules, r_log);
 
    optimize(*mod, c->getCodeGenOpts().OptimizationLevel, !create_library);
@@ -395,3 +414,51 @@ clover::llvm::link_program(const std::vector<module> &modules,
       unreachable("Unsupported IR.");
    }
 }
+
+#ifdef HAVE_CLOVER_SPIRV
+module
+clover::llvm::compile_to_spirv(const std::string &source,
+                               const header_map &headers,
+                               const device &dev,
+                               const std::string &opts,
+                               std::string &r_log) {
+   if (has_flag(debug::clc))
+      debug::log(".cl", "// Options: " + opts + '\n' + source);
+
+   auto ctx = create_context(r_log);
+   const std::string target = dev.address_bits() == 32u ?
+      "-spir-unknown-unknown" :
+      "-spir64-unknown-unknown";
+   auto c = create_compiler_instance(dev, target,
+                                     tokenize(opts + " input.cl"), r_log);
+   auto mod = compile(*ctx, *c, "input.cl", source, headers, dev, opts, false,
+                      r_log);
+
+   if (has_flag(debug::llvm))
+      debug::log(".ll", print_module_bitcode(*mod));
+
+   std::string error_msg;
+   if (!::llvm::regularizeLlvmForSpirv(mod.get(), error_msg)) {
+      r_log += "Failed to regularize LLVM IR for SPIR-V: " + error_msg + ".\n";
+      throw error(CL_INVALID_VALUE);
+   }
+
+   std::ostringstream os;
+   if (!::llvm::writeSpirv(mod.get(), os, error_msg)) {
+      r_log += "Translation from LLVM IR to SPIR-V failed: " + error_msg + ".\n";
+      throw error(CL_INVALID_VALUE);
+   }
+
+   const std::string osContent = os.str();
+   std::vector<char> binary(osContent.begin(), osContent.end());
+   if (binary.empty()) {
+      r_log += "Failed to retrieve SPIR-V binary.\n";
+      throw error(CL_INVALID_VALUE);
+   }
+
+   if (has_flag(debug::spirv))
+      debug::log(".spvasm", spirv::print_module(binary, dev.device_version()));
+
+   return spirv::compile_program(binary, dev, r_log);
+}
+#endif
diff --git a/src/gallium/state_trackers/clover/llvm/invocation.hpp b/src/gallium/state_trackers/clover/llvm/invocation.hpp
index ff9caa457ca..1f0e9db2cf7 100644
--- a/src/gallium/state_trackers/clover/llvm/invocation.hpp
+++ b/src/gallium/state_trackers/clover/llvm/invocation.hpp
@@ -40,6 +40,14 @@ namespace clover {
                           const device &device,
                           const std::string &opts,
                           std::string &r_log);
+
+#ifdef HAVE_CLOVER_SPIRV
+      module compile_to_spirv(const std::string &source,
+                              const header_map &headers,
+                              const device &dev,
+                              const std::string &opts,
+                              std::string &r_log);
+#endif
    }
 }
 
diff --git a/src/gallium/state_trackers/clover/meson.build b/src/gallium/state_trackers/clover/meson.build
index 0d8e0675eb9..2f5b287d976 100644
--- a/src/gallium/state_trackers/clover/meson.build
+++ b/src/gallium/state_trackers/clover/meson.build
@@ -53,7 +53,7 @@ libclllvm = static_library(
       dep_llvm.version(), 'include',
     )),
   ],
-  dependencies : [dep_llvm, dep_elf],
+  dependencies : [dep_llvm, dep_elf, dep_llvmspirvlib],
   override_options : clover_cpp_std,
 )
 
-- 
cgit v1.2.3