diff --git a/src/backend.rs b/src/backend.rs index 7829b7bf1f..994d0c5e96 100644 --- a/src/backend.rs +++ b/src/backend.rs @@ -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) { diff --git a/src/disassemble.rs b/src/disassemble.rs index d3bb9cafe5..e846d12341 100644 --- a/src/disassemble.rs +++ b/src/disassemble.rs @@ -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(); diff --git a/src/function_body.rs b/src/function_body.rs index 0983973f9f..00e07025c6 100644 --- a/src/function_body.rs +++ b/src/function_body.rs @@ -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(); diff --git a/src/tests.rs b/src/tests.rs index 0e434c7233..14b48c15b9 100644 --- a/src/tests.rs +++ b/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#"