Add support for dynamic trampolines in compiler-llvm.

This commit is contained in:
Nick Lewycky
2020-05-29 16:56:41 -07:00
parent eaff807bc3
commit 45e80dec7b
2 changed files with 213 additions and 8 deletions

View File

@@ -128,9 +128,19 @@ impl Compiler for LLVMCompiler {
fn compile_dynamic_function_trampolines(
&self,
_module: &ModuleInfo,
module: &ModuleInfo,
) -> Result<PrimaryMap<FunctionIndex, FunctionBody>, CompileError> {
Ok(PrimaryMap::new())
// unimplemented!("Dynamic function trampolines not yet implemented");
Ok(module
.functions
.values()
.take(module.num_imported_funcs)
.collect::<Vec<_>>()
.par_iter()
.map_init(FuncTrampoline::new, |func_trampoline, sig_index| {
func_trampoline.dynamic_trampoline(&module.signatures[**sig_index], self.config())
})
.collect::<Result<Vec<_>, CompileError>>()?
.into_iter()
.collect::<PrimaryMap<FunctionIndex, FunctionBody>>())
}
}

View File

@@ -1,10 +1,20 @@
use crate::config::LLVMConfig;
use crate::object_file::load_object_file;
use crate::translator::intrinsics::{func_type_to_llvm, type_to_llvm_ptr, Intrinsics};
use inkwell::{
context::Context, module::Linkage, passes::PassManager, targets::FileType, types::BasicType,
values::FunctionValue, AddressSpace,
use crate::translator::intrinsics::{
func_type_to_llvm, type_to_llvm, type_to_llvm_ptr, Intrinsics,
};
use inkwell::{
context::Context,
module::Linkage,
passes::PassManager,
targets::FileType,
types::BasicType,
values::{BasicValue, FunctionValue},
AddressSpace,
};
use std::cmp;
use std::convert::TryInto;
use std::iter;
use wasm_common::{FunctionType, Type};
use wasmer_compiler::{CompileError, FunctionBody};
@@ -12,7 +22,7 @@ pub struct FuncTrampoline {
ctx: Context,
}
const FUNCTION_SECTION: &str = "wasmer_trampoline";
const FUNCTION_SECTION: &str = ".wasmer_trampoline";
impl FuncTrampoline {
pub fn new() -> Self {
@@ -111,6 +121,94 @@ impl FuncTrampoline {
unwind_info: function.body.unwind_info,
})
}
pub fn dynamic_trampoline(
&mut self,
ty: &FunctionType,
config: &LLVMConfig,
) -> Result<FunctionBody, CompileError> {
let module = self.ctx.create_module("");
let target_triple = config.target_triple();
let target_machine = config.target_machine();
module.set_triple(&target_triple);
module.set_data_layout(&target_machine.get_target_data().get_data_layout());
let intrinsics = Intrinsics::declare(&module, &self.ctx);
let params = iter::once(intrinsics.ctx_ptr_ty.as_basic_type_enum())
.chain(
ty.params()
.iter()
.map(|param_ty| type_to_llvm(&intrinsics, *param_ty)),
)
.collect::<Vec<_>>();
let trampoline_ty = intrinsics.void_ty.fn_type(params.as_slice(), false);
let trampoline_func = module.add_function("", trampoline_ty, Some(Linkage::External));
trampoline_func
.as_global_value()
.set_section(FUNCTION_SECTION);
generate_dynamic_trampoline(trampoline_func, ty, &self.ctx, &intrinsics)?;
// TODO: remove debugging
//module.print_to_stderr();
let pass_manager = PassManager::create(());
if config.enable_verifier {
pass_manager.add_verifier_pass();
}
pass_manager.add_early_cse_pass();
pass_manager.run_on(&module);
// TODO: remove debugging
//module.print_to_stderr();
let memory_buffer = target_machine
.write_to_memory_buffer(&module, FileType::Object)
.unwrap();
/*
// TODO: remove debugging
let mem_buf_slice = memory_buffer.as_slice();
let mut file = fs::File::create("/home/nicholas/trampoline.o").unwrap();
let mut pos = 0;
while pos < mem_buf_slice.len() {
pos += file.write(&mem_buf_slice[pos..]).unwrap();
}
*/
let mem_buf_slice = memory_buffer.as_slice();
let (function, sections) =
load_object_file(mem_buf_slice, FUNCTION_SECTION, None, |name: &String| {
Err(CompileError::Codegen(format!(
"trampoline generation produced reference to unknown function {}",
name
)))
})?;
if !sections.is_empty() {
return Err(CompileError::Codegen(
"trampoline generation produced custom sections".into(),
));
}
if !function.relocations.is_empty() {
return Err(CompileError::Codegen(
"trampoline generation produced relocations".into(),
));
}
if !function.jt_offsets.is_empty() {
return Err(CompileError::Codegen(
"trampoline generation produced jump tables".into(),
));
}
// Ignore CompiledFunctionFrameInfo. Extra frame info isn't a problem.
Ok(FunctionBody {
body: function.body.body,
unwind_info: function.body.unwind_info,
})
}
}
fn generate_trampoline<'ctx>(
@@ -191,3 +289,100 @@ fn generate_trampoline<'ctx>(
builder.build_return(None);
Ok(())
}
fn generate_dynamic_trampoline<'ctx>(
trampoline_func: FunctionValue,
func_sig: &FunctionType,
context: &'ctx Context,
intrinsics: &Intrinsics<'ctx>,
) -> Result<(), CompileError> {
let entry_block = context.append_basic_block(trampoline_func, "entry");
let builder = context.create_builder();
builder.position_at_end(entry_block);
/*
// TODO: remove debugging
builder.build_call(
intrinsics.debug_trap,
&[],
"");
*/
// Allocate stack space for the params and results.
let values = builder.build_alloca(
intrinsics.i128_ty.array_type(cmp::max(
func_sig.params().len().try_into().unwrap(),
func_sig.results().len().try_into().unwrap(),
)),
"",
);
// Copy params to 'values'.
for i in 0..func_sig.params().len() {
let ptr = unsafe {
builder.build_in_bounds_gep(
values,
&[
intrinsics.i32_zero,
intrinsics.i32_ty.const_int(i.try_into().unwrap(), false),
],
"",
)
};
let ptr = builder
.build_bitcast(ptr, type_to_llvm_ptr(intrinsics, func_sig.params()[i]), "")
.into_pointer_value();
builder.build_store(ptr, trampoline_func.get_nth_param(i as u32 + 1).unwrap());
}
let callee_ty = intrinsics
.void_ty
.fn_type(
&[
intrinsics.ctx_ptr_ty.as_basic_type_enum(),
intrinsics.i128_ptr_ty.as_basic_type_enum(),
],
false,
)
.ptr_type(AddressSpace::Generic)
.ptr_type(AddressSpace::Generic);
let vmctx = trampoline_func.get_nth_param(0).unwrap();
let callee = builder
.build_load(
builder
.build_bitcast(vmctx, callee_ty, "")
.into_pointer_value(),
"",
)
.into_pointer_value();
builder.build_call(
callee,
&[vmctx.as_basic_value_enum(), values.as_basic_value_enum()],
"",
);
match func_sig.results() {
[] => {
builder.build_return(None);
}
[ty] => {
let ptr = unsafe {
builder.build_in_bounds_gep(
values,
&[intrinsics.i32_zero, intrinsics.i32_ty.const_int(0, false)],
"",
)
};
let ptr = builder
.build_bitcast(ptr, type_to_llvm_ptr(intrinsics, *ty), "")
.into_pointer_value();
let ret = builder.build_load(ptr, "");
builder.build_return(Some(&ret));
}
_ => unimplemented!("multi-value return is not yet implemented"),
};
Ok(())
}