From 97980c1f3896a630e146fb12d475edbb6c52cbf7 Mon Sep 17 00:00:00 2001 From: nsfisis Date: Sat, 10 Jan 2026 00:18:17 +0900 Subject: feat: support passing arguments larger than 8 bytes --- src/ast.c | 6 +++- src/codegen.c | 86 +++++++++++++++++++++++++++++++++++++++++++------ src/parse.c | 80 +++++++++++++++++++++++---------------------- tests/function_basics.c | 37 +++++++++++++++++++++ 4 files changed, 160 insertions(+), 49 deletions(-) diff --git a/src/ast.c b/src/ast.c index 388e211..59a2087 100644 --- a/src/ast.c +++ b/src/ast.c @@ -113,7 +113,7 @@ Type* type_new_func(Type* result, AstNode* params) { } bool type_is_unsized(Type* ty) { - return ty->kind == TypeKind_void || ty->kind == TypeKind_func; + return ty->kind == TypeKind_void; } int type_sizeof(Type* ty) { @@ -141,6 +141,8 @@ int type_sizeof(Type* ty) { return 8; else if (ty->kind == TypeKind_array) return type_sizeof(ty->base) * ty->array_size; + else if (ty->kind == TypeKind_func) + return 8; else unreachable(); } @@ -170,6 +172,8 @@ int type_alignof(Type* ty) { return 8; else if (ty->kind == TypeKind_array) return type_alignof(ty->base); + else if (ty->kind == TypeKind_func) + return 8; else unreachable(); } diff --git a/src/codegen.c b/src/codegen.c index 0b586be..7adff5a 100644 --- a/src/codegen.c +++ b/src/codegen.c @@ -56,8 +56,11 @@ static const char* param_reg(int n) { static void codegen_func_prologue(CodeGen* g, AstNode* ast) { fprintf(g->out, " push rbp\n"); fprintf(g->out, " mov rbp, rsp\n"); - for (int i = 0; i < ast->node_params->node_len && i < 6; ++i) { - fprintf(g->out, " push %s\n", param_reg(i)); + for (int i = 0, j = 0; i < ast->node_params->node_len; ++i) { + AstNode* param = &ast->node_params->node_items[i]; + if (param->node_stack_offset >= 0) { + fprintf(g->out, " push %s\n", param_reg(j++)); + } } // Note: rsp must be aligned to 8. fprintf(g->out, " sub rsp, %d\n", to_aligned(ast->node_stack_size, 8)); @@ -364,13 +367,52 @@ static void codegen_assign_expr(CodeGen* g, AstNode* ast) { } static void codegen_args(CodeGen* g, AstNode* args) { + bool* pass_by_stack = calloc(args->node_len, sizeof(bool)); + + int gp_regs = 6; + for (int i = 0; i < args->node_len; ++i) { + AstNode* arg = &args->node_items[i]; + int ty_size = type_sizeof(arg->ty); + // TODO + if (arg->ty->kind == TypeKind_array) { + ty_size = 8; + } + int required_gp_regs; + if (ty_size <= 8) { + required_gp_regs = 1; + } else if (ty_size <= 16) { + required_gp_regs = 2; + } else { + required_gp_regs = 0; + } + if (required_gp_regs <= gp_regs) { + gp_regs -= required_gp_regs; + } else { + required_gp_regs = 0; + } + pass_by_stack[i] = required_gp_regs == 0; + } + // Evaluate arguments in the reverse order (right to left). + // Arguments passed by stack. + for (int i = args->node_len - 1; i >= 0; --i) { + AstNode* arg = &args->node_items[i]; + if (pass_by_stack[i]) { + codegen_expr(g, arg, GenMode_rval); + } + } + // Arguments passed by registers. for (int i = args->node_len - 1; i >= 0; --i) { AstNode* arg = &args->node_items[i]; - codegen_expr(g, arg, GenMode_rval); + if (!pass_by_stack[i]) { + codegen_expr(g, arg, GenMode_rval); + } } - for (int i = 0; i < args->node_len && i < 6; ++i) { - fprintf(g->out, " pop %s\n", param_reg(i)); + // Pop pushed arguments onto registers. + for (int i = 0, j = 0; i < args->node_len; ++i) { + if (!pass_by_stack[i]) { + fprintf(g->out, " pop %s\n", param_reg(j++)); + } } } @@ -406,12 +448,38 @@ static void codegen_func_call(CodeGen* g, AstNode* ast) { } AstNode* args = ast->node_args; - int overflow_args_count = args->node_len <= 6 ? 0 : args->node_len - 6; + + int gp_regs = 6; + int pass_by_stack_offset = -16; + for (int i = 0; i < args->node_len; ++i) { + AstNode* arg = &args->node_items[i]; + int ty_size = type_sizeof(arg->ty); + // TODO + if (arg->ty->kind == TypeKind_array) { + ty_size = 8; + } + int required_gp_regs; + if (ty_size <= 8) { + required_gp_regs = 1; + } else if (ty_size <= 16) { + required_gp_regs = 2; + } else { + required_gp_regs = 0; + } + if (required_gp_regs <= gp_regs) { + gp_regs -= required_gp_regs; + } else { + required_gp_regs = 0; + } + if (required_gp_regs == 0) { + pass_by_stack_offset -= to_aligned(ty_size, 8); + } + } int label = codegen_new_label(g); fprintf(g->out, " mov rax, rsp\n"); - if (overflow_args_count % 2 == 1) { + if (-pass_by_stack_offset % 16 != 0) { fprintf(g->out, " add rax, 8\n"); } fprintf(g->out, " and rax, 15\n"); @@ -433,9 +501,7 @@ static void codegen_func_call(CodeGen* g, AstNode* ast) { fprintf(g->out, ".Lend%d:\n", label); // Pop pass-by-stack arguments. - for (int i = 0; i < overflow_args_count; i++) { - fprintf(g->out, " add rsp, 8\n"); - } + fprintf(g->out, " add rsp, %d\n", -pass_by_stack_offset - 16); fprintf(g->out, " push rax\n"); } diff --git a/src/parse.c b/src/parse.c index 078e4d5..b201aaf 100644 --- a/src/parse.c +++ b/src/parse.c @@ -229,38 +229,19 @@ static int find_lvar(Parser* p, const char* name) { return -1; } -static int calc_stack_offset(Parser* p, Type* ty, bool is_param) { - if (is_param) { - if (8 < type_sizeof(ty) || 8 < type_alignof(ty)) { - fatal_error("too large"); - } - int offset; - if (p->lvars.len == 0) { - offset = 0; - } else { - offset = p->lvars.data[p->lvars.len - 1].stack_offset; - } - if (offset < 0) { - return offset - 8; - } else if (offset >= 6 * 8) { - return -16; - } else { - return offset + 8; - } +static int calc_lvar_stack_offset(Parser* p, Type* ty) { + int offset; + if (p->lvars.len == 0) { + offset = 0; } else { - int offset = 0; - for (size_t i = 0; i < p->lvars.len; i++) { - int o = p->lvars.data[i].stack_offset; - if (offset < o) { - offset = o; - } - } - return to_aligned(offset + type_sizeof(ty), type_alignof(ty)); + offset = p->lvars.data[p->lvars.len - 1].stack_offset; + if (offset < 0) + offset = 0; } + return to_aligned(offset + type_sizeof(ty), type_alignof(ty)); } -static int add_lvar(Parser* p, const char* name, Type* ty, bool is_param) { - int stack_offset = calc_stack_offset(p, ty, is_param); +static int add_lvar(Parser* p, const char* name, Type* ty, int stack_offset) { LocalVar* lvar = lvars_push_new(&p->lvars); lvar->name = name; lvar->ty = ty; @@ -272,7 +253,7 @@ static int add_lvar(Parser* p, const char* name, Type* ty, bool is_param) { } static AstNode* generate_temporary_lvar(Parser* p, Type* ty) { - int stack_offset = add_lvar(p, NULL, ty, false); + int stack_offset = add_lvar(p, NULL, ty, calc_lvar_stack_offset(p, ty)); AstNode* lvar = ast_new(AstNodeKind_lvar); lvar->name = NULL; lvar->node_stack_offset = stack_offset; @@ -1369,9 +1350,35 @@ static AstNode* parse_stmt(Parser* p) { } static void register_params(Parser* p, AstNode* params) { + int gp_regs = 6; + int pass_by_reg_offset = 8; + int pass_by_stack_offset = -16; for (int i = 0; i < params->node_len; ++i) { - AstNode* param = params->node_items + i; - add_lvar(p, param->name, param->ty, true); + AstNode* param = ¶ms->node_items[i]; + int ty_size = type_sizeof(param->ty); + int required_gp_regs; + if (ty_size <= 8) { + required_gp_regs = 1; + } else if (ty_size <= 16) { + required_gp_regs = 2; + } else { + required_gp_regs = 0; + } + if (required_gp_regs <= gp_regs) { + gp_regs -= required_gp_regs; + } else { + required_gp_regs = 0; + } + int stack_offset; + if (required_gp_regs == 0) { + stack_offset = pass_by_stack_offset; + pass_by_stack_offset -= to_aligned(ty_size, 8); + } else { + stack_offset = pass_by_reg_offset; + pass_by_reg_offset += to_aligned(ty_size, 8); + } + param->node_stack_offset = stack_offset; + add_lvar(p, param->name, param->ty, stack_offset); } } @@ -1398,7 +1405,7 @@ static void declare_func_or_var(Parser* p, AstNode* decl) { // TODO: use name's location. fatal_error("%s:%d: '%s' redeclared", peek_token(p)->loc.filename, peek_token(p)->loc.line, decl->name); } - int stack_offset = add_lvar(p, decl->name, decl->ty, false); + int stack_offset = add_lvar(p, decl->name, decl->ty, calc_lvar_stack_offset(p, decl->ty)); if (decl->node_init) { AstNode* lhs = ast_new(AstNodeKind_lvar); @@ -1466,12 +1473,9 @@ static AstNode* parse_func_def(Parser* p, AstNode* decls) { if (p->lvars.len == 0) { func->node_stack_size = 0; } else { - int stack_size = 0; - for (size_t i = 0; i < p->lvars.len; i++) { - int s = p->lvars.data[i].stack_offset + type_sizeof(p->lvars.data[i].ty); - if (stack_size < s) { - stack_size = s; - } + int stack_size = p->lvars.data[p->lvars.len - 1].stack_offset + type_sizeof(p->lvars.data[p->lvars.len - 1].ty); + if (stack_size < 0) { + stack_size = 0; } func->node_stack_size = stack_size; } diff --git a/tests/function_basics.c b/tests/function_basics.c index 1d79ad7..83fb66e 100644 --- a/tests/function_basics.c +++ b/tests/function_basics.c @@ -71,6 +71,31 @@ char* f8(char* buf, int a, int b, int c, int d, int e, int f, int g, int h, int return buf; } +typedef struct { + long x, y; +} S; + +int f9(int select, int a, int b, int c, int d, S e, int f, int g) { + switch (select) { + case 0: + return a; + case 1: + return b; + case 2: + return c; + case 3: + return d; + case 4: + return e.x; + case 5: + return e.y; + case 6: + return f; + case 7: + return g; + } +} + int main() { ASSERT_EQ(66, foo()); ASSERT_EQ(10, 10 * f(1, 2, 3, 4, 5, 6)); @@ -89,4 +114,16 @@ int main() { char buf[100]; ASSERT_EQ(0, strcmp("1,2,3,4,5,6,7,8,9,10", f8(buf, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10))); + + S s; + s.x = 5; + s.y = 6; + ASSERT_EQ(1, f9(0, 1, 2, 3, 4, s, 7, 8)); + ASSERT_EQ(2, f9(1, 1, 2, 3, 4, s, 7, 8)); + ASSERT_EQ(3, f9(2, 1, 2, 3, 4, s, 7, 8)); + ASSERT_EQ(4, f9(3, 1, 2, 3, 4, s, 7, 8)); + ASSERT_EQ(5, f9(4, 1, 2, 3, 4, s, 7, 8)); + ASSERT_EQ(6, f9(5, 1, 2, 3, 4, s, 7, 8)); + ASSERT_EQ(7, f9(6, 1, 2, 3, 4, s, 7, 8)); + ASSERT_EQ(8, f9(7, 1, 2, 3, 4, s, 7, 8)); } -- cgit v1.2.3-70-g09d2