2019-02-12 00:52:17 +08:00
|
|
|
use super::codegen::*;
|
2019-02-20 22:56:32 +08:00
|
|
|
use super::stack::{ControlFrame, ControlStack, ValueInfo, ValueLocation, ValueStack};
|
2019-02-15 02:21:52 +08:00
|
|
|
use dynasmrt::{
|
|
|
|
x64::Assembler, AssemblyOffset, DynamicLabel, DynasmApi, DynasmLabelApi, ExecutableBuffer,
|
|
|
|
};
|
|
|
|
use wasmer_runtime_core::{
|
|
|
|
backend::{Backend, Compiler, FuncResolver, ProtectedCaller, Token, UserTrapper},
|
|
|
|
error::{CompileError, CompileResult, RuntimeError, RuntimeResult},
|
|
|
|
module::{ModuleInfo, ModuleInner, StringTable},
|
|
|
|
structures::{Map, TypedIndex},
|
|
|
|
types::{
|
|
|
|
FuncIndex, FuncSig, GlobalIndex, LocalFuncIndex, MemoryIndex, SigIndex, TableIndex, Type,
|
|
|
|
Value,
|
|
|
|
},
|
|
|
|
vm::{self, ImportBacking},
|
|
|
|
};
|
2019-02-12 00:52:17 +08:00
|
|
|
use wasmparser::{Operator, Type as WpType};
|
|
|
|
|
2019-02-13 20:04:10 +08:00
|
|
|
#[repr(u8)]
|
|
|
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
|
|
|
pub enum Register {
|
|
|
|
RAX,
|
|
|
|
RCX,
|
|
|
|
RDX,
|
|
|
|
RBX,
|
|
|
|
RSP,
|
|
|
|
RBP,
|
|
|
|
RSI,
|
|
|
|
RDI,
|
|
|
|
R8,
|
|
|
|
R9,
|
|
|
|
R10,
|
|
|
|
R11,
|
|
|
|
R12,
|
|
|
|
R13,
|
|
|
|
R14,
|
|
|
|
R15,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Register {
|
|
|
|
pub fn from_scratch_reg(id: u8) -> Register {
|
|
|
|
use self::Register::*;
|
|
|
|
match id {
|
|
|
|
0 => RDI,
|
|
|
|
1 => RSI,
|
|
|
|
2 => RDX,
|
|
|
|
3 => RCX,
|
|
|
|
4 => R8,
|
|
|
|
5 => R9,
|
|
|
|
6 => R10,
|
|
|
|
7 => R11,
|
|
|
|
_ => unreachable!(),
|
|
|
|
}
|
|
|
|
}
|
2019-02-19 20:25:09 +08:00
|
|
|
|
|
|
|
pub fn is_used(&self, stack: &ValueStack) -> bool {
|
|
|
|
use self::Register::*;
|
|
|
|
for val in &stack.values {
|
|
|
|
match val.location {
|
|
|
|
ValueLocation::Register(x) => {
|
|
|
|
if Register::from_scratch_reg(x) == *self {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ValueLocation::Stack => break,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
false
|
|
|
|
}
|
2019-02-13 20:04:10 +08:00
|
|
|
}
|
|
|
|
|
2019-02-12 00:52:17 +08:00
|
|
|
#[derive(Default)]
|
|
|
|
pub struct X64ModuleCodeGenerator {
|
|
|
|
functions: Vec<X64FunctionCode>,
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct X64FunctionCode {
|
2019-02-12 23:15:57 +08:00
|
|
|
id: usize,
|
|
|
|
begin_label: DynamicLabel,
|
2019-02-15 02:21:52 +08:00
|
|
|
begin_offset: AssemblyOffset,
|
2019-02-12 00:52:17 +08:00
|
|
|
assembler: Option<Assembler>,
|
2019-02-14 00:53:06 +08:00
|
|
|
returns: Vec<WpType>,
|
2019-02-12 00:52:17 +08:00
|
|
|
locals: Vec<Local>,
|
2019-02-12 23:15:57 +08:00
|
|
|
num_params: usize,
|
2019-02-12 00:52:17 +08:00
|
|
|
current_stack_offset: usize,
|
2019-02-12 23:15:57 +08:00
|
|
|
value_stack: ValueStack,
|
2019-02-20 22:56:32 +08:00
|
|
|
control_stack: Option<ControlStack>,
|
2019-02-21 21:14:10 +08:00
|
|
|
unreachable_depth: usize,
|
2019-02-12 00:52:17 +08:00
|
|
|
}
|
|
|
|
|
2019-02-15 02:21:52 +08:00
|
|
|
pub struct X64ExecutionContext {
|
|
|
|
code: ExecutableBuffer,
|
|
|
|
functions: Vec<X64FunctionCode>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ProtectedCaller for X64ExecutionContext {
|
|
|
|
fn call(
|
|
|
|
&self,
|
|
|
|
_module: &ModuleInner,
|
|
|
|
_func_index: FuncIndex,
|
|
|
|
_params: &[Value],
|
|
|
|
_import_backing: &ImportBacking,
|
|
|
|
_vmctx: *mut vm::Ctx,
|
|
|
|
_: Token,
|
|
|
|
) -> RuntimeResult<Vec<Value>> {
|
|
|
|
let index = _func_index.index();
|
|
|
|
let ptr = self.code.ptr(self.functions[index].begin_offset);
|
|
|
|
let return_ty = self.functions[index].returns.last().cloned();
|
|
|
|
|
|
|
|
if self.functions[index].num_params != _params.len() {
|
|
|
|
return Err(RuntimeError::User {
|
|
|
|
msg: "param count mismatch".into(),
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
match self.functions[index].num_params {
|
|
|
|
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!(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn get_early_trapper(&self) -> Box<dyn UserTrapper> {
|
|
|
|
pub struct Trapper;
|
|
|
|
|
|
|
|
impl UserTrapper for Trapper {
|
|
|
|
unsafe fn do_early_trap(&self, msg: String) -> ! {
|
|
|
|
panic!("{}", msg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Box::new(Trapper)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-12 23:15:57 +08:00
|
|
|
#[derive(Copy, Clone, Debug)]
|
2019-02-12 00:52:17 +08:00
|
|
|
struct Local {
|
|
|
|
ty: WpType,
|
|
|
|
stack_offset: usize,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl X64ModuleCodeGenerator {
|
|
|
|
pub fn new() -> X64ModuleCodeGenerator {
|
|
|
|
X64ModuleCodeGenerator::default()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-15 02:21:52 +08:00
|
|
|
impl ModuleCodeGenerator<X64FunctionCode, X64ExecutionContext> for X64ModuleCodeGenerator {
|
2019-02-12 00:52:17 +08:00
|
|
|
fn next_function(&mut self) -> Result<&mut X64FunctionCode, CodegenError> {
|
2019-02-12 23:15:57 +08:00
|
|
|
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 begin_label = assembler.new_dynamic_label();
|
2019-02-15 02:21:52 +08:00
|
|
|
let begin_offset = assembler.offset();
|
2019-02-12 23:15:57 +08:00
|
|
|
dynasm!(
|
|
|
|
assembler
|
|
|
|
; => begin_label
|
2019-02-13 20:04:10 +08:00
|
|
|
; push rbp
|
|
|
|
; mov rbp, rsp
|
2019-02-20 23:21:33 +08:00
|
|
|
; int 3
|
2019-02-12 23:15:57 +08:00
|
|
|
);
|
2019-02-12 00:52:17 +08:00
|
|
|
let code = X64FunctionCode {
|
2019-02-12 23:15:57 +08:00
|
|
|
id: self.functions.len(),
|
|
|
|
begin_label: begin_label,
|
2019-02-15 02:21:52 +08:00
|
|
|
begin_offset: begin_offset,
|
2019-02-12 23:15:57 +08:00
|
|
|
assembler: Some(assembler),
|
2019-02-14 00:53:06 +08:00
|
|
|
returns: vec![],
|
2019-02-12 00:52:17 +08:00
|
|
|
locals: vec![],
|
2019-02-12 23:15:57 +08:00
|
|
|
num_params: 0,
|
2019-02-12 00:52:17 +08:00
|
|
|
current_stack_offset: 0,
|
2019-02-19 19:19:40 +08:00
|
|
|
value_stack: ValueStack::new(4),
|
2019-02-20 22:56:32 +08:00
|
|
|
control_stack: None,
|
2019-02-21 21:14:10 +08:00
|
|
|
unreachable_depth: 0,
|
2019-02-12 00:52:17 +08:00
|
|
|
};
|
|
|
|
self.functions.push(code);
|
|
|
|
Ok(self.functions.last_mut().unwrap())
|
|
|
|
}
|
2019-02-14 00:53:06 +08:00
|
|
|
|
2019-02-15 02:21:52 +08:00
|
|
|
fn finalize(mut self) -> Result<X64ExecutionContext, CodegenError> {
|
2019-02-14 00:53:06 +08:00
|
|
|
let mut assembler = match self.functions.last_mut() {
|
|
|
|
Some(x) => x.assembler.take().unwrap(),
|
2019-02-15 02:21:52 +08:00
|
|
|
None => {
|
|
|
|
return Err(CodegenError {
|
|
|
|
message: "no function",
|
|
|
|
})
|
|
|
|
}
|
2019-02-14 00:53:06 +08:00
|
|
|
};
|
|
|
|
let output = assembler.finalize().unwrap();
|
2019-02-15 02:21:52 +08:00
|
|
|
Ok(X64ExecutionContext {
|
|
|
|
code: output,
|
|
|
|
functions: self.functions,
|
|
|
|
})
|
2019-02-14 00:53:06 +08:00
|
|
|
}
|
2019-02-12 00:52:17 +08:00
|
|
|
}
|
|
|
|
|
2019-02-13 20:04:10 +08:00
|
|
|
impl X64FunctionCode {
|
|
|
|
fn gen_rt_pop(assembler: &mut Assembler, info: &ValueInfo) -> Result<(), CodegenError> {
|
|
|
|
match info.location {
|
|
|
|
ValueLocation::Register(_) => {}
|
|
|
|
ValueLocation::Stack => {
|
|
|
|
let size = get_size_of_type(&info.ty)?;
|
|
|
|
dynasm!(
|
|
|
|
assembler
|
|
|
|
; add rsp, size as i32
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
2019-02-19 20:25:09 +08:00
|
|
|
|
|
|
|
/// Emits a binary operator.
|
|
|
|
///
|
|
|
|
/// Guarantees that the first Register parameter to callback `f` will never be `Register::RAX`.
|
|
|
|
fn emit_binop_i32<F: FnOnce(&mut Assembler, &ValueStack, Register, Register)>(
|
|
|
|
assembler: &mut Assembler,
|
|
|
|
value_stack: &mut ValueStack,
|
|
|
|
f: F,
|
|
|
|
) -> Result<(), CodegenError> {
|
|
|
|
let (a, b) = value_stack.pop2()?;
|
|
|
|
if a.ty != WpType::I32 || b.ty != WpType::I32 {
|
|
|
|
return Err(CodegenError {
|
|
|
|
message: "I32Add type mismatch",
|
|
|
|
});
|
|
|
|
}
|
|
|
|
value_stack.push(WpType::I32);
|
|
|
|
|
|
|
|
if a.location.is_register() && b.location.is_register() {
|
|
|
|
// output is in a_reg.
|
|
|
|
f(
|
|
|
|
assembler,
|
|
|
|
value_stack,
|
|
|
|
Register::from_scratch_reg(a.location.get_register()?),
|
|
|
|
Register::from_scratch_reg(b.location.get_register()?),
|
|
|
|
);
|
|
|
|
} else if a.location.is_register() {
|
|
|
|
dynasm!(
|
|
|
|
assembler
|
|
|
|
; mov eax, [rsp]
|
|
|
|
; add rsp, 4
|
|
|
|
);
|
|
|
|
f(
|
|
|
|
assembler,
|
|
|
|
value_stack,
|
|
|
|
Register::from_scratch_reg(a.location.get_register()?),
|
|
|
|
Register::RAX,
|
|
|
|
);
|
|
|
|
} else if b.location.is_register() {
|
|
|
|
unreachable!();
|
|
|
|
} else {
|
|
|
|
dynasm!(
|
|
|
|
assembler
|
|
|
|
; push rcx
|
|
|
|
; mov ecx, [rsp + 12]
|
|
|
|
; mov eax, [rsp + 8]
|
|
|
|
);
|
|
|
|
f(assembler, value_stack, Register::RCX, Register::RAX);
|
|
|
|
dynasm!(
|
|
|
|
assembler
|
|
|
|
; mov [rsp + 12], ecx
|
|
|
|
; pop rcx
|
|
|
|
; add rsp, 4
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn emit_div_i32(
|
|
|
|
assembler: &mut Assembler,
|
|
|
|
value_stack: &ValueStack,
|
|
|
|
left: Register,
|
|
|
|
right: Register,
|
|
|
|
signed: bool,
|
|
|
|
out: Register,
|
|
|
|
) {
|
|
|
|
let dx_used = Register::RDX.is_used(value_stack);
|
|
|
|
if dx_used {
|
|
|
|
dynasm!(
|
|
|
|
assembler
|
|
|
|
; push rdx
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
if right == Register::RAX {
|
|
|
|
dynasm!(
|
|
|
|
assembler
|
|
|
|
; push rax
|
|
|
|
; mov eax, Rd(left as u8)
|
|
|
|
; mov edx, 0
|
|
|
|
; mov Rd(left as u8), [rsp]
|
|
|
|
);
|
|
|
|
|
|
|
|
if signed {
|
|
|
|
dynasm!(
|
|
|
|
assembler
|
|
|
|
; idiv Rd(left as u8)
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
dynasm!(
|
|
|
|
assembler
|
|
|
|
; div Rd(left as u8)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
dynasm!(
|
|
|
|
assembler
|
|
|
|
; mov Rd(left as u8), Rd(out as u8)
|
|
|
|
; pop rax
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
dynasm!(
|
|
|
|
assembler
|
|
|
|
; mov eax, Rd(left as u8)
|
|
|
|
; mov edx, 0
|
|
|
|
);
|
|
|
|
if signed {
|
|
|
|
dynasm!(
|
|
|
|
assembler
|
|
|
|
; idiv Rd(right as u8)
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
dynasm!(
|
|
|
|
assembler
|
|
|
|
; div Rd(right as u8)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
dynasm!(
|
|
|
|
assembler
|
|
|
|
; mov Rd(left as u8), Rd(out as u8)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
if dx_used {
|
|
|
|
dynasm!(
|
|
|
|
assembler
|
|
|
|
; pop rdx
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
2019-02-20 22:56:32 +08:00
|
|
|
|
2019-02-20 23:21:33 +08:00
|
|
|
fn emit_pop_into_ax(
|
|
|
|
assembler: &mut Assembler,
|
|
|
|
value_stack: &mut ValueStack,
|
|
|
|
) -> Result<(), CodegenError> {
|
|
|
|
let val = value_stack.pop()?;
|
|
|
|
match val.location {
|
|
|
|
ValueLocation::Register(x) => {
|
|
|
|
let reg = Register::from_scratch_reg(x);
|
|
|
|
dynasm!(
|
|
|
|
assembler
|
|
|
|
; mov rax, Rq(reg as u8)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
ValueLocation::Stack => {
|
|
|
|
if is_dword(get_size_of_type(&val.ty)?) {
|
|
|
|
dynasm!(
|
|
|
|
assembler
|
|
|
|
; mov eax, [rsp]
|
|
|
|
; add rsp, 4
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
dynasm!(
|
|
|
|
assembler
|
|
|
|
; mov rax, [rsp]
|
|
|
|
; add rsp, 8
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2019-02-20 22:56:32 +08:00
|
|
|
fn emit_leave_frame(
|
|
|
|
assembler: &mut Assembler,
|
|
|
|
frame: &ControlFrame,
|
|
|
|
value_stack: &mut ValueStack,
|
|
|
|
) -> Result<(), CodegenError> {
|
|
|
|
let ret_ty = match frame.returns.len() {
|
|
|
|
1 => Some(frame.returns[0]),
|
|
|
|
0 => None,
|
|
|
|
_ => {
|
|
|
|
return Err(CodegenError {
|
|
|
|
message: "more than one block returns are not yet supported",
|
|
|
|
})
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
if ret_ty.is_some() && frame.loop_like {
|
|
|
|
return Err(CodegenError {
|
|
|
|
message: "return value is not supported for loops",
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
if value_stack.values.len() < frame.value_stack_depth_before + frame.returns.len() {
|
|
|
|
return Err(CodegenError {
|
|
|
|
message: "value stack underflow",
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(ty) = ret_ty {
|
2019-02-20 23:21:33 +08:00
|
|
|
if value_stack.values.iter().last().map(|x| x.ty) != ret_ty {
|
|
|
|
return Err(CodegenError {
|
|
|
|
message: "value type != return type",
|
|
|
|
});
|
2019-02-20 22:56:32 +08:00
|
|
|
}
|
2019-02-20 23:21:33 +08:00
|
|
|
Self::emit_pop_into_ax(assembler, value_stack)?;
|
2019-02-20 22:56:32 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn emit_block_end(
|
|
|
|
assembler: &mut Assembler,
|
|
|
|
control_stack: &mut ControlStack,
|
|
|
|
value_stack: &mut ValueStack,
|
|
|
|
) -> Result<(), CodegenError> {
|
|
|
|
let frame = match control_stack.frames.pop() {
|
|
|
|
Some(x) => x,
|
|
|
|
None => {
|
|
|
|
return Err(CodegenError {
|
|
|
|
message: "no frame",
|
|
|
|
})
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
Self::emit_leave_frame(assembler, &frame, value_stack)?;
|
|
|
|
|
|
|
|
if value_stack.values.len() != frame.value_stack_depth_before {
|
|
|
|
return Err(CodegenError {
|
|
|
|
message: "value_stack.values.len() != frame.value_stack_depth_before",
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
if !frame.loop_like {
|
|
|
|
dynasm!(
|
|
|
|
assembler
|
|
|
|
; => frame.label
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
if frame.returns.len() == 1 {
|
|
|
|
let loc = value_stack.push(frame.returns[0]);
|
|
|
|
match loc {
|
|
|
|
ValueLocation::Register(x) => {
|
|
|
|
let reg = Register::from_scratch_reg(x);
|
|
|
|
dynasm!(
|
|
|
|
assembler
|
|
|
|
; mov Rq(x as u8), rax
|
|
|
|
);
|
|
|
|
}
|
|
|
|
ValueLocation::Stack => {
|
|
|
|
if is_dword(get_size_of_type(&frame.returns[0])?) {
|
|
|
|
dynasm!(
|
|
|
|
assembler
|
|
|
|
; sub rsp, 4
|
|
|
|
; mov [rsp], eax
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
dynasm!(
|
|
|
|
assembler
|
|
|
|
; sub rsp, 8
|
|
|
|
; mov [rsp], rax
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn emit_jmp(
|
|
|
|
assembler: &mut Assembler,
|
|
|
|
control_stack: &ControlStack,
|
|
|
|
value_stack: &mut ValueStack,
|
|
|
|
relative_frame_offset: usize,
|
|
|
|
) -> Result<(), CodegenError> {
|
|
|
|
let frame = if relative_frame_offset >= control_stack.frames.len() {
|
|
|
|
return Err(CodegenError {
|
|
|
|
message: "jmp offset out of bounds",
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
&control_stack.frames[control_stack.frames.len() - 1 - relative_frame_offset]
|
|
|
|
};
|
|
|
|
|
|
|
|
Self::emit_leave_frame(assembler, frame, value_stack)?;
|
|
|
|
|
|
|
|
let mut sp_diff: usize = 0;
|
|
|
|
|
|
|
|
for i in 0..value_stack.values.len() - frame.value_stack_depth_before {
|
|
|
|
let vi = value_stack.values[value_stack.values.len() - 1 - i];
|
|
|
|
if vi.location == ValueLocation::Stack {
|
|
|
|
sp_diff += get_size_of_type(&vi.ty)?;
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
dynasm!(
|
|
|
|
assembler
|
|
|
|
; add rsp, sp_diff as i32
|
|
|
|
; jmp =>frame.label
|
|
|
|
);
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
2019-02-21 21:14:10 +08:00
|
|
|
|
|
|
|
fn emit_return(
|
|
|
|
assembler: &mut Assembler,
|
|
|
|
value_stack: &mut ValueStack,
|
|
|
|
returns: &Vec<WpType>,
|
|
|
|
) -> Result<(), CodegenError> {
|
|
|
|
match returns.len() {
|
|
|
|
0 => {}
|
|
|
|
1 => {
|
|
|
|
if value_stack.values.iter().last().map(|x| x.ty) != Some(returns[0]) {
|
|
|
|
return Err(CodegenError {
|
|
|
|
message: "self.value_stack.last().cloned() != Some(self.returns[0])",
|
|
|
|
});
|
|
|
|
}
|
|
|
|
Self::emit_pop_into_ax(assembler, value_stack)?;
|
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
return Err(CodegenError {
|
|
|
|
message: "multiple return values is not yet supported",
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
dynasm!(
|
|
|
|
assembler
|
|
|
|
; mov rsp, rbp
|
|
|
|
; pop rbp
|
|
|
|
; ret
|
|
|
|
);
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
2019-02-13 20:04:10 +08:00
|
|
|
}
|
|
|
|
|
2019-02-12 00:52:17 +08:00
|
|
|
impl FunctionCodeGenerator for X64FunctionCode {
|
2019-02-14 00:53:06 +08:00
|
|
|
fn feed_return(&mut self, ty: WpType) -> Result<(), CodegenError> {
|
|
|
|
self.returns.push(ty);
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2019-02-12 00:52:17 +08:00
|
|
|
fn feed_param(&mut self, ty: WpType) -> Result<(), CodegenError> {
|
2019-02-13 20:04:10 +08:00
|
|
|
let assembler = self.assembler.as_mut().unwrap();
|
2019-02-12 00:52:17 +08:00
|
|
|
let size = get_size_of_type(&ty)?;
|
2019-02-13 20:04:10 +08:00
|
|
|
|
2019-02-12 23:15:57 +08:00
|
|
|
self.current_stack_offset += size;
|
2019-02-12 00:52:17 +08:00
|
|
|
self.locals.push(Local {
|
|
|
|
ty: ty,
|
|
|
|
stack_offset: self.current_stack_offset,
|
|
|
|
});
|
2019-02-13 20:04:10 +08:00
|
|
|
|
|
|
|
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",
|
|
|
|
})
|
|
|
|
}
|
|
|
|
};
|
2019-02-12 23:15:57 +08:00
|
|
|
self.num_params += 1;
|
2019-02-13 20:04:10 +08:00
|
|
|
|
|
|
|
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)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2019-02-12 00:52:17 +08:00
|
|
|
Ok(())
|
|
|
|
}
|
2019-02-13 20:04:10 +08:00
|
|
|
|
2019-02-12 00:52:17 +08:00
|
|
|
fn feed_local(&mut self, ty: WpType, n: usize) -> Result<(), CodegenError> {
|
2019-02-13 20:04:10 +08:00
|
|
|
let assembler = self.assembler.as_mut().unwrap();
|
2019-02-12 00:52:17 +08:00
|
|
|
let size = get_size_of_type(&ty)?;
|
|
|
|
for _ in 0..n {
|
|
|
|
// FIXME: check range of n
|
2019-02-12 23:15:57 +08:00
|
|
|
self.current_stack_offset += size;
|
2019-02-12 00:52:17 +08:00
|
|
|
self.locals.push(Local {
|
|
|
|
ty: ty,
|
|
|
|
stack_offset: self.current_stack_offset,
|
|
|
|
});
|
2019-02-13 20:04:10 +08:00
|
|
|
match size {
|
|
|
|
4 => dynasm!(
|
2019-02-12 23:15:57 +08:00
|
|
|
assembler
|
2019-02-13 20:04:10 +08:00
|
|
|
; sub rsp, 4
|
|
|
|
; mov DWORD [rsp], 0
|
|
|
|
),
|
|
|
|
8 => dynasm!(
|
2019-02-12 23:15:57 +08:00
|
|
|
assembler
|
2019-02-13 20:04:10 +08:00
|
|
|
; sub rsp, 8
|
|
|
|
; mov QWORD [rsp], 0
|
|
|
|
),
|
|
|
|
_ => unreachable!(),
|
2019-02-12 23:15:57 +08:00
|
|
|
}
|
2019-02-12 00:52:17 +08:00
|
|
|
}
|
2019-02-13 20:04:10 +08:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
fn begin_body(&mut self) -> Result<(), CodegenError> {
|
2019-02-20 22:56:32 +08:00
|
|
|
self.control_stack = Some(ControlStack::new(
|
|
|
|
self.assembler.as_mut().unwrap().new_dynamic_label(),
|
|
|
|
self.returns.clone(),
|
|
|
|
));
|
2019-02-12 00:52:17 +08:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
fn feed_opcode(&mut self, op: Operator) -> Result<(), CodegenError> {
|
2019-02-21 21:14:10 +08:00
|
|
|
let was_unreachable;
|
|
|
|
|
|
|
|
if self.unreachable_depth > 0 {
|
|
|
|
was_unreachable = true;
|
|
|
|
match op {
|
|
|
|
Operator::Block { .. } | Operator::Loop { .. } | Operator::If { .. } => {
|
|
|
|
self.unreachable_depth += 1;
|
|
|
|
}
|
|
|
|
Operator::End => {
|
|
|
|
self.unreachable_depth -= 1;
|
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
if self.unreachable_depth > 0 {
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
was_unreachable = false;
|
|
|
|
}
|
|
|
|
|
2019-02-12 23:15:57 +08:00
|
|
|
let assembler = self.assembler.as_mut().unwrap();
|
2019-02-21 21:14:10 +08:00
|
|
|
|
2019-02-12 23:15:57 +08:00
|
|
|
match op {
|
|
|
|
Operator::GetLocal { local_index } => {
|
|
|
|
let local_index = local_index as usize;
|
|
|
|
if local_index >= self.locals.len() {
|
|
|
|
return Err(CodegenError {
|
|
|
|
message: "local out of bounds",
|
|
|
|
});
|
|
|
|
}
|
|
|
|
let local = self.locals[local_index];
|
2019-02-13 20:04:10 +08:00
|
|
|
let location = self.value_stack.push(local.ty);
|
|
|
|
let size = get_size_of_type(&local.ty)?;
|
|
|
|
|
|
|
|
match location {
|
|
|
|
ValueLocation::Register(id) => {
|
|
|
|
if is_dword(size) {
|
|
|
|
dynasm!(
|
|
|
|
assembler
|
|
|
|
; mov Rd(Register::from_scratch_reg(id) as u8), [rbp - (local.stack_offset as i32)]
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
dynasm!(
|
|
|
|
assembler
|
|
|
|
; mov Rq(Register::from_scratch_reg(id) as u8), [rbp - (local.stack_offset as i32)]
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ValueLocation::Stack => {
|
|
|
|
if is_dword(size) {
|
|
|
|
dynasm!(
|
|
|
|
assembler
|
|
|
|
; mov eax, [rbp - (local.stack_offset as i32)]
|
|
|
|
; sub rsp, 4
|
|
|
|
; mov [rsp], eax
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
dynasm!(
|
|
|
|
assembler
|
|
|
|
; mov rax, [rbp - (local.stack_offset as i32)]
|
|
|
|
; sub rsp, 8
|
|
|
|
; mov [rsp], rax
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Operator::I32Add => {
|
2019-02-19 20:25:09 +08:00
|
|
|
Self::emit_binop_i32(
|
|
|
|
assembler,
|
|
|
|
&mut self.value_stack,
|
|
|
|
|assembler, value_stack, left, right| {
|
|
|
|
dynasm!(
|
|
|
|
assembler
|
|
|
|
; add Rd(left as u8), Rd(right as u8)
|
|
|
|
)
|
|
|
|
},
|
|
|
|
)?;
|
|
|
|
}
|
|
|
|
Operator::I32Sub => {
|
|
|
|
Self::emit_binop_i32(
|
|
|
|
assembler,
|
|
|
|
&mut self.value_stack,
|
|
|
|
|assembler, value_stack, left, right| {
|
|
|
|
dynasm!(
|
|
|
|
assembler
|
|
|
|
; sub Rd(left as u8), Rd(right as u8)
|
|
|
|
)
|
|
|
|
},
|
|
|
|
)?;
|
|
|
|
}
|
|
|
|
Operator::I32Mul => {
|
|
|
|
Self::emit_binop_i32(
|
|
|
|
assembler,
|
|
|
|
&mut self.value_stack,
|
|
|
|
|assembler, value_stack, left, right| {
|
|
|
|
dynasm!(
|
|
|
|
assembler
|
|
|
|
; imul Rd(left as u8), Rd(right as u8)
|
|
|
|
)
|
|
|
|
},
|
|
|
|
)?;
|
|
|
|
}
|
|
|
|
Operator::I32DivU => {
|
|
|
|
Self::emit_binop_i32(
|
|
|
|
assembler,
|
|
|
|
&mut self.value_stack,
|
|
|
|
|assembler, value_stack, left, right| {
|
|
|
|
Self::emit_div_i32(
|
|
|
|
assembler,
|
|
|
|
value_stack,
|
|
|
|
left,
|
|
|
|
right,
|
|
|
|
false,
|
|
|
|
Register::RAX,
|
|
|
|
);
|
|
|
|
},
|
|
|
|
)?;
|
|
|
|
}
|
|
|
|
Operator::I32DivS => {
|
|
|
|
Self::emit_binop_i32(
|
|
|
|
assembler,
|
|
|
|
&mut self.value_stack,
|
|
|
|
|assembler, value_stack, left, right| {
|
|
|
|
Self::emit_div_i32(
|
|
|
|
assembler,
|
|
|
|
value_stack,
|
|
|
|
left,
|
|
|
|
right,
|
|
|
|
true,
|
|
|
|
Register::RAX,
|
|
|
|
);
|
|
|
|
},
|
|
|
|
)?;
|
|
|
|
}
|
|
|
|
Operator::I32RemU => {
|
|
|
|
Self::emit_binop_i32(
|
|
|
|
assembler,
|
|
|
|
&mut self.value_stack,
|
|
|
|
|assembler, value_stack, left, right| {
|
|
|
|
Self::emit_div_i32(
|
|
|
|
assembler,
|
|
|
|
value_stack,
|
|
|
|
left,
|
|
|
|
right,
|
|
|
|
false,
|
|
|
|
Register::RDX,
|
|
|
|
);
|
|
|
|
},
|
|
|
|
)?;
|
|
|
|
}
|
|
|
|
Operator::I32RemS => {
|
|
|
|
Self::emit_binop_i32(
|
|
|
|
assembler,
|
|
|
|
&mut self.value_stack,
|
|
|
|
|assembler, value_stack, left, right| {
|
|
|
|
Self::emit_div_i32(
|
|
|
|
assembler,
|
|
|
|
value_stack,
|
|
|
|
left,
|
|
|
|
right,
|
|
|
|
true,
|
|
|
|
Register::RDX,
|
|
|
|
);
|
|
|
|
},
|
|
|
|
)?;
|
2019-02-13 20:04:10 +08:00
|
|
|
}
|
2019-02-20 22:56:32 +08:00
|
|
|
Operator::Block { ty } => {}
|
2019-02-13 20:04:10 +08:00
|
|
|
Operator::Drop => {
|
|
|
|
let info = self.value_stack.pop()?;
|
|
|
|
Self::gen_rt_pop(assembler, &info)?;
|
2019-02-12 23:15:57 +08:00
|
|
|
}
|
2019-02-21 21:14:10 +08:00
|
|
|
Operator::Return => {
|
|
|
|
Self::emit_return(assembler, &mut self.value_stack, &self.returns)?;
|
|
|
|
self.unreachable_depth = 1;
|
|
|
|
}
|
|
|
|
Operator::End => {
|
|
|
|
if self.control_stack.as_ref().unwrap().frames.len() == 1 {
|
|
|
|
let frame = self.control_stack.as_mut().unwrap().frames.pop().unwrap();
|
|
|
|
Self::emit_leave_frame(assembler, &frame, &mut self.value_stack)?;
|
2019-02-20 23:21:33 +08:00
|
|
|
dynasm!(
|
|
|
|
assembler
|
2019-02-21 21:14:10 +08:00
|
|
|
; =>frame.label
|
2019-02-20 23:21:33 +08:00
|
|
|
);
|
2019-02-21 21:14:10 +08:00
|
|
|
} else {
|
|
|
|
Self::emit_block_end(
|
|
|
|
assembler,
|
|
|
|
self.control_stack.as_mut().unwrap(),
|
|
|
|
&mut self.value_stack,
|
|
|
|
)?;
|
2019-02-14 00:53:06 +08:00
|
|
|
}
|
|
|
|
}
|
2019-02-20 23:21:33 +08:00
|
|
|
Operator::Br { relative_depth } => {
|
|
|
|
Self::emit_jmp(
|
|
|
|
assembler,
|
|
|
|
self.control_stack.as_ref().unwrap(),
|
|
|
|
&mut self.value_stack,
|
|
|
|
relative_depth as usize,
|
|
|
|
)?;
|
2019-02-21 21:14:10 +08:00
|
|
|
self.unreachable_depth = 1;
|
2019-02-20 23:21:33 +08:00
|
|
|
}
|
2019-02-12 23:15:57 +08:00
|
|
|
_ => unimplemented!(),
|
|
|
|
}
|
2019-02-12 00:52:17 +08:00
|
|
|
Ok(())
|
|
|
|
}
|
2019-02-20 22:56:32 +08:00
|
|
|
|
2019-02-12 00:52:17 +08:00
|
|
|
fn finalize(&mut self) -> Result<(), CodegenError> {
|
2019-02-12 23:15:57 +08:00
|
|
|
let assembler = self.assembler.as_mut().unwrap();
|
2019-02-20 22:56:32 +08:00
|
|
|
|
2019-02-12 23:15:57 +08:00
|
|
|
dynasm!(
|
|
|
|
assembler
|
2019-02-21 21:14:10 +08:00
|
|
|
; mov rsp, rbp
|
|
|
|
; pop rbp
|
|
|
|
; ret
|
2019-02-20 22:56:32 +08:00
|
|
|
);
|
|
|
|
|
2019-02-20 23:21:33 +08:00
|
|
|
if self.value_stack.values.len() != 0
|
|
|
|
|| self.control_stack.as_ref().unwrap().frames.len() != 0
|
|
|
|
{
|
|
|
|
return Err(CodegenError {
|
|
|
|
message: "control/value stack not empty at end of function",
|
|
|
|
});
|
2019-02-20 22:56:32 +08:00
|
|
|
}
|
|
|
|
|
2019-02-12 00:52:17 +08:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn get_size_of_type(ty: &WpType) -> Result<usize, CodegenError> {
|
|
|
|
match *ty {
|
|
|
|
WpType::I32 | WpType::F32 => Ok(4),
|
|
|
|
WpType::I64 | WpType::F64 => Ok(8),
|
|
|
|
_ => Err(CodegenError {
|
|
|
|
message: "unknown type",
|
|
|
|
}),
|
|
|
|
}
|
|
|
|
}
|
2019-02-13 20:04:10 +08:00
|
|
|
|
|
|
|
fn is_dword(n: usize) -> bool {
|
|
|
|
n == 4
|
|
|
|
}
|
2019-02-15 02:21:52 +08:00
|
|
|
|
|
|
|
fn value_to_i64(v: &Value) -> i64 {
|
|
|
|
match *v {
|
|
|
|
Value::F32(x) => x.to_bits() as u64 as i64,
|
|
|
|
Value::F64(x) => x.to_bits() as u64 as i64,
|
|
|
|
Value::I32(x) => x as u64 as i64,
|
|
|
|
Value::I64(x) => x as u64 as i64,
|
|
|
|
}
|
|
|
|
}
|