Allow variadic function to take more than 6 parameters

This commit is contained in:
Rui Ueyama 2020-09-04 18:27:31 +09:00
parent d7bad96114
commit b6d3cd00df
4 changed files with 41 additions and 17 deletions

View File

@ -1178,9 +1178,11 @@ static void emit_text(Obj *prog) {
int off = fn->va_area->offset;
// va_elem
println(" movl $%d, %d(%%rbp)", gp * 8, off);
println(" movl $%d, %d(%%rbp)", fp * 8 + 48, off + 4);
println(" movq %%rbp, %d(%%rbp)", off + 16);
println(" movl $%d, %d(%%rbp)", gp * 8, off); // gp_offset
println(" movl $%d, %d(%%rbp)", fp * 8 + 48, off + 4); // fp_offset
println(" movq %%rbp, %d(%%rbp)", off + 8); // overflow_arg_area
println(" addq $16, %d(%%rbp)", off + 8);
println(" movq %%rbp, %d(%%rbp)", off + 16); // reg_save_area
println(" addq $%d, %d(%%rbp)", off + 24, off + 16);
// __reg_save_area__

View File

@ -15,28 +15,38 @@ typedef __va_elem va_list[1];
#define va_end(ap)
static void *__va_arg_gp(__va_elem *ap) {
void *r = (char *)ap->reg_save_area + ap->gp_offset;
static void *__va_arg_mem(__va_elem *ap, int sz, int align) {
void *p = ap->overflow_arg_area;
if (align > 8)
p = (p + 15) / 16 * 16;
ap->overflow_arg_area = ((unsigned long)p + sz + 7) / 8 * 8;
return p;
}
static void *__va_arg_gp(__va_elem *ap, int sz, int align) {
if (ap->gp_offset >= 48)
return __va_arg_mem(ap, sz, align);
void *r = ap->reg_save_area + ap->gp_offset;
ap->gp_offset += 8;
return r;
}
static void *__va_arg_fp(__va_elem *ap) {
void *r = (char *)ap->reg_save_area + ap->fp_offset;
static void *__va_arg_fp(__va_elem *ap, int sz, int align) {
if (ap->fp_offset >= 112)
return __va_arg_mem(ap, sz, align);
void *r = ap->reg_save_area + ap->fp_offset;
ap->fp_offset += 8;
return r;
}
static void *__va_arg_mem(__va_elem *ap) {
1 / 0; // not implemented
}
#define va_arg(ap, type) \
({ \
int klass = __builtin_reg_class(type); \
*(type *)(klass == 0 ? __va_arg_gp(ap) : \
klass == 1 ? __va_arg_fp(ap) : \
__va_arg_mem(ap)); \
#define va_arg(ap, ty) \
({ \
int klass = __builtin_reg_class(ty); \
*(ty *)(klass == 0 ? __va_arg_gp(ap, sizeof(ty), _Alignof(ty)) : \
klass == 1 ? __va_arg_fp(ap, sizeof(ty), _Alignof(ty)) : \
__va_arg_mem(ap, sizeof(ty), _Alignof(ty))); \
})
#define __GNUC_VA_LIST 1

View File

@ -3,6 +3,7 @@
void assert(int expected, int actual, char *code);
int printf(char *fmt, ...);
int sprintf(char *buf, char *fmt, ...);
int vsprintf(char *buf, char *fmt, void *ap);
int strcmp(char *p, char *q);
int strncmp(char *p, char *q, long n);
int memcmp(char *p, char *q, long n);

View File

@ -28,9 +28,20 @@ int sum2(int x, ...) {
}
}
char *fmt(char *buf, char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
vsprintf(buf, fmt, ap);
va_end(ap);
return buf;
}
int main() {
ASSERT(6, sum1(1, 2, 3, 0));
ASSERT(55, sum1(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0));
ASSERT(21, sum2(1, 2.0, 3, 4.0, 5, 6.0, 0));
ASSERT(21, sum2(1, 2.0, 3, 4.0, 5, 6.0, 0));
ASSERT(210, sum2(1, 2.0, 3, 4.0, 5, 6.0, 7, 8.0, 9, 10.0, 11, 12.0, 13, 14.0, 15, 16.0, 17, 18.0, 19, 20.0, 0));
printf("OK\n");
return 0;