Add float operations

This commit is contained in:
Jef
2019-02-25 15:35:45 +01:00
parent ab6b8e983a
commit f726a8f36d
7 changed files with 602 additions and 333 deletions

View File

@@ -1,6 +1,6 @@
#![allow(dead_code)] // for now
use microwasm::{SignlessType, F32, F64, I32, I64};
use microwasm::{SignlessType, Type, F32, F64, I32, I64};
use self::registers::*;
use dynasmrt::x64::Assembler;
@@ -25,7 +25,35 @@ pub enum GPR {
Rx(RegId),
}
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
pub enum GPRType {
Rq,
Rx,
}
impl From<SignlessType> for GPRType {
fn from(other: SignlessType) -> GPRType {
match other {
I32 | I64 => GPRType::Rq,
F32 | F64 => GPRType::Rx,
}
}
}
impl From<SignlessType> for Option<GPRType> {
fn from(other: SignlessType) -> Self {
Some(other.into())
}
}
impl GPR {
fn type_(&self) -> GPRType {
match self {
GPR::Rq(_) => GPRType::Rq,
GPR::Rx(_) => GPRType::Rx,
}
}
fn rq(self) -> Option<RegId> {
match self {
GPR::Rq(r) => Some(r),
@@ -308,20 +336,15 @@ impl Registers {
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_64(&mut self) -> GPR {
let out = self.scratch_64.0.take();
self.scratch_64.1[out as usize] += 1;
GPR::Rq(out)
}
pub fn take(&mut self, ty: impl Into<GPRType>) -> GPR {
let (mk_gpr, scratch_counts) = match ty.into() {
GPRType::Rq => (GPR::Rq as fn(_) -> _, &mut self.scratch_64),
GPRType::Rx => (GPR::Rx as fn(_) -> _, &mut self.scratch_128),
};
// 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)
let out = scratch_counts.0.take();
scratch_counts.1[out as usize] += 1;
mk_gpr(out)
}
pub fn release(&mut self, gpr: GPR) {
@@ -430,8 +453,8 @@ const FLOAT_ARGS_IN_GPRS: &[GPR] = &[XMM0, XMM1, XMM2, XMM3, XMM4, XMM5, XMM6, X
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, XMM1, XMM2, XMM3, XMM4, XMM5, XMM6, XMM7, XMM8, XMM9,
XMM10, XMM11, XMM12, XMM13, XMM14, XMM15,
RSI, RDX, RCX, R8, R9, RAX, R10, R11, XMM0, XMM1, XMM2, XMM3, XMM4, XMM5, XMM6, XMM7, XMM8,
XMM9, XMM10, XMM11, XMM12, XMM13, XMM14, XMM15,
];
const VMCTX: RegId = rq::RDI;
@@ -643,14 +666,14 @@ macro_rules! unop {
),
ValueLocation::Stack(offset) => {
let offset = self.adjusted_offset(offset);
let temp = self.block_state.regs.take_64();
let temp = self.block_state.regs.take(Type::for_::<$typ>());
dynasm!(self.asm
; $instr $reg_ty(temp.rq().unwrap()), [rsp + offset]
);
ValueLocation::Reg(temp)
}
ValueLocation::Reg(reg) => {
let temp = self.block_state.regs.take_64();
let temp = self.block_state.regs.take(Type::for_::<$typ>());
dynasm!(self.asm
; $instr $reg_ty(temp.rq().unwrap()), $reg_ty(reg.rq().unwrap())
);
@@ -665,7 +688,7 @@ macro_rules! unop {
// TODO: Support immediate `count` parameters
macro_rules! shift {
($name:ident, $reg_ty:ident, $instr:ident, $const_fallback:expr) => {
($name:ident, $reg_ty:ident, $instr:ident, $const_fallback:expr, $ty:expr) => {
pub fn $name(&mut self) {
enum RestoreRcx {
MoveValBack(GPR),
@@ -676,7 +699,7 @@ macro_rules! shift {
let mut val = self.pop();
if val == ValueLocation::Reg(RCX) {
val = ValueLocation::Reg(self.into_temp_reg(val));
val = ValueLocation::Reg(self.into_temp_reg($ty, val));
}
// TODO: Maybe allocate `RCX`, write `count` to it and then free `count`.
@@ -688,7 +711,7 @@ macro_rules! shift {
let out = if self.block_state.regs.is_free(RCX) {
None
} else {
let new_reg = self.block_state.regs.take_64();
let new_reg = self.block_state.regs.take(I32);
dynasm!(self.asm
; mov Rq(new_reg.rq().unwrap()), rcx
);
@@ -722,7 +745,7 @@ macro_rules! shift {
self.block_state.regs.mark_used(RCX);
count = ValueLocation::Reg(RCX);
let reg = self.into_reg(val);
let reg = self.into_reg($ty, val);
dynasm!(self.asm
; $instr $reg_ty(reg.rq().unwrap()), cl
@@ -751,7 +774,7 @@ macro_rules! cmp_i32 {
let out = if let Some(i) = left.imm_i32() {
match right {
ValueLocation::Stack(offset) => {
let result = self.block_state.regs.take_64();
let result = self.block_state.regs.take(I32);
let offset = self.adjusted_offset(offset);
dynasm!(self.asm
@@ -762,7 +785,7 @@ macro_rules! cmp_i32 {
ValueLocation::Reg(result)
}
ValueLocation::Reg(rreg) => {
let result = self.block_state.regs.take_64();
let result = self.block_state.regs.take(I32);
dynasm!(self.asm
; xor Rd(result.rq().unwrap()), Rd(result.rq().unwrap())
; cmp Rd(rreg.rq().unwrap()), i
@@ -781,11 +804,11 @@ macro_rules! cmp_i32 {
}
}
} else {
let lreg = self.into_reg(left);
let lreg = self.into_reg(I32, left);
// TODO: Make `into_reg` take an `&mut`?
left = ValueLocation::Reg(lreg);
let result = self.block_state.regs.take_64();
let result = self.block_state.regs.take(I32);
match right {
ValueLocation::Stack(offset) => {
@@ -832,7 +855,7 @@ macro_rules! cmp_i64 {
let out = if let Some(i) = left.imm_i64() {
match right {
ValueLocation::Stack(offset) => {
let result = self.block_state.regs.take_64();
let result = self.block_state.regs.take(I64);
let offset = self.adjusted_offset(offset);
if let Some(i) = i.try_into() {
dynasm!(self.asm
@@ -846,7 +869,7 @@ macro_rules! cmp_i64 {
ValueLocation::Reg(result)
}
ValueLocation::Reg(rreg) => {
let result = self.block_state.regs.take_64();
let result = self.block_state.regs.take(I64);
if let Some(i) = i.try_into() {
dynasm!(self.asm
; xor Rd(result.rq().unwrap()), Rd(result.rq().unwrap())
@@ -869,10 +892,10 @@ macro_rules! cmp_i64 {
}
}
} else {
let lreg = self.into_reg(left);
let lreg = self.into_reg(I64, left);
left = ValueLocation::Reg(lreg);
let result = self.block_state.regs.take_64();
let result = self.block_state.regs.take(I64);
match right {
ValueLocation::Stack(offset) => {
@@ -916,97 +939,111 @@ macro_rules! cmp_i64 {
macro_rules! commutative_binop_i32 {
($name:ident, $instr:ident, $const_fallback:expr) => {
pub fn $name(&mut self) {
let op0 = self.pop();
let op1 = self.pop();
if let Some(i1) = op1.imm_i32() {
if let Some(i0) = op0.imm_i32() {
self.push(ValueLocation::Immediate($const_fallback(i1, i0).into()));
return;
}
}
let (op1, op0) = match op1 {
ValueLocation::Reg(_) => (self.into_temp_reg(op1), op0),
_ => if op0.immediate().is_some() {
(self.into_temp_reg(op1), op0)
} else {
(self.into_temp_reg(op0), op1)
}
commutative_binop!(
$name,
$instr,
$const_fallback,
Rd,
rq,
I32,
imm_i32,
|this: &mut Context<_>, op1: GPR, i| dynasm!(this.asm
; $instr Rd(op1.rq().unwrap()), i
)
);
};
match op0 {
ValueLocation::Reg(reg) => {
dynasm!(self.asm
; $instr Rd(op1.rq().unwrap()), Rd(reg.rq().unwrap())
);
}
ValueLocation::Stack(offset) => {
let offset = self.adjusted_offset(offset);
dynasm!(self.asm
; $instr Rd(op1.rq().unwrap()), [rsp + offset]
);
}
ValueLocation::Immediate(i) => {
dynasm!(self.asm
; $instr Rd(op1.rq().unwrap()), i.as_i32().unwrap()
);
}
}
self.free_value(op0);
self.push(ValueLocation::Reg(op1));
}
}
}
macro_rules! commutative_binop_i64 {
($name:ident, $instr:ident, $const_fallback:expr) => {
commutative_binop!(
$name,
$instr,
$const_fallback,
Rq,
rq,
I64,
imm_i64,
|this: &mut Context<_>, op1: GPR, i| dynasm!(this.asm
; $instr Rq(op1.rq().unwrap()), i
)
);
};
}
macro_rules! commutative_binop_f32 {
($name:ident, $instr:ident, $const_fallback:expr) => {
commutative_binop!(
$name,
$instr,
$const_fallback,
Rx,
rx,
F32,
imm_f32,
|_, _, _| unreachable!()
);
};
}
macro_rules! commutative_binop_f64 {
($name:ident, $instr:ident, $const_fallback:expr) => {
commutative_binop!(
$name,
$instr,
$const_fallback,
Rx,
rx,
F64,
imm_f64,
|_, _, _| unreachable!()
);
};
}
macro_rules! commutative_binop {
($name:ident, $instr:ident, $const_fallback:expr, $reg_ty:ident, $reg_fn:ident, $ty:expr, $imm_fn:ident, $direct_imm:expr) => {
pub fn $name(&mut self) {
let op0 = self.pop();
let op1 = self.pop();
if let Some(i1) = op1.imm_i64() {
if let Some(i0) = op0.imm_i64() {
if let Some(i1) = op1.$imm_fn() {
if let Some(i0) = op0.$imm_fn() {
self.block_state.stack.push(ValueLocation::Immediate($const_fallback(i1, i0).into()));
return;
}
}
let (op1, op0) = match op1 {
ValueLocation::Reg(reg) => (reg, op0),
ValueLocation::Reg(_) => (self.into_temp_reg($ty, op1), op0),
_ => if op0.immediate().is_some() {
(self.into_temp_reg(op1), op0)
(self.into_temp_reg($ty, op1), op0)
} else {
(self.into_temp_reg(op0), op1)
(self.into_temp_reg($ty, op0), op1)
}
};
match op0 {
ValueLocation::Reg(reg) => {
dynasm!(self.asm
; $instr Rq(op1.rq().unwrap()), Rq(reg.rq().unwrap())
; $instr $reg_ty(op1.$reg_fn().unwrap()), $reg_ty(reg.$reg_fn().unwrap())
);
}
ValueLocation::Stack(offset) => {
let offset = self.adjusted_offset(offset);
dynasm!(self.asm
; $instr Rq(op1.rq().unwrap()), [rsp + offset]
; $instr $reg_ty(op1.$reg_fn().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.rq().unwrap()), i
);
if let Some(i) = i.as_int().and_then(|i| i.try_into()) {
$direct_imm(self, op1, i);
} else {
let scratch = self.block_state.regs.take_64();
let scratch = self.block_state.regs.take($ty);
self.immediate_to_reg(scratch, i);
dynasm!(self.asm
; mov Rq(scratch.rq().unwrap()), QWORD i
; $instr Rq(op1.rq().unwrap()), Rq(scratch.rq().unwrap())
; $instr $reg_ty(op1.$reg_fn().unwrap()), $reg_ty(scratch.$reg_fn().unwrap())
);
self.block_state.regs.release(scratch);
@@ -1021,7 +1058,7 @@ macro_rules! commutative_binop_i64 {
}
macro_rules! load {
($name:ident, $reg_ty:ident, $instruction_name:expr) => {
($name:ident, $reg_ty:ident, $instruction_name:expr, $out_ty:expr) => {
pub fn $name(&mut self, offset: u32) -> Result<(), Error> {
fn load_to_reg<_M: ModuleContext>(
ctx: &mut Context<_M>,
@@ -1029,7 +1066,7 @@ macro_rules! load {
(offset, runtime_offset): (i32, Result<i32, GPR>)
) {
let vmctx_mem_ptr_offset = ctx.module_context.offset_of_memory_ptr() as i32;
let mem_ptr_reg = ctx.block_state.regs.take_64();
let mem_ptr_reg = ctx.block_state.regs.take(I64);
dynasm!(ctx.asm
; mov Rq(mem_ptr_reg.rq().unwrap()), [Rq(VMCTX) + vmctx_mem_ptr_offset]
);
@@ -1052,14 +1089,14 @@ macro_rules! load {
let base = self.pop();
let temp = self.block_state.regs.take_64();
let temp = self.block_state.regs.take($out_ty);
match base {
ValueLocation::Immediate(i) => {
load_to_reg(self, temp, (offset as _, Ok(i.as_i32().unwrap())));
}
base => {
let gpr = self.into_reg(base);
let gpr = self.into_reg(I32, base);
load_to_reg(self, temp, (offset as _, Err(gpr)));
self.block_state.regs.release(gpr);
}
@@ -1073,7 +1110,7 @@ macro_rules! load {
}
macro_rules! store {
($name:ident, $reg_ty:ident, $size:ident, $instruction_name:expr) => {
($name:ident, $reg_ty:ident, $size:ident, $instruction_name:expr, $in_ty:expr) => {
pub fn $name(&mut self, offset: u32) -> Result<(), Error> {
fn store_from_reg<_M: ModuleContext>(
ctx: &mut Context<_M>,
@@ -1081,7 +1118,7 @@ macro_rules! store {
(offset, runtime_offset): (i32, Result<i32, GPR>)
) {
let vmctx_mem_ptr_offset = ctx.module_context.offset_of_memory_ptr() as i32;
let mem_ptr_reg = ctx.block_state.regs.take_64();
let mem_ptr_reg = ctx.block_state.regs.take(I64);
dynasm!(ctx.asm
; mov Rq(mem_ptr_reg.rq().unwrap()), [Rq(VMCTX) + vmctx_mem_ptr_offset]
);
@@ -1105,14 +1142,16 @@ macro_rules! store {
let src = self.pop();
let base = self.pop();
let src_reg = self.into_reg(src);
let src_reg = self.into_reg($in_ty, src);
// TODO
debug_assert!(stringify!($reg_ty) == "Rq" || stringify!($reg_ty) == "Rd");
match base {
ValueLocation::Immediate(i) => {
store_from_reg(self, src_reg, (offset as i32, Ok(i.as_i32().unwrap())));
}
base => {
let gpr = self.into_reg(base);
let gpr = self.into_reg(I32, base);
store_from_reg(self, src_reg, (offset as i32, Err(gpr)));
self.block_state.regs.release(gpr);
}
@@ -1227,8 +1266,8 @@ impl<M: ModuleContext> Context<'_, M> {
return;
}
let reg = self.into_reg(val);
let out = self.block_state.regs.take_64();
let reg = self.into_reg(I32, val);
let out = self.block_state.regs.take(I32);
dynasm!(self.asm
; xor Rd(out.rq().unwrap()), Rd(out.rq().unwrap())
@@ -1251,8 +1290,8 @@ impl<M: ModuleContext> Context<'_, M> {
return;
}
let reg = self.into_reg(val);
let out = self.block_state.regs.take_64();
let reg = self.into_reg(I64, val);
let out = self.block_state.regs.take(I64);
dynasm!(self.asm
; xor Rd(out.rq().unwrap()), Rd(out.rq().unwrap())
@@ -1272,7 +1311,7 @@ impl<M: ModuleContext> Context<'_, M> {
f(self);
let predicate = self.into_reg(val);
let predicate = self.into_reg(I32, val);
dynasm!(self.asm
; test Rd(predicate.rq().unwrap()), Rd(predicate.rq().unwrap())
@@ -1289,7 +1328,7 @@ impl<M: ModuleContext> Context<'_, M> {
f(self);
let predicate = self.into_reg(val);
let predicate = self.into_reg(I32, val);
dynasm!(self.asm
; test Rd(predicate.rq().unwrap()), Rd(predicate.rq().unwrap())
@@ -1316,7 +1355,7 @@ impl<M: ModuleContext> Context<'_, M> {
);
}
} else if self.block_state.depth.0 > depth.0 {
let trash = self.block_state.regs.take_64();
let trash = self.block_state.regs.take(I64);
// 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 {
@@ -1373,7 +1412,7 @@ impl<M: ModuleContext> Context<'_, M> {
let val = self.pop();
// TODO: We can use stack slots for values already on the stack but we
// don't refcount stack slots right now
let loc = CCLoc::Reg(self.into_temp_reg(val));
let loc = CCLoc::Reg(self.into_temp_reg(None, val));
out.push(loc);
}
@@ -1387,7 +1426,6 @@ impl<M: ModuleContext> Context<'_, M> {
}
fn immediate_to_reg(&mut self, reg: GPR, val: Value) {
// TODO: Floats
match reg {
GPR::Rq(r) => {
let val = val.as_bytes();
@@ -1402,7 +1440,7 @@ impl<M: ModuleContext> Context<'_, M> {
}
}
GPR::Rx(r) => {
let temp = self.block_state.regs.take_64();
let temp = self.block_state.regs.take(I64);
self.immediate_to_reg(temp, val);
dynasm!(self.asm
; movq Rx(r), Rq(temp.rq().unwrap())
@@ -1421,7 +1459,7 @@ impl<M: ModuleContext> 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_64();
let gpr = self.block_state.regs.take(I64);
dynasm!(self.asm
; mov Rq(gpr.rq().unwrap()), [rsp + in_offset]
; mov [rsp + out_offset], Rq(gpr.rq().unwrap())
@@ -1458,7 +1496,7 @@ impl<M: ModuleContext> Context<'_, M> {
; mov DWORD [rsp + out_offset], i as i32
);
} else {
let scratch = self.block_state.regs.take_64();
let scratch = self.block_state.regs.take(I64);
dynasm!(self.asm
; mov Rq(scratch.rq().unwrap()), QWORD i
@@ -1497,12 +1535,12 @@ impl<M: ModuleContext> Context<'_, M> {
}
(GPR::Rx(in_reg), GPR::Rq(out_reg)) => {
dynasm!(self.asm
; movq Rx(out_reg), Rq(in_reg)
; movq Rq(out_reg), Rx(in_reg)
);
}
(GPR::Rq(in_reg), GPR::Rx(out_reg)) => {
dynasm!(self.asm
; movq Rq(out_reg), Rx(in_reg)
; movq Rx(out_reg), Rq(in_reg)
);
}
(GPR::Rx(in_reg), GPR::Rx(out_reg)) => {
@@ -1558,10 +1596,10 @@ impl<M: ModuleContext> Context<'_, M> {
self.block_state.depth = cc.stack_depth;
}
load!(i32_load, Rd, "i32.load");
load!(i64_load, Rq, "i64.load");
store!(i32_store, Rd, DWORD, "i32.store");
store!(i64_store, Rq, QWORD, "i64.store");
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);
fn push_physical(&mut self, value: ValueLocation) -> ValueLocation {
self.block_state.depth.reserve(1);
@@ -1580,9 +1618,9 @@ impl<M: ModuleContext> Context<'_, M> {
);
}
ValueLocation::Immediate(imm) => {
let gpr = self.block_state.regs.take_64();
let gpr = self.block_state.regs.take(I64);
dynasm!(self.asm
; mov Rq(gpr.rq().unwrap()), QWORD imm.as_int().unwrap()
; mov Rq(gpr.rq().unwrap()), QWORD imm.as_bytes()
; push Rq(gpr.rq().unwrap())
);
self.block_state.regs.release(gpr);
@@ -1633,24 +1671,15 @@ impl<M: ModuleContext> Context<'_, M> {
}
/// Puts this value into a register so that it can be efficiently read
fn into_reg(&mut self, val: ValueLocation) -> GPR {
// TODO: We should allow choosing which reg type we want to allocate here (Rx/Rq)
fn into_reg(&mut self, ty: impl Into<Option<GPRType>>, val: ValueLocation) -> GPR {
match val {
ValueLocation::Reg(r) => r,
ValueLocation::Immediate(i) => {
let scratch = self.block_state.regs.take_64();
self.immediate_to_reg(scratch, i);
scratch
}
ValueLocation::Stack(offset) => {
// TODO: We can use `pop` if the number of usages is 1
// 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_64();
ValueLocation::Reg(r) if ty.into().map(|t| t == r.type_()).unwrap_or(true) => r,
val => {
let scratch = self.block_state.regs.take(ty.into().unwrap_or(GPRType::Rq));
dynasm!(self.asm
; mov Rq(scratch.rq().unwrap()), [rsp + offset]
);
self.copy_value(&val, &mut ValueLocation::Reg(scratch));
self.free_value(val);
scratch
}
@@ -1659,22 +1688,25 @@ impl<M: ModuleContext> Context<'_, M> {
/// Puts this value into a temporary register so that operations
/// on that register don't write to a local.
fn into_temp_reg(&mut self, val: ValueLocation) -> GPR {
fn into_temp_reg(&mut self, ty: impl Into<Option<GPRType>>, val: ValueLocation) -> GPR {
// If we have `None` as the type then it always matches (`.unwrap_or(true)`)
match val {
ValueLocation::Reg(r) => {
if self.block_state.regs.num_usages(r) <= 1 {
assert_eq!(self.block_state.regs.num_usages(r), 1);
let ty = ty.into();
let type_matches = ty.map(|t| t == r.type_()).unwrap_or(true);
if self.block_state.regs.num_usages(r) <= 1 && type_matches {
r
} else {
let new_reg = self.block_state.regs.take_64();
self.block_state.regs.release(r);
dynasm!(self.asm
; mov Rq(new_reg.rq().unwrap()), Rq(r.rq().unwrap())
);
new_reg
let scratch = self.block_state.regs.take(ty.unwrap_or(GPRType::Rq));
self.copy_value(&val, &mut ValueLocation::Reg(scratch));
self.free_value(val);
scratch
}
}
val => self.into_reg(val),
val => self.into_reg(ty, val),
}
}
@@ -1697,17 +1729,84 @@ impl<M: ModuleContext> Context<'_, M> {
commutative_binop_i64!(i64_or, or, |a, b| a | b);
commutative_binop_i64!(i64_xor, xor, |a, b| a ^ b);
shift!(i32_shl, Rd, shl, |a, b| (a as i32).wrapping_shl(b as _));
shift!(i32_shr_s, Rd, sar, |a, b| (a as i32).wrapping_shr(b as _));
shift!(i32_shr_u, Rd, shr, |a, b| (a as u32).wrapping_shr(b as _));
shift!(i32_rotl, Rd, rol, |a, b| (a as u32).rotate_left(b as _));
shift!(i32_rotr, Rd, ror, |a, b| (a as u32).rotate_right(b as _));
commutative_binop_f32!(f32_add, addss, |a: wasmparser::Ieee32, b: wasmparser::Ieee32| wasmparser::Ieee32(
(f32::from_bits(a.bits()) + f32::from_bits(b.bits())).to_bits()
));
commutative_binop_f64!(f64_add, addsd, |a: wasmparser::Ieee64, b: wasmparser::Ieee64| wasmparser::Ieee64(
(f64::from_bits(a.bits()) + f64::from_bits(b.bits())).to_bits()
));
shift!(i64_shl, Rq, shl, |a, b| (a as i64).wrapping_shl(b as _));
shift!(i64_shr_s, Rq, sar, |a, b| (a as i64).wrapping_shr(b as _));
shift!(i64_shr_u, Rq, shr, |a, b| (a as u64).wrapping_shr(b as _));
shift!(i64_rotl, Rq, rol, |a, b| (a as u64).rotate_left(b as _));
shift!(i64_rotr, Rq, ror, |a, b| (a as u64).rotate_right(b as _));
shift!(
i32_shl,
Rd,
shl,
|a, b| (a as i32).wrapping_shl(b as _),
I32
);
shift!(
i32_shr_s,
Rd,
sar,
|a, b| (a as i32).wrapping_shr(b as _),
I32
);
shift!(
i32_shr_u,
Rd,
shr,
|a, b| (a as u32).wrapping_shr(b as _),
I32
);
shift!(
i32_rotl,
Rd,
rol,
|a, b| (a as u32).rotate_left(b as _),
I32
);
shift!(
i32_rotr,
Rd,
ror,
|a, b| (a as u32).rotate_right(b as _),
I32
);
shift!(
i64_shl,
Rq,
shl,
|a, b| (a as i64).wrapping_shl(b as _),
I64
);
shift!(
i64_shr_s,
Rq,
sar,
|a, b| (a as i64).wrapping_shr(b as _),
I64
);
shift!(
i64_shr_u,
Rq,
shr,
|a, b| (a as u64).wrapping_shr(b as _),
I64
);
shift!(
i64_rotl,
Rq,
rol,
|a, b| (a as u64).rotate_left(b as _),
I64
);
shift!(
i64_rotr,
Rq,
ror,
|a, b| (a as u64).rotate_right(b as _),
I64
);
// `sub` is not commutative, so we have to handle it differently (we _must_ use the `op1`
// temp register as the output)
@@ -1724,7 +1823,7 @@ impl<M: ModuleContext> Context<'_, M> {
}
}
let op1 = self.into_temp_reg(op1);
let op1 = self.into_temp_reg(I32, op1);
match op0 {
ValueLocation::Reg(reg) => {
dynasm!(self.asm
@@ -1771,12 +1870,12 @@ impl<M: ModuleContext> Context<'_, M> {
}
let (op1, op0) = match op1 {
ValueLocation::Reg(_) => (self.into_temp_reg(op1), op0),
ValueLocation::Reg(_) => (self.into_temp_reg(I64, op1), op0),
_ => {
if op0.immediate().is_some() {
(self.into_temp_reg(op1), op0)
(self.into_temp_reg(I64, op1), op0)
} else {
(self.into_temp_reg(op0), op1)
(self.into_temp_reg(I64, op0), op1)
}
}
};
@@ -1826,7 +1925,7 @@ impl<M: ModuleContext> Context<'_, M> {
}
}
let op1 = self.into_temp_reg(op1);
let op1 = self.into_temp_reg(I64, op1);
match op0 {
ValueLocation::Reg(reg) => {
dynasm!(self.asm
@@ -1866,12 +1965,12 @@ impl<M: ModuleContext> Context<'_, M> {
}
let (op1, op0) = match op1 {
ValueLocation::Reg(_) => (self.into_temp_reg(op1), op0),
ValueLocation::Reg(_) => (self.into_temp_reg(I32, op1), op0),
_ => {
if op0.immediate().is_some() {
(self.into_temp_reg(op1), op0)
(self.into_temp_reg(I32, op1), op0)
} else {
(self.into_temp_reg(op0), op1)
(self.into_temp_reg(I32, op0), op1)
}
}
};
@@ -1915,7 +2014,7 @@ impl<M: ModuleContext> Context<'_, M> {
return;
}
other => {
let reg = self.into_reg(other);
let reg = self.into_reg(I32, other);
dynasm!(self.asm
; test Rd(reg.rq().unwrap()), Rd(reg.rq().unwrap())
@@ -1925,21 +2024,33 @@ impl<M: ModuleContext> Context<'_, M> {
}
}
let out = self.block_state.regs.take_64();
let out_gpr = self.block_state.regs.take(GPRType::Rq);
// TODO: Can do this better for variables on stack
let reg = self.into_reg(else_);
macro_rules! cmov_helper {
($instr:ident, $val:expr) => {
match $val {
ValueLocation::Stack(offset) => {
let offset = self.adjusted_offset(offset);
dynasm!(self.asm
; cmovz Rq(out.rq().unwrap()), Rq(reg.rq().unwrap())
; $instr Rq(out_gpr.rq().unwrap()), [rsp + offset]
);
self.block_state.regs.release(reg);
let reg = self.into_reg(then);
}
other => {
let scratch = self.into_reg(GPRType::Rq, other);
dynasm!(self.asm
; cmovnz Rq(out.rq().unwrap()), Rq(reg.rq().unwrap())
; $instr Rq(out_gpr.rq().unwrap()), Rq(scratch.rq().unwrap())
);
self.block_state.regs.release(reg);
self.block_state.regs.release(scratch);
}
}
}
}
self.push(ValueLocation::Reg(out));
cmov_helper!(cmovz, else_);
cmov_helper!(cmovnz, then);
self.push(ValueLocation::Reg(out_gpr));
}
pub fn pick(&mut self, depth: u32) {
@@ -1956,7 +2067,7 @@ impl<M: ModuleContext> Context<'_, M> {
self.block_state.stack.push(v);
}
pub fn literal(&mut self, imm: Value) {
pub fn const_(&mut self, imm: Value) {
self.push(ValueLocation::Immediate(imm));
}
@@ -2034,7 +2145,7 @@ impl<M: ModuleContext> Context<'_, M> {
if offset == -(WORD_SIZE as i32) {
self.push_physical(val);
} else {
let gpr = self.into_reg(val);
let gpr = self.into_reg(GPRType::Rq, val);
dynasm!(self.asm
; mov [rsp + offset], Rq(gpr.rq().unwrap())
);
@@ -2108,8 +2219,8 @@ impl<M: ModuleContext> Context<'_, M> {
}
let callee = self.pop();
let callee = self.into_temp_reg(callee);
let temp0 = self.block_state.regs.take_64();
let callee = self.into_temp_reg(I32, callee);
let temp0 = self.block_state.regs.take(I64);
for &loc in &locs {
if let CCLoc::Reg(r) = loc {

View File

@@ -4,8 +4,7 @@ use crate::microwasm::*;
use crate::module::{quickhash, ModuleContext, SigType, Signature};
use either::{Either, Left, Right};
use multi_mut::HashMapMultiMut;
use std::collections::HashMap;
use std::hash::Hash;
use std::{collections::HashMap, convert::TryInto, hash::Hash};
#[derive(Debug)]
struct Block {
@@ -27,6 +26,47 @@ impl Block {
}
}
pub fn translate_wasm<M: ModuleContext>(
session: &mut CodeGenSession<M>,
func_idx: u32,
body: &wasmparser::FunctionBody,
) -> Result<(), Error>
where
for<'any> &'any M::Signature: Into<OpSig>,
{
let ty = session.module_context.func_type(func_idx);
if false {
let mut microwasm = vec![];
let microwasm_conv = MicrowasmConv::new(
session.module_context,
ty.params().iter().map(SigType::to_microwasm_type),
ty.returns().iter().map(SigType::to_microwasm_type),
body,
);
for ops in microwasm_conv {
microwasm.extend(ops?);
}
println!("{}", crate::microwasm::dis(func_idx, &microwasm));
}
let microwasm_conv = MicrowasmConv::new(
session.module_context,
ty.params().iter().map(SigType::to_microwasm_type),
ty.returns().iter().map(SigType::to_microwasm_type),
body,
);
translate(
session,
func_idx,
microwasm_conv.flat_map(|i| i.expect("TODO: Make this not panic")),
)
}
pub fn translate<M: ModuleContext, I, L>(
session: &mut CodeGenSession<M>,
func_idx: u32,
@@ -315,8 +355,10 @@ where
Operator::Clz(Size::_64) => ctx.i64_clz(),
Operator::Ctz(Size::_64) => ctx.i64_ctz(),
Operator::Popcnt(Size::_64) => ctx.i64_popcnt(),
Operator::Add(F64) => ctx.f64_add(),
Operator::Add(F32) => ctx.f32_add(),
Operator::Drop(range) => ctx.drop(range),
Operator::Const(val) => ctx.literal(val),
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)?,

View File

@@ -4,7 +4,8 @@
const_slice_len,
never_type,
alloc_layout_extra,
try_from
try_from,
try_trait,
)]
#![plugin(dynasm)]
@@ -44,5 +45,5 @@ mod translate_sections;
mod tests;
pub use backend::CodeGenSession;
pub use function_body::translate as translate_function;
pub use function_body::translate_wasm as translate_function;
pub use module::{translate, ExecutableModule, ModuleContext, Signature, TranslatedModule};

View File

@@ -1,9 +1,12 @@
use crate::module::ModuleContext;
use crate::module::{ModuleContext, SigType, Signature};
use cranelift_codegen::ir::Signature as CraneliftSignature;
use smallvec::SmallVec;
use std::{
convert::TryFrom,
fmt,
iter::{self, FromIterator},
ops::RangeInclusive,
option::NoneError,
};
use wasmparser::{
FunctionBody, Ieee32, Ieee64, MemoryImmediate, Operator as WasmOperator, OperatorsReader,
@@ -180,6 +183,52 @@ pub enum Type<I> {
Float(Size),
}
pub trait IntoType<T> {
fn into_type() -> T;
}
impl IntoType<SignlessType> for i32 {
fn into_type() -> SignlessType {
I32
}
}
impl IntoType<SignlessType> for i64 {
fn into_type() -> SignlessType {
I64
}
}
impl IntoType<SignlessType> for u32 {
fn into_type() -> SignlessType {
I32
}
}
impl IntoType<SignlessType> for u64 {
fn into_type() -> SignlessType {
I64
}
}
impl IntoType<SignlessType> for f32 {
fn into_type() -> SignlessType {
F32
}
}
impl IntoType<SignlessType> for f64 {
fn into_type() -> SignlessType {
F64
}
}
impl<I> Type<I> {
pub fn for_<T: IntoType<Self>>() -> Self {
T::into_type()
}
}
impl fmt::Display for SignfulType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
@@ -251,6 +300,14 @@ impl SignlessType {
}
}
impl TryFrom<wasmparser::Type> for SignlessType {
type Error = NoneError;
fn try_from(other: wasmparser::Type) -> Result<SignlessType, NoneError> {
Ok(SignlessType::from_wasm(other)?)
}
}
#[derive(Debug, Clone)]
pub struct BrTable<L> {
targets: Vec<L>,
@@ -713,8 +770,10 @@ enum ControlFrameKind {
},
}
#[derive(Debug, Clone, PartialEq)]
struct ControlFrame {
id: u32,
arguments: u32,
returns: u32,
kind: ControlFrameKind,
}
@@ -760,7 +819,6 @@ pub struct MicrowasmConv<'a, 'b, M> {
internal: OperatorsReader<'a>,
module: &'b M,
current_id: u32,
num_locals: u32,
control_frames: Vec<ControlFrame>,
unreachable: bool,
}
@@ -801,17 +859,20 @@ impl OpSig {
}
}
impl From<&'_ wasmparser::FuncType> for OpSig {
fn from(other: &wasmparser::FuncType) -> Self {
impl<T> From<&'_ T> for OpSig
where
T: Signature,
{
fn from(other: &T) -> Self {
OpSig::new(
other
.params
.params()
.iter()
.map(|t| SigT::Concrete(Type::from_wasm(*t).unwrap())),
.map(|t| SigT::Concrete(t.to_microwasm_type())),
other
.returns
.returns()
.iter()
.map(|t| SigT::Concrete(Type::from_wasm(*t).unwrap())),
.map(|t| SigT::Concrete(t.to_microwasm_type())),
)
}
}
@@ -820,6 +881,56 @@ impl<'a, 'b, M: ModuleContext> MicrowasmConv<'a, 'b, M>
where
for<'any> &'any M::Signature: Into<OpSig>,
{
pub fn new(
context: &'b M,
params: impl IntoIterator<Item = SignlessType>,
returns: impl IntoIterator<Item = SignlessType>,
reader: &'a FunctionBody,
) -> Self {
// TODO: Don't panic!
let locals_reader = reader
.get_locals_reader()
.expect("Failed to get locals reader");
let mut locals = Vec::from_iter(params);
let mut consts = Vec::new();
for loc in locals_reader {
let (count, ty) = loc.expect("Getting local failed");
let ty = Type::from_wasm(ty).expect("Invalid local type");
locals.extend(std::iter::repeat(ty).take(count as _));
consts.extend(
std::iter::repeat(ty)
.map(Value::default_for_type)
.take(count as _),
)
}
let num_locals = locals.len() as _;
let mut out = Self {
is_done: false,
stack: locals,
module: context,
consts_to_emit: Some(consts),
internal: reader
.get_operators_reader()
.expect("Failed to get operators reader"),
current_id: 0,
control_frames: vec![],
unreachable: false,
};
let id = out.next_id();
out.control_frames.push(ControlFrame {
id,
arguments: num_locals,
returns: returns.into_iter().count() as _,
kind: ControlFrameKind::Function,
});
out
}
fn op_sig(&self, op: &WasmOperator) -> OpSig {
use self::SigT::T;
use std::iter::{empty as none, once};
@@ -1111,8 +1222,12 @@ where
fn apply_op(&mut self, sig: OpSig) {
let mut ty_param = None;
for p in sig.input.into_iter().rev() {
let stack_ty = self.stack.pop().expect("Stack is empty");
for p in sig.input.iter().rev() {
let stack_ty = self
.stack
.pop()
.unwrap_or_else(|| panic!("Stack is empty (while processing {:?})", sig));
let ty = match p {
SigT::T => {
if let Some(t) = ty_param {
@@ -1122,7 +1237,7 @@ where
stack_ty
}
}
SigT::Concrete(ty) => ty,
SigT::Concrete(ty) => *ty,
};
debug_assert_eq!(ty, stack_ty);
@@ -1153,57 +1268,6 @@ where
for _ in self.stack.drain(internal_range) {}
}
// I don't know if we need to know the return type
pub fn new(
context: &'b M,
params: impl IntoIterator<Item = SignlessType>,
returns: impl IntoIterator<Item = SignlessType>,
reader: &'a FunctionBody,
) -> Self {
// TODO: Don't panic!
let locals_reader = reader
.get_locals_reader()
.expect("Failed to get locals reader");
let mut locals = Vec::from_iter(params);
let mut consts = Vec::new();
for loc in locals_reader {
let (count, ty) = loc.expect("Getting local failed");
let ty = Type::from_wasm(ty).expect("Invalid local type");
locals.extend(std::iter::repeat(ty).take(count as _));
consts.extend(
std::iter::repeat(ty)
.map(Value::default_for_type)
.take(count as _),
)
}
let num_locals = locals.len() as _;
let mut out = Self {
is_done: false,
stack: locals,
module: context,
num_locals,
consts_to_emit: Some(consts),
internal: reader
.get_operators_reader()
.expect("Failed to get operators reader"),
current_id: 0,
control_frames: vec![],
unreachable: false,
};
let id = out.next_id();
out.control_frames.push(ControlFrame {
id,
returns: returns.into_iter().count() as _,
kind: ControlFrameKind::Function,
});
out
}
}
impl<'a, 'b, M: ModuleContext> Iterator for MicrowasmConv<'a, 'b, M>
@@ -1215,12 +1279,13 @@ where
fn next(&mut self) -> Option<wasmparser::Result<SmallVec<[OperatorFromWasm; 1]>>> {
macro_rules! to_drop {
($block:expr) => {{
let first_non_local_depth = $block.returns;
let block = &$block;
let first_non_local_depth = block.returns;
(|| {
let last_non_local_depth = (self.stack.len() as u32)
.checked_sub(1)?
.checked_sub(self.num_locals + 1)?;
.checked_sub(block.arguments)?;
if first_non_local_depth <= last_non_local_depth {
Some(first_non_local_depth..=last_non_local_depth)
@@ -1266,6 +1331,8 @@ where
if depth == 0 {
let block = self.control_frames.last_mut().expect("Failed");
self.stack = block.params().unwrap().to_vec();
if let ControlFrameKind::If { has_else, .. } = &mut block.kind {
*has_else = true;
}
@@ -1332,6 +1399,7 @@ where
let id = self.next_id();
self.control_frames.push(ControlFrame {
id,
arguments: self.stack.len() as u32,
returns: if ty == wasmparser::Type::EmptyBlockType {
0
} else {
@@ -1350,6 +1418,7 @@ where
let id = self.next_id();
self.control_frames.push(ControlFrame {
id,
arguments: self.stack.len() as u32,
returns: if ty == wasmparser::Type::EmptyBlockType {
0
} else {
@@ -1372,6 +1441,7 @@ where
let params = self.block_params();
self.control_frames.push(ControlFrame {
id,
arguments: self.stack.len() as u32,
returns: if ty == wasmparser::Type::EmptyBlockType {
0
} else {
@@ -1507,7 +1577,11 @@ where
if let Some(_to_drop) = to_drop {
// TODO: We want to generate an intermediate block here, but that might cause
// us to generate a spurious `jmp`.
unimplemented!()
unimplemented!(
"We don't yet support passing different numbers of arguments \
to each half of a `br_if` - supporting this efficiently without \
complicating the backend might be difficult"
);
} else {
smallvec![
Operator::block(params, label),
@@ -1519,7 +1593,7 @@ where
]
}
}
WasmOperator::BrTable { .. } => unimplemented!(),
WasmOperator::BrTable { .. } => unimplemented!("{:?}", op),
WasmOperator::Return => {
self.unreachable = true;
@@ -1634,8 +1708,8 @@ where
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::RefNull => unimplemented!("{:?}", op),
WasmOperator::RefIsNull => unimplemented!("{:?}", op),
WasmOperator::I32Eqz => smallvec![Operator::Eqz(Size::_32)],
WasmOperator::I32Eq => smallvec![Operator::Eq(I32)],
WasmOperator::I32Ne => smallvec![Operator::Ne(I32)],
@@ -1734,49 +1808,49 @@ where
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!(),
WasmOperator::I32TruncSF64 => unimplemented!(),
WasmOperator::I32TruncUF64 => unimplemented!(),
WasmOperator::I64ExtendSI32 => unimplemented!(),
WasmOperator::I64ExtendUI32 => unimplemented!(),
WasmOperator::I64TruncSF32 => unimplemented!(),
WasmOperator::I64TruncUF32 => unimplemented!(),
WasmOperator::I64TruncSF64 => unimplemented!(),
WasmOperator::I64TruncUF64 => unimplemented!(),
WasmOperator::F32ConvertSI32 => unimplemented!(),
WasmOperator::F32ConvertUI32 => unimplemented!(),
WasmOperator::F32ConvertSI64 => unimplemented!(),
WasmOperator::F32ConvertUI64 => unimplemented!(),
WasmOperator::F32DemoteF64 => unimplemented!(),
WasmOperator::F64ConvertSI32 => unimplemented!(),
WasmOperator::F64ConvertUI32 => unimplemented!(),
WasmOperator::F64ConvertSI64 => unimplemented!(),
WasmOperator::F64ConvertUI64 => unimplemented!(),
WasmOperator::F64PromoteF32 => unimplemented!(),
WasmOperator::I32ReinterpretF32 => unimplemented!(),
WasmOperator::I64ReinterpretF64 => unimplemented!(),
WasmOperator::F32ReinterpretI32 => unimplemented!(),
WasmOperator::F64ReinterpretI64 => unimplemented!(),
WasmOperator::I32Extend8S => unimplemented!(),
WasmOperator::I32Extend16S => unimplemented!(),
WasmOperator::I64Extend8S => unimplemented!(),
WasmOperator::I64Extend16S => unimplemented!(),
WasmOperator::I64Extend32S => unimplemented!(),
WasmOperator::I32WrapI64 => unimplemented!("{:?}", op),
WasmOperator::I32TruncSF32 => unimplemented!("{:?}", op),
WasmOperator::I32TruncUF32 => unimplemented!("{:?}", op),
WasmOperator::I32TruncSF64 => unimplemented!("{:?}", op),
WasmOperator::I32TruncUF64 => unimplemented!("{:?}", op),
WasmOperator::I64ExtendSI32 => unimplemented!("{:?}", op),
WasmOperator::I64ExtendUI32 => unimplemented!("{:?}", op),
WasmOperator::I64TruncSF32 => unimplemented!("{:?}", op),
WasmOperator::I64TruncUF32 => unimplemented!("{:?}", op),
WasmOperator::I64TruncSF64 => unimplemented!("{:?}", op),
WasmOperator::I64TruncUF64 => unimplemented!("{:?}", op),
WasmOperator::F32ConvertSI32 => unimplemented!("{:?}", op),
WasmOperator::F32ConvertUI32 => unimplemented!("{:?}", op),
WasmOperator::F32ConvertSI64 => unimplemented!("{:?}", op),
WasmOperator::F32ConvertUI64 => unimplemented!("{:?}", op),
WasmOperator::F32DemoteF64 => unimplemented!("{:?}", op),
WasmOperator::F64ConvertSI32 => unimplemented!("{:?}", op),
WasmOperator::F64ConvertUI32 => unimplemented!("{:?}", op),
WasmOperator::F64ConvertSI64 => unimplemented!("{:?}", op),
WasmOperator::F64ConvertUI64 => unimplemented!("{:?}", op),
WasmOperator::F64PromoteF32 => unimplemented!("{:?}", op),
WasmOperator::I32ReinterpretF32 => unimplemented!("{:?}", op),
WasmOperator::I64ReinterpretF64 => unimplemented!("{:?}", op),
WasmOperator::F32ReinterpretI32 => unimplemented!("{:?}", op),
WasmOperator::F64ReinterpretI64 => unimplemented!("{:?}", op),
WasmOperator::I32Extend8S => unimplemented!("{:?}", op),
WasmOperator::I32Extend16S => unimplemented!("{:?}", op),
WasmOperator::I64Extend8S => unimplemented!("{:?}", op),
WasmOperator::I64Extend16S => unimplemented!("{:?}", op),
WasmOperator::I64Extend32S => unimplemented!("{:?}", op),
// 0xFC operators
// Non-trapping Float-to-int Conversions
WasmOperator::I32TruncSSatF32 => unimplemented!(),
WasmOperator::I32TruncUSatF32 => unimplemented!(),
WasmOperator::I32TruncSSatF64 => unimplemented!(),
WasmOperator::I32TruncUSatF64 => unimplemented!(),
WasmOperator::I64TruncSSatF32 => unimplemented!(),
WasmOperator::I64TruncUSatF32 => unimplemented!(),
WasmOperator::I64TruncSSatF64 => unimplemented!(),
WasmOperator::I64TruncUSatF64 => unimplemented!(),
WasmOperator::I32TruncSSatF32 => unimplemented!("{:?}", op),
WasmOperator::I32TruncUSatF32 => unimplemented!("{:?}", op),
WasmOperator::I32TruncSSatF64 => unimplemented!("{:?}", op),
WasmOperator::I32TruncUSatF64 => unimplemented!("{:?}", op),
WasmOperator::I64TruncSSatF32 => unimplemented!("{:?}", op),
WasmOperator::I64TruncUSatF32 => unimplemented!("{:?}", op),
WasmOperator::I64TruncSSatF64 => unimplemented!("{:?}", op),
WasmOperator::I64TruncUSatF64 => unimplemented!("{:?}", op),
_ => unimplemented!(),
other => unimplemented!("{:?}", other),
}))
}
}

View File

@@ -334,10 +334,18 @@ impl SigType for AbiParam {
fn to_microwasm_type(&self) -> microwasm::SignlessType {
use microwasm::{Size::*, Type::*};
if self.value_type == ir::Type::int(32).unwrap() {
Int(_32)
} else if self.value_type == ir::Type::int(64).unwrap() {
Int(_64)
if self.value_type.is_int() {
match self.value_type.bits() {
32 => Int(_32),
64 => Int(_64),
_ => unimplemented!(),
}
} else if self.value_type.is_float() {
match self.value_type.bits() {
32 => Float(_32),
64 => Float(_64),
_ => unimplemented!(),
}
} else {
unimplemented!()
}

View File

@@ -290,6 +290,53 @@ mod op64 {
binop_test!(rotr, |a, b| (a as u64).rotate_right(b as _) as i64);
}
mod opf32 {
use super::translate_wat;
quickcheck! {
fn as_params(a: f64, b: f64) -> bool {
const CODE: &str = r#"(module
(func (param f64) (param f64) (result f64)
(f64.add (get_local 0) (get_local 1))))
"#;
translate_wat(CODE).execute_func::<(f64, f64), f64>(0, (a, b)) == Ok(a + b)
}
fn lit_lit(a: f64, b: f64) -> bool {
translate_wat(&format!("
(module (func (result f64)
(f64.add (f64.const {left}) (f64.const {right}))))
", left = a, right = b)).execute_func::<(), f64>(0, ()) == Ok(a + b)
}
fn lit_reg(a: f64, b: f64) -> bool {
use std::sync::Once;
let translated = translate_wat(&format!("
(module (func (param f64) (result f64)
(f64.add (f64.const {left}) (get_local 0))))
", left = a));
static ONCE: Once = Once::new();
ONCE.call_once(|| translated.disassemble());
translated.execute_func::<(f64,), f64>(0, (b,)) == Ok(a + b)
}
fn reg_lit(a: f64, b: f64) -> bool {
use std::sync::Once;
let translated = translate_wat(&format!("
(module (func (param f64) (result f64)
(f64.add (get_local 0) (f64.const {right}))))
", right = b));
static ONCE: Once = Once::new();
ONCE.call_once(|| translated.disassemble());
translated.execute_func::<(f64,), f64>(0, (a,)) == Ok(a + b)
}
}
}
quickcheck! {
fn if_then_else(a: u32, b: u32) -> bool {
const CODE: &str = r#"

View File

@@ -114,20 +114,6 @@ pub fn code(
let mut session = CodeGenSession::new(func_count, translation_ctx);
for (idx, body) in code.into_iter().enumerate() {
let body = body?;
let mut microwasm_conv = MicrowasmConv::new(
translation_ctx,
translation_ctx
.func_type(idx as _)
.params
.iter()
.map(|t| MWType::from_wasm(*t).unwrap()),
translation_ctx
.func_type(idx as _)
.returns
.iter()
.map(|t| MWType::from_wasm(*t).unwrap()),
&body,
);
if true {
let mut microwasm = vec![];
@@ -154,10 +140,10 @@ pub fn code(
println!("{}", crate::microwasm::dis(idx, &microwasm));
}
function_body::translate(
function_body::translate_wasm(
&mut session,
idx as u32,
microwasm_conv.flat_map(|i| i.expect("TODO: Make this not panic")),
&body,
)?;
}
Ok(session.into_translated_code_section()?)