Path: blob/21.2-virgl/src/gallium/frontends/clover/llvm/invocation.cpp
4573 views
//1// Copyright 2012-2016 Francisco Jerez2// Copyright 2012-2016 Advanced Micro Devices, Inc.3// Copyright 2014-2016 Jan Vesely4// Copyright 2014-2015 Serge Martin5// Copyright 2015 Zoltan Gilian6//7// Permission is hereby granted, free of charge, to any person obtaining a8// copy of this software and associated documentation files (the "Software"),9// to deal in the Software without restriction, including without limitation10// the rights to use, copy, modify, merge, publish, distribute, sublicense,11// and/or sell copies of the Software, and to permit persons to whom the12// Software is furnished to do so, subject to the following conditions:13//14// The above copyright notice and this permission notice shall be included in15// all copies or substantial portions of the Software.16//17// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR18// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,19// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL20// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR21// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,22// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR23// OTHER DEALINGS IN THE SOFTWARE.24//2526#include <llvm/IR/DiagnosticPrinter.h>27#include <llvm/IR/DiagnosticInfo.h>28#include <llvm/IR/LLVMContext.h>29#include <llvm/Support/raw_ostream.h>30#include <llvm/Transforms/IPO/PassManagerBuilder.h>31#include <llvm-c/Target.h>32#ifdef HAVE_CLOVER_SPIRV33#include <LLVMSPIRVLib/LLVMSPIRVLib.h>34#endif3536#include <clang/CodeGen/CodeGenAction.h>37#include <clang/Lex/PreprocessorOptions.h>38#include <clang/Frontend/TextDiagnosticBuffer.h>39#include <clang/Frontend/TextDiagnosticPrinter.h>40#include <clang/Basic/TargetInfo.h>4142// We need to include internal headers last, because the internal headers43// include CL headers which have #define's like:44//45//#define cl_khr_gl_sharing 146//#define cl_khr_icd 147//48// Which will break the compilation of clang/Basic/OpenCLOptions.h4950#include "core/error.hpp"51#include "llvm/codegen.hpp"52#include "llvm/compat.hpp"53#include "llvm/invocation.hpp"54#include "llvm/metadata.hpp"55#include "llvm/util.hpp"56#ifdef HAVE_CLOVER_SPIRV57#include "spirv/invocation.hpp"58#endif59#include "util/algorithm.hpp"606162using clover::module;63using clover::device;64using clover::build_error;65using clover::invalid_build_options_error;66using clover::map;67using clover::header_map;68using namespace clover::llvm;6970using ::llvm::Function;71using ::llvm::LLVMContext;72using ::llvm::Module;73using ::llvm::raw_string_ostream;7475namespace {7677static const cl_version ANY_VERSION = CL_MAKE_VERSION(9, 9, 9);78const cl_version cl_versions[] = {79CL_MAKE_VERSION(1, 1, 0),80CL_MAKE_VERSION(1, 2, 0),81CL_MAKE_VERSION(2, 0, 0),82CL_MAKE_VERSION(2, 1, 0),83CL_MAKE_VERSION(2, 2, 0),84CL_MAKE_VERSION(3, 0, 0),85};8687struct clc_version_lang_std {88cl_version version_number; // CLC Version89clang::LangStandard::Kind clc_lang_standard;90};9192const clc_version_lang_std cl_version_lang_stds[] = {93{ CL_MAKE_VERSION(1, 0, 0), clang::LangStandard::lang_opencl10},94{ CL_MAKE_VERSION(1, 1, 0), clang::LangStandard::lang_opencl11},95{ CL_MAKE_VERSION(1, 2, 0), clang::LangStandard::lang_opencl12},96{ CL_MAKE_VERSION(2, 0, 0), clang::LangStandard::lang_opencl20},97#if LLVM_VERSION_MAJOR >= 1298{ CL_MAKE_VERSION(3, 0, 0), clang::LangStandard::lang_opencl30},99#endif100};101102bool103are_equal(cl_version_khr version1, cl_version_khr version2,104bool ignore_patch_version = false) {105if (ignore_patch_version) {106version1 &= ~CL_VERSION_PATCH_MASK_KHR;107version2 &= ~CL_VERSION_PATCH_MASK_KHR;108}109return version1 == version2;110}111112void113init_targets() {114static bool targets_initialized = false;115if (!targets_initialized) {116LLVMInitializeAllTargets();117LLVMInitializeAllTargetInfos();118LLVMInitializeAllTargetMCs();119LLVMInitializeAllAsmParsers();120LLVMInitializeAllAsmPrinters();121targets_initialized = true;122}123}124125void126diagnostic_handler(const ::llvm::DiagnosticInfo &di, void *data) {127if (di.getSeverity() == ::llvm::DS_Error) {128raw_string_ostream os { *reinterpret_cast<std::string *>(data) };129::llvm::DiagnosticPrinterRawOStream printer { os };130di.print(printer);131throw build_error();132}133}134135std::unique_ptr<LLVMContext>136create_context(std::string &r_log) {137init_targets();138std::unique_ptr<LLVMContext> ctx { new LLVMContext };139140ctx->setDiagnosticHandlerCallBack(diagnostic_handler, &r_log);141return ctx;142}143144const struct clc_version_lang_std&145get_cl_lang_standard(unsigned requested, unsigned max = ANY_VERSION) {146for (const struct clc_version_lang_std &version : cl_version_lang_stds) {147if (version.version_number == max ||148version.version_number == requested) {149return version;150}151}152throw build_error("Unknown/Unsupported language version");153}154155const cl_version156get_cl_version(cl_version requested,157cl_version max = ANY_VERSION) {158for (const auto &version : cl_versions) {159if (are_equal(version, max, true) ||160are_equal(version, requested, true)) {161return version;162}163}164throw build_error("Unknown/Unsupported language version");165}166167clang::LangStandard::Kind168get_lang_standard_from_version(const cl_version input_version,169bool is_build_opt = false) {170171//Per CL 2.0 spec, section 5.8.4.5:172// If it's an option, use the value directly.173// If it's a device version, clamp to max 1.x version, a.k.a. 1.2174const cl_version version =175get_cl_version(input_version, is_build_opt ? ANY_VERSION : 120);176177const struct clc_version_lang_std standard =178get_cl_lang_standard(version);179180return standard.clc_lang_standard;181}182183clang::LangStandard::Kind184get_language_version(const std::vector<std::string> &opts,185const cl_version device_version) {186187const std::string search = "-cl-std=CL";188189for (auto &opt: opts) {190auto pos = opt.find(search);191if (pos == 0){192std::stringstream ver_str(opt.substr(pos + search.size()));193unsigned int ver_major = 0;194char separator = '\0';195unsigned int ver_minor = 0;196ver_str >> ver_major >> separator >> ver_minor;197if (ver_str.fail() || ver_str.bad() || !ver_str.eof() ||198separator != '.') {199throw build_error();200}201const auto ver = CL_MAKE_VERSION_KHR(ver_major, ver_minor, 0);202const auto device_ver = get_cl_version(device_version);203const auto requested = get_cl_version(ver);204if (requested > device_ver) {205throw build_error();206}207return get_lang_standard_from_version(ver, true);208}209}210211return get_lang_standard_from_version(device_version);212}213214std::unique_ptr<clang::CompilerInstance>215create_compiler_instance(const device &dev, const std::string& ir_target,216const std::vector<std::string> &opts,217std::string &r_log) {218std::unique_ptr<clang::CompilerInstance> c { new clang::CompilerInstance };219clang::TextDiagnosticBuffer *diag_buffer = new clang::TextDiagnosticBuffer;220clang::DiagnosticsEngine diag { new clang::DiagnosticIDs,221new clang::DiagnosticOptions, diag_buffer };222223// Parse the compiler options. A file name should be present at the end224// and must have the .cl extension in order for the CompilerInvocation225// class to recognize it as an OpenCL source file.226const std::vector<const char *> copts =227map(std::mem_fn(&std::string::c_str), opts);228229const target &target = ir_target;230const cl_version device_clc_version = dev.device_clc_version();231232if (!compat::create_compiler_invocation_from_args(233c->getInvocation(), copts, diag))234throw invalid_build_options_error();235236diag_buffer->FlushDiagnostics(diag);237if (diag.hasErrorOccurred())238throw invalid_build_options_error();239240c->getTargetOpts().CPU = target.cpu;241c->getTargetOpts().Triple = target.triple;242c->getLangOpts().NoBuiltin = true;243244// This is a workaround for a Clang bug which causes the number245// of warnings and errors to be printed to stderr.246// http://www.llvm.org/bugs/show_bug.cgi?id=19735247c->getDiagnosticOpts().ShowCarets = false;248249compat::compiler_set_lang_defaults(c, compat::ik_opencl,250::llvm::Triple(target.triple),251get_language_version(opts, device_clc_version));252253c->createDiagnostics(new clang::TextDiagnosticPrinter(254*new raw_string_ostream(r_log),255&c->getDiagnosticOpts(), true));256257c->setTarget(clang::TargetInfo::CreateTargetInfo(258c->getDiagnostics(), c->getInvocation().TargetOpts));259260return c;261}262263std::unique_ptr<Module>264compile(LLVMContext &ctx, clang::CompilerInstance &c,265const std::string &name, const std::string &source,266const header_map &headers, const device &dev,267const std::string &opts, bool use_libclc, std::string &r_log) {268c.getFrontendOpts().ProgramAction = clang::frontend::EmitLLVMOnly;269c.getHeaderSearchOpts().UseBuiltinIncludes = true;270c.getHeaderSearchOpts().UseStandardSystemIncludes = true;271c.getHeaderSearchOpts().ResourceDir = CLANG_RESOURCE_DIR;272273if (use_libclc) {274// Add libclc generic search path275c.getHeaderSearchOpts().AddPath(LIBCLC_INCLUDEDIR,276clang::frontend::Angled,277false, false);278279// Add libclc include280c.getPreprocessorOpts().Includes.push_back("clc/clc.h");281} else {282// Add opencl-c generic search path283c.getHeaderSearchOpts().AddPath(CLANG_RESOURCE_DIR,284clang::frontend::Angled,285false, false);286287// Add opencl include288c.getPreprocessorOpts().Includes.push_back("opencl-c.h");289}290291// Add definition for the OpenCL version292const auto dev_version = dev.device_version();293c.getPreprocessorOpts().addMacroDef("__OPENCL_VERSION__=" +294std::to_string(CL_VERSION_MAJOR_KHR(dev_version)) +295std::to_string(CL_VERSION_MINOR_KHR(dev_version)) + "0");296297if (CL_VERSION_MAJOR(dev.version) >= 3) {298const auto features = dev.opencl_c_features();299for (const auto &feature : features)300c.getPreprocessorOpts().addMacroDef(feature.name);301}302303// clc.h requires that this macro be defined:304c.getPreprocessorOpts().addMacroDef("cl_clang_storage_class_specifiers");305c.getPreprocessorOpts().addRemappedFile(306name, ::llvm::MemoryBuffer::getMemBuffer(source).release());307308if (headers.size()) {309const std::string tmp_header_path = "/tmp/clover/";310311c.getHeaderSearchOpts().AddPath(tmp_header_path,312clang::frontend::Angled,313false, false);314315for (const auto &header : headers)316c.getPreprocessorOpts().addRemappedFile(317tmp_header_path + header.first,318::llvm::MemoryBuffer::getMemBuffer(header.second).release());319}320321// Tell clang to link this file before performing any322// optimizations. This is required so that we can replace calls323// to the OpenCL C barrier() builtin with calls to target324// intrinsics that have the noduplicate attribute. This325// attribute will prevent Clang from creating illegal uses of326// barrier() (e.g. Moving barrier() inside a conditional that is327// no executed by all threads) during its optimizaton passes.328if (use_libclc) {329clang::CodeGenOptions::BitcodeFileToLink F;330331F.Filename = LIBCLC_LIBEXECDIR + dev.ir_target() + ".bc";332F.PropagateAttrs = true;333F.LinkFlags = ::llvm::Linker::Flags::None;334c.getCodeGenOpts().LinkBitcodeFiles.emplace_back(F);335}336337// undefine __IMAGE_SUPPORT__ for device without image support338if (!dev.image_support())339c.getPreprocessorOpts().addMacroUndef("__IMAGE_SUPPORT__");340341// Compile the code342clang::EmitLLVMOnlyAction act(&ctx);343if (!c.ExecuteAction(act))344throw build_error();345346return act.takeModule();347}348349#ifdef HAVE_CLOVER_SPIRV350SPIRV::TranslatorOpts351get_spirv_translator_options(const device &dev) {352const auto supported_versions = clover::spirv::supported_versions();353const auto max_supported = clover::spirv::to_spirv_version_encoding(supported_versions.back().version);354const auto maximum_spirv_version =355std::min(static_cast<SPIRV::VersionNumber>(max_supported),356SPIRV::VersionNumber::MaximumVersion);357358SPIRV::TranslatorOpts::ExtensionsStatusMap spirv_extensions;359for (auto &ext : clover::spirv::supported_extensions()) {360#define EXT(X) if (ext == #X) spirv_extensions.insert({ SPIRV::ExtensionID::X, true });361#include <LLVMSPIRVLib/LLVMSPIRVExtensions.inc>362#undef EXT363}364365return SPIRV::TranslatorOpts(maximum_spirv_version, spirv_extensions);366}367#endif368}369370module371clover::llvm::compile_program(const std::string &source,372const header_map &headers,373const device &dev,374const std::string &opts,375std::string &r_log) {376if (has_flag(debug::clc))377debug::log(".cl", "// Options: " + opts + '\n' + source);378379auto ctx = create_context(r_log);380auto c = create_compiler_instance(dev, dev.ir_target(),381tokenize(opts + " input.cl"), r_log);382auto mod = compile(*ctx, *c, "input.cl", source, headers, dev, opts, true,383r_log);384385if (has_flag(debug::llvm))386debug::log(".ll", print_module_bitcode(*mod));387388return build_module_library(*mod, module::section::text_intermediate);389}390391namespace {392void393optimize(Module &mod, unsigned optimization_level,394bool internalize_symbols) {395::llvm::legacy::PassManager pm;396397// By default, the function internalizer pass will look for a function398// called "main" and then mark all other functions as internal. Marking399// functions as internal enables the optimizer to perform optimizations400// like function inlining and global dead-code elimination.401//402// When there is no "main" function in a module, the internalize pass will403// treat the module like a library, and it won't internalize any functions.404// Since there is no "main" function in our kernels, we need to tell405// the internalizer pass that this module is not a library by passing a406// list of kernel functions to the internalizer. The internalizer will407// treat the functions in the list as "main" functions and internalize408// all of the other functions.409if (internalize_symbols) {410std::vector<std::string> names =411map(std::mem_fn(&Function::getName), get_kernels(mod));412pm.add(::llvm::createInternalizePass(413[=](const ::llvm::GlobalValue &gv) {414return std::find(names.begin(), names.end(),415gv.getName()) != names.end();416}));417}418419::llvm::PassManagerBuilder pmb;420pmb.OptLevel = optimization_level;421pmb.LibraryInfo = new ::llvm::TargetLibraryInfoImpl(422::llvm::Triple(mod.getTargetTriple()));423pmb.populateModulePassManager(pm);424pm.run(mod);425}426427std::unique_ptr<Module>428link(LLVMContext &ctx, const clang::CompilerInstance &c,429const std::vector<module> &modules, std::string &r_log) {430std::unique_ptr<Module> mod { new Module("link", ctx) };431std::unique_ptr< ::llvm::Linker> linker { new ::llvm::Linker(*mod) };432433for (auto &m : modules) {434if (linker->linkInModule(parse_module_library(m, ctx, r_log)))435throw build_error();436}437438return mod;439}440}441442module443clover::llvm::link_program(const std::vector<module> &modules,444const device &dev, const std::string &opts,445std::string &r_log) {446std::vector<std::string> options = tokenize(opts + " input.cl");447const bool create_library = count("-create-library", options);448erase_if(equals("-create-library"), options);449450auto ctx = create_context(r_log);451auto c = create_compiler_instance(dev, dev.ir_target(), options, r_log);452auto mod = link(*ctx, *c, modules, r_log);453454optimize(*mod, c->getCodeGenOpts().OptimizationLevel, !create_library);455456static std::atomic_uint seq(0);457const std::string id = "." + mod->getModuleIdentifier() + "-" +458std::to_string(seq++);459460if (has_flag(debug::llvm))461debug::log(id + ".ll", print_module_bitcode(*mod));462463if (create_library) {464return build_module_library(*mod, module::section::text_library);465466} else if (dev.ir_format() == PIPE_SHADER_IR_NATIVE) {467if (has_flag(debug::native))468debug::log(id + ".asm", print_module_native(*mod, dev.ir_target()));469470return build_module_native(*mod, dev.ir_target(), *c, r_log);471472} else {473unreachable("Unsupported IR.");474}475}476477#ifdef HAVE_CLOVER_SPIRV478module479clover::llvm::compile_to_spirv(const std::string &source,480const header_map &headers,481const device &dev,482const std::string &opts,483std::string &r_log) {484if (has_flag(debug::clc))485debug::log(".cl", "// Options: " + opts + '\n' + source);486487auto ctx = create_context(r_log);488const std::string target = dev.address_bits() == 32u ?489"-spir-unknown-unknown" :490"-spir64-unknown-unknown";491auto c = create_compiler_instance(dev, target,492tokenize(opts + " -O0 -fgnu89-inline input.cl"), r_log);493auto mod = compile(*ctx, *c, "input.cl", source, headers, dev, opts, false,494r_log);495496if (has_flag(debug::llvm))497debug::log(".ll", print_module_bitcode(*mod));498499const auto spirv_options = get_spirv_translator_options(dev);500501std::string error_msg;502std::ostringstream os;503if (!::llvm::writeSpirv(mod.get(), spirv_options, os, error_msg)) {504r_log += "Translation from LLVM IR to SPIR-V failed: " + error_msg + ".\n";505throw error(CL_INVALID_VALUE);506}507508const std::string osContent = os.str();509std::string binary(osContent.begin(), osContent.end());510if (binary.empty()) {511r_log += "Failed to retrieve SPIR-V binary.\n";512throw error(CL_INVALID_VALUE);513}514515if (has_flag(debug::spirv))516debug::log(".spvasm", spirv::print_module(binary, dev.device_version()));517518return spirv::compile_program(binary, dev, r_log);519}520#endif521522523