feat: in/out 参数 — out 关键字引用传递

fn swap(out x: i64, out y: i64) 声明 out 参数,codegen 层面
函数签名变为 T* 指针,调用点自动传 &variable 地址。
in 是默认行为(值传递),无需显式标注。

Token → Parser → Sema → Codegen 全流水线:
- TOK_OUT + "out" 关键字注册
- AST parameter.is_out 字段
- parse_function 解析 out 前缀
- Sema: out 参数注册为 SYM_VARIABLE+is_mut(可赋值)
- Codegen: LLVM 函数签名使用 T*,调用点传 alloca

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-06-07 14:45:38 +08:00
parent 0a0667776a
commit c8da286d31
14 changed files with 162 additions and 28 deletions
+27 -1
View File
@@ -239,8 +239,34 @@ static LLVMValueRef cg_call_impl(CgCtx* ctx, AstNode* node) {
if (!fn) return NULL;
LLVMValueRef args[16];
if (node->as.call.arg_count > 16) { ctx->error = "函数参数过多(最多16)"; return NULL; }
FnEntry* fn_entry = find_fn_entry(ctx, node->as.call.name);
for (size_t i = 0; i < node->as.call.arg_count; i++) {
args[i] = codegen_expr(ctx, node->as.call.args[i]);
bool is_out = fn_entry && fn_entry->out_params
&& i < fn_entry->pc && fn_entry->out_params[i];
if (is_out) {
// out 参数传 alloca 地址而非加载后的值
AstNode* arg = node->as.call.args[i];
if (arg->kind == AST_IDENT_EXPR) {
args[i] = find_var(ctx, arg->as.ident.name);
} else if (arg->kind == AST_INDEX_EXPR) {
// arr[i]: 生成 GEP 得到元素指针
LLVMValueRef arr_ptr = find_var(ctx, arg->as.index_expr.array->as.ident.name);
LLVMValueRef idx_val = codegen_expr(ctx, arg->as.index_expr.index);
if (!arr_ptr || !idx_val) return NULL;
LLVMValueRef indices[] = {
LLVMConstInt(LLVMInt32TypeInContext(ctx->context), 0, false),
LLVMBuildIntCast2(ctx->builder, idx_val,
LLVMInt32TypeInContext(ctx->context), false, "idx32")
};
args[i] = LLVMBuildGEP2(ctx->builder,
LLVMGetElementType(LLVMTypeOf(arr_ptr)),
arr_ptr, indices, 2, "out_gep");
} else {
args[i] = codegen_expr(ctx, node->as.call.args[i]);
}
} else {
args[i] = codegen_expr(ctx, node->as.call.args[i]);
}
if (!args[i]) return NULL;
}
LLVMTypeRef fn_ty = LLVMGlobalGetValueType(fn);
+31 -9
View File
@@ -23,17 +23,24 @@ LLVMValueRef find_fn(CgCtx* ctx, const char* name) {
return NULL;
}
void add_fn(CgCtx* ctx, const char* name, LLVMValueRef fn) {
void add_fn(CgCtx* ctx, const char* name, LLVMValueRef fn, bool* out_params, size_t pc) {
FnEntry* e = arena_alloc(ctx->arena, sizeof(*e));
if (!e) return;
e->name = name; e->fn = fn;
e->ret = TYPE_VOID;
e->params = NULL;
e->pc = 0;
e->out_params = out_params;
e->pc = pc;
e->next = ctx->fn_table;
ctx->fn_table = e;
}
FnEntry* find_fn_entry(CgCtx* ctx, const char* name) {
for (FnEntry* e = ctx->fn_table; e; e = e->next)
if (strcmp(e->name, name) == 0) return e;
return NULL;
}
// === 结构体类型表 ===
void add_struct_type(CgCtx* ctx, const char* name, LLVMTypeRef ty, size_t fc) {
StructTypeEntry* e = arena_alloc(ctx->arena, sizeof(*e));
@@ -379,14 +386,23 @@ LLVMModuleRef codegen_module(AstNode* ast, Arena* codegen_arena,
AstNode* fn = ast->as.program.functions[i];
LLVMTypeRef* ptypes = arena_alloc(ctx.arena,
fn->as.function.param_count * sizeof(LLVMTypeRef));
bool* out_params = NULL;
if (fn->as.function.param_count > 0) {
out_params = arena_alloc(ctx.arena,
fn->as.function.param_count * sizeof(bool));
}
for (size_t j = 0; j < fn->as.function.param_count; j++) {
AstNode* param = fn->as.function.params[j];
bool is_out = param->as.parameter.is_out;
if (out_params) out_params[j] = is_out;
LLVMTypeRef inner_ty;
if (param->as.parameter.type == TYPE_STRUCT &&
param->as.parameter.struct_type_name) {
ptypes[j] = find_struct_type(&ctx, param->as.parameter.struct_type_name);
inner_ty = find_struct_type(&ctx, param->as.parameter.struct_type_name);
} else {
ptypes[j] = to_llvm_type(&ctx, param->as.parameter.type);
inner_ty = to_llvm_type(&ctx, param->as.parameter.type);
}
ptypes[j] = is_out ? LLVMPointerType(inner_ty, 0) : inner_ty;
}
LLVMTypeRef ret_ty;
if (fn->as.function.return_type == TYPE_STRUCT &&
@@ -398,7 +414,8 @@ LLVMModuleRef codegen_module(AstNode* ast, Arena* codegen_arena,
LLVMTypeRef fty = LLVMFunctionType(ret_ty,
ptypes, (unsigned)fn->as.function.param_count, false);
LLVMValueRef lfn = LLVMAddFunction(ctx.module, fn->as.function.name, fty);
add_fn(&ctx, fn->as.function.name, lfn);
add_fn(&ctx, fn->as.function.name, lfn, out_params,
fn->as.function.param_count);
}
// 第二遍:生成函数体
@@ -422,10 +439,15 @@ LLVMModuleRef codegen_module(AstNode* ast, Arena* codegen_arena,
} else {
param_ty = to_llvm_type(&ctx, pnode->as.parameter.type);
}
LLVMValueRef alloca = LLVMBuildAlloca(ctx.builder,
param_ty, pnode->as.parameter.name);
LLVMBuildStore(ctx.builder, param, alloca);
add_var(&ctx, pnode->as.parameter.name, alloca, param_ty);
if (pnode->as.parameter.is_out) {
// out 参数: param 已是指向调用者变量的指针, 直接用作 alloca
add_var(&ctx, pnode->as.parameter.name, param, param_ty);
} else {
LLVMValueRef alloca = LLVMBuildAlloca(ctx.builder,
param_ty, pnode->as.parameter.name);
LLVMBuildStore(ctx.builder, param, alloca);
add_var(&ctx, pnode->as.parameter.name, alloca, param_ty);
}
}
ctx.defer_count = 0;
+3 -1
View File
@@ -28,6 +28,7 @@ typedef struct FnEntry {
LLVMValueRef fn;
TypeKind ret;
TypeKind* params;
bool* out_params; // 哪些参数是 out (引用传递)
size_t pc;
struct FnEntry* next;
} FnEntry;
@@ -71,7 +72,8 @@ LLVMValueRef coerce_int(CgCtx* ctx, LLVMValueRef val, LLVMTypeRef from_ty, LLVMT
LLVMValueRef find_var(CgCtx* ctx, const char* name);
void add_var(CgCtx* ctx, const char* name, LLVMValueRef alloca, LLVMTypeRef alloca_type);
LLVMValueRef find_fn(CgCtx* ctx, const char* name);
void add_fn(CgCtx* ctx, const char* name, LLVMValueRef fn);
FnEntry* find_fn_entry(CgCtx* ctx, const char* name);
void add_fn(CgCtx* ctx, const char* name, LLVMValueRef fn, bool* out_params, size_t pc);
void add_struct_type(CgCtx* ctx, const char* name, LLVMTypeRef ty, size_t fc);
LLVMTypeRef find_struct_type(CgCtx* ctx, const char* name);