17ec681f3Smrg//
27ec681f3Smrg// Copyright 2012-2016 Francisco Jerez
37ec681f3Smrg// Copyright 2012-2016 Advanced Micro Devices, Inc.
47ec681f3Smrg// Copyright 2014-2016 Jan Vesely
57ec681f3Smrg// Copyright 2014-2015 Serge Martin
67ec681f3Smrg// Copyright 2015 Zoltan Gilian
77ec681f3Smrg//
87ec681f3Smrg// Permission is hereby granted, free of charge, to any person obtaining a
97ec681f3Smrg// copy of this software and associated documentation files (the "Software"),
107ec681f3Smrg// to deal in the Software without restriction, including without limitation
117ec681f3Smrg// the rights to use, copy, modify, merge, publish, distribute, sublicense,
127ec681f3Smrg// and/or sell copies of the Software, and to permit persons to whom the
137ec681f3Smrg// Software is furnished to do so, subject to the following conditions:
147ec681f3Smrg//
157ec681f3Smrg// The above copyright notice and this permission notice shall be included in
167ec681f3Smrg// all copies or substantial portions of the Software.
177ec681f3Smrg//
187ec681f3Smrg// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
197ec681f3Smrg// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
207ec681f3Smrg// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
217ec681f3Smrg// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
227ec681f3Smrg// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
237ec681f3Smrg// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
247ec681f3Smrg// OTHER DEALINGS IN THE SOFTWARE.
257ec681f3Smrg//
267ec681f3Smrg
277ec681f3Smrg#include <llvm/IR/DiagnosticPrinter.h>
287ec681f3Smrg#include <llvm/IR/DiagnosticInfo.h>
297ec681f3Smrg#include <llvm/IR/LLVMContext.h>
307ec681f3Smrg#include <llvm/Support/raw_ostream.h>
317ec681f3Smrg#include <llvm/Transforms/IPO/PassManagerBuilder.h>
327ec681f3Smrg#include <llvm-c/Target.h>
337ec681f3Smrg#ifdef HAVE_CLOVER_SPIRV
347ec681f3Smrg#include <LLVMSPIRVLib/LLVMSPIRVLib.h>
357ec681f3Smrg#endif
367ec681f3Smrg
377ec681f3Smrg#include <clang/CodeGen/CodeGenAction.h>
387ec681f3Smrg#include <clang/Lex/PreprocessorOptions.h>
397ec681f3Smrg#include <clang/Frontend/TextDiagnosticBuffer.h>
407ec681f3Smrg#include <clang/Frontend/TextDiagnosticPrinter.h>
417ec681f3Smrg#include <clang/Basic/TargetInfo.h>
427ec681f3Smrg
437ec681f3Smrg// We need to include internal headers last, because the internal headers
447ec681f3Smrg// include CL headers which have #define's like:
457ec681f3Smrg//
467ec681f3Smrg//#define cl_khr_gl_sharing 1
477ec681f3Smrg//#define cl_khr_icd 1
487ec681f3Smrg//
497ec681f3Smrg// Which will break the compilation of clang/Basic/OpenCLOptions.h
507ec681f3Smrg
517ec681f3Smrg#include "core/error.hpp"
527ec681f3Smrg#include "llvm/codegen.hpp"
537ec681f3Smrg#include "llvm/compat.hpp"
547ec681f3Smrg#include "llvm/invocation.hpp"
557ec681f3Smrg#include "llvm/metadata.hpp"
567ec681f3Smrg#include "llvm/util.hpp"
577ec681f3Smrg#ifdef HAVE_CLOVER_SPIRV
587ec681f3Smrg#include "spirv/invocation.hpp"
597ec681f3Smrg#endif
607ec681f3Smrg#include "util/algorithm.hpp"
617ec681f3Smrg
627ec681f3Smrg
637ec681f3Smrgusing clover::binary;
647ec681f3Smrgusing clover::device;
657ec681f3Smrgusing clover::build_error;
667ec681f3Smrgusing clover::invalid_build_options_error;
677ec681f3Smrgusing clover::map;
687ec681f3Smrgusing clover::header_map;
697ec681f3Smrgusing namespace clover::llvm;
707ec681f3Smrg
717ec681f3Smrgusing ::llvm::Function;
727ec681f3Smrgusing ::llvm::LLVMContext;
737ec681f3Smrgusing ::llvm::Module;
747ec681f3Smrgusing ::llvm::raw_string_ostream;
757ec681f3Smrg
767ec681f3Smrgnamespace {
777ec681f3Smrg
787ec681f3Smrg   static const cl_version ANY_VERSION = CL_MAKE_VERSION(9, 9, 9);
797ec681f3Smrg   const cl_version cl_versions[] = {
807ec681f3Smrg      CL_MAKE_VERSION(1, 1, 0),
817ec681f3Smrg      CL_MAKE_VERSION(1, 2, 0),
827ec681f3Smrg      CL_MAKE_VERSION(2, 0, 0),
837ec681f3Smrg      CL_MAKE_VERSION(2, 1, 0),
847ec681f3Smrg      CL_MAKE_VERSION(2, 2, 0),
857ec681f3Smrg      CL_MAKE_VERSION(3, 0, 0),
867ec681f3Smrg   };
877ec681f3Smrg
887ec681f3Smrg    struct clc_version_lang_std {
897ec681f3Smrg        cl_version version_number; // CLC Version
907ec681f3Smrg        clang::LangStandard::Kind clc_lang_standard;
917ec681f3Smrg    };
927ec681f3Smrg
937ec681f3Smrg    const clc_version_lang_std cl_version_lang_stds[] = {
947ec681f3Smrg       { CL_MAKE_VERSION(1, 0, 0), clang::LangStandard::lang_opencl10},
957ec681f3Smrg       { CL_MAKE_VERSION(1, 1, 0), clang::LangStandard::lang_opencl11},
967ec681f3Smrg       { CL_MAKE_VERSION(1, 2, 0), clang::LangStandard::lang_opencl12},
977ec681f3Smrg       { CL_MAKE_VERSION(2, 0, 0), clang::LangStandard::lang_opencl20},
987ec681f3Smrg#if LLVM_VERSION_MAJOR >= 12
997ec681f3Smrg       { CL_MAKE_VERSION(3, 0, 0), clang::LangStandard::lang_opencl30},
1007ec681f3Smrg#endif
1017ec681f3Smrg    };
1027ec681f3Smrg
1037ec681f3Smrg   bool
1047ec681f3Smrg   are_equal(cl_version_khr version1, cl_version_khr version2,
1057ec681f3Smrg             bool ignore_patch_version = false) {
1067ec681f3Smrg      if (ignore_patch_version) {
1077ec681f3Smrg         version1 &= ~CL_VERSION_PATCH_MASK_KHR;
1087ec681f3Smrg         version2 &= ~CL_VERSION_PATCH_MASK_KHR;
1097ec681f3Smrg      }
1107ec681f3Smrg      return version1 == version2;
1117ec681f3Smrg   }
1127ec681f3Smrg
1137ec681f3Smrg   void
1147ec681f3Smrg   init_targets() {
1157ec681f3Smrg      static bool targets_initialized = false;
1167ec681f3Smrg      if (!targets_initialized) {
1177ec681f3Smrg         LLVMInitializeAllTargets();
1187ec681f3Smrg         LLVMInitializeAllTargetInfos();
1197ec681f3Smrg         LLVMInitializeAllTargetMCs();
1207ec681f3Smrg         LLVMInitializeAllAsmParsers();
1217ec681f3Smrg         LLVMInitializeAllAsmPrinters();
1227ec681f3Smrg         targets_initialized = true;
1237ec681f3Smrg      }
1247ec681f3Smrg   }
1257ec681f3Smrg
1267ec681f3Smrg   void
1277ec681f3Smrg   diagnostic_handler(const ::llvm::DiagnosticInfo &di, void *data) {
1287ec681f3Smrg      if (di.getSeverity() == ::llvm::DS_Error) {
1297ec681f3Smrg         raw_string_ostream os { *reinterpret_cast<std::string *>(data) };
1307ec681f3Smrg         ::llvm::DiagnosticPrinterRawOStream printer { os };
1317ec681f3Smrg         di.print(printer);
1327ec681f3Smrg         throw build_error();
1337ec681f3Smrg      }
1347ec681f3Smrg   }
1357ec681f3Smrg
1367ec681f3Smrg   std::unique_ptr<LLVMContext>
1377ec681f3Smrg   create_context(std::string &r_log) {
1387ec681f3Smrg      init_targets();
1397ec681f3Smrg      std::unique_ptr<LLVMContext> ctx { new LLVMContext };
1407ec681f3Smrg
1417ec681f3Smrg      ctx->setDiagnosticHandlerCallBack(diagnostic_handler, &r_log);
1427ec681f3Smrg      return ctx;
1437ec681f3Smrg   }
1447ec681f3Smrg
1457ec681f3Smrg   const struct clc_version_lang_std&
1467ec681f3Smrg   get_cl_lang_standard(unsigned requested, unsigned max = ANY_VERSION) {
1477ec681f3Smrg       for (const struct clc_version_lang_std &version : cl_version_lang_stds) {
1487ec681f3Smrg           if (version.version_number == max ||
1497ec681f3Smrg                   version.version_number == requested) {
1507ec681f3Smrg               return version;
1517ec681f3Smrg           }
1527ec681f3Smrg       }
1537ec681f3Smrg       throw build_error("Unknown/Unsupported language version");
1547ec681f3Smrg   }
1557ec681f3Smrg
1567ec681f3Smrg   const cl_version
1577ec681f3Smrg   get_cl_version(cl_version requested,
1587ec681f3Smrg                  cl_version max = ANY_VERSION) {
1597ec681f3Smrg      for (const auto &version : cl_versions) {
1607ec681f3Smrg         if (are_equal(version, max, true) ||
1617ec681f3Smrg             are_equal(version, requested, true)) {
1627ec681f3Smrg            return version;
1637ec681f3Smrg         }
1647ec681f3Smrg      }
1657ec681f3Smrg      throw build_error("Unknown/Unsupported language version");
1667ec681f3Smrg   }
1677ec681f3Smrg
1687ec681f3Smrg   clang::LangStandard::Kind
1697ec681f3Smrg   get_lang_standard_from_version(const cl_version input_version,
1707ec681f3Smrg                                  bool is_build_opt = false) {
1717ec681f3Smrg
1727ec681f3Smrg       //Per CL 2.0 spec, section 5.8.4.5:
1737ec681f3Smrg       //  If it's an option, use the value directly.
1747ec681f3Smrg       //  If it's a device version, clamp to max 1.x version, a.k.a. 1.2
1757ec681f3Smrg      const cl_version version =
1767ec681f3Smrg         get_cl_version(input_version, is_build_opt ? ANY_VERSION : 120);
1777ec681f3Smrg
1787ec681f3Smrg      const struct clc_version_lang_std standard =
1797ec681f3Smrg         get_cl_lang_standard(version);
1807ec681f3Smrg
1817ec681f3Smrg      return standard.clc_lang_standard;
1827ec681f3Smrg   }
1837ec681f3Smrg
1847ec681f3Smrg   clang::LangStandard::Kind
1857ec681f3Smrg   get_language_version(const std::vector<std::string> &opts,
1867ec681f3Smrg                        const cl_version device_version) {
1877ec681f3Smrg
1887ec681f3Smrg      const std::string search = "-cl-std=CL";
1897ec681f3Smrg
1907ec681f3Smrg      for (auto &opt: opts) {
1917ec681f3Smrg         auto pos = opt.find(search);
1927ec681f3Smrg         if (pos == 0){
1937ec681f3Smrg            std::stringstream ver_str(opt.substr(pos + search.size()));
1947ec681f3Smrg            unsigned int ver_major = 0;
1957ec681f3Smrg            char separator = '\0';
1967ec681f3Smrg            unsigned int ver_minor = 0;
1977ec681f3Smrg            ver_str >> ver_major >> separator >> ver_minor;
1987ec681f3Smrg            if (ver_str.fail() || ver_str.bad() || !ver_str.eof() ||
1997ec681f3Smrg                 separator != '.') {
2007ec681f3Smrg               throw build_error();
2017ec681f3Smrg            }
2027ec681f3Smrg            const auto ver = CL_MAKE_VERSION_KHR(ver_major, ver_minor, 0);
2037ec681f3Smrg            const auto device_ver = get_cl_version(device_version);
2047ec681f3Smrg            const auto requested = get_cl_version(ver);
2057ec681f3Smrg            if (requested > device_ver) {
2067ec681f3Smrg               throw build_error();
2077ec681f3Smrg            }
2087ec681f3Smrg            return get_lang_standard_from_version(ver, true);
2097ec681f3Smrg         }
2107ec681f3Smrg      }
2117ec681f3Smrg
2127ec681f3Smrg      return get_lang_standard_from_version(device_version);
2137ec681f3Smrg   }
2147ec681f3Smrg
2157ec681f3Smrg   std::unique_ptr<clang::CompilerInstance>
2167ec681f3Smrg   create_compiler_instance(const device &dev, const std::string& ir_target,
2177ec681f3Smrg                            const std::vector<std::string> &opts,
2187ec681f3Smrg                            std::string &r_log) {
2197ec681f3Smrg      std::unique_ptr<clang::CompilerInstance> c { new clang::CompilerInstance };
2207ec681f3Smrg      clang::TextDiagnosticBuffer *diag_buffer = new clang::TextDiagnosticBuffer;
2217ec681f3Smrg      clang::DiagnosticsEngine diag { new clang::DiagnosticIDs,
2227ec681f3Smrg            new clang::DiagnosticOptions, diag_buffer };
2237ec681f3Smrg
2247ec681f3Smrg      // Parse the compiler options.  A file name should be present at the end
2257ec681f3Smrg      // and must have the .cl extension in order for the CompilerInvocation
2267ec681f3Smrg      // class to recognize it as an OpenCL source file.
2277ec681f3Smrg#if LLVM_VERSION_MAJOR >= 12
2287ec681f3Smrg      std::vector<const char *> copts;
2297ec681f3Smrg      for (auto &opt : opts) {
2307ec681f3Smrg         if (opt == "-cl-denorms-are-zero")
2317ec681f3Smrg            copts.push_back("-fdenormal-fp-math=positive-zero");
2327ec681f3Smrg         else
2337ec681f3Smrg            copts.push_back(opt.c_str());
2347ec681f3Smrg      }
2357ec681f3Smrg#else
2367ec681f3Smrg      const std::vector<const char *> copts =
2377ec681f3Smrg         map(std::mem_fn(&std::string::c_str), opts);
2387ec681f3Smrg#endif
2397ec681f3Smrg
2407ec681f3Smrg      const target &target = ir_target;
2417ec681f3Smrg      const cl_version device_clc_version = dev.device_clc_version();
2427ec681f3Smrg
2437ec681f3Smrg      if (!compat::create_compiler_invocation_from_args(
2447ec681f3Smrg             c->getInvocation(), copts, diag))
2457ec681f3Smrg         throw invalid_build_options_error();
2467ec681f3Smrg
2477ec681f3Smrg      diag_buffer->FlushDiagnostics(diag);
2487ec681f3Smrg      if (diag.hasErrorOccurred())
2497ec681f3Smrg         throw invalid_build_options_error();
2507ec681f3Smrg
2517ec681f3Smrg      c->getTargetOpts().CPU = target.cpu;
2527ec681f3Smrg      c->getTargetOpts().Triple = target.triple;
2537ec681f3Smrg      c->getLangOpts().NoBuiltin = true;
2547ec681f3Smrg
2557ec681f3Smrg#if LLVM_VERSION_MAJOR >= 13
2567ec681f3Smrg      c->getTargetOpts().OpenCLExtensionsAsWritten.push_back("-__opencl_c_generic_address_space");
2577ec681f3Smrg      c->getTargetOpts().OpenCLExtensionsAsWritten.push_back("-__opencl_c_pipes");
2587ec681f3Smrg      c->getTargetOpts().OpenCLExtensionsAsWritten.push_back("-__opencl_c_device_enqueue");
2597ec681f3Smrg      c->getTargetOpts().OpenCLExtensionsAsWritten.push_back("-__opencl_c_program_scope_global_variables");
2607ec681f3Smrg      c->getTargetOpts().OpenCLExtensionsAsWritten.push_back("-__opencl_c_subgroups");
2617ec681f3Smrg      c->getTargetOpts().OpenCLExtensionsAsWritten.push_back("-__opencl_c_work_group_collective_functions");
2627ec681f3Smrg      c->getTargetOpts().OpenCLExtensionsAsWritten.push_back("-__opencl_c_atomic_scope_device");
2637ec681f3Smrg      c->getTargetOpts().OpenCLExtensionsAsWritten.push_back("-__opencl_c_atomic_order_seq_cst");
2647ec681f3Smrg#endif
2657ec681f3Smrg
2667ec681f3Smrg      // This is a workaround for a Clang bug which causes the number
2677ec681f3Smrg      // of warnings and errors to be printed to stderr.
2687ec681f3Smrg      // http://www.llvm.org/bugs/show_bug.cgi?id=19735
2697ec681f3Smrg      c->getDiagnosticOpts().ShowCarets = false;
2707ec681f3Smrg
2717ec681f3Smrg      compat::compiler_set_lang_defaults(c, compat::ik_opencl,
2727ec681f3Smrg                                ::llvm::Triple(target.triple),
2737ec681f3Smrg                                get_language_version(opts, device_clc_version));
2747ec681f3Smrg
2757ec681f3Smrg      c->createDiagnostics(new clang::TextDiagnosticPrinter(
2767ec681f3Smrg                              *new raw_string_ostream(r_log),
2777ec681f3Smrg                              &c->getDiagnosticOpts(), true));
2787ec681f3Smrg
2797ec681f3Smrg      c->setTarget(clang::TargetInfo::CreateTargetInfo(
2807ec681f3Smrg                           c->getDiagnostics(), c->getInvocation().TargetOpts));
2817ec681f3Smrg
2827ec681f3Smrg      return c;
2837ec681f3Smrg   }
2847ec681f3Smrg
2857ec681f3Smrg   std::unique_ptr<Module>
2867ec681f3Smrg   compile(LLVMContext &ctx, clang::CompilerInstance &c,
2877ec681f3Smrg           const std::string &name, const std::string &source,
2887ec681f3Smrg           const header_map &headers, const device &dev,
2897ec681f3Smrg           const std::string &opts, bool use_libclc, std::string &r_log) {
2907ec681f3Smrg      c.getFrontendOpts().ProgramAction = clang::frontend::EmitLLVMOnly;
2917ec681f3Smrg      c.getHeaderSearchOpts().UseBuiltinIncludes = true;
2927ec681f3Smrg      c.getHeaderSearchOpts().UseStandardSystemIncludes = true;
2937ec681f3Smrg      c.getHeaderSearchOpts().ResourceDir = CLANG_RESOURCE_DIR;
2947ec681f3Smrg
2957ec681f3Smrg      if (use_libclc) {
2967ec681f3Smrg         // Add libclc generic search path
2977ec681f3Smrg         c.getHeaderSearchOpts().AddPath(LIBCLC_INCLUDEDIR,
2987ec681f3Smrg                                         clang::frontend::Angled,
2997ec681f3Smrg                                         false, false);
3007ec681f3Smrg
3017ec681f3Smrg         // Add libclc include
3027ec681f3Smrg         c.getPreprocessorOpts().Includes.push_back("clc/clc.h");
3037ec681f3Smrg      } else {
3047ec681f3Smrg         // Add opencl-c generic search path
3057ec681f3Smrg         c.getHeaderSearchOpts().AddPath(CLANG_RESOURCE_DIR,
3067ec681f3Smrg                                         clang::frontend::Angled,
3077ec681f3Smrg                                         false, false);
3087ec681f3Smrg
3097ec681f3Smrg         // Add opencl include
3107ec681f3Smrg         c.getPreprocessorOpts().Includes.push_back("opencl-c.h");
3117ec681f3Smrg      }
3127ec681f3Smrg
3137ec681f3Smrg      // Add definition for the OpenCL version
3147ec681f3Smrg      const auto dev_version = dev.device_version();
3157ec681f3Smrg      c.getPreprocessorOpts().addMacroDef("__OPENCL_VERSION__=" +
3167ec681f3Smrg                                          std::to_string(CL_VERSION_MAJOR_KHR(dev_version)) +
3177ec681f3Smrg                                          std::to_string(CL_VERSION_MINOR_KHR(dev_version)) + "0");
3187ec681f3Smrg
3197ec681f3Smrg      if (CL_VERSION_MAJOR(dev.version) >= 3) {
3207ec681f3Smrg         const auto features = dev.opencl_c_features();
3217ec681f3Smrg         for (const auto &feature : features)
3227ec681f3Smrg            c.getPreprocessorOpts().addMacroDef(feature.name);
3237ec681f3Smrg      }
3247ec681f3Smrg
3257ec681f3Smrg      // clc.h requires that this macro be defined:
3267ec681f3Smrg      c.getPreprocessorOpts().addMacroDef("cl_clang_storage_class_specifiers");
3277ec681f3Smrg      c.getPreprocessorOpts().addRemappedFile(
3287ec681f3Smrg              name, ::llvm::MemoryBuffer::getMemBuffer(source).release());
3297ec681f3Smrg
3307ec681f3Smrg      if (headers.size()) {
3317ec681f3Smrg         const std::string tmp_header_path = "/tmp/clover/";
3327ec681f3Smrg
3337ec681f3Smrg         c.getHeaderSearchOpts().AddPath(tmp_header_path,
3347ec681f3Smrg                                         clang::frontend::Angled,
3357ec681f3Smrg                                         false, false);
3367ec681f3Smrg
3377ec681f3Smrg         for (const auto &header : headers)
3387ec681f3Smrg            c.getPreprocessorOpts().addRemappedFile(
3397ec681f3Smrg               tmp_header_path + header.first,
3407ec681f3Smrg               ::llvm::MemoryBuffer::getMemBuffer(header.second).release());
3417ec681f3Smrg      }
3427ec681f3Smrg
3437ec681f3Smrg      // Tell clang to link this file before performing any
3447ec681f3Smrg      // optimizations.  This is required so that we can replace calls
3457ec681f3Smrg      // to the OpenCL C barrier() builtin with calls to target
3467ec681f3Smrg      // intrinsics that have the noduplicate attribute.  This
3477ec681f3Smrg      // attribute will prevent Clang from creating illegal uses of
3487ec681f3Smrg      // barrier() (e.g. Moving barrier() inside a conditional that is
3497ec681f3Smrg      // no executed by all threads) during its optimizaton passes.
3507ec681f3Smrg      if (use_libclc) {
3517ec681f3Smrg         clang::CodeGenOptions::BitcodeFileToLink F;
3527ec681f3Smrg
3537ec681f3Smrg         F.Filename = LIBCLC_LIBEXECDIR + dev.ir_target() + ".bc";
3547ec681f3Smrg         F.PropagateAttrs = true;
3557ec681f3Smrg         F.LinkFlags = ::llvm::Linker::Flags::None;
3567ec681f3Smrg         c.getCodeGenOpts().LinkBitcodeFiles.emplace_back(F);
3577ec681f3Smrg      }
3587ec681f3Smrg
3597ec681f3Smrg      // undefine __IMAGE_SUPPORT__ for device without image support
3607ec681f3Smrg      if (!dev.image_support())
3617ec681f3Smrg         c.getPreprocessorOpts().addMacroUndef("__IMAGE_SUPPORT__");
3627ec681f3Smrg
3637ec681f3Smrg      // Compile the code
3647ec681f3Smrg      clang::EmitLLVMOnlyAction act(&ctx);
3657ec681f3Smrg      if (!c.ExecuteAction(act))
3667ec681f3Smrg         throw build_error();
3677ec681f3Smrg
3687ec681f3Smrg      return act.takeModule();
3697ec681f3Smrg   }
3707ec681f3Smrg
3717ec681f3Smrg#ifdef HAVE_CLOVER_SPIRV
3727ec681f3Smrg   SPIRV::TranslatorOpts
3737ec681f3Smrg   get_spirv_translator_options(const device &dev) {
3747ec681f3Smrg      const auto supported_versions = clover::spirv::supported_versions();
3757ec681f3Smrg      const auto max_supported = clover::spirv::to_spirv_version_encoding(supported_versions.back().version);
3767ec681f3Smrg      const auto maximum_spirv_version =
3777ec681f3Smrg         std::min(static_cast<SPIRV::VersionNumber>(max_supported),
3787ec681f3Smrg                  SPIRV::VersionNumber::MaximumVersion);
3797ec681f3Smrg
3807ec681f3Smrg      SPIRV::TranslatorOpts::ExtensionsStatusMap spirv_extensions;
3817ec681f3Smrg      for (auto &ext : clover::spirv::supported_extensions()) {
3827ec681f3Smrg         #define EXT(X) if (ext == #X) spirv_extensions.insert({ SPIRV::ExtensionID::X, true });
3837ec681f3Smrg         #include <LLVMSPIRVLib/LLVMSPIRVExtensions.inc>
3847ec681f3Smrg         #undef EXT
3857ec681f3Smrg      }
3867ec681f3Smrg
3877ec681f3Smrg      return SPIRV::TranslatorOpts(maximum_spirv_version, spirv_extensions);
3887ec681f3Smrg   }
3897ec681f3Smrg#endif
3907ec681f3Smrg}
3917ec681f3Smrg
3927ec681f3Smrgbinary
3937ec681f3Smrgclover::llvm::compile_program(const std::string &source,
3947ec681f3Smrg                              const header_map &headers,
3957ec681f3Smrg                              const device &dev,
3967ec681f3Smrg                              const std::string &opts,
3977ec681f3Smrg                              std::string &r_log) {
3987ec681f3Smrg   if (has_flag(debug::clc))
3997ec681f3Smrg      debug::log(".cl", "// Options: " + opts + '\n' + source);
4007ec681f3Smrg
4017ec681f3Smrg   auto ctx = create_context(r_log);
4027ec681f3Smrg   auto c = create_compiler_instance(dev, dev.ir_target(),
4037ec681f3Smrg                                     tokenize(opts + " input.cl"), r_log);
4047ec681f3Smrg   auto mod = compile(*ctx, *c, "input.cl", source, headers, dev, opts, true,
4057ec681f3Smrg                      r_log);
4067ec681f3Smrg
4077ec681f3Smrg   if (has_flag(debug::llvm))
4087ec681f3Smrg      debug::log(".ll", print_module_bitcode(*mod));
4097ec681f3Smrg
4107ec681f3Smrg   return build_module_library(*mod, binary::section::text_intermediate);
4117ec681f3Smrg}
4127ec681f3Smrg
4137ec681f3Smrgnamespace {
4147ec681f3Smrg   void
4157ec681f3Smrg   optimize(Module &mod, unsigned optimization_level,
4167ec681f3Smrg            bool internalize_symbols) {
4177ec681f3Smrg      ::llvm::legacy::PassManager pm;
4187ec681f3Smrg
4197ec681f3Smrg      // By default, the function internalizer pass will look for a function
4207ec681f3Smrg      // called "main" and then mark all other functions as internal.  Marking
4217ec681f3Smrg      // functions as internal enables the optimizer to perform optimizations
4227ec681f3Smrg      // like function inlining and global dead-code elimination.
4237ec681f3Smrg      //
4247ec681f3Smrg      // When there is no "main" function in a binary, the internalize pass will
4257ec681f3Smrg      // treat the binary like a library, and it won't internalize any functions.
4267ec681f3Smrg      // Since there is no "main" function in our kernels, we need to tell
4277ec681f3Smrg      // the internalizer pass that this binary is not a library by passing a
4287ec681f3Smrg      // list of kernel functions to the internalizer.  The internalizer will
4297ec681f3Smrg      // treat the functions in the list as "main" functions and internalize
4307ec681f3Smrg      // all of the other functions.
4317ec681f3Smrg      if (internalize_symbols) {
4327ec681f3Smrg         std::vector<std::string> names =
4337ec681f3Smrg            map(std::mem_fn(&Function::getName), get_kernels(mod));
4347ec681f3Smrg         pm.add(::llvm::createInternalizePass(
4357ec681f3Smrg                      [=](const ::llvm::GlobalValue &gv) {
4367ec681f3Smrg                         return std::find(names.begin(), names.end(),
4377ec681f3Smrg                                          gv.getName()) != names.end();
4387ec681f3Smrg                      }));
4397ec681f3Smrg      }
4407ec681f3Smrg
4417ec681f3Smrg      ::llvm::PassManagerBuilder pmb;
4427ec681f3Smrg      pmb.OptLevel = optimization_level;
4437ec681f3Smrg      pmb.LibraryInfo = new ::llvm::TargetLibraryInfoImpl(
4447ec681f3Smrg         ::llvm::Triple(mod.getTargetTriple()));
4457ec681f3Smrg      pmb.populateModulePassManager(pm);
4467ec681f3Smrg      pm.run(mod);
4477ec681f3Smrg   }
4487ec681f3Smrg
4497ec681f3Smrg   std::unique_ptr<Module>
4507ec681f3Smrg   link(LLVMContext &ctx, const clang::CompilerInstance &c,
4517ec681f3Smrg        const std::vector<binary> &binaries, std::string &r_log) {
4527ec681f3Smrg      std::unique_ptr<Module> mod { new Module("link", ctx) };
4537ec681f3Smrg      std::unique_ptr< ::llvm::Linker> linker { new ::llvm::Linker(*mod) };
4547ec681f3Smrg
4557ec681f3Smrg      for (auto &b : binaries) {
4567ec681f3Smrg         if (linker->linkInModule(parse_module_library(b, ctx, r_log)))
4577ec681f3Smrg            throw build_error();
4587ec681f3Smrg      }
4597ec681f3Smrg
4607ec681f3Smrg      return mod;
4617ec681f3Smrg   }
4627ec681f3Smrg}
4637ec681f3Smrg
4647ec681f3Smrgbinary
4657ec681f3Smrgclover::llvm::link_program(const std::vector<binary> &binaries,
4667ec681f3Smrg                           const device &dev, const std::string &opts,
4677ec681f3Smrg                           std::string &r_log) {
4687ec681f3Smrg   std::vector<std::string> options = tokenize(opts + " input.cl");
4697ec681f3Smrg   const bool create_library = count("-create-library", options);
4707ec681f3Smrg   erase_if(equals("-create-library"), options);
4717ec681f3Smrg
4727ec681f3Smrg   auto ctx = create_context(r_log);
4737ec681f3Smrg   auto c = create_compiler_instance(dev, dev.ir_target(), options, r_log);
4747ec681f3Smrg   auto mod = link(*ctx, *c, binaries, r_log);
4757ec681f3Smrg
4767ec681f3Smrg   optimize(*mod, c->getCodeGenOpts().OptimizationLevel, !create_library);
4777ec681f3Smrg
4787ec681f3Smrg   static std::atomic_uint seq(0);
4797ec681f3Smrg   const std::string id = "." + mod->getModuleIdentifier() + "-" +
4807ec681f3Smrg      std::to_string(seq++);
4817ec681f3Smrg
4827ec681f3Smrg   if (has_flag(debug::llvm))
4837ec681f3Smrg      debug::log(id + ".ll", print_module_bitcode(*mod));
4847ec681f3Smrg
4857ec681f3Smrg   if (create_library) {
4867ec681f3Smrg      return build_module_library(*mod, binary::section::text_library);
4877ec681f3Smrg
4887ec681f3Smrg   } else if (dev.ir_format() == PIPE_SHADER_IR_NATIVE) {
4897ec681f3Smrg      if (has_flag(debug::native))
4907ec681f3Smrg         debug::log(id +  ".asm", print_module_native(*mod, dev.ir_target()));
4917ec681f3Smrg
4927ec681f3Smrg      return build_module_native(*mod, dev.ir_target(), *c, r_log);
4937ec681f3Smrg
4947ec681f3Smrg   } else {
4957ec681f3Smrg      unreachable("Unsupported IR.");
4967ec681f3Smrg   }
4977ec681f3Smrg}
4987ec681f3Smrg
4997ec681f3Smrg#ifdef HAVE_CLOVER_SPIRV
5007ec681f3Smrgbinary
5017ec681f3Smrgclover::llvm::compile_to_spirv(const std::string &source,
5027ec681f3Smrg                               const header_map &headers,
5037ec681f3Smrg                               const device &dev,
5047ec681f3Smrg                               const std::string &opts,
5057ec681f3Smrg                               std::string &r_log) {
5067ec681f3Smrg   if (has_flag(debug::clc))
5077ec681f3Smrg      debug::log(".cl", "// Options: " + opts + '\n' + source);
5087ec681f3Smrg
5097ec681f3Smrg   auto ctx = create_context(r_log);
5107ec681f3Smrg   const std::string target = dev.address_bits() == 32u ?
5117ec681f3Smrg      "-spir-unknown-unknown" :
5127ec681f3Smrg      "-spir64-unknown-unknown";
5137ec681f3Smrg   auto c = create_compiler_instance(dev, target,
5147ec681f3Smrg                                     tokenize(opts + " -O0 -fgnu89-inline input.cl"), r_log);
5157ec681f3Smrg   auto mod = compile(*ctx, *c, "input.cl", source, headers, dev, opts, false,
5167ec681f3Smrg                      r_log);
5177ec681f3Smrg
5187ec681f3Smrg   if (has_flag(debug::llvm))
5197ec681f3Smrg      debug::log(".ll", print_module_bitcode(*mod));
5207ec681f3Smrg
5217ec681f3Smrg   const auto spirv_options = get_spirv_translator_options(dev);
5227ec681f3Smrg
5237ec681f3Smrg   std::string error_msg;
5247ec681f3Smrg   std::ostringstream os;
5257ec681f3Smrg   if (!::llvm::writeSpirv(mod.get(), spirv_options, os, error_msg)) {
5267ec681f3Smrg      r_log += "Translation from LLVM IR to SPIR-V failed: " + error_msg + ".\n";
5277ec681f3Smrg      throw error(CL_INVALID_VALUE);
5287ec681f3Smrg   }
5297ec681f3Smrg
5307ec681f3Smrg   const std::string osContent = os.str();
5317ec681f3Smrg   std::string binary(osContent.begin(), osContent.end());
5327ec681f3Smrg   if (binary.empty()) {
5337ec681f3Smrg      r_log += "Failed to retrieve SPIR-V binary.\n";
5347ec681f3Smrg      throw error(CL_INVALID_VALUE);
5357ec681f3Smrg   }
5367ec681f3Smrg
5377ec681f3Smrg   if (has_flag(debug::spirv))
5387ec681f3Smrg      debug::log(".spvasm", spirv::print_module(binary, dev.device_version()));
5397ec681f3Smrg
5407ec681f3Smrg   return spirv::compile_program(binary, dev, r_log);
5417ec681f3Smrg}
5427ec681f3Smrg#endif
543