diff --git a/Cargo.toml b/Cargo.toml index d7a0397ccc..5e8c250527 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ keywords = ["webassembly", "wasm", "compile", "compiler", "jit"] publish = false [dependencies] -arrayvec = "0.4" +smallvec = "0.6" dynasm = "0.2.3" dynasmrt = "0.2.3" wasmparser = { path = "./wasmparser.rs" } diff --git a/src/backend.rs b/src/backend.rs index cd31708e79..c2a41fc55d 100644 --- a/src/backend.rs +++ b/src/backend.rs @@ -1,29 +1,52 @@ #![allow(dead_code)] // for now -use microwasm::{SignlessType, I32, I64}; +use microwasm::{SignlessType, F32, F64, I32, I64}; use self::registers::*; use dynasmrt::x64::Assembler; use dynasmrt::{AssemblyOffset, DynamicLabel, DynasmApi, DynasmLabelApi, ExecutableBuffer}; use error::Error; +use microwasm::Value; +use module::{ModuleContext, RuntimeFunc}; use std::{ iter::{self, FromIterator}, mem, ops::RangeInclusive, }; -use module::{ModuleContext, RuntimeFunc}; - /// Size of a pointer on the target in bytes. const WORD_SIZE: u32 = 8; -type GPR = u8; +type RegId = u8; + +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +pub enum GPR { + Rq(RegId), + Rx(RegId), +} + +impl GPR { + fn rq(self) -> Option { + match self { + GPR::Rq(r) => Some(r), + GPR::Rx(_) => None, + } + } + + fn rx(self) -> Option { + match self { + GPR::Rx(r) => Some(r), + GPR::Rq(_) => None, + } + } +} 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 stack_idx = 0; for ty in types { @@ -35,7 +58,12 @@ pub fn arg_locs(types: impl IntoIterator) -> Vec { out }, )), - _ => {} + F32 | F64 => out.push( + float_gpr_iter + .next() + .map(|&r| CCLoc::Reg(r)) + .expect("Float args on stack not yet supported"), + ), } } @@ -47,6 +75,7 @@ pub fn ret_locs(types: impl IntoIterator) -> Vec { 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(); for ty in types { match ty { @@ -55,7 +84,11 @@ pub fn ret_locs(types: impl IntoIterator) -> Vec { .next() .expect("We don't support stack returns yet"), )), - _ => panic!("We don't support floats yet"), + F32 | F64 => out.push(CCLoc::Reg( + *float_gpr_iter + .next() + .expect("We don't support stack returns yet"), + )), } } @@ -74,22 +107,63 @@ impl GPRs { } pub mod registers { - pub const RAX: u8 = 0; - pub const RCX: u8 = 1; - pub const RDX: u8 = 2; - pub const RBX: u8 = 3; - pub const RSP: u8 = 4; - pub const RBP: u8 = 5; - pub const RSI: u8 = 6; - pub const RDI: u8 = 7; - pub const R8: u8 = 8; - pub const R9: u8 = 9; - pub const R10: u8 = 10; - pub const R11: u8 = 11; - pub const R12: u8 = 12; - pub const R13: u8 = 13; - pub const R14: u8 = 14; - pub const R15: u8 = 15; + use super::{RegId, GPR}; + + pub mod rq { + use super::RegId; + + pub const RAX: RegId = 0; + pub const RCX: RegId = 1; + pub const RDX: RegId = 2; + pub const RBX: RegId = 3; + pub const RSP: RegId = 4; + pub const RBP: RegId = 5; + pub const RSI: RegId = 6; + pub const RDI: RegId = 7; + pub const R8: RegId = 8; + pub const R9: RegId = 9; + pub const R10: RegId = 10; + pub const R11: RegId = 11; + pub const R12: RegId = 12; + pub const R13: RegId = 13; + pub const R14: RegId = 14; + pub const R15: RegId = 15; + } + + pub const RAX: GPR = GPR::Rq(self::rq::RAX); + pub const RCX: GPR = GPR::Rq(self::rq::RCX); + pub const RDX: GPR = GPR::Rq(self::rq::RDX); + pub const RBX: GPR = GPR::Rq(self::rq::RBX); + pub const RSP: GPR = GPR::Rq(self::rq::RSP); + pub const RBP: GPR = GPR::Rq(self::rq::RBP); + pub const RSI: GPR = GPR::Rq(self::rq::RSI); + pub const RDI: GPR = GPR::Rq(self::rq::RDI); + pub const R8: GPR = GPR::Rq(self::rq::R8); + pub const R9: GPR = GPR::Rq(self::rq::R9); + pub const R10: GPR = GPR::Rq(self::rq::R10); + pub const R11: GPR = GPR::Rq(self::rq::R11); + pub const R12: GPR = GPR::Rq(self::rq::R12); + pub const R13: GPR = GPR::Rq(self::rq::R13); + pub const R14: GPR = GPR::Rq(self::rq::R14); + pub const R15: GPR = GPR::Rq(self::rq::R15); + + pub const XMM0: GPR = GPR::Rx(0); + pub const XMM1: GPR = GPR::Rx(1); + pub const XMM2: GPR = GPR::Rx(2); + pub const XMM3: GPR = GPR::Rx(3); + pub const XMM4: GPR = GPR::Rx(4); + pub const XMM5: GPR = GPR::Rx(5); + pub const XMM6: GPR = GPR::Rx(6); + pub const XMM7: GPR = GPR::Rx(7); + pub const XMM8: GPR = GPR::Rx(8); + pub const XMM9: GPR = GPR::Rx(9); + pub const XMM10: GPR = GPR::Rx(10); + pub const XMM11: GPR = GPR::Rx(11); + pub const XMM12: GPR = GPR::Rx(12); + pub const XMM13: GPR = GPR::Rx(13); + pub const XMM14: GPR = GPR::Rx(14); + pub const XMM15: GPR = GPR::Rx(15); + pub const NUM_GPRS: u8 = 16; } @@ -150,19 +224,19 @@ macro_rules! asm_println { } impl GPRs { - fn take(&mut self) -> GPR { + fn take(&mut self) -> RegId { let lz = self.bits.trailing_zeros(); debug_assert!(lz < 16, "ran out of free GPRs"); - let gpr = lz as GPR; + let gpr = lz as RegId; self.mark_used(gpr); gpr } - fn mark_used(&mut self, gpr: GPR) { + fn mark_used(&mut self, gpr: RegId) { self.bits &= !(1 << gpr as u16); } - fn release(&mut self, gpr: GPR) { + fn release(&mut self, gpr: RegId) { debug_assert!( !self.is_free(gpr), "released register {} was already free", @@ -175,15 +249,17 @@ impl GPRs { self.bits.count_ones() } - fn is_free(&self, gpr: GPR) -> bool { + fn is_free(&self, gpr: RegId) -> bool { (self.bits & (1 << gpr)) != 0 } } #[derive(Debug, Copy, Clone)] pub struct Registers { - scratch: GPRs, - counts: [u8; NUM_GPRS as usize], + /// Registers at 64 bits and below (al/ah/ax/eax/rax, for example) + scratch_64: (GPRs, [u8; NUM_GPRS as usize]), + /// Registers at 128 bits (xmm0, for example) + scratch_128: (GPRs, [u8; NUM_GPRS as usize]), } impl Default for Registers { @@ -195,49 +271,79 @@ impl Default for Registers { impl Registers { pub fn new() -> Self { let mut result = Self { - scratch: GPRs::new(), - counts: [1; NUM_GPRS as _], + scratch_64: (GPRs::new(), [1; NUM_GPRS as _]), + scratch_128: (GPRs::new(), [1; NUM_GPRS as _]), }; // Give ourselves a few scratch registers to work with, for now. for &scratch in SCRATCH_REGS { - result.release_scratch_gpr(scratch); + result.release(scratch); } result } + fn scratch_counts_mut(&mut self, gpr: GPR) -> (u8, &mut (GPRs, [u8; NUM_GPRS as usize])) { + match gpr { + GPR::Rq(r) => (r, &mut self.scratch_64), + GPR::Rx(r) => (r, &mut self.scratch_128), + } + } + + fn scratch_counts(&self, gpr: GPR) -> (u8, &(GPRs, [u8; NUM_GPRS as usize])) { + match gpr { + GPR::Rq(r) => (r, &self.scratch_64), + GPR::Rx(r) => (r, &self.scratch_128), + } + } + pub fn mark_used(&mut self, gpr: GPR) { - self.scratch.mark_used(gpr); - self.counts[gpr as usize] += 1; + let (gpr, scratch_counts) = self.scratch_counts_mut(gpr); + scratch_counts.0.mark_used(gpr); + scratch_counts.1[gpr as usize] += 1; } pub fn num_usages(&self, gpr: GPR) -> u8 { - self.counts[gpr as usize] + let (gpr, scratch_counts) = self.scratch_counts(gpr); + scratch_counts.1[gpr as usize] } // TODO: Add function that takes a scratch register if possible // but otherwise gives a fresh stack location. - pub fn take_scratch_gpr(&mut self) -> GPR { - let out = self.scratch.take(); - self.counts[out as usize] += 1; - out + pub fn take_64(&mut self) -> GPR { + let out = self.scratch_64.0.take(); + self.scratch_64.1[out as usize] += 1; + GPR::Rq(out) } - pub fn release_scratch_gpr(&mut self, gpr: GPR) { - let c = &mut self.counts[gpr as usize]; + // TODO: Add function that takes a scratch register if possible + // but otherwise gives a fresh stack location. + pub fn take_128(&mut self) -> GPR { + let out = self.scratch_128.0.take(); + self.scratch_128.1[out as usize] += 1; + GPR::Rx(out) + } + + pub fn release(&mut self, gpr: GPR) { + let (gpr, scratch_counts) = self.scratch_counts_mut(gpr); + let c = &mut scratch_counts.1[gpr as usize]; *c -= 1; if *c == 0 { - self.scratch.release(gpr); + scratch_counts.0.release(gpr); } } pub fn is_free(&self, gpr: GPR) -> bool { - self.scratch.is_free(gpr) + let (gpr, scratch_counts) = self.scratch_counts(gpr); + scratch_counts.0.is_free(gpr) } - pub fn free_scratch(&self) -> u32 { - self.scratch.free_count() + pub fn free_64(&self) -> u32 { + self.scratch_64.0.free_count() + } + + pub fn free_128(&self) -> u32 { + self.scratch_128.0.free_count() } } @@ -277,8 +383,8 @@ pub enum ValueLocation { /// Value exists on the stack. Note that this offset is from the rsp as it /// was when we entered the function. Stack(i32), - /// Value is a literal (TODO: Support more than just `i32`) - Immediate(i64), + /// Value is a literal + Immediate(Value), } impl From for ValueLocation { @@ -291,12 +397,28 @@ impl From for ValueLocation { } impl ValueLocation { - fn immediate(&self) -> Option { + fn immediate(self) -> Option { match self { - ValueLocation::Immediate(i) => Some(*i), + ValueLocation::Immediate(i) => Some(i), _ => None, } } + + fn imm_i32(self) -> Option { + self.immediate().and_then(Value::as_i32) + } + + fn imm_i64(self) -> Option { + self.immediate().and_then(Value::as_i64) + } + + fn imm_f32(self) -> Option { + self.immediate().and_then(Value::as_f32) + } + + fn imm_f64(self) -> Option { + self.immediate().and_then(Value::as_f64) + } } // TODO: This assumes only system-v calling convention. @@ -304,9 +426,14 @@ impl ValueLocation { // All rest arguments are passed on the stack. const INTEGER_ARGS_IN_GPRS: &[GPR] = &[RSI, RDX, RCX, R8, R9]; const INTEGER_RETURN_GPRS: &[GPR] = &[RAX, RDX]; +const FLOAT_ARGS_IN_GPRS: &[GPR] = &[XMM0, XMM1, XMM2, XMM3, XMM4, XMM5, XMM6, XMM7]; +const FLOAT_RETURN_GPRS: &[GPR] = &[XMM0, XMM1]; // List of scratch registers taken from https://wiki.osdev.org/System_V_ABI -const SCRATCH_REGS: &[GPR] = &[RSI, RDX, RCX, R8, R9, RAX, R10, R11]; -const VMCTX: GPR = RDI; +const SCRATCH_REGS: &[GPR] = &[ + RSI, RDX, RCX, R8, R9, RAX, R10, R11, XMM1, XMM2, XMM3, XMM4, XMM5, XMM6, XMM7, XMM8, XMM9, + XMM10, XMM11, XMM12, XMM13, XMM14, XMM15, +]; +const VMCTX: RegId = rq::RDI; #[must_use] #[derive(Debug, Clone)] @@ -510,19 +637,22 @@ macro_rules! unop { let val = self.pop(); let out_val = match val { - ValueLocation::Immediate(imm) => ValueLocation::Immediate($const_fallback(imm as $typ) as _), + ValueLocation::Immediate(imm) => + ValueLocation::Immediate( + ($const_fallback(imm.as_int().unwrap() as $typ) as $typ).into() + ), ValueLocation::Stack(offset) => { let offset = self.adjusted_offset(offset); - let temp = self.block_state.regs.take_scratch_gpr(); + let temp = self.block_state.regs.take_64(); dynasm!(self.asm - ; $instr $reg_ty(temp), [rsp + offset] + ; $instr $reg_ty(temp.rq().unwrap()), [rsp + offset] ); ValueLocation::Reg(temp) } ValueLocation::Reg(reg) => { - let temp = self.block_state.regs.take_scratch_gpr(); + let temp = self.block_state.regs.take_64(); dynasm!(self.asm - ; $instr $reg_ty(temp), $reg_ty(reg) + ; $instr $reg_ty(temp.rq().unwrap()), $reg_ty(reg.rq().unwrap()) ); ValueLocation::Reg(temp) } @@ -558,9 +688,9 @@ macro_rules! shift { let out = if self.block_state.regs.is_free(RCX) { None } else { - let new_reg = self.block_state.regs.take_scratch_gpr(); + let new_reg = self.block_state.regs.take_64(); dynasm!(self.asm - ; mov Rq(new_reg), rcx + ; mov Rq(new_reg.rq().unwrap()), rcx ); Some(new_reg) }; @@ -568,7 +698,7 @@ macro_rules! shift { match other { ValueLocation::Reg(gpr) => { dynasm!(self.asm - ; mov cl, Rb(gpr) + ; mov cl, Rb(gpr.rq().unwrap()) ); } ValueLocation::Stack(offset) => { @@ -579,7 +709,7 @@ macro_rules! shift { } ValueLocation::Immediate(imm) => { dynasm!(self.asm - ; mov cl, imm as i8 + ; mov cl, imm.as_int().unwrap() as i8 ); } } @@ -595,16 +725,16 @@ macro_rules! shift { let reg = self.into_reg(val); dynasm!(self.asm - ; $instr $reg_ty(reg), cl + ; $instr $reg_ty(reg.rq().unwrap()), cl ); self.free_value(count); if let Some(gpr) = temp_rcx { dynasm!(self.asm - ; mov rcx, Rq(gpr) + ; mov rcx, Rq(gpr.rq().unwrap()) ); - self.block_state.regs.release_scratch_gpr(gpr); + self.block_state.regs.release(gpr); } self.push(ValueLocation::Reg(reg)); @@ -618,59 +748,66 @@ macro_rules! cmp_i32 { let right = self.pop(); let mut left = self.pop(); - let out = if let Some(i) = left.immediate() { + let out = if let Some(i) = left.imm_i32() { match right { ValueLocation::Stack(offset) => { - let result = self.block_state.regs.take_scratch_gpr(); + let result = self.block_state.regs.take_64(); let offset = self.adjusted_offset(offset); dynasm!(self.asm - ; xor Rd(result), Rd(result) - ; cmp DWORD [rsp + offset], i as i32 - ; $reverse_instr Rb(result) + ; xor Rd(result.rq().unwrap()), Rd(result.rq().unwrap()) + ; cmp DWORD [rsp + offset], i + ; $reverse_instr Rb(result.rq().unwrap()) ); ValueLocation::Reg(result) } ValueLocation::Reg(rreg) => { - let result = self.block_state.regs.take_scratch_gpr(); + let result = self.block_state.regs.take_64(); dynasm!(self.asm - ; xor Rd(result), Rd(result) - ; cmp Rd(rreg), i as i32 - ; $reverse_instr Rb(result) + ; xor Rd(result.rq().unwrap()), Rd(result.rq().unwrap()) + ; cmp Rd(rreg.rq().unwrap()), i + ; $reverse_instr Rb(result.rq().unwrap()) ); ValueLocation::Reg(result) } ValueLocation::Immediate(right) => { - ValueLocation::Immediate(if $const_fallback(i as i32, right as i32) { 1 } else { 0 }) + ValueLocation::Immediate( + (if $const_fallback(i, right.as_i32().unwrap()) { + 1i32 + } else { + 0i32 + }).into() + ) } } } else { let lreg = self.into_reg(left); // TODO: Make `into_reg` take an `&mut`? left = ValueLocation::Reg(lreg); - let result = self.block_state.regs.take_scratch_gpr(); + + let result = self.block_state.regs.take_64(); match right { ValueLocation::Stack(offset) => { let offset = self.adjusted_offset(offset); dynasm!(self.asm - ; xor Rd(result), Rd(result) - ; cmp Rd(lreg), [rsp + offset] - ; $instr Rb(result) + ; xor Rd(result.rq().unwrap()), Rd(result.rq().unwrap()) + ; cmp Rd(lreg.rq().unwrap()), [rsp + offset] + ; $instr Rb(result.rq().unwrap()) ); } ValueLocation::Reg(rreg) => { dynasm!(self.asm - ; xor Rd(result), Rd(result) - ; cmp Rd(lreg), Rd(rreg) - ; $instr Rb(result) + ; xor Rd(result.rq().unwrap()), Rd(result.rq().unwrap()) + ; cmp Rd(lreg.rq().unwrap()), Rd(rreg.rq().unwrap()) + ; $instr Rb(result.rq().unwrap()) ); } ValueLocation::Immediate(i) => { dynasm!(self.asm - ; xor Rd(result), Rd(result) - ; cmp Rd(lreg), i as i32 - ; $instr Rb(result) + ; xor Rd(result.rq().unwrap()), Rd(result.rq().unwrap()) + ; cmp Rd(lreg.rq().unwrap()), i.as_i32().unwrap() + ; $instr Rb(result.rq().unwrap()) ); } } @@ -692,16 +829,16 @@ macro_rules! cmp_i64 { let right = self.pop(); let mut left = self.pop(); - let out = if let Some(i) = left.immediate() { + let out = if let Some(i) = left.imm_i64() { match right { ValueLocation::Stack(offset) => { - let result = self.block_state.regs.take_scratch_gpr(); + let result = self.block_state.regs.take_64(); let offset = self.adjusted_offset(offset); if let Some(i) = i.try_into() { dynasm!(self.asm - ; xor Rd(result), Rd(result) + ; xor Rd(result.rq().unwrap()), Rd(result.rq().unwrap()) ; cmp QWORD [rsp + offset], i - ; $reverse_instr Rb(result) + ; $reverse_instr Rb(result.rq().unwrap()) ); } else { unimplemented!("Unsupported `cmp` with large 64-bit immediate operand"); @@ -709,12 +846,12 @@ macro_rules! cmp_i64 { ValueLocation::Reg(result) } ValueLocation::Reg(rreg) => { - let result = self.block_state.regs.take_scratch_gpr(); + let result = self.block_state.regs.take_64(); if let Some(i) = i.try_into() { dynasm!(self.asm - ; xor Rd(result), Rd(result) - ; cmp Rq(rreg), i - ; $reverse_instr Rb(result) + ; xor Rd(result.rq().unwrap()), Rd(result.rq().unwrap()) + ; cmp Rq(rreg.rq().unwrap()), i + ; $reverse_instr Rb(result.rq().unwrap()) ); } else { unimplemented!("Unsupported `cmp` with large 64-bit immediate operand"); @@ -722,37 +859,44 @@ macro_rules! cmp_i64 { ValueLocation::Reg(result) } ValueLocation::Immediate(right) => { - ValueLocation::Immediate(if $const_fallback(i, right) { 1 } else { 0 }) + ValueLocation::Immediate( + (if $const_fallback(i, right.as_i64().unwrap()) { + 1i32 + } else { + 0i32 + }).into() + ) } } } else { let lreg = self.into_reg(left); left = ValueLocation::Reg(lreg); - let result = self.block_state.regs.take_scratch_gpr(); + let result = self.block_state.regs.take_64(); match right { ValueLocation::Stack(offset) => { let offset = self.adjusted_offset(offset); dynasm!(self.asm - ; xor Rd(result), Rd(result) - ; cmp Rq(lreg), [rsp + offset] - ; $instr Rb(result) + ; xor Rd(result.rq().unwrap()), Rd(result.rq().unwrap()) + ; cmp Rq(lreg.rq().unwrap()), [rsp + offset] + ; $instr Rb(result.rq().unwrap()) ); } ValueLocation::Reg(rreg) => { dynasm!(self.asm - ; xor Rd(result), Rd(result) - ; cmp Rq(lreg), Rq(rreg) - ; $instr Rb(result) + ; xor Rd(result.rq().unwrap()), Rd(result.rq().unwrap()) + ; cmp Rq(lreg.rq().unwrap()), Rq(rreg.rq().unwrap()) + ; $instr Rb(result.rq().unwrap()) ); } ValueLocation::Immediate(i) => { + let i = i.as_i64().unwrap(); if let Some(i) = i.try_into() { dynasm!(self.asm - ; xor Rd(result), Rd(result) - ; cmp Rq(lreg), i - ; $instr Rb(result) + ; xor Rd(result.rq().unwrap()), Rd(result.rq().unwrap()) + ; cmp Rq(lreg.rq().unwrap()), i + ; $instr Rb(result.rq().unwrap()) ); } else { unimplemented!("Unsupported `cmp` with large 64-bit immediate operand"); @@ -776,9 +920,9 @@ macro_rules! commutative_binop_i32 { let op0 = self.pop(); let op1 = self.pop(); - if let Some(i1) = op1.immediate() { - if let Some(i0) = op0.immediate() { - self.push(ValueLocation::Immediate($const_fallback(i1 as i32, i0 as i32) as _)); + if let Some(i1) = op1.imm_i32() { + if let Some(i0) = op0.imm_i32() { + self.push(ValueLocation::Immediate($const_fallback(i1, i0).into())); return; } } @@ -795,18 +939,18 @@ macro_rules! commutative_binop_i32 { match op0 { ValueLocation::Reg(reg) => { dynasm!(self.asm - ; $instr Rd(op1), Rd(reg) + ; $instr Rd(op1.rq().unwrap()), Rd(reg.rq().unwrap()) ); } ValueLocation::Stack(offset) => { let offset = self.adjusted_offset(offset); dynasm!(self.asm - ; $instr Rd(op1), [rsp + offset] + ; $instr Rd(op1.rq().unwrap()), [rsp + offset] ); } ValueLocation::Immediate(i) => { dynasm!(self.asm - ; $instr Rd(op1), i as i32 + ; $instr Rd(op1.rq().unwrap()), i.as_i32().unwrap() ); } } @@ -823,9 +967,9 @@ macro_rules! commutative_binop_i64 { let op0 = self.pop(); let op1 = self.pop(); - if let Some(i1) = op1.immediate() { - if let Some(i0) = op0.immediate() { - self.block_state.stack.push(ValueLocation::Immediate($const_fallback(i1, i0))); + if let Some(i1) = op1.imm_i64() { + if let Some(i0) = op0.imm_i64() { + self.block_state.stack.push(ValueLocation::Immediate($const_fallback(i1, i0).into())); return; } } @@ -842,29 +986,30 @@ macro_rules! commutative_binop_i64 { match op0 { ValueLocation::Reg(reg) => { dynasm!(self.asm - ; $instr Rq(op1), Rq(reg) + ; $instr Rq(op1.rq().unwrap()), Rq(reg.rq().unwrap()) ); } ValueLocation::Stack(offset) => { let offset = self.adjusted_offset(offset); dynasm!(self.asm - ; $instr Rq(op1), [rsp + offset] + ; $instr Rq(op1.rq().unwrap()), [rsp + offset] ); } ValueLocation::Immediate(i) => { + let i = i.as_i64().unwrap(); if let Some(i) = i.try_into() { dynasm!(self.asm - ; $instr Rq(op1), i + ; $instr Rq(op1.rq().unwrap()), i ); } else { - let scratch = self.block_state.regs.take_scratch_gpr(); + let scratch = self.block_state.regs.take_64(); dynasm!(self.asm - ; mov Rq(scratch), QWORD i - ; $instr Rq(op1), Rq(scratch) + ; mov Rq(scratch.rq().unwrap()), QWORD i + ; $instr Rq(op1.rq().unwrap()), Rq(scratch.rq().unwrap()) ); - self.block_state.regs.release_scratch_gpr(scratch); + self.block_state.regs.release(scratch); } } } @@ -884,49 +1029,39 @@ macro_rules! load { (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_scratch_gpr(); + let mem_ptr_reg = ctx.block_state.regs.take_64(); dynasm!(ctx.asm - ; mov Rq(mem_ptr_reg), [Rq(VMCTX) + vmctx_mem_ptr_offset] + ; 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(mem_ptr_reg) + offset + imm] + ; 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(mem_ptr_reg) + Rq(offset_reg) + offset] + ; mov $reg_ty(dst.rq().unwrap()), [Rq(mem_ptr_reg.rq().unwrap()) + Rq(offset_reg.rq().unwrap()) + offset] ); } } - ctx.block_state.regs.release_scratch_gpr(mem_ptr_reg); + ctx.block_state.regs.release(mem_ptr_reg); } assert!(offset <= i32::max_value() as u32); let base = self.pop(); - let temp = self.block_state.regs.take_scratch_gpr(); + let temp = self.block_state.regs.take_64(); match base { ValueLocation::Immediate(i) => { - let val = if let Some(i) = i.try_into() { - Ok(i) - } else { - Err(self.into_temp_reg(base)) - }; - - load_to_reg(self, temp, (offset as _, val)); - - if let Err(r) = val { - self.block_state.regs.release_scratch_gpr(r); - } + load_to_reg(self, temp, (offset as _, Ok(i.as_i32().unwrap()))); } base => { let gpr = self.into_reg(base); load_to_reg(self, temp, (offset as _, Err(gpr))); - self.block_state.regs.release_scratch_gpr(gpr); + self.block_state.regs.release(gpr); } } @@ -946,23 +1081,23 @@ 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_scratch_gpr(); + let mem_ptr_reg = ctx.block_state.regs.take_64(); dynasm!(ctx.asm - ; mov Rq(mem_ptr_reg), [Rq(VMCTX) + vmctx_mem_ptr_offset] + ; 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) + offset + imm], $reg_ty(src) + ; 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(offset_reg) + offset], $reg_ty(src) + ; mov [Rq(mem_ptr_reg.rq().unwrap()) + Rq(offset_reg.rq().unwrap()) + offset], $reg_ty(src.rq().unwrap()) ); } } - ctx.block_state.regs.release_scratch_gpr(mem_ptr_reg); + ctx.block_state.regs.release(mem_ptr_reg); } assert!(offset <= i32::max_value() as u32); @@ -974,26 +1109,16 @@ macro_rules! store { match base { ValueLocation::Immediate(i) => { - let val = if let Some(i) = i.try_into() { - Ok(i) - } else { - Err(self.into_temp_reg(base)) - }; - - store_from_reg(self, src_reg, (offset as i32, val)); - - if let Err(r) = val { - self.block_state.regs.release_scratch_gpr(r); - } + store_from_reg(self, src_reg, (offset as i32, Ok(i.as_i32().unwrap()))); } base => { let gpr = self.into_reg(base); store_from_reg(self, src_reg, (offset as i32, Err(gpr))); - self.block_state.regs.release_scratch_gpr(gpr); + self.block_state.regs.release(gpr); } } - self.block_state.regs.release_scratch_gpr(src_reg); + self.block_state.regs.release(src_reg); Ok(()) } @@ -1095,21 +1220,23 @@ impl Context<'_, M> { pub fn i32_eqz(&mut self) { let val = self.pop(); - if let ValueLocation::Immediate(i) = val { - self.push(ValueLocation::Immediate(if i == 0 { 1 } else { 0 })); + if let ValueLocation::Immediate(Value::I32(i)) = val { + self.push(ValueLocation::Immediate( + (if i == 0 { 1i32 } else { 0 }).into(), + )); return; } let reg = self.into_reg(val); - let out = self.block_state.regs.take_scratch_gpr(); + let out = self.block_state.regs.take_64(); dynasm!(self.asm - ; xor Rd(out), Rd(out) - ; test Rd(reg), Rd(reg) - ; setz Rb(out) + ; xor Rd(out.rq().unwrap()), Rd(out.rq().unwrap()) + ; test Rd(reg.rq().unwrap()), Rd(reg.rq().unwrap()) + ; setz Rb(out.rq().unwrap()) ); - self.block_state.regs.release_scratch_gpr(reg); + self.block_state.regs.release(reg); self.push(ValueLocation::Reg(out)); } @@ -1117,21 +1244,23 @@ impl Context<'_, M> { pub fn i64_eqz(&mut self) { let val = self.pop(); - if let ValueLocation::Immediate(i) = val { - self.push(ValueLocation::Immediate(if i == 0 { 1 } else { 0 })); + if let ValueLocation::Immediate(Value::I64(i)) = val { + self.push(ValueLocation::Immediate( + (if i == 0 { 1i32 } else { 0 }).into(), + )); return; } let reg = self.into_reg(val); - let out = self.block_state.regs.take_scratch_gpr(); + let out = self.block_state.regs.take_64(); dynasm!(self.asm - ; xor Rd(out), Rd(out) - ; test Rq(reg), Rq(reg) - ; setz Rb(out) + ; xor Rd(out.rq().unwrap()), Rd(out.rq().unwrap()) + ; test Rq(reg.rq().unwrap()), Rq(reg.rq().unwrap()) + ; setz Rb(out.rq().unwrap()) ); - self.block_state.regs.release_scratch_gpr(reg); + self.block_state.regs.release(reg); self.push(ValueLocation::Reg(out)); } @@ -1146,11 +1275,11 @@ impl Context<'_, M> { let predicate = self.into_reg(val); dynasm!(self.asm - ; test Rd(predicate), Rd(predicate) + ; test Rd(predicate.rq().unwrap()), Rd(predicate.rq().unwrap()) ; jz =>label.0 ); - self.block_state.regs.release_scratch_gpr(predicate); + self.block_state.regs.release(predicate); } /// Pops i32 predicate and branches to the specified label @@ -1163,11 +1292,11 @@ impl Context<'_, M> { let predicate = self.into_reg(val); dynasm!(self.asm - ; test Rd(predicate), Rd(predicate) + ; test Rd(predicate.rq().unwrap()), Rd(predicate.rq().unwrap()) ; jnz =>label.0 ); - self.block_state.regs.release_scratch_gpr(predicate); + self.block_state.regs.release(predicate); } /// Branch unconditionally to the specified label. @@ -1187,12 +1316,12 @@ impl Context<'_, M> { ); } } else if self.block_state.depth.0 > depth.0 { - let trash = self.block_state.regs.take_scratch_gpr(); + let trash = self.block_state.regs.take_64(); // TODO: We need to preserve ZF on `br_if` so we use `push`/`pop` but that isn't // necessary on (for example) `br`. for _ in 0..self.block_state.depth.0 - depth.0 { dynasm!(self.asm - ; pop Rq(trash) + ; pop Rq(trash.rq().unwrap()) ); } } @@ -1257,15 +1386,29 @@ impl Context<'_, M> { } } - fn immediate_to_reg(&mut self, reg: GPR, val: i64) { - if (val as u64) <= u32::max_value() as u64 { - dynasm!(self.asm - ; mov Rd(reg), val as i32 - ); - } else { - dynasm!(self.asm - ; mov Rq(reg), QWORD val - ); + fn immediate_to_reg(&mut self, reg: GPR, val: Value) { + // TODO: Floats + match reg { + GPR::Rq(r) => { + let val = val.as_bytes(); + if (val as u64) <= u32::max_value() as u64 { + dynasm!(self.asm + ; mov Rd(r), val as i32 + ); + } else { + dynasm!(self.asm + ; mov Rq(r), QWORD val + ); + } + } + GPR::Rx(r) => { + let temp = self.block_state.regs.take_64(); + self.immediate_to_reg(temp, val); + dynasm!(self.asm + ; movq Rx(r), Rq(temp.rq().unwrap()) + ); + self.block_state.regs.release(temp); + } } } @@ -1278,51 +1421,100 @@ impl Context<'_, M> { let in_offset = self.adjusted_offset(in_offset); let out_offset = self.adjusted_offset(out_offset); if in_offset != out_offset { - let gpr = self.block_state.regs.take_scratch_gpr(); + let gpr = self.block_state.regs.take_64(); dynasm!(self.asm - ; mov Rq(gpr), [rsp + in_offset] - ; mov [rsp + out_offset], Rq(gpr) + ; mov Rq(gpr.rq().unwrap()), [rsp + in_offset] + ; mov [rsp + out_offset], Rq(gpr.rq().unwrap()) ); - self.block_state.regs.release_scratch_gpr(gpr); + self.block_state.regs.release(gpr); } } + // TODO: XMM registers (ValueLocation::Reg(in_reg), ValueLocation::Stack(out_offset)) => { let out_offset = self.adjusted_offset(out_offset); - dynasm!(self.asm - ; mov [rsp + out_offset], Rq(in_reg) - ); + match in_reg { + GPR::Rq(in_reg) => { + // We can always use `Rq` here for now because stack slots are in multiples of + // 8 bytes + dynasm!(self.asm + ; mov [rsp + out_offset], Rq(in_reg) + ); + } + GPR::Rx(in_reg) => { + // We can always use `movq` here for now because stack slots are in multiples of + // 8 bytes + dynasm!(self.asm + ; movq [rsp + out_offset], Rx(in_reg) + ); + } + } } (ValueLocation::Immediate(i), ValueLocation::Stack(out_offset)) => { + // TODO: Floats + let i = i.as_bytes(); let out_offset = self.adjusted_offset(out_offset); if (i as u64) <= u32::max_value() as u64 { dynasm!(self.asm ; mov DWORD [rsp + out_offset], i as i32 ); } else { - let scratch = self.block_state.regs.take_scratch_gpr(); + let scratch = self.block_state.regs.take_64(); dynasm!(self.asm - ; mov Rq(scratch), QWORD i - ; mov [rsp + out_offset], Rq(scratch) + ; mov Rq(scratch.rq().unwrap()), QWORD i + ; mov [rsp + out_offset], Rq(scratch.rq().unwrap()) ); - self.block_state.regs.release_scratch_gpr(scratch); + self.block_state.regs.release(scratch); } } (ValueLocation::Stack(in_offset), ValueLocation::Reg(out_reg)) => { let in_offset = self.adjusted_offset(in_offset); - dynasm!(self.asm - ; mov Rq(out_reg), [rsp + in_offset] - ); + match out_reg { + GPR::Rq(out_reg) => { + // We can always use `Rq` here for now because stack slots are in multiples of + // 8 bytes + dynasm!(self.asm + ; mov Rq(out_reg), [rsp + in_offset] + ); + } + GPR::Rx(out_reg) => { + // We can always use `movq` here for now because stack slots are in multiples of + // 8 bytes + dynasm!(self.asm + ; movq Rx(out_reg), [rsp + in_offset] + ); + } + } } (ValueLocation::Reg(in_reg), ValueLocation::Reg(out_reg)) => { if in_reg != out_reg { - dynasm!(self.asm - ; mov Rq(out_reg), Rq(in_reg) - ); + match (in_reg, out_reg) { + (GPR::Rq(in_reg), GPR::Rq(out_reg)) => { + dynasm!(self.asm + ; mov Rq(out_reg), Rq(in_reg) + ); + } + (GPR::Rx(in_reg), GPR::Rq(out_reg)) => { + dynasm!(self.asm + ; movq Rx(out_reg), Rq(in_reg) + ); + } + (GPR::Rq(in_reg), GPR::Rx(out_reg)) => { + dynasm!(self.asm + ; movq Rq(out_reg), Rx(in_reg) + ); + } + (GPR::Rx(in_reg), GPR::Rx(out_reg)) => { + dynasm!(self.asm + ; movq Rx(out_reg), Rx(in_reg) + ); + } + } } } (ValueLocation::Immediate(i), ValueLocation::Reg(out_reg)) => { + // TODO: Floats self.immediate_to_reg(out_reg, i); } // TODO: Have separate `ReadLocation` and `WriteLocation`? @@ -1377,9 +1569,9 @@ impl Context<'_, M> { ValueLocation::Reg(gpr) => { // TODO: Proper stack allocation scheme dynasm!(self.asm - ; push Rq(gpr) + ; push Rq(gpr.rq().unwrap()) ); - self.block_state.regs.release_scratch_gpr(gpr); + self.block_state.regs.release(gpr); } ValueLocation::Stack(o) => { let offset = self.adjusted_offset(o); @@ -1388,12 +1580,12 @@ impl Context<'_, M> { ); } ValueLocation::Immediate(imm) => { - let gpr = self.block_state.regs.take_scratch_gpr(); + let gpr = self.block_state.regs.take_64(); dynasm!(self.asm - ; mov Rq(gpr), QWORD imm - ; push Rq(gpr) + ; mov Rq(gpr.rq().unwrap()), QWORD imm.as_int().unwrap() + ; push Rq(gpr.rq().unwrap()) ); - self.block_state.regs.release_scratch_gpr(gpr); + self.block_state.regs.release(gpr); } } ValueLocation::Stack(-(self.block_state.depth.0 as i32)) @@ -1433,7 +1625,7 @@ impl Context<'_, M> { fn free_value(&mut self, val: ValueLocation) { match val { ValueLocation::Reg(r) => { - self.block_state.regs.release_scratch_gpr(r); + self.block_state.regs.release(r); } // TODO: Refcounted stack slots _ => {} @@ -1445,7 +1637,7 @@ impl Context<'_, M> { match val { ValueLocation::Reg(r) => r, ValueLocation::Immediate(i) => { - let scratch = self.block_state.regs.take_scratch_gpr(); + let scratch = self.block_state.regs.take_64(); self.immediate_to_reg(scratch, i); scratch } @@ -1454,10 +1646,10 @@ impl Context<'_, M> { // Even better, with an SSE-like `Value` abstraction // we can make it so we only load it once. let offset = self.adjusted_offset(offset); - let scratch = self.block_state.regs.take_scratch_gpr(); + let scratch = self.block_state.regs.take_64(); dynasm!(self.asm - ; mov Rq(scratch), [rsp + offset] + ; mov Rq(scratch.rq().unwrap()), [rsp + offset] ); scratch @@ -1474,10 +1666,10 @@ impl Context<'_, M> { assert_eq!(self.block_state.regs.num_usages(r), 1); r } else { - let new_reg = self.block_state.regs.take_scratch_gpr(); - self.block_state.regs.release_scratch_gpr(r); + let new_reg = self.block_state.regs.take_64(); + self.block_state.regs.release(r); dynasm!(self.asm - ; mov Rq(new_reg), Rq(r) + ; mov Rq(new_reg.rq().unwrap()), Rq(r.rq().unwrap()) ); new_reg } @@ -1525,7 +1717,9 @@ impl Context<'_, M> { if let Some(i1) = op1.immediate() { if let Some(i0) = op0.immediate() { - self.push(ValueLocation::Immediate(i1 - i0)); + self.push(ValueLocation::Immediate( + (i1.as_i64().unwrap() - i0.as_i64().unwrap()).into(), + )); return; } } @@ -1534,19 +1728,20 @@ impl Context<'_, M> { match op0 { ValueLocation::Reg(reg) => { dynasm!(self.asm - ; sub Rq(op1), Rq(reg) + ; sub Rq(op1.rq().unwrap()), Rq(reg.rq().unwrap()) ); } ValueLocation::Stack(offset) => { let offset = self.adjusted_offset(offset); dynasm!(self.asm - ; sub Rq(op1), [rsp + offset] + ; sub Rq(op1.rq().unwrap()), [rsp + offset] ); } ValueLocation::Immediate(i) => { + let i = i.as_int().unwrap(); if let Some(i) = i.try_into() { dynasm!(self.asm - ; sub Rq(op1), i + ; sub Rq(op1.rq().unwrap()), i ); } else { unimplemented!(concat!( @@ -1566,11 +1761,11 @@ impl Context<'_, M> { let op0 = self.pop(); let op1 = self.pop(); - if let Some(i1) = op1.immediate() { - if let Some(i0) = op0.immediate() { + if let Some(i1) = op1.imm_i64() { + if let Some(i0) = op0.imm_i64() { self.block_state .stack - .push(ValueLocation::Immediate(i64::wrapping_mul(i1, i0))); + .push(ValueLocation::Immediate(i64::wrapping_mul(i1, i0).into())); return; } } @@ -1589,19 +1784,20 @@ impl Context<'_, M> { match op0 { ValueLocation::Reg(reg) => { dynasm!(self.asm - ; imul Rq(op1), Rq(reg) + ; imul Rq(op1.rq().unwrap()), Rq(reg.rq().unwrap()) ); } ValueLocation::Stack(offset) => { let offset = self.adjusted_offset(offset); dynasm!(self.asm - ; imul Rq(op1), [rsp + offset] + ; imul Rq(op1.rq().unwrap()), [rsp + offset] ); } ValueLocation::Immediate(i) => { + let i = i.as_int().unwrap(); if let Some(i) = i.try_into() { dynasm!(self.asm - ; imul Rq(op1), Rq(op1), i + ; imul Rq(op1.rq().unwrap()), Rq(op1.rq().unwrap()), i ); } else { unimplemented!(concat!( @@ -1621,11 +1817,11 @@ impl Context<'_, M> { let op0 = self.pop(); let op1 = self.pop(); - if let Some(i1) = op1.immediate() { - if let Some(i0) = op0.immediate() { + if let Some(i1) = op1.imm_i32() { + if let Some(i0) = op0.imm_i32() { self.block_state .stack - .push(ValueLocation::Immediate(i1 - i0)); + .push(ValueLocation::Immediate(i1.wrapping_sub(i0).into())); return; } } @@ -1634,18 +1830,18 @@ impl Context<'_, M> { match op0 { ValueLocation::Reg(reg) => { dynasm!(self.asm - ; sub Rd(op1), Rd(reg) + ; sub Rd(op1.rq().unwrap()), Rd(reg.rq().unwrap()) ); } ValueLocation::Stack(offset) => { let offset = self.adjusted_offset(offset); dynasm!(self.asm - ; sub Rd(op1), [rsp + offset] + ; sub Rd(op1.rq().unwrap()), [rsp + offset] ); } ValueLocation::Immediate(i) => { dynasm!(self.asm - ; sub Rd(op1), i as i32 + ; sub Rd(op1.rq().unwrap()), i.as_i32().unwrap() ); } } @@ -1663,7 +1859,7 @@ impl Context<'_, M> { if let Some(i1) = op1.immediate() { if let Some(i0) = op0.immediate() { self.push(ValueLocation::Immediate( - i32::wrapping_mul(i1 as i32, i0 as i32) as _, + i32::wrapping_mul(i1.as_i32().unwrap(), i0.as_i32().unwrap()).into(), )); return; } @@ -1683,18 +1879,18 @@ impl Context<'_, M> { match op0 { ValueLocation::Reg(reg) => { dynasm!(self.asm - ; imul Rd(op1), Rd(reg) + ; imul Rd(op1.rq().unwrap()), Rd(reg.rq().unwrap()) ); } ValueLocation::Stack(offset) => { let offset = self.adjusted_offset(offset); dynasm!(self.asm - ; imul Rd(op1), [rsp + offset] + ; imul Rd(op1.rq().unwrap()), [rsp + offset] ); } ValueLocation::Immediate(i) => { dynasm!(self.asm - ; imul Rd(op1), Rd(op1), i as i32 + ; imul Rd(op1.rq().unwrap()), Rd(op1.rq().unwrap()), i.as_i32().unwrap() ); } } @@ -1710,7 +1906,7 @@ impl Context<'_, M> { match cond { ValueLocation::Immediate(i) => { - if i == 0 { + if i.as_i32().unwrap() == 0 { self.push(else_); } else { self.push(then); @@ -1722,26 +1918,26 @@ impl Context<'_, M> { let reg = self.into_reg(other); dynasm!(self.asm - ; test Rd(reg), Rd(reg) + ; test Rd(reg.rq().unwrap()), Rd(reg.rq().unwrap()) ); - self.block_state.regs.release_scratch_gpr(reg); + self.block_state.regs.release(reg); } } - let out = self.block_state.regs.take_scratch_gpr(); + let out = self.block_state.regs.take_64(); // TODO: Can do this better for variables on stack let reg = self.into_reg(else_); dynasm!(self.asm - ; cmovz Rq(out), Rq(reg) + ; cmovz Rq(out.rq().unwrap()), Rq(reg.rq().unwrap()) ); - self.block_state.regs.release_scratch_gpr(reg); + self.block_state.regs.release(reg); let reg = self.into_reg(then); dynasm!(self.asm - ; cmovnz Rq(out), Rq(reg) + ; cmovnz Rq(out.rq().unwrap()), Rq(reg.rq().unwrap()) ); - self.block_state.regs.release_scratch_gpr(reg); + self.block_state.regs.release(reg); self.push(ValueLocation::Reg(out)); } @@ -1760,11 +1956,7 @@ impl Context<'_, M> { self.block_state.stack.push(v); } - pub fn i32_literal(&mut self, imm: i32) { - self.push(ValueLocation::Immediate(imm as _)); - } - - pub fn i64_literal(&mut self, imm: i64) { + pub fn literal(&mut self, imm: Value) { self.push(ValueLocation::Immediate(imm)); } @@ -1844,9 +2036,9 @@ impl Context<'_, M> { } else { let gpr = self.into_reg(val); dynasm!(self.asm - ; mov [rsp + offset], Rq(gpr) + ; mov [rsp + offset], Rq(gpr.rq().unwrap()) ); - self.block_state.regs.release_scratch_gpr(gpr); + self.block_state.regs.release(gpr); } } CCLoc::Reg(r) => { @@ -1917,11 +2109,11 @@ impl Context<'_, M> { let callee = self.pop(); let callee = self.into_temp_reg(callee); - let temp0 = self.block_state.regs.take_scratch_gpr(); + let temp0 = self.block_state.regs.take_64(); for &loc in &locs { if let CCLoc::Reg(r) = loc { - self.block_state.regs.release_scratch_gpr(r); + self.block_state.regs.release(r); } } @@ -1931,13 +2123,13 @@ impl Context<'_, M> { // TODO: Consider generating a single trap function and jumping to that instead. dynasm!(self.asm - ; cmp Rd(callee), [Rq(VMCTX) + self.module_context.offset_of_funcs_len() as i32] + ; cmp Rd(callee.rq().unwrap()), [Rq(VMCTX) + self.module_context.offset_of_funcs_len() as i32] ; jae =>fail - ; imul Rd(callee), Rd(callee), mem::size_of::() as i32 - ; mov Rq(temp0), [Rq(VMCTX) + self.module_context.offset_of_funcs_ptr() as i32] + ; imul Rd(callee.rq().unwrap()), Rd(callee.rq().unwrap()), mem::size_of::() as i32 + ; mov Rq(temp0.rq().unwrap()), [Rq(VMCTX) + self.module_context.offset_of_funcs_ptr() as i32] ; cmp DWORD [ - Rq(temp0) + - Rq(callee) + + Rq(temp0.rq().unwrap()) + + Rq(callee.rq().unwrap()) + RuntimeFunc::offset_of_sig_hash() as i32 ], signature_hash as i32 ; jne =>fail @@ -1945,14 +2137,14 @@ impl Context<'_, M> { dynasm!(self.asm ; call QWORD [ - Rq(temp0) + - Rq(callee) + + Rq(temp0.rq().unwrap()) + + Rq(callee.rq().unwrap()) + RuntimeFunc::offset_of_func_start() as i32 ] ); - self.block_state.regs.release_scratch_gpr(temp0); - self.block_state.regs.release_scratch_gpr(callee); + self.block_state.regs.release(temp0); + self.block_state.regs.release(callee); self.push_function_return(return_arity); } diff --git a/src/function_body.rs b/src/function_body.rs index 3a624635a3..22ceade833 100644 --- a/src/function_body.rs +++ b/src/function_body.rs @@ -316,8 +316,7 @@ where Operator::Ctz(Size::_64) => ctx.i64_ctz(), Operator::Popcnt(Size::_64) => ctx.i64_popcnt(), Operator::Drop(range) => ctx.drop(range), - Operator::Const(Value::I32(value)) => ctx.i32_literal(value), - Operator::Const(Value::I64(value)) => ctx.i64_literal(value), + Operator::Const(val) => ctx.literal(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)?, diff --git a/src/lib.rs b/src/lib.rs index 92fd5746c6..fe849e6897 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,8 +9,8 @@ #![plugin(dynasm)] extern crate test; - -extern crate arrayvec; +#[macro_use] +extern crate smallvec; extern crate capstone; extern crate either; extern crate failure; diff --git a/src/microwasm.rs b/src/microwasm.rs index 96c900ffa5..934005e728 100644 --- a/src/microwasm.rs +++ b/src/microwasm.rs @@ -1,4 +1,5 @@ use crate::module::ModuleContext; +use smallvec::SmallVec; use std::{ fmt, iter::{self, FromIterator}, @@ -63,6 +64,56 @@ impl fmt::Display for Value { } impl Value { + pub fn as_int(self) -> Option { + self.as_i64().or_else(|| self.as_i32().map(|i| i as _)) + } + + pub fn as_bytes(self) -> i64 { + match self { + Value::I32(val) => val as _, + Value::I64(val) => val, + Value::F32(val) => val.0 as _, + Value::F64(val) => val.0 as _, + } + } + + pub fn as_i32(self) -> Option { + match self { + Value::I32(val) => Some(val), + _ => None, + } + } + + pub fn as_i64(self) -> Option { + match self { + Value::I64(val) => Some(val), + _ => None, + } + } + + pub fn as_f32(self) -> Option { + match self { + Value::F32(val) => Some(val), + _ => None, + } + } + + pub fn as_f64(self) -> Option { + match self { + Value::F64(val) => Some(val), + _ => None, + } + } + + pub fn type_(&self) -> SignlessType { + match self { + Value::I32(_) => Type::Int(Size::_32), + Value::I64(_) => Type::Int(Size::_64), + Value::F32(Ieee32(_)) => Type::Float(Size::_32), + Value::F64(Ieee64(_)) => Type::Float(Size::_64), + } + } + fn default_for_type(ty: SignlessType) -> Self { match ty { Type::Int(Size::_32) => Value::I32(0), @@ -73,6 +124,37 @@ impl Value { } } +impl From for Value { + fn from(other: i32) -> Self { + Value::I32(other) + } +} +impl From for Value { + fn from(other: i64) -> Self { + Value::I64(other) + } +} +impl From for Value { + fn from(other: u32) -> Self { + Value::I32(other as _) + } +} +impl From for Value { + fn from(other: u64) -> Self { + Value::I64(other as _) + } +} +impl From for Value { + fn from(other: Ieee32) -> Self { + Value::F32(other) + } +} +impl From for Value { + fn from(other: Ieee64) -> Self { + Value::F64(other) + } +} + /// Whether to interpret an integer as signed or unsigned #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum Signedness { @@ -697,19 +779,20 @@ impl From for SigT { #[derive(Debug)] pub struct OpSig { - input: Vec, - output: Vec, + input: SmallVec<[SigT; 3]>, + output: SmallVec<[SigT; 3]>, } impl OpSig { + #[inline(always)] fn new(input: I0, output: I1) -> Self where I0: IntoIterator, I1: IntoIterator, { OpSig { - input: Vec::from_iter(input), - output: Vec::from_iter(output), + input: SmallVec::from_iter(input), + output: SmallVec::from_iter(output), } } @@ -741,6 +824,7 @@ where use self::SigT::T; use std::iter::{empty as none, once}; + #[inline(always)] fn one(a: A) -> impl IntoIterator where A: Into, @@ -748,6 +832,7 @@ where once(a.into()) } + #[inline(always)] fn two(a: A, b: B) -> impl IntoIterator where A: Into, @@ -756,6 +841,7 @@ where once(a.into()).chain(once(b.into())) } + #[inline(always)] fn three(a: A, b: B, c: C) -> impl IntoIterator where A: Into, @@ -1124,10 +1210,9 @@ impl<'a, 'b, M: ModuleContext> Iterator for MicrowasmConv<'a, 'b, M> where for<'any> &'any M::Signature: Into, { - type Item = wasmparser::Result>; + type Item = wasmparser::Result>; - // TODO: We don't need to use vec here, we can maybe use `ArrayVec` or `Option`+`chain` - fn next(&mut self) -> Option>> { + fn next(&mut self) -> Option>> { macro_rules! to_drop { ($block:expr) => {{ let first_non_local_depth = $block.returns; @@ -1154,7 +1239,7 @@ where return Some(Ok(consts .into_iter() .map(|value| Operator::Const(value)) - .collect::>())); + .collect())); } if self.unreachable { @@ -1185,7 +1270,7 @@ where *has_else = true; } - break vec![Operator::Label((block.id, NameTag::Else))]; + break smallvec![Operator::Label((block.id, NameTag::Else))]; } } WasmOperator::End => { @@ -1209,7 +1294,7 @@ where { self.stack = block.params().unwrap().to_vec(); - break vec![ + break smallvec![ Operator::Label((block.id, NameTag::Else)), Operator::Br { target: BrTarget::Label(end_label), @@ -1217,7 +1302,7 @@ where Operator::Label(end_label), ]; } else { - break vec![Operator::Label((block.id, NameTag::End))]; + break smallvec![Operator::Label((block.id, NameTag::End))]; } } else { depth -= 1; @@ -1240,9 +1325,9 @@ where Some(Ok(match op { WasmOperator::Unreachable => { self.unreachable = true; - vec![Operator::Unreachable] + smallvec![Operator::Unreachable] } - WasmOperator::Nop => vec![], + WasmOperator::Nop => smallvec![], WasmOperator::Block { ty } => { let id = self.next_id(); self.control_frames.push(ControlFrame { @@ -1256,7 +1341,7 @@ where needs_end_label: false, }, }); - vec![Operator::end( + smallvec![Operator::end( self.block_params_with_wasm_type(ty), (id, NameTag::End), )] @@ -1273,7 +1358,7 @@ where kind: ControlFrameKind::Loop, }); let label = (id, NameTag::Header); - vec![ + smallvec![ Operator::loop_(self.block_params(), label), Operator::end(self.block_params_with_wasm_type(ty), (id, NameTag::End)), Operator::Br { @@ -1302,7 +1387,7 @@ where (id, NameTag::Else), (id, NameTag::End), ); - vec![ + smallvec![ Operator::block(self.block_params(), then), Operator::block(self.block_params(), else_), Operator::end(self.block_params_with_wasm_type(ty), end), @@ -1326,7 +1411,7 @@ where let label = (block.id, NameTag::Else); - Vec::from_iter( + SmallVec::from_iter( to_drop .into_iter() .map(Operator::Drop) @@ -1357,7 +1442,7 @@ where to_drop .map(Operator::Drop) .into_iter() - .chain(vec![ + .chain::>(smallvec![ Operator::Br { target: BrTarget::Label(else_), }, @@ -1369,7 +1454,7 @@ where ]) .collect() } else { - Vec::from_iter(if self.control_frames.is_empty() { + SmallVec::from_iter(if self.control_frames.is_empty() { self.is_done = true; None.into_iter() @@ -1405,7 +1490,7 @@ where let block = self.nth_block_mut(relative_depth as _); block.mark_branched_to(); - Vec::from_iter(to_drop.into_iter().map(Operator::Drop).chain(iter::once( + SmallVec::from_iter(to_drop.into_iter().map(Operator::Drop).chain(iter::once( Operator::Br { target: block.br_target(), }, @@ -1424,7 +1509,7 @@ where // us to generate a spurious `jmp`. unimplemented!() } else { - vec![ + smallvec![ Operator::block(params, label), Operator::BrIf { then: block.br_target(), @@ -1441,212 +1526,214 @@ where let block = self.function_block(); let to_drop = to_drop!(block); - Vec::from_iter(to_drop.into_iter().map(Operator::Drop).chain(iter::once( + SmallVec::from_iter(to_drop.into_iter().map(Operator::Drop).chain(iter::once( Operator::Br { target: block.br_target(), }, ))) } - WasmOperator::Call { function_index } => vec![Operator::Call { function_index }], - WasmOperator::CallIndirect { index, table_index } => vec![Operator::CallIndirect { - type_index: index, - table_index, - }], - WasmOperator::Drop => vec![Operator::Drop(0..=0)], - WasmOperator::Select => vec![Operator::Select], + WasmOperator::Call { function_index } => smallvec![Operator::Call { function_index }], + WasmOperator::CallIndirect { index, table_index } => { + smallvec![Operator::CallIndirect { + type_index: index, + table_index, + }] + } + WasmOperator::Drop => smallvec![Operator::Drop(0..=0)], + WasmOperator::Select => smallvec![Operator::Select], WasmOperator::GetLocal { local_index } => { // TODO: `- 1` because we apply the stack difference _before_ this point let depth = self.local_depth(local_index) - 1; - vec![Operator::Pick { depth }] + smallvec![Operator::Pick { depth }] } WasmOperator::SetLocal { local_index } => { // TODO: `+ 1` because we apply the stack difference _before_ this point let depth = self.local_depth(local_index) + 1; - vec![Operator::Swap { depth }, Operator::Drop(0..=0)] + smallvec![Operator::Swap { depth }, Operator::Drop(0..=0)] } WasmOperator::TeeLocal { local_index } => { let depth = self.local_depth(local_index); - vec![ + smallvec![ Operator::Swap { depth }, Operator::Drop(0..=0), Operator::Pick { depth: depth - 1 }, ] } - WasmOperator::I32Load { memarg } => vec![Operator::Load { ty: I32, memarg }], - WasmOperator::I64Load { memarg } => vec![Operator::Load { ty: I64, memarg }], - WasmOperator::F32Load { memarg } => vec![Operator::Load { ty: F32, memarg }], - WasmOperator::F64Load { memarg } => vec![Operator::Load { ty: F64, memarg }], - WasmOperator::I32Load8S { memarg } => vec![Operator::Load8 { + WasmOperator::I32Load { memarg } => smallvec![Operator::Load { ty: I32, memarg }], + WasmOperator::I64Load { memarg } => smallvec![Operator::Load { ty: I64, memarg }], + WasmOperator::F32Load { memarg } => smallvec![Operator::Load { ty: F32, memarg }], + WasmOperator::F64Load { memarg } => smallvec![Operator::Load { ty: F64, memarg }], + WasmOperator::I32Load8S { memarg } => smallvec![Operator::Load8 { ty: sint::I32, memarg, }], - WasmOperator::I32Load8U { memarg } => vec![Operator::Load8 { + WasmOperator::I32Load8U { memarg } => smallvec![Operator::Load8 { ty: sint::U32, memarg, }], - WasmOperator::I32Load16S { memarg } => vec![Operator::Load16 { + WasmOperator::I32Load16S { memarg } => smallvec![Operator::Load16 { ty: sint::I32, memarg, }], - WasmOperator::I32Load16U { memarg } => vec![Operator::Load16 { + WasmOperator::I32Load16U { memarg } => smallvec![Operator::Load16 { ty: sint::U32, memarg, }], - WasmOperator::I64Load8S { memarg } => vec![Operator::Load8 { + WasmOperator::I64Load8S { memarg } => smallvec![Operator::Load8 { ty: sint::I64, memarg, }], - WasmOperator::I64Load8U { memarg } => vec![Operator::Load8 { + WasmOperator::I64Load8U { memarg } => smallvec![Operator::Load8 { ty: sint::U64, memarg, }], - WasmOperator::I64Load16S { memarg } => vec![Operator::Load16 { + WasmOperator::I64Load16S { memarg } => smallvec![Operator::Load16 { ty: sint::I64, memarg, }], - WasmOperator::I64Load16U { memarg } => vec![Operator::Load16 { + WasmOperator::I64Load16U { memarg } => smallvec![Operator::Load16 { ty: sint::U64, memarg, }], - WasmOperator::I64Load32S { memarg } => vec![Operator::Load32 { + WasmOperator::I64Load32S { memarg } => smallvec![Operator::Load32 { sign: Signedness::Signed, memarg, }], - WasmOperator::I64Load32U { memarg } => vec![Operator::Load32 { + WasmOperator::I64Load32U { memarg } => smallvec![Operator::Load32 { sign: Signedness::Unsigned, memarg, }], - WasmOperator::I32Store { memarg } => vec![Operator::Store { ty: I32, memarg }], - WasmOperator::I64Store { memarg } => vec![Operator::Store { ty: I64, memarg }], - WasmOperator::F32Store { memarg } => vec![Operator::Store { ty: F32, memarg }], - WasmOperator::F64Store { memarg } => vec![Operator::Store { ty: F64, memarg }], + WasmOperator::I32Store { memarg } => smallvec![Operator::Store { ty: I32, memarg }], + WasmOperator::I64Store { memarg } => smallvec![Operator::Store { ty: I64, memarg }], + WasmOperator::F32Store { memarg } => smallvec![Operator::Store { ty: F32, memarg }], + WasmOperator::F64Store { memarg } => smallvec![Operator::Store { ty: F64, memarg }], - WasmOperator::I32Store8 { memarg } => vec![Operator::Store8 { + WasmOperator::I32Store8 { memarg } => smallvec![Operator::Store8 { ty: Size::_32, memarg, }], - WasmOperator::I32Store16 { memarg } => vec![Operator::Store16 { + WasmOperator::I32Store16 { memarg } => smallvec![Operator::Store16 { ty: Size::_32, memarg, }], - WasmOperator::I64Store8 { memarg } => vec![Operator::Store8 { + WasmOperator::I64Store8 { memarg } => smallvec![Operator::Store8 { ty: Size::_64, memarg, }], - WasmOperator::I64Store16 { memarg } => vec![Operator::Store16 { + WasmOperator::I64Store16 { memarg } => smallvec![Operator::Store16 { ty: Size::_64, memarg, }], - WasmOperator::I64Store32 { memarg } => vec![Operator::Store32 { memarg }], - WasmOperator::MemorySize { reserved } => vec![Operator::MemorySize { reserved }], - WasmOperator::MemoryGrow { reserved } => vec![Operator::MemoryGrow { reserved }], - WasmOperator::I32Const { value } => vec![Operator::Const(Value::I32(value))], - WasmOperator::I64Const { value } => vec![Operator::Const(Value::I64(value))], - WasmOperator::F32Const { value } => vec![Operator::Const(Value::F32(value))], - WasmOperator::F64Const { value } => vec![Operator::Const(Value::F64(value))], + WasmOperator::I64Store32 { memarg } => smallvec![Operator::Store32 { memarg }], + WasmOperator::MemorySize { reserved } => smallvec![Operator::MemorySize { reserved }], + WasmOperator::MemoryGrow { reserved } => smallvec![Operator::MemoryGrow { reserved }], + WasmOperator::I32Const { value } => smallvec![Operator::Const(Value::I32(value))], + WasmOperator::I64Const { value } => smallvec![Operator::Const(Value::I64(value))], + WasmOperator::F32Const { value } => smallvec![Operator::Const(Value::F32(value))], + WasmOperator::F64Const { value } => smallvec![Operator::Const(Value::F64(value))], WasmOperator::RefNull => unimplemented!(), WasmOperator::RefIsNull => unimplemented!(), - WasmOperator::I32Eqz => vec![Operator::Eqz(Size::_32)], - WasmOperator::I32Eq => vec![Operator::Eq(I32)], - WasmOperator::I32Ne => vec![Operator::Ne(I32)], - WasmOperator::I32LtS => vec![Operator::Lt(SI32)], - WasmOperator::I32LtU => vec![Operator::Lt(SU32)], - WasmOperator::I32GtS => vec![Operator::Gt(SI32)], - WasmOperator::I32GtU => vec![Operator::Gt(SU32)], - WasmOperator::I32LeS => vec![Operator::Le(SI32)], - WasmOperator::I32LeU => vec![Operator::Le(SU32)], - WasmOperator::I32GeS => vec![Operator::Ge(SI32)], - WasmOperator::I32GeU => vec![Operator::Ge(SU32)], - WasmOperator::I64Eqz => vec![Operator::Eqz(Size::_64)], - WasmOperator::I64Eq => vec![Operator::Eq(I64)], - WasmOperator::I64Ne => vec![Operator::Ne(I64)], - WasmOperator::I64LtS => vec![Operator::Lt(SI64)], - WasmOperator::I64LtU => vec![Operator::Lt(SU64)], - WasmOperator::I64GtS => vec![Operator::Gt(SI64)], - WasmOperator::I64GtU => vec![Operator::Gt(SU64)], - WasmOperator::I64LeS => vec![Operator::Le(SI64)], - WasmOperator::I64LeU => vec![Operator::Le(SU64)], - WasmOperator::I64GeS => vec![Operator::Ge(SI64)], - WasmOperator::I64GeU => vec![Operator::Ge(SU64)], - WasmOperator::F32Eq => vec![Operator::Eq(F32)], - WasmOperator::F32Ne => vec![Operator::Ne(F32)], - WasmOperator::F32Lt => vec![Operator::Lt(SF32)], - WasmOperator::F32Gt => vec![Operator::Gt(SF32)], - WasmOperator::F32Le => vec![Operator::Le(SF32)], - WasmOperator::F32Ge => vec![Operator::Ge(SF32)], - WasmOperator::F64Eq => vec![Operator::Eq(F64)], - WasmOperator::F64Ne => vec![Operator::Ne(F64)], - WasmOperator::F64Lt => vec![Operator::Lt(SF64)], - WasmOperator::F64Gt => vec![Operator::Gt(SF64)], - WasmOperator::F64Le => vec![Operator::Le(SF64)], - WasmOperator::F64Ge => vec![Operator::Ge(SF64)], - WasmOperator::I32Clz => vec![Operator::Clz(Size::_32)], - WasmOperator::I32Ctz => vec![Operator::Ctz(Size::_32)], - WasmOperator::I32Popcnt => vec![Operator::Popcnt(Size::_32)], - WasmOperator::I32Add => vec![Operator::Add(I32)], - WasmOperator::I32Sub => vec![Operator::Sub(I32)], - WasmOperator::I32Mul => vec![Operator::Mul(I32)], - WasmOperator::I32DivS => vec![Operator::Div(SI32)], - WasmOperator::I32DivU => vec![Operator::Div(SU32)], - WasmOperator::I32RemS => vec![Operator::Rem(sint::I32)], - WasmOperator::I32RemU => vec![Operator::Rem(sint::U32)], - WasmOperator::I32And => vec![Operator::And(Size::_32)], - WasmOperator::I32Or => vec![Operator::Or(Size::_32)], - WasmOperator::I32Xor => vec![Operator::Xor(Size::_32)], - WasmOperator::I32Shl => vec![Operator::Shl(Size::_32)], - WasmOperator::I32ShrS => vec![Operator::Shr(sint::I32)], - WasmOperator::I32ShrU => vec![Operator::Shr(sint::U32)], - WasmOperator::I32Rotl => vec![Operator::Rotl(Size::_32)], - WasmOperator::I32Rotr => vec![Operator::Rotr(Size::_32)], - WasmOperator::I64Clz => vec![Operator::Clz(Size::_64)], - WasmOperator::I64Ctz => vec![Operator::Ctz(Size::_64)], - WasmOperator::I64Popcnt => vec![Operator::Popcnt(Size::_64)], - WasmOperator::I64Add => vec![Operator::Add(I64)], - WasmOperator::I64Sub => vec![Operator::Sub(I64)], - WasmOperator::I64Mul => vec![Operator::Mul(I64)], - WasmOperator::I64DivS => vec![Operator::Div(SI64)], - WasmOperator::I64DivU => vec![Operator::Div(SU64)], - WasmOperator::I64RemS => vec![Operator::Rem(sint::I64)], - WasmOperator::I64RemU => vec![Operator::Rem(sint::U64)], - WasmOperator::I64And => vec![Operator::And(Size::_64)], - WasmOperator::I64Or => vec![Operator::Or(Size::_64)], - WasmOperator::I64Xor => vec![Operator::Xor(Size::_64)], - WasmOperator::I64Shl => vec![Operator::Shl(Size::_64)], - WasmOperator::I64ShrS => vec![Operator::Shr(sint::I64)], - WasmOperator::I64ShrU => vec![Operator::Shr(sint::U64)], - WasmOperator::I64Rotl => vec![Operator::Rotl(Size::_64)], - WasmOperator::I64Rotr => vec![Operator::Rotr(Size::_64)], - WasmOperator::F32Abs => vec![Operator::Abs(Size::_32)], - WasmOperator::F32Neg => vec![Operator::Neg(Size::_32)], - WasmOperator::F32Ceil => vec![Operator::Ceil(Size::_32)], - WasmOperator::F32Floor => vec![Operator::Floor(Size::_32)], - WasmOperator::F32Trunc => vec![Operator::Trunc(Size::_32)], - WasmOperator::F32Nearest => vec![Operator::Nearest(Size::_32)], - WasmOperator::F32Sqrt => vec![Operator::Sqrt(Size::_32)], - WasmOperator::F32Add => vec![Operator::Add(F32)], - WasmOperator::F32Sub => vec![Operator::Sub(F32)], - WasmOperator::F32Mul => vec![Operator::Mul(F32)], - WasmOperator::F32Div => vec![Operator::Div(SF32)], - WasmOperator::F32Min => vec![Operator::Min(Size::_32)], - WasmOperator::F32Max => vec![Operator::Max(Size::_32)], - WasmOperator::F32Copysign => vec![Operator::Copysign(Size::_32)], - WasmOperator::F64Abs => vec![Operator::Abs(Size::_64)], - WasmOperator::F64Neg => vec![Operator::Neg(Size::_64)], - WasmOperator::F64Ceil => vec![Operator::Ceil(Size::_64)], - WasmOperator::F64Floor => vec![Operator::Floor(Size::_64)], - WasmOperator::F64Trunc => vec![Operator::Trunc(Size::_64)], - WasmOperator::F64Nearest => vec![Operator::Nearest(Size::_64)], - WasmOperator::F64Sqrt => vec![Operator::Sqrt(Size::_64)], - WasmOperator::F64Add => vec![Operator::Add(F64)], - WasmOperator::F64Sub => vec![Operator::Sub(F64)], - WasmOperator::F64Mul => vec![Operator::Mul(F64)], - WasmOperator::F64Div => vec![Operator::Div(SF64)], - WasmOperator::F64Min => vec![Operator::Min(Size::_64)], - WasmOperator::F64Max => vec![Operator::Max(Size::_64)], - WasmOperator::F64Copysign => vec![Operator::Copysign(Size::_64)], + WasmOperator::I32Eqz => smallvec![Operator::Eqz(Size::_32)], + WasmOperator::I32Eq => smallvec![Operator::Eq(I32)], + WasmOperator::I32Ne => smallvec![Operator::Ne(I32)], + WasmOperator::I32LtS => smallvec![Operator::Lt(SI32)], + WasmOperator::I32LtU => smallvec![Operator::Lt(SU32)], + WasmOperator::I32GtS => smallvec![Operator::Gt(SI32)], + WasmOperator::I32GtU => smallvec![Operator::Gt(SU32)], + WasmOperator::I32LeS => smallvec![Operator::Le(SI32)], + WasmOperator::I32LeU => smallvec![Operator::Le(SU32)], + WasmOperator::I32GeS => smallvec![Operator::Ge(SI32)], + WasmOperator::I32GeU => smallvec![Operator::Ge(SU32)], + WasmOperator::I64Eqz => smallvec![Operator::Eqz(Size::_64)], + WasmOperator::I64Eq => smallvec![Operator::Eq(I64)], + WasmOperator::I64Ne => smallvec![Operator::Ne(I64)], + WasmOperator::I64LtS => smallvec![Operator::Lt(SI64)], + WasmOperator::I64LtU => smallvec![Operator::Lt(SU64)], + WasmOperator::I64GtS => smallvec![Operator::Gt(SI64)], + WasmOperator::I64GtU => smallvec![Operator::Gt(SU64)], + WasmOperator::I64LeS => smallvec![Operator::Le(SI64)], + WasmOperator::I64LeU => smallvec![Operator::Le(SU64)], + WasmOperator::I64GeS => smallvec![Operator::Ge(SI64)], + WasmOperator::I64GeU => smallvec![Operator::Ge(SU64)], + WasmOperator::F32Eq => smallvec![Operator::Eq(F32)], + WasmOperator::F32Ne => smallvec![Operator::Ne(F32)], + WasmOperator::F32Lt => smallvec![Operator::Lt(SF32)], + WasmOperator::F32Gt => smallvec![Operator::Gt(SF32)], + WasmOperator::F32Le => smallvec![Operator::Le(SF32)], + WasmOperator::F32Ge => smallvec![Operator::Ge(SF32)], + WasmOperator::F64Eq => smallvec![Operator::Eq(F64)], + WasmOperator::F64Ne => smallvec![Operator::Ne(F64)], + WasmOperator::F64Lt => smallvec![Operator::Lt(SF64)], + WasmOperator::F64Gt => smallvec![Operator::Gt(SF64)], + WasmOperator::F64Le => smallvec![Operator::Le(SF64)], + WasmOperator::F64Ge => smallvec![Operator::Ge(SF64)], + WasmOperator::I32Clz => smallvec![Operator::Clz(Size::_32)], + WasmOperator::I32Ctz => smallvec![Operator::Ctz(Size::_32)], + WasmOperator::I32Popcnt => smallvec![Operator::Popcnt(Size::_32)], + WasmOperator::I32Add => smallvec![Operator::Add(I32)], + WasmOperator::I32Sub => smallvec![Operator::Sub(I32)], + WasmOperator::I32Mul => smallvec![Operator::Mul(I32)], + WasmOperator::I32DivS => smallvec![Operator::Div(SI32)], + WasmOperator::I32DivU => smallvec![Operator::Div(SU32)], + WasmOperator::I32RemS => smallvec![Operator::Rem(sint::I32)], + WasmOperator::I32RemU => smallvec![Operator::Rem(sint::U32)], + WasmOperator::I32And => smallvec![Operator::And(Size::_32)], + WasmOperator::I32Or => smallvec![Operator::Or(Size::_32)], + WasmOperator::I32Xor => smallvec![Operator::Xor(Size::_32)], + WasmOperator::I32Shl => smallvec![Operator::Shl(Size::_32)], + WasmOperator::I32ShrS => smallvec![Operator::Shr(sint::I32)], + WasmOperator::I32ShrU => smallvec![Operator::Shr(sint::U32)], + WasmOperator::I32Rotl => smallvec![Operator::Rotl(Size::_32)], + WasmOperator::I32Rotr => smallvec![Operator::Rotr(Size::_32)], + WasmOperator::I64Clz => smallvec![Operator::Clz(Size::_64)], + WasmOperator::I64Ctz => smallvec![Operator::Ctz(Size::_64)], + WasmOperator::I64Popcnt => smallvec![Operator::Popcnt(Size::_64)], + WasmOperator::I64Add => smallvec![Operator::Add(I64)], + WasmOperator::I64Sub => smallvec![Operator::Sub(I64)], + WasmOperator::I64Mul => smallvec![Operator::Mul(I64)], + WasmOperator::I64DivS => smallvec![Operator::Div(SI64)], + WasmOperator::I64DivU => smallvec![Operator::Div(SU64)], + WasmOperator::I64RemS => smallvec![Operator::Rem(sint::I64)], + WasmOperator::I64RemU => smallvec![Operator::Rem(sint::U64)], + WasmOperator::I64And => smallvec![Operator::And(Size::_64)], + WasmOperator::I64Or => smallvec![Operator::Or(Size::_64)], + WasmOperator::I64Xor => smallvec![Operator::Xor(Size::_64)], + WasmOperator::I64Shl => smallvec![Operator::Shl(Size::_64)], + WasmOperator::I64ShrS => smallvec![Operator::Shr(sint::I64)], + WasmOperator::I64ShrU => smallvec![Operator::Shr(sint::U64)], + WasmOperator::I64Rotl => smallvec![Operator::Rotl(Size::_64)], + WasmOperator::I64Rotr => smallvec![Operator::Rotr(Size::_64)], + WasmOperator::F32Abs => smallvec![Operator::Abs(Size::_32)], + WasmOperator::F32Neg => smallvec![Operator::Neg(Size::_32)], + WasmOperator::F32Ceil => smallvec![Operator::Ceil(Size::_32)], + WasmOperator::F32Floor => smallvec![Operator::Floor(Size::_32)], + WasmOperator::F32Trunc => smallvec![Operator::Trunc(Size::_32)], + WasmOperator::F32Nearest => smallvec![Operator::Nearest(Size::_32)], + WasmOperator::F32Sqrt => smallvec![Operator::Sqrt(Size::_32)], + WasmOperator::F32Add => smallvec![Operator::Add(F32)], + WasmOperator::F32Sub => smallvec![Operator::Sub(F32)], + WasmOperator::F32Mul => smallvec![Operator::Mul(F32)], + WasmOperator::F32Div => smallvec![Operator::Div(SF32)], + WasmOperator::F32Min => smallvec![Operator::Min(Size::_32)], + WasmOperator::F32Max => smallvec![Operator::Max(Size::_32)], + WasmOperator::F32Copysign => smallvec![Operator::Copysign(Size::_32)], + WasmOperator::F64Abs => smallvec![Operator::Abs(Size::_64)], + WasmOperator::F64Neg => smallvec![Operator::Neg(Size::_64)], + WasmOperator::F64Ceil => smallvec![Operator::Ceil(Size::_64)], + WasmOperator::F64Floor => smallvec![Operator::Floor(Size::_64)], + WasmOperator::F64Trunc => smallvec![Operator::Trunc(Size::_64)], + WasmOperator::F64Nearest => smallvec![Operator::Nearest(Size::_64)], + WasmOperator::F64Sqrt => smallvec![Operator::Sqrt(Size::_64)], + WasmOperator::F64Add => smallvec![Operator::Add(F64)], + WasmOperator::F64Sub => smallvec![Operator::Sub(F64)], + WasmOperator::F64Mul => smallvec![Operator::Mul(F64)], + WasmOperator::F64Div => smallvec![Operator::Div(SF64)], + WasmOperator::F64Min => smallvec![Operator::Min(Size::_64)], + WasmOperator::F64Max => smallvec![Operator::Max(Size::_64)], + WasmOperator::F64Copysign => smallvec![Operator::Copysign(Size::_64)], WasmOperator::I32WrapI64 => unimplemented!(), WasmOperator::I32TruncSF32 => unimplemented!(), WasmOperator::I32TruncUF32 => unimplemented!(), diff --git a/src/tests.rs b/src/tests.rs index 298f1adfaf..d68290e22d 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -729,19 +729,70 @@ fn brif_block_passthru() { assert_eq!(execute_wat(code, 0, 3), 6); } -#[test] -fn literals() { - let code = r#" -(module - (func (param i32) (param i32) (result i32) - (i32.const 228) - ) -) - "#; +quickcheck! { + #[test] + fn literals(a: i32, b: i64, c: i32, d: i64) -> bool { + let code = format!(r#" + (module + (func (result i32) + (i32.const {}) + ) + (func (result i64) + (i64.const {}) + ) + (func (result f32) + (f32.const {}) + ) + (func (result f64) + (f64.const {}) + ) + ) + "#, a, b, c, d); + + let translated = translate_wat(&code); + + assert_eq!(translated.execute_func::<(), i32>(0, ()), Ok(a)); + assert_eq!(translated.execute_func::<(), i64>(1, ()), Ok(b)); + assert_eq!(translated.execute_func::<(), f32>(2, ()), Ok(c as _)); + assert_eq!(translated.execute_func::<(), f64>(3, ()), Ok(d as _)); - assert_eq!(execute_wat(code, 0, 0), 228); + true + } } +quickcheck! { + #[test] + fn params(a: i32, b: i64, c: i32, d: i64) -> bool { + let code = r#" + (module + (func (param i32) (param i64) (param f32) (param f64) (result i32) + (get_local 0) + ) + (func (param i32) (param i64) (param f32) (param f64) (result i64) + (get_local 1) + ) + (func (param i32) (param i64) (param f32) (param f64) (result f32) + (get_local 2) + ) + (func (param i32) (param i64) (param f32) (param f64) (result f64) + (get_local 3) + ) + ) + "#; + + let c = c as f32; + let d = d as f64; + + let translated = translate_wat(&code); + + assert_eq!(translated.execute_func::<(i32, i64, f32, f64), i32>(0, (a, b, c, d)), Ok(a)); + assert_eq!(translated.execute_func::<(i32, i64, f32, f64), i64>(1, (a, b, c, d)), Ok(b)); + assert_eq!(translated.execute_func::<(i32, i64, f32, f64), f32>(2, (a, b, c, d)), Ok(c)); + assert_eq!(translated.execute_func::<(i32, i64, f32, f64), f64>(3, (a, b, c, d)), Ok(d)); + + true + } +} #[test] fn wrong_type() { let code = r#" @@ -1094,7 +1145,7 @@ macro_rules! test_select { fn lit(cond: bool, then: $ty, else_: $ty) -> bool { let icond: i32 = if cond { 1 } else { 0 }; - let translated = translate_wat(&format!(" + let translated = translate_wat(&format!(" (module (func (param {ty}) (param {ty}) (result {ty}) (select (get_local 0) (get_local 1) (i32.const {val})))) ", @@ -1130,6 +1181,13 @@ fn bench_fibonacci_run(b: &mut test::Bencher) { b.iter(|| module.execute_func::<_, u32>(0, (20,))); } +#[bench] +fn bench_fibonacci_compile_run(b: &mut test::Bencher) { + let wasm = wabt::wat2wasm(FIBONACCI).unwrap(); + + b.iter(|| translate(&wasm).unwrap().execute_func::<_, u32>(0, (20,))); +} + #[bench] fn bench_fibonacci_baseline(b: &mut test::Bencher) { fn fib(n: i32) -> i32 { @@ -1142,3 +1200,349 @@ fn bench_fibonacci_baseline(b: &mut test::Bencher) { b.iter(|| test::black_box(fib(test::black_box(20)))); } + +// #[test] +#[allow(dead_code)] +fn sieve() { + const CODE: &str = r#" +(module + (type $t0 (func (param i32 i32))) + (type $t1 (func (param i32 i32 i32) (result i32))) + (type $t2 (func (param i32 i32 i32))) + (type $t3 (func (param i32))) + (type $t4 (func (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32))) + (type $t5 (func (param i32 i32) (result i32))) + (func $optimized_sieve (export "optimized_sieve") (type $t0) (param $p0 i32) (param $p1 i32) + (local $l0 i32) (local $l1 i32) (local $l2 i32) (local $l3 i32) (local $l4 i32) (local $l5 i32) (local $l6 i32) (local $l7 i32) (local $l8 i32) (local $l9 i32) (local $l10 i32) (local $l11 i32) (local $l12 i32) (local $l13 i32) (local $l14 i32) (local $l15 i32) (local $l16 i32) (local $l17 i32) (local $l18 f64) + get_global $g0 + i32.const 64 + i32.sub + tee_local $l0 + set_global $g0 + i32.const 0 + set_local $l1 + get_local $l0 + i32.const 0 + i32.const 64 + call $memset + set_local $l0 + block $B0 + block $B1 + block $B2 + block $B3 + get_local $p1 + i32.const 2 + i32.gt_u + br_if $B3 + i32.const -1 + i32.const 0 + get_local $p1 + i32.const 2 + i32.eq + select + set_local $p1 + i32.const 0 + set_local $l2 + i32.const 0 + set_local $l3 + i32.const 0 + set_local $l4 + i32.const 0 + set_local $l5 + i32.const 0 + set_local $l6 + i32.const 0 + set_local $l7 + i32.const 0 + set_local $l8 + i32.const 0 + set_local $l9 + i32.const 0 + set_local $l10 + i32.const 0 + set_local $l11 + i32.const 0 + set_local $l12 + i32.const 0 + set_local $l13 + i32.const 0 + set_local $l14 + i32.const 0 + set_local $l15 + i32.const 0 + set_local $l16 + i32.const 0 + set_local $l17 + br $B2 + end + get_local $p1 + i32.const -3 + i32.add + i32.const 1 + i32.shr_u + set_local $l3 + block $B4 + block $B5 + get_local $p1 + f64.convert_u/i32 + f64.sqrt + tee_local $l18 + f64.const 0x1p+32 (;=4.29497e+09;) + f64.lt + get_local $l18 + f64.const 0x0p+0 (;=0;) + f64.ge + i32.and + br_if $B5 + i32.const 0 + set_local $p1 + br $B4 + end + get_local $l18 + i32.trunc_u/f64 + set_local $p1 + end + get_local $l3 + i32.const 1 + i32.add + set_local $l17 + get_local $p1 + i32.const -3 + i32.add + i32.const 1 + i32.shr_u + set_local $l5 + i32.const 0 + set_local $p1 + loop $L6 + get_local $p1 + tee_local $l4 + i32.const 5 + i32.shr_u + set_local $p1 + get_local $l4 + i32.const 511 + i32.gt_u + br_if $B0 + block $B7 + get_local $l0 + get_local $p1 + i32.const 2 + i32.shl + i32.add + i32.load + i32.const 1 + get_local $l4 + i32.const 31 + i32.and + i32.shl + i32.and + br_if $B7 + get_local $l4 + i32.const 1 + i32.shl + i32.const 3 + i32.add + tee_local $l2 + get_local $l2 + i32.mul + i32.const -3 + i32.add + i32.const 1 + i32.shr_u + tee_local $p1 + get_local $l3 + i32.gt_u + br_if $B7 + loop $L8 + get_local $p1 + i32.const 5 + i32.shr_u + set_local $l1 + get_local $p1 + i32.const 511 + i32.gt_u + br_if $B1 + get_local $l0 + get_local $l1 + i32.const 2 + i32.shl + i32.add + tee_local $l1 + get_local $l1 + i32.load + i32.const 1 + get_local $p1 + i32.const 31 + i32.and + i32.shl + i32.or + i32.store + get_local $p1 + get_local $l2 + i32.add + tee_local $p1 + get_local $l3 + i32.le_u + br_if $L8 + end + end + get_local $l4 + i32.const 1 + i32.add + set_local $p1 + get_local $l4 + get_local $l5 + i32.lt_u + br_if $L6 + end + i32.const -1 + set_local $p1 + get_local $l0 + i32.load offset=60 + set_local $l1 + get_local $l0 + i32.load offset=56 + set_local $l2 + get_local $l0 + i32.load offset=52 + set_local $l3 + get_local $l0 + i32.load offset=48 + set_local $l4 + get_local $l0 + i32.load offset=44 + set_local $l5 + get_local $l0 + i32.load offset=40 + set_local $l6 + get_local $l0 + i32.load offset=36 + set_local $l7 + get_local $l0 + i32.load offset=32 + set_local $l8 + get_local $l0 + i32.load offset=28 + set_local $l9 + get_local $l0 + i32.load offset=24 + set_local $l10 + get_local $l0 + i32.load offset=20 + set_local $l11 + get_local $l0 + i32.load offset=16 + set_local $l12 + get_local $l0 + i32.load offset=12 + set_local $l13 + get_local $l0 + i32.load offset=8 + set_local $l14 + get_local $l0 + i32.load offset=4 + set_local $l15 + get_local $l0 + i32.load + set_local $l16 + end + get_local $p0 + get_local $l1 + i32.store offset=68 + get_local $p0 + get_local $l2 + i32.store offset=64 + get_local $p0 + get_local $l3 + i32.store offset=60 + get_local $p0 + get_local $l4 + i32.store offset=56 + get_local $p0 + get_local $l5 + i32.store offset=52 + get_local $p0 + get_local $l6 + i32.store offset=48 + get_local $p0 + get_local $l7 + i32.store offset=44 + get_local $p0 + get_local $l8 + i32.store offset=40 + get_local $p0 + get_local $l9 + i32.store offset=36 + get_local $p0 + get_local $l10 + i32.store offset=32 + get_local $p0 + get_local $l11 + i32.store offset=28 + get_local $p0 + get_local $l12 + i32.store offset=24 + get_local $p0 + get_local $l13 + i32.store offset=20 + get_local $p0 + get_local $l14 + i32.store offset=16 + get_local $p0 + get_local $l15 + i32.store offset=12 + get_local $p0 + get_local $l16 + i32.store offset=8 + get_local $p0 + get_local $l17 + i32.store offset=4 + get_local $p0 + get_local $p1 + i32.store + get_local $l0 + i32.const 64 + i32.add + set_global $g0 + return + end + i32.const 1396 + get_local $l1 + i32.const 16 + unreachable + end + i32.const 1380 + get_local $p1 + i32.const 16 + unreachable) + (func $memset (type $t1) (param $p0 i32) (param $p1 i32) (param $p2 i32) (result i32) + (local $l0 i32) + block $B0 + get_local $p2 + i32.eqz + br_if $B0 + get_local $p0 + set_local $l0 + loop $L1 + get_local $l0 + get_local $p1 + i32.store8 + get_local $l0 + i32.const 1 + i32.add + set_local $l0 + get_local $p2 + i32.const -1 + i32.add + tee_local $p2 + br_if $L1 + end + end + get_local $p0) + (memory $memory (export "memory") 17 17) + (global $g0 (mut i32) (i32.const 1050032))) +"#; + + translate(&wabt::wat2wasm(CODE).unwrap()).unwrap(); +} diff --git a/src/translate_sections.rs b/src/translate_sections.rs index b024bab772..64b765ca0c 100644 --- a/src/translate_sections.rs +++ b/src/translate_sections.rs @@ -129,7 +129,7 @@ pub fn code( &body, ); - if false { + if true { let mut microwasm = vec![]; let mut microwasm_conv = MicrowasmConv::new(