diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-01-08 23:10:30 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-01-09 01:29:43 +0900 |
| commit | 1e89f9ff938ed458e8be8904bb29b7ced406f9d5 (patch) | |
| tree | e92a832c6b409542017985ad9b423037fcfb721c | |
| parent | dbbaf84db126bc9b72c50987aa724b923f6f52f3 (diff) | |
| download | ducc-1e89f9ff938ed458e8be8904bb29b7ced406f9d5.tar.gz ducc-1e89f9ff938ed458e8be8904bb29b7ced406f9d5.tar.zst ducc-1e89f9ff938ed458e8be8904bb29b7ced406f9d5.zip | |
feat: support seven or more parameters/arguments
| -rw-r--r-- | src/codegen.c | 39 | ||||
| -rw-r--r-- | src/parse.c | 50 | ||||
| -rw-r--r-- | tests/function_basics.c | 43 | ||||
| -rw-r--r-- | tests/helpers.h | 2 | ||||
| -rw-r--r-- | tests/run.sh | 2 |
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 |
