aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-01-10 00:18:17 +0900
committernsfisis <nsfisis@gmail.com>2026-01-10 00:23:37 +0900
commit97980c1f3896a630e146fb12d475edbb6c52cbf7 (patch)
tree7fc51e5525b9feeb660bc46ea622189bbdc2ef55 /src
parent2f386c19d96ab774467e0e97e8c032441e08ced8 (diff)
downloadducc-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.c6
-rw-r--r--src/codegen.c86
-rw-r--r--src/parse.c80
3 files changed, 123 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 = &params->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;
}