Fix div and rem
This commit is contained in:
40
README.md
40
README.md
@@ -170,10 +170,10 @@ It's hard to judge, since each test in the spec testsuite covers a wide range of
|
||||
```
|
||||
running 76 tests
|
||||
test misc_testsuite::stack_overflow ... ok
|
||||
test misc_testsuite::misc_traps ... ok
|
||||
test spec_testsuite::binary ... ok
|
||||
test spec_testsuite::align ... FAILED
|
||||
test misc_testsuite::misc_traps ... ok
|
||||
test spec_testsuite::block ... FAILED
|
||||
test spec_testsuite::align ... FAILED
|
||||
test spec_testsuite::br_if ... FAILED
|
||||
test spec_testsuite::break_drop ... ok
|
||||
test spec_testsuite::call ... FAILED
|
||||
@@ -184,46 +184,46 @@ test spec_testsuite::const_ ... ok
|
||||
test spec_testsuite::custom ... ok
|
||||
test spec_testsuite::custom_section ... ok
|
||||
test spec_testsuite::data ... ok
|
||||
test spec_testsuite::elem ... FAILED
|
||||
test spec_testsuite::conversions ... FAILED
|
||||
test spec_testsuite::elem ... FAILED
|
||||
test spec_testsuite::endianness ... FAILED
|
||||
test spec_testsuite::br ... ok
|
||||
test spec_testsuite::exports ... ok
|
||||
test spec_testsuite::br ... ok
|
||||
test spec_testsuite::f32_bitwise ... FAILED
|
||||
test spec_testsuite::br_table ... FAILED
|
||||
test spec_testsuite::f64_bitwise ... FAILED
|
||||
test spec_testsuite::f32 ... FAILED
|
||||
test spec_testsuite::f32_cmp ... FAILED
|
||||
test spec_testsuite::fac ... ok
|
||||
test spec_testsuite::float_literals ... FAILED
|
||||
test spec_testsuite::f32_cmp ... FAILED
|
||||
test spec_testsuite::f64 ... FAILED
|
||||
test spec_testsuite::float_misc ... FAILED
|
||||
test spec_testsuite::forward ... ok
|
||||
test spec_testsuite::f64_cmp ... FAILED
|
||||
test spec_testsuite::float_memory ... ok
|
||||
test spec_testsuite::forward ... ok
|
||||
test spec_testsuite::float_misc ... FAILED
|
||||
test spec_testsuite::func_ptrs ... FAILED
|
||||
test spec_testsuite::get_local ... FAILED
|
||||
test spec_testsuite::float_memory ... ok
|
||||
test spec_testsuite::globals ... FAILED
|
||||
test spec_testsuite::float_exprs ... FAILED
|
||||
test spec_testsuite::i64 ... FAILED
|
||||
test spec_testsuite::i32 ... FAILED
|
||||
test spec_testsuite::if_ ... FAILED
|
||||
test spec_testsuite::imports ... FAILED
|
||||
test spec_testsuite::inline_module ... ok
|
||||
test spec_testsuite::if_ ... FAILED
|
||||
test spec_testsuite::int_exprs ... FAILED
|
||||
test spec_testsuite::labels ... ok
|
||||
test spec_testsuite::left_to_right ... FAILED
|
||||
test spec_testsuite::int_literals ... ok
|
||||
test spec_testsuite::linking ... FAILED
|
||||
test spec_testsuite::float_literals ... ok
|
||||
test spec_testsuite::func ... FAILED
|
||||
test spec_testsuite::memory_grow ... FAILED
|
||||
test spec_testsuite::int_exprs ... FAILED
|
||||
test spec_testsuite::i32 ... ok
|
||||
test spec_testsuite::linking ... FAILED
|
||||
test spec_testsuite::left_to_right ... FAILED
|
||||
test spec_testsuite::loop_ ... FAILED
|
||||
test spec_testsuite::int_literals ... ok
|
||||
test spec_testsuite::memory_grow ... FAILED
|
||||
test spec_testsuite::memory_redundancy ... ok
|
||||
test spec_testsuite::memory ... FAILED
|
||||
test spec_testsuite::memory_trap ... FAILED
|
||||
test spec_testsuite::labels ... ok
|
||||
test spec_testsuite::resizing ... FAILED
|
||||
test spec_testsuite::nop ... FAILED
|
||||
test spec_testsuite::return_minimal ... ok
|
||||
test spec_testsuite::nop ... FAILED
|
||||
test spec_testsuite::set_local ... FAILED
|
||||
test spec_testsuite::select ... FAILED
|
||||
test spec_testsuite::stack ... FAILED
|
||||
@@ -240,13 +240,13 @@ test spec_testsuite::unreached_invalid ... ok
|
||||
test spec_testsuite::unwind ... FAILED
|
||||
test spec_testsuite::utf8_custom_section_id ... ok
|
||||
test spec_testsuite::utf8_import_field ... ok
|
||||
test spec_testsuite::return_ ... ok
|
||||
test spec_testsuite::utf8_import_module ... ok
|
||||
test spec_testsuite::utf8_invalid_encoding ... ok
|
||||
test spec_testsuite::return_ ... ok
|
||||
test spec_testsuite::unreachable ... ok
|
||||
test spec_testsuite::names ... FAILED
|
||||
|
||||
test result: FAILED. 31 passed; 45 failed; 0 ignored; 0 measured; 0 filtered out
|
||||
test result: FAILED. 33 passed; 43 failed; 0 ignored; 0 measured; 0 filtered out
|
||||
```
|
||||
|
||||
All the failed tests apart from `address` and `unwind` (whose failure hasn't been investigated yet) are due to features that have yet to be implemented.
|
||||
|
||||
275
src/backend.rs
275
src/backend.rs
@@ -690,6 +690,55 @@ macro_rules! unop {
|
||||
}
|
||||
};
|
||||
|
||||
self.free_value(val);
|
||||
self.push(out_val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! conversion {
|
||||
(
|
||||
$name:ident,
|
||||
$instr:ident,
|
||||
$in_reg_ty:ident,
|
||||
$in_reg_fn:ident,
|
||||
$out_reg_ty:ident,
|
||||
$out_reg_fn:ident,
|
||||
$in_typ:ty,
|
||||
$out_typ:ty,
|
||||
$const_ty_fn:ident,
|
||||
$const_fallback:expr
|
||||
) => {
|
||||
pub fn $name(&mut self) {
|
||||
let mut val = self.pop();
|
||||
|
||||
let out_val = match val {
|
||||
ValueLocation::Immediate(imm) =>
|
||||
ValueLocation::Immediate(
|
||||
($const_fallback(imm.$const_ty_fn().unwrap()) as $out_typ).into()
|
||||
),
|
||||
ValueLocation::Stack(offset) => {
|
||||
let offset = self.adjusted_offset(offset);
|
||||
let temp = self.block_state.regs.take(Type::for_::<$out_typ>());
|
||||
dynasm!(self.asm
|
||||
; $instr $out_reg_ty(temp.rq().unwrap()), [rsp + offset]
|
||||
);
|
||||
ValueLocation::Reg(temp)
|
||||
}
|
||||
ValueLocation::Reg(_) => {
|
||||
let reg = self.into_reg(Type::for_::<$in_typ>(), val);
|
||||
let temp = self.block_state.regs.take(Type::for_::<$out_typ>());
|
||||
val = ValueLocation::Reg(reg);
|
||||
|
||||
dynasm!(self.asm
|
||||
; $instr $out_reg_ty(temp.$out_reg_fn().unwrap()), $in_reg_ty(reg.$in_reg_fn().unwrap())
|
||||
);
|
||||
ValueLocation::Reg(temp)
|
||||
}
|
||||
};
|
||||
|
||||
self.free_value(val);
|
||||
|
||||
self.push(out_val);
|
||||
}
|
||||
}
|
||||
@@ -1624,9 +1673,17 @@ impl<'module, M: ModuleContext> Context<'module, M> {
|
||||
|
||||
/// Pops i32 predicate and branches to the specified label
|
||||
/// if the predicate is equal to zero.
|
||||
pub fn br_if_false(&mut self, target: impl Into<BrTarget<Label>>, pass_args: impl FnOnce(&mut Self)) {
|
||||
pub fn br_if_false(
|
||||
&mut self,
|
||||
target: impl Into<BrTarget<Label>>,
|
||||
pass_args: impl FnOnce(&mut Self),
|
||||
) {
|
||||
let val = self.pop();
|
||||
let label = target.into().label().map(|c| *c).unwrap_or_else(|| self.ret_label());
|
||||
let label = target
|
||||
.into()
|
||||
.label()
|
||||
.map(|c| *c)
|
||||
.unwrap_or_else(|| self.ret_label());
|
||||
|
||||
pass_args(self);
|
||||
|
||||
@@ -1642,9 +1699,17 @@ impl<'module, M: ModuleContext> Context<'module, M> {
|
||||
|
||||
/// Pops i32 predicate and branches to the specified label
|
||||
/// if the predicate is not equal to zero.
|
||||
pub fn br_if_true(&mut self, target: impl Into<BrTarget<Label>>, pass_args: impl FnOnce(&mut Self)) {
|
||||
pub fn br_if_true(
|
||||
&mut self,
|
||||
target: impl Into<BrTarget<Label>>,
|
||||
pass_args: impl FnOnce(&mut Self),
|
||||
) {
|
||||
let val = self.pop();
|
||||
let label = target.into().label().map(|c| *c).unwrap_or_else(|| self.ret_label());
|
||||
let label = target
|
||||
.into()
|
||||
.label()
|
||||
.map(|c| *c)
|
||||
.unwrap_or_else(|| self.ret_label());
|
||||
|
||||
pass_args(self);
|
||||
|
||||
@@ -1685,18 +1750,7 @@ impl<'module, M: ModuleContext> Context<'module, M> {
|
||||
|
||||
pass_args(self);
|
||||
|
||||
if count == 0 {
|
||||
if let Some(default) = default {
|
||||
match default {
|
||||
BrTarget::Label(label) => self.br(label),
|
||||
BrTarget::Return => {
|
||||
dynasm!(self.asm
|
||||
; ret
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if let Some(imm) = selector.imm_i32() {
|
||||
if let Some(imm) = selector.imm_i32() {
|
||||
if let Some(target) = targets.nth(imm as _).or(default) {
|
||||
match target {
|
||||
BrTarget::Label(label) => self.br(label),
|
||||
@@ -1708,17 +1762,34 @@ impl<'module, M: ModuleContext> Context<'module, M> {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if count > 0 {
|
||||
let selector_reg = self.into_reg(GPRType::Rq, selector);
|
||||
selector = ValueLocation::Reg(selector_reg);
|
||||
|
||||
// TODO: Jump table (wrestling with dynasm to implement it is too much work)
|
||||
let tmp = self.block_state.regs.take(I64);
|
||||
|
||||
self.immediate_to_reg(tmp, (count as u32).into());
|
||||
dynasm!(self.asm
|
||||
; cmp Rq(selector_reg.rq().unwrap()), Rq(tmp.rq().unwrap())
|
||||
; cmova Rq(selector_reg.rq().unwrap()), Rq(tmp.rq().unwrap())
|
||||
; lea Rq(tmp.rq().unwrap()), [>start_label]
|
||||
; lea Rq(selector_reg.rq().unwrap()), [
|
||||
Rq(selector_reg.rq().unwrap()) * 5
|
||||
]
|
||||
; add Rq(selector_reg.rq().unwrap()), Rq(tmp.rq().unwrap())
|
||||
; jmp Rq(selector_reg.rq().unwrap())
|
||||
; start_label:
|
||||
);
|
||||
|
||||
self.block_state.regs.release(tmp);
|
||||
|
||||
for (i, target) in targets.enumerate() {
|
||||
let label = self.target_to_label(target);
|
||||
dynasm!(self.asm
|
||||
; cmp Rq(selector_reg.rq().unwrap()), i as i32
|
||||
; je =>label.0
|
||||
; jmp =>label.0
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(def) = default {
|
||||
match def {
|
||||
@@ -2251,6 +2322,18 @@ impl<'module, M: ModuleContext> Context<'module, M> {
|
||||
}
|
||||
|
||||
unop!(i32_popcnt, popcnt, Rd, u32, u32::count_ones);
|
||||
conversion!(
|
||||
i32_truncate_f32,
|
||||
cvttss2si,
|
||||
Rx,
|
||||
rx,
|
||||
Rd,
|
||||
rq,
|
||||
f32,
|
||||
i32,
|
||||
as_f32,
|
||||
|a: wasmparser::Ieee32| a.bits()
|
||||
);
|
||||
unop!(i64_popcnt, popcnt, Rq, u64, |a: u64| a.count_ones() as u64);
|
||||
|
||||
// TODO: Use `lea` when the LHS operand isn't a temporary but both of the operands
|
||||
@@ -2270,10 +2353,12 @@ impl<'module, M: ModuleContext> Context<'module, M> {
|
||||
commutative_binop_f32!(f32_add, addss, |a, b| a + b);
|
||||
commutative_binop_f32!(f32_mul, mulss, |a, b| a * b);
|
||||
binop_f32!(f32_sub, subss, |a, b| a - b);
|
||||
binop_f32!(f32_div, divss, |a, b| a / b);
|
||||
|
||||
commutative_binop_f64!(f64_add, addsd, |a, b| a + b);
|
||||
commutative_binop_f64!(f64_mul, mulsd, |a, b| a * b);
|
||||
binop_f64!(f64_sub, subsd, |a, b| a - b);
|
||||
binop_f64!(f64_div, divsd, |a, b| a / b);
|
||||
|
||||
shift!(
|
||||
i32_shl,
|
||||
@@ -2358,27 +2443,30 @@ impl<'module, M: ModuleContext> Context<'module, M> {
|
||||
) -> (
|
||||
ValueLocation,
|
||||
ValueLocation,
|
||||
impl Iterator<Item = (GPR, GPR)> + Clone + 'module,
|
||||
impl Iterator<Item = (GPR, GPR)> + Clone,
|
||||
) {
|
||||
let divisor = if ValueLocation::Reg(RAX) == divisor {
|
||||
self.block_state.regs.mark_used(RAX);
|
||||
self.block_state.regs.mark_used(RDX);
|
||||
let divisor = if divisor == ValueLocation::Reg(RAX) || divisor == ValueLocation::Reg(RDX) {
|
||||
let new_reg = self.block_state.regs.take(I32);
|
||||
self.copy_value(&divisor, &mut ValueLocation::Reg(new_reg));
|
||||
self.block_state.regs.release(RAX);
|
||||
self.free_value(divisor);
|
||||
ValueLocation::Reg(new_reg)
|
||||
} else if let ValueLocation::Stack(_) = divisor {
|
||||
divisor
|
||||
} else {
|
||||
ValueLocation::Reg(self.into_temp_reg(I32, divisor))
|
||||
ValueLocation::Reg(self.into_reg(I32, divisor))
|
||||
};
|
||||
|
||||
self.free_value(quotient);
|
||||
let should_save_rax = !self.block_state.regs.is_free(RAX);
|
||||
let should_save_rdx = !self.block_state.regs.is_free(RDX);
|
||||
self.block_state.regs.release(RDX);
|
||||
self.block_state.regs.release(RAX);
|
||||
|
||||
if let ValueLocation::Reg(r) = quotient {
|
||||
self.block_state.regs.mark_used(r);
|
||||
}
|
||||
|
||||
let should_save_rax =
|
||||
quotient != ValueLocation::Reg(RAX) && !self.block_state.regs.is_free(RAX);
|
||||
|
||||
let saved_rax = if should_save_rax {
|
||||
let new_reg = self.block_state.regs.take(I32);
|
||||
dynasm!(self.asm
|
||||
@@ -2389,6 +2477,12 @@ impl<'module, M: ModuleContext> Context<'module, M> {
|
||||
None
|
||||
};
|
||||
|
||||
self.block_state.regs.mark_used(RAX);
|
||||
self.copy_value("ient, &mut ValueLocation::Reg(RAX));
|
||||
self.free_value(quotient);
|
||||
|
||||
let should_save_rdx = !self.block_state.regs.is_free(RDX);
|
||||
|
||||
let saved_rdx = if should_save_rdx {
|
||||
let new_reg = self.block_state.regs.take(I32);
|
||||
dynasm!(self.asm
|
||||
@@ -2399,16 +2493,14 @@ impl<'module, M: ModuleContext> Context<'module, M> {
|
||||
None
|
||||
};
|
||||
|
||||
dynasm!(self.asm
|
||||
; cdq
|
||||
);
|
||||
|
||||
do_div(self, divisor);
|
||||
self.block_state.regs.mark_used(RAX);
|
||||
|
||||
self.free_value(divisor);
|
||||
self.block_state.regs.mark_used(RDX);
|
||||
|
||||
(
|
||||
divisor,
|
||||
ValueLocation::Reg(RAX),
|
||||
ValueLocation::Reg(RDX),
|
||||
saved_rax
|
||||
.map(|s| (s, RAX))
|
||||
.into_iter()
|
||||
@@ -2429,12 +2521,14 @@ impl<'module, M: ModuleContext> Context<'module, M> {
|
||||
ValueLocation::Stack(offset) => {
|
||||
let offset = this.adjusted_offset(offset);
|
||||
dynasm!(this.asm
|
||||
; xor edx, edx
|
||||
; div [rsp + offset]
|
||||
);
|
||||
}
|
||||
ValueLocation::Reg(r) => {
|
||||
dynasm!(this.asm
|
||||
; div Rq(r.rq().unwrap())
|
||||
; xor edx, edx
|
||||
; div Rd(r.rq().unwrap())
|
||||
);
|
||||
}
|
||||
ValueLocation::Immediate(_) => unreachable!(),
|
||||
@@ -2454,12 +2548,14 @@ impl<'module, M: ModuleContext> Context<'module, M> {
|
||||
ValueLocation::Stack(offset) => {
|
||||
let offset = this.adjusted_offset(offset);
|
||||
dynasm!(this.asm
|
||||
; cdq
|
||||
; idiv [rsp + offset]
|
||||
);
|
||||
}
|
||||
ValueLocation::Reg(r) => {
|
||||
dynasm!(this.asm
|
||||
; idiv Rq(r.rq().unwrap())
|
||||
; cdq
|
||||
; idiv Rd(r.rq().unwrap())
|
||||
);
|
||||
}
|
||||
ValueLocation::Immediate(_) => unreachable!(),
|
||||
@@ -2496,6 +2592,59 @@ impl<'module, M: ModuleContext> Context<'module, M> {
|
||||
let (div, rem, saved) = self.i32_full_div_u(divisor, quotient);
|
||||
|
||||
self.free_value(rem);
|
||||
|
||||
let div = match div {
|
||||
ValueLocation::Reg(div) if saved.clone().any(|(_, dst)| dst == div) => {
|
||||
let new = self.block_state.regs.take(I32);
|
||||
dynasm!(self.asm
|
||||
; mov Rq(new.rq().unwrap()), Rq(div.rq().unwrap())
|
||||
);
|
||||
self.block_state.regs.release(div);
|
||||
ValueLocation::Reg(new)
|
||||
}
|
||||
_ => div,
|
||||
};
|
||||
|
||||
self.cleanup_gprs(saved);
|
||||
|
||||
self.push(div);
|
||||
}
|
||||
|
||||
// TODO: Fast div using mul for constant divisor? It looks like LLVM doesn't do that for us when
|
||||
// emitting Wasm.
|
||||
pub fn i32_div_s(&mut self) {
|
||||
let divisor = self.pop();
|
||||
let quotient = self.pop();
|
||||
|
||||
if let (Some(quotient), Some(divisor)) = (quotient.imm_i32(), divisor.imm_i32()) {
|
||||
if divisor == 0 {
|
||||
self.trap();
|
||||
self.push(ValueLocation::Immediate(0u32.into()));
|
||||
} else {
|
||||
self.push(ValueLocation::Immediate(
|
||||
i32::wrapping_div(quotient, divisor).into(),
|
||||
));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
let (div, rem, saved) = self.i32_full_div_s(divisor, quotient);
|
||||
|
||||
self.free_value(rem);
|
||||
|
||||
let div = match div {
|
||||
ValueLocation::Reg(div) if saved.clone().any(|(_, dst)| dst == div) => {
|
||||
let new = self.block_state.regs.take(I32);
|
||||
dynasm!(self.asm
|
||||
; mov Rq(new.rq().unwrap()), Rq(div.rq().unwrap())
|
||||
);
|
||||
self.block_state.regs.release(div);
|
||||
ValueLocation::Reg(new)
|
||||
}
|
||||
_ => div,
|
||||
};
|
||||
|
||||
self.cleanup_gprs(saved);
|
||||
|
||||
self.push(div);
|
||||
@@ -2521,19 +2670,21 @@ impl<'module, M: ModuleContext> Context<'module, M> {
|
||||
|
||||
self.free_value(div);
|
||||
|
||||
let rem = if saved.clone().any(|(_, dst)| dst == RAX) {
|
||||
let rem = match rem {
|
||||
ValueLocation::Reg(rem) if saved.clone().any(|(_, dst)| dst == rem) => {
|
||||
let new = self.block_state.regs.take(I32);
|
||||
dynasm!(self.asm
|
||||
; mov Rq(new.rq().unwrap()), rax
|
||||
; mov Rq(new.rq().unwrap()), Rq(rem.rq().unwrap())
|
||||
);
|
||||
new
|
||||
} else {
|
||||
RAX
|
||||
self.block_state.regs.release(rem);
|
||||
ValueLocation::Reg(new)
|
||||
}
|
||||
_ => rem,
|
||||
};
|
||||
|
||||
self.cleanup_gprs(saved);
|
||||
|
||||
self.push(ValueLocation::Reg(rem));
|
||||
self.push(rem);
|
||||
}
|
||||
|
||||
pub fn i32_rem_s(&mut self) {
|
||||
@@ -2554,46 +2705,21 @@ impl<'module, M: ModuleContext> Context<'module, M> {
|
||||
|
||||
self.free_value(div);
|
||||
|
||||
let rem = if saved.clone().any(|(_, dst)| dst == RAX) {
|
||||
let rem = match rem {
|
||||
ValueLocation::Reg(rem) if saved.clone().any(|(_, dst)| dst == rem) => {
|
||||
let new = self.block_state.regs.take(I32);
|
||||
dynasm!(self.asm
|
||||
; mov Rq(new.rq().unwrap()), rax
|
||||
; mov Rq(new.rq().unwrap()), Rq(rem.rq().unwrap())
|
||||
);
|
||||
new
|
||||
} else {
|
||||
RAX
|
||||
self.block_state.regs.release(rem);
|
||||
ValueLocation::Reg(new)
|
||||
}
|
||||
_ => rem,
|
||||
};
|
||||
|
||||
self.cleanup_gprs(saved);
|
||||
|
||||
self.push(ValueLocation::Reg(rem));
|
||||
}
|
||||
|
||||
// TODO: Fast div using mul for constant divisor? It looks like LLVM doesn't do that for us when
|
||||
// emitting Wasm.
|
||||
pub fn i32_div_s(&mut self) {
|
||||
let divisor = self.pop();
|
||||
let quotient = self.pop();
|
||||
|
||||
if let (Some(quotient), Some(divisor)) = (quotient.imm_i32(), divisor.imm_i32()) {
|
||||
if divisor == 0 {
|
||||
self.trap();
|
||||
self.push(ValueLocation::Immediate(0u32.into()));
|
||||
} else {
|
||||
self.push(ValueLocation::Immediate(
|
||||
i32::wrapping_div(quotient, divisor).into(),
|
||||
));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
let (div, rem, saved) = self.i32_full_div_s(divisor, quotient);
|
||||
self.free_value(rem);
|
||||
|
||||
self.cleanup_gprs(saved);
|
||||
|
||||
self.push(div);
|
||||
self.push(rem);
|
||||
}
|
||||
|
||||
// `i32_mul` needs to be separate because the immediate form of the instruction
|
||||
@@ -3141,3 +3267,4 @@ impl<'module, M: ModuleContext> Context<'module, M> {
|
||||
label
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -288,7 +288,8 @@ where
|
||||
|
||||
**then_cc = {
|
||||
let mut cc = cc.clone();
|
||||
if let (Some(cc), Some(to_drop)) = (cc.as_mut(), then.to_drop.clone())
|
||||
if let (Some(cc), Some(to_drop)) =
|
||||
(cc.as_mut(), then.to_drop.clone())
|
||||
{
|
||||
match cc {
|
||||
Left(cc) => drop_elements(&mut cc.arguments, to_drop),
|
||||
@@ -299,7 +300,8 @@ where
|
||||
};
|
||||
**else_cc = {
|
||||
let mut cc = cc;
|
||||
if let (Some(cc), Some(to_drop)) = (cc.as_mut(), else_.to_drop.clone())
|
||||
if let (Some(cc), Some(to_drop)) =
|
||||
(cc.as_mut(), else_.to_drop.clone())
|
||||
{
|
||||
match cc {
|
||||
Left(cc) => drop_elements(&mut cc.arguments, to_drop),
|
||||
@@ -357,8 +359,6 @@ where
|
||||
|
||||
if let Some(max) = max_num_callers {
|
||||
max_num_callers = block.num_callers.map(|n| max.max(n));
|
||||
} else {
|
||||
max_num_callers = block.num_callers;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -387,8 +387,8 @@ where
|
||||
}
|
||||
});
|
||||
}
|
||||
Operator::Swap { depth } => ctx.swap(depth),
|
||||
Operator::Pick { depth } => ctx.pick(depth),
|
||||
Operator::Swap(depth) => ctx.swap(depth),
|
||||
Operator::Pick(depth) => ctx.pick(depth),
|
||||
Operator::Eq(I32) => ctx.i32_eq(),
|
||||
Operator::Eqz(Size::_32) => ctx.i32_eqz(),
|
||||
Operator::Ne(I32) => ctx.i32_neq(),
|
||||
@@ -408,8 +408,8 @@ where
|
||||
Operator::Mul(I32) => ctx.i32_mul(),
|
||||
Operator::Div(SU32) => ctx.i32_div_u(),
|
||||
Operator::Div(SI32) => ctx.i32_div_s(),
|
||||
Operator::Rem(sint::I32) => ctx.i32_rem_u(),
|
||||
Operator::Rem(sint::U32) => ctx.i32_rem_s(),
|
||||
Operator::Rem(sint::I32) => ctx.i32_rem_s(),
|
||||
Operator::Rem(sint::U32) => ctx.i32_rem_u(),
|
||||
Operator::Shl(Size::_32) => ctx.i32_shl(),
|
||||
Operator::Shr(sint::I32) => ctx.i32_shr_s(),
|
||||
Operator::Shr(sint::U32) => ctx.i32_shr_u(),
|
||||
@@ -446,6 +446,7 @@ where
|
||||
Operator::Add(F32) => ctx.f32_add(),
|
||||
Operator::Mul(F32) => ctx.f32_mul(),
|
||||
Operator::Sub(F32) => ctx.f32_sub(),
|
||||
Operator::Div(SF32) => ctx.f32_div(),
|
||||
Operator::Neg(Size::_32) => ctx.f32_neg(),
|
||||
Operator::Gt(SF32) => ctx.f32_gt(),
|
||||
Operator::Ge(SF32) => ctx.f32_ge(),
|
||||
@@ -454,6 +455,7 @@ where
|
||||
Operator::Add(F64) => ctx.f64_add(),
|
||||
Operator::Mul(F64) => ctx.f64_mul(),
|
||||
Operator::Sub(F64) => ctx.f64_sub(),
|
||||
Operator::Div(SF64) => ctx.f64_div(),
|
||||
Operator::Neg(Size::_64) => ctx.f64_neg(),
|
||||
Operator::Gt(SF64) => ctx.f64_gt(),
|
||||
Operator::Ge(SF64) => ctx.f64_ge(),
|
||||
@@ -462,6 +464,17 @@ where
|
||||
Operator::Drop(range) => ctx.drop(range),
|
||||
Operator::Const(val) => ctx.const_(val),
|
||||
Operator::I32WrapFromI64 => {}
|
||||
// All reinterpret operators are no-ops - we do the conversion at the point of usage.
|
||||
Operator::I32ReinterpretFromF32 => {}
|
||||
Operator::I64ReinterpretFromF64 => {}
|
||||
Operator::F32ReinterpretFromI32 => {}
|
||||
Operator::F64ReinterpretFromI64 => {}
|
||||
Operator::ITruncFromF {
|
||||
input_ty: Size::_32,
|
||||
output_ty: SignfulInt(_, Size::_32),
|
||||
} => {
|
||||
ctx.i32_truncate_f32();
|
||||
}
|
||||
Operator::Extend {
|
||||
sign: Signedness::Unsigned,
|
||||
} => ctx.i32_extend_u(),
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
never_type,
|
||||
alloc_layout_extra,
|
||||
try_from,
|
||||
try_trait
|
||||
try_trait,
|
||||
bind_by_move_pattern_guards
|
||||
)]
|
||||
#![plugin(dynasm)]
|
||||
|
||||
|
||||
@@ -174,7 +174,7 @@ type Int = Size;
|
||||
type Float = Size;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub struct SignfulInt(Signedness, Size);
|
||||
pub struct SignfulInt(pub Signedness, pub Size);
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum Type<I> {
|
||||
@@ -461,16 +461,12 @@ pub enum Operator<Label> {
|
||||
Select,
|
||||
/// Duplicate the element at depth `depth` to the top of the stack. This can be used to implement
|
||||
/// `GetLocal`.
|
||||
Pick {
|
||||
depth: u32,
|
||||
},
|
||||
Pick(u32),
|
||||
/// Swap the top element of the stack with the element at depth `depth`. This can be used to implement
|
||||
/// `SetLocal`.
|
||||
// TODO: Is it better to have `Swap`, to have `Pull` (which moves the `nth` element instead of swapping)
|
||||
// or to have both?
|
||||
Swap {
|
||||
depth: u32,
|
||||
},
|
||||
Swap(u32),
|
||||
GetGlobal {
|
||||
index: u32,
|
||||
},
|
||||
@@ -715,8 +711,8 @@ where
|
||||
Ok(())
|
||||
}
|
||||
Operator::Select => write!(f, "select"),
|
||||
Operator::Pick { depth } => write!(f, "pick {}", depth),
|
||||
Operator::Swap { depth } => write!(f, "swap {}", depth),
|
||||
Operator::Pick(depth) => write!(f, "pick {}", depth),
|
||||
Operator::Swap(depth) => write!(f, "swap {}", depth),
|
||||
Operator::Load { ty, memarg } => {
|
||||
write!(f, "{}.load {}, {}", ty, memarg.flags, memarg.offset)
|
||||
}
|
||||
@@ -1697,19 +1693,19 @@ where
|
||||
WasmOperator::GetLocal { local_index } => {
|
||||
// TODO: `- 1` because we apply the stack difference _before_ this point
|
||||
let depth = self.local_depth(local_index) - 1;
|
||||
smallvec![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;
|
||||
smallvec![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);
|
||||
smallvec![
|
||||
Operator::Swap { depth },
|
||||
Operator::Swap(depth),
|
||||
Operator::Drop(0..=0),
|
||||
Operator::Pick { depth: depth - 1 },
|
||||
Operator::Pick(depth - 1),
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1830,8 +1826,49 @@ where
|
||||
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)],
|
||||
// Unlike Wasm, our `rem_s` instruction _does_ trap on `-1`. Instead
|
||||
// of handling this complexity in the backend, we handle it here
|
||||
// (where it's way easier to debug).
|
||||
WasmOperator::I32RemS => {
|
||||
let id = self.next_id();
|
||||
let params = self.block_params();
|
||||
|
||||
let then = (id, NameTag::Header);
|
||||
let else_ = (id, NameTag::Else);
|
||||
let end = (id, NameTag::End);
|
||||
|
||||
let mut end_params = self.block_params();
|
||||
|
||||
end_params.pop();
|
||||
end_params.pop();
|
||||
end_params.push(I32);
|
||||
|
||||
smallvec![
|
||||
Operator::block(self.block_params(), then),
|
||||
Operator::block(self.block_params(), else_),
|
||||
Operator::end(end_params, end),
|
||||
Operator::Pick(0),
|
||||
Operator::Const((-1i32).into()),
|
||||
Operator::Ne(I32),
|
||||
Operator::BrIf {
|
||||
then: BrTarget::Label(then).into(),
|
||||
else_: BrTarget::Label(else_).into()
|
||||
},
|
||||
Operator::Label(then),
|
||||
Operator::Rem(sint::I32),
|
||||
Operator::Br {
|
||||
target: BrTarget::Label(end).into()
|
||||
},
|
||||
Operator::Label(else_),
|
||||
Operator::Drop(0..=1),
|
||||
Operator::Const(0i32.into()),
|
||||
Operator::Br {
|
||||
target: BrTarget::Label(end).into()
|
||||
},
|
||||
Operator::Label(end),
|
||||
]
|
||||
}
|
||||
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)],
|
||||
@@ -1887,8 +1924,14 @@ where
|
||||
WasmOperator::F64Max => smallvec![Operator::Max(Size::_64)],
|
||||
WasmOperator::F64Copysign => smallvec![Operator::Copysign(Size::_64)],
|
||||
WasmOperator::I32WrapI64 => smallvec![Operator::I32WrapFromI64],
|
||||
WasmOperator::I32TruncSF32 => unimplemented!("{:?}", op),
|
||||
WasmOperator::I32TruncUF32 => unimplemented!("{:?}", op),
|
||||
WasmOperator::I32TruncSF32 => smallvec![Operator::ITruncFromF {
|
||||
input_ty: Size::_32,
|
||||
output_ty: sint::I32
|
||||
}],
|
||||
WasmOperator::I32TruncUF32 => smallvec![Operator::ITruncFromF {
|
||||
input_ty: Size::_32,
|
||||
output_ty: sint::U32
|
||||
}],
|
||||
WasmOperator::I32TruncSF64 => unimplemented!("{:?}", op),
|
||||
WasmOperator::I32TruncUF64 => unimplemented!("{:?}", op),
|
||||
WasmOperator::I64ExtendSI32 => smallvec![Operator::Extend {
|
||||
@@ -1911,10 +1954,10 @@ where
|
||||
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::I32ReinterpretF32 => smallvec![Operator::I32ReinterpretFromF32],
|
||||
WasmOperator::I64ReinterpretF64 => smallvec![Operator::I64ReinterpretFromF64],
|
||||
WasmOperator::F32ReinterpretI32 => smallvec![Operator::F32ReinterpretFromI32],
|
||||
WasmOperator::F64ReinterpretI64 => smallvec![Operator::F64ReinterpretFromI64],
|
||||
WasmOperator::I32Extend8S => unimplemented!("{:?}", op),
|
||||
WasmOperator::I32Extend16S => unimplemented!("{:?}", op),
|
||||
WasmOperator::I64Extend8S => unimplemented!("{:?}", op),
|
||||
@@ -1936,3 +1979,4 @@ where
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
61
src/tests.rs
61
src/tests.rs
@@ -1193,35 +1193,63 @@ fn i32_div() {
|
||||
let translated = translate_wat(CODE);
|
||||
translated.disassemble();
|
||||
|
||||
assert_eq!(translated.execute_func::<_, u32>(0, (1, 1)), Ok(1));
|
||||
assert_eq!(translated.execute_func::<_, u32>(0, (-1, -1)), Ok(1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn i32_rem() {
|
||||
const CODE: &str = r"
|
||||
(module
|
||||
(func (param i32) (param i32) (result i32)
|
||||
(i32.rem_s (get_local 0) (get_local 1))
|
||||
)
|
||||
)";
|
||||
|
||||
let translated = translate_wat(CODE);
|
||||
translated.disassemble();
|
||||
|
||||
assert_eq!(translated.execute_func::<_, u32>(0, (123121, -1)), Ok(0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn br_table() {
|
||||
const CODE: &str = r"
|
||||
(module
|
||||
(func
|
||||
(block (br_table 0 0 0 (i32.const 0)) (call $dummy))
|
||||
(func (param $i i32) (result i32)
|
||||
(return
|
||||
(block $2 (result i32)
|
||||
(i32.add (i32.const 10)
|
||||
(block $1 (result i32)
|
||||
(i32.add (i32.const 100)
|
||||
(block $0 (result i32)
|
||||
(i32.add (i32.const 1000)
|
||||
(block $default (result i32)
|
||||
(br_table $0 $1 $2 $default
|
||||
(i32.mul (i32.const 2) (get_local $i))
|
||||
(i32.and (i32.const 3) (get_local $i))
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(func
|
||||
(block (call $dummy) (br_table 0 0 0 (i32.const 0)) (call $dummy))
|
||||
)
|
||||
(func
|
||||
(block (nop) (call $dummy) (br_table 0 0 0 (i32.const 0)))
|
||||
)
|
||||
(func $dummy)
|
||||
)
|
||||
";
|
||||
|
||||
let translated = translate_wat(CODE);
|
||||
translated.disassemble();
|
||||
|
||||
println!("as-block-first");
|
||||
assert_eq!(translated.execute_func::<_, ()>(0, ()), Ok(()),);
|
||||
println!("as-block-mid");
|
||||
assert_eq!(translated.execute_func::<_, ()>(1, ()), Ok(()),);
|
||||
println!("as-block-last");
|
||||
assert_eq!(translated.execute_func::<_, ()>(2, ()), Ok(()),);
|
||||
assert_eq!(translated.execute_func::<_, u32>(0, (0u32,)), Ok(110));
|
||||
assert_eq!(translated.execute_func::<_, u32>(0, (1u32,)), Ok(12));
|
||||
assert_eq!(translated.execute_func::<_, u32>(0, (2u32,)), Ok(4));
|
||||
assert_eq!(translated.execute_func::<_, u32>(0, (3u32,)), Ok(1116));
|
||||
assert_eq!(translated.execute_func::<_, u32>(0, (4u32,)), Ok(118));
|
||||
assert_eq!(translated.execute_func::<_, u32>(0, (5u32,)), Ok(20));
|
||||
assert_eq!(translated.execute_func::<_, u32>(0, (6u32,)), Ok(12));
|
||||
assert_eq!(translated.execute_func::<_, u32>(0, (7u32,)), Ok(1124));
|
||||
assert_eq!(translated.execute_func::<_, u32>(0, (8u32,)), Ok(126));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1955,3 +1983,4 @@ fn sieve() {
|
||||
|
||||
translate(&wabt::wat2wasm(CODE).unwrap()).unwrap();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user