feat: 自动内存管理 — 作用域级 RAII for str
- codegen: 声明 CRT free(), cleanup_add/cleanup_emit 辅助函数 - LET_STMT: str 拼接/struct 初始化 → 追踪到 cleanup_list - BLOCK: 作用域退出时自动 emit free() 释放 str 堆分配 - RETURN: return 前先释放当前作用域所有 str 分配 - 函数隐式 return: 退出前 cleanup_emit - str 字面量不追踪(全局常量,free 会崩溃) 内存模型(类 Rust): let s = "a" + "b" → malloc 分配 → 作用域退出 → free let s = "literal" → 全局常量 → 不追踪 → 不 free let mut s = ... → 重新赋值时旧值需手动管理(v0.5 扩展) 消除报告 §5-1 str concat malloc 内存泄漏。
This commit is contained in:
+59
-7
@@ -42,8 +42,12 @@ typedef struct {
|
||||
LLVMTypeRef printf_ty;
|
||||
// 字符串拼接运行时支持
|
||||
LLVMValueRef malloc_fn;
|
||||
LLVMValueRef free_fn; // auto-free 需要 free()
|
||||
LLVMValueRef strlen_fn;
|
||||
LLVMValueRef memcpy_fn;
|
||||
// 自动内存管理: 追踪需要 free 的 str alloca
|
||||
LLVMValueRef cleanup_list[64];
|
||||
size_t cleanup_count;
|
||||
} CgCtx;
|
||||
|
||||
// === 类型映射(需要 Context)===
|
||||
@@ -340,6 +344,26 @@ static LLVMValueRef codegen_expr(CgCtx* ctx, AstNode* node) {
|
||||
}
|
||||
}
|
||||
|
||||
// === 自动内存管理: 作用域退出时释放 str 堆分配 ===
|
||||
static void cleanup_add(CgCtx* ctx, LLVMValueRef alloca) {
|
||||
if (ctx->cleanup_count < 64) {
|
||||
ctx->cleanup_list[ctx->cleanup_count++] = alloca;
|
||||
}
|
||||
}
|
||||
|
||||
// 释放从 mark 位置开始的所有 str 变量
|
||||
static void cleanup_emit(CgCtx* ctx, size_t from_mark) {
|
||||
for (size_t j = from_mark; j < ctx->cleanup_count; j++) {
|
||||
LLVMValueRef ptr = ctx->cleanup_list[j];
|
||||
LLVMValueRef val = LLVMBuildLoad2(ctx->builder,
|
||||
LLVMPointerType(LLVMInt8TypeInContext(ctx->context), 0), ptr, "free_load");
|
||||
LLVMBuildCall2(ctx->builder,
|
||||
LLVMGlobalGetValueType(ctx->free_fn), ctx->free_fn,
|
||||
(LLVMValueRef[]){val}, 1, "");
|
||||
}
|
||||
ctx->cleanup_count = from_mark;
|
||||
}
|
||||
|
||||
// === 语句代码生成 ===
|
||||
static void codegen_stmt(CgCtx* ctx, AstNode* node) {
|
||||
if (!node) return;
|
||||
@@ -362,6 +386,17 @@ static void codegen_stmt(CgCtx* ctx, AstNode* node) {
|
||||
var_type, node->as.let_stmt.name);
|
||||
LLVMBuildStore(ctx->builder, init_val, alloca);
|
||||
add_var(ctx, node->as.let_stmt.name, alloca);
|
||||
|
||||
// 自动内存管理: str 堆分配追踪
|
||||
// 只有 BINARY_EXPR (拼接) 和 STRUCT_INIT 产生堆内存
|
||||
if (node->as.let_stmt.init->type.kind == TYPE_STR) {
|
||||
AstKind ik = node->as.let_stmt.init->kind;
|
||||
if (ik == AST_BINARY_EXPR || ik == AST_STRUCT_INIT || ik == AST_CALL_EXPR) {
|
||||
cleanup_add(ctx, alloca);
|
||||
}
|
||||
} else if (node->as.let_stmt.init->type.kind == TYPE_STRUCT) {
|
||||
cleanup_add(ctx, alloca); // struct 可能含 str 字段
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -378,20 +413,30 @@ static void codegen_stmt(CgCtx* ctx, AstNode* node) {
|
||||
codegen_expr(ctx, node->as.expr_stmt.expr);
|
||||
break;
|
||||
|
||||
case AST_RETURN_STMT:
|
||||
if (node->as.return_stmt.expr) {
|
||||
LLVMValueRef val = codegen_expr(ctx, node->as.return_stmt.expr);
|
||||
if (val) LLVMBuildRet(ctx->builder, val);
|
||||
} else {
|
||||
LLVMBuildRetVoid(ctx->builder);
|
||||
case AST_RETURN_STMT: {
|
||||
// 先计算返回值
|
||||
LLVMValueRef ret_val = NULL;
|
||||
bool has_val = node->as.return_stmt.expr != NULL;
|
||||
if (has_val) {
|
||||
ret_val = codegen_expr(ctx, node->as.return_stmt.expr);
|
||||
if (!ret_val) return;
|
||||
}
|
||||
// return 前释放当前作用域所有 str 堆分配
|
||||
cleanup_emit(ctx, 0);
|
||||
// 然后 emit ret
|
||||
if (has_val) LLVMBuildRet(ctx->builder, ret_val);
|
||||
else LLVMBuildRetVoid(ctx->builder);
|
||||
break;
|
||||
}
|
||||
|
||||
case AST_BLOCK:
|
||||
case AST_BLOCK: {
|
||||
size_t block_mark = ctx->cleanup_count;
|
||||
for (size_t i = 0; i < node->as.block.stmt_count; i++) {
|
||||
codegen_stmt(ctx, node->as.block.stmts[i]);
|
||||
}
|
||||
cleanup_emit(ctx, block_mark); // 作用域退出: 释放块内 str 堆分配
|
||||
break;
|
||||
}
|
||||
|
||||
case AST_IF_STMT: {
|
||||
LLVMValueRef cond = codegen_expr(ctx, node->as.if_stmt.cond);
|
||||
@@ -478,6 +523,11 @@ LLVMModuleRef codegen_module(AstNode* ast, Arena* codegen_arena,
|
||||
LLVMPointerType(LLVMInt8TypeInContext(ctx.context), 0), malloc_args, 1, false);
|
||||
ctx.malloc_fn = LLVMAddFunction(ctx.module, "malloc", malloc_ty);
|
||||
|
||||
// 声明 free: void free(void*)
|
||||
LLVMTypeRef free_args[] = { LLVMPointerType(LLVMInt8TypeInContext(ctx.context), 0) };
|
||||
LLVMTypeRef free_ty = LLVMFunctionType(LLVMVoidTypeInContext(ctx.context), free_args, 1, false);
|
||||
ctx.free_fn = LLVMAddFunction(ctx.module, "free", free_ty);
|
||||
|
||||
// 声明 strlen: size_t strlen(const char*)
|
||||
LLVMTypeRef strlen_args[] = { LLVMPointerType(LLVMInt8TypeInContext(ctx.context), 0) };
|
||||
LLVMTypeRef strlen_ty = LLVMFunctionType(
|
||||
@@ -560,6 +610,8 @@ LLVMModuleRef codegen_module(AstNode* ast, Arena* codegen_arena,
|
||||
|
||||
// 确保函数有终止指令(terminator)
|
||||
if (!LLVMGetBasicBlockTerminator(LLVMGetInsertBlock(ctx.builder))) {
|
||||
// 函数结尾隐式 return: 先释放所有 str 堆分配
|
||||
cleanup_emit(&ctx, 0);
|
||||
if (fn->as.function.return_type == TYPE_VOID)
|
||||
LLVMBuildRetVoid(ctx.builder);
|
||||
else
|
||||
|
||||
Reference in New Issue
Block a user