From 754a24fafcea637cab8bc01bb2702069109a0358 Mon Sep 17 00:00:00 2001 From: Rui Ueyama Date: Sun, 25 Aug 2019 11:48:44 +0900 Subject: [PATCH] Add va_start to support variadic functions --- chibicc.h | 1 + codegen.c | 30 ++++++++++++++++++++++++++++++ parse.c | 2 ++ test/function.c | 23 +++++++++++++++++++++++ 4 files changed, 56 insertions(+) diff --git a/chibicc.h b/chibicc.h index 0addaaa..39055cc 100644 --- a/chibicc.h +++ b/chibicc.h @@ -92,6 +92,7 @@ struct Obj { Obj *params; Node *body; Obj *locals; + Obj *va_area; int stack_size; }; diff --git a/codegen.c b/codegen.c index 1371fa8..f6860f5 100644 --- a/codegen.c +++ b/codegen.c @@ -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) diff --git a/parse.c b/parse.c index 510cf5a..26711fc 100644 --- a/parse.c +++ b/parse.c @@ -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); diff --git a/test/function.c b/test/function.c index 0c0d36d..12cb8fb 100644 --- a/test/function.c +++ b/test/function.c @@ -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; } -- GitLab