When making an outgoing call, some arguments may have to be passed on the stack. Allocate OutgoingArg stack slots for these arguments and write them immediately before the outgoing call instruction. Do the same for incoming function arguments on the stack, but use IncomingArg stack slots instead. This was previously done in the spiller, but we move it to the legalizer so it is done at the same time as outgoing stack arguments. These stack slot assignments are done in the legalizer before live range analysis because the outgoing arguments usually are in different SSSA values with their own short live ranges.
135 lines
3.7 KiB
Plaintext
135 lines
3.7 KiB
Plaintext
; Test legalizer's handling of ABI boundaries.
|
|
test legalizer
|
|
isa riscv
|
|
|
|
; regex: V=v\d+
|
|
; regex: SS=ss\d+
|
|
; regex: WS=\s+
|
|
|
|
function %int_split_args(i64) -> i64 {
|
|
ebb0(v0: i64):
|
|
; check: $ebb0($(v0l=$V): i32, $(v0h=$V): i32, $(link=$V): i32):
|
|
; check: $v0 = iconcat $v0l, $v0h
|
|
v1 = iadd_imm v0, 1
|
|
; check: $(v1l=$V), $(v1h=$V) = isplit $v1
|
|
; check: return $v1l, $v1h, $link
|
|
return v1
|
|
}
|
|
|
|
function %split_call_arg(i32) {
|
|
fn1 = function %foo(i64)
|
|
fn2 = function %foo(i32, i64)
|
|
ebb0(v0: i32):
|
|
v1 = uextend.i64 v0
|
|
call fn1(v1)
|
|
; check: $(v1l=$V), $(v1h=$V) = isplit $v1
|
|
; check: call $fn1($v1l, $v1h)
|
|
call fn2(v0, v1)
|
|
; check: call $fn2($v0, $V, $V)
|
|
return
|
|
}
|
|
|
|
function %split_ret_val() {
|
|
fn1 = function %foo() -> i64
|
|
ebb0:
|
|
v1 = call fn1()
|
|
; check: $ebb0($(link=$V): i32):
|
|
; nextln: $(v1l=$V), $(v1h=$V) = call $fn1()
|
|
; check: $v1 = iconcat $v1l, $v1h
|
|
jump ebb1(v1)
|
|
; check: jump $ebb1($v1)
|
|
|
|
ebb1(v10: i64):
|
|
jump ebb1(v10)
|
|
}
|
|
|
|
; First return value is fine, second one is expanded.
|
|
function %split_ret_val2() {
|
|
fn1 = function %foo() -> i32, i64
|
|
ebb0:
|
|
v1, v2 = call fn1()
|
|
; check: $ebb0($(link=$V): i32):
|
|
; nextln: $v1, $(v2l=$V), $(v2h=$V) = call $fn1()
|
|
; check: $v2 = iconcat $v2l, $v2h
|
|
jump ebb1(v1, v2)
|
|
; check: jump $ebb1($v1, $v2)
|
|
|
|
ebb1(v9: i32, v10: i64):
|
|
jump ebb1(v9, v10)
|
|
}
|
|
|
|
function %int_ext(i8, i8 sext, i8 uext) -> i8 uext {
|
|
ebb0(v1: i8, v2: i8, v3: i8):
|
|
; check: $ebb0($v1: i8, $(v2x=$V): i32, $(v3x=$V): i32, $(link=$V): i32):
|
|
; check: $v2 = ireduce.i8 $v2x
|
|
; check: $v3 = ireduce.i8 $v3x
|
|
; check: $(v1x=$V) = uextend.i32 $v1
|
|
; check: return $v1x, $link
|
|
return v1
|
|
}
|
|
|
|
; Function produces single return value, still need to copy.
|
|
function %ext_ret_val() {
|
|
fn1 = function %foo() -> i8 sext
|
|
ebb0:
|
|
v1 = call fn1()
|
|
; check: $ebb0($V: i32):
|
|
; nextln: $(rv=$V) = call $fn1()
|
|
; check: $v1 = ireduce.i8 $rv
|
|
jump ebb1(v1)
|
|
; check: jump $ebb1($v1)
|
|
|
|
ebb1(v10: i8):
|
|
jump ebb1(v10)
|
|
}
|
|
|
|
function %vector_split_args(i64x4) -> i64x4 {
|
|
ebb0(v0: i64x4):
|
|
; check: $ebb0($(v0al=$V): i32, $(v0ah=$V): i32, $(v0bl=$V): i32, $(v0bh=$V): i32, $(v0cl=$V): i32, $(v0ch=$V): i32, $(v0dl=$V): i32, $(v0dh=$V): i32, $(link=$V): i32):
|
|
; check: $(v0a=$V) = iconcat $v0al, $v0ah
|
|
; check: $(v0b=$V) = iconcat $v0bl, $v0bh
|
|
; check: $(v0ab=$V) = vconcat $v0a, $v0b
|
|
; check: $(v0c=$V) = iconcat $v0cl, $v0ch
|
|
; check: $(v0d=$V) = iconcat $v0dl, $v0dh
|
|
; check: $(v0cd=$V) = vconcat $v0c, $v0d
|
|
; check: $v0 = vconcat $v0ab, $v0cd
|
|
v1 = bxor v0, v0
|
|
; check: $(v1ab=$V), $(v1cd=$V) = vsplit $v1
|
|
; check: $(v1a=$V), $(v1b=$V) = vsplit $v1ab
|
|
; check: $(v1al=$V), $(v1ah=$V) = isplit $v1a
|
|
; check: $(v1bl=$V), $(v1bh=$V) = isplit $v1b
|
|
; check: $(v1c=$V), $(v1d=$V) = vsplit $v1cd
|
|
; check: $(v1cl=$V), $(v1ch=$V) = isplit $v1c
|
|
; check: $(v1dl=$V), $(v1dh=$V) = isplit $v1d
|
|
; check: return $v1al, $v1ah, $v1bl, $v1bh, $v1cl, $v1ch, $v1dl, $v1dh, $link
|
|
return v1
|
|
}
|
|
|
|
function %indirect(i32) {
|
|
sig1 = signature()
|
|
ebb0(v0: i32):
|
|
call_indirect sig1, v0()
|
|
return
|
|
}
|
|
|
|
; The first argument to call_indirect doesn't get altered.
|
|
function %indirect_arg(i32, f32x2) {
|
|
sig1 = signature(f32x2)
|
|
ebb0(v0: i32, v1: f32x2):
|
|
call_indirect sig1, v0(v1)
|
|
; check: call_indirect $sig1, $v0($V, $V)
|
|
return
|
|
}
|
|
|
|
; Call a function that takes arguments on the stack.
|
|
function %stack_args(i32) {
|
|
; check: $(ss0=$SS) = outgoing_arg 4
|
|
fn1 = function %foo(i64, i64, i64, i64, i32)
|
|
ebb0(v0: i32):
|
|
v1 = iconst.i64 1
|
|
call fn1(v1, v1, v1, v1, v0)
|
|
; check: [GPsp#48,$ss0]$WS $(v0s=$V) = spill $v0
|
|
; check: call $fn1($(=.*), $v0s)
|
|
return
|
|
}
|