--- src/gallium/state_trackers/clover/Makefile.sources | 3 +- .../state_trackers/clover/llvm/ir_compiler.cpp | 282 +++++++++++++++++++++ .../state_trackers/clover/llvm/ir_compiler.hpp | 59 +++++ 3 files changed, 343 insertions(+), 1 deletion(-) create mode 100644 src/gallium/state_trackers/clover/llvm/ir_compiler.cpp create mode 100644 src/gallium/state_trackers/clover/llvm/ir_compiler.hpp
diff --git a/src/gallium/state_trackers/clover/Makefile.sources b/src/gallium/state_trackers/clover/Makefile.sources index 10bbda0..c4ad598 100644 --- a/src/gallium/state_trackers/clover/Makefile.sources +++ b/src/gallium/state_trackers/clover/Makefile.sources @@ -54,7 +54,8 @@ CPP_SOURCES := \ util/tuple.hpp LLVM_SOURCES := \ - llvm/invocation.cpp + llvm/invocation.cpp \ + llvm/ir_compiler.cpp TGSI_SOURCES := \ tgsi/compiler.cpp diff --git a/src/gallium/state_trackers/clover/llvm/ir_compiler.cpp b/src/gallium/state_trackers/clover/llvm/ir_compiler.cpp new file mode 100644 index 0000000..8042a3e --- /dev/null +++ b/src/gallium/state_trackers/clover/llvm/ir_compiler.cpp @@ -0,0 +1,282 @@ +// Copyright 2016 Serge Martin +// +// 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 "llvm/ir_compiler.hpp" + +#include "core/error.hpp" +#include "core/module.hpp" + +#include <clang/Frontend/TextDiagnosticBuffer.h> +#include <clang/Frontend/TextDiagnosticPrinter.h> +#include <clang/CodeGen/CodeGenAction.h> +#include <clang/Basic/TargetInfo.h> +#include <llvm/Bitcode/ReaderWriter.h> +#include <llvm/Linker/Linker.h> +#if HAVE_LLVM >= 0x0307 +#include <llvm/IR/LegacyPassManager.h> +#else +#include <llvm/PassManager.h> +#endif +#include <llvm/Transforms/IPO.h> +#include <llvm/Transforms/IPO/PassManagerBuilder.h> + +#include <llvm/IR/DataLayout.h> +#if HAVE_LLVM >= 0x0307 +#include <llvm/Analysis/TargetLibraryInfo.h> +#else +#include <llvm/Target/TargetLibraryInfo.h> +#endif +#include <sstream> + +static const std::string input_name("input.cl"); + +llvm_ir_compiler::llvm_ir_compiler(llvm::LLVMContext *llvm_ctx) : + _llvm_ctx(llvm_ctx), _module(nullptr), _raw_log(_log) { +}; + +llvm_ir_compiler::~llvm_ir_compiler() { +#if HAVE_LLVM >= 0x0306 + // LLVM 3.6 and newer, the user takes ownership of the module. + delete _module; +#endif +}; + +void +llvm_ir_compiler::parse_args(const std::string &target, + const std::string &opts) { + assert(!_module); + + // Parse the compiler options. + std::vector<std::string> opts_array; + std::istringstream ss(opts); + + while (!ss.eof()) { + std::string opt; + getline(ss, opt, ' '); + opts_array.push_back(opt); + } + + // A file name should be present at the end + // and must have the .cl extension in order for the + // CompilerInvocation class to recognize it as an OpenCL source file. + opts_array.push_back(input_name); + + std::vector<const char *> opts_carray; + for (unsigned i = 0; i < opts_array.size(); i++) + opts_carray.push_back(opts_array.at(i).c_str()); + + llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs> DiagID; + llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> DiagOpts; + clang::TextDiagnosticBuffer *DiagsBuffer; + + DiagID = new clang::DiagnosticIDs(); + DiagOpts = new clang::DiagnosticOptions(); + DiagsBuffer = new clang::TextDiagnosticBuffer(); + + clang::DiagnosticsEngine Diags(DiagID, &*DiagOpts, DiagsBuffer); + + bool success = clang::CompilerInvocation::CreateFromArgs( + _ci.getInvocation(), + opts_carray.data(), + opts_carray.data() + opts_carray.size(), + Diags); + if (!success) + throw clover::error(CL_INVALID_COMPILER_OPTIONS); + + size_t len = target.find_first_of("-"); + std::string processor(target, 0, len); + std::string triple(target, len + 1, target.size() - len - 1); + + _ci.getLangOpts().NoBuiltin = true; + _ci.getTargetOpts().Triple = triple; + _ci.getTargetOpts().CPU = processor; + + _ci.getInvocation().setLangDefaults(_ci.getLangOpts(), clang::IK_OpenCL, + clang::LangStandard::lang_opencl11); + + // Setting this attribute tells clang to link this file before + // performing any optimizations. This is required so that + // we can replace calls to the OpenCL C barrier() builtin + // with calls to target intrinsics that have the noduplicate + // attribute. This 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. + const std::string libclc_path = LIBCLC_LIBEXECDIR + target + ".bc"; +#if HAVE_LLVM >= 0x0308 + _ci.getCodeGenOpts().LinkBitcodeFiles.emplace_back( + llvm::Linker::Flags::None, + libclc_path); +#else + _ci.getCodeGenOpts().LinkBitcodeFile = libclc_path; +#endif + + // This is a workaround for a Clang bug which causes the number + // of warnings and errors to be printed to stderr. + // http://www.llvm.org/bugs/show_bug.cgi?id=19735 + _ci.getDiagnosticOpts().ShowCarets = false; + + _ci.createDiagnostics( new clang::TextDiagnosticPrinter( + _raw_log, &_ci.getDiagnosticOpts())); + + _ci.setTarget(clang::TargetInfo::CreateTargetInfo(_ci.getDiagnostics(), + _ci.getInvocation().TargetOpts)); +} + +void +llvm_ir_compiler::compile(const std::string &source, + const header_map &headers) { + assert(!_module); + + clang::EmitLLVMOnlyAction act(_llvm_ctx); + + _ci.getFrontendOpts().ProgramAction = clang::frontend::EmitLLVMOnly; + _ci.getHeaderSearchOpts().UseBuiltinIncludes = true; + _ci.getHeaderSearchOpts().UseStandardSystemIncludes = true; + _ci.getHeaderSearchOpts().ResourceDir = CLANG_RESOURCE_DIR; + + // Add libclc generic search path + _ci.getHeaderSearchOpts().AddPath(LIBCLC_INCLUDEDIR, + clang::frontend::Angled, + false, false + ); + + // Add libclc include + _ci.getPreprocessorOpts().Includes.push_back("clc/clc.h"); + + // clc.h requires that this macro be defined: + _ci.getPreprocessorOpts().addMacroDef("cl_clang_storage_class_specifiers"); + +#if HAVE_LLVM >= 0x0306 + _ci.getPreprocessorOpts().addRemappedFile(input_name, + llvm::MemoryBuffer::getMemBuffer(source).release()); +#else + _ci.getPreprocessorOpts().addRemappedFile(input_name, + llvm::MemoryBuffer::getMemBuffer(source)); +#endif + + if (headers.size()) { + const std::string tmp_header_path = "/tmp/clover/"; + + _ci.getHeaderSearchOpts().AddPath(tmp_header_path, + clang::frontend::Angled, + false, false + ); + + for (header_map::const_iterator it = headers.begin(); + it != headers.end(); ++it) { + const std::string path = tmp_header_path + std::string(it->first); + _ci.getPreprocessorOpts().addRemappedFile(path, +#if HAVE_LLVM >= 0x0306 + llvm::MemoryBuffer::getMemBuffer(it->second.c_str()).release()); +#else + llvm::MemoryBuffer::getMemBuffer(it->second.c_str())); +#endif + } + } + + // Compile the code + bool success = _ci.ExecuteAction(act); + + if (!success) + throw clover::compile_error(); + +#if HAVE_LLVM >= 0x0306 + _module = act.takeModule().release(); +#else + _module = act.takeModule(); +#endif +} + +void +llvm_ir_compiler::optimize() { + assert(_module); + +#if HAVE_LLVM >= 0x0307 + llvm::legacy::PassManager PM; +#else + llvm::PassManager PM; +#endif + + // Add a function internalizer pass. + // + // By default, the function internalizer pass will look for a function + // called "main" and then mark all other functions as internal. Marking + // functions as internal enables the optimizer to perform optimizations + // like function inlining and global dead-code elimination. + // + // When there is no "main" function in a module, the internalize pass will + // treat the module like a library, and it won't internalize any functions. + // Since there is no "main" function in our kernels, we need to tell + // the internalizer pass that this module is not a library by passing a + // list of kernel functions to the internalizer. The internalizer will + // treat the functions in the list as "main" functions and internalize + // all of the other functions. + std::vector<const char*> export_list; + + const llvm::NamedMDNode *kernel_node = + _module->getNamedMetadata("opencl.kernels"); + if (kernel_node) { + export_list.reserve(kernel_node->getNumOperands()); + for (unsigned i = 0; i < kernel_node->getNumOperands(); ++i) { +#if HAVE_LLVM >= 0x0306 + llvm::Function *kernel = llvm::mdconst::dyn_extract<llvm::Function>( +#else + llvm::Function *kernel = llvm::dyn_cast<llvm::Function>( +#endif + kernel_node->getOperand(i)->getOperand(0)); + export_list.push_back(kernel->getName().data()); + } + } + +#if HAVE_LLVM < 0x0306 + PM.add(new llvm::DataLayoutPass(_module)); +#elif HAVE_LLVM < 0x0307 + PM.add(new llvm::DataLayoutPass()); +#endif + + PM.add(llvm::createInternalizePass(export_list)); + + llvm::PassManagerBuilder PMB; + PMB.OptLevel = _ci.getCodeGenOpts().OptimizationLevel; +#if HAVE_LLVM < 0x0307 + PMB.LibraryInfo = new llvm::TargetLibraryInfo( +#else + PMB.LibraryInfo = new llvm::TargetLibraryInfoImpl( +#endif + llvm::Triple(_module->getTargetTriple())); + PMB.populateModulePassManager(PM); + PM.run(*_module); +} + +const clang::TargetInfo & +llvm_ir_compiler::get_info() const { + return _ci.getTarget(); +} + +std::string +llvm_ir_compiler::get_log() const { + return _log; +} + +llvm::Module * +llvm_ir_compiler::get_module() const { + return _module; +} diff --git a/src/gallium/state_trackers/clover/llvm/ir_compiler.hpp b/src/gallium/state_trackers/clover/llvm/ir_compiler.hpp new file mode 100644 index 0000000..c825c6e --- /dev/null +++ b/src/gallium/state_trackers/clover/llvm/ir_compiler.hpp @@ -0,0 +1,59 @@ +// +// Copyright 2016 Serge Martin +// +// 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_LLVM_COMPILER_HPP +#define CLOVER_LLVM_COMPILER_HPP + +#include <llvm/IR/LLVMContext.h> +#include <llvm/IR/Module.h> +#include <clang/Frontend/CompilerInstance.h> + +typedef std::vector<std::pair<std::string, std::string> > header_map; + +namespace clover { + class module; +} + +class llvm_ir_compiler { +public: + llvm_ir_compiler(llvm::LLVMContext *llvm_ctx); + ~llvm_ir_compiler(); + + void parse_args(const std::string &target, const std::string &opts); + void compile(const std::string &source, const header_map &headers); + void optimize(); + + const clang::TargetInfo &get_info() const; + std::string get_log() const; + llvm::Module *get_module() const; + +private: + llvm::LLVMContext *_llvm_ctx; + llvm::Module *_module; + + clang::CompilerInstance _ci; + + std::string _log; + llvm::raw_string_ostream _raw_log; +}; + +#endif -- 2.5.0 _______________________________________________ mesa-dev mailing list mesa-dev@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/mesa-dev