f8c5e18188
- codegen.c: VarEntry/FnEntry/ptypes 全部改用 arena_alloc,消除 malloc/free - codegen_module 新增 Arena* 参数,main.c 传入主 arena - 新增 .codegraphignore 排除 build/ 和 .codegraph/ - 基于 Codex 分析报告第7节技术债务
170 lines
5.2 KiB
C
170 lines
5.2 KiB
C
#include "l_lang.h"
|
|
#include "lexer.h"
|
|
#include "parser.h"
|
|
#include "sema.h"
|
|
#include "codegen.h"
|
|
#include "error.h"
|
|
#include "arena.h"
|
|
|
|
#include <llvm-c/Core.h>
|
|
#include <llvm-c/TargetMachine.h>
|
|
#include <llvm-c/Target.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
// 读取整个文件到内存
|
|
static char* read_file(const char* path, size_t* size) {
|
|
FILE* f = fopen(path, "rb");
|
|
if (!f) { fprintf(stderr, "无法打开文件: %s\n", path); return NULL; }
|
|
fseek(f, 0, SEEK_END);
|
|
*size = ftell(f);
|
|
fseek(f, 0, SEEK_SET);
|
|
char* buf = malloc(*size + 1);
|
|
if (!buf) { fclose(f); return NULL; }
|
|
fread(buf, 1, *size, f);
|
|
buf[*size] = '\0';
|
|
fclose(f);
|
|
return buf;
|
|
}
|
|
|
|
// 写入字符串到文件
|
|
static bool write_file(const char* path, const char* data) {
|
|
FILE* f = fopen(path, "w");
|
|
if (!f) return false;
|
|
fputs(data, f);
|
|
fclose(f);
|
|
return true;
|
|
}
|
|
|
|
int main(int argc, char** argv) {
|
|
const char* input = NULL;
|
|
const char* output = "a.exe";
|
|
bool emit_ir = false;
|
|
|
|
// 解析命令行参数
|
|
for (int i = 1; i < argc; i++) {
|
|
if (strcmp(argv[i], "--emit-ir") == 0) { emit_ir = true; }
|
|
else if (strcmp(argv[i], "-o") == 0 && i + 1 < argc) { output = argv[++i]; }
|
|
else if (argv[i][0] != '-') { input = argv[i]; }
|
|
}
|
|
|
|
if (!input) {
|
|
fprintf(stderr, "用法: l_lang <文件.l> [-o <输出>] [--emit-ir]\n");
|
|
return 1;
|
|
}
|
|
|
|
// 1. 读取源文件
|
|
size_t src_size;
|
|
char* source = read_file(input, &src_size);
|
|
if (!source) return 1;
|
|
|
|
// 2. 初始化
|
|
Arena arena = arena_create(8); // 8 MB
|
|
if (!arena.memory) { fprintf(stderr, "内存分配失败\n"); free(source); return 1; }
|
|
|
|
ErrorInfo error = {0};
|
|
ErrorList error_list; error_init(&error_list);
|
|
|
|
// 3. 词法分析
|
|
size_t token_count;
|
|
Token* tokens = lex(&arena, source, input, &token_count, &error);
|
|
if (!tokens) {
|
|
fprintf(stderr, "词法错误: %s:%d:%d: %s\n",
|
|
error.filename, error.line, error.col, error.message);
|
|
free(source); arena_destroy(&arena);
|
|
return 1;
|
|
}
|
|
|
|
// 4. 语法分析
|
|
AstNode* ast = parse(&arena, tokens, token_count, input, &error);
|
|
if (!ast) {
|
|
fprintf(stderr, "语法错误: %s:%d:%d: %s\n",
|
|
error.filename, error.line, error.col, error.message);
|
|
free(source); arena_destroy(&arena);
|
|
return 1;
|
|
}
|
|
|
|
// 5. 语义分析
|
|
sema_analyze(ast, &error_list, &arena);
|
|
if (error_list.count > 0) {
|
|
error_print(&error_list);
|
|
free(source); arena_destroy(&arena);
|
|
return 1;
|
|
}
|
|
|
|
// 6. LLVM IR 生成
|
|
const char* codegen_error = NULL;
|
|
LLVMModuleRef module = codegen_module(ast, &arena, "l_module", &codegen_error);
|
|
if (!module) {
|
|
fprintf(stderr, "IR 生成错误: %s\n", codegen_error);
|
|
free(source); arena_destroy(&arena);
|
|
return 1;
|
|
}
|
|
|
|
if (emit_ir) {
|
|
// 输出 LLVM IR 文本
|
|
char* ir = LLVMPrintModuleToString(module);
|
|
char ir_path[512];
|
|
snprintf(ir_path, sizeof(ir_path), "%s.ll", input);
|
|
write_file(ir_path, ir);
|
|
printf("IR 已输出到: %s\n", ir_path);
|
|
LLVMDisposeMessage(ir);
|
|
} else {
|
|
// 初始化 X86 目标(LLVM-C.lib 中没有 InitializeAll 系列符号)
|
|
LLVMInitializeX86TargetInfo();
|
|
LLVMInitializeX86Target();
|
|
LLVMInitializeX86TargetMC();
|
|
LLVMInitializeX86AsmPrinter();
|
|
LLVMInitializeX86AsmParser();
|
|
|
|
char* triple = LLVMGetDefaultTargetTriple();
|
|
LLVMTargetRef target;
|
|
char* target_error = NULL;
|
|
if (LLVMGetTargetFromTriple(triple, &target, &target_error)) {
|
|
fprintf(stderr, "目标平台错误: %s\n", target_error);
|
|
LLVMDisposeMessage(target_error); LLVMDisposeMessage(triple);
|
|
free(source); arena_destroy(&arena); LLVMDisposeModule(module);
|
|
return 1;
|
|
}
|
|
|
|
LLVMTargetMachineRef tm = LLVMCreateTargetMachine(
|
|
target, triple, "generic", "",
|
|
LLVMCodeGenLevelDefault, LLVMRelocDefault,
|
|
LLVMCodeModelDefault);
|
|
LLVMDisposeMessage(triple);
|
|
|
|
// 输出目标文件
|
|
char obj_path[512];
|
|
snprintf(obj_path, sizeof(obj_path), "%s.o", input);
|
|
char* obj_error = NULL;
|
|
if (LLVMTargetMachineEmitToFile(tm, module, obj_path,
|
|
LLVMObjectFile, &obj_error)) {
|
|
fprintf(stderr, "目标代码生成错误: %s\n", obj_error);
|
|
LLVMDisposeMessage(obj_error);
|
|
free(source); arena_destroy(&arena);
|
|
LLVMDisposeTargetMachine(tm); LLVMDisposeModule(module);
|
|
return 1;
|
|
}
|
|
|
|
// 调用 gcc 链接(MinGW 环境可用)
|
|
char cmd[1024];
|
|
snprintf(cmd, sizeof(cmd),
|
|
"gcc \"%s\" -o \"%s\"",
|
|
obj_path, output);
|
|
int ret = system(cmd);
|
|
if (ret != 0) {
|
|
fprintf(stderr, "链接失败 (exit code %d)\n", ret);
|
|
} else {
|
|
printf("编译成功: %s\n", output);
|
|
}
|
|
LLVMDisposeTargetMachine(tm);
|
|
}
|
|
|
|
// 清理
|
|
LLVMDisposeModule(module);
|
|
free(source);
|
|
arena_destroy(&arena);
|
|
return 0;
|
|
}
|