[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