diff --git a/README.md b/README.md index 0f5de722e6..611d9986bc 100644 --- a/README.md +++ b/README.md @@ -172,14 +172,14 @@ running 76 tests test misc_testsuite::stack_overflow ... ok test spec_testsuite::binary ... ok test misc_testsuite::misc_traps ... ok -test spec_testsuite::block ... FAILED test spec_testsuite::align ... FAILED +test spec_testsuite::block ... FAILED test spec_testsuite::br_if ... FAILED test spec_testsuite::break_drop ... ok test spec_testsuite::call ... FAILED test spec_testsuite::call_indirect ... FAILED -test spec_testsuite::comments ... ok test spec_testsuite::address ... FAILED +test spec_testsuite::comments ... ok test spec_testsuite::const_ ... ok test spec_testsuite::custom ... 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::f64_bitwise ... FAILED test spec_testsuite::f32 ... FAILED -test spec_testsuite::fac ... ok test spec_testsuite::f32_cmp ... FAILED +test spec_testsuite::fac ... ok test spec_testsuite::f64 ... FAILED -test spec_testsuite::f64_cmp ... FAILED test spec_testsuite::float_memory ... ok +test spec_testsuite::f64_cmp ... FAILED 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::globals ... 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::imports ... FAILED test spec_testsuite::inline_module ... ok -test spec_testsuite::float_literals ... ok test spec_testsuite::func ... FAILED -test spec_testsuite::int_exprs ... FAILED 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::linking ... FAILED test spec_testsuite::loop_ ... FAILED +test spec_testsuite::labels ... ok 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::return_minimal ... ok 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::stack ... FAILED -test spec_testsuite::start ... FAILED -test spec_testsuite::store_retval ... ok +test spec_testsuite::set_local ... 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::token ... ok test spec_testsuite::tee_local ... FAILED test spec_testsuite::type_ ... ok test spec_testsuite::traps ... FAILED test spec_testsuite::typecheck ... ok +test spec_testsuite::return_ ... ok 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::utf8_import_module ... ok test spec_testsuite::unreachable ... ok 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. diff --git a/src/backend.rs b/src/backend.rs index 3481a7d780..f47ea3169a 100644 --- a/src/backend.rs +++ b/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 { ($name:ident, $instr:ident, $reg_ty:ident, $typ:ty, $const_fallback:expr) => { pub fn $name(&mut self) { @@ -715,13 +871,13 @@ macro_rules! conversion { let out_val = match val { ValueLocation::Immediate(imm) => ValueLocation::Immediate( - ($const_fallback(imm.$const_ty_fn().unwrap()) as $out_typ).into() + $const_fallback(imm.$const_ty_fn().unwrap()).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] + ; $instr $out_reg_ty(temp.$out_reg_fn().unwrap()), DWORD [rsp + offset] ); ValueLocation::Reg(temp) } @@ -2334,6 +2490,47 @@ impl<'module, M: ModuleContext> Context<'module, M> { as_f32, |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); // 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 ); + fn cleanup_gprs(&mut self, gprs: impl Iterator) { + 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` // TODO: With a proper SSE-like "Value" system we could do this way better (we wouldn't have // to move `RAX` back afterwards). - fn i32_full_div( + fn full_div( &mut self, divisor: ValueLocation, quotient: ValueLocation, @@ -2517,12 +2745,12 @@ impl<'module, M: ModuleContext> Context<'module, M> { ValueLocation, impl Iterator + Clone + 'module, ) { - self.i32_full_div(divisor, quotient, |this, divisor| match divisor { + self.full_div(divisor, quotient, |this, divisor| match divisor { ValueLocation::Stack(offset) => { let offset = this.adjusted_offset(offset); dynasm!(this.asm ; xor edx, edx - ; div [rsp + offset] + ; div DWORD [rsp + offset] ); } ValueLocation::Reg(r) => { @@ -2544,12 +2772,12 @@ impl<'module, M: ModuleContext> Context<'module, M> { ValueLocation, impl Iterator + Clone + 'module, ) { - self.i32_full_div(divisor, quotient, |this, divisor| match divisor { + self.full_div(divisor, quotient, |this, divisor| match divisor { ValueLocation::Stack(offset) => { let offset = this.adjusted_offset(offset); dynasm!(this.asm ; cdq - ; idiv [rsp + offset] + ; idiv DWORD [rsp + offset] ); } ValueLocation::Reg(r) => { @@ -2562,164 +2790,58 @@ impl<'module, M: ModuleContext> Context<'module, M> { }) } - fn cleanup_gprs(&mut self, gprs: impl Iterator) { - 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); - } + fn i64_full_div_u( + &mut self, + divisor: ValueLocation, + quotient: ValueLocation, + ) -> ( + ValueLocation, + ValueLocation, + impl Iterator + Clone + 'module, + ) { + self.full_div(divisor, quotient, |this, divisor| match divisor { + ValueLocation::Stack(offset) => { + let offset = this.adjusted_offset(offset); + dynasm!(this.asm + ; xor rdx, rdx + ; div QWORD [rsp + offset] + ); + } + ValueLocation::Reg(r) => { + dynasm!(this.asm + ; xor rdx, rdx + ; div Rq(r.rq().unwrap()) + ); + } + ValueLocation::Immediate(_) => unreachable!(), + }) } - // 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_u(&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( - 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()) + fn i64_full_div_s( + &mut self, + divisor: ValueLocation, + quotient: ValueLocation, + ) -> ( + ValueLocation, + ValueLocation, + impl Iterator + Clone + 'module, + ) { + self.full_div(divisor, quotient, |this, divisor| match divisor { + ValueLocation::Stack(offset) => { + let offset = this.adjusted_offset(offset); + dynasm!(this.asm + ; cqo + ; idiv QWORD [rsp + offset] ); - 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()) + ValueLocation::Reg(r) => { + dynasm!(this.asm + ; cqo + ; idiv Rq(r.rq().unwrap()) ); - self.block_state.regs.release(div); - ValueLocation::Reg(new) } - _ => div, - }; - - self.cleanup_gprs(saved); - - self.push(div); - } - - pub fn i32_rem_u(&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 as u32 % divisor as u32).into(), - )); - } - return; - } - - 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, - }; - - self.cleanup_gprs(saved); - - 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, - }; - - self.cleanup_gprs(saved); - - self.push(rem); + ValueLocation::Immediate(_) => unreachable!(), + }) } // `i32_mul` needs to be separate because the immediate form of the instruction diff --git a/src/function_body.rs b/src/function_body.rs index da079aac69..2e11e715cd 100644 --- a/src/function_body.rs +++ b/src/function_body.rs @@ -435,6 +435,10 @@ where Operator::Or(Size::_64) => ctx.i64_or(), Operator::Xor(Size::_64) => ctx.i64_xor(), 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::Shr(sint::I64) => ctx.i64_shr_s(), Operator::Shr(sint::U64) => ctx.i64_shr_u(), @@ -481,6 +485,18 @@ where Operator::Extend { sign: Signedness::Signed, } => 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 { ty: sint::U32, memarg, diff --git a/src/microwasm.rs b/src/microwasm.rs index 24e198af49..1c171eac7b 100644 --- a/src/microwasm.rs +++ b/src/microwasm.rs @@ -786,16 +786,20 @@ where Operator::Min(ty) => write!(f, "{}.min", Type::::Float(*ty)), Operator::Max(ty) => write!(f, "{}.max", Type::::Float(*ty)), Operator::Copysign(ty) => write!(f, "{}.copysign", Type::::Float(*ty)), - Operator::I32WrapFromI64 => write!(f, "i32.wrapfromi64"), - Operator::F32DemoteFromF64 => write!(f, "f32.demotefromf64"), - Operator::F64PromoteFromF32 => write!(f, "f64.promotefromf32"), - Operator::I32ReinterpretFromF32 => write!(f, "i32.reinterpretfromf32"), - Operator::I64ReinterpretFromF64 => write!(f, "i64.reinterpretfromf64"), - Operator::F32ReinterpretFromI32 => write!(f, "f32.reinterpretfromi32"), - Operator::F64ReinterpretFromI64 => write!(f, "f64.reinterpretfromi64"), + Operator::I32WrapFromI64 => write!(f, "i32.wrap_from.i64"), + Operator::F32DemoteFromF64 => write!(f, "f32.demote_from.f64"), + Operator::F64PromoteFromF32 => write!(f, "f64.promote_from.f32"), + Operator::I32ReinterpretFromF32 => write!(f, "i32.reinterpret_from.f32"), + Operator::I64ReinterpretFromF64 => write!(f, "i64.reinterpret_from.f64"), + Operator::F32ReinterpretFromI32 => write!(f, "f32.reinterpret_from.i32"), + Operator::F64ReinterpretFromI64 => write!(f, "f64.reinterpret_from.i64"), Operator::MemoryCopy => write!(f, "memory.copy"), Operator::MemoryFill => write!(f, "memory.fill"), Operator::TableCopy => write!(f, "table.copy"), + Operator::FConvertFromI { + input_ty, + output_ty, + } => write!(f, "{}.convert_from.{}", input_ty, Type::Float::(*output_ty)), _ => unimplemented!(), } } @@ -1831,7 +1835,6 @@ where // (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); @@ -1885,7 +1888,44 @@ where WasmOperator::I64Mul => smallvec![Operator::Mul(I64)], WasmOperator::I64DivS => smallvec![Operator::Div(SI64)], WasmOperator::I64DivU => smallvec![Operator::Div(SU64)], - WasmOperator::I64RemS => smallvec![Operator::Rem(sint::I64)], + WasmOperator::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::I64And => smallvec![Operator::And(Size::_64)], WasmOperator::I64Or => smallvec![Operator::Or(Size::_64)], @@ -1944,15 +1984,39 @@ where 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::F32ConvertSI32 => smallvec![Operator::FConvertFromI { + input_ty: sint::I32, + output_ty: Size::_32 + }], + 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::F64ConvertSI32 => unimplemented!("{:?}", op), - WasmOperator::F64ConvertUI32 => unimplemented!("{:?}", op), - WasmOperator::F64ConvertSI64 => unimplemented!("{:?}", op), - WasmOperator::F64ConvertUI64 => unimplemented!("{:?}", op), WasmOperator::F64PromoteF32 => unimplemented!("{:?}", op), WasmOperator::I32ReinterpretF32 => smallvec![Operator::I32ReinterpretFromF32], WasmOperator::I64ReinterpretF64 => smallvec![Operator::I64ReinterpretFromF64], diff --git a/src/tests.rs b/src/tests.rs index b1d313b4a4..0a9bd60086 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -1211,6 +1211,36 @@ fn i32_rem() { 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] fn br_table() { const CODE: &str = r"