Fix receiving more than 6 arguments, allow calling functions with more than 6 arguments

This commit is contained in:
Jef
2018-12-12 13:23:43 +01:00
parent 86353cba5e
commit 189996accd
4 changed files with 107 additions and 10 deletions

View File

@@ -89,13 +89,13 @@ enum ArgLocation {
Stack(i32), 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. /// Get a location for an argument at the given position.
fn abi_loc_for_arg(pos: u32) -> ArgLocation { 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(&reg) = ARGS_IN_GPRS.get(pos as usize) { if let Some(&reg) = ARGS_IN_GPRS.get(pos as usize) {
ArgLocation::Reg(reg) ArgLocation::Reg(reg)
} else { } 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); let loc = abi_loc_for_arg(arg_pos);
// First, ensure the argument is in a register. // 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), ctx.regs.scratch_gprs.is_free(RAX),
"we assume that RAX can be used as a scratch register for now", "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 dynasm!(ctx.asm
; mov Rq(RAX), [rsp + offset] ; 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) { pub fn pass_outgoing_args(ctx: &mut Context, arity: u32) {
let mut stack_args = vec![];
for arg_pos in (0..arity).rev() { for arg_pos in (0..arity).rev() {
ctx.sp_depth.free(1); ctx.sp_depth.free(1);
@@ -356,9 +358,22 @@ pub fn pass_outgoing_args(ctx: &mut Context, arity: u32) {
; pop Rq(gpr) ; 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) { pub fn call_direct(ctx: &mut Context, index: u32, return_arity: u32) {

View File

@@ -20,7 +20,7 @@ pub fn disassemble(mem: &[u8]) -> Result<(), Error> {
for b in i.bytes() { for b in i.bytes() {
write!(&mut bytes_str, "{:02x} ", b).unwrap(); 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() { if let Some(s) = i.mnemonic() {
write!(&mut line, "{}\t", s).unwrap(); write!(&mut line, "{}\t", s).unwrap();

View File

@@ -115,7 +115,7 @@ pub fn translate(
prologue(&mut ctx, framesize); prologue(&mut ctx, framesize);
for arg_pos in 0..arg_count { 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(); let mut control_frames = Vec::new();

View File

@@ -130,7 +130,7 @@ fn function_call() {
} }
#[test] #[test]
fn large_function_call() { fn large_function() {
let code = r#" let code = r#"
(module (module
(func (param i32) (param i32) (param i32) (param i32) (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] #[test]
fn literals() { fn literals() {
let code = r#" let code = r#"