[Mesa-dev] [PATCH 03/10] clover: Add support for compiling to native object code
Francisco Jerez
currojerez at riseup.net
Tue Oct 7 06:05:46 PDT 2014
Tom Stellard <thomas.stellard at amd.com> writes:
> ---
> .../state_trackers/clover/llvm/invocation.cpp | 173 ++++++++++++++++++++-
> src/gallium/targets/opencl/Makefile.am | 1 +
> 2 files changed, 167 insertions(+), 7 deletions(-)
>
> diff --git a/src/gallium/state_trackers/clover/llvm/invocation.cpp b/src/gallium/state_trackers/clover/llvm/invocation.cpp
> index 3137591..088039a 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;
>
> @@ -120,7 +135,8 @@ namespace {
> compile(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);
> }
>
> @@ -405,6 +430,125 @@ namespace {
>
> return m;
> }
> +
> + module
> + build_module_native(llvm::Module *mod,
> + const std::vector<llvm::Function *> &kernels,
> + clang::LangAS::Map& address_spaces,
> + std::string triple, 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) {
nn> + 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();
> + }
> +
This function seems pretty big and complex to me. Apparently it's
taking care of three different things: calling LLVM to emit machine code
From the IR, reading back metadata from the ELF headers and building a
clover::module for the binary. Maybe structuring it in two or maybe
three functions for each separate task would make it a bit easier to
follow?
> + buffer_size = LLVMGetBufferSize(out_buffer);
> + buffer_data = LLVMGetBufferStart(out_buffer);
> +
It seems like buffer_data is only used once? It's probably not worth
defining separate variables for these.
> + // One of the libelf implementations
> + // (http://www.mr511.de/software/english.htm) requires calling
> + // elf_version() before elf_memory().
> + //
> + elf_version(EV_CURRENT);
> + char *elf_buffer = (char*)MALLOC(buffer_size);
You forgot to free this buffer. Memory handling is hard, can you use an
std:: or compat::vector<char> instead so we don't have to worry about it?
> + memcpy(elf_buffer, buffer_data, buffer_size);
> +
> + LLVMDisposeMemoryBuffer(out_buffer);
> + LLVMDisposeTargetMachine(tm);
> +
> + Elf *elf = elf_memory(elf_buffer, buffer_size);
Should this Elf structure get released at some point?
> + 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
> + while ((section = elf_nextscn(elf, section))) {
> + const char *name;
> + if (gelf_getshdr(section, &symtab_header) != &symtab_header) {
> + throw build_error("Failed to read ELF section header\n");
> + }
> + name = elf_strptr(elf, section_str_index, symtab_header.sh_name);
> + if (!strcmp(name, ".symtab")) {
> + symtab = section;
> + break;
> + }
> + }
> + if (!symtab) {
> + throw build_error("Unable to find symbol table.");
> + }
> +
> + // 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);
> +
> + int i = 0;
It's a bit confusing that "i" is redefined with a different type in a
nested scope below. Maybe use a for-style loop so you can make it local
to this block?
> + // Determine the offsets for each kernel
> + while ((symbol = gelf_getsym(symtab_data, i++, &s))) {
> + char *name = elf_strptr(elf, symtab_header.sh_link, symbol->st_name);
> + for (std::vector<llvm::Function*>::const_iterator i = kernels.begin(),
> + e = kernels.end(); i != e; ++i) {
> + llvm::Function *f = *i;
> + if (f->getName() == std::string(name))
> + kernel_offsets[f->getName()] = symbol->st_value;
> + }
> + }
> +
> + // 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 = buffer_size;
> + std::string data;
> + data.insert(0, (char*)(&header), sizeof(header));
> + data.insert(data.end(), elf_buffer, elf_buffer + buffer_size);
Probably you can just use string::append() here.
> + 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, args);
> + m.syms.push_back(module::symbol(i->first, 0, i->second, args ));
> + }
> +
> + return m;
> + }
> } // End anonymous namespace
>
> module
> @@ -414,23 +558,34 @@ clover::compile_program_llvm(const compat::string &source,
> const compat::string &opts,
> compat::string &r_log) {
>
> + static bool target_init = false;
> +
> + if (!target_init) {
> +
> + LLVMInitializeAllTargets();
> + LLVMInitializeAllTargetInfos();
> + LLVMInitializeAllTargetMCs();
> + LLVMInitializeAllAsmPrinters();
> + target_init = true;
> + }
> +
Maybe you could wrap this one-time initialization into a function of its
own too?
Thanks!
> 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);
> + 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
> @@ -440,9 +595,13 @@ 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:
> + m = build_module_native(mod, kernels, address_spaces, triple,
> + processor, 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 \
> --
> 1.8.5.5
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 212 bytes
Desc: not available
URL: <http://lists.freedesktop.org/archives/mesa-dev/attachments/20141007/f019605d/attachment.sig>
More information about the mesa-dev
mailing list