[Mesa-dev] [Mesa-dev, v6, 1/3] clover: separate compile and link stages
Tom Stellard
tom at stellard.net
Mon Jan 25 16:55:25 PST 2016
Hi,
>diff --git a/src/gallium/state_trackers/clover/api/program.cpp b/src/gallium/state_trackers/clover/api/program.cpp
>index 27ca2ef..f8d946e 100644
>--- a/src/gallium/state_trackers/clover/api/program.cpp
>+++ b/src/gallium/state_trackers/clover/api/program.cpp
>@@ -181,13 +181,20 @@ clBuildProgram(cl_program d_prog, cl_uint num_devs,
>
> validate_build_program_common(prog, num_devs, d_devs, pfn_notify, user_data);
>
>- prog.build(devs, opts);
>+ if (prog.has_source) {
>+ prog.compile(devs, opts);
>+ prog.link(devs, opts, { prog });
>+ }
> return CL_SUCCESS;
> } catch (error &e) {
>- if (e.get() == CL_INVALID_COMPILER_OPTIONS)
>- return CL_INVALID_BUILD_OPTIONS;
>- if (e.get() == CL_COMPILE_PROGRAM_FAILURE)
>- return CL_BUILD_PROGRAM_FAILURE;
>+ switch (e.get()) {
>+ case CL_INVALID_COMPILER_OPTIONS:
>+ case CL_INVALID_LINKER_OPTIONS:
>+ return CL_INVALID_BUILD_OPTIONS;
>+ case CL_COMPILE_PROGRAM_FAILURE:
>+ case CL_LINK_PROGRAM_FAILURE:
>+ return CL_BUILD_PROGRAM_FAILURE;
>+ }
> return e.get();
> }
>
>@@ -224,7 +231,7 @@ clCompileProgram(cl_program d_prog, cl_uint num_devs,
> range(header_names, num_headers),
> objs<allow_empty_tag>(d_header_progs, num_headers));
>
>- prog.build(devs, opts, headers);
>+ prog.compile(devs, opts, headers);
> return CL_SUCCESS;
>
> } catch (error &e) {
>diff --git a/src/gallium/state_trackers/clover/core/compiler.hpp b/src/gallium/state_trackers/clover/core/compiler.hpp
>index 2076417..0d6766a 100644
>--- a/src/gallium/state_trackers/clover/core/compiler.hpp
>+++ b/src/gallium/state_trackers/clover/core/compiler.hpp
>@@ -32,11 +32,16 @@ namespace clover {
>
> module compile_program_llvm(const std::string &source,
> const header_map &headers,
>- pipe_shader_ir ir,
> const std::string &target,
> const std::string &opts,
> std::string &r_log);
>
>+ module link_program_llvm(const std::vector<module> &modules,
>+ enum pipe_shader_ir ir,
>+ const std::string &target,
>+ const std::string &opts,
>+ std::string &r_log);
>+
> module compile_program_tgsi(const std::string &source,
> std::string &r_log);
> }
>diff --git a/src/gallium/state_trackers/clover/core/error.hpp b/src/gallium/state_trackers/clover/core/error.hpp
>index 59a5af4..4ec619c 100644
>--- a/src/gallium/state_trackers/clover/core/error.hpp
>+++ b/src/gallium/state_trackers/clover/core/error.hpp
>@@ -72,6 +72,13 @@ namespace clover {
> }
> };
>
>+ class link_error : public error {
>+ public:
>+ link_error(const std::string &what = "") :
>+ error(CL_LINK_PROGRAM_FAILURE , what) {
>+ }
>+ };
>+
> template<typename O>
> class invalid_object_error;
>
>diff --git a/src/gallium/state_trackers/clover/core/program.cpp b/src/gallium/state_trackers/clover/core/program.cpp
>index 6eebd9c..4aa2622 100644
>--- a/src/gallium/state_trackers/clover/core/program.cpp
>+++ b/src/gallium/state_trackers/clover/core/program.cpp
>@@ -40,8 +40,8 @@ program::program(clover::context &ctx,
> }
>
> void
>-program::build(const ref_vector<device> &devs, const char *opts,
>- const header_map &headers) {
>+program::compile(const ref_vector<device> &devs, const std::string &opts,
>+ const header_map &headers) {
> if (has_source) {
> _devices = devs;
>
>@@ -58,9 +58,7 @@ program::build(const ref_vector<device> &devs, const char *opts,
> auto module = (dev.ir_format() == PIPE_SHADER_IR_TGSI ?
> compile_program_tgsi(_source, log) :
> compile_program_llvm(_source, headers,
>- dev.ir_format(),
>- dev.ir_target(), build_opts(dev),
>- log));
>+ dev.ir_target(), opts, log));
> _binaries.insert({ &dev, module });
> _logs.insert({ &dev, log });
> } catch (const error &) {
>@@ -71,6 +69,39 @@ program::build(const ref_vector<device> &devs, const char *opts,
> }
> }
>
>+void
>+program::link(const ref_vector<device> &devs, const std::string &opts,
>+ const ref_vector<program> &progs) {
>+ _devices = devs;
>+
>+ for (auto &dev : devs) {
>+ if (dev.ir_format() == PIPE_SHADER_IR_TGSI)
>+ continue;
>+
>+ const std::vector<module> mods = map([&](const program &prog) {
>+ return prog.binary(dev);
>+ }, progs);
>+
>+ _binaries.erase(&dev);
>+ _opts.erase(&dev);
>+
>+ _opts.insert({ &dev, opts });
>+
>+ std::string log;
>+
>+ try {
>+ auto module = link_program_llvm(mods,
>+ dev.ir_format(), dev.ir_target(),
>+ opts, log);
>+ _binaries.insert({ &dev, module });
>+ _logs[&dev] += log;
>+ } catch (const error &) {
>+ _logs[&dev] += log;
>+ throw;
>+ }
>+ }
>+}
>+
> const std::string &
> program::source() const {
> return _source;
>diff --git a/src/gallium/state_trackers/clover/core/program.hpp b/src/gallium/state_trackers/clover/core/program.hpp
>index 183145e..7d86018 100644
>--- a/src/gallium/state_trackers/clover/core/program.hpp
>+++ b/src/gallium/state_trackers/clover/core/program.hpp
>@@ -47,8 +47,10 @@ namespace clover {
> program &
> operator=(const program &prog) = delete;
>
>- void build(const ref_vector<device> &devs, const char *opts,
>+ void compile(const ref_vector<device> &devs, const std::string &opts,
> const header_map &headers = {});
>+ void link(const ref_vector<device> &devs, const std::string &opts,
>+ const ref_vector<program> &progs);
>
> const bool has_source;
> const std::string &source() const;
>diff --git a/src/gallium/state_trackers/clover/llvm/invocation.cpp b/src/gallium/state_trackers/clover/llvm/invocation.cpp
>index 4d11c24..cb09d72 100644
>--- a/src/gallium/state_trackers/clover/llvm/invocation.cpp
>+++ b/src/gallium/state_trackers/clover/llvm/invocation.cpp
>@@ -130,22 +130,15 @@ namespace {
> }
> }
>
>- llvm::Module *
>- compile_llvm(llvm::LLVMContext &llvm_ctx, const std::string &source,
>- const header_map &headers,
>- const std::string &name, const std::string &triple,
>- const std::string &processor, const std::string &opts,
>- clang::LangAS::Map& address_spaces, unsigned &optimization_level,
>- std::string &r_log) {
>-
>- clang::CompilerInstance c;
>- clang::EmitLLVMOnlyAction act(&llvm_ctx);
>- std::string log;
>- llvm::raw_string_ostream s_log(log);
>- std::string libclc_path = LIBCLC_LIBEXECDIR + processor + "-"
>- + triple + ".bc";
>-
>- // Parse the compiler options:
>+ bool
>+ create_from_arg_llvm(clang::CompilerInstance &c,
>+ const std::string &target, const std::string &opts,
>
You should split the part of the patch which adds this function into another
patch. This will make the rest of the changes easy to follow.
>
>+ llvm::raw_string_ostream &log) {
>+
>+ // Parse the compiler options.
>+ // 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.
> std::vector<std::string> opts_array;
> std::istringstream ss(opts);
>
>@@ -155,8 +148,6 @@ namespace {
> opts_array.push_back(opt);
> }
>
>- opts_array.push_back(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());
>@@ -171,15 +162,58 @@ namespace {
> DiagsBuffer = new clang::TextDiagnosticBuffer();
>
> clang::DiagnosticsEngine Diags(DiagID, &*DiagOpts, DiagsBuffer);
>- bool Success;
>
>+ bool Success;
> Success = clang::CompilerInvocation::CreateFromArgs(c.getInvocation(),
> opts_carray.data(),
> opts_carray.data() + opts_carray.size(),
> Diags);
>- if (!Success) {
>+ if (Success) {
>+ const size_t processor_str_len = target.find_first_of("-");
>+ const std::string processor(target, 0, processor_str_len);
>+ const std::string triple(target.begin() + processor_str_len + 1,
>+ target.end());
>+
>+ c.getLangOpts().NoBuiltin = true;
>+ c.getTargetOpts().Triple = triple;
>+ c.getTargetOpts().CPU = processor;
>+
>+ // 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
>+ c.getDiagnosticOpts().ShowCarets = false;
>+ c.getInvocation().setLangDefaults(c.getLangOpts(), clang::IK_OpenCL,
>+ clang::LangStandard::lang_opencl11);
>+ c.createDiagnostics(
>+ new clang::TextDiagnosticPrinter(
>+ log, &c.getDiagnosticOpts())
>+ );
>+
>+ c.setTarget(clang::TargetInfo::CreateTargetInfo(c.getDiagnostics(),
>+ c.getInvocation().TargetOpts));
>+ }
>+
>+ return Success;
>+ }
>+
>+ llvm::Module *
>+ compile_llvm(llvm::LLVMContext &llvm_ctx, const std::string &source,
>+ const header_map &headers,
>+ const std::string &target, const std::string &opts,
>+ clang::LangAS::Map& address_spaces, unsigned &optimization_level,
>+ std::string &r_log) {
>+
>+ clang::CompilerInstance c;
>+ clang::EmitLLVMOnlyAction act(&llvm_ctx);
>+ std::string log;
>+ llvm::raw_string_ostream s_log(log);
>+ const std::string name = "compile.cl";
>+
>+ if (!create_from_arg_llvm(c, target, opts + " " + name, s_log)) {
>+ r_log = log;
> throw error(CL_INVALID_COMPILER_OPTIONS);
> }
>+
> c.getFrontendOpts().ProgramAction = clang::frontend::EmitLLVMOnly;
> c.getHeaderSearchOpts().UseBuiltinIncludes = true;
> c.getHeaderSearchOpts().UseStandardSystemIncludes = true;
>@@ -197,21 +231,6 @@ namespace {
> // clc.h requires that this macro be defined:
> c.getPreprocessorOpts().addMacroDef("cl_clang_storage_class_specifiers");
>
>- c.getLangOpts().NoBuiltin = true;
>- c.getTargetOpts().Triple = triple;
>- c.getTargetOpts().CPU = processor;
>-
>- // 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
>- c.getDiagnosticOpts().ShowCarets = false;
>- c.getInvocation().setLangDefaults(c.getLangOpts(), clang::IK_OpenCL,
>- clang::LangStandard::lang_opencl11);
>- c.createDiagnostics(
>- new clang::TextDiagnosticPrinter(
>- s_log,
>- &c.getDiagnosticOpts()));
>-
> #if HAVE_LLVM >= 0x0306
> c.getPreprocessorOpts().addRemappedFile(name,
> llvm::MemoryBuffer::getMemBuffer(source).release());
>@@ -247,6 +266,7 @@ namespace {
> // 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
> c.getCodeGenOpts().LinkBitcodeFiles.emplace_back(llvm::Linker::Flags::None,
> libclc_path);
>@@ -627,7 +647,7 @@ namespace {
> LLVMDisposeMessage(err_message);
>
> if (err) {
>- throw compile_error();
>+ throw link_error();
> }
> }
>
>@@ -647,7 +667,7 @@ namespace {
> if (LLVMGetTargetFromTriple(triple.c_str(), &target, &error_message)) {
> r_log = std::string(error_message);
> LLVMDisposeMessage(error_message);
>- throw compile_error();
>+ throw link_error();
> }
>
> LLVMTargetMachineRef tm = LLVMCreateTargetMachine(
>@@ -656,7 +676,7 @@ namespace {
>
> if (!tm) {
> r_log = "Could not create TargetMachine: " + triple;
>- throw compile_error();
>+ throw link_error();
> }
>
> if (dump_asm) {
>@@ -713,7 +733,7 @@ namespace {
> const char *name;
> if (gelf_getshdr(section, &symtab_header) != &symtab_header) {
> r_log = "Failed to read ELF section header.";
>- throw compile_error();
>+ throw link_error();
> }
> name = elf_strptr(elf, section_str_index, symtab_header.sh_name);
> if (!strcmp(name, ".symtab")) {
>@@ -723,9 +743,9 @@ namespace {
> }
> if (!symtab) {
> r_log = "Unable to find symbol table.";
>- throw compile_error();
>+ throw link_error();
> }
>- } catch (compile_error &e) {
>+ } catch (link_error &e) {
> elf_end(elf);
> throw e;
> }
>@@ -787,6 +807,7 @@ namespace {
> return m;
> }
>
>+ template<typename T>
> void
> diagnostic_handler(const llvm::DiagnosticInfo &di, void *data) {
> if (di.getSeverity() == llvm::DS_Error) {
>@@ -797,7 +818,7 @@ namespace {
> stream.flush();
> *(std::string*)data = message;
>
>- throw compile_error();
>+ throw T();
> }
> }
>
>@@ -837,34 +858,24 @@ namespace {
> module
> clover::compile_program_llvm(const std::string &source,
> const header_map &headers,
>- enum pipe_shader_ir ir,
> const std::string &target,
> const std::string &opts,
> std::string &r_log) {
>-
> init_targets();
>
>- size_t processor_str_len = std::string(target).find_first_of("-");
>- std::string processor(target, 0, processor_str_len);
>- std::string triple(target, processor_str_len + 1,
>- target.size() - processor_str_len - 1);
> clang::LangAS::Map address_spaces;
> llvm::LLVMContext llvm_ctx;
> unsigned optimization_level;
>
>- llvm_ctx.setDiagnosticHandler(diagnostic_handler, &r_log);
>+ llvm_ctx.setDiagnosticHandler(diagnostic_handler<compile_error>, &r_log);
>
> if (get_debug_flags() & DBG_CLC)
>- debug_log("// Build options: " + opts + '\n' + source, ".cl");
>+ debug_log("// Options: " + opts + '\n' + source, ".cl");
>
>- // 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(llvm_ctx, source, headers, "input.cl",
>- triple, processor, opts, address_spaces,
>+ llvm::Module *mod = compile_llvm(llvm_ctx, source, headers,
>+ target, opts, address_spaces,
> optimization_level, r_log);
>
>- optimize(mod, optimization_level);
>-
> if (get_debug_flags() & DBG_LLVM) {
> std::string log;
> llvm::raw_string_ostream s_log(log);
>@@ -873,25 +884,20 @@ clover::compile_program_llvm(const std::string &source,
> debug_log(log, ".ll");
> }
>
>+ //serialize for later use
> module m;
>- // Build the clover::module
>- switch (ir) {
>- case PIPE_SHADER_IR_TGSI:
>- //XXX: Handle TGSI
>- assert(0);
>- m = module();
>- break;
>- case PIPE_SHADER_IR_LLVM:
>- m = build_module_llvm(mod, address_spaces);
>- break;
>- case PIPE_SHADER_IR_NATIVE: {
>- std::vector<char> code = compile_native(mod, triple, processor,
>- get_debug_flags() & DBG_ASM,
>- r_log);
>- m = build_module_native(code, mod, address_spaces, r_log);
>- break;
>- }
>- }
>+ llvm::SmallVector<char, 1024> llvm_bitcode;
>+ llvm::raw_svector_ostream bitcode_ostream(llvm_bitcode);
>+ llvm::BitstreamWriter writer(llvm_bitcode);
>+ llvm::WriteBitcodeToFile(mod, bitcode_ostream);
>+#if HAVE_LLVM < 0x0308
>+ bitcode_ostream.flush();
>+#endif
>+
>+ std::vector<char> data(llvm_bitcode.begin(), llvm_bitcode.end());
>+ m.secs.push_back(module::section(0, module::section::text,
>+ data.size(), data));
>+
> #if HAVE_LLVM >= 0x0306
> // LLVM 3.6 and newer, the user takes ownership of the module.
> delete mod;
>@@ -899,3 +905,119 @@ clover::compile_program_llvm(const std::string &source,
>
> return m;
> }
>+
>+module
>+clover::link_program_llvm(const std::vector<module> &modules,
>+ enum pipe_shader_ir ir, const std::string &target,
>+ const std::string &opts, std::string &r_log) {
>
The addition of this function should also be its own patch.
-Tom
>
>+ init_targets();
>+
>+ std::string options = opts;
>+ bool create_library = false;
>+ size_t pos = options.find("-create-library");
>+ if (pos != std::string::npos) {
>+ create_library = true;
>+ options.erase(pos, 15);
>+ }
>+ options += " link.cl";
>+
>+ llvm::LLVMContext llvm_ctx;
>+ llvm_ctx.setDiagnosticHandler(diagnostic_handler<link_error>, &r_log);
>+
>+ std::string log;
>+ llvm::raw_string_ostream s_log(log);
>+ clang::CompilerInstance c;
>+ if (!create_from_arg_llvm(c, target, options, s_log)) {
>+ r_log = log;
>+ throw error(CL_INVALID_LINKER_OPTIONS);
>+ }
>+
>+ llvm::Module linked_mod("link", llvm_ctx);
>+#if HAVE_LLVM >= 0x0308
>+ llvm::Linker linker(linked_mod);
>+#else
>+ llvm::Linker linker(&linked_mod);
>+#endif
>+
>+ for (const auto &mod : modules) {
>+ const auto s = llvm::StringRef(mod.secs[0].data.data(),
>+ mod.secs[0].data.size());
>+
>+ auto m =
>+ llvm::parseBitcodeFile(llvm::MemoryBufferRef(s, " "), llvm_ctx);
>+
>+ if (!m) {
>+ r_log = m.getError().message();
>+ throw error(CL_INVALID_PROGRAM);
>+ }
>+
>+#if HAVE_LLVM == 0x0305
>+ if (linker.linkInModule(*m, &r_log))
>+#elif HAVE_LLVM == 0x0306
>+ if (linker.linkInModule(*m))
>+#elif HAVE_LLVM == 0x0307
>+ if (linker.linkInModule((*m).get()))
>+#else
>+ if (linker.linkInModule(std::move(m.get())))
>+#endif
>+ throw link_error();
>+ }
>+
>+ module m;
>+
>+ if (!create_library) {
>+ unsigned optimization_level = c.getCodeGenOpts().OptimizationLevel;
>+ optimize(&linked_mod, optimization_level);
>+ }
>+
>+ unsigned debug_flags = get_debug_flags();
>+
>+ if (debug_flags & DBG_LLVM) {
>+ std::string l;
>+ llvm::raw_string_ostream s_l(l);
>+ linked_mod.print(s_l, NULL);
>+ s_l.flush();
>+ debug_log(l, ".ll");
>+ }
>+
>+ if (create_library) {
>+ //serialize for later use
>+ llvm::SmallVector<char, 1024> llvm_bitcode;
>+ llvm::raw_svector_ostream bitcode_ostream(llvm_bitcode);
>+ llvm::BitstreamWriter writer(llvm_bitcode);
>+ llvm::WriteBitcodeToFile(&linked_mod, bitcode_ostream);
>+#if HAVE_LLVM < 0x0308
>+ bitcode_ostream.flush();
>+#endif
>+
>+ std::vector<char> data(llvm_bitcode.begin(), llvm_bitcode.end());
>+ m.secs.push_back(module::section(0, module::section::text,
>+ data.size(), data));
>+
>+ } else {
>+ // Get address spaces map to be able to find kernel argument address space
>+ clang::LangAS::Map address_spaces;
>+ memcpy(address_spaces, c.getTarget().getAddressSpaceMap(),
>+ sizeof(address_spaces));
>+
>+ // Build the clover::module
>+ switch (ir) {
>+ case PIPE_SHADER_IR_TGSI:
>+ assert(0); // not supported
>+ break;
>+ case PIPE_SHADER_IR_LLVM:
>+ m = build_module_llvm(&linked_mod, address_spaces);
>+ break;
>+ case PIPE_SHADER_IR_NATIVE: {
>+ auto code = compile_native(&linked_mod, c.getTargetOpts().Triple,
>+ c.getTargetOpts().CPU,
>+ debug_flags & DBG_ASM, r_log);
>+ m = build_module_native(code, &linked_mod, address_spaces, r_log);
>+ break;
>+ }
>+ }
>+
>+ }
>+
>+ return m;
>+}
More information about the mesa-dev
mailing list