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:
+27
-1
@@ -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
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user