diff --git a/src/backend.rs b/src/backend.rs index 90d15cac19..171388e9c1 100644 --- a/src/backend.rs +++ b/src/backend.rs @@ -47,7 +47,7 @@ impl From for Option { } impl GPR { - fn type_(&self) -> GPRType { + fn type_(self) -> GPRType { match self { GPR::Rq(_) => GPRType::Rq, GPR::Rx(_) => GPRType::Rx, @@ -73,8 +73,8 @@ pub fn arg_locs(types: impl IntoIterator) -> Vec { let types = types.into_iter(); let mut out = Vec::with_capacity(types.size_hint().0); // TODO: VmCtx is in the first register - let mut int_gpr_iter = INTEGER_ARGS_IN_GPRS.into_iter(); - let mut float_gpr_iter = FLOAT_ARGS_IN_GPRS.into_iter(); + let mut int_gpr_iter = INTEGER_ARGS_IN_GPRS.iter(); + let mut float_gpr_iter = FLOAT_ARGS_IN_GPRS.iter(); let mut stack_idx = 0; for ty in types { @@ -102,8 +102,8 @@ pub fn ret_locs(types: impl IntoIterator) -> Vec { let types = types.into_iter(); let mut out = Vec::with_capacity(types.size_hint().0); // TODO: VmCtx is in the first register - let mut int_gpr_iter = INTEGER_RETURN_GPRS.into_iter(); - let mut float_gpr_iter = FLOAT_RETURN_GPRS.into_iter(); + let mut int_gpr_iter = INTEGER_RETURN_GPRS.iter(); + let mut float_gpr_iter = FLOAT_RETURN_GPRS.iter(); for ty in types { match ty { @@ -1224,40 +1224,43 @@ macro_rules! binop { }; ($name:ident, $instr:ident, $const_fallback:expr, $reg_ty:ident, $reg_fn:ident, $ty:expr, $imm_fn:ident, $direct_imm:expr, $map_op:expr) => { pub fn $name(&mut self) { - let op0 = self.pop(); - let op1 = self.pop(); + let right = self.pop(); + let left = self.pop(); - if let Some(i1) = op1.$imm_fn() { - if let Some(i0) = op0.$imm_fn() { + if let Some(i1) = left.$imm_fn() { + if let Some(i0) = right.$imm_fn() { self.block_state.stack.push(ValueLocation::Immediate($const_fallback(i1, i0).into())); return; } } - let (op1, op0) = $map_op(op1, op0); - let op1 = self.into_temp_reg($ty, op1); + let (left, mut right) = $map_op(left, right); + let left = self.into_temp_reg($ty, left); - match op0 { - ValueLocation::Reg(reg) => { + match right { + ValueLocation::Reg(_) => { + // This handles the case where we (for example) have a float in an `Rq` reg + let right_reg = self.into_reg($ty, right); + right = ValueLocation::Reg(right_reg); dynasm!(self.asm - ; $instr $reg_ty(op1.$reg_fn().unwrap()), $reg_ty(reg.$reg_fn().unwrap()) + ; $instr $reg_ty(left.$reg_fn().unwrap()), $reg_ty(right_reg.$reg_fn().unwrap()) ); } ValueLocation::Stack(offset) => { let offset = self.adjusted_offset(offset); dynasm!(self.asm - ; $instr $reg_ty(op1.$reg_fn().unwrap()), [rsp + offset] + ; $instr $reg_ty(left.$reg_fn().unwrap()), [rsp + offset] ); } ValueLocation::Immediate(i) => { if let Some(i) = i.as_int().and_then(|i| i.try_into()) { - $direct_imm(self, op1, i); + $direct_imm(self, left, i); } else { let scratch = self.block_state.regs.take($ty); self.immediate_to_reg(scratch, i); dynasm!(self.asm - ; $instr $reg_ty(op1.$reg_fn().unwrap()), $reg_ty(scratch.$reg_fn().unwrap()) + ; $instr $reg_ty(left.$reg_fn().unwrap()), $reg_ty(scratch.$reg_fn().unwrap()) ); self.block_state.regs.release(scratch); @@ -1265,15 +1268,15 @@ macro_rules! binop { } } - self.free_value(op0); - self.push(ValueLocation::Reg(op1)); + self.free_value(right); + self.push(ValueLocation::Reg(left)); } } } macro_rules! load { - ($name:ident, $reg_ty:ident, $instruction_name:expr, $out_ty:expr) => { - pub fn $name(&mut self, offset: u32) -> Result<(), Error> { + (@inner $name:ident, $reg_ty:ident, $emit_fn:expr) => { + pub fn $name(&mut self, ty: impl Into, offset: u32) -> Result<(), Error> { fn load_to_reg<_M: ModuleContext>( ctx: &mut Context<_M>, dst: GPR, @@ -1284,18 +1287,7 @@ macro_rules! load { dynasm!(ctx.asm ; mov Rq(mem_ptr_reg.rq().unwrap()), [Rq(VMCTX) + vmctx_mem_ptr_offset] ); - match runtime_offset { - Ok(imm) => { - dynasm!(ctx.asm - ; mov $reg_ty(dst.rq().unwrap()), [Rq(mem_ptr_reg.rq().unwrap()) + offset + imm] - ); - } - Err(offset_reg) => { - dynasm!(ctx.asm - ; mov $reg_ty(dst.rq().unwrap()), [Rq(mem_ptr_reg.rq().unwrap()) + Rq(offset_reg.rq().unwrap()) + offset] - ); - } - } + $emit_fn(ctx, dst, mem_ptr_reg, runtime_offset, offset); ctx.block_state.regs.release(mem_ptr_reg); } @@ -1303,7 +1295,7 @@ macro_rules! load { let base = self.pop(); - let temp = self.block_state.regs.take($out_ty); + let temp = self.block_state.regs.take(ty); match base { ValueLocation::Immediate(i) => { @@ -1320,11 +1312,61 @@ macro_rules! load { Ok(()) } - } + }; + ($name:ident, $reg_ty:ident, NONE) => { + load!(@inner + $name, + $reg_ty, + |ctx: &mut Context<_>, dst: GPR, mem_ptr_reg: GPR, runtime_offset: Result, offset: i32| { + match runtime_offset { + Ok(imm) => { + dynasm!(ctx.asm + ; mov $reg_ty(dst.rq().unwrap()), [Rq(mem_ptr_reg.rq().unwrap()) + offset + imm] + ); + } + Err(offset_reg) => { + dynasm!(ctx.asm + ; mov $reg_ty(dst.rq().unwrap()), [Rq(mem_ptr_reg.rq().unwrap()) + Rq(offset_reg.rq().unwrap()) + offset] + ); + } + } + } + ); + }; + ($name:ident, $reg_ty:ident, $xmm_instr:ident) => { + load!(@inner + $name, + $reg_ty, + |ctx: &mut Context<_>, dst: GPR, mem_ptr_reg: GPR, runtime_offset: Result, offset: i32| { + match (dst, runtime_offset) { + (GPR::Rq(r), Ok(imm)) => { + dynasm!(ctx.asm + ; mov $reg_ty(r), [Rq(mem_ptr_reg.rq().unwrap()) + offset + imm] + ); + } + (GPR::Rx(r), Ok(imm)) => { + dynasm!(ctx.asm + ; $xmm_instr Rx(r), [Rq(mem_ptr_reg.rq().unwrap()) + offset + imm] + ); + } + (GPR::Rq(r), Err(offset_reg)) => { + dynasm!(ctx.asm + ; mov $reg_ty(r), [Rq(mem_ptr_reg.rq().unwrap()) + Rq(offset_reg.rq().unwrap()) + offset] + ); + } + (GPR::Rx(r), Err(offset_reg)) => { + dynasm!(ctx.asm + ; $xmm_instr Rx(r), [Rq(mem_ptr_reg.rq().unwrap()) + Rq(offset_reg.rq().unwrap()) + offset] + ); + } + } + } + ); + }; } macro_rules! store { - ($name:ident, $reg_ty:ident, $size:ident, $instruction_name:expr, $in_ty:expr) => { + (@inner $name:ident, $int_reg_ty:ident, $match_offset:expr, $size:ident) => { pub fn $name(&mut self, offset: u32) -> Result<(), Error> { fn store_from_reg<_M: ModuleContext>( ctx: &mut Context<_M>, @@ -1332,23 +1374,14 @@ macro_rules! store { (offset, runtime_offset): (i32, Result) ) { let vmctx_mem_ptr_offset = ctx.module_context.offset_of_memory_ptr() as i32; - let mem_ptr_reg = ctx.block_state.regs.take(I64); + let mem_ptr_reg = ctx.block_state.regs.take(GPRType::Rq); dynasm!(ctx.asm ; mov Rq(mem_ptr_reg.rq().unwrap()), [Rq(VMCTX) + vmctx_mem_ptr_offset] ); - match runtime_offset { - Ok(imm) => { - dynasm!(ctx.asm - ; mov [Rq(mem_ptr_reg.rq().unwrap()) + offset + imm], $reg_ty(src.rq().unwrap()) - ); - } - Err(offset_reg) => { - dynasm!(ctx.asm - ; mov [Rq(mem_ptr_reg.rq().unwrap()) + Rq(offset_reg.rq().unwrap()) + offset], $reg_ty(src.rq().unwrap()) - ); - } - } + let src = $match_offset(ctx, mem_ptr_reg, runtime_offset, offset, src); ctx.block_state.regs.release(mem_ptr_reg); + + ctx.block_state.regs.release(src); } assert!(offset <= i32::max_value() as u32); @@ -1356,9 +1389,7 @@ macro_rules! store { let src = self.pop(); let base = self.pop(); - let src_reg = self.into_reg($in_ty, src); - // TODO - debug_assert!(stringify!($reg_ty) == "Rq" || stringify!($reg_ty) == "Rd"); + let src_reg = self.into_reg(None, src); match base { ValueLocation::Immediate(i) => { @@ -1371,11 +1402,67 @@ macro_rules! store { } } - self.block_state.regs.release(src_reg); - Ok(()) } - } + }; + ($name:ident, $int_reg_ty:ident, NONE, $size:ident) => { + store!(@inner + $name, + $int_reg_ty, + |ctx: &mut Context<_>, mem_ptr_reg: GPR, runtime_offset: Result, offset: i32, src| { + let src_reg = ctx.into_temp_reg(GPRType::Rq, ValueLocation::Reg(src)); + + match runtime_offset { + Ok(imm) => { + dynasm!(ctx.asm + ; mov [Rq(mem_ptr_reg.rq().unwrap()) + offset + imm], $int_reg_ty(src_reg.rq().unwrap()) + ); + } + Err(offset_reg) => { + dynasm!(ctx.asm + ; mov [Rq(mem_ptr_reg.rq().unwrap()) + Rq(offset_reg.rq().unwrap()) + offset], $int_reg_ty(src_reg.rq().unwrap()) + ); + } + } + + src_reg + }, + $size + ); + }; + ($name:ident, $int_reg_ty:ident, $xmm_instr:ident, $size:ident) => { + store!(@inner + $name, + $int_reg_ty, + |ctx: &mut Context<_>, mem_ptr_reg: GPR, runtime_offset: Result, offset: i32, src| { + match (runtime_offset, src) { + (Ok(imm), GPR::Rq(r)) => { + dynasm!(ctx.asm + ; mov [Rq(mem_ptr_reg.rq().unwrap()) + offset + imm], $int_reg_ty(r) + ); + } + (Ok(imm), GPR::Rx(r)) => { + dynasm!(ctx.asm + ; $xmm_instr [Rq(mem_ptr_reg.rq().unwrap()) + offset + imm], Rx(r) + ); + } + (Err(offset_reg), GPR::Rq(r)) => { + dynasm!(ctx.asm + ; mov [Rq(mem_ptr_reg.rq().unwrap()) + Rq(offset_reg.rq().unwrap()) + offset], $int_reg_ty(r) + ); + } + (Err(offset_reg), GPR::Rx(r)) => { + dynasm!(ctx.asm + ; $xmm_instr [Rq(mem_ptr_reg.rq().unwrap()) + Rq(offset_reg.rq().unwrap()) + offset], Rx(r) + ); + } + } + + src + }, + $size + ); + }; } trait TryInto { @@ -1442,6 +1529,21 @@ impl Context<'_, M> { (self.block_state.depth.0 as i32 + offset) * WORD_SIZE as i32 } + fn zero_reg(&mut self, reg: GPR) { + match reg { + GPR::Rq(r) => { + dynasm!(self.asm + ; xor Rq(r), Rq(r) + ); + } + GPR::Rx(r) => { + dynasm!(self.asm + ; pxor Rx(r), Rx(r) + ); + } + } + } + cmp_i32!(i32_eq, sete, sete, |a, b| a == b); cmp_i32!(i32_neq, setne, setne, |a, b| a != b); // `dynasm-rs` inexplicably doesn't support setb but `setnae` (and `setc`) are synonymous @@ -1888,10 +1990,15 @@ impl Context<'_, M> { self.block_state.depth = cc.stack_depth; } - load!(i32_load, Rd, "i32.load", I32); - load!(i64_load, Rq, "i64.load", I64); - store!(i32_store, Rd, DWORD, "i32.store", I32); - store!(i64_store, Rq, QWORD, "i64.store", I64); + load!(load8, Rb, NONE); + load!(load16, Rw, NONE); + load!(load32, Rd, movd); + load!(load64, Rq, movq); + + store!(store8, Rb, NONE, DWORD); + store!(store16, Rw, NONE, QWORD); + store!(store32, Rd, movd, DWORD); + store!(store64, Rq, movq, QWORD); fn push_physical(&mut self, value: ValueLocation) -> ValueLocation { self.block_state.depth.reserve(1); @@ -2166,11 +2273,7 @@ impl Context<'_, M> { }; self.free_value(quotient); - let should_save_rax = if self.block_state.regs.is_free(RAX) { - false - } else { - true - }; + let should_save_rax = !self.block_state.regs.is_free(RAX); if let ValueLocation::Reg(r) = quotient { self.block_state.regs.mark_used(r); @@ -2874,4 +2977,3 @@ impl Context<'_, M> { label } } - diff --git a/src/function_body.rs b/src/function_body.rs index 52efc81df1..a7c1efb06a 100644 --- a/src/function_body.rs +++ b/src/function_body.rs @@ -113,13 +113,7 @@ where }, ); - loop { - let op = if let Some(op) = body.next() { - op - } else { - break; - }; - + while let Some(op) = body.next() { if let Some(Operator::Label(label)) = body.peek() { let block = blocks .get_mut(&BrTarget::Label(label.clone())) @@ -313,19 +307,16 @@ where use itertools::Itertools; let (def, params) = { - let def = blocks.get(&default).unwrap(); + let def = &blocks[&default]; ( - if def.is_next { - None - } else { - Some(def.label) - }, - def.params.clone() + if def.is_next { None } else { Some(def.label) }, + def.params, ) }; - let target_labels = targets.iter() - .map(|target| blocks.get(target).unwrap().label) + let target_labels = targets + .iter() + .map(|target| blocks[target].label) .collect::>(); ctx.br_table(target_labels, def, |ctx| { @@ -438,10 +429,25 @@ where Operator::Le(SF64) => ctx.f64_le(), Operator::Drop(range) => ctx.drop(range), Operator::Const(val) => ctx.const_(val), - Operator::Load { ty: I32, memarg } => ctx.i32_load(memarg.offset)?, - Operator::Load { ty: I64, memarg } => ctx.i64_load(memarg.offset)?, - Operator::Store { ty: I32, memarg } => ctx.i32_store(memarg.offset)?, - Operator::Store { ty: I64, memarg } => ctx.i64_store(memarg.offset)?, + Operator::Load8 { ty: _, memarg } => ctx.load8(GPRType::Rq, memarg.offset)?, + Operator::Load16 { ty: _, memarg } => ctx.load16(GPRType::Rq, memarg.offset)?, + Operator::Load { ty: ty @ I32, memarg } | Operator::Load { ty: ty @ F32, memarg } => ctx.load32(ty, memarg.offset)?, + Operator::Load { ty: ty @ I64, memarg } | Operator::Load { ty: ty @ F64, memarg } => ctx.load64(ty, memarg.offset)?, + Operator::Store8 { ty: _, memarg } => { + ctx.store8(memarg.offset)? + } + Operator::Store16 { ty: _, memarg } => { + ctx.store16(memarg.offset)? + } + Operator::Store32 { memarg } => { + ctx.store32(memarg.offset)? + } + Operator::Store { ty: I32, memarg } | Operator::Store { ty: F32, memarg } => { + ctx.store32(memarg.offset)? + } + Operator::Store { ty: I64, memarg } | Operator::Store { ty: F64, memarg } => { + ctx.store64(memarg.offset)? + } Operator::Select => { ctx.select(); } diff --git a/src/lib.rs b/src/lib.rs index dea2d99232..d431826bce 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,7 +5,7 @@ never_type, alloc_layout_extra, try_from, - try_trait, + try_trait )] #![plugin(dynasm)] diff --git a/src/microwasm.rs b/src/microwasm.rs index 0b85325d72..0c8b54512a 100644 --- a/src/microwasm.rs +++ b/src/microwasm.rs @@ -657,7 +657,7 @@ where } write!(f, "], {}", default) - }, + } Operator::Call { function_index } => write!(f, "call {}", function_index), Operator::CallIndirect { .. } => write!(f, "call_indirect"), Operator::Drop(range) => { @@ -803,9 +803,8 @@ impl ControlFrame { } fn mark_branched_to(&mut self) { - match &mut self.kind { - ControlFrameKind::Block { needs_end_label } => *needs_end_label = true, - _ => {} + if let ControlFrameKind::Block { needs_end_label } = &mut self.kind { + *needs_end_label = true } } @@ -1318,10 +1317,7 @@ where } if let Some(consts) = self.consts_to_emit.take() { - return Some(Ok(consts - .into_iter() - .map(|value| Operator::Const(value)) - .collect())); + return Some(Ok(consts.into_iter().map(Operator::Const).collect())); } if self.unreachable { @@ -1564,7 +1560,6 @@ where .map(Operator::Drop) .into_iter() .chain(None) - .into_iter() .chain(None) }) } @@ -1617,7 +1612,7 @@ where Err(e) => return Some(Err(e)), }; let targets = entries - .into_iter() + .iter() .map(|depth| { let block = self.nth_block_mut(*depth as _); block.mark_branched_to(); diff --git a/src/tests.rs b/src/tests.rs index 5abf11ab12..b48fd5c24f 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -1202,19 +1202,144 @@ fn br_table() { translated.disassemble(); println!("as-block-first"); - assert_eq!( - translated.execute_func::<_, ()>(0, ()), - Ok(()), - ); + assert_eq!(translated.execute_func::<_, ()>(0, ()), Ok(()),); println!("as-block-mid"); - assert_eq!( - translated.execute_func::<_, ()>(1, ()), - Ok(()), - ); + assert_eq!(translated.execute_func::<_, ()>(1, ()), Ok(()),); println!("as-block-last"); + assert_eq!(translated.execute_func::<_, ()>(2, ()), Ok(()),); +} + +#[test] +fn f32_storage() { + const CODE: &str = r#" +(module + (memory (data "\00\00\a0\7f")) + + (func (result f32) + (f32.load (i32.const 0)) + ) + (func (result i32) + (i32.load (i32.const 0)) + ) + (func + (f32.store (i32.const 0) (f32.const nan:0x200000)) + ) + (func + (i32.store (i32.const 0) (i32.const 0x7fa00000)) + ) + (func + (i32.store (i32.const 0) (i32.const 0)) + ) +) + "#; + const EXPECTED: u32 = 0x7fa00000; + + let translated = translate_wat(CODE); + translated.disassemble(); + + // TODO: We don't support the data section with Lightbeam's test runtime + assert!(translated.execute_func::<(), ()>(2, ()).is_ok()); + + assert_eq!(translated.execute_func::<(), u32>(1, ()), Ok(EXPECTED)); assert_eq!( - translated.execute_func::<_, ()>(2, ()), - Ok(()), + translated + .execute_func::<(), f32>(0, ()) + .map(|f| f.to_bits()), + Ok(EXPECTED) + ); + assert!(translated.execute_func::<(), ()>(4, ()).is_ok()); + assert_eq!(translated.execute_func::<(), u32>(1, ()), Ok(0)); + assert_eq!( + translated + .execute_func::<(), f32>(0, ()) + .map(|f| f.to_bits()), + Ok(0) + ); + assert!(translated.execute_func::<(), ()>(2, ()).is_ok()); + assert_eq!(translated.execute_func::<(), u32>(1, ()), Ok(EXPECTED)); + assert_eq!( + translated + .execute_func::<(), f32>(0, ()) + .map(|f| f.to_bits()), + Ok(EXPECTED) + ); + assert!(translated.execute_func::<(), ()>(4, ()).is_ok()); + assert_eq!(translated.execute_func::<(), u32>(1, ()), Ok(0)); + assert_eq!( + translated + .execute_func::<(), f32>(0, ()) + .map(|f| f.to_bits()), + Ok(0) + ); + assert!(translated.execute_func::<(), ()>(3, ()).is_ok()); + assert_eq!(translated.execute_func::<(), u32>(1, ()), Ok(EXPECTED)); + assert_eq!( + translated + .execute_func::<(), f32>(0, ()) + .map(|f| f.to_bits()), + Ok(EXPECTED) + ); +} + +#[test] +fn f64_storage() { + const CODE: &str = r#" +(module + (memory (data "\00\00\00\00\00\00\f4\7f")) + + (func (export "f64.load") (result f64) (f64.load (i32.const 0))) + (func (export "i64.load") (result i64) (i64.load (i32.const 0))) + (func (export "f64.store") (f64.store (i32.const 0) (f64.const nan:0x4000000000000))) + (func (export "i64.store") (i64.store (i32.const 0) (i64.const 0x7ff4000000000000))) + (func (export "reset") (i64.store (i32.const 0) (i64.const 0))) +) + "#; + const EXPECTED: u64 = 0x7ff4000000000000; + + let translated = translate_wat(CODE); + translated.disassemble(); + + // TODO: We don't support the data section with Lightbeam's test runtime + assert!(translated.execute_func::<(), ()>(2, ()).is_ok()); + + assert_eq!(translated.execute_func::<(), u64>(1, ()), Ok(EXPECTED)); + assert_eq!( + translated + .execute_func::<(), f64>(0, ()) + .map(|f| f.to_bits()), + Ok(EXPECTED) + ); + assert!(translated.execute_func::<(), ()>(4, ()).is_ok()); + assert_eq!(translated.execute_func::<(), u64>(1, ()), Ok(0)); + assert_eq!( + translated + .execute_func::<(), f64>(0, ()) + .map(|f| f.to_bits()), + Ok(0) + ); + assert!(translated.execute_func::<(), ()>(2, ()).is_ok()); + assert_eq!(translated.execute_func::<(), u64>(1, ()), Ok(EXPECTED)); + assert_eq!( + translated + .execute_func::<(), f64>(0, ()) + .map(|f| f.to_bits()), + Ok(EXPECTED) + ); + assert!(translated.execute_func::<(), ()>(4, ()).is_ok()); + assert_eq!(translated.execute_func::<(), u64>(1, ()), Ok(0)); + assert_eq!( + translated + .execute_func::<(), f64>(0, ()) + .map(|f| f.to_bits()), + Ok(0) + ); + assert!(translated.execute_func::<(), ()>(3, ()).is_ok()); + assert_eq!(translated.execute_func::<(), u64>(1, ()), Ok(EXPECTED)); + assert_eq!( + translated + .execute_func::<(), f64>(0, ()) + .map(|f| f.to_bits()), + Ok(EXPECTED) ); } diff --git a/src/translate_sections.rs b/src/translate_sections.rs index a163e1980f..ff920863cd 100644 --- a/src/translate_sections.rs +++ b/src/translate_sections.rs @@ -116,11 +116,7 @@ pub fn code( for (idx, body) in code.into_iter().enumerate() { let body = body?; - function_body::translate_wasm( - &mut session, - idx as u32, - &body, - )?; + function_body::translate_wasm(&mut session, idx as u32, &body)?; } Ok(session.into_translated_code_section()?)