riscv: implement stdarg functions

this also fixes passing of params > 16 bytes.  In riscv
they aren't passed by value on stack, but via reference (and
because callees are allowed to modify by-ref params the caller must
allocate an own copy per call).

This fixes the stdarg parts of 73_arm.c.
This commit is contained in:
Michael Matz 2019-07-21 03:25:12 +02:00
parent f44df9d85b
commit 982de78e8d
4 changed files with 61 additions and 15 deletions

View File

@ -63,6 +63,16 @@ typedef struct {
#define va_end(ap) ((void)0)
#define va_copy(dest, src) ((dest) = (src))
#elif defined __riscv
#define __va_reg_size (__riscv_xlen >> 3)
#define _tcc_align(addr,type) (((unsigned long)addr + __alignof__(type) - 1) \
& -(__alignof__(type)))
typedef char *va_list;
#define va_start __builtin_va_start
#define va_arg(ap,type) (*(sizeof(type) > (2*__va_reg_size) ? *(type **)((ap += __va_reg_size) - __va_reg_size) : (ap = (va_list)(_tcc_align(ap,type) + (sizeof(type)+__va_reg_size - 1)& -__va_reg_size), (type *)(ap - ((sizeof(type)+ __va_reg_size - 1)& -__va_reg_size)))))
#define va_copy(dest, src) (dest) = (src)
#define va_end(ap) ((void)0)
#else /* __i386__ */
typedef char *va_list;
/* only correct for i386 */

View File

@ -418,12 +418,15 @@ ST_FUNC void gfunc_call(int nb_args)
sv->type.t &= ~VT_ARRAY; // XXX this should be done in tccgen.c
size = type_size(&sv->type, &align);
if (size > 16) {
if (align < XLEN)
align = XLEN;
tempspace = (tempspace + align - 1) & -align;
tempofs = tempspace;
tempspace += size;
size = align = 8;
byref = 1;
} else if (size > 8)
}
if (size > 8)
nregs = 2;
else
nregs = 1;
@ -447,6 +450,8 @@ ST_FUNC void gfunc_call(int nb_args)
}
} else {
info[i] = 32;
if (align < XLEN)
align = XLEN;
stack_adj += (size + align - 1) & -align;
if (!sa)
force_stack = 1;
@ -478,6 +483,8 @@ ST_FUNC void gfunc_call(int nb_args)
size = align = 8;
}
if (info[i] & 32) {
if (align < XLEN)
align = XLEN;
/* Once we support offseted regs we can do this:
vset(&vtop->type, TREG_SP | VT_LVAL, ofs);
to construct the lvalue for the outgoing stack slot,
@ -537,7 +544,7 @@ ST_FUNC void gfunc_call(int nb_args)
EI(0x13, 0, 2, 2, stack_adj + tempspace); // addi sp, sp, adj
}
static int func_sub_sp_offset;
static int func_sub_sp_offset, num_va_regs;
ST_FUNC void gfunc_prolog(CType *func_type)
{
@ -552,9 +559,6 @@ ST_FUNC void gfunc_prolog(CType *func_type)
loc = -16; // for ra and s0
func_sub_sp_offset = ind;
ind += 5 * 4;
if (sym->f.func_type == FUNC_ELLIPSIS) {
tcc_error("unimp: vararg prologue");
}
aireg = afreg = 0;
addr = 0; // XXX not correct
@ -569,10 +573,17 @@ ST_FUNC void gfunc_prolog(CType *func_type)
}
/* define parameters */
while ((sym = sym->next) != NULL) {
int byref = 0;
type = &sym->type;
size = type_size(type, &align);
if (size > 2 * XLEN) {
type = &char_pointer_type;
size = align = byref = 8;
}
if (size > 2 * XLEN) {
from_stack:
if (align < XLEN)
align = XLEN;
addr = (addr + align - 1) & -align;
param_addr = addr;
addr += size;
@ -603,8 +614,16 @@ ST_FUNC void gfunc_prolog(CType *func_type)
(*pareg)++;
}
}
sym_push(sym->v & ~SYM_FIELD, type,
VT_LOCAL | lvalue_type(type->t), param_addr);
sym_push(sym->v & ~SYM_FIELD, &sym->type,
(byref ? VT_LLOCAL : VT_LOCAL) | lvalue_type(sym->type.t),
param_addr);
}
num_va_regs = 0;
if (func_type->ref->f.func_type == FUNC_ELLIPSIS) {
for (; aireg < 8; aireg++) {
num_va_regs++;
ES(0x23, 3, 8, 10 + aireg, -8 + num_va_regs * 8); // sd aX, loc(s0)
}
}
}
@ -620,7 +639,7 @@ ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret,
if (size > 16)
return 0;
if (size > 8)
ret->t = VT_LDOUBLE;
ret->t = VT_LLONG;
else if (size > 4)
ret->t = VT_LLONG;
else if (size > 2)
@ -660,6 +679,7 @@ ST_FUNC void gfunc_epilog(void)
{
int v, saved_ind, d, large_ofs_ind;
loc = (loc - num_va_regs * 8);
d = v = (-loc + 15) & -16;
if (v >= (1 << 11)) {
@ -668,13 +688,13 @@ ST_FUNC void gfunc_epilog(void)
EI(0x13, 0, 5, 5, (v-16) << 20 >> 20); // addi t0, t0, lo(v)
o(0x33 | (2 << 7) | (2 << 15) | (5 << 20)); //add sp, sp, t0
}
EI(0x03, 3, 1, 2, d - 8); // ld ra, v-8(sp)
EI(0x03, 3, 8, 2, d - 16); // ld s0, v-16(sp)
EI(0x03, 3, 1, 2, d - 8 - num_va_regs * 8); // ld ra, v-8(sp)
EI(0x03, 3, 8, 2, d - 16 - num_va_regs * 8); // ld s0, v-16(sp)
EI(0x13, 0, 2, 2, d); // addi sp, sp, v
EI(0x67, 0, 0, 1, 0); // jalr x0, 0(x1), aka ret
if (v >= (1 << 11)) {
large_ofs_ind = ind;
EI(0x13, 0, 8, 2, d); // addi s0, sp, d
EI(0x13, 0, 8, 2, d - num_va_regs * 8); // addi s0, sp, d
o(0x37 | (5 << 7) | ((0x800 + (v-16)) & 0xfffff000)); //lui t0, upper(v)
EI(0x13, 0, 5, 5, (v-16) << 20 >> 20); // addi t0, t0, lo(v)
o(0x33 | (2 << 7) | (2 << 15) | (5 << 20) | (0x20 << 25)); //sub sp, sp, t0
@ -684,10 +704,10 @@ ST_FUNC void gfunc_epilog(void)
ind = func_sub_sp_offset;
EI(0x13, 0, 2, 2, -d); // addi sp, sp, -d
ES(0x23, 3, 2, 1, d - 8); // sd ra, d-8(sp)
ES(0x23, 3, 2, 8, d - 16); // sd s0, d-16(sp)
ES(0x23, 3, 2, 1, d - 8 - num_va_regs * 8); // sd ra, d-8(sp)
ES(0x23, 3, 2, 8, d - 16 - num_va_regs * 8); // sd s0, d-16(sp)
if (v < (1 << 11))
EI(0x13, 0, 8, 2, d); // addi s0, sp, d
EI(0x13, 0, 8, 2, d - num_va_regs * 8); // addi s0, sp, d
else
gjmp_addr(large_ofs_ind);
if ((ind - func_sub_sp_offset) != 5*4)

View File

@ -5159,6 +5159,20 @@ ST_FUNC void unary(void)
}
}
break;
#ifdef TCC_TARGET_RISCV64
case TOK_builtin_va_start:
parse_builtin_params(0, "ee");
r = vtop->r & VT_VALMASK;
if (r == VT_LLOCAL)
r = VT_LOCAL;
if (r != VT_LOCAL)
tcc_error("__builtin_va_start expects a local variable");
vtop->r = r;
vtop->type = char_pointer_type;
vtop->c.i = 0;
vstore();
break;
#endif
#ifdef TCC_TARGET_X86_64
#ifdef TCC_TARGET_PE
case TOK_builtin_va_start:
@ -5972,7 +5986,7 @@ static void expr_cond(void)
if (c < 0) {
r1 = gv(rc);
move_reg(r2, r1, type.t);
move_reg(r2, r1, islv ? VT_PTR : type.t);
vtop->r = r2;
gsym(tt);
}

View File

@ -158,6 +158,8 @@
#elif defined TCC_TARGET_ARM64
DEF(TOK___va_start, "__va_start")
DEF(TOK___va_arg, "__va_arg")
#elif defined TCC_TARGET_RISCV64
DEF(TOK_builtin_va_start, "__builtin_va_start")
#endif
/* pragma */