Add va_start to support variadic functions

This commit is contained in:
Rui Ueyama 2019-08-25 11:48:44 +09:00
parent 58fc86137c
commit 754a24fafc
4 changed files with 56 additions and 0 deletions

View File

@ -92,6 +92,7 @@ struct Obj {
Obj *params;
Node *body;
Obj *locals;
Obj *va_area;
int stack_size;
};

View File

@ -567,6 +567,36 @@ static void emit_text(Obj *prog) {
println(" mov %%rsp, %%rbp");
println(" sub $%d, %%rsp", fn->stack_size);
// Save arg registers if function is variadic
if (fn->va_area) {
int gp = 0;
for (Obj *var = fn->params; var; var = var->next)
gp++;
int off = fn->va_area->offset;
// va_elem
println(" movl $%d, %d(%%rbp)", gp * 8, off);
println(" movl $0, %d(%%rbp)", off + 4);
println(" movq %%rbp, %d(%%rbp)", off + 16);
println(" addq $%d, %d(%%rbp)", off + 24, off + 16);
// __reg_save_area__
println(" movq %%rdi, %d(%%rbp)", off + 24);
println(" movq %%rsi, %d(%%rbp)", off + 32);
println(" movq %%rdx, %d(%%rbp)", off + 40);
println(" movq %%rcx, %d(%%rbp)", off + 48);
println(" movq %%r8, %d(%%rbp)", off + 56);
println(" movq %%r9, %d(%%rbp)", off + 64);
println(" movsd %%xmm0, %d(%%rbp)", off + 72);
println(" movsd %%xmm1, %d(%%rbp)", off + 80);
println(" movsd %%xmm2, %d(%%rbp)", off + 88);
println(" movsd %%xmm3, %d(%%rbp)", off + 96);
println(" movsd %%xmm4, %d(%%rbp)", off + 104);
println(" movsd %%xmm5, %d(%%rbp)", off + 112);
println(" movsd %%xmm6, %d(%%rbp)", off + 120);
println(" movsd %%xmm7, %d(%%rbp)", off + 128);
}
// Save passed-by-register arguments to the stack
int i = 0;
for (Obj *var = fn->params; var; var = var->next)

View File

@ -2212,6 +2212,8 @@ static Token *function(Token *tok, Type *basety, VarAttr *attr) {
enter_scope();
create_param_lvars(ty->params);
fn->params = locals;
if (ty->is_variadic)
fn->va_area = new_lvar("__va_area__", array_of(ty_char, 136));
tok = skip(tok, "{");
fn->body = compound_stmt(&tok, tok);

View File

@ -72,6 +72,25 @@ short short_fn();
int add_all(int n, ...);
typedef struct {
int gp_offset;
int fp_offset;
void *overflow_arg_area;
void *reg_save_area;
} __va_elem;
typedef __va_elem va_list[1];
int add_all(int n, ...);
int sprintf(char *buf, char *fmt, ...);
int vsprintf(char *buf, char *fmt, va_list ap);
char *fmt(char *buf, char *fmt, ...) {
va_list ap;
*ap = *(__va_elem *)__va_area__;
vsprintf(buf, fmt, ap);
}
int main() {
ASSERT(3, ret3());
ASSERT(8, add2(3, 5));
@ -121,8 +140,12 @@ int main() {
ASSERT(6, add_all(3,1,2,3));
ASSERT(5, add_all(4,1,2,3,-1));
{ char buf[100]; fmt(buf, "%d %d %s", 1, 2, "foo"); printf("%s\n", buf); }
ASSERT(0, ({ char buf[100]; sprintf(buf, "%d %d %s", 1, 2, "foo"); strcmp("1 2 foo", buf); }));
ASSERT(0, ({ char buf[100]; fmt(buf, "%d %d %s", 1, 2, "foo"); strcmp("1 2 foo", buf); }));
printf("OK\n");
return 0;
}