diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-01-10 00:18:17 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-01-10 00:23:37 +0900 |
| commit | 97980c1f3896a630e146fb12d475edbb6c52cbf7 (patch) | |
| tree | 7fc51e5525b9feeb660bc46ea622189bbdc2ef55 /src | |
| parent | 2f386c19d96ab774467e0e97e8c032441e08ced8 (diff) | |
| download | ducc-97980c1f3896a630e146fb12d475edbb6c52cbf7.tar.gz ducc-97980c1f3896a630e146fb12d475edbb6c52cbf7.tar.zst ducc-97980c1f3896a630e146fb12d475edbb6c52cbf7.zip | |
feat: support passing arguments larger than 8 bytes
Diffstat (limited to 'src')
| -rw-r--r-- | src/ast.c | 6 | ||||
| -rw-r--r-- | src/codegen.c | 86 | ||||
| -rw-r--r-- | src/parse.c | 80 |
3 files changed, 123 insertions, 49 deletions
@@ -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; } |
