Start work on object loader using llvm's RuntimeDyld api.

This commit is contained in:
Lachlan Sneff
2019-02-25 18:07:22 -08:00
parent 2a913f5663
commit 4f833876e0
9 changed files with 579 additions and 122 deletions

View File

@ -1,45 +1,76 @@
use crate::intrinsics::Intrinsics;
use dlopen::symbor::Library;
use inkwell::{
module::Module,
execution_engine::{ExecutionEngine, JitFunction},
targets::{CodeModel, FileType, InitializationConfig, RelocMode, Target, TargetMachine},
OptimizationLevel,
};
use crate::intrinsics::Intrinsics;
use std::ptr::NonNull;
use std::{io::Write, ptr::NonNull};
use tempfile::NamedTempFile;
use wasmer_runtime_core::{
module::ModuleInner,
types::LocalFuncIndex,
structures::TypedIndex,
backend::{FuncResolver, vm},
backend::FuncResolver, module::ModuleInner, structures::TypedIndex, types::LocalFuncIndex, vm,
};
pub struct LLVMBackend {
exec_engine: ExecutionEngine,
tempfile: NamedTempFile,
library: Library,
}
impl LLVMBackend {
pub fn new(module: Module, intrinsics: Intrinsics) -> Self {
let exec_engine = module.create_jit_execution_engine(OptimizationLevel::Default).unwrap();
Target::initialize_x86(&InitializationConfig {
asm_parser: true,
asm_printer: true,
base: true,
disassembler: true,
info: true,
machine_code: true,
});
let triple = TargetMachine::get_default_triple().to_string();
let target = Target::from_triple(&triple).unwrap();
let target_machine = target
.create_target_machine(
&triple,
&TargetMachine::get_host_cpu_name().to_string(),
&TargetMachine::get_host_cpu_features().to_string(),
OptimizationLevel::Default,
RelocMode::PIC,
CodeModel::Default,
)
.unwrap();
exec_engine.add_global_mapping(&intrinsics.memory_grow_dynamic_local, vmcalls::local_dynamic_memory_grow as usize);
exec_engine.add_global_mapping(&intrinsics.memory_grow_static_local, vmcalls::local_static_memory_grow as usize);
exec_engine.add_global_mapping(&intrinsics.memory_grow_dynamic_import, vmcalls::imported_dynamic_memory_grow as usize);
exec_engine.add_global_mapping(&intrinsics.memory_grow_static_import, vmcalls::imported_static_memory_grow as usize);
exec_engine.add_global_mapping(&intrinsics.memory_size_dynamic_local, vmcalls::local_dynamic_memory_size as usize);
exec_engine.add_global_mapping(&intrinsics.memory_size_static_local, vmcalls::local_static_memory_size as usize);
exec_engine.add_global_mapping(&intrinsics.memory_size_dynamic_import, vmcalls::imported_dynamic_memory_size as usize);
exec_engine.add_global_mapping(&intrinsics.memory_size_static_import, vmcalls::imported_static_memory_size as usize);
let memory_buffer = target_machine
.write_to_memory_buffer(&module, FileType::Object)
.unwrap();
Self { exec_engine }
let mut tempfile = NamedTempFile::new().unwrap();
tempfile.write_all(memory_buffer.as_slice()).unwrap();
tempfile.flush().unwrap();
let library = Library::open(tempfile.path()).unwrap();
Self { tempfile, library }
}
}
impl FuncResolver for LLVMBackend {
fn get(&self, module: &ModuleInner, local_func_index: LocalFuncIndex) -> Option<NonNull<vm::Func>> {
fn get(
&self,
module: &ModuleInner,
local_func_index: LocalFuncIndex,
) -> Option<NonNull<vm::Func>> {
let index = module.info.imported_functions.len() + local_func_index.index();
let name = format!("fn{}", index);
let name = if cfg!(macos) {
format!("_fn{}", index)
} else {
format!("fn{}", index)
};
unsafe {
let func: JitFunction<unsafe extern fn()> = self.exec_engine.get_function(&name).ok()?;
self.library
.symbol::<NonNull<vm::Func>>(&name)
.ok()
.map(|symbol| *symbol)
}
}
}
}

View File

@ -481,6 +481,7 @@ fn parse_function(
// Emit an unreachable instruction.
// If llvm cannot prove that this is never touched,
// it will emit a `ud2` instruction on x86_64 arches.
ctx.build_trap();
builder.build_unreachable();
state.reachable = false;
}
@ -623,7 +624,8 @@ fn parse_function(
}
}
Operator::CallIndirect { index, table_index } => {
let expected_dynamic_sigindex = ctx.dynamic_sigindex(SigIndex::new(index as usize));
let sig_index = SigIndex::new(index as usize);
let expected_dynamic_sigindex = ctx.dynamic_sigindex(sig_index);
let (table_base, table_bound) = ctx.table(TableIndex::new(table_index as usize));
let func_index = state.pop1()?.into_int_value();
@ -651,12 +653,10 @@ fn parse_function(
"func_ptr",
)
.into_pointer_value(),
builder
.build_load(
builder.build_struct_gep(anyfunc_struct_ptr, 1, "ctx_ptr_ptr"),
"ctx_ptr",
)
.into_pointer_value(),
builder.build_load(
builder.build_struct_gep(anyfunc_struct_ptr, 1, "ctx_ptr_ptr"),
"ctx_ptr",
),
builder
.build_load(
builder.build_struct_gep(anyfunc_struct_ptr, 2, "sigindex_ptr"),
@ -685,21 +685,49 @@ fn parse_function(
)
.try_as_basic_value()
.left()
.unwrap();
.unwrap()
.into_int_value();
let continue_block = context.append_basic_block(&function, "continue_block");
let sigindices_notequal_block =
context.append_basic_block(&function, "sigindices_notequal_block");
builder.build_conditional_branch(
sigindices_equal,
&continue_block,
&sigindices_notequal_block,
);
builder.position_at_end(&sigindices_notequal_block);
ctx.build_trap();
builder.build_unreachable();
builder.position_at_end(&continue_block);
println!("func ptr: {:#?}", func_ptr);
println!("ctx ptr: {:#?}", ctx_ptr);
println!("found dynamic sigindex: {:#?}", found_dynamic_sigindex);
let wasmer_fn_sig = &info.signatures[sig_index];
let fn_ty = signatures[sig_index];
unimplemented!("{}, {}", index, table_index);
let pushed_args = state.popn_save(wasmer_fn_sig.params().len())?;
let args: Vec<_> = std::iter::once(ctx_ptr)
.chain(pushed_args.into_iter())
.collect();
println!("args: {:?}", args);
let typed_func_ptr = builder.build_pointer_cast(
func_ptr,
fn_ty.ptr_type(AddressSpace::Generic),
"typed_func_ptr",
);
let call_site = builder.build_call(typed_func_ptr, &args, "indirect_call");
match wasmer_fn_sig.returns() {
[] => {}
[_] => {
let value = call_site.try_as_basic_value().left().unwrap();
state.push1(value);
}
returns @ _ => unimplemented!("multi-value returns"),
}
}
/***************************

View File

@ -64,6 +64,7 @@ pub struct Intrinsics {
pub copysign_f64: FunctionValue,
pub expect_i1: FunctionValue,
pub trap: FunctionValue,
pub void_ty: VoidType,
pub i1_ty: IntType,
@ -255,6 +256,7 @@ impl Intrinsics {
copysign_f64: module.add_function("llvm.copysign.f64", ret_f64_take_f64_f64, None),
expect_i1: module.add_function("llvm.expect.i1", ret_i1_take_i1_i1, None),
trap: module.add_function("llvm.trap", void_ty.fn_type(&[], false), None),
void_ty,
i1_ty,
@ -695,7 +697,9 @@ impl<'a> CtxType<'a> {
(imported_func_cache.func_ptr, imported_func_cache.ctx_ptr)
}
// pub fn table(&mut self, table_index: TableIndex, elem_index: IntValue) ->
pub fn build_trap(&self) {
self.builder.build_call(self.intrinsics.trap, &[], "trap");
}
}
// pub struct Ctx {

View File

@ -10,11 +10,11 @@ use wasmer_runtime_core::{
module::ModuleInner,
};
mod backend;
mod code;
mod intrinsics;
mod read_info;
mod state;
// mod backend;
pub struct LLVMCompiler {
_private: (),
@ -28,13 +28,61 @@ impl LLVMCompiler {
impl Compiler for LLVMCompiler {
fn compile(&self, wasm: &[u8], _: Token) -> Result<ModuleInner, CompileError> {
let (_info, _code_reader) = read_info::read_module(wasm).unwrap();
let (info, code_reader) = read_info::read_module(wasm).unwrap();
let (module, intrinsics) = code::parse_function_bodies(&info, code_reader).unwrap();
unimplemented!()
let backend = backend::LLVMBackend::new(module, intrinsics);
// Create placeholder values here.
let (protected_caller, cache_gen) = {
use wasmer_runtime_core::backend::{
sys::Memory, CacheGen, ProtectedCaller, UserTrapper,
};
use wasmer_runtime_core::cache::Error as CacheError;
use wasmer_runtime_core::error::RuntimeResult;
use wasmer_runtime_core::module::ModuleInfo;
use wasmer_runtime_core::types::{FuncIndex, Value};
use wasmer_runtime_core::vm;
struct Placeholder;
impl ProtectedCaller for Placeholder {
fn call(
&self,
_module: &ModuleInner,
_func_index: FuncIndex,
_params: &[Value],
_import_backing: &vm::ImportBacking,
_vmctx: *mut vm::Ctx,
_: Token,
) -> RuntimeResult<Vec<Value>> {
Ok(vec![])
}
fn get_early_trapper(&self) -> Box<dyn UserTrapper> {
unimplemented!()
}
}
impl CacheGen for Placeholder {
fn generate_cache(
&self,
module: &ModuleInner,
) -> Result<(Box<ModuleInfo>, Box<[u8]>, Memory), CacheError> {
unimplemented!()
}
}
(Box::new(Placeholder), Box::new(Placeholder))
};
Ok(ModuleInner {
func_resolver: Box::new(backend),
protected_caller,
cache_gen,
info,
})
}
unsafe fn from_cache(&self, _artifact: Artifact, _: Token) -> Result<ModuleInner, CacheError> {
unimplemented!()
unimplemented!("the llvm backend doesn't support caching yet")
}
}
@ -55,14 +103,9 @@ fn test_read_module() {
get_local 0
i32.const 0
call_indirect (type $t0)
memory.grow
)
(func $foobar (type $t0)
get_local 0
)
(func $bar (type $t0) (param i32) (result i32)
get_local 0
call $foo
))
"#;
let wasm = wat2wasm(wat).unwrap();
@ -71,86 +114,50 @@ fn test_read_module() {
let (module, intrinsics) = code::parse_function_bodies(&info, code_reader).unwrap();
{
Target::initialize_x86(&InitializationConfig {
asm_parser: true,
asm_printer: true,
base: true,
disassembler: true,
info: true,
machine_code: true,
});
let triple = TargetMachine::get_default_triple().to_string();
let target = Target::from_triple(&triple).unwrap();
let target_machine = target
.create_target_machine(
&triple,
&TargetMachine::get_host_cpu_name().to_string(),
&TargetMachine::get_host_cpu_features().to_string(),
OptimizationLevel::Default,
RelocMode::PIC,
CodeModel::Default,
)
.unwrap();
// let backend = backend::LLVMBackend::new(module, intrinsics);
let memory_buffer = target_machine
.write_to_memory_buffer(&module, FileType::Object)
.unwrap();
// std::fs::write("memory_buffer", memory_buffer.as_slice()).unwrap();
let mem_buf_slice = memory_buffer.as_slice();
let macho = goblin::mach::MachO::parse(mem_buf_slice, 0).unwrap();
let symbols = macho.symbols.as_ref().unwrap();
let relocations = macho.relocations().unwrap();
for (_, reloc_iter, section) in relocations.into_iter() {
println!("section: {:#?}", section);
for reloc_info in reloc_iter {
let reloc_info = reloc_info.unwrap();
println!("\treloc_info: {:#?}", reloc_info);
println!(
"\tsymbol: {:#?}",
symbols.get(reloc_info.r_symbolnum()).unwrap()
);
}
}
extern "C" {
fn test_cpp();
}
let exec_engine = module
.create_jit_execution_engine(OptimizationLevel::Default)
.unwrap();
unsafe { test_cpp() };
exec_engine.add_global_mapping(
&intrinsics.memory_grow_dynamic_local,
vmcalls::local_dynamic_memory_grow as usize,
);
exec_engine.add_global_mapping(
&intrinsics.memory_grow_static_local,
vmcalls::local_static_memory_grow as usize,
);
exec_engine.add_global_mapping(
&intrinsics.memory_grow_dynamic_import,
vmcalls::imported_dynamic_memory_grow as usize,
);
exec_engine.add_global_mapping(
&intrinsics.memory_grow_static_import,
vmcalls::imported_static_memory_grow as usize,
);
exec_engine.add_global_mapping(
&intrinsics.memory_size_dynamic_local,
vmcalls::local_dynamic_memory_size as usize,
);
exec_engine.add_global_mapping(
&intrinsics.memory_size_static_local,
vmcalls::local_static_memory_size as usize,
);
exec_engine.add_global_mapping(
&intrinsics.memory_size_dynamic_import,
vmcalls::imported_dynamic_memory_size as usize,
);
exec_engine.add_global_mapping(
&intrinsics.memory_size_static_import,
vmcalls::imported_static_memory_size as usize,
);
// let exec_engine = module
// .create_jit_execution_engine(OptimizationLevel::Default)
// .unwrap();
// exec_engine.add_global_mapping(
// &intrinsics.memory_grow_dynamic_local,
// vmcalls::local_dynamic_memory_grow as usize,
// );
// exec_engine.add_global_mapping(
// &intrinsics.memory_grow_static_local,
// vmcalls::local_static_memory_grow as usize,
// );
// exec_engine.add_global_mapping(
// &intrinsics.memory_grow_dynamic_import,
// vmcalls::imported_dynamic_memory_grow as usize,
// );
// exec_engine.add_global_mapping(
// &intrinsics.memory_grow_static_import,
// vmcalls::imported_static_memory_grow as usize,
// );
// exec_engine.add_global_mapping(
// &intrinsics.memory_size_dynamic_local,
// vmcalls::local_dynamic_memory_size as usize,
// );
// exec_engine.add_global_mapping(
// &intrinsics.memory_size_static_local,
// vmcalls::local_static_memory_size as usize,
// );
// exec_engine.add_global_mapping(
// &intrinsics.memory_size_dynamic_import,
// vmcalls::imported_dynamic_memory_size as usize,
// );
// exec_engine.add_global_mapping(
// &intrinsics.memory_size_static_import,
// vmcalls::imported_static_memory_size as usize,
// );
// unsafe {
// let func: JitFunction<unsafe extern fn(*mut u8, i32) -> i32> = exec_engine.get_function("fn0").unwrap();