x64 sign-extends imm32 arguments, so don't truncate them in an unsigned way
This commit is contained in:
133
src/backend.rs
133
src/backend.rs
@@ -744,9 +744,9 @@ macro_rules! commutative_binop_i64 {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
ValueLocation::Immediate(i) => {
|
ValueLocation::Immediate(i) => {
|
||||||
if (i as u64) <= u32::max_value() as u64 {
|
if let Some(i) = i.try_into() {
|
||||||
dynasm!(ctx.asm
|
dynasm!(ctx.asm
|
||||||
; $instr Rq(op1), i as i32
|
; $instr Rq(op1), i
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
let scratch = ctx.block_state.regs.take_scratch_gpr();
|
let scratch = ctx.block_state.regs.take_scratch_gpr();
|
||||||
@@ -766,6 +766,7 @@ macro_rules! commutative_binop_i64 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Use `inc`/`dec` where possible?
|
// TODO: Use `inc`/`dec` where possible?
|
||||||
commutative_binop_i32!(i32_add, add, |a, b| (a as i32).wrapping_add(b as i32));
|
commutative_binop_i32!(i32_add, add, |a, b| (a as i32).wrapping_add(b as i32));
|
||||||
commutative_binop_i32!(i32_and, and, |a, b| a & b);
|
commutative_binop_i32!(i32_and, and, |a, b| a & b);
|
||||||
@@ -777,53 +778,21 @@ commutative_binop_i64!(i64_and, and, |a, b| a & b);
|
|||||||
commutative_binop_i64!(i64_or, or, |a, b| a | b);
|
commutative_binop_i64!(i64_or, or, |a, b| a | b);
|
||||||
commutative_binop_i64!(i64_xor, xor, |a, b| a ^ b);
|
commutative_binop_i64!(i64_xor, xor, |a, b| a ^ b);
|
||||||
|
|
||||||
// `i32_mul` needs to be seperate because the immediate form of the instruction
|
trait TryInto<O> {
|
||||||
// has a different syntax to the immediate form of the other instructions.
|
fn try_into(self) -> Option<O>;
|
||||||
pub fn i32_mul(ctx: &mut Context) {
|
|
||||||
let op0 = pop(ctx);
|
|
||||||
let op1 = pop(ctx);
|
|
||||||
|
|
||||||
if let Some(i1) = op1.immediate() {
|
|
||||||
if let Some(i0) = op0.immediate() {
|
|
||||||
ctx.block_state.stack.push(StackValue::Immediate(
|
|
||||||
i32::wrapping_mul(i1 as i32, i0 as i32) as _,
|
|
||||||
));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let (op1, op0) = match op1 {
|
impl TryInto<i32> for i64 {
|
||||||
Value::Temp(reg) => (reg, op0),
|
fn try_into(self) -> Option<i32> {
|
||||||
_ => {
|
let min = i32::min_value() as i64;
|
||||||
if op0.immediate().is_some() {
|
let max = i32::max_value() as i64;
|
||||||
(into_temp_reg(ctx, op1), op0)
|
|
||||||
|
if self > min && self < max {
|
||||||
|
Some(self as i32)
|
||||||
} else {
|
} else {
|
||||||
(into_temp_reg(ctx, op0), op1)
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
match op0.location(&ctx.block_state.locals) {
|
|
||||||
ValueLocation::Reg(reg) => {
|
|
||||||
dynasm!(ctx.asm
|
|
||||||
; imul Rd(op1), Rd(reg)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
ValueLocation::Stack(offset) => {
|
|
||||||
let offset = adjusted_offset(ctx, offset);
|
|
||||||
dynasm!(ctx.asm
|
|
||||||
; imul Rd(op1), [rsp + offset]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
ValueLocation::Immediate(i) => {
|
|
||||||
dynasm!(ctx.asm
|
|
||||||
; imul Rd(op1), Rd(op1), i as i32
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.block_state.stack.push(StackValue::Temp(op1));
|
|
||||||
free_value(ctx, op0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// `sub` is not commutative, so we have to handle it differently (we _must_ use the `op1`
|
// `sub` is not commutative, so we have to handle it differently (we _must_ use the `op1`
|
||||||
@@ -853,9 +822,9 @@ pub fn i64_sub(ctx: &mut Context) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
ValueLocation::Immediate(i) => {
|
ValueLocation::Immediate(i) => {
|
||||||
if (i as u64) <= u32::max_value() as u64 {
|
if let Some(i) = i.try_into() {
|
||||||
dynasm!(ctx.asm
|
dynasm!(ctx.asm
|
||||||
; sub Rq(op1), i as i32
|
; sub Rq(op1), i
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
unimplemented!(concat!(
|
unimplemented!(concat!(
|
||||||
@@ -908,9 +877,9 @@ pub fn i64_mul(ctx: &mut Context) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
ValueLocation::Immediate(i) => {
|
ValueLocation::Immediate(i) => {
|
||||||
if (i as u64) <= u32::max_value() as u64 {
|
if let Some(i) = i.try_into() {
|
||||||
dynasm!(ctx.asm
|
dynasm!(ctx.asm
|
||||||
; imul Rq(op1), Rq(op1), i as i32
|
; imul Rq(op1), Rq(op1), i
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
unimplemented!(concat!(
|
unimplemented!(concat!(
|
||||||
@@ -951,11 +920,66 @@ pub fn i32_sub(ctx: &mut Context) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
ValueLocation::Immediate(i) => {
|
ValueLocation::Immediate(i) => {
|
||||||
|
if i == 1 {
|
||||||
|
dynasm!(ctx.asm
|
||||||
|
; dec Rd(op1)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
dynasm!(ctx.asm
|
dynasm!(ctx.asm
|
||||||
; sub Rd(op1), i as i32
|
; sub Rd(op1), i as i32
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.block_state.stack.push(StackValue::Temp(op1));
|
||||||
|
free_value(ctx, op0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// `i32_mul` needs to be seperate because the immediate form of the instruction
|
||||||
|
// has a different syntax to the immediate form of the other instructions.
|
||||||
|
pub fn i32_mul(ctx: &mut Context) {
|
||||||
|
let op0 = pop(ctx);
|
||||||
|
let op1 = pop(ctx);
|
||||||
|
|
||||||
|
if let Some(i1) = op1.immediate() {
|
||||||
|
if let Some(i0) = op0.immediate() {
|
||||||
|
ctx.block_state.stack.push(StackValue::Immediate(
|
||||||
|
i32::wrapping_mul(i1 as i32, i0 as i32) as _,
|
||||||
|
));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let (op1, op0) = match op1 {
|
||||||
|
Value::Temp(reg) => (reg, op0),
|
||||||
|
_ => {
|
||||||
|
if op0.immediate().is_some() {
|
||||||
|
(into_temp_reg(ctx, op1), op0)
|
||||||
|
} else {
|
||||||
|
(into_temp_reg(ctx, op0), op1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match op0.location(&ctx.block_state.locals) {
|
||||||
|
ValueLocation::Reg(reg) => {
|
||||||
|
dynasm!(ctx.asm
|
||||||
|
; imul Rd(op1), Rd(reg)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
ValueLocation::Stack(offset) => {
|
||||||
|
let offset = adjusted_offset(ctx, offset);
|
||||||
|
dynasm!(ctx.asm
|
||||||
|
; imul Rd(op1), [rsp + offset]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
ValueLocation::Immediate(i) => {
|
||||||
|
dynasm!(ctx.asm
|
||||||
|
; imul Rd(op1), Rd(op1), i as i32
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ctx.block_state.stack.push(StackValue::Temp(op1));
|
ctx.block_state.stack.push(StackValue::Temp(op1));
|
||||||
free_value(ctx, op0);
|
free_value(ctx, op0);
|
||||||
@@ -1115,10 +1139,10 @@ 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 {
|
if let Some(i) = i.try_into() {
|
||||||
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
|
||||||
; $instr Rb(result)
|
; $instr Rb(result)
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@@ -1128,10 +1152,10 @@ macro_rules! cmp_i64 {
|
|||||||
}
|
}
|
||||||
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 {
|
if let Some(i) = i.try_into() {
|
||||||
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
|
||||||
; $reverse_instr Rb(result)
|
; $reverse_instr Rb(result)
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@@ -1164,10 +1188,10 @@ macro_rules! cmp_i64 {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
ValueLocation::Immediate(i) => {
|
ValueLocation::Immediate(i) => {
|
||||||
if (i as u64) <= u32::max_value() as u64 {
|
if let Some(i) = i.try_into() {
|
||||||
dynasm!(ctx.asm
|
dynasm!(ctx.asm
|
||||||
; xor Rd(result), Rd(result)
|
; xor Rd(result), Rd(result)
|
||||||
; cmp Rq(lreg), i as i32
|
; cmp Rq(lreg), i
|
||||||
; $instr Rb(result)
|
; $instr Rb(result)
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@@ -1575,3 +1599,4 @@ pub fn trap(ctx: &mut Context) {
|
|||||||
; ud2
|
; ud2
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
132
src/tests.rs
132
src/tests.rs
@@ -36,20 +36,20 @@ mod op32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
quickcheck! {
|
quickcheck! {
|
||||||
fn as_params(a: u32, b: u32) -> bool {
|
fn as_params(a: i32, b: i32) -> bool {
|
||||||
unsafe { AS_PARAMS.execute_func::<(u32, u32), u32>(0, (a, b)) == $func(a, b) }
|
unsafe { AS_PARAMS.execute_func::<(i32, i32), i32>(0, (a, b)) == $func(a, b) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lit_lit(a: u32, b: u32) -> bool {
|
fn lit_lit(a: i32, b: i32) -> bool {
|
||||||
unsafe {
|
unsafe {
|
||||||
translate_wat(&format!("
|
translate_wat(&format!("
|
||||||
(module (func (result i32)
|
(module (func (result i32)
|
||||||
(i32.{op} (i32.const {left}) (i32.const {right}))))
|
(i32.{op} (i32.const {left}) (i32.const {right}))))
|
||||||
", op = OP, left = a, right = b)).execute_func::<(), u32>(0, ()) == $func(a, b)
|
", op = OP, left = a, right = b)).execute_func::<(), i32>(0, ()) == $func(a, b)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lit_reg(a: u32, b: u32) -> bool {
|
fn lit_reg(a: i32, b: i32) -> bool {
|
||||||
use std::sync::Once;
|
use std::sync::Once;
|
||||||
|
|
||||||
let translated = translate_wat(&format!("
|
let translated = translate_wat(&format!("
|
||||||
@@ -59,16 +59,16 @@ mod op32 {
|
|||||||
static ONCE: Once = Once::new();
|
static ONCE: Once = Once::new();
|
||||||
ONCE.call_once(|| translated.disassemble());
|
ONCE.call_once(|| translated.disassemble());
|
||||||
unsafe {
|
unsafe {
|
||||||
translated.execute_func::<(u32,), u32>(0, (b,)) == $func(a, b)
|
translated.execute_func::<(i32,), i32>(0, (b,)) == $func(a, b)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reg_lit(a: u32, b: u32) -> bool {
|
fn reg_lit(a: i32, b: i32) -> bool {
|
||||||
unsafe {
|
unsafe {
|
||||||
translate_wat(&format!("
|
translate_wat(&format!("
|
||||||
(module (func (param i32) (result i32)
|
(module (func (param i32) (result i32)
|
||||||
(i32.{op} (get_local 0) (i32.const {right}))))
|
(i32.{op} (get_local 0) (i32.const {right}))))
|
||||||
", op = OP, right = b)).execute_func::<(u32,), u32>(0, (a,)) == $func(a, b)
|
", op = OP, right = b)).execute_func::<(i32,), i32>(0, (a,)) == $func(a, b)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -76,22 +76,22 @@ mod op32 {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
binop_test!(add, u32::wrapping_add);
|
binop_test!(add, i32::wrapping_add);
|
||||||
binop_test!(sub, u32::wrapping_sub);
|
binop_test!(sub, i32::wrapping_sub);
|
||||||
binop_test!(and, std::ops::BitAnd::bitand);
|
binop_test!(and, std::ops::BitAnd::bitand);
|
||||||
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, i32::wrapping_mul);
|
||||||
binop_test!(eq, |a, b| if a == b { 1 } else { 0 });
|
binop_test!(eq, |a, b| if a == b { 1 } else { 0 });
|
||||||
binop_test!(ne, |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 as u32) < (b as u32) { 1 } else { 0 });
|
||||||
binop_test!(le_u, |a, b| if a <= b { 1 } else { 0 });
|
binop_test!(le_u, |a, b| if (a as u32) <= (b as u32) { 1 } else { 0 });
|
||||||
binop_test!(gt_u, |a, b| if a > b { 1 } else { 0 });
|
binop_test!(gt_u, |a, b| if (a as u32) > (b as u32) { 1 } else { 0 });
|
||||||
binop_test!(ge_u, |a, b| if a >= b { 1 } else { 0 });
|
binop_test!(ge_u, |a, b| if (a as u32) >= (b as u32) { 1 } else { 0 });
|
||||||
binop_test!(lt_s, |a, b| if (a as i32) < (b as i32) { 1 } else { 0 });
|
binop_test!(lt_s, |a, b| if a < b { 1 } else { 0 });
|
||||||
binop_test!(le_s, |a, b| if (a as i32) <= (b as i32) { 1 } else { 0 });
|
binop_test!(le_s, |a, b| if a <= b { 1 } else { 0 });
|
||||||
binop_test!(gt_s, |a, b| if (a as i32) > (b as i32) { 1 } else { 0 });
|
binop_test!(gt_s, |a, b| if a > b { 1 } else { 0 });
|
||||||
binop_test!(ge_s, |a, b| if (a as i32) >= (b as i32) { 1 } else { 0 });
|
binop_test!(ge_s, |a, b| if a >= b { 1 } else { 0 });
|
||||||
}
|
}
|
||||||
|
|
||||||
mod op64 {
|
mod op64 {
|
||||||
@@ -116,11 +116,11 @@ mod op64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
quickcheck! {
|
quickcheck! {
|
||||||
fn as_params(a: u64, b: u64) -> bool {
|
fn as_params(a: i64, b: i64) -> bool {
|
||||||
unsafe { AS_PARAMS.execute_func::<(u64, u64), $retty>(0, (a, b)) == ($func(a, b) as $retty) }
|
unsafe { AS_PARAMS.execute_func::<(i64, i64), $retty>(0, (a, b)) == ($func(a, b) as $retty) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lit_lit(a: u64, b: u64) -> bool {
|
fn lit_lit(a: i64, b: i64) -> bool {
|
||||||
unsafe {
|
unsafe {
|
||||||
translate_wat(&format!("
|
translate_wat(&format!("
|
||||||
(module (func (result {retty})
|
(module (func (result {retty})
|
||||||
@@ -129,7 +129,7 @@ mod op64 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lit_reg(a: u64, b: u64) -> bool {
|
fn lit_reg(a: i64, b: i64) -> bool {
|
||||||
use std::sync::Once;
|
use std::sync::Once;
|
||||||
|
|
||||||
let translated = translate_wat(&format!("
|
let translated = translate_wat(&format!("
|
||||||
@@ -139,16 +139,16 @@ mod op64 {
|
|||||||
static ONCE: Once = Once::new();
|
static ONCE: Once = Once::new();
|
||||||
ONCE.call_once(|| translated.disassemble());
|
ONCE.call_once(|| translated.disassemble());
|
||||||
unsafe {
|
unsafe {
|
||||||
translated.execute_func::<(u64,), $retty>(0, (b,)) == ($func(a, b) as $retty)
|
translated.execute_func::<(i64,), $retty>(0, (b,)) == ($func(a, b) as $retty)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reg_lit(a: u64, b: u64) -> bool {
|
fn reg_lit(a: i64, b: i64) -> bool {
|
||||||
unsafe {
|
unsafe {
|
||||||
translate_wat(&format!("
|
translate_wat(&format!("
|
||||||
(module (func (param i64) (result {retty})
|
(module (func (param i64) (result {retty})
|
||||||
(i64.{op} (get_local 0) (i64.const {right}))))
|
(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)
|
", retty = RETTY, op = OP, right = b)).execute_func::<(i64,), $retty>(0, (a,)) == ($func(a, b) as $retty)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -156,38 +156,38 @@ mod op64 {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
binop_test!(add, u64::wrapping_add);
|
binop_test!(add, i64::wrapping_add);
|
||||||
binop_test!(sub, u64::wrapping_sub);
|
binop_test!(sub, i64::wrapping_sub);
|
||||||
binop_test!(and, std::ops::BitAnd::bitand);
|
binop_test!(and, std::ops::BitAnd::bitand);
|
||||||
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, i64::wrapping_mul);
|
||||||
binop_test!(eq, |a, b| if a == b { 1 } else { 0 }, i32);
|
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!(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!(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!(ge_u, |a, b| if a >= b { 1 } else { 0 }, i32);
|
|
||||||
binop_test!(
|
binop_test!(
|
||||||
lt_s,
|
lt_u,
|
||||||
|a, b| if (a as i64) < (b as i64) { 1 } else { 0 },
|
|a, b| if (a as u64) < (b as u64) { 1 } else { 0 },
|
||||||
i32
|
i32
|
||||||
);
|
);
|
||||||
binop_test!(
|
binop_test!(
|
||||||
le_s,
|
le_u,
|
||||||
|a, b| if (a as i64) <= (b as i64) { 1 } else { 0 },
|
|a, b| if (a as u64) <= (b as u64) { 1 } else { 0 },
|
||||||
i32
|
i32
|
||||||
);
|
);
|
||||||
binop_test!(
|
binop_test!(
|
||||||
gt_s,
|
gt_u,
|
||||||
|a, b| if (a as i64) > (b as i64) { 1 } else { 0 },
|
|a, b| if (a as u64) > (b as u64) { 1 } else { 0 },
|
||||||
i32
|
i32
|
||||||
);
|
);
|
||||||
binop_test!(
|
binop_test!(
|
||||||
ge_s,
|
ge_u,
|
||||||
|a, b| if (a as i64) >= (b as i64) { 1 } else { 0 },
|
|a, b| if (a as u64) >= (b as u64) { 1 } else { 0 },
|
||||||
i32
|
i32
|
||||||
);
|
);
|
||||||
|
binop_test!(lt_s, |a, b| if a < b { 1 } else { 0 }, i32);
|
||||||
|
binop_test!(le_s, |a, b| if a <= b { 1 } else { 0 }, i32);
|
||||||
|
binop_test!(gt_s, |a, b| if a > b { 1 } else { 0 }, i32);
|
||||||
|
binop_test!(ge_s, |a, b| if a >= b { 1 } else { 0 }, i32);
|
||||||
}
|
}
|
||||||
|
|
||||||
quickcheck! {
|
quickcheck! {
|
||||||
@@ -348,14 +348,17 @@ fn function_read_args_spill_to_stack() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro_rules! mk_function_write_args_spill_to_stack {
|
||||||
|
($name:ident, $typ:ty) => {
|
||||||
#[test]
|
#[test]
|
||||||
fn function_write_args_spill_to_stack() {
|
fn $name() {
|
||||||
let code = r#"
|
let code = format!(
|
||||||
|
"
|
||||||
(module
|
(module
|
||||||
(func (param i32) (param i32) (param i32) (param i32)
|
(func (param {typ}) (param {typ}) (param {typ}) (param {typ})
|
||||||
(param i32) (param i32) (param i32) (param i32)
|
(param {typ}) (param {typ}) (param {typ}) (param {typ})
|
||||||
(param i32) (param i32) (param i32) (param i32)
|
(param {typ}) (param {typ}) (param {typ}) (param {typ})
|
||||||
(result i32)
|
(result {typ})
|
||||||
|
|
||||||
(call $called
|
(call $called
|
||||||
(get_local 0)
|
(get_local 0)
|
||||||
@@ -374,10 +377,10 @@ fn function_write_args_spill_to_stack() {
|
|||||||
)
|
)
|
||||||
|
|
||||||
(func $called
|
(func $called
|
||||||
(param i32) (param i32) (param i32) (param i32)
|
(param {typ}) (param {typ}) (param {typ}) (param {typ})
|
||||||
(param i32) (param i32) (param i32) (param i32)
|
(param {typ}) (param {typ}) (param {typ}) (param {typ})
|
||||||
(param i32) (param i32) (param i32) (param i32)
|
(param {typ}) (param {typ}) (param {typ}) (param {typ})
|
||||||
(result i32)
|
(result {typ})
|
||||||
|
|
||||||
(call $assert_zero
|
(call $assert_zero
|
||||||
(get_local 11)
|
(get_local 11)
|
||||||
@@ -385,26 +388,39 @@ fn function_write_args_spill_to_stack() {
|
|||||||
(get_local 0)
|
(get_local 0)
|
||||||
)
|
)
|
||||||
|
|
||||||
(func $assert_zero (param $v i32)
|
(func $assert_zero (param $v {typ})
|
||||||
(local i32)
|
(local {typ})
|
||||||
(if (get_local $v)
|
(if ({typ}.ne (get_local $v) ({typ}.const 0))
|
||||||
(unreachable)
|
(unreachable)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
"#;
|
",
|
||||||
|
typ = stringify!($typ)
|
||||||
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
{
|
{
|
||||||
let translated = translate_wat(code);
|
let translated = translate_wat(&code);
|
||||||
translated.disassemble();
|
translated.disassemble();
|
||||||
let out: u32 =
|
let out: $typ = unsafe {
|
||||||
unsafe { translated.execute_func(0, (11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)) };
|
translated.execute_func(
|
||||||
|
0,
|
||||||
|
(
|
||||||
|
11 as $typ, 10 as $typ, 9 as $typ, 8 as $typ, 7 as $typ, 6 as $typ,
|
||||||
|
5 as $typ, 4 as $typ, 3 as $typ, 2 as $typ, 1 as $typ, 0 as $typ,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
};
|
||||||
out
|
out
|
||||||
},
|
},
|
||||||
11
|
11
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
mk_function_write_args_spill_to_stack!(function_write_args_spill_to_stack_i32, i32);
|
||||||
|
mk_function_write_args_spill_to_stack!(function_write_args_spill_to_stack_i64, i64);
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn block() {
|
fn block() {
|
||||||
|
|||||||
Reference in New Issue
Block a user