Changed to using custom calling conventions; Implemented direct calls.

This commit is contained in:
losfair
2019-02-24 00:52:32 +08:00
parent e9c0325784
commit 09cbd4aeb0
4 changed files with 371 additions and 57 deletions

View File

@ -1,9 +1,23 @@
use wasmer_runtime_core::backend::ProtectedCaller;
use std::sync::Arc;
use wasmer_runtime_core::{
backend::ProtectedCaller,
structures::Map,
types::{FuncIndex, FuncSig, SigIndex},
units::Pages,
};
use wasmparser::{Operator, Type as WpType};
pub trait ModuleCodeGenerator<FCG: FunctionCodeGenerator, PC: ProtectedCaller> {
fn next_function(&mut self) -> Result<&mut FCG, CodegenError>;
fn finalize(self) -> Result<PC, CodegenError>;
fn feed_signatures(
&mut self,
signatures: Map<SigIndex, Arc<FuncSig>>,
) -> Result<(), CodegenError>;
fn feed_function_signatures(
&mut self,
assoc: Map<FuncIndex, SigIndex>,
) -> Result<(), CodegenError>;
}
pub trait FunctionCodeGenerator {

View File

@ -1,8 +1,10 @@
use super::codegen::*;
use super::stack::{ControlFrame, ControlStack, ValueInfo, ValueLocation, ValueStack};
use byteorder::{ByteOrder, LittleEndian};
use dynasmrt::{
x64::Assembler, AssemblyOffset, DynamicLabel, DynasmApi, DynasmLabelApi, ExecutableBuffer,
};
use std::{collections::HashMap, sync::Arc};
use wasmer_runtime_core::{
backend::{Backend, Compiler, FuncResolver, ProtectedCaller, Token, UserTrapper},
error::{CompileError, CompileResult, RuntimeError, RuntimeResult},
@ -16,6 +18,40 @@ use wasmer_runtime_core::{
};
use wasmparser::{Operator, Type as WpType};
lazy_static! {
static ref CALL_WASM: unsafe extern "C" fn(params: *const u8, params_len: usize, target: *const u8) -> i64 = {
let mut assembler = Assembler::new().unwrap();
let offset = assembler.offset();
dynasm!(
assembler
; lea rax, [>after_call]
; push rax
; push rbp
; mov rbp, rsp
; sub rsp, rsi // params_len
; mov rcx, 0
; mov r8, rsp
; _loop:
; cmp rsi, 0
; je >_loop_end
; mov eax, [rdi]
; mov [r8], eax
; add r8, 4
; add rdi, 4
; sub rsi, 4
; jmp <_loop
; _loop_end:
; jmp rdx
; after_call:
; ret
);
let buf = assembler.finalize().unwrap();
let ret = unsafe { ::std::mem::transmute(buf.ptr(offset)) };
::std::mem::forget(buf);
ret
};
}
#[repr(u8)]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Register {
@ -73,13 +109,19 @@ impl Register {
#[derive(Default)]
pub struct X64ModuleCodeGenerator {
functions: Vec<X64FunctionCode>,
signatures: Option<Arc<Map<SigIndex, Arc<FuncSig>>>>,
function_signatures: Option<Arc<Map<FuncIndex, SigIndex>>>,
}
pub struct X64FunctionCode {
signatures: Arc<Map<SigIndex, Arc<FuncSig>>>,
function_signatures: Arc<Map<FuncIndex, SigIndex>>,
id: usize,
begin_label: DynamicLabel,
begin_offset: AssemblyOffset,
assembler: Option<Assembler>,
function_labels: Option<HashMap<usize, DynamicLabel>>,
returns: Vec<WpType>,
locals: Vec<Local>,
num_params: usize,
@ -114,23 +156,48 @@ impl ProtectedCaller for X64ExecutionContext {
});
}
match self.functions[index].num_params {
0 => unsafe {
let ptr: extern "C" fn() -> i64 = ::std::mem::transmute(ptr);
Ok(vec![Value::I32(ptr() as i32)])
},
1 => unsafe {
let ptr: extern "C" fn(i64) -> i64 = ::std::mem::transmute(ptr);
Ok(vec![Value::I32(ptr(value_to_i64(&_params[0])) as i32)])
},
2 => unsafe {
let ptr: extern "C" fn(i64, i64) -> i64 = ::std::mem::transmute(ptr);
Ok(vec![Value::I32(
ptr(value_to_i64(&_params[0]), value_to_i64(&_params[1])) as i32,
)])
},
_ => unimplemented!(),
let f = &self.functions[index];
let mut total_size: usize = 0;
for local in &f.locals[0..f.num_params] {
total_size += get_size_of_type(&local.ty).unwrap();
}
let mut param_buf: Vec<u8> = vec![0; total_size];
for i in 0..f.num_params {
let local = &f.locals[i];
let buf = &mut param_buf[total_size - local.stack_offset..];
let size = get_size_of_type(&local.ty).unwrap();
if is_dword(size) {
match _params[i] {
Value::I32(x) => LittleEndian::write_u32(buf, x as u32),
Value::F32(x) => LittleEndian::write_u32(buf, f32::to_bits(x)),
_ => {
return Err(RuntimeError::User {
msg: "signature mismatch".into(),
})
}
}
} else {
match _params[i] {
Value::I64(x) => LittleEndian::write_u64(buf, x as u64),
Value::F64(x) => LittleEndian::write_u64(buf, f64::to_bits(x)),
_ => {
return Err(RuntimeError::User {
msg: "signature mismatch".into(),
})
}
}
}
}
let ret = unsafe { CALL_WASM(param_buf.as_ptr(), param_buf.len(), ptr) };
Ok(if let Some(ty) = return_ty {
vec![Value::I64(ret)]
} else {
vec![]
})
}
fn get_early_trapper(&self) -> Box<dyn UserTrapper> {
@ -160,31 +227,41 @@ impl X64ModuleCodeGenerator {
impl ModuleCodeGenerator<X64FunctionCode, X64ExecutionContext> for X64ModuleCodeGenerator {
fn next_function(&mut self) -> Result<&mut X64FunctionCode, CodegenError> {
let mut assembler = match self.functions.last_mut() {
Some(x) => x.assembler.take().unwrap(),
None => match Assembler::new() {
Ok(x) => x,
Err(_) => {
return Err(CodegenError {
message: "cannot initialize assembler",
})
}
},
let (mut assembler, mut function_labels) = match self.functions.last_mut() {
Some(x) => (
x.assembler.take().unwrap(),
x.function_labels.take().unwrap(),
),
None => (
match Assembler::new() {
Ok(x) => x,
Err(_) => {
return Err(CodegenError {
message: "cannot initialize assembler",
})
}
},
HashMap::new(),
),
};
let begin_label = assembler.new_dynamic_label();
let begin_label = *function_labels
.entry(self.functions.len())
.or_insert_with(|| assembler.new_dynamic_label());
let begin_offset = assembler.offset();
dynasm!(
assembler
; => begin_label
; push rbp
; mov rbp, rsp
//; int 3
);
let code = X64FunctionCode {
signatures: self.signatures.as_ref().unwrap().clone(),
function_signatures: self.function_signatures.as_ref().unwrap().clone(),
id: self.functions.len(),
begin_label: begin_label,
begin_offset: begin_offset,
assembler: Some(assembler),
function_labels: Some(function_labels),
returns: vec![],
locals: vec![],
num_params: 0,
@ -212,6 +289,22 @@ impl ModuleCodeGenerator<X64FunctionCode, X64ExecutionContext> for X64ModuleCode
functions: self.functions,
})
}
fn feed_signatures(
&mut self,
signatures: Map<SigIndex, Arc<FuncSig>>,
) -> Result<(), CodegenError> {
self.signatures = Some(Arc::new(signatures));
Ok(())
}
fn feed_function_signatures(
&mut self,
assoc: Map<FuncIndex, SigIndex>,
) -> Result<(), CodegenError> {
self.function_signatures = Some(Arc::new(assoc));
Ok(())
}
}
impl X64FunctionCode {
@ -486,6 +579,40 @@ impl X64FunctionCode {
Ok(val.ty)
}
fn emit_push_from_ax(
assembler: &mut Assembler,
value_stack: &mut ValueStack,
ty: WpType,
) -> Result<(), CodegenError> {
let loc = value_stack.push(ty);
match loc {
ValueLocation::Register(x) => {
let reg = Register::from_scratch_reg(x);
dynasm!(
assembler
; mov Rq(reg as u8), rax
);
}
ValueLocation::Stack => {
if is_dword(get_size_of_type(&ty)?) {
dynasm!(
assembler
; sub rsp, 4
; mov [rsp], eax
);
} else {
dynasm!(
assembler
; sub rsp, 8
; mov [rsp], rax
);
}
}
}
Ok(())
}
fn emit_leave_frame(
assembler: &mut Assembler,
frame: &ControlFrame,
@ -658,6 +785,147 @@ impl X64FunctionCode {
Ok(())
}
fn emit_call_raw(
assembler: &mut Assembler,
value_stack: &mut ValueStack,
target: DynamicLabel,
params: &[WpType],
returns: &[WpType],
) -> Result<(), CodegenError> {
let mut total_size: usize = 0;
for ty in params {
total_size += get_size_of_type(ty)?;
}
if params.len() > value_stack.values.len() {
return Err(CodegenError {
message: "value stack underflow in call",
});
}
let mut saved_regs: Vec<Register> = Vec::new();
for v in &value_stack.values[0..value_stack.values.len() - params.len()] {
match v.location {
ValueLocation::Register(x) => {
let reg = Register::from_scratch_reg(x);
dynasm!(
assembler
; push Rq(reg as u8)
);
saved_regs.push(reg);
}
ValueLocation::Stack => break,
}
}
dynasm!(
assembler
; lea rax, [>after_call] // TODO: Is this correct?
; push rax
; push rbp
);
if total_size != 0 {
dynasm!(
assembler
; sub rsp, total_size as i32
);
}
let mut offset: usize = 0;
let mut caller_stack_offset: usize = 0;
for ty in params {
let val = value_stack.pop()?;
if val.ty != *ty {
return Err(CodegenError {
message: "value type mismatch",
});
}
let size = get_size_of_type(ty)?;
match val.location {
ValueLocation::Register(x) => {
let reg = Register::from_scratch_reg(x);
if is_dword(size) {
dynasm!(
assembler
; mov [rsp + offset as i32], Rd(reg as u8)
);
} else {
dynasm!(
assembler
; mov [rsp + offset as i32], Rq(reg as u8)
);
}
}
ValueLocation::Stack => {
if is_dword(size) {
dynasm!(
assembler
; mov eax, [rsp + (total_size + 16 + caller_stack_offset) as i32]
; mov [rsp + offset as i32], eax
);
} else {
dynasm!(
assembler
; mov rax, [rsp + (total_size + 16 + caller_stack_offset) as i32]
; mov [rsp + offset as i32], rax
);
}
caller_stack_offset += size;
}
}
offset += size;
}
assert_eq!(offset, total_size);
dynasm!(
assembler
; mov rbp, rsp
);
if total_size != 0 {
dynasm!(
assembler
; add rbp, total_size as i32
);
}
dynasm!(
assembler
; jmp =>target
; after_call:
);
if caller_stack_offset != 0 {
dynasm!(
assembler
; add rsp, caller_stack_offset as i32
);
}
match returns.len() {
0 => {}
1 => {
Self::emit_push_from_ax(assembler, value_stack, returns[0])?;
}
_ => {
return Err(CodegenError {
message: "more than 1 function returns are not supported",
})
}
}
for reg in saved_regs.iter().rev() {
dynasm!(
assembler
; pop Rq(*reg as u8)
);
}
Ok(())
}
}
impl FunctionCodeGenerator for X64FunctionCode {
@ -666,6 +934,11 @@ impl FunctionCodeGenerator for X64FunctionCode {
Ok(())
}
/// Stack layout of a call frame:
/// - Return address
/// - Old RBP
/// - Params in reversed order, caller initialized
/// - Locals in reversed order, callee initialized
fn feed_param(&mut self, ty: WpType) -> Result<(), CodegenError> {
let assembler = self.assembler.as_mut().unwrap();
let size = get_size_of_type(&ty)?;
@ -676,35 +949,8 @@ impl FunctionCodeGenerator for X64FunctionCode {
stack_offset: self.current_stack_offset,
});
let param_reg = match self.num_params {
0 => Register::RDI,
1 => Register::RSI,
2 => Register::RDX,
3 => Register::RCX,
4 => Register::R8,
5 => Register::R9,
_ => {
return Err(CodegenError {
message: "more than 6 function parameters is not yet supported",
})
}
};
self.num_params += 1;
if is_dword(size) {
dynasm!(
assembler
; sub rsp, 4
; mov [rsp], Rd(param_reg as u8)
);
} else {
dynasm!(
assembler
; sub rsp, 8
; mov [rsp], Rq(param_reg as u8)
);
}
Ok(())
}
@ -1137,6 +1383,42 @@ impl FunctionCodeGenerator for X64FunctionCode {
Self::emit_return(assembler, &mut self.value_stack, &self.returns)?;
self.unreachable_depth = 1;
}
Operator::Call { function_index } => {
let function_index = function_index as usize;
let label = *self
.function_labels
.as_mut()
.unwrap()
.entry(function_index)
.or_insert_with(|| assembler.new_dynamic_label());
let sig_index = match self.function_signatures.get(FuncIndex::new(function_index)) {
Some(x) => *x,
None => {
return Err(CodegenError {
message: "signature not found",
})
}
};
let sig = match self.signatures.get(sig_index) {
Some(x) => x,
None => {
return Err(CodegenError {
message: "signature does not exist",
})
}
};
let param_types: Vec<WpType> =
sig.params().iter().cloned().map(type_to_wp_type).collect();
let return_types: Vec<WpType> =
sig.returns().iter().cloned().map(type_to_wp_type).collect();
Self::emit_call_raw(
assembler,
&mut self.value_stack,
label,
&param_types,
&return_types,
)?;
}
Operator::End => {
if self.control_stack.as_ref().unwrap().frames.len() == 1 {
let frame = self.control_stack.as_mut().unwrap().frames.pop().unwrap();
@ -1255,3 +1537,12 @@ fn value_to_i64(v: &Value) -> i64 {
Value::I64(x) => x as u64 as i64,
}
}
fn type_to_wp_type(ty: Type) -> WpType {
match ty {
Type::I32 => WpType::I32,
Type::I64 => WpType::I64,
Type::F32 => WpType::F32,
Type::F64 => WpType::F64,
}
}

View File

@ -6,6 +6,11 @@ extern crate dynasmrt;
#[macro_use]
extern crate dynasm;
#[macro_use]
extern crate lazy_static;
extern crate byteorder;
mod codegen;
mod codegen_x64;
mod parse;

View File

@ -89,6 +89,8 @@ pub fn read_module<
let ty = ty?;
info.signatures.push(Arc::new(func_type_to_func_sig(ty)?));
}
mcg.feed_signatures(info.signatures.clone())?;
}
SectionCode::Import => {
let import_reader = section.get_import_section_reader()?;
@ -148,6 +150,8 @@ pub fn read_module<
let sigindex = SigIndex::new(sigindex as usize);
info.func_assoc.push(sigindex);
}
mcg.feed_function_signatures(info.func_assoc.clone())?;
}
SectionCode::Table => {
let table_decl_reader = section.get_table_section_reader()?;