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),
|
Stack(i32),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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.
|
// TODO: This assumes only system-v calling convention.
|
||||||
// In system-v calling convention the first 6 arguments are passed via registers.
|
// In system-v calling convention the first 6 arguments are passed via registers.
|
||||||
// All rest arguments are passed on the stack.
|
// All rest arguments are passed on the stack.
|
||||||
const ARGS_IN_GPRS: &'static [GPR] = &[RDI, RSI, RDX, RCX, R8, R9];
|
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 {
|
||||||
if let Some(®) = ARGS_IN_GPRS.get(pos as usize) {
|
if let Some(®) = 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,11 +358,24 @@ 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) {
|
||||||
assert!(return_arity == 0 || return_arity == 1);
|
assert!(return_arity == 0 || return_arity == 1);
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
84
src/tests.rs
84
src/tests.rs
@@ -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#"
|
||||||
|
|||||||
Reference in New Issue
Block a user