From d06be92a4e7cb5502833c3ef4ad42d830b6459dd Mon Sep 17 00:00:00 2001 From: Jef Date: Thu, 17 Jan 2019 14:34:34 +0100 Subject: [PATCH] Don't allocate context if it's unused --- src/backend.rs | 60 ++++++++++++++++++++++++++++++++++++++++++++------ src/module.rs | 27 +++++++++++++++-------- src/tests.rs | 13 +++++++---- 3 files changed, 80 insertions(+), 20 deletions(-) diff --git a/src/backend.rs b/src/backend.rs index 2da6ce455e..325502d1d4 100644 --- a/src/backend.rs +++ b/src/backend.rs @@ -1036,14 +1036,55 @@ impl Context<'_> { cmp_i64!(i64_gt_s, setg, setnge, |a, b| a > b); cmp_i64!(i64_ge_s, setge, setng, |a, b| a >= b); + // TODO: Should we do this logic in `eq` and just have this delegate to `eq`? + // That would mean that `eqz` and `eq` with a const 0 argument don't + // result in different code. pub fn i32_eqz(&mut self) { - self.push(Value::Immediate(0)); - self.i32_eq(); + let val = self.pop(); + + if let Value::Immediate(i) = val { + self.push(Value::Immediate(if i == 0 { 1 } else { 0 })); + return; + } + + let (reg, needs_release) = self.into_reg(val); + let out = self.block_state.regs.take_scratch_gpr(); + + dynasm!(self.asm + ; xor Rd(out), Rd(out) + ; test Rd(reg), Rd(reg) + ; setz Rb(out) + ); + + if needs_release { + self.block_state.regs.release_scratch_gpr(reg); + } + + self.push(Value::Temp(out)); } pub fn i64_eqz(&mut self) { - self.push(Value::Immediate(0)); - self.i64_eq(); + let val = self.pop(); + + if let Value::Immediate(i) = val { + self.push(Value::Immediate(if i == 0 { 1 } else { 0 })); + return; + } + + let (reg, needs_release) = self.into_reg(val); + let out = self.block_state.regs.take_scratch_gpr(); + + dynasm!(self.asm + ; xor Rd(out), Rd(out) + ; test Rq(reg), Rq(reg) + ; setz Rb(out) + ); + + if needs_release { + self.block_state.regs.release_scratch_gpr(reg); + } + + self.push(Value::Temp(out)); } /// Pops i32 predicate and branches to the specified label @@ -2133,7 +2174,8 @@ impl Context<'_> { pub fn start_function(&mut self, arguments: u32, locals: u32) -> FunctionEnd { // To support `vmctx` let arguments = arguments + 1; - let (reg_args, locals_in_gprs) = ARGS_IN_GPRS.split_at((arguments as usize).min(ARGS_IN_GPRS.len())); + let (reg_args, locals_in_gprs) = + ARGS_IN_GPRS.split_at((arguments as usize).min(ARGS_IN_GPRS.len())); let reg_locals = &locals_in_gprs[..(locals as usize).min(locals_in_gprs.len())]; // We need space to store the register arguments if we need to call a function @@ -2144,8 +2186,12 @@ impl Context<'_> { let aligned_stack_slots = (stack_slots + 1) & !1; let frame_size: i32 = aligned_stack_slots as i32 * WORD_SIZE as i32; - self.block_state.locals.register_locals = - reg_args.iter().chain(reg_locals).cloned().map(ArgLoc::Register).collect(); + self.block_state.locals.register_locals = reg_args + .iter() + .chain(reg_locals) + .cloned() + .map(ArgLoc::Register) + .collect(); self.block_state.locals.num_stack_args = arguments.saturating_sub(ARGS_IN_GPRS.len() as _); self.block_state.locals.num_local_stack_slots = stack_slots; self.block_state.return_register = Some(RAX); diff --git a/src/module.rs b/src/module.rs index 24cc79bdb1..8702eaef58 100644 --- a/src/module.rs +++ b/src/module.rs @@ -135,18 +135,24 @@ impl TranslatedModule { .extend(Layout::array::(mem_size * WASM_PAGE_SIZE).unwrap()) .unwrap(); - let ptr = unsafe { alloc::alloc_zeroed(layout) } as *mut VmCtx; + let ctx = if mem_size > 0 || slice.len > 0 { + let ptr = unsafe { alloc::alloc_zeroed(layout) } as *mut VmCtx; - unsafe { - *ptr = VmCtx { - table: slice, - mem_size, + unsafe { + *ptr = VmCtx { + table: slice, + mem_size, + } } - } + + Some(Allocation { ptr, layout }) + } else { + None + }; ExecutableModule { module: self, - context: Allocation { ptr, layout }, + context: ctx, } } @@ -184,7 +190,7 @@ pub enum ExecutionError { pub struct ExecutableModule { module: TranslatedModule, - context: Allocation, + context: Option>, } impl ExecutableModule { @@ -216,7 +222,10 @@ impl ExecutableModule { Ok(unsafe { args.call( Args::into_func(start_buf), - self.context.ptr as *const VmCtx as *const u8, + self.context + .as_ref() + .map(|ctx| ctx.ptr as *const VmCtx as *const u8) + .unwrap_or(std::ptr::null()), ) }) } diff --git a/src/tests.rs b/src/tests.rs index 7931bc01bf..7f28b791ab 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -116,6 +116,7 @@ mod op32 { unop_test!(clz, u32::leading_zeros); unop_test!(ctz, u32::trailing_zeros); unop_test!(popcnt, u32::count_ones); + unop_test!(eqz, |a: u32| if a == 0 { 1 } else { 0 }); binop_test!(add, i32::wrapping_add); binop_test!(sub, i32::wrapping_sub); @@ -194,31 +195,34 @@ mod op64 { macro_rules! unop_test { ($name:ident, $func:expr) => { + unop_test!($name, $func, i64); + }; + ($name:ident, $func:expr, $out_ty:ty) => { mod $name { use super::{translate_wat, ExecutableModule}; use std::sync::Once; lazy_static! { static ref AS_PARAM: ExecutableModule = translate_wat( - concat!("(module (func (param i64) (result i64) + concat!("(module (func (param i64) (result ",stringify!($out_ty),") (i64.",stringify!($name)," (get_local 0))))"), ); } quickcheck! { fn as_param(a: u64) -> bool { - AS_PARAM.execute_func::<(u64,), u64>(0, (a,)) == Ok($func(a)) + AS_PARAM.execute_func::<(u64,), $out_ty>(0, (a,)) == Ok($func(a)) } fn lit(a: u64) -> bool { let translated = translate_wat(&format!(concat!(" - (module (func (result i64) + (module (func (result ",stringify!($out_ty),") (i64.",stringify!($name)," (i64.const {val})))) "), val = a)); static ONCE: Once = Once::new(); ONCE.call_once(|| translated.disassemble()); - translated.execute_func::<(), u64>(0, ()) == Ok($func(a)) + translated.execute_func::<(), $out_ty>(0, ()) == Ok($func(a)) } } } @@ -228,6 +232,7 @@ mod op64 { unop_test!(clz, |a: u64| a.leading_zeros() as _); unop_test!(ctz, |a: u64| a.trailing_zeros() as _); unop_test!(popcnt, |a: u64| a.count_ones() as _); + unop_test!(eqz, |a: u64| if a == 0 { 1 } else { 0 }, i32); binop_test!(add, i64::wrapping_add); binop_test!(sub, i64::wrapping_sub);