[Mesa-dev] [PATCH 3/5] clover: Add support for compiling to native object code v3
Tom Stellard
tom at stellard.net
Tue Oct 21 09:50:38 PDT 2014
On Tue, Oct 21, 2014 at 12:16:12PM -0400, Jan Vesely wrote:
> On Thu, 2014-10-09 at 11:07 -0400, Tom Stellard wrote:
> > v2:
> > - Split build_module_native() into three separate functions.
> > - Code cleanups.
> >
> > v3:
> > - More cleanups.
> > ---
> > .../state_trackers/clover/llvm/invocation.cpp | 208 ++++++++++++++++++++-
> > src/gallium/targets/opencl/Makefile.am | 1 +
> > 2 files changed, 200 insertions(+), 9 deletions(-)
> >
> > diff --git a/src/gallium/state_trackers/clover/llvm/invocation.cpp b/src/gallium/state_trackers/clover/llvm/invocation.cpp
> > index b8badb2..ac52710 100644
> > --- a/src/gallium/state_trackers/clover/llvm/invocation.cpp
> > +++ b/src/gallium/state_trackers/clover/llvm/invocation.cpp
> > @@ -45,12 +45,18 @@
> > #include <llvm/Support/SourceMgr.h>
> > #include <llvm/IRReader/IRReader.h>
> > #endif
> > +#if HAVE_LLVM < 0x0305
> > +#include <llvm/ADT/OwningPtr.h>
> > +#endif
> > #include <llvm/PassManager.h>
> > +#include <llvm/Support/CodeGen.h>
> > #include <llvm/Support/TargetSelect.h>
> > #include <llvm/Support/MemoryBuffer.h>
> > #if HAVE_LLVM < 0x0303
> > #include <llvm/Support/PathV1.h>
> > #endif
> > +#include <llvm/Support/FormattedStream.h>
> > +#include <llvm/Support/TargetRegistry.h>
> > #include <llvm/Transforms/IPO.h>
> > #include <llvm/Transforms/IPO/PassManagerBuilder.h>
> >
> > @@ -61,6 +67,13 @@
> > #else
> > #include <llvm/IR/DataLayout.h>
> > #endif
> > +#include <llvm/Target/TargetLibraryInfo.h>
> > +#include <llvm/Target/TargetMachine.h>
> > +#include <llvm/Target/TargetOptions.h>
> > +
> > +#include <llvm-c/Target.h>
> > +#include <llvm-c/TargetMachine.h>
> > +#include <llvm-c/Core.h>
> >
> > #include "pipe/p_state.h"
> > #include "util/u_memory.h"
> > @@ -71,6 +84,8 @@
> > #include <fstream>
> > #include <cstdio>
> > #include <sstream>
> > +#include <libelf.h>
> > +#include <gelf.h>
> >
> > using namespace clover;
> >
> > @@ -117,10 +132,11 @@ namespace {
> > #endif
> >
> > llvm::Module *
> > - compile(llvm::LLVMContext &llvm_ctx, const std::string &source,
> > + compile_llvm(llvm::LLVMContext &llvm_ctx, const std::string &source,
> > const std::string &name, const std::string &triple,
> > const std::string &processor, const std::string &opts,
> > - clang::LangAS::Map& address_spaces, compat::string &r_log) {
> > + clang::LangAS::Map& address_spaces, unsigned &optimization_level,
> > + compat::string &r_log) {
> >
> > clang::CompilerInstance c;
> > clang::EmitLLVMOnlyAction act(&llvm_ctx);
> > @@ -228,6 +244,8 @@ namespace {
> > // that is no executed by all threads) during its optimizaton passes.
> > c.getCodeGenOpts().LinkBitcodeFile = libclc_path;
> >
> > + optimization_level = c.getCodeGenOpts().OptimizationLevel;
> > +
> > // Compile the code
> > bool ExecSuccess = c.ExecuteAction(act);
> > r_log = log;
> > @@ -264,8 +282,8 @@ namespace {
> > }
> >
> > void
> > - internalize_functions(llvm::Module *mod,
> > - const std::vector<llvm::Function *> &kernels) {
> > + optimize(llvm::Module *mod, unsigned optimization_level,
> > + const std::vector<llvm::Function *> &kernels) {
> >
> > llvm::PassManager PM;
> > // Add a function internalizer pass.
> > @@ -289,7 +307,14 @@ namespace {
> > llvm::Function *kernel = *I;
> > export_list.push_back(kernel->getName().data());
> > }
> > + PM.add(new llvm::DataLayoutPass());
> > PM.add(llvm::createInternalizePass(export_list));
> > +
> > + llvm::PassManagerBuilder PMB;
> > + PMB.OptLevel = optimization_level;
> > + PMB.LibraryInfo = new llvm::TargetLibraryInfo(
> > + llvm::Triple(mod->getTargetTriple()));
> > + PMB.populateModulePassManager(PM);
> > PM.run(*mod);
> > }
> >
> > @@ -404,6 +429,163 @@ namespace {
> >
> > return m;
> > }
> > +
> > + std::vector<char>
> > + compile_native(const llvm::Module *mod, const std::string &triple,
> > + const std::string &processor, compat::string &r_log) {
> > +
> > + std::string log;
> > + LLVMTargetRef target;
> > + char *error_message;
> > + LLVMMemoryBufferRef out_buffer;
> > + unsigned buffer_size;
> > + const char *buffer_data;
> > + LLVMBool err;
> > + LLVMModuleRef mod_ref = wrap(mod);
> > +
> > + if (LLVMGetTargetFromTriple(triple.c_str(), &target, &error_message)) {
> > + r_log = std::string(error_message);
> > + LLVMDisposeMessage(error_message);
> > + throw build_error();
> > + }
> > +
> > + LLVMTargetMachineRef tm = LLVMCreateTargetMachine(
> > + target, triple.c_str(), processor.c_str(), "",
> > + LLVMCodeGenLevelDefault, LLVMRelocDefault, LLVMCodeModelDefault);
> > +
> > + if (!tm) {
> > + r_log = "Could not create TargetMachine: " + triple;
> > + throw build_error();
> > + }
> > +
> > + err = LLVMTargetMachineEmitToMemoryBuffer(tm, mod_ref, LLVMObjectFile,
> > + &error_message, &out_buffer);
> > +
> > + if (err) {
> > + LLVMDisposeTargetMachine(tm);
> > + r_log = std::string(error_message);
> > + LLVMDisposeMessage(error_message);
> > + throw build_error();
> > + }
> > +
> > + buffer_size = LLVMGetBufferSize(out_buffer);
> > + buffer_data = LLVMGetBufferStart(out_buffer);
> > +
> > + std::vector<char> code(buffer_data, buffer_data + buffer_size);
> > +
> > + LLVMDisposeMemoryBuffer(out_buffer);
> > + LLVMDisposeTargetMachine(tm);
> > +
> > + return code;
> > + }
> > +
> > + std::map<std::string, unsigned>
> > + get_kernel_offsets(std::vector<char> &code,
> > + const std::vector<llvm::Function *> &kernels,
> > + compat::string &r_log) {
> > +
> > + // One of the libelf implementations
> > + // (http://www.mr511.de/software/english.htm) requires calling
> > + // elf_version() before elf_memory().
> > + //
> > + elf_version(EV_CURRENT);
> > +
> > + Elf *elf = elf_memory(&code[0], code.size());
> > + size_t section_str_index;
> > + elf_getshdrstrndx(elf, §ion_str_index);
> > + Elf_Scn *section = NULL;
> > + Elf_Scn *symtab = NULL;
> > + GElf_Shdr symtab_header;
> > +
> > + // Find the symbol table
> > + try {
> > + while ((section = elf_nextscn(elf, section))) {
> > + const char *name;
> > + if (gelf_getshdr(section, &symtab_header) != &symtab_header) {
> > + r_log = "Failed to read ELF section header.";
> > + throw build_error();
> > + }
> > + name = elf_strptr(elf, section_str_index, symtab_header.sh_name);
> > + if (!strcmp(name, ".symtab")) {
> > + symtab = section;
> > + break;
> > + }
> > + }
> > + if (!symtab) {
> > + r_log = "Unable to find symbol table.";
> > + throw build_error();
> > + }
> > + } catch (build_error &e) {
> > + elf_end(elf);
> > + throw e;
> > + }
> > +
> > +
> > + // Extract symbol information from the table
> > + Elf_Data *symtab_data = NULL;
> > + GElf_Sym *symbol;
> > + GElf_Sym s;
> > +
> > + std::map<std::string, unsigned> kernel_offsets;
> > + symtab_data = elf_getdata(symtab, symtab_data);
> > +
> > + // Determine the offsets for each kernel
> > + for (int i = 0; (symbol = gelf_getsym(symtab_data, i, &s)); i++) {
> > + char *name = elf_strptr(elf, symtab_header.sh_link, symbol->st_name);
> > + for (std::vector<llvm::Function*>::const_iterator it = kernels.begin(),
> > + e = kernels.end(); it != e; ++it) {
> > + llvm::Function *f = *it;
> > + if (f->getName() == std::string(name))
> > + kernel_offsets[f->getName()] = symbol->st_value;
> > + }
> > + }
> > + elf_end(elf);
> > + return kernel_offsets;
> > + }
> > +
> > + module
> > + build_module_native(std::vector<char> &code,
> > + const llvm::Module *mod,
> > + const std::vector<llvm::Function *> &kernels,
> > + const clang::LangAS::Map &address_spaces,
> > + compat::string &r_log) {
> > +
> > + std::map<std::string, unsigned> kernel_offsets =
> > + get_kernel_offsets(code, kernels, r_log);
> > +
> > + // Begin building the clover module
> > + module m;
> > + struct pipe_llvm_program_header header;
> > +
> > + // Store the generated ELF binary in the module's text section.
> > + header.num_bytes = code.size();
> > + std::string data;
> > + data.append((char*)(&header), sizeof(header));
> > + data.append(code.begin(), code.end());
> > + m.secs.push_back(module::section(0, module::section::text,
> > + header.num_bytes, data));
> > +
> > + for (std::map<std::string, unsigned>::iterator i = kernel_offsets.begin(),
> > + e = kernel_offsets.end(); i != e; ++i) {
> > + compat::vector<module::argument> args =
> > + get_kernel_args(mod, i->first, address_spaces);
> > + m.syms.push_back(module::symbol(i->first, 0, i->second, args ));
> > + }
> > +
> > + return m;
> > + }
> > +
> > + void
> > + init_targets() {
> > + static bool targets_initialized = false;
> > + if (!targets_initialized) {
> > + LLVMInitializeAllTargets();
> > + LLVMInitializeAllTargetInfos();
> > + LLVMInitializeAllTargetMCs();
> > + LLVMInitializeAllAsmPrinters();
> > + targets_initialized = true;
> Hi,
> this part causes linking errors when llvm is built using cmake (split
> shared libraries)
> In function `LLVMInitializeAllAsmPrinters':
> /home/vesely/.local/include/llvm/Config/AsmPrinters.def:27: undefined
> reference to `LLVMInitializeAArch64AsmPrinter'
> ...
> for all targets other than r600
>
> a quick fix would be to link against all LLVM libs (and let something
> like -Wl,--as-needed sort it out)
>
> but i think it would be nicer to have a pipe callback to do the
> building. is there something I'm missing with that approach?
>
Having a callback is an interesting idea. One downside I can see is
that it would require to the pipe drivers to link against clang and LLVM.
For the time being, I think the best fix would be to do conditional
initialization based on the targets that may actually be used. I can
write up a patch for this. Ideally we would link against as few target
libraries as possible. I think if we used --as-needed, we may end up
pulling in all the libraries due to the way the library dependencies work
(I'm not 100% sure about this, though).
-Tom
> jan
>
>
> > + }
> > + }
> > } // End anonymous namespace
> >
> > module
> > @@ -413,23 +595,26 @@ clover::compile_program_llvm(const compat::string &source,
> > const compat::string &opts,
> > compat::string &r_log) {
> >
> > + init_targets();
> > +
> > std::vector<llvm::Function *> kernels;
> > size_t processor_str_len = std::string(target.begin()).find_first_of("-");
> > std::string processor(target.begin(), 0, processor_str_len);
> > std::string triple(target.begin(), processor_str_len + 1,
> > target.size() - processor_str_len - 1);
> > clang::LangAS::Map address_spaces;
> > -
> > llvm::LLVMContext llvm_ctx;
> > + unsigned optimization_level;
> >
> > // The input file name must have the .cl extension in order for the
> > // CompilerInvocation class to recognize it as an OpenCL source file.
> > - llvm::Module *mod = compile(llvm_ctx, source, "input.cl", triple, processor,
> > - opts, address_spaces, r_log);
> > + llvm::Module *mod = compile_llvm(llvm_ctx, source, "input.cl", triple,
> > + processor, opts, address_spaces,
> > + optimization_level, r_log);
> >
> > find_kernels(mod, kernels);
> >
> > - internalize_functions(mod, kernels);
> > + optimize(mod, optimization_level, kernels);
> >
> > module m;
> > // Build the clover::module
> > @@ -439,9 +624,14 @@ clover::compile_program_llvm(const compat::string &source,
> > assert(0);
> > m = module();
> > break;
> > - default:
> > + case PIPE_SHADER_IR_LLVM:
> > m = build_module_llvm(mod, kernels, address_spaces);
> > break;
> > + case PIPE_SHADER_IR_NATIVE: {
> > + std::vector<char> code = compile_native(mod, triple, processor, r_log);
> > + m = build_module_native(code, mod, kernels, address_spaces, r_log);
> > + break;
> > + }
> > }
> > #if HAVE_LLVM >= 0x0306
> > // LLVM 3.6 and newer, the user takes ownership of the module.
> > diff --git a/src/gallium/targets/opencl/Makefile.am b/src/gallium/targets/opencl/Makefile.am
> > index 43ba5dd..1c5a908 100644
> > --- a/src/gallium/targets/opencl/Makefile.am
> > +++ b/src/gallium/targets/opencl/Makefile.am
> > @@ -23,6 +23,7 @@ lib at OPENCL_LIBNAME@_la_LIBADD = \
> > $(top_builddir)/src/util/libmesautil.la \
> > $(GALLIUM_PIPE_LOADER_WINSYS_LIBS) \
> > $(GALLIUM_PIPE_LOADER_CLIENT_LIBS) \
> > + $(ELF_LIB) \
> > -ldl \
> > -lclangCodeGen \
> > -lclangFrontendTool \
>
> --
> Jan Vesely <jan.vesely at rutgers.edu>
> _______________________________________________
> mesa-dev mailing list
> mesa-dev at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/mesa-dev
More information about the mesa-dev
mailing list