Fix receiving more than 6 arguments, allow calling functions with more than 6 arguments
This commit is contained in:
@@ -89,13 +89,13 @@ enum ArgLocation {
|
||||
Stack(i32),
|
||||
}
|
||||
|
||||
// TODO: This assumes only system-v calling convention.
|
||||
// In system-v calling convention the first 6 arguments are passed via registers.
|
||||
// All rest arguments are passed on the stack.
|
||||
const ARGS_IN_GPRS: &'static [GPR] = &[RDI, RSI, RDX, RCX, R8, R9];
|
||||
|
||||
/// Get a location for an argument at the given position.
|
||||
fn abi_loc_for_arg(pos: u32) -> ArgLocation {
|
||||
// TODO: This assumes only system-v calling convention.
|
||||
// In system-v calling convention the first 6 arguments are passed via registers.
|
||||
// All rest arguments are passed on the stack.
|
||||
const ARGS_IN_GPRS: &'static [GPR] = &[RDI, RSI, RDX, RCX, R8, R9];
|
||||
|
||||
if let Some(®) = ARGS_IN_GPRS.get(pos as usize) {
|
||||
ArgLocation::Reg(reg)
|
||||
} else {
|
||||
@@ -320,7 +320,7 @@ pub fn prepare_return_value(ctx: &mut Context) {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn copy_incoming_arg(ctx: &mut Context, arg_pos: u32) {
|
||||
pub fn copy_incoming_arg(ctx: &mut Context, frame_size: u32, arg_pos: u32) {
|
||||
let loc = abi_loc_for_arg(arg_pos);
|
||||
|
||||
// First, ensure the argument is in a register.
|
||||
@@ -331,6 +331,7 @@ pub fn copy_incoming_arg(ctx: &mut Context, arg_pos: u32) {
|
||||
ctx.regs.scratch_gprs.is_free(RAX),
|
||||
"we assume that RAX can be used as a scratch register for now",
|
||||
);
|
||||
let offset = offset + (frame_size * WORD_SIZE) as i32;
|
||||
dynasm!(ctx.asm
|
||||
; mov Rq(RAX), [rsp + offset]
|
||||
);
|
||||
@@ -346,6 +347,7 @@ pub fn copy_incoming_arg(ctx: &mut Context, arg_pos: u32) {
|
||||
}
|
||||
|
||||
pub fn pass_outgoing_args(ctx: &mut Context, arity: u32) {
|
||||
let mut stack_args = vec![];
|
||||
for arg_pos in (0..arity).rev() {
|
||||
ctx.sp_depth.free(1);
|
||||
|
||||
@@ -356,9 +358,22 @@ pub fn pass_outgoing_args(ctx: &mut Context, arity: u32) {
|
||||
; pop Rq(gpr)
|
||||
);
|
||||
}
|
||||
_ => unimplemented!("don't know how to pass argument {} via {:?}", arg_pos, loc),
|
||||
ArgLocation::Stack(_) => {
|
||||
let gpr = ctx.regs.take_scratch_gpr();
|
||||
dynasm!(ctx.asm
|
||||
; pop Rq(gpr)
|
||||
);
|
||||
stack_args.push(gpr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for gpr in stack_args {
|
||||
dynasm!(ctx.asm
|
||||
; push Rq(gpr)
|
||||
);
|
||||
ctx.regs.release_scratch_gpr(gpr);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn call_direct(ctx: &mut Context, index: u32, return_arity: u32) {
|
||||
|
||||
@@ -20,7 +20,7 @@ pub fn disassemble(mem: &[u8]) -> Result<(), Error> {
|
||||
for b in i.bytes() {
|
||||
write!(&mut bytes_str, "{:02x} ", b).unwrap();
|
||||
}
|
||||
write!(&mut line, "{:21}\t", bytes_str).unwrap();
|
||||
write!(&mut line, "{:24}\t", bytes_str).unwrap();
|
||||
|
||||
if let Some(s) = i.mnemonic() {
|
||||
write!(&mut line, "{}\t", s).unwrap();
|
||||
|
||||
@@ -115,7 +115,7 @@ pub fn translate(
|
||||
prologue(&mut ctx, framesize);
|
||||
|
||||
for arg_pos in 0..arg_count {
|
||||
copy_incoming_arg(&mut ctx, arg_pos);
|
||||
copy_incoming_arg(&mut ctx, framesize, arg_pos);
|
||||
}
|
||||
|
||||
let mut control_frames = Vec::new();
|
||||
|
||||
84
src/tests.rs
84
src/tests.rs
@@ -130,7 +130,7 @@ fn function_call() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn large_function_call() {
|
||||
fn large_function() {
|
||||
let code = r#"
|
||||
(module
|
||||
(func (param i32) (param i32) (param i32) (param i32)
|
||||
@@ -162,6 +162,88 @@ fn large_function_call() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn function_read_args_spill_to_stack() {
|
||||
let code = r#"
|
||||
(module
|
||||
(func (param i32) (param i32) (param i32) (param i32)
|
||||
(param i32) (param i32) (param i32) (param i32)
|
||||
(result i32)
|
||||
|
||||
(call $assert_zero
|
||||
(get_local 7)
|
||||
)
|
||||
(get_local 0)
|
||||
)
|
||||
|
||||
(func $assert_zero (param $v i32)
|
||||
(local i32)
|
||||
(if (get_local $v)
|
||||
(unreachable)
|
||||
)
|
||||
)
|
||||
)
|
||||
"#;
|
||||
|
||||
assert_eq!(
|
||||
{
|
||||
let translated = translate_wat(code);
|
||||
let out: u32 = unsafe { translated.execute_func(0, (7, 6, 5, 4, 3, 2, 1, 0)) };
|
||||
out
|
||||
},
|
||||
7
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn function_write_args_spill_to_stack() {
|
||||
let code = r#"
|
||||
(module
|
||||
(func (param i32) (param i32) (param i32) (param i32)
|
||||
(param i32) (param i32) (param i32) (param i32)
|
||||
(result i32)
|
||||
|
||||
(call $called
|
||||
(get_local 0)
|
||||
(get_local 1)
|
||||
(get_local 2)
|
||||
(get_local 3)
|
||||
(get_local 4)
|
||||
(get_local 5)
|
||||
(get_local 6)
|
||||
(get_local 7)
|
||||
)
|
||||
)
|
||||
|
||||
(func $called
|
||||
(param i32) (param i32) (param i32) (param i32)
|
||||
(param i32) (param i32) (param i32) (param i32)
|
||||
(result i32)
|
||||
|
||||
(call $assert_zero
|
||||
(get_local 7)
|
||||
)
|
||||
(get_local 0)
|
||||
)
|
||||
|
||||
(func $assert_zero (param $v i32)
|
||||
(local i32)
|
||||
(if (get_local $v)
|
||||
(unreachable)
|
||||
)
|
||||
)
|
||||
)
|
||||
"#;
|
||||
|
||||
assert_eq!(
|
||||
{
|
||||
let translated = translate_wat(code);
|
||||
let out: u32 = unsafe { translated.execute_func(0, (7, 6, 5, 4, 3, 2, 1, 0)) };
|
||||
out
|
||||
},
|
||||
7
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn literals() {
|
||||
let code = r#"
|
||||
|
||||
Reference in New Issue
Block a user