endif()
find_package(LLVM REQUIRED CONFIG)
-if(LLVM_PACKAGE_VERSION VERSION_LESS 3.8)
- message(FATAL_ERROR "unsupported version of llvm; requires version 3.8 or greater")
+if(LLVM_PACKAGE_VERSION VERSION_LESS 4.0)
+ message(FATAL_ERROR "unsupported version of llvm; requires version 4.0 or greater")
endif()
if(NOT LLVM_ENABLE_THREADS)
message(FATAL_ERROR "llvm was not built multi-threaded")
}
std::cerr << dump_callbacks.ss.str() << std::endl;
}
+ auto llvm_target_machine = llvm_wrapper::Target_machine::create_native_target_machine();
auto llvm_context = llvm_wrapper::Context::create();
std::uint64_t next_module_id = 1;
spirv_to_llvm::Converted_module converted_module;
try
{
- converted_module = spirv_to_llvm::spirv_to_llvm(llvm_context.get(), file->data(), file->size(), next_module_id++);
+ converted_module = spirv_to_llvm::spirv_to_llvm(llvm_context.get(),
+ llvm_target_machine.get(),
+ file->data(),
+ file->size(),
+ next_module_id++);
}
catch(spirv::Parser_error &e)
{
}
std::cerr << "Translation to LLVM succeeded." << std::endl;
::LLVMDumpModule(converted_module.module.get());
- bool failed = ::LLVMVerifyModule(converted_module.module.get(), ::LLVMPrintMessageAction, nullptr);
+ bool failed =
+ ::LLVMVerifyModule(converted_module.module.get(), ::LLVMPrintMessageAction, nullptr);
if(failed)
return 1;
}
set(sources llvm_wrapper.cpp)
add_library(llvm_wrapper STATIC ${sources})
-if(0)
+if(1)
llvm_map_components_to_libnames(llvm_libraries core mcjit native analysis)
else()
set(llvm_libraries LLVM)
*/
#include "llvm_wrapper.h"
#include <llvm/Support/Host.h>
+#include <llvm/ExecutionEngine/SectionMemoryManager.h>
#include <stdexcept>
#include <iostream>
#include <cstdlib>
+#include <algorithm>
namespace vulkan_cpu
{
namespace llvm_wrapper
{
-Context Context::create()
+void Context::init_helper()
{
if(!::LLVMIsMultithreaded())
throw std::runtime_error("LLVM is not multithreaded");
::LLVMLinkInMCJIT();
if(::LLVMInitializeNativeTarget() != 0)
throw std::runtime_error("LLVMInitializeNativeTarget failed");
- return Context(::LLVMContextCreate());
}
LLVM_string Target::get_process_target_triple()
{
+ Context::init();
return LLVM_string::from(llvm::sys::getProcessTriple());
}
LLVM_string Target::get_host_cpu_name()
{
+ Context::init();
return LLVM_string::from(llvm::sys::getHostCPUName());
}
LLVM_string Target::get_host_cpu_features()
{
+ Context::init();
llvm::StringMap<bool> features{};
if(!llvm::sys::getHostCPUFeatures(features))
return LLVM_string::from("");
std::string retval;
- bool first = true;
+ std::vector<std::string> names;
+ names.reserve(features.size());
for(auto &entry : features)
+ names.push_back(entry.first());
+ std::sort(names.begin(), names.end());
+ bool first = true;
+ for(auto &name : names)
{
if(first)
first = false;
else
retval += ',';
- if(entry.second)
+ if(features[name])
retval += '+';
else
retval += '-';
- retval += entry.first();
+ retval += name;
}
return LLVM_string::from(retval);
}
::LLVMCodeModelJITDefault));
}
-void Module::set_target_to_native(::LLVMModuleRef module)
+void Module::set_target_machine(::LLVMModuleRef module, ::LLVMTargetMachineRef target_machine)
+{
+ ::LLVMSetTarget(module, Target_machine::get_target_triple(target_machine).get());
+ ::LLVMSetModuleDataLayout(module,
+ Target_machine::create_target_data_layout(target_machine).get());
+}
+
+void Module::set_function_target_machine(::LLVMValueRef function,
+ ::LLVMTargetMachineRef target_machine)
+{
+ ::LLVMAddTargetDependentFunctionAttr(
+ function, "target-cpu", Target_machine::get_cpu(target_machine).get());
+ ::LLVMAddTargetDependentFunctionAttr(
+ function, "target-features", Target_machine::get_feature_string(target_machine).get());
+}
+
+Shared_memory_manager Shared_memory_manager::create()
{
- auto target_machine = Target_machine::create_native_target_machine();
- ::LLVMSetTarget(module, target_machine.get_target_triple().get());
- auto target_data_string = target_machine.create_target_data_layout().to_string();
- ::LLVMSetModuleDataLayout(module, target_machine.create_target_data_layout().get());
+ Context::init();
+ auto memory_manager = std::make_shared<llvm::SectionMemoryManager>();
+ std::unique_ptr<std::shared_ptr<llvm::SectionMemoryManager>> memory_manager_wrapper;
+ memory_manager_wrapper.reset(new std::shared_ptr<llvm::SectionMemoryManager>(memory_manager));
+ MCJIT_memory_manager mcjit_memory_manager(::LLVMCreateSimpleMCJITMemoryManager(
+ static_cast<void *>(memory_manager_wrapper.get()),
+ [](void *user_data,
+ std::uintptr_t size,
+ unsigned alignment,
+ unsigned section_id,
+ const char *section_name) -> std::uint8_t *
+ {
+ auto &memory_manager =
+ *static_cast<std::shared_ptr<llvm::SectionMemoryManager> *>(user_data);
+ return memory_manager->allocateCodeSection(size, alignment, section_id, section_name);
+ },
+ [](void *user_data,
+ std::uintptr_t size,
+ unsigned alignment,
+ unsigned section_id,
+ const char *section_name,
+ LLVMBool is_read_only) -> std::uint8_t *
+ {
+ auto &memory_manager =
+ *static_cast<std::shared_ptr<llvm::SectionMemoryManager> *>(user_data);
+ return memory_manager->allocateDataSection(
+ size, alignment, section_id, section_name, is_read_only);
+ },
+ [](void *user_data, char **error_message_out) -> LLVMBool
+ {
+ auto &memory_manager =
+ *static_cast<std::shared_ptr<llvm::SectionMemoryManager> *>(user_data);
+ if(!error_message_out)
+ return memory_manager->finalizeMemory(nullptr);
+ std::string error_message;
+ bool failed = memory_manager->finalizeMemory(&error_message);
+ if(failed)
+ *error_message_out = LLVM_string::from(error_message).release();
+ return failed;
+ },
+ [](void *user_data) noexcept
+ {
+ delete static_cast<std::shared_ptr<llvm::SectionMemoryManager> *>(user_data);
+ }));
+ memory_manager_wrapper.release();
+ return Shared_memory_manager(std::move(mcjit_memory_manager), std::move(memory_manager));
}
}
}
struct Context : public Wrapper<::LLVMContextRef, Context_deleter>
{
using Wrapper::Wrapper;
- static Context create();
+
+private:
+ static void init_helper();
+
+public:
+ static void init()
+ {
+ static int v = (init_helper(), 0);
+ static_cast<void>(v);
+ }
+ static Context create()
+ {
+ init();
+ return Context(::LLVMContextCreate());
+ }
};
struct Target_deleter
using Wrapper::Wrapper;
static LLVM_string get_default_target_triple()
{
+ Context::init();
return LLVM_string::wrap(::LLVMGetDefaultTargetTriple());
}
static LLVM_string get_process_target_triple();
typedef util::variant<Target, LLVM_string> Target_or_error_message;
static Target_or_error_message get_target_from_target_triple(const char *triple)
{
+ Context::init();
::LLVMTargetRef target = nullptr;
char *error_message = nullptr;
if(::LLVMGetTargetFromTriple(triple, &target, &error_message) == 0)
}
static Target get_native_target()
{
+ Context::init();
auto native_triple = get_process_target_triple();
auto retval = get_target_from_target_triple(native_triple.get());
auto *target = util::get_if<Target>(&retval);
{
return create_target_data_layout(get());
}
+ static LLVM_string get_cpu(::LLVMTargetMachineRef tm)
+ {
+ return LLVM_string::wrap(::LLVMGetTargetMachineCPU(tm));
+ }
+ LLVM_string get_cpu() const
+ {
+ return get_cpu(get());
+ }
+ static LLVM_string get_feature_string(::LLVMTargetMachineRef tm)
+ {
+ return LLVM_string::wrap(::LLVMGetTargetMachineFeatureString(tm));
+ }
+ LLVM_string get_feature_string() const
+ {
+ return get_feature_string(get());
+ }
};
struct Module_deleter
{
return Module(::LLVMModuleCreateWithNameInContext(id, context));
}
- static Module create_native(const char *id, ::LLVMContextRef context)
+ static Module create_with_target_machine(const char *id,
+ ::LLVMContextRef context,
+ ::LLVMTargetMachineRef target_machine)
{
Module retval = create(id, context);
- retval.set_target_to_native();
+ retval.set_target_machine(target_machine);
return retval;
}
- static void set_target_to_native(::LLVMModuleRef module);
- void set_target_to_native()
+ static void set_target_machine(::LLVMModuleRef module, ::LLVMTargetMachineRef target_machine);
+ static void set_function_target_machine(::LLVMValueRef function,
+ ::LLVMTargetMachineRef target_machine);
+ void set_target_machine(::LLVMTargetMachineRef target_machine)
{
- set_target_to_native(get());
+ set_target_machine(get(), target_machine);
}
};
return ::LLVMGetElementType(type);
return type;
}
+
+struct MCJIT_memory_manager_deleter
+{
+ void operator()(::LLVMMCJITMemoryManagerRef v) noexcept
+ {
+ ::LLVMDisposeMCJITMemoryManager(v);
+ }
+};
+
+struct MCJIT_memory_manager
+ : public Wrapper<::LLVMMCJITMemoryManagerRef, MCJIT_memory_manager_deleter>
+{
+ using Wrapper::Wrapper;
+};
+
+struct Shared_memory_manager
+{
+ MCJIT_memory_manager mcjit_memory_manager;
+ std::shared_ptr<void> shared_memory_manager;
+ explicit Shared_memory_manager(MCJIT_memory_manager mcjit_memory_manager,
+ std::shared_ptr<void> shared_memory_manager) noexcept
+ : mcjit_memory_manager(std::move(mcjit_memory_manager)),
+ shared_memory_manager(std::move(shared_memory_manager))
+ {
+ }
+ Shared_memory_manager() noexcept
+ {
+ }
+ static Shared_memory_manager create();
+};
+
+struct Execution_engine_deleter
+{
+ void operator()(::LLVMExecutionEngineRef v) noexcept
+ {
+ ::LLVMDisposeExecutionEngine(v);
+ }
+};
+
+struct Execution_engine : public Wrapper<::LLVMExecutionEngineRef, Execution_engine_deleter>
+{
+ using Wrapper::Wrapper;
+};
}
}
Word input_generator_magic_number = 0;
util::Enum_set<Capability> enabled_capabilities;
::LLVMContextRef context;
+ ::LLVMTargetMachineRef target_machine;
[[gnu::unused]] const std::uint64_t shader_id;
std::string name_prefix_string;
llvm_wrapper::Module module;
}
public:
- explicit Spirv_to_llvm(::LLVMContextRef context, std::uint64_t shader_id)
- : context(context), shader_id(shader_id), stage()
+ explicit Spirv_to_llvm(::LLVMContextRef context,
+ ::LLVMTargetMachineRef target_machine,
+ std::uint64_t shader_id)
+ : context(context), target_machine(target_machine), shader_id(shader_id), stage()
{
{
std::ostringstream ss;
ss << "shader_" << shader_id << "_";
name_prefix_string = ss.str();
}
- module = llvm_wrapper::Module::create_native(get_prefixed_name("module").c_str(), context);
+ module = llvm_wrapper::Module::create_with_target_machine(
+ get_prefixed_name("module").c_str(), context, target_machine);
builder = llvm_wrapper::Builder::create(context);
auto target_data = ::LLVMGetModuleDataLayout(module.get());
constexpr std::size_t no_instruction_index = 0;
+ std::string(get_enumerant_name(instruction.get_operation())));
auto function_type =
get_type<Function_type_descriptor>(instruction.function_type, instruction_start_index);
- state.function = Function_state(
- function_type,
- ::LLVMAddFunction(module.get(),
- get_prefixed_name(get_name(current_function_id)).c_str(),
- function_type->get_or_make_type()));
+ auto function = ::LLVMAddFunction(module.get(),
+ get_prefixed_name(get_name(current_function_id)).c_str(),
+ function_type->get_or_make_type());
+ llvm_wrapper::Module::set_function_target_machine(function, target_machine);
+ state.function = Function_state(function_type, function);
break;
}
}
}
Converted_module spirv_to_llvm(::LLVMContextRef context,
+ ::LLVMTargetMachineRef target_machine,
const Word *shader_words,
std::size_t shader_size,
std::uint64_t shader_id)
{
- return Spirv_to_llvm(context, shader_id).run(shader_words, shader_size);
+ return Spirv_to_llvm(context, target_machine, shader_id).run(shader_words, shader_size);
}
}
}
class Spirv_to_llvm;
Converted_module spirv_to_llvm(::LLVMContextRef context,
+ ::LLVMTargetMachineRef target_machine,
const spirv::Word *shader_words,
std::size_t shader_size,
std::uint64_t shader_id);