Fix comparisons, add more tests for binops
This commit is contained in:
@@ -749,7 +749,14 @@ macro_rules! commutative_binop_i64 {
|
|||||||
; $instr Rq(op1), i as i32
|
; $instr Rq(op1), i as i32
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
unimplemented!(concat!("Unsupported `", stringify!($instr), "` with large 64-bit immediate operand"));
|
let scratch = ctx.block_state.regs.take_scratch_gpr();
|
||||||
|
|
||||||
|
dynasm!(ctx.asm
|
||||||
|
; mov Rq(scratch), QWORD i
|
||||||
|
; $instr Rq(op1), Rq(scratch)
|
||||||
|
);
|
||||||
|
|
||||||
|
ctx.block_state.regs.release_scratch_gpr(scratch);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1028,7 +1035,7 @@ pub fn literal_i64(ctx: &mut Context, imm: i64) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! cmp_i32 {
|
macro_rules! cmp_i32 {
|
||||||
($name:ident, $instr:ident, $const_fallback:expr) => {
|
($name:ident, $instr:ident, $reverse_instr:ident, $const_fallback:expr) => {
|
||||||
pub fn $name(ctx: &mut Context) {
|
pub fn $name(ctx: &mut Context) {
|
||||||
let right = pop(ctx);
|
let right = pop(ctx);
|
||||||
let left = pop(ctx);
|
let left = pop(ctx);
|
||||||
@@ -1050,7 +1057,7 @@ macro_rules! cmp_i32 {
|
|||||||
dynasm!(ctx.asm
|
dynasm!(ctx.asm
|
||||||
; xor Rd(result), Rd(result)
|
; xor Rd(result), Rd(result)
|
||||||
; cmp Rd(rreg), i as i32
|
; cmp Rd(rreg), i as i32
|
||||||
; $instr Rb(result)
|
; $reverse_instr Rb(result)
|
||||||
);
|
);
|
||||||
Value::Temp(result)
|
Value::Temp(result)
|
||||||
}
|
}
|
||||||
@@ -1098,7 +1105,7 @@ macro_rules! cmp_i32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! cmp_i64 {
|
macro_rules! cmp_i64 {
|
||||||
($name:ident, $instr:ident, $const_fallback:expr) => {
|
($name:ident, $instr:ident, $reverse_instr:ident, $const_fallback:expr) => {
|
||||||
pub fn $name(ctx: &mut Context) {
|
pub fn $name(ctx: &mut Context) {
|
||||||
let right = pop(ctx);
|
let right = pop(ctx);
|
||||||
let left = pop(ctx);
|
let left = pop(ctx);
|
||||||
@@ -1108,20 +1115,28 @@ macro_rules! cmp_i64 {
|
|||||||
ValueLocation::Stack(offset) => {
|
ValueLocation::Stack(offset) => {
|
||||||
let result = ctx.block_state.regs.take_scratch_gpr();
|
let result = ctx.block_state.regs.take_scratch_gpr();
|
||||||
let offset = adjusted_offset(ctx, offset);
|
let offset = adjusted_offset(ctx, offset);
|
||||||
|
if (i as u64) <= u32::max_value() as u64 {
|
||||||
dynasm!(ctx.asm
|
dynasm!(ctx.asm
|
||||||
; xor Rd(result), Rd(result)
|
; xor Rd(result), Rd(result)
|
||||||
; cmp QWORD [rsp + offset], i as i32
|
; cmp QWORD [rsp + offset], i as i32
|
||||||
; $instr Rb(result)
|
; $instr Rb(result)
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
unimplemented!("Unsupported `cmp` with large 64-bit immediate operand");
|
||||||
|
}
|
||||||
Value::Temp(result)
|
Value::Temp(result)
|
||||||
}
|
}
|
||||||
ValueLocation::Reg(rreg) => {
|
ValueLocation::Reg(rreg) => {
|
||||||
let result = ctx.block_state.regs.take_scratch_gpr();
|
let result = ctx.block_state.regs.take_scratch_gpr();
|
||||||
|
if (i as u64) <= u32::max_value() as u64 {
|
||||||
dynasm!(ctx.asm
|
dynasm!(ctx.asm
|
||||||
; xor Rd(result), Rd(result)
|
; xor Rd(result), Rd(result)
|
||||||
; cmp Rq(rreg), i as i32
|
; cmp Rq(rreg), i as i32
|
||||||
; $instr Rb(result)
|
; $reverse_instr Rb(result)
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
unimplemented!("Unsupported `cmp` with large 64-bit immediate operand");
|
||||||
|
}
|
||||||
Value::Temp(result)
|
Value::Temp(result)
|
||||||
}
|
}
|
||||||
ValueLocation::Immediate(right) => {
|
ValueLocation::Immediate(right) => {
|
||||||
@@ -1156,7 +1171,7 @@ macro_rules! cmp_i64 {
|
|||||||
; $instr Rb(result)
|
; $instr Rb(result)
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
unimplemented!("Have yet to implement `cmp` with imm64 operand");
|
unimplemented!("Unsupported `cmp` with large 64-bit immediate operand");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1171,29 +1186,29 @@ macro_rules! cmp_i64 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cmp_i32!(i32_eq, sete, |a, b| a == b);
|
cmp_i32!(i32_eq, sete, sete, |a, b| a == b);
|
||||||
cmp_i32!(i32_neq, setne, |a, b| a != b);
|
cmp_i32!(i32_neq, setne, setne, |a, b| a != b);
|
||||||
// `dynasm-rs` inexplicably doesn't support setb but `setnae` (and `setc`) are synonymous
|
// `dynasm-rs` inexplicably doesn't support setb but `setnae` (and `setc`) are synonymous
|
||||||
cmp_i32!(i32_lt_u, setnae, |a, b| (a as u32) < (b as u32));
|
cmp_i32!(i32_lt_u, setnae, seta, |a, b| (a as u32) < (b as u32));
|
||||||
cmp_i32!(i32_le_u, setbe, |a, b| (a as u32) <= (b as u32));
|
cmp_i32!(i32_le_u, setbe, setae, |a, b| (a as u32) <= (b as u32));
|
||||||
cmp_i32!(i32_gt_u, seta, |a, b| (a as u32) > (b as u32));
|
cmp_i32!(i32_gt_u, seta, setnae, |a, b| (a as u32) > (b as u32));
|
||||||
cmp_i32!(i32_ge_u, setae, |a, b| (a as u32) >= (b as u32));
|
cmp_i32!(i32_ge_u, setae, setna, |a, b| (a as u32) >= (b as u32));
|
||||||
cmp_i32!(i32_lt_s, setl, |a, b| a < b);
|
cmp_i32!(i32_lt_s, setl, setnle, |a, b| a < b);
|
||||||
cmp_i32!(i32_le_s, setle, |a, b| a <= b);
|
cmp_i32!(i32_le_s, setle, setnl, |a, b| a <= b);
|
||||||
cmp_i32!(i32_gt_s, setg, |a, b| a == b);
|
cmp_i32!(i32_gt_s, setg, setnge, |a, b| a > b);
|
||||||
cmp_i32!(i32_ge_s, setge, |a, b| a == b);
|
cmp_i32!(i32_ge_s, setge, setng, |a, b| a >= b);
|
||||||
|
|
||||||
cmp_i64!(i64_eq, sete, |a, b| a == b);
|
cmp_i64!(i64_eq, sete, sete, |a, b| a == b);
|
||||||
cmp_i64!(i64_neq, setne, |a, b| a != b);
|
cmp_i64!(i64_neq, setne, setne, |a, b| a != b);
|
||||||
// `dynasm-rs` inexplicably doesn't support setb but `setnae` (and `setc`) are synonymous
|
// `dynasm-rs` inexplicably doesn't support setb but `setnae` (and `setc`) are synonymous
|
||||||
cmp_i64!(i64_lt_u, setnae, |a, b| (a as u64) < (b as u64));
|
cmp_i64!(i64_lt_u, setnae, seta, |a, b| (a as u64) < (b as u64));
|
||||||
cmp_i64!(i64_le_u, setbe, |a, b| (a as u64) <= (b as u64));
|
cmp_i64!(i64_le_u, setbe, setae, |a, b| (a as u64) <= (b as u64));
|
||||||
cmp_i64!(i64_gt_u, seta, |a, b| (a as u64) > (b as u64));
|
cmp_i64!(i64_gt_u, seta, setnae, |a, b| (a as u64) > (b as u64));
|
||||||
cmp_i64!(i64_ge_u, setae, |a, b| (a as u64) >= (b as u64));
|
cmp_i64!(i64_ge_u, setae, setna, |a, b| (a as u64) >= (b as u64));
|
||||||
cmp_i64!(i64_lt_s, setl, |a, b| a < b);
|
cmp_i64!(i64_lt_s, setl, setnle, |a, b| a < b);
|
||||||
cmp_i64!(i64_le_s, setle, |a, b| a <= b);
|
cmp_i64!(i64_le_s, setle, setnl, |a, b| a <= b);
|
||||||
cmp_i64!(i64_gt_s, setg, |a, b| a == b);
|
cmp_i64!(i64_gt_s, setg, setnge, |a, b| a > b);
|
||||||
cmp_i64!(i64_ge_s, setge, |a, b| a == b);
|
cmp_i64!(i64_ge_s, setge, setng, |a, b| a >= b);
|
||||||
|
|
||||||
/// Pops i32 predicate and branches to the specified label
|
/// Pops i32 predicate and branches to the specified label
|
||||||
/// if the predicate is equal to zero.
|
/// if the predicate is equal to zero.
|
||||||
@@ -1219,12 +1234,10 @@ fn immediate_to_reg(ctx: &mut Context, reg: GPR, val: i64) {
|
|||||||
dynasm!(ctx.asm
|
dynasm!(ctx.asm
|
||||||
; mov Rd(reg), val as i32
|
; mov Rd(reg), val as i32
|
||||||
);
|
);
|
||||||
} else if reg == RAX {
|
|
||||||
dynasm!(ctx.asm
|
|
||||||
; movabs rax, val
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
unimplemented!("dynasm doesn't yet support mov r64, imm64");
|
dynasm!(ctx.asm
|
||||||
|
; mov Rq(reg), QWORD val
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1250,14 +1263,19 @@ fn copy_value(ctx: &mut Context, src: ValueLocation, dst: ValueLocation) {
|
|||||||
}
|
}
|
||||||
(ValueLocation::Immediate(i), ValueLocation::Stack(out_offset)) => {
|
(ValueLocation::Immediate(i), ValueLocation::Stack(out_offset)) => {
|
||||||
let out_offset = adjusted_offset(ctx, out_offset);
|
let out_offset = adjusted_offset(ctx, out_offset);
|
||||||
|
if (i as u64) <= u32::max_value() as u64 {
|
||||||
dynasm!(ctx.asm
|
dynasm!(ctx.asm
|
||||||
; mov DWORD [rsp + out_offset], i as i32
|
; mov DWORD [rsp + out_offset], i as i32
|
||||||
);
|
);
|
||||||
if (i as u64) > u32::max_value() as u64 {
|
} else {
|
||||||
let i = (i >> 4) as i32;
|
let scratch = ctx.block_state.regs.take_scratch_gpr();
|
||||||
|
|
||||||
dynasm!(ctx.asm
|
dynasm!(ctx.asm
|
||||||
; mov DWORD [rsp + out_offset + 4], i
|
; mov Rq(scratch), QWORD i
|
||||||
|
; mov [rsp + out_offset], Rq(scratch)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
ctx.block_state.regs.release_scratch_gpr(scratch);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(ValueLocation::Stack(in_offset), ValueLocation::Reg(out_reg)) => {
|
(ValueLocation::Stack(in_offset), ValueLocation::Reg(out_reg)) => {
|
||||||
|
|||||||
@@ -330,6 +330,7 @@ pub fn translate(
|
|||||||
Operator::SetLocal { local_index } => set_local_i32(ctx, local_index),
|
Operator::SetLocal { local_index } => set_local_i32(ctx, local_index),
|
||||||
Operator::GetLocal { local_index } => get_local_i32(ctx, local_index),
|
Operator::GetLocal { local_index } => get_local_i32(ctx, local_index),
|
||||||
Operator::I32Const { value } => literal_i32(ctx, value),
|
Operator::I32Const { value } => literal_i32(ctx, value),
|
||||||
|
Operator::I64Const { value } => literal_i64(ctx, value),
|
||||||
Operator::Call { function_index } => {
|
Operator::Call { function_index } => {
|
||||||
let callee_ty = translation_ctx.func_type(function_index);
|
let callee_ty = translation_ctx.func_type(function_index);
|
||||||
|
|
||||||
|
|||||||
140
src/tests.rs
140
src/tests.rs
@@ -23,19 +23,54 @@ mod op32 {
|
|||||||
|
|
||||||
macro_rules! binop_test {
|
macro_rules! binop_test {
|
||||||
($op:ident, $func:expr) => {
|
($op:ident, $func:expr) => {
|
||||||
quickcheck! {
|
mod $op {
|
||||||
fn $op(a: u32, b: u32) -> bool {
|
use super::{translate_wat, TranslatedModule};
|
||||||
static CODE: &str = concat!(
|
|
||||||
"(module (func (param i32) (param i32) (result i32) (i32.",
|
const OP: &str = stringify!($op);
|
||||||
stringify!($op),
|
|
||||||
" (get_local 0) (get_local 1))))"
|
|
||||||
);
|
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref TRANSLATED: TranslatedModule = translate_wat(CODE);
|
static ref AS_PARAMS: TranslatedModule = translate_wat(&format!("
|
||||||
|
(module (func (param i32) (param i32) (result i32)
|
||||||
|
(i32.{op} (get_local 0) (get_local 1))))
|
||||||
|
", op = OP));
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe { TRANSLATED.execute_func::<(u32, u32), u32>(0, (a, b)) == $func(a, b) }
|
quickcheck! {
|
||||||
|
fn as_params(a: u32, b: u32) -> bool {
|
||||||
|
unsafe { AS_PARAMS.execute_func::<(u32, u32), u32>(0, (a, b)) == $func(a, b) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lit_lit(a: u32, b: u32) -> bool {
|
||||||
|
unsafe {
|
||||||
|
translate_wat(&format!("
|
||||||
|
(module (func (result i32)
|
||||||
|
(i32.{op} (i32.const {left}) (i32.const {right}))))
|
||||||
|
", op = OP, left = a, right = b)).execute_func::<(), u32>(0, ()) == $func(a, b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lit_reg(a: u32, b: u32) -> bool {
|
||||||
|
use std::sync::Once;
|
||||||
|
|
||||||
|
let translated = translate_wat(&format!("
|
||||||
|
(module (func (param i32) (result i32)
|
||||||
|
(i32.{op} (i32.const {left}) (get_local 0))))
|
||||||
|
", op = OP, left = a));
|
||||||
|
static ONCE: Once = Once::new();
|
||||||
|
ONCE.call_once(|| translated.disassemble());
|
||||||
|
unsafe {
|
||||||
|
translated.execute_func::<(u32,), u32>(0, (b,)) == $func(a, b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reg_lit(a: u32, b: u32) -> bool {
|
||||||
|
unsafe {
|
||||||
|
translate_wat(&format!("
|
||||||
|
(module (func (param i32) (result i32)
|
||||||
|
(i32.{op} (get_local 0) (i32.const {right}))))
|
||||||
|
", op = OP, right = b)).execute_func::<(u32,), u32>(0, (a,)) == $func(a, b)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -47,6 +82,8 @@ mod op32 {
|
|||||||
binop_test!(or, std::ops::BitOr::bitor);
|
binop_test!(or, std::ops::BitOr::bitor);
|
||||||
binop_test!(xor, std::ops::BitXor::bitxor);
|
binop_test!(xor, std::ops::BitXor::bitxor);
|
||||||
binop_test!(mul, u32::wrapping_mul);
|
binop_test!(mul, u32::wrapping_mul);
|
||||||
|
binop_test!(eq, |a, b| if a == b { 1 } else { 0 });
|
||||||
|
binop_test!(ne, |a, b| if a != b { 1 } else { 0 });
|
||||||
binop_test!(lt_u, |a, b| if a < b { 1 } else { 0 });
|
binop_test!(lt_u, |a, b| if a < b { 1 } else { 0 });
|
||||||
binop_test!(le_u, |a, b| if a <= b { 1 } else { 0 });
|
binop_test!(le_u, |a, b| if a <= b { 1 } else { 0 });
|
||||||
binop_test!(gt_u, |a, b| if a > b { 1 } else { 0 });
|
binop_test!(gt_u, |a, b| if a > b { 1 } else { 0 });
|
||||||
@@ -65,21 +102,55 @@ mod op64 {
|
|||||||
binop_test!($op, $func, i64);
|
binop_test!($op, $func, i64);
|
||||||
};
|
};
|
||||||
($op:ident, $func:expr, $retty:ident) => {
|
($op:ident, $func:expr, $retty:ident) => {
|
||||||
quickcheck! {
|
mod $op {
|
||||||
fn $op(a: u64, b: u64) -> bool {
|
use super::{translate_wat, TranslatedModule};
|
||||||
static CODE: &str = concat!(
|
|
||||||
"(module (func (param i64) (param i64) (result ",
|
const RETTY: &str = stringify!($retty);
|
||||||
stringify!($retty),
|
const OP: &str = stringify!($op);
|
||||||
") (i64.",
|
|
||||||
stringify!($op),
|
|
||||||
" (get_local 0) (get_local 1))))"
|
|
||||||
);
|
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref TRANSLATED: TranslatedModule = translate_wat(CODE);
|
static ref AS_PARAMS: TranslatedModule = translate_wat(&format!("
|
||||||
|
(module (func (param i64) (param i64) (result {retty})
|
||||||
|
(i64.{op} (get_local 0) (get_local 1))))
|
||||||
|
", retty = RETTY, op = OP));
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe { TRANSLATED.execute_func::<(u64, u64), u64>(0, (a, b)) == $func(a, b) }
|
quickcheck! {
|
||||||
|
fn as_params(a: u64, b: u64) -> bool {
|
||||||
|
unsafe { AS_PARAMS.execute_func::<(u64, u64), $retty>(0, (a, b)) == ($func(a, b) as $retty) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lit_lit(a: u64, b: u64) -> bool {
|
||||||
|
unsafe {
|
||||||
|
translate_wat(&format!("
|
||||||
|
(module (func (result {retty})
|
||||||
|
(i64.{op} (i64.const {left}) (i64.const {right}))))
|
||||||
|
", retty = RETTY, op = OP, left = a, right = b)).execute_func::<(), $retty>(0, ()) == ($func(a, b) as $retty)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lit_reg(a: u64, b: u64) -> bool {
|
||||||
|
use std::sync::Once;
|
||||||
|
|
||||||
|
let translated = translate_wat(&format!("
|
||||||
|
(module (func (param i64) (result {retty})
|
||||||
|
(i64.{op} (i64.const {left}) (get_local 0))))
|
||||||
|
", retty = RETTY, op = OP, left = a));
|
||||||
|
static ONCE: Once = Once::new();
|
||||||
|
ONCE.call_once(|| translated.disassemble());
|
||||||
|
unsafe {
|
||||||
|
translated.execute_func::<(u64,), $retty>(0, (b,)) == ($func(a, b) as $retty)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reg_lit(a: u64, b: u64) -> bool {
|
||||||
|
unsafe {
|
||||||
|
translate_wat(&format!("
|
||||||
|
(module (func (param i64) (result {retty})
|
||||||
|
(i64.{op} (get_local 0) (i64.const {right}))))
|
||||||
|
", retty = RETTY, op = OP, right = b)).execute_func::<(u64,), $retty>(0, (a,)) == ($func(a, b) as $retty)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -91,14 +162,32 @@ mod op64 {
|
|||||||
binop_test!(or, std::ops::BitOr::bitor);
|
binop_test!(or, std::ops::BitOr::bitor);
|
||||||
binop_test!(xor, std::ops::BitXor::bitxor);
|
binop_test!(xor, std::ops::BitXor::bitxor);
|
||||||
binop_test!(mul, u64::wrapping_mul);
|
binop_test!(mul, u64::wrapping_mul);
|
||||||
|
binop_test!(eq, |a, b| if a == b { 1 } else { 0 }, i32);
|
||||||
|
binop_test!(ne, |a, b| if a != b { 1 } else { 0 }, i32);
|
||||||
binop_test!(lt_u, |a, b| if a < b { 1 } else { 0 }, i32);
|
binop_test!(lt_u, |a, b| if a < b { 1 } else { 0 }, i32);
|
||||||
binop_test!(le_u, |a, b| if a <= b { 1 } else { 0 }, i32);
|
binop_test!(le_u, |a, b| if a <= b { 1 } else { 0 }, i32);
|
||||||
binop_test!(gt_u, |a, b| if a > b { 1 } else { 0 }, i32);
|
binop_test!(gt_u, |a, b| if a > b { 1 } else { 0 }, i32);
|
||||||
binop_test!(ge_u, |a, b| if a >= b { 1 } else { 0 }, i32);
|
binop_test!(ge_u, |a, b| if a >= b { 1 } else { 0 }, i32);
|
||||||
binop_test!(lt_s, |a, b| if (a as i64) < (b as i64) { 1 } else { 0 }, i32);
|
binop_test!(
|
||||||
binop_test!(le_s, |a, b| if (a as i64) <= (b as i64) { 1 } else { 0 }, i32);
|
lt_s,
|
||||||
binop_test!(gt_s, |a, b| if (a as i64) > (b as i64) { 1 } else { 0 }, i32);
|
|a, b| if (a as i64) < (b as i64) { 1 } else { 0 },
|
||||||
binop_test!(ge_s, |a, b| if (a as i64) >= (b as i64) { 1 } else { 0 }, i32);
|
i32
|
||||||
|
);
|
||||||
|
binop_test!(
|
||||||
|
le_s,
|
||||||
|
|a, b| if (a as i64) <= (b as i64) { 1 } else { 0 },
|
||||||
|
i32
|
||||||
|
);
|
||||||
|
binop_test!(
|
||||||
|
gt_s,
|
||||||
|
|a, b| if (a as i64) > (b as i64) { 1 } else { 0 },
|
||||||
|
i32
|
||||||
|
);
|
||||||
|
binop_test!(
|
||||||
|
ge_s,
|
||||||
|
|a, b| if (a as i64) >= (b as i64) { 1 } else { 0 },
|
||||||
|
i32
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
quickcheck! {
|
quickcheck! {
|
||||||
@@ -433,7 +522,7 @@ fn spec_loop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
quickcheck! {
|
quickcheck! {
|
||||||
fn spec_fac(n: i32) -> bool {
|
fn spec_fac(n: i8) -> bool {
|
||||||
const CODE: &str = r#"
|
const CODE: &str = r#"
|
||||||
(module
|
(module
|
||||||
(func (param i32) (result i32)
|
(func (param i32) (result i32)
|
||||||
@@ -505,6 +594,7 @@ quickcheck! {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let n = n as i32;
|
||||||
unsafe {
|
unsafe {
|
||||||
TRANSLATED.execute_func::<(i32,), i32>(0, (n,)) == fac(n)
|
TRANSLATED.execute_func::<(i32,), i32>(0, (n,)) == fac(n)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user