[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, &section_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