Support a proper vmctx that contains more than just memory offset

This commit is contained in:
Jef
2019-01-16 16:34:24 +01:00
parent d7434fe5d2
commit a7fa7da7d4
7 changed files with 375 additions and 101 deletions

View File

@@ -9,6 +9,8 @@ use dynasmrt::{AssemblyOffset, DynamicLabel, DynasmApi, DynasmLabelApi, Executab
use error::Error;
use std::{iter, mem};
use module::VmCtx;
/// Size of a pointer on the target in bytes.
const WORD_SIZE: u32 = 8;
@@ -770,15 +772,16 @@ macro_rules! load {
vmctx: GPR,
(offset, runtime_offset): (i32, Result<i32, GPR>)
) {
let vmctx_mem_offset = VmCtx::offset_of_memory() as i32;
match runtime_offset {
Ok(imm) => {
dynasm!(ctx.asm
; mov $reg_ty(dst), [Rq(vmctx) + offset + imm]
; mov $reg_ty(dst), [Rq(vmctx) + offset + imm + vmctx_mem_offset]
);
}
Err(offset_reg) => {
dynasm!(ctx.asm
; mov $reg_ty(dst), [Rq(vmctx) + Rq(offset_reg) + offset]
; mov $reg_ty(dst), [Rq(vmctx) + Rq(offset_reg) + offset + vmctx_mem_offset]
);
}
}
@@ -854,15 +857,16 @@ macro_rules! store {
vmctx: GPR,
(offset, runtime_offset): (i32, Result<i32, GPR>)
) {
let vmctx_mem_offset = VmCtx::offset_of_memory() as i32;
match runtime_offset {
Ok(imm) => {
dynasm!(ctx.asm
; mov [Rq(vmctx) + offset + imm], $reg_ty(src)
; mov [Rq(vmctx) + offset + imm + vmctx_mem_offset], $reg_ty(src)
);
}
Err(offset_reg) => {
dynasm!(ctx.asm
; mov [Rq(vmctx) + Rq(offset_reg) + offset], $reg_ty(src)
; mov [Rq(vmctx) + Rq(offset_reg) + offset + vmctx_mem_offset], $reg_ty(src)
);
}
}
@@ -1003,6 +1007,16 @@ impl Context<'_> {
cmp_i64!(i64_gt_s, setg, setnge, |a, b| a > b);
cmp_i64!(i64_ge_s, setge, setng, |a, b| a >= b);
pub fn i32_eqz(&mut self) {
self.push(Value::Immediate(0));
self.i32_eq();
}
pub fn i64_eqz(&mut self) {
self.push(Value::Immediate(0));
self.i64_eq();
}
/// Pops i32 predicate and branches to the specified label
/// if the predicate is equal to zero.
pub fn jump_if_false(&mut self, label: Label) {
@@ -1297,7 +1311,7 @@ impl Context<'_> {
Value::Local(loc) => StackValue::Local(loc),
Value::Immediate(i) => StackValue::Immediate(i),
Value::Temp(gpr) => {
if self.block_state.regs.free_scratch() >= 1 {
if self.block_state.regs.free_scratch() >= 2 {
StackValue::Temp(gpr)
} else {
self.block_state.depth.reserve(1);
@@ -1890,12 +1904,14 @@ impl Context<'_> {
fn save_volatile(&mut self) -> ArrayVec<[GPR; SCRATCH_REGS.len()]> {
let mut out = ArrayVec::new();
// TODO: If there are no `StackValue::Pop`s that need to be popped
// before we reach our `Temp` value, we can set the `StackValue`
// for the register to be restored to `StackValue::Pop` (and
// release the register!) instead of restoring it.
for &reg in SCRATCH_REGS.iter() {
if !self.block_state.regs.is_free(reg) {
if self
.block_state
.stack
.iter()
.filter_map(|v| v.location(&self.block_state.locals))
.any(|p| p == ValueLocation::Reg(reg))
{
dynasm!(self.asm
; push Rq(reg)
);
@@ -1995,6 +2011,59 @@ impl Context<'_> {
self.push(Value::Temp(RAX));
}
pub fn call_indirect(
&mut self,
valid_indexes: impl IntoIterator<Item = u32>,
arg_arity: u32,
return_arity: u32,
) {
debug_assert!(
return_arity == 0 || return_arity == 1,
"We don't support multiple return yet"
);
let callee = self.pop();
let (callee, callee_needs_release) = self.into_reg(callee);
let vmctx = StackValue::Local(self.block_state.locals.vmctx_index());
let count = self.block_state.stack.len();
let label = self.create_label();
let index_reg = self.block_state.regs.take_scratch_gpr();
// TODO: Generate faster check using bitsets like GCC does?
for index in valid_indexes {
dynasm!(self.asm
; lea Rq(index_reg), [=>self.func_starts[index as usize].1]
; cmp Rd(callee), index as i32
; je =>label.0
);
}
self.trap();
self.define_label(label);
if callee_needs_release {
self.block_state.regs.release_scratch_gpr(callee);
}
// TODO: I believe that this can't cause quadratic runtime but I'm not
// certain.
self.block_state
.stack
.insert(count - arg_arity as usize, vmctx);
let cleanup = self.pass_outgoing_args(arg_arity + 1, return_arity, true);
dynasm!(self.asm
; call Rq(index_reg)
);
self.block_state.regs.release_scratch_gpr(index_reg);
self.post_call_cleanup(cleanup);
self.push_function_return(return_arity);
}
/// Call a function with the given index
pub fn call_direct(&mut self, index: u32, arg_arity: u32, return_arity: u32) {
debug_assert!(