aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--include/stdarg.h1
-rw-r--r--src/codegen.c42
-rw-r--r--src/parse.c10
-rw-r--r--tests/variadic_functions.sh20
4 files changed, 70 insertions, 3 deletions
diff --git a/include/stdarg.h b/include/stdarg.h
index 609e6af..f879db1 100644
--- a/include/stdarg.h
+++ b/include/stdarg.h
@@ -16,6 +16,7 @@ typedef struct __ducc_va_list va_list[1];
#define va_start(args, start) __ducc_va_start(args, start)
#define va_end(args)
+#define va_arg(args, T) (*(T*)__ducc_va_arg(args, sizeof(T)))
// For glibc:
typedef va_list __gnuc_va_list;
diff --git a/src/codegen.c b/src/codegen.c
index 7adff5a..1c7abf7 100644
--- a/src/codegen.c
+++ b/src/codegen.c
@@ -447,6 +447,48 @@ static void codegen_func_call(CodeGen* g, AstNode* ast) {
return;
}
+ if (strcmp(func_name, "__ducc_va_arg") == 0) {
+ fprintf(g->out, " # __ducc_va_arg BEGIN\n");
+
+ // Evaluate va_list argument (first argument)
+ AstNode* va_list_arg = &ast->node_args->node_items[0];
+ codegen_expr(g, va_list_arg, GenMode_rval);
+ fprintf(g->out, " pop rdi\n"); // rdi = pointer to va_list
+
+ // Evaluate size argument (second argument)
+ AstNode* size_arg = &ast->node_args->node_items[1];
+ codegen_expr(g, size_arg, GenMode_rval);
+ fprintf(g->out, " pop rsi\n"); // rsi = size
+
+ int label = codegen_new_label(g);
+
+ // Check if gp_offset < 48 (6 registers * 8 bytes)
+ fprintf(g->out, " mov eax, DWORD PTR [rdi]\n"); // eax = gp_offset
+ fprintf(g->out, " cmp eax, 48\n");
+ fprintf(g->out, " jae .Lva_arg_overflow%d\n", label);
+
+ // Fetch from register save area
+ fprintf(g->out, " mov rcx, QWORD PTR [rdi+16]\n"); // rcx = reg_save_area
+ fprintf(g->out, " movsx rdx, eax\n"); // rdx = gp_offset (sign-extended)
+ fprintf(g->out, " add rcx, rdx\n"); // rcx = reg_save_area + gp_offset
+ fprintf(g->out, " add eax, 8\n"); // gp_offset += 8
+ fprintf(g->out, " mov DWORD PTR [rdi], eax\n"); // store updated gp_offset
+ fprintf(g->out, " mov rax, rcx\n"); // return pointer to argument
+ fprintf(g->out, " jmp .Lva_arg_end%d\n", label);
+
+ // Fetch from overflow area (stack)
+ fprintf(g->out, ".Lva_arg_overflow%d:\n", label);
+ fprintf(g->out, " mov rcx, QWORD PTR [rdi+8]\n"); // rcx = overflow_arg_area
+ fprintf(g->out, " mov rax, rcx\n"); // return pointer to argument
+ fprintf(g->out, " add rcx, 8\n"); // overflow_arg_area += 8
+ fprintf(g->out, " mov QWORD PTR [rdi+8], rcx\n"); // store updated overflow_arg_area
+
+ fprintf(g->out, ".Lva_arg_end%d:\n", label);
+ fprintf(g->out, " push rax\n");
+ fprintf(g->out, " # __ducc_va_arg END\n");
+ return;
+ }
+
AstNode* args = ast->node_args;
int gp_regs = 6;
diff --git a/src/parse.c b/src/parse.c
index b201aaf..c602073 100644
--- a/src/parse.c
+++ b/src/parse.c
@@ -165,8 +165,11 @@ static Parser* parser_new(TokenArray* tokens) {
Func* va_start_func = funcs_push_new(&p->funcs);
va_start_func->name = "__ducc_va_start";
- va_start_func->ty = calloc(1, sizeof(Type));
- va_start_func->ty->kind = TypeKind_void;
+ va_start_func->ty = type_new_func(type_new(TypeKind_void), NULL);
+
+ Func* va_arg_func = funcs_push_new(&p->funcs);
+ va_arg_func->name = "__ducc_va_arg";
+ va_arg_func->ty = type_new_func(type_new_ptr(type_new(TypeKind_void)), NULL);
return p;
}
@@ -524,6 +527,7 @@ static bool is_type_token(Parser* p, Token* tok) {
}
static Type* parse_type_name(Parser* p);
+static AstNode* parse_cast_expr(Parser* p);
static AstNode* parse_prefix_expr(Parser* p) {
TokenKind op = peek_token(p)->kind;
@@ -540,7 +544,7 @@ static AstNode* parse_prefix_expr(Parser* p) {
AstNode* operand = parse_prefix_expr(p);
return ast_new_ref_expr(operand);
} else if (consume_token_if(p, TokenKind_star)) {
- AstNode* operand = parse_prefix_expr(p);
+ AstNode* operand = parse_cast_expr(p);
return ast_new_deref_expr(operand);
} else if (consume_token_if(p, TokenKind_plusplus)) {
AstNode* operand = parse_prefix_expr(p);
diff --git a/tests/variadic_functions.sh b/tests/variadic_functions.sh
index 4f899ee..155fc1a 100644
--- a/tests/variadic_functions.sh
+++ b/tests/variadic_functions.sh
@@ -29,3 +29,23 @@ int main() {
return 0;
}
EOF
+
+test_exit_code 0 <<'EOF'
+#include <stdarg.h>
+#include <helpers.h>
+
+int sum(int n, ...) {
+ va_list args;
+ va_start(args, n);
+ int s = 0;
+ for (int i = 0; i < n; ++i) {
+ s += va_arg(args, int);
+ }
+ va_end(args);
+ return s;
+}
+
+int main() {
+ ASSERT_EQ(400, sum(5, 100, 90, 80, 70, 60));
+}
+EOF