aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-01-08 23:10:30 +0900
committernsfisis <nsfisis@gmail.com>2026-01-09 01:29:43 +0900
commit1e89f9ff938ed458e8be8904bb29b7ced406f9d5 (patch)
treee92a832c6b409542017985ad9b423037fcfb721c
parentdbbaf84db126bc9b72c50987aa724b923f6f52f3 (diff)
downloadducc-1e89f9ff938ed458e8be8904bb29b7ced406f9d5.tar.gz
ducc-1e89f9ff938ed458e8be8904bb29b7ced406f9d5.tar.zst
ducc-1e89f9ff938ed458e8be8904bb29b7ced406f9d5.zip
feat: support seven or more parameters/arguments
-rw-r--r--src/codegen.c39
-rw-r--r--src/parse.c50
-rw-r--r--tests/function_basics.c43
-rw-r--r--tests/helpers.h2
-rw-r--r--tests/run.sh2
5 files changed, 99 insertions, 37 deletions
diff --git a/src/codegen.c b/src/codegen.c
index 97a9209..0b586be 100644
--- a/src/codegen.c
+++ b/src/codegen.c
@@ -56,7 +56,7 @@ 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) {
+ for (int i = 0; i < ast->node_params->node_len && i < 6; ++i) {
fprintf(g->out, " push %s\n", param_reg(i));
}
// Note: rsp must be aligned to 8.
@@ -363,6 +363,17 @@ static void codegen_assign_expr(CodeGen* g, AstNode* ast) {
}
}
+static void codegen_args(CodeGen* g, AstNode* args) {
+ // Evaluate arguments in the reverse order (right to left).
+ for (int i = args->node_len - 1; i >= 0; --i) {
+ AstNode* arg = &args->node_items[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));
+ }
+}
+
static void codegen_func_call(CodeGen* g, AstNode* ast) {
const char* func_name = ast->name;
@@ -395,41 +406,41 @@ static void codegen_func_call(CodeGen* g, AstNode* ast) {
}
AstNode* args = ast->node_args;
- // Evaluate arguments in the reverse order (right to left).
- for (int i = args->node_len - 1; i >= 0; --i) {
- AstNode* arg = &args->node_items[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));
- }
+ int overflow_args_count = args->node_len <= 6 ? 0 : args->node_len - 6;
int label = codegen_new_label(g);
fprintf(g->out, " mov rax, rsp\n");
+ if (overflow_args_count % 2 == 1) {
+ fprintf(g->out, " add rax, 8\n");
+ }
fprintf(g->out, " and rax, 15\n");
fprintf(g->out, " cmp rax, 0\n");
fprintf(g->out, " je .Laligned%d\n", label);
- fprintf(g->out, " mov rax, 0\n");
fprintf(g->out, " sub rsp, 8\n");
+ codegen_args(g, args);
+ fprintf(g->out, " mov rax, 0\n");
fprintf(g->out, " call %s\n", func_name);
fprintf(g->out, " add rsp, 8\n");
- fprintf(g->out, " push rax\n");
fprintf(g->out, " jmp .Lend%d\n", label);
fprintf(g->out, ".Laligned%d:\n", label);
+ codegen_args(g, args);
fprintf(g->out, " mov rax, 0\n");
fprintf(g->out, " call %s\n", func_name);
- fprintf(g->out, " push rax\n");
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, " push rax\n");
}
static void codegen_lvar(CodeGen* g, AstNode* ast, GenMode gen_mode) {
- fprintf(g->out, " mov rax, rbp\n");
- fprintf(g->out, " sub rax, %d\n", ast->node_stack_offset);
+ fprintf(g->out, " lea rax, %d[rbp]\n", -ast->node_stack_offset);
fprintf(g->out, " push rax\n");
if (gen_mode == GenMode_rval) {
codegen_lval2rval(g, ast->ty);
diff --git a/src/parse.c b/src/parse.c
index f336c0c..195a3b0 100644
--- a/src/parse.c
+++ b/src/parse.c
@@ -230,25 +230,33 @@ static int find_lvar(Parser* p, const char* name) {
}
static int calc_stack_offset(Parser* p, Type* ty, bool is_param) {
- int align;
if (is_param) {
if (8 < type_sizeof(ty) || 8 < type_alignof(ty)) {
fatal_error("too large");
}
- align = 8;
- } else {
- align = type_alignof(ty);
- }
-
- int offset;
- if (p->lvars.len == 0) {
- offset = 0;
+ 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;
+ }
} else {
- offset = p->lvars.data[p->lvars.len - 1].stack_offset;
+ 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 += type_sizeof(ty);
- return to_aligned(offset, align);
}
static int add_lvar(Parser* p, const char* name, Type* ty, bool is_param) {
@@ -448,9 +456,6 @@ static AstNode* parse_arg_list(Parser* p) {
break;
}
}
- if (list->node_len > 6) {
- fatal_error("too many arguments");
- }
return list;
}
@@ -1008,9 +1013,6 @@ static AstNode* parse_parameter_type_list(Parser* p) {
has_void |= params->node_items[i].ty->kind == TypeKind_void;
}
- if (params->node_len > 6) {
- fatal_error("too many parameters");
- }
if (has_void) {
if (params->node_len != 1) {
fatal_error("invalid use of void param");
@@ -1464,8 +1466,14 @@ static AstNode* parse_func_def(Parser* p, AstNode* decls) {
if (p->lvars.len == 0) {
func->node_stack_size = 0;
} else {
- func->node_stack_size =
- p->lvars.data[p->lvars.len - 1].stack_offset + type_sizeof(p->lvars.data[p->lvars.len - 1].ty);
+ 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;
+ }
+ }
+ func->node_stack_size = stack_size;
}
return func;
}
diff --git a/tests/function_basics.c b/tests/function_basics.c
index f02384c..1d79ad7 100644
--- a/tests/function_basics.c
+++ b/tests/function_basics.c
@@ -1,5 +1,8 @@
#include <helpers.h>
+int sprintf(char*, const char*, ...);
+int strcmp(const char*, const char*);
+
int foo() {
int i;
int ret;
@@ -38,6 +41,36 @@ int f6(int a, int b, int c, int d, int e, int f) {
return f;
}
+int f7(int select, int a, int b, int c, int d, int e, int f, int g, int h, int i, int j) {
+ switch (select) {
+ case 0:
+ return a;
+ case 1:
+ return b;
+ case 2:
+ return c;
+ case 3:
+ return d;
+ case 4:
+ return e;
+ case 5:
+ return f;
+ case 6:
+ return g;
+ case 7:
+ return h;
+ case 8:
+ return i;
+ case 9:
+ return j;
+ }
+}
+
+char* f8(char* buf, int a, int b, int c, int d, int e, int f, int g, int h, int i, int j) {
+ sprintf(buf, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", a, b, c, d, e, f, g, h, i, j);
+ return buf;
+}
+
int main() {
ASSERT_EQ(66, foo());
ASSERT_EQ(10, 10 * f(1, 2, 3, 4, 5, 6));
@@ -46,4 +79,14 @@ int main() {
ASSERT_EQ(40, 10 * f4(1, 2, 3, 4, 5, 6));
ASSERT_EQ(50, 10 * f5(1, 2, 3, 4, 5, 6));
ASSERT_EQ(60, 10 * f6(1, 2, 3, 4, 5, 6));
+
+ ASSERT_EQ(1, f7(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
+ ASSERT_EQ(2, f7(1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
+ ASSERT_EQ(6, f7(5, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
+ ASSERT_EQ(7, f7(6, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
+ ASSERT_EQ(8, f7(7, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
+ ASSERT_EQ(10, f7(9, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
+
+ 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)));
}
diff --git a/tests/helpers.h b/tests/helpers.h
index f8affe7..39274e4 100644
--- a/tests/helpers.h
+++ b/tests/helpers.h
@@ -10,7 +10,7 @@ extern FILE* stderr;
#define ASSERT(a, file, line) \
do { \
if (!(a)) { \
- fprintf(stderr, "%s:%d: assert failed", file, line); \
+ fprintf(stderr, "%s:%d: assert failed\n", file, line); \
exit(1); \
} \
} while (0)
diff --git a/tests/run.sh b/tests/run.sh
index 0835d9a..6b3bb00 100644
--- a/tests/run.sh
+++ b/tests/run.sh
@@ -14,7 +14,7 @@ if [[ -f "$c_test_file" ]]; then
echo "$c_test_file"
mkdir -p "$tmp_dir"
cd "$tmp_dir"
- test_exit_code < "../../../$c_test_file"
+ test_exit_code 0 < "../../../$c_test_file"
cd "../../.."
elif [[ -f "$sh_test_file" ]]; then
source tests/helpers.sh