mirror of
https://github.com/mii443/wasmer.git
synced 2025-12-08 21:58:20 +00:00
Add support for dynamic trampolines in compiler-llvm.
This commit is contained in:
@@ -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>>())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user