mirror of
https://github.com/fluencelabs/wasmer
synced 2025-03-30 14:31:07 +00:00
268 lines
8.0 KiB
Rust
268 lines
8.0 KiB
Rust
use crate::cache::TrampolineCache;
|
|
use cranelift_codegen::{
|
|
binemit::{NullTrapSink, Reloc, RelocSink},
|
|
cursor::{Cursor, FuncCursor},
|
|
ir::{self, InstBuilder},
|
|
isa, Context,
|
|
};
|
|
use hashbrown::HashMap;
|
|
use std::ffi::c_void;
|
|
use std::{iter, mem};
|
|
use wasmer_runtime_core::{
|
|
backend::sys::{Memory, Protect},
|
|
module::{ExportIndex, ModuleInfo},
|
|
types::{FuncSig, SigIndex, Type},
|
|
vm,
|
|
};
|
|
|
|
struct NullRelocSink {}
|
|
|
|
impl RelocSink for NullRelocSink {
|
|
fn reloc_ebb(&mut self, _: u32, _: Reloc, _: u32) {}
|
|
fn reloc_external(&mut self, _: u32, _: Reloc, _: &ir::ExternalName, _: i64) {}
|
|
fn reloc_jt(&mut self, _: u32, _: Reloc, _: ir::JumpTable) {}
|
|
}
|
|
|
|
pub type Trampoline =
|
|
unsafe extern "C" fn(*mut vm::Ctx, *const vm::Func, *const u64, *mut u64) -> c_void;
|
|
|
|
pub struct Trampolines {
|
|
memory: Memory,
|
|
offsets: HashMap<SigIndex, usize>,
|
|
}
|
|
|
|
impl Trampolines {
|
|
pub fn from_trampoline_cache(cache: TrampolineCache) -> Self {
|
|
// pub struct TrampolineCache {
|
|
// #[serde(with = "serde_bytes")]
|
|
// code: Vec<u8>,
|
|
// offsets: HashMap<SigIndex, usize>,
|
|
// }
|
|
|
|
let mut memory = Memory::with_size(cache.code.len()).unwrap();
|
|
unsafe {
|
|
memory.protect(.., Protect::ReadWrite).unwrap();
|
|
|
|
// Copy over the compiled code.
|
|
memory.as_slice_mut()[..cache.code.len()].copy_from_slice(cache.code.as_slice());
|
|
|
|
memory.protect(.., Protect::ReadExec).unwrap();
|
|
}
|
|
|
|
Self {
|
|
memory,
|
|
offsets: cache.offsets,
|
|
}
|
|
}
|
|
|
|
pub fn to_trampoline_cache(&self) -> TrampolineCache {
|
|
let mut code = vec![0; self.memory.size()];
|
|
|
|
unsafe {
|
|
code.copy_from_slice(self.memory.as_slice());
|
|
}
|
|
|
|
TrampolineCache {
|
|
code,
|
|
offsets: self.offsets.clone(),
|
|
}
|
|
}
|
|
|
|
pub fn new(isa: &isa::TargetIsa, module: &ModuleInfo) -> Self {
|
|
let func_index_iter = module
|
|
.exports
|
|
.values()
|
|
.filter_map(|export| match export {
|
|
ExportIndex::Func(func_index) => Some(func_index),
|
|
_ => None,
|
|
})
|
|
.chain(module.start_func.iter());
|
|
|
|
let mut compiled_functions = Vec::new();
|
|
let mut ctx = Context::new();
|
|
let mut total_size = 0;
|
|
|
|
for exported_func_index in func_index_iter {
|
|
let sig_index = module.func_assoc[*exported_func_index];
|
|
let func_sig = &module.signatures[sig_index];
|
|
|
|
let trampoline_func = generate_func(&func_sig);
|
|
|
|
ctx.func = trampoline_func;
|
|
|
|
let mut code_buf = Vec::new();
|
|
|
|
ctx.compile_and_emit(
|
|
isa,
|
|
&mut code_buf,
|
|
&mut NullRelocSink {},
|
|
&mut NullTrapSink {},
|
|
)
|
|
.expect("unable to compile trampolines");
|
|
ctx.clear();
|
|
|
|
total_size += round_up(code_buf.len(), mem::size_of::<usize>());
|
|
compiled_functions.push((sig_index, code_buf));
|
|
}
|
|
|
|
let mut memory = Memory::with_size(total_size).unwrap();
|
|
unsafe {
|
|
memory.protect(.., Protect::ReadWrite).unwrap();
|
|
}
|
|
|
|
// "\xCC" disassembles to "int3", which will immediately cause
|
|
// an interrupt.
|
|
for i in unsafe { memory.as_slice_mut() } {
|
|
*i = 0xCC;
|
|
}
|
|
|
|
let mut previous_end = 0;
|
|
let mut trampolines = HashMap::with_capacity(compiled_functions.len());
|
|
|
|
for (sig_index, compiled) in compiled_functions.iter() {
|
|
let new_end = previous_end + round_up(compiled.len(), mem::size_of::<usize>());
|
|
unsafe {
|
|
memory.as_slice_mut()[previous_end..previous_end + compiled.len()]
|
|
.copy_from_slice(&compiled[..]);
|
|
}
|
|
trampolines.insert(*sig_index, previous_end);
|
|
previous_end = new_end;
|
|
}
|
|
|
|
unsafe {
|
|
memory.protect(.., Protect::ReadExec).unwrap();
|
|
}
|
|
|
|
Self {
|
|
memory,
|
|
offsets: trampolines,
|
|
}
|
|
}
|
|
|
|
pub fn lookup(&self, sig_index: SigIndex) -> Option<Trampoline> {
|
|
let offset = *self.offsets.get(&sig_index)?;
|
|
let ptr = unsafe { self.memory.as_ptr().add(offset) };
|
|
|
|
unsafe { Some(mem::transmute(ptr)) }
|
|
}
|
|
}
|
|
|
|
/// This function generates a trampoline for the specific signature
|
|
/// passed into it.
|
|
fn generate_func(func_sig: &FuncSig) -> ir::Function {
|
|
let trampoline_sig = generate_trampoline_signature();
|
|
|
|
let mut func =
|
|
ir::Function::with_name_signature(ir::ExternalName::testcase("trampln"), trampoline_sig);
|
|
|
|
let export_sig_ref = func.import_signature(generate_export_signature(func_sig));
|
|
|
|
let entry_ebb = func.dfg.make_ebb();
|
|
let vmctx_ptr = func.dfg.append_ebb_param(entry_ebb, ir::types::I64);
|
|
let func_ptr = func.dfg.append_ebb_param(entry_ebb, ir::types::I64);
|
|
let args_ptr = func.dfg.append_ebb_param(entry_ebb, ir::types::I64);
|
|
let returns_ptr = func.dfg.append_ebb_param(entry_ebb, ir::types::I64);
|
|
func.layout.append_ebb(entry_ebb);
|
|
|
|
let mut pos = FuncCursor::new(&mut func).at_first_insertion_point(entry_ebb);
|
|
|
|
let mut args_vec = Vec::with_capacity(func_sig.params().len() + 1);
|
|
args_vec.push(vmctx_ptr);
|
|
for (index, wasm_ty) in func_sig.params().iter().enumerate() {
|
|
let mem_flags = ir::MemFlags::trusted();
|
|
|
|
let val = pos.ins().load(
|
|
wasm_ty_to_clif(*wasm_ty),
|
|
mem_flags,
|
|
args_ptr,
|
|
(index * mem::size_of::<u64>()) as i32,
|
|
);
|
|
args_vec.push(val);
|
|
}
|
|
|
|
let call_inst = pos.ins().call_indirect(export_sig_ref, func_ptr, &args_vec);
|
|
|
|
let return_values = pos.func.dfg.inst_results(call_inst).to_vec();
|
|
|
|
for (index, return_val) in return_values.iter().enumerate() {
|
|
let mem_flags = ir::MemFlags::trusted();
|
|
|
|
pos.ins().store(
|
|
mem_flags,
|
|
*return_val,
|
|
returns_ptr,
|
|
(index * mem::size_of::<u64>()) as i32,
|
|
);
|
|
}
|
|
|
|
pos.ins().return_(&[]);
|
|
|
|
func
|
|
}
|
|
|
|
fn wasm_ty_to_clif(ty: Type) -> ir::types::Type {
|
|
match ty {
|
|
Type::I32 => ir::types::I32,
|
|
Type::I64 => ir::types::I64,
|
|
Type::F32 => ir::types::F32,
|
|
Type::F64 => ir::types::F64,
|
|
}
|
|
}
|
|
|
|
fn generate_trampoline_signature() -> ir::Signature {
|
|
let isa = super::get_isa();
|
|
let call_convention = isa.default_call_conv();
|
|
let mut sig = ir::Signature::new(call_convention);
|
|
|
|
let ptr_param = ir::AbiParam {
|
|
value_type: ir::types::I64,
|
|
purpose: ir::ArgumentPurpose::Normal,
|
|
extension: ir::ArgumentExtension::None,
|
|
location: ir::ArgumentLoc::Unassigned,
|
|
};
|
|
|
|
sig.params = vec![ptr_param, ptr_param, ptr_param, ptr_param];
|
|
|
|
sig
|
|
}
|
|
|
|
fn generate_export_signature(func_sig: &FuncSig) -> ir::Signature {
|
|
let isa = super::get_isa();
|
|
let call_convention = isa.default_call_conv();
|
|
let mut export_clif_sig = ir::Signature::new(call_convention);
|
|
|
|
let func_sig_iter = func_sig.params().iter().map(|wasm_ty| ir::AbiParam {
|
|
value_type: wasm_ty_to_clif(*wasm_ty),
|
|
purpose: ir::ArgumentPurpose::Normal,
|
|
extension: ir::ArgumentExtension::None,
|
|
location: ir::ArgumentLoc::Unassigned,
|
|
});
|
|
|
|
export_clif_sig.params = iter::once(ir::AbiParam {
|
|
value_type: ir::types::I64,
|
|
purpose: ir::ArgumentPurpose::VMContext,
|
|
extension: ir::ArgumentExtension::None,
|
|
location: ir::ArgumentLoc::Unassigned,
|
|
})
|
|
.chain(func_sig_iter)
|
|
.collect();
|
|
|
|
export_clif_sig.returns = func_sig
|
|
.returns()
|
|
.iter()
|
|
.map(|wasm_ty| ir::AbiParam {
|
|
value_type: wasm_ty_to_clif(*wasm_ty),
|
|
purpose: ir::ArgumentPurpose::Normal,
|
|
extension: ir::ArgumentExtension::None,
|
|
location: ir::ArgumentLoc::Unassigned,
|
|
})
|
|
.collect();
|
|
|
|
export_clif_sig
|
|
}
|
|
|
|
#[inline]
|
|
fn round_up(n: usize, multiple: usize) -> usize {
|
|
(n + multiple - 1) & !(multiple - 1)
|
|
}
|