Add conversions, update tests in readme
This commit is contained in:
36
README.md
36
README.md
@@ -172,14 +172,14 @@ running 76 tests
|
|||||||
test misc_testsuite::stack_overflow ... ok
|
test misc_testsuite::stack_overflow ... ok
|
||||||
test spec_testsuite::binary ... ok
|
test spec_testsuite::binary ... ok
|
||||||
test misc_testsuite::misc_traps ... ok
|
test misc_testsuite::misc_traps ... ok
|
||||||
test spec_testsuite::block ... FAILED
|
|
||||||
test spec_testsuite::align ... FAILED
|
test spec_testsuite::align ... FAILED
|
||||||
|
test spec_testsuite::block ... FAILED
|
||||||
test spec_testsuite::br_if ... FAILED
|
test spec_testsuite::br_if ... FAILED
|
||||||
test spec_testsuite::break_drop ... ok
|
test spec_testsuite::break_drop ... ok
|
||||||
test spec_testsuite::call ... FAILED
|
test spec_testsuite::call ... FAILED
|
||||||
test spec_testsuite::call_indirect ... FAILED
|
test spec_testsuite::call_indirect ... FAILED
|
||||||
test spec_testsuite::comments ... ok
|
|
||||||
test spec_testsuite::address ... FAILED
|
test spec_testsuite::address ... FAILED
|
||||||
|
test spec_testsuite::comments ... ok
|
||||||
test spec_testsuite::const_ ... ok
|
test spec_testsuite::const_ ... ok
|
||||||
test spec_testsuite::custom ... ok
|
test spec_testsuite::custom ... ok
|
||||||
test spec_testsuite::custom_section ... ok
|
test spec_testsuite::custom_section ... ok
|
||||||
@@ -193,60 +193,60 @@ test spec_testsuite::f32_bitwise ... FAILED
|
|||||||
test spec_testsuite::br_table ... FAILED
|
test spec_testsuite::br_table ... FAILED
|
||||||
test spec_testsuite::f64_bitwise ... FAILED
|
test spec_testsuite::f64_bitwise ... FAILED
|
||||||
test spec_testsuite::f32 ... FAILED
|
test spec_testsuite::f32 ... FAILED
|
||||||
test spec_testsuite::fac ... ok
|
|
||||||
test spec_testsuite::f32_cmp ... FAILED
|
test spec_testsuite::f32_cmp ... FAILED
|
||||||
|
test spec_testsuite::fac ... ok
|
||||||
test spec_testsuite::f64 ... FAILED
|
test spec_testsuite::f64 ... FAILED
|
||||||
test spec_testsuite::f64_cmp ... FAILED
|
|
||||||
test spec_testsuite::float_memory ... ok
|
test spec_testsuite::float_memory ... ok
|
||||||
|
test spec_testsuite::f64_cmp ... FAILED
|
||||||
test spec_testsuite::forward ... ok
|
test spec_testsuite::forward ... ok
|
||||||
test spec_testsuite::float_misc ... FAILED
|
test spec_testsuite::float_misc ... FAILED
|
||||||
test spec_testsuite::func_ptrs ... FAILED
|
test spec_testsuite::func_ptrs ... FAILED
|
||||||
test spec_testsuite::get_local ... FAILED
|
test spec_testsuite::get_local ... FAILED
|
||||||
test spec_testsuite::globals ... FAILED
|
|
||||||
test spec_testsuite::float_exprs ... FAILED
|
test spec_testsuite::float_exprs ... FAILED
|
||||||
test spec_testsuite::i64 ... FAILED
|
test spec_testsuite::globals ... FAILED
|
||||||
|
test spec_testsuite::float_literals ... ok
|
||||||
test spec_testsuite::if_ ... FAILED
|
test spec_testsuite::if_ ... FAILED
|
||||||
test spec_testsuite::imports ... FAILED
|
test spec_testsuite::imports ... FAILED
|
||||||
test spec_testsuite::inline_module ... ok
|
test spec_testsuite::inline_module ... ok
|
||||||
test spec_testsuite::float_literals ... ok
|
|
||||||
test spec_testsuite::func ... FAILED
|
test spec_testsuite::func ... FAILED
|
||||||
test spec_testsuite::int_exprs ... FAILED
|
|
||||||
test spec_testsuite::i32 ... ok
|
test spec_testsuite::i32 ... ok
|
||||||
test spec_testsuite::linking ... FAILED
|
test spec_testsuite::i64 ... ok
|
||||||
test spec_testsuite::left_to_right ... FAILED
|
test spec_testsuite::left_to_right ... FAILED
|
||||||
|
test spec_testsuite::linking ... FAILED
|
||||||
test spec_testsuite::loop_ ... FAILED
|
test spec_testsuite::loop_ ... FAILED
|
||||||
|
test spec_testsuite::labels ... ok
|
||||||
test spec_testsuite::int_literals ... ok
|
test spec_testsuite::int_literals ... ok
|
||||||
test spec_testsuite::memory_grow ... FAILED
|
test spec_testsuite::memory_grow ... FAILED
|
||||||
test spec_testsuite::memory_redundancy ... ok
|
test spec_testsuite::memory_redundancy ... ok
|
||||||
test spec_testsuite::memory ... FAILED
|
test spec_testsuite::memory ... FAILED
|
||||||
test spec_testsuite::memory_trap ... FAILED
|
test spec_testsuite::memory_trap ... FAILED
|
||||||
test spec_testsuite::labels ... ok
|
|
||||||
test spec_testsuite::resizing ... FAILED
|
test spec_testsuite::resizing ... FAILED
|
||||||
test spec_testsuite::return_minimal ... ok
|
|
||||||
test spec_testsuite::nop ... FAILED
|
test spec_testsuite::nop ... FAILED
|
||||||
test spec_testsuite::set_local ... FAILED
|
test spec_testsuite::return_minimal ... ok
|
||||||
test spec_testsuite::select ... FAILED
|
test spec_testsuite::select ... FAILED
|
||||||
test spec_testsuite::stack ... FAILED
|
test spec_testsuite::set_local ... FAILED
|
||||||
test spec_testsuite::start ... FAILED
|
|
||||||
test spec_testsuite::store_retval ... ok
|
|
||||||
test spec_testsuite::skip_stack_guard_page ... FAILED
|
test spec_testsuite::skip_stack_guard_page ... FAILED
|
||||||
|
test spec_testsuite::stack ... FAILED
|
||||||
|
test spec_testsuite::int_exprs ... ok
|
||||||
|
test spec_testsuite::store_retval ... ok
|
||||||
|
test spec_testsuite::start ... FAILED
|
||||||
test spec_testsuite::switch ... ok
|
test spec_testsuite::switch ... ok
|
||||||
test spec_testsuite::token ... ok
|
test spec_testsuite::token ... ok
|
||||||
test spec_testsuite::tee_local ... FAILED
|
test spec_testsuite::tee_local ... FAILED
|
||||||
test spec_testsuite::type_ ... ok
|
test spec_testsuite::type_ ... ok
|
||||||
test spec_testsuite::traps ... FAILED
|
test spec_testsuite::traps ... FAILED
|
||||||
test spec_testsuite::typecheck ... ok
|
test spec_testsuite::typecheck ... ok
|
||||||
|
test spec_testsuite::return_ ... ok
|
||||||
test spec_testsuite::unreached_invalid ... ok
|
test spec_testsuite::unreached_invalid ... ok
|
||||||
test spec_testsuite::unwind ... FAILED
|
test spec_testsuite::unwind ... FAILED
|
||||||
test spec_testsuite::utf8_custom_section_id ... ok
|
test spec_testsuite::utf8_custom_section_id ... ok
|
||||||
test spec_testsuite::utf8_import_field ... 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::utf8_invalid_encoding ... ok
|
||||||
|
test spec_testsuite::utf8_import_module ... ok
|
||||||
test spec_testsuite::unreachable ... ok
|
test spec_testsuite::unreachable ... ok
|
||||||
test spec_testsuite::names ... FAILED
|
test spec_testsuite::names ... FAILED
|
||||||
|
|
||||||
test result: FAILED. 33 passed; 43 failed; 0 ignored; 0 measured; 0 filtered out
|
test result: FAILED. 35 passed; 41 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.
|
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.
|
||||||
|
|||||||
432
src/backend.rs
432
src/backend.rs
@@ -663,6 +663,162 @@ impl StackDepth {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro_rules! int_div {
|
||||||
|
($full_div_s:ident, $full_div_u:ident, $div_u:ident, $div_s:ident, $rem_u:ident, $rem_s:ident, $imm_fn:ident, $signed_ty:ty, $unsigned_ty:ty) => {
|
||||||
|
// TODO: Fast div using mul for constant divisor? It looks like LLVM doesn't do that for us when
|
||||||
|
// emitting Wasm.
|
||||||
|
pub fn $div_u(&mut self) {
|
||||||
|
let divisor = self.pop();
|
||||||
|
let quotient = self.pop();
|
||||||
|
|
||||||
|
if let (Some(quotient), Some(divisor)) = (quotient.$imm_fn(), divisor.$imm_fn()) {
|
||||||
|
if divisor == 0 {
|
||||||
|
self.trap();
|
||||||
|
self.push(ValueLocation::Immediate((0 as $unsigned_ty).into()));
|
||||||
|
} else {
|
||||||
|
self.push(ValueLocation::Immediate(
|
||||||
|
<$unsigned_ty>::wrapping_div(quotient as _, divisor as _).into(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let (div, rem, saved) = self.$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 $div_s(&mut self) {
|
||||||
|
let divisor = self.pop();
|
||||||
|
let quotient = self.pop();
|
||||||
|
|
||||||
|
if let (Some(quotient), Some(divisor)) = (quotient.$imm_fn(), divisor.$imm_fn()) {
|
||||||
|
if divisor == 0 {
|
||||||
|
self.trap();
|
||||||
|
self.push(ValueLocation::Immediate((0 as $signed_ty).into()));
|
||||||
|
} else {
|
||||||
|
self.push(ValueLocation::Immediate(
|
||||||
|
<$signed_ty>::wrapping_div(quotient, divisor).into(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let (div, rem, saved) = self.$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);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn $rem_u(&mut self) {
|
||||||
|
let divisor = self.pop();
|
||||||
|
let quotient = self.pop();
|
||||||
|
|
||||||
|
if let (Some(quotient), Some(divisor)) = (quotient.$imm_fn(), divisor.$imm_fn()) {
|
||||||
|
if divisor == 0 {
|
||||||
|
self.trap();
|
||||||
|
self.push(ValueLocation::Immediate((0 as $unsigned_ty).into()));
|
||||||
|
} else {
|
||||||
|
self.push(ValueLocation::Immediate(
|
||||||
|
(quotient as $unsigned_ty % divisor as $unsigned_ty).into(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let (div, rem, saved) = self.$full_div_u(divisor, quotient);
|
||||||
|
|
||||||
|
self.free_value(div);
|
||||||
|
|
||||||
|
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()), Rq(rem.rq().unwrap())
|
||||||
|
);
|
||||||
|
self.block_state.regs.release(rem);
|
||||||
|
ValueLocation::Reg(new)
|
||||||
|
}
|
||||||
|
_ => rem,
|
||||||
|
};
|
||||||
|
|
||||||
|
self.cleanup_gprs(saved);
|
||||||
|
|
||||||
|
self.push(rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn $rem_s(&mut self) {
|
||||||
|
let divisor = self.pop();
|
||||||
|
let quotient = self.pop();
|
||||||
|
|
||||||
|
if let (Some(quotient), Some(divisor)) = (quotient.$imm_fn(), divisor.$imm_fn()) {
|
||||||
|
if divisor == 0 {
|
||||||
|
self.trap();
|
||||||
|
self.push(ValueLocation::Immediate((0 as $signed_ty).into()));
|
||||||
|
} else {
|
||||||
|
self.push(ValueLocation::Immediate((quotient % divisor).into()));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let (div, rem, saved) = self.$full_div_s(divisor, quotient);
|
||||||
|
|
||||||
|
self.free_value(div);
|
||||||
|
|
||||||
|
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()), Rq(rem.rq().unwrap())
|
||||||
|
);
|
||||||
|
self.block_state.regs.release(rem);
|
||||||
|
ValueLocation::Reg(new)
|
||||||
|
}
|
||||||
|
_ => rem,
|
||||||
|
};
|
||||||
|
|
||||||
|
self.cleanup_gprs(saved);
|
||||||
|
|
||||||
|
self.push(rem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
macro_rules! unop {
|
macro_rules! unop {
|
||||||
($name:ident, $instr:ident, $reg_ty:ident, $typ:ty, $const_fallback:expr) => {
|
($name:ident, $instr:ident, $reg_ty:ident, $typ:ty, $const_fallback:expr) => {
|
||||||
pub fn $name(&mut self) {
|
pub fn $name(&mut self) {
|
||||||
@@ -715,13 +871,13 @@ macro_rules! conversion {
|
|||||||
let out_val = match val {
|
let out_val = match val {
|
||||||
ValueLocation::Immediate(imm) =>
|
ValueLocation::Immediate(imm) =>
|
||||||
ValueLocation::Immediate(
|
ValueLocation::Immediate(
|
||||||
($const_fallback(imm.$const_ty_fn().unwrap()) as $out_typ).into()
|
$const_fallback(imm.$const_ty_fn().unwrap()).into()
|
||||||
),
|
),
|
||||||
ValueLocation::Stack(offset) => {
|
ValueLocation::Stack(offset) => {
|
||||||
let offset = self.adjusted_offset(offset);
|
let offset = self.adjusted_offset(offset);
|
||||||
let temp = self.block_state.regs.take(Type::for_::<$out_typ>());
|
let temp = self.block_state.regs.take(Type::for_::<$out_typ>());
|
||||||
dynasm!(self.asm
|
dynasm!(self.asm
|
||||||
; $instr $out_reg_ty(temp.rq().unwrap()), [rsp + offset]
|
; $instr $out_reg_ty(temp.$out_reg_fn().unwrap()), DWORD [rsp + offset]
|
||||||
);
|
);
|
||||||
ValueLocation::Reg(temp)
|
ValueLocation::Reg(temp)
|
||||||
}
|
}
|
||||||
@@ -2334,6 +2490,47 @@ impl<'module, M: ModuleContext> Context<'module, M> {
|
|||||||
as_f32,
|
as_f32,
|
||||||
|a: wasmparser::Ieee32| a.bits()
|
|a: wasmparser::Ieee32| a.bits()
|
||||||
);
|
);
|
||||||
|
conversion!(
|
||||||
|
f32_convert_from_i32_s,
|
||||||
|
cvtsi2ss,
|
||||||
|
Rd,
|
||||||
|
rq,
|
||||||
|
Rx,
|
||||||
|
rx,
|
||||||
|
i32,
|
||||||
|
f32,
|
||||||
|
as_i32,
|
||||||
|
|a| wasmparser::Ieee32((a as f32).to_bits())
|
||||||
|
);
|
||||||
|
|
||||||
|
pub fn f32_convert_from_i32_u(&mut self) {
|
||||||
|
let mut val = self.pop();
|
||||||
|
|
||||||
|
let out_val = match val {
|
||||||
|
ValueLocation::Immediate(imm) =>
|
||||||
|
ValueLocation::Immediate(
|
||||||
|
wasmparser::Ieee32((imm.as_i32().unwrap() as u32 as f32).to_bits()).into()
|
||||||
|
),
|
||||||
|
_ => {
|
||||||
|
let reg = self.into_reg(I32, val);
|
||||||
|
val = ValueLocation::Reg(reg);
|
||||||
|
|
||||||
|
let temp = self.block_state.regs.take(F32);
|
||||||
|
|
||||||
|
dynasm!(self.asm
|
||||||
|
; mov Rq(reg.rq().unwrap()), Rq(reg.rq().unwrap())
|
||||||
|
; cvtsi2ss Rx(temp.rx().unwrap()), Rq(reg.rq().unwrap())
|
||||||
|
);
|
||||||
|
|
||||||
|
ValueLocation::Reg(temp)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
self.free_value(val);
|
||||||
|
|
||||||
|
self.push(out_val);
|
||||||
|
}
|
||||||
|
|
||||||
unop!(i64_popcnt, popcnt, Rq, u64, |a: u64| a.count_ones() as u64);
|
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
|
// TODO: Use `lea` when the LHS operand isn't a temporary but both of the operands
|
||||||
@@ -2432,10 +2629,41 @@ impl<'module, M: ModuleContext> Context<'module, M> {
|
|||||||
I64
|
I64
|
||||||
);
|
);
|
||||||
|
|
||||||
|
fn cleanup_gprs(&mut self, gprs: impl Iterator<Item = (GPR, GPR)>) {
|
||||||
|
for (src, dst) in gprs {
|
||||||
|
self.copy_value(&ValueLocation::Reg(src), &mut ValueLocation::Reg(dst));
|
||||||
|
self.block_state.regs.release(src);
|
||||||
|
self.block_state.regs.mark_used(dst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int_div!(
|
||||||
|
i32_full_div_s,
|
||||||
|
i32_full_div_u,
|
||||||
|
i32_div_u,
|
||||||
|
i32_div_s,
|
||||||
|
i32_rem_u,
|
||||||
|
i32_rem_s,
|
||||||
|
imm_i32,
|
||||||
|
i32,
|
||||||
|
u32
|
||||||
|
);
|
||||||
|
int_div!(
|
||||||
|
i64_full_div_s,
|
||||||
|
i64_full_div_u,
|
||||||
|
i64_div_u,
|
||||||
|
i64_div_s,
|
||||||
|
i64_rem_u,
|
||||||
|
i64_rem_s,
|
||||||
|
imm_i64,
|
||||||
|
i64,
|
||||||
|
u64
|
||||||
|
);
|
||||||
|
|
||||||
/// Returned divisor is guaranteed not to be `RAX`
|
/// Returned divisor is guaranteed not to be `RAX`
|
||||||
// TODO: With a proper SSE-like "Value" system we could do this way better (we wouldn't have
|
// TODO: With a proper SSE-like "Value" system we could do this way better (we wouldn't have
|
||||||
// to move `RAX` back afterwards).
|
// to move `RAX` back afterwards).
|
||||||
fn i32_full_div(
|
fn full_div(
|
||||||
&mut self,
|
&mut self,
|
||||||
divisor: ValueLocation,
|
divisor: ValueLocation,
|
||||||
quotient: ValueLocation,
|
quotient: ValueLocation,
|
||||||
@@ -2517,12 +2745,12 @@ impl<'module, M: ModuleContext> Context<'module, M> {
|
|||||||
ValueLocation,
|
ValueLocation,
|
||||||
impl Iterator<Item = (GPR, GPR)> + Clone + 'module,
|
impl Iterator<Item = (GPR, GPR)> + Clone + 'module,
|
||||||
) {
|
) {
|
||||||
self.i32_full_div(divisor, quotient, |this, divisor| match divisor {
|
self.full_div(divisor, quotient, |this, divisor| match divisor {
|
||||||
ValueLocation::Stack(offset) => {
|
ValueLocation::Stack(offset) => {
|
||||||
let offset = this.adjusted_offset(offset);
|
let offset = this.adjusted_offset(offset);
|
||||||
dynasm!(this.asm
|
dynasm!(this.asm
|
||||||
; xor edx, edx
|
; xor edx, edx
|
||||||
; div [rsp + offset]
|
; div DWORD [rsp + offset]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
ValueLocation::Reg(r) => {
|
ValueLocation::Reg(r) => {
|
||||||
@@ -2544,12 +2772,12 @@ impl<'module, M: ModuleContext> Context<'module, M> {
|
|||||||
ValueLocation,
|
ValueLocation,
|
||||||
impl Iterator<Item = (GPR, GPR)> + Clone + 'module,
|
impl Iterator<Item = (GPR, GPR)> + Clone + 'module,
|
||||||
) {
|
) {
|
||||||
self.i32_full_div(divisor, quotient, |this, divisor| match divisor {
|
self.full_div(divisor, quotient, |this, divisor| match divisor {
|
||||||
ValueLocation::Stack(offset) => {
|
ValueLocation::Stack(offset) => {
|
||||||
let offset = this.adjusted_offset(offset);
|
let offset = this.adjusted_offset(offset);
|
||||||
dynasm!(this.asm
|
dynasm!(this.asm
|
||||||
; cdq
|
; cdq
|
||||||
; idiv [rsp + offset]
|
; idiv DWORD [rsp + offset]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
ValueLocation::Reg(r) => {
|
ValueLocation::Reg(r) => {
|
||||||
@@ -2562,164 +2790,58 @@ impl<'module, M: ModuleContext> Context<'module, M> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cleanup_gprs(&mut self, gprs: impl Iterator<Item = (GPR, GPR)>) {
|
fn i64_full_div_u(
|
||||||
for (src, dst) in gprs {
|
&mut self,
|
||||||
self.copy_value(&ValueLocation::Reg(src), &mut ValueLocation::Reg(dst));
|
divisor: ValueLocation,
|
||||||
self.block_state.regs.release(src);
|
quotient: ValueLocation,
|
||||||
self.block_state.regs.mark_used(dst);
|
) -> (
|
||||||
}
|
ValueLocation,
|
||||||
}
|
ValueLocation,
|
||||||
|
impl Iterator<Item = (GPR, GPR)> + Clone + 'module,
|
||||||
// TODO: Fast div using mul for constant divisor? It looks like LLVM doesn't do that for us when
|
) {
|
||||||
// emitting Wasm.
|
self.full_div(divisor, quotient, |this, divisor| match divisor {
|
||||||
pub fn i32_div_u(&mut self) {
|
ValueLocation::Stack(offset) => {
|
||||||
let divisor = self.pop();
|
let offset = this.adjusted_offset(offset);
|
||||||
let quotient = self.pop();
|
dynasm!(this.asm
|
||||||
|
; xor rdx, rdx
|
||||||
if let (Some(quotient), Some(divisor)) = (quotient.imm_i32(), divisor.imm_i32()) {
|
; div QWORD [rsp + offset]
|
||||||
if divisor == 0 {
|
|
||||||
self.trap();
|
|
||||||
self.push(ValueLocation::Immediate(0u32.into()));
|
|
||||||
} else {
|
|
||||||
self.push(ValueLocation::Immediate(
|
|
||||||
u32::wrapping_div(quotient as _, divisor as _).into(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
ValueLocation::Reg(r) => {
|
||||||
};
|
dynasm!(this.asm
|
||||||
|
; xor rdx, rdx
|
||||||
self.cleanup_gprs(saved);
|
; div Rq(r.rq().unwrap())
|
||||||
|
|
||||||
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,
|
ValueLocation::Immediate(_) => unreachable!(),
|
||||||
};
|
})
|
||||||
|
|
||||||
self.cleanup_gprs(saved);
|
|
||||||
|
|
||||||
self.push(div);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn i32_rem_u(&mut self) {
|
fn i64_full_div_s(
|
||||||
let divisor = self.pop();
|
&mut self,
|
||||||
let quotient = self.pop();
|
divisor: ValueLocation,
|
||||||
|
quotient: ValueLocation,
|
||||||
if let (Some(quotient), Some(divisor)) = (quotient.imm_i32(), divisor.imm_i32()) {
|
) -> (
|
||||||
if divisor == 0 {
|
ValueLocation,
|
||||||
self.trap();
|
ValueLocation,
|
||||||
self.push(ValueLocation::Immediate(0u32.into()));
|
impl Iterator<Item = (GPR, GPR)> + Clone + 'module,
|
||||||
} else {
|
) {
|
||||||
self.push(ValueLocation::Immediate(
|
self.full_div(divisor, quotient, |this, divisor| match divisor {
|
||||||
(quotient as u32 % divisor as u32).into(),
|
ValueLocation::Stack(offset) => {
|
||||||
));
|
let offset = this.adjusted_offset(offset);
|
||||||
}
|
dynasm!(this.asm
|
||||||
return;
|
; cqo
|
||||||
}
|
; idiv QWORD [rsp + offset]
|
||||||
|
|
||||||
let (div, rem, saved) = self.i32_full_div_u(divisor, quotient);
|
|
||||||
|
|
||||||
self.free_value(div);
|
|
||||||
|
|
||||||
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()), Rq(rem.rq().unwrap())
|
|
||||||
);
|
);
|
||||||
self.block_state.regs.release(rem);
|
|
||||||
ValueLocation::Reg(new)
|
|
||||||
}
|
}
|
||||||
_ => rem,
|
ValueLocation::Reg(r) => {
|
||||||
};
|
dynasm!(this.asm
|
||||||
|
; cqo
|
||||||
self.cleanup_gprs(saved);
|
; idiv Rq(r.rq().unwrap())
|
||||||
|
|
||||||
self.push(rem);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn i32_rem_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((quotient % divisor).into()));
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let (div, rem, saved) = self.i32_full_div_s(divisor, quotient);
|
|
||||||
|
|
||||||
self.free_value(div);
|
|
||||||
|
|
||||||
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()), Rq(rem.rq().unwrap())
|
|
||||||
);
|
);
|
||||||
self.block_state.regs.release(rem);
|
|
||||||
ValueLocation::Reg(new)
|
|
||||||
}
|
}
|
||||||
_ => rem,
|
ValueLocation::Immediate(_) => unreachable!(),
|
||||||
};
|
})
|
||||||
|
|
||||||
self.cleanup_gprs(saved);
|
|
||||||
|
|
||||||
self.push(rem);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// `i32_mul` needs to be separate because the immediate form of the instruction
|
// `i32_mul` needs to be separate because the immediate form of the instruction
|
||||||
|
|||||||
@@ -435,6 +435,10 @@ where
|
|||||||
Operator::Or(Size::_64) => ctx.i64_or(),
|
Operator::Or(Size::_64) => ctx.i64_or(),
|
||||||
Operator::Xor(Size::_64) => ctx.i64_xor(),
|
Operator::Xor(Size::_64) => ctx.i64_xor(),
|
||||||
Operator::Mul(I64) => ctx.i64_mul(),
|
Operator::Mul(I64) => ctx.i64_mul(),
|
||||||
|
Operator::Div(SU64) => ctx.i64_div_u(),
|
||||||
|
Operator::Div(SI64) => ctx.i64_div_s(),
|
||||||
|
Operator::Rem(sint::I64) => ctx.i64_rem_s(),
|
||||||
|
Operator::Rem(sint::U64) => ctx.i64_rem_u(),
|
||||||
Operator::Shl(Size::_64) => ctx.i64_shl(),
|
Operator::Shl(Size::_64) => ctx.i64_shl(),
|
||||||
Operator::Shr(sint::I64) => ctx.i64_shr_s(),
|
Operator::Shr(sint::I64) => ctx.i64_shr_s(),
|
||||||
Operator::Shr(sint::U64) => ctx.i64_shr_u(),
|
Operator::Shr(sint::U64) => ctx.i64_shr_u(),
|
||||||
@@ -481,6 +485,18 @@ where
|
|||||||
Operator::Extend {
|
Operator::Extend {
|
||||||
sign: Signedness::Signed,
|
sign: Signedness::Signed,
|
||||||
} => ctx.i32_extend_s(),
|
} => ctx.i32_extend_s(),
|
||||||
|
Operator::FConvertFromI {
|
||||||
|
input_ty: sint::I32,
|
||||||
|
output_ty: Size::_32
|
||||||
|
} => {
|
||||||
|
ctx.f32_convert_from_i32_s()
|
||||||
|
},
|
||||||
|
Operator::FConvertFromI {
|
||||||
|
input_ty: sint::U32,
|
||||||
|
output_ty: Size::_32
|
||||||
|
} => {
|
||||||
|
ctx.f32_convert_from_i32_u()
|
||||||
|
},
|
||||||
Operator::Load8 {
|
Operator::Load8 {
|
||||||
ty: sint::U32,
|
ty: sint::U32,
|
||||||
memarg,
|
memarg,
|
||||||
|
|||||||
@@ -786,16 +786,20 @@ where
|
|||||||
Operator::Min(ty) => write!(f, "{}.min", Type::<Size>::Float(*ty)),
|
Operator::Min(ty) => write!(f, "{}.min", Type::<Size>::Float(*ty)),
|
||||||
Operator::Max(ty) => write!(f, "{}.max", Type::<Size>::Float(*ty)),
|
Operator::Max(ty) => write!(f, "{}.max", Type::<Size>::Float(*ty)),
|
||||||
Operator::Copysign(ty) => write!(f, "{}.copysign", Type::<Size>::Float(*ty)),
|
Operator::Copysign(ty) => write!(f, "{}.copysign", Type::<Size>::Float(*ty)),
|
||||||
Operator::I32WrapFromI64 => write!(f, "i32.wrapfromi64"),
|
Operator::I32WrapFromI64 => write!(f, "i32.wrap_from.i64"),
|
||||||
Operator::F32DemoteFromF64 => write!(f, "f32.demotefromf64"),
|
Operator::F32DemoteFromF64 => write!(f, "f32.demote_from.f64"),
|
||||||
Operator::F64PromoteFromF32 => write!(f, "f64.promotefromf32"),
|
Operator::F64PromoteFromF32 => write!(f, "f64.promote_from.f32"),
|
||||||
Operator::I32ReinterpretFromF32 => write!(f, "i32.reinterpretfromf32"),
|
Operator::I32ReinterpretFromF32 => write!(f, "i32.reinterpret_from.f32"),
|
||||||
Operator::I64ReinterpretFromF64 => write!(f, "i64.reinterpretfromf64"),
|
Operator::I64ReinterpretFromF64 => write!(f, "i64.reinterpret_from.f64"),
|
||||||
Operator::F32ReinterpretFromI32 => write!(f, "f32.reinterpretfromi32"),
|
Operator::F32ReinterpretFromI32 => write!(f, "f32.reinterpret_from.i32"),
|
||||||
Operator::F64ReinterpretFromI64 => write!(f, "f64.reinterpretfromi64"),
|
Operator::F64ReinterpretFromI64 => write!(f, "f64.reinterpret_from.i64"),
|
||||||
Operator::MemoryCopy => write!(f, "memory.copy"),
|
Operator::MemoryCopy => write!(f, "memory.copy"),
|
||||||
Operator::MemoryFill => write!(f, "memory.fill"),
|
Operator::MemoryFill => write!(f, "memory.fill"),
|
||||||
Operator::TableCopy => write!(f, "table.copy"),
|
Operator::TableCopy => write!(f, "table.copy"),
|
||||||
|
Operator::FConvertFromI {
|
||||||
|
input_ty,
|
||||||
|
output_ty,
|
||||||
|
} => write!(f, "{}.convert_from.{}", input_ty, Type::Float::<Int>(*output_ty)),
|
||||||
_ => unimplemented!(),
|
_ => unimplemented!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1831,7 +1835,6 @@ where
|
|||||||
// (where it's way easier to debug).
|
// (where it's way easier to debug).
|
||||||
WasmOperator::I32RemS => {
|
WasmOperator::I32RemS => {
|
||||||
let id = self.next_id();
|
let id = self.next_id();
|
||||||
let params = self.block_params();
|
|
||||||
|
|
||||||
let then = (id, NameTag::Header);
|
let then = (id, NameTag::Header);
|
||||||
let else_ = (id, NameTag::Else);
|
let else_ = (id, NameTag::Else);
|
||||||
@@ -1885,7 +1888,44 @@ where
|
|||||||
WasmOperator::I64Mul => smallvec![Operator::Mul(I64)],
|
WasmOperator::I64Mul => smallvec![Operator::Mul(I64)],
|
||||||
WasmOperator::I64DivS => smallvec![Operator::Div(SI64)],
|
WasmOperator::I64DivS => smallvec![Operator::Div(SI64)],
|
||||||
WasmOperator::I64DivU => smallvec![Operator::Div(SU64)],
|
WasmOperator::I64DivU => smallvec![Operator::Div(SU64)],
|
||||||
WasmOperator::I64RemS => smallvec![Operator::Rem(sint::I64)],
|
WasmOperator::I64RemS => {
|
||||||
|
let id = self.next_id();
|
||||||
|
|
||||||
|
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(I64);
|
||||||
|
|
||||||
|
smallvec![
|
||||||
|
Operator::block(self.block_params(), then),
|
||||||
|
Operator::block(self.block_params(), else_),
|
||||||
|
Operator::end(end_params, end),
|
||||||
|
Operator::Pick(0),
|
||||||
|
Operator::Const((-1i64).into()),
|
||||||
|
Operator::Ne(I64),
|
||||||
|
Operator::BrIf {
|
||||||
|
then: BrTarget::Label(then).into(),
|
||||||
|
else_: BrTarget::Label(else_).into()
|
||||||
|
},
|
||||||
|
Operator::Label(then),
|
||||||
|
Operator::Rem(sint::I64),
|
||||||
|
Operator::Br {
|
||||||
|
target: BrTarget::Label(end).into()
|
||||||
|
},
|
||||||
|
Operator::Label(else_),
|
||||||
|
Operator::Drop(0..=1),
|
||||||
|
Operator::Const(0i64.into()),
|
||||||
|
Operator::Br {
|
||||||
|
target: BrTarget::Label(end).into()
|
||||||
|
},
|
||||||
|
Operator::Label(end),
|
||||||
|
]
|
||||||
|
}
|
||||||
WasmOperator::I64RemU => smallvec![Operator::Rem(sint::U64)],
|
WasmOperator::I64RemU => smallvec![Operator::Rem(sint::U64)],
|
||||||
WasmOperator::I64And => smallvec![Operator::And(Size::_64)],
|
WasmOperator::I64And => smallvec![Operator::And(Size::_64)],
|
||||||
WasmOperator::I64Or => smallvec![Operator::Or(Size::_64)],
|
WasmOperator::I64Or => smallvec![Operator::Or(Size::_64)],
|
||||||
@@ -1944,15 +1984,39 @@ where
|
|||||||
WasmOperator::I64TruncUF32 => unimplemented!("{:?}", op),
|
WasmOperator::I64TruncUF32 => unimplemented!("{:?}", op),
|
||||||
WasmOperator::I64TruncSF64 => unimplemented!("{:?}", op),
|
WasmOperator::I64TruncSF64 => unimplemented!("{:?}", op),
|
||||||
WasmOperator::I64TruncUF64 => unimplemented!("{:?}", op),
|
WasmOperator::I64TruncUF64 => unimplemented!("{:?}", op),
|
||||||
WasmOperator::F32ConvertSI32 => unimplemented!("{:?}", op),
|
WasmOperator::F32ConvertSI32 => smallvec![Operator::FConvertFromI {
|
||||||
WasmOperator::F32ConvertUI32 => unimplemented!("{:?}", op),
|
input_ty: sint::I32,
|
||||||
WasmOperator::F32ConvertSI64 => unimplemented!("{:?}", op),
|
output_ty: Size::_32
|
||||||
WasmOperator::F32ConvertUI64 => unimplemented!("{:?}", op),
|
}],
|
||||||
|
WasmOperator::F32ConvertUI32 => smallvec![Operator::FConvertFromI {
|
||||||
|
input_ty: sint::U32,
|
||||||
|
output_ty: Size::_32
|
||||||
|
}],
|
||||||
|
WasmOperator::F32ConvertSI64 => smallvec![Operator::FConvertFromI {
|
||||||
|
input_ty: sint::I64,
|
||||||
|
output_ty: Size::_32
|
||||||
|
}],
|
||||||
|
WasmOperator::F32ConvertUI64 => smallvec![Operator::FConvertFromI {
|
||||||
|
input_ty: sint::U64,
|
||||||
|
output_ty: Size::_32
|
||||||
|
}],
|
||||||
|
WasmOperator::F64ConvertSI32 => smallvec![Operator::FConvertFromI {
|
||||||
|
input_ty: sint::I32,
|
||||||
|
output_ty: Size::_64
|
||||||
|
}],
|
||||||
|
WasmOperator::F64ConvertUI32 => smallvec![Operator::FConvertFromI {
|
||||||
|
input_ty: sint::U32,
|
||||||
|
output_ty: Size::_64
|
||||||
|
}],
|
||||||
|
WasmOperator::F64ConvertSI64 => smallvec![Operator::FConvertFromI {
|
||||||
|
input_ty: sint::I64,
|
||||||
|
output_ty: Size::_64
|
||||||
|
}],
|
||||||
|
WasmOperator::F64ConvertUI64 => smallvec![Operator::FConvertFromI {
|
||||||
|
input_ty: sint::U64,
|
||||||
|
output_ty: Size::_64
|
||||||
|
}],
|
||||||
WasmOperator::F32DemoteF64 => 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::F64PromoteF32 => unimplemented!("{:?}", op),
|
||||||
WasmOperator::I32ReinterpretF32 => smallvec![Operator::I32ReinterpretFromF32],
|
WasmOperator::I32ReinterpretF32 => smallvec![Operator::I32ReinterpretFromF32],
|
||||||
WasmOperator::I64ReinterpretF64 => smallvec![Operator::I64ReinterpretFromF64],
|
WasmOperator::I64ReinterpretF64 => smallvec![Operator::I64ReinterpretFromF64],
|
||||||
|
|||||||
30
src/tests.rs
30
src/tests.rs
@@ -1211,6 +1211,36 @@ fn i32_rem() {
|
|||||||
assert_eq!(translated.execute_func::<_, u32>(0, (123121, -1)), Ok(0));
|
assert_eq!(translated.execute_func::<_, u32>(0, (123121, -1)), Ok(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn i64_div() {
|
||||||
|
const CODE: &str = r"
|
||||||
|
(module
|
||||||
|
(func (param i64) (param i64) (result i64)
|
||||||
|
(i64.div_s (get_local 0) (get_local 1))
|
||||||
|
)
|
||||||
|
)";
|
||||||
|
|
||||||
|
let translated = translate_wat(CODE);
|
||||||
|
translated.disassemble();
|
||||||
|
|
||||||
|
assert_eq!(translated.execute_func::<_, u64>(0, (-1i64, -1i64)), Ok(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn i64_rem() {
|
||||||
|
const CODE: &str = r"
|
||||||
|
(module
|
||||||
|
(func (param i64) (param i64) (result i64)
|
||||||
|
(i64.rem_s (get_local 0) (get_local 1))
|
||||||
|
)
|
||||||
|
)";
|
||||||
|
|
||||||
|
let translated = translate_wat(CODE);
|
||||||
|
translated.disassemble();
|
||||||
|
|
||||||
|
assert_eq!(translated.execute_func::<_, u64>(0, (123121i64, -1i64)), Ok(0));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn br_table() {
|
fn br_table() {
|
||||||
const CODE: &str = r"
|
const CODE: &str = r"
|
||||||
|
|||||||
Reference in New Issue
Block a user