[Mesa-dev] [PATCH v7 1/7] clover: add a LLVM compiler class
Serge Martin
edb+mesa at sigluy.net
Sat Feb 13 22:08:35 UTC 2016
---
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
More information about the mesa-dev
mailing list