chore(messages): TransPyV 运行时中文字符串英文化

把 TransPyV/App 下 57 个文件的运行时中文字符串(编译器报错/告警/日志、CLI 帮助与横幅、REPL 提示、Phase1/Phase2 流程日志、Codegen 各 Mixin 的类型与调用报错、Stub/Vpi 诊断等)共 1091 行译为英文。

口径:只改字符串字面量内部文字,行数零变化(+1092/-1092);printf 格式符与转义序列多重集合校验不变(_strcheck 异常 0);译文不引入撇号以免干扰单引号字面量解析。验证:三重重编 SUCCESS(136.20s)+ 全量 77/77 顺序回归通过 + VET 编译 SUCCESS / --search-paths 7 条 exit 0;新产物 help/日志输出已为英文。
This commit is contained in:
TermiNexus committed 2026-10-07 00:03:58 +08:00
1 parent 0cf80632c1
commit 7a9bc5ae75
57 files changed
+1092 -1092

No files matched your search

+83 -83
View File
@@ -85,7 +85,7 @@ def _append_traceback_dbghelp(linker_flags: str) -> str:
need: sizet = string.strlen(linker_flags) + 16
buf: str = _mbuddy.alloc(need)
if buf is None:
VLogger.error("--traceback: -ldbghelp 链接参数缓冲分配失败,终止编译", "TRACEBACK")
VLogger.error("--traceback: failed to allocate -ldbghelp linker argument buffer, aborting compilation", "TRACEBACK")
return linker_flags
viperlib.snprintf(buf, need, "%s -ldbghelp", linker_flags)
return buf
@@ -105,11 +105,11 @@ def _tb_register_vpcall(path: str) -> int:
if _tb_vpcall_buf is None:
_tb_vpcall_buf = stdlib.malloc(sizet(TB_VPCALL_MAX) * 8)
if _tb_vpcall_buf is None:
VLogger.error("--traceback: .vpcall 路径表分配失败,终止编译", "TRACEBACK")
VLogger.error("--traceback: failed to allocate .vpcall path table, aborting compilation", "TRACEBACK")
return 1
_tb_vpcall_n = 0
if _tb_vpcall_n >= TB_VPCALL_MAX:
VLogger.error("--traceback: .vpcall 路径表溢出(模块数超上限),终止编译", "TRACEBACK")
VLogger.error("--traceback: .vpcall path table overflow (module count exceeds limit), aborting compilation", "TRACEBACK")
return 1
slot: str* = (str*)(uint64(_tb_vpcall_buf) + sizet(_tb_vpcall_n) * 8)
slot[0] = path
@@ -122,11 +122,11 @@ def WriteCallSidecar(temp_dir: str, sha1: str, text: str) -> int:
if temp_dir is None or sha1 is None or text is None: return 0
vpath: str = StubMergerCollect._sliced_path(temp_dir, string.strlen(temp_dir), sha1, "vpcall")
if vpath is None:
VLogger.error(f"--traceback: .vpcall 路径构造失败(sha1={sha1}),终止编译", "TRACEBACK")
VLogger.error(f"--traceback: failed to build .vpcall path (sha1={sha1}), aborting compilation", "TRACEBACK")
return 1
vf: fileio.File* = fileio.File(vpath, fileio.MODE.W)
if vf.closed:
VLogger.error(f"--traceback: 写出 .vpcall 失败(无法打开 {vpath}),终止编译", "TRACEBACK")
VLogger.error(f"--traceback: failed to write .vpcall (cannot open {vpath}), aborting compilation", "TRACEBACK")
stdlib.free(vpath)
return 1
vf.write(text, string.strlen(text))
@@ -154,7 +154,7 @@ def TranslateFileGetTrans(mb: memhub.MemBuddy*, file_path: str, sha1_val: str, c
# read_all's capacity includes the terminator (reads cap-1 bytes max) and silently
# truncates longer files; reject by size first, else parsing gets confusing syntax errors.
if f.size() >= SRC_BUF_SIZE:
VLogger.error(f"源文件超过 {SRC_BUF_SIZE} 字节上限, 拒绝读取: {file_path}", "fileio")
VLogger.error(f"source file exceeds {SRC_BUF_SIZE} byte limit, refusing to read: {file_path}", "fileio")
f.close()
return None
src_buf: bytes = stdlib.malloc(SRC_BUF_SIZE)
@@ -179,7 +179,7 @@ def TranslateFileGetTrans(mb: memhub.MemBuddy*, file_path: str, sha1_val: str, c
GenApi.dump_parse_traceback(file_path)
err_line: int = ast.get_parse_err_line()
err_col: int = ast.get_parse_err_col()
VLogger.error(f"解析失败: {file_path} (line {err_line} col {err_col})", "PARSE")
VLogger.error(f"parse failed: {file_path} (line {err_line} col {err_col})", "PARSE")
return None
# Generate the .vpi stub from the AST before tr.translate (same as Phase1); it lets
# other modules query function signatures and CTypedef definitions.
@@ -247,7 +247,7 @@ def TranslateFileGetTrans(mb: memhub.MemBuddy*, file_path: str, sha1_val: str, c
if gen is not None: gen.ExprGenCls.extract_module_docstrings(tree, sha1_val)
ret: int = tr.translate(tree)
if ret != 0:
VLogger.error(f"文件翻译失败: {file_path}", "Translate")
VLogger.error(f"file translation failed: {file_path}", "Translate")
return None
# --traceback: for includes/traceback.vp, inject the startup Install() ctor into its IR.
# The injector lives in the Gen layer, so no new cross-module mlirlite dependency.
@@ -385,11 +385,11 @@ def compile_ll_to_obj(ir_path: str, output_dir: str, module_name: str, cc_cmd: s
else:
mo_rc: int = _run_cmd_wait(cmd_mo)
if mo_rc != 0:
VLogger.error(f"mlir-opt 失败 (module={module_name}, exit={mo_rc}, cmd={cmd_mo})", "MLIR")
VLogger.error(f"mlir-opt failed (module={module_name}, exit={mo_rc}, cmd={cmd_mo})", "MLIR")
return 1
mt_rc: int = _run_cmd_wait(cmd_mt)
if mt_rc != 0:
VLogger.error(f"mlir-translate 失败 (module={module_name}, exit={mt_rc}, cmd={cmd_mt})", "MLIR")
VLogger.error(f"mlir-translate failed (module={module_name}, exit={mt_rc}, cmd={cmd_mt})", "MLIR")
return 1
compile_input = ll_local_path
@@ -422,7 +422,7 @@ def compile_ll_to_obj(ir_path: str, output_dir: str, module_name: str, cc_cmd: s
viperlib.snprintf(opt_cmd, opt_cmd_len, "opt -passes=forceattrs,asan -force-attribute=sanitize_address -asan-globals=false -o %s %s", asan_path, compile_input)
opt_ret: int = stdlib.system(opt_cmd)
if opt_ret != 0:
VLogger.error(f"opt ASan 插桩失败 (module={module_name})", "ASAN")
VLogger.error(f"opt ASan instrumentation failed (module={module_name})", "ASAN")
return 1
compile_input = asan_path
@@ -437,7 +437,7 @@ def compile_ll_to_obj(ir_path: str, output_dir: str, module_name: str, cc_cmd: s
# inherits the console. On failure, log module sha1 + exit code + full command.
sys_ret: int = _run_cmd_wait(cmd)
if sys_ret != 0:
VLogger.error(f"编译失败 (module={module_name}, exit={sys_ret}, cmd={cmd})", "LLC")
VLogger.error(f"compilation failed (module={module_name}, exit={sys_ret}, cmd={cmd})", "LLC")
return 1
return 0
@@ -545,7 +545,7 @@ def _phase2_write_cmd(cmd: str) -> None:
if _phase2_cmd_path is None or cmd is None: return
f: fileio.File* = fileio.File(_phase2_cmd_path, fileio.MODE.A)
if f.closed:
VLogger.error("phase2command: bat 打开失败", "phase2cmd")
VLogger.error("phase2command: failed to open bat", "phase2cmd")
return
if _phase2_bat_init == 0:
_phase2_bat_init = 1
@@ -584,7 +584,7 @@ def _run_cmd_wait(cmd: str) -> int:
# it). Use warning, not error (error would sys.exit in a worker thread).
if ok == 0:
cp_err: uint32 = w32.win32base.GetLastError()
VLogger.warning(f"CreateProcessA 失败 (GetLastError={cp_err}) 外部命令未启动: cmd={cmd}", "LLC")
VLogger.warning(f"CreateProcessA failed (GetLastError={cp_err}) external command not started: cmd={cmd}", "LLC")
stdlib.free(cmd_line)
if ok == 0: return -1
win32sync.WaitForSingleObject(pi.hProcess, w32.win32base.INFINITE)
@@ -644,14 +644,14 @@ def run_llc_parallel(tasks: bytes, task_count: int, max_threads: int) -> int:
if tsk_p.MlirTranslateCmd is not None: _phase2_write_cmd(tsk_p.MlirTranslateCmd)
if tsk_p.OptCmd is not None: _phase2_write_cmd(tsk_p.OptCmd)
if tsk_p.LlcCmd is not None: _phase2_write_cmd(tsk_p.LlcCmd)
VLogger.info(f"phase2command 模式:{task_count} 条 llc 命令已写入 {_phase2_cmd_path}", "phase2cmd")
VLogger.info(f"phase2command mode: {task_count} llc commands written to {_phase2_cmd_path}", "phase2cmd")
return 0
if max_threads <= 0: max_threads = 1
# Thread-handle array: list[str] (str=bytes=i8*, explicit elem_size=8) stores handles;
# list[t.CPtr] append crashes reading page zero. Grab the handle right after start().
handles: list[str]* = list[str](_mbuddy, 8)
if handles is None:
VLogger.error("run_llc_parallel: 线程句柄数组分配失败", "PhaseB")
VLogger.error("run_llc_parallel: failed to allocate thread handle array", "PhaseB")
return 1
slot_size: sizet = LlcTask.__sizeof__()
ret: int = 0
@@ -670,7 +670,7 @@ def run_llc_parallel(tasks: bytes, task_count: int, max_threads: int) -> int:
for j in range(batch_n):
h_j: t.CPtr = t.CPtr(handles.get(batch_start + j))
wr: uint32 = win32sync.WaitForSingleObject(h_j, 600000)
if wr != 0: VLogger.error("llc 线程 join 超时/失败(600 秒未完成)", "PhaseB")
if wr != 0: VLogger.error("llc thread join timeout/failure (not finished within 600 seconds)", "PhaseB")
w32.win32base.CloseHandle(h_j)
# check this batch's results
for j in range(batch_n):
@@ -679,21 +679,21 @@ def run_llc_parallel(tasks: bytes, task_count: int, max_threads: int) -> int:
# Failure: use FailStage to pinpoint the step and print that step's command.
# Previously everything printed the LlcCmd as "llc failed", misdirecting MLIR failures.
st: int = tsk2.FailStage
stage_name: str = "未知阶段(worker 未跑完)"
stage_name: str = "unknown stage (worker did not finish)"
stage_cmd: bytes = tsk2.LlcCmd
if st == LLC_STAGE_MLIR_OPT:
stage_name = "mlir-opt 校验"
stage_name = "mlir-opt validation"
stage_cmd = tsk2.MlirOptCmd
elif st == LLC_STAGE_MLIR_TRANSLATE:
stage_name = "mlir-translate 降级"
stage_name = "mlir-translate lowering"
stage_cmd = tsk2.MlirTranslateCmd
elif st == LLC_STAGE_ASAN_OPT:
stage_name = "opt ASan 插桩"
stage_name = "opt ASan instrumentation"
stage_cmd = tsk2.OptCmd
elif st == LLC_STAGE_LLC:
stage_name = "llc 编译"
stage_name = "llc compile"
stage_cmd = tsk2.LlcCmd
VLogger.error(f"{stage_name}失败: {(tsk2.ModuleSha1 if tsk2.ModuleSha1 is not None else "?")} (exit={tsk2.ExitCode} cmd={(stage_cmd if stage_cmd is not None else "?")})", "LLC")
VLogger.error(f"{stage_name} failed: {(tsk2.ModuleSha1 if tsk2.ModuleSha1 is not None else "?")} (exit={tsk2.ExitCode} cmd={(stage_cmd if stage_cmd is not None else "?")})", "LLC")
ret = 1
return ret
@@ -702,20 +702,20 @@ def _CollectObjFilesImpl(base_dir: str, bd_len: sizet, out_buf: bytes, out_pos:
"""Recursively scan base_dir, collect .obj paths into out_buf, return the new out_pos."""
find_data: w32.win32file.WIN32_FIND_DATAA* = _mbuddy.alloc(w32.win32file.WIN32_FIND_DATAA.__sizeof__())
if find_data is None:
VLogger.warning("_CollectObjFilesImpl 分配 find_data 失败,少收集 .obj", "collect")
VLogger.warning("_CollectObjFilesImpl failed to allocate find_data, fewer .obj collected", "collect")
return out_pos
string.memset(find_data, 0, w32.win32file.WIN32_FIND_DATAA.__sizeof__())
pattern: bytes = _mbuddy.alloc(bd_len + 8)
if pattern is None:
VLogger.warning("_CollectObjFilesImpl 分配 pattern 失败,少收集 .obj", "collect")
VLogger.warning("_CollectObjFilesImpl failed to allocate pattern, fewer .obj collected", "collect")
return out_pos
viperlib.snprintf(pattern, bd_len + 8, "%s/*", base_dir)
handle: w32.win32base.HANDLE = w32.win32file.FindFirstFileA(pattern, find_data)
_mbuddy.free(pattern)
if handle == w32.win32base.INVALID_HANDLE_VALUE:
VLogger.warning("_CollectObjFilesImpl 无法打开目录,少收集 .obj", "collect")
VLogger.warning("_CollectObjFilesImpl cannot open directory, fewer .obj collected", "collect")
return out_pos
while True:
@@ -774,10 +774,10 @@ def _append_host_export_implib(linker_flags: str, implib_path: str) -> str:
extra: sizet = string.strlen(" -Wl,--out-implib=") + string.strlen(implib_path) + 1
buf: str = _mbuddy.alloc(string.strlen(linker_flags) + extra)
if buf is None:
VLogger.warning("宿主导出:import lib 参数缓冲分配失败(片段将无法导入宿主符号)", "link")
VLogger.warning("host export: failed to allocate import lib argument buffer (fragments cannot import host symbols)", "link")
return linker_flags
viperlib.snprintf(buf, string.strlen(linker_flags) + extra, "%s -Wl,--out-implib=%s", linker_flags, implib_path)
VLogger.info(f"宿主导出:产出 import lib {implib_path}", "link")
VLogger.info(f"host export: produced import lib {implib_path}", "link")
return buf
@@ -808,8 +808,8 @@ def link_obj_to_exe(output_dir: str, module_name: str, linker_cmd: str, linker_f
extra_len: sizet = 0
if includes_binary_dir is not None:
extra_len = collect_obj_files(includes_binary_dir, extra_objs, EXTRA_BUF_SIZE)
if extra_len > 0: VLogger.info(f"附加 {extra_len} 字节的 includes.binary .obj 文件", "link")
else: VLogger.warning(f"includes.binary 无 .obj 文件: {includes_binary_dir}", "link")
if extra_len > 0: VLogger.info(f"appended {extra_len} bytes of includes.binary .obj files", "link")
else: VLogger.warning(f"includes.binary has no .obj files: {includes_binary_dir}", "link")
# Command: clang++ {obj_path} extra_objs [libs] -o {...}/{linker_output} linker_flags. Objects
# and explicit libs must precede -l flags. The ASan runtime is declared in project.vpj, not hardcoded.
@@ -846,15 +846,15 @@ def link_obj_to_exe(output_dir: str, module_name: str, linker_cmd: str, linker_f
result: subprocess.CompletedProcess* = subprocess.run(cmd, True, True)
if result is None:
VLogger.error("subprocess.run 返回 None", "link")
VLogger.error("subprocess.run returned None", "link")
return 1
if result.returncode != 0:
VLogger.error(f"链接失败,返回码: {result.returncode}", "link")
VLogger.error(f"命令: {cmd}", "link")
VLogger.error(f"link failed, return code: {result.returncode}", "link")
VLogger.error(f"command: {cmd}", "link")
# Show linker error output.
# Note: subprocess merges stderr into stdout on Windows, so result.stderr is always None.
if result.stdout is not None: VLogger.error(result.stdout, "link")
else: VLogger.error("无输出捕获", "link")
else: VLogger.error("no output captured", "link")
if result.stderr is not None: VLogger.error(result.stderr, "link")
return 1
return 0
@@ -879,12 +879,12 @@ def asm_to_obj(asm_path: str, sha1_val: str, output_dir: str, triple: str) -> in
result: subprocess.CompletedProcess* = subprocess.run(cmd, True, True)
if result is None:
VLogger.error("llvm-mc subprocess.run 返回 None", "asm")
VLogger.error("llvm-mc subprocess.run returned None", "asm")
return 1
if result.returncode != 0:
if result.stdout is not None: stdio.printf("%s\n", result.stdout)
if result.stderr is not None: stdio.printf("%s\n", result.stderr)
VLogger.error(f"llvm-mc 汇编失败,返回码: {result.returncode}, 命令: {cmd}", "asm")
VLogger.error(f"llvm-mc assembly failed, return code: {result.returncode}, command: {cmd}", "asm")
return 1
return 0
@@ -896,7 +896,7 @@ def compile_module_to_obj(ir_buf: bytes, ir_len: sizet, temp_dir: str, output_di
# Step 1: write the .ll file
ret: int = write_ir_to_file(ir_buf, ir_len, temp_dir, module_name)
if ret != 0:
VLogger.error(f"写 .ll 失败: {module_name}", "compile")
VLogger.error(f"failed to write .ll: {module_name}", "compile")
return 1
# Step 2: build the sliced .ll path and compile -> .obj
@@ -905,7 +905,7 @@ def compile_module_to_obj(ir_buf: bytes, ir_len: sizet, temp_dir: str, output_di
ret = compile_ll_to_obj(ir_path, output_dir, module_name, cc_cmd, cc_flags)
if ret != 0:
VLogger.error(f"llc 编译失败: {module_name}", "compile")
VLogger.error(f"llc compilation failed: {module_name}", "compile")
return 1
return 0
@@ -1008,15 +1008,15 @@ def link_objs_to_exe(obj_paths: str, obj_paths_len: sizet, linker_cmd: str, link
return 0
result: subprocess.CompletedProcess* = subprocess.run(cmd, True, True)
if result is None:
VLogger.error("subprocess.run 返回 None", "link")
VLogger.error("subprocess.run returned None", "link")
return 1
if result.returncode != 0:
# First print clang++'s full error output (incl. undefined reference) with stdio.printf;
# it must precede VLogger.error, which calls sys.exit(1) and terminates the process.
if result.stdout is not None: stdio.printf("=== clang++ 链接错误输出 ===\n%s\n", result.stdout)
if result.stdout is not None: stdio.printf("=== clang++ link error output ===\n%s\n", result.stdout)
if result.stderr is not None:
stdio.printf("=== clang++ stderr ===\n%s\n", result.stderr)
VLogger.error(f"链接失败,返回码: {result.returncode}, 命令: {cmd}", "link")
VLogger.error(f"link failed, return code: {result.returncode}, command: {cmd}", "link")
return 1
return 0
@@ -1347,7 +1347,7 @@ def _tb_emit_row(mb: memhub.MemBuddy*, d: TbDump*, addr: uint64, path: str, line
ncap: int = d.RowCap * 2
nb: bytes = _tb_grow_rec(mb, d.RowBuf, d.RowCap, TB_ROW_SIZE, ncap)
if nb is None:
VLogger.error("--traceback: DWARF 行记录表扩容失败,终止编译", "TRACEBACK")
VLogger.error("--traceback: failed to grow DWARF line record table, aborting compilation", "TRACEBACK")
return 1
d.RowBuf = nb
d.RowCap = ncap
@@ -1369,7 +1369,7 @@ def _tb_emit_func(mb: memhub.MemBuddy*, d: TbDump*, low: uint64, high: uint64, n
ncap: int = d.FuncCap * 2
nb: bytes = _tb_grow_rec(mb, d.FuncBuf, d.FuncCap, TB_FUNC_SIZE, ncap)
if nb is None:
VLogger.error("--traceback: DWARF 子程序表扩容失败,终止编译", "TRACEBACK")
VLogger.error("--traceback: failed to grow DWARF subprogram table, aborting compilation", "TRACEBACK")
return 1
d.FuncBuf = nb
d.FuncCap = ncap
@@ -1478,7 +1478,7 @@ def _tb_parse_dump(mb: memhub.MemBuddy*, dump: str) -> TbDump*:
files: bytes = mb.alloc(sizet(TB_FILE_CAP) * TB_FILE_SIZE)
linebuf: bytes = mb.alloc(LINE_CAP)
if d.RowBuf is None or d.FuncBuf is None or dirs is None or files is None or linebuf is None:
VLogger.error("--traceback: dwarfdump 解析缓冲分配失败,终止编译", "TRACEBACK")
VLogger.error("--traceback: failed to allocate dwarfdump parse buffer, aborting compilation", "TRACEBACK")
return None
in_line: int = 0
@@ -1778,7 +1778,7 @@ def _tb_cp_add_cr(mb: memhub.MemBuddy*, cp: TbCallParse*, caller: str, callee: s
ncap: int = cp.CrCap * 2
nb: bytes = _tb_grow_rec(mb, cp.Cr, cp.CrCap, TB_CR_SIZE, ncap)
if nb is None:
VLogger.error("--traceback: 调用记录表扩容失败,终止编译", "TRACEBACK")
VLogger.error("--traceback: failed to grow call record table, aborting compilation", "TRACEBACK")
return 1
cp.Cr = nb
cp.CrCap = ncap
@@ -1797,7 +1797,7 @@ def _tb_cp_add_fr(mb: memhub.MemBuddy*, cp: TbCallParse*, sym: str, file: str, d
ncap: int = cp.FrCap * 2
nb: bytes = _tb_grow_rec(mb, cp.Fr, cp.FrCap, TB_FR_SIZE, ncap)
if nb is None:
VLogger.error("--traceback: 函数记录表扩容失败,终止编译", "TRACEBACK")
VLogger.error("--traceback: failed to grow function record table, aborting compilation", "TRACEBACK")
return 1
cp.Fr = nb
cp.FrCap = ncap
@@ -1877,7 +1877,7 @@ def _tb_parse_c_line(mb: memhub.MemBuddy*, s: str, a: sizet, b: sizet, cp: TbCal
def _tb_parse_vpcall_file(mb: memhub.MemBuddy*, path: str, cp: TbCallParse*) -> int:
whole: str = _tb_read_whole(mb, path)
if whole is None:
VLogger.error(f"--traceback: 读取 .vpcall 失败:{path},终止编译", "TRACEBACK")
VLogger.error(f"--traceback: failed to read .vpcall: {path}, aborting compilation", "TRACEBACK")
return 1
n: sizet = string.strlen(whole)
pos: sizet = 0
@@ -1912,7 +1912,7 @@ def _tb_build_groups(mb: memhub.MemBuddy*, cp: TbCallParse*) -> int:
cp.GrpCap = _tb_next_pow2(cp.CrN * 2 + 16)
cp.Grp = mb.alloc(sizet(cp.GrpCap) * TB_GRP_SIZE)
if cp.Grp is None:
VLogger.error("--traceback: 调用点分组表分配失败,终止编译", "TRACEBACK")
VLogger.error("--traceback: failed to allocate call-site group table, aborting compilation", "TRACEBACK")
return 1
string.memset(cp.Grp, 0, sizet(cp.GrpCap) * TB_GRP_SIZE)
for i in range(cp.CrN):
@@ -1921,7 +1921,7 @@ def _tb_build_groups(mb: memhub.MemBuddy*, cp: TbCallParse*) -> int:
callee: str = rawptr(_tb_rd_u64(cp.Cr, off + TB_CR_OFF_CALLEE))
slot: int = _tb_grp_get(cp.Grp, cp.GrpCap, caller, callee, 1)
if slot < 0:
VLogger.error("--traceback: 调用点分组哈希表溢出,终止编译", "TRACEBACK")
VLogger.error("--traceback: call-site group hash table overflow, aborting compilation", "TRACEBACK")
return 1
goff: sizet = sizet(slot) * TB_GRP_SIZE
tailv: uint64 = uint64(_tb_rd_u32(cp.Grp, goff + TB_GRP_OFF_TAIL))
@@ -1981,18 +1981,18 @@ def _tb_run_objdump(mb: memhub.MemBuddy*, exe_path: str) -> str:
cl: sizet = string.strlen(exe_path) + 64
cmd: bytes = mb.alloc(cl)
if cmd is None:
VLogger.error("--traceback: objdump 命令缓冲分配失败,终止编译", "TRACEBACK")
VLogger.error("--traceback: failed to allocate objdump command buffer, aborting compilation", "TRACEBACK")
return None
viperlib.snprintf(cmd, cl, "llvm-objdump -d \"%s\"", exe_path)
res: subprocess.CompletedProcess* = subprocess.run(cmd, True, True)
if res is None or res.returncode != 0:
rc: int = -1
if res is not None: rc = res.returncode
VLogger.error(f"--traceback: 调用 llvm-objdump 失败(返回码 {rc};请确认 PATH 内含 LLVM 工具),终止编译", "TRACEBACK")
VLogger.error(f"--traceback: failed to run llvm-objdump (return code {rc}; ensure LLVM tools are in PATH), aborting compilation", "TRACEBACK")
return None
dump: str = res.stdout
if dump is None:
VLogger.error("--traceback: llvm-objdump 无输出,终止编译", "TRACEBACK")
VLogger.error("--traceback: llvm-objdump produced no output, aborting compilation", "TRACEBACK")
return None
return dump
@@ -2087,16 +2087,16 @@ def _tb_scan_objdump(mb: memhub.MemBuddy*, dump: str, img_lo: uint64, d: TbDump*
def WriteStackMap(mb: memhub.MemBuddy*, exe_path: str) -> int:
"""--traceback: export <exe>.stackmap after a successful link. 0 = ok."""
if mb is None or exe_path is None:
VLogger.error("--traceback: WriteStackMap 参数为空,终止编译", "TRACEBACK")
VLogger.error("--traceback: WriteStackMap argument is null, aborting compilation", "TRACEBACK")
return 1
lay: TbPeLayout* = _tb_read_pe_layout(mb, exe_path)
if lay is None or lay.Ok == 0:
VLogger.error(f"--traceback: 只支持 Windows PE 产物,但 {exe_path} 不是有效 PE(这是 --traceback 的要求)", "TRACEBACK")
VLogger.error(f"--traceback: only Windows PE output is supported, but {exe_path} is not a valid PE (required by --traceback)", "TRACEBACK")
return 1
size_of_image: uint64 = lay.SizeOfImage
if size_of_image == 0:
VLogger.error(f"--traceback: 读到的 PE.SizeOfImage 非正({size_of_image}),无法界定 RVA 域(这是 --traceback 的要求)", "TRACEBACK")
VLogger.error(f"--traceback: read PE.SizeOfImage is not positive ({size_of_image}), cannot bound RVA range (required by --traceback)", "TRACEBACK")
return 1
img_lo: uint64 = lay.ImageBase
img_hi: uint64 = lay.ImageBase + size_of_image
@@ -2106,18 +2106,18 @@ def WriteStackMap(mb: memhub.MemBuddy*, exe_path: str) -> int:
cmd_len: sizet = string.strlen(exe_path) + 64
cmd: bytes = mb.alloc(cmd_len)
if cmd is None:
VLogger.error("--traceback: dwarfdump 命令缓冲分配失败,终止编译", "TRACEBACK")
VLogger.error("--traceback: failed to allocate dwarfdump command buffer, aborting compilation", "TRACEBACK")
return 1
viperlib.snprintf(cmd, cmd_len, "llvm-dwarfdump --debug-line --debug-info \"%s\"", exe_path)
res: subprocess.CompletedProcess* = subprocess.run(cmd, True, True)
if res is None or res.returncode != 0:
rc: int = -1
if res is not None: rc = res.returncode
VLogger.error(f"--traceback: 调用 llvm-dwarfdump 失败(返回码 {rc};请确认 PATH 内含 LLVM 工具),终止编译", "TRACEBACK")
VLogger.error(f"--traceback: failed to run llvm-dwarfdump (return code {rc}; ensure LLVM tools are in PATH), aborting compilation", "TRACEBACK")
return 1
dump: str = res.stdout
if dump is None:
VLogger.error("--traceback: llvm-dwarfdump 无输出,终止编译", "TRACEBACK")
VLogger.error("--traceback: llvm-dwarfdump produced no output, aborting compilation", "TRACEBACK")
return 1
d: TbDump* = _tb_parse_dump(mb, dump)
@@ -2128,7 +2128,7 @@ def WriteStackMap(mb: memhub.MemBuddy*, exe_path: str) -> int:
if _tb_vpcall_n > 0:
cp: TbCallParse* = _tb_cp_init(mb)
if cp is None:
VLogger.error("--traceback: 调用点解析状态分配失败,终止编译", "TRACEBACK")
VLogger.error("--traceback: failed to allocate call-site parse state, aborting compilation", "TRACEBACK")
return 1
if _tb_load_vpcalls(mb, cp) != 0: return 1
if _tb_build_groups(mb, cp) != 0: return 1
@@ -2137,12 +2137,12 @@ def WriteStackMap(mb: memhub.MemBuddy*, exe_path: str) -> int:
our_before: int = d.RowCount
if _tb_scan_objdump(mb, odump, img_lo, d, cp) != 0: return 1
our_added: int = d.RowCount - our_before
if our_added > 0: VLogger.success(f"--traceback: 从 {_tb_vpcall_n} 个 .vpcall 生成 {our_added} 条我方调用点记录(F={cp.FrN} 函数, C={cp.CrN} 调用)", "TRACEBACK")
else: VLogger.warning(f"--traceback: 读到 {_tb_vpcall_n} 个 .vpcall(F={cp.FrN}, C={cp.CrN})但未匹配到任何 objdump 调用点——我方帧行号仍缺,退化到 DWARF", "TRACEBACK")
else: VLogger.warning("--traceback: 未登记任何 .vpcall(我方模块无调用点记录),退化到 DWARF 行为", "TRACEBACK")
if our_added > 0: VLogger.success(f"--traceback: from {_tb_vpcall_n} .vpcall files generated {our_added} own call-site records (F={cp.FrN} functions, C={cp.CrN} calls)", "TRACEBACK")
else: VLogger.warning(f"--traceback: read {_tb_vpcall_n} .vpcall files (F={cp.FrN}, C={cp.CrN}) but matched no objdump call sites -- own frame line numbers still missing, falling back to DWARF", "TRACEBACK")
else: VLogger.warning("--traceback: no .vpcall registered (own modules have no call-site records), falling back to DWARF behavior", "TRACEBACK")
if d.RawRowCount == 0:
VLogger.error(f"--traceback: 未能从 {exe_path} 解析出任何行记录(DWARF .debug_line 为空且无我方 .vpcall 记录,这是 --traceback 的要求)", "TRACEBACK")
VLogger.error(f"--traceback: could not parse any line records from {exe_path} (DWARF .debug_line is empty and there are no own .vpcall records; required by --traceback)", "TRACEBACK")
return 1
# ---- 2) rows: normalise to RVA, same rva keeps the largest line ----
@@ -2166,7 +2166,7 @@ def WriteStackMap(mb: memhub.MemBuddy*, exe_path: str) -> int:
i += 1
tmp: bytes = mb.alloc(TB_ROW_SIZE)
if tmp is None:
VLogger.error("--traceback: 行记录排序缓冲分配失败,终止编译", "TRACEBACK")
VLogger.error("--traceback: failed to allocate line record sort buffer, aborting compilation", "TRACEBACK")
return 1
si2: int = 1
while si2 < kept:
@@ -2200,9 +2200,9 @@ def WriteStackMap(mb: memhub.MemBuddy*, exe_path: str) -> int:
_tb_wr_u64(d.RowBuf, sizet(uniq) * TB_ROW_SIZE + TB_ROW_OFF_FUNC, bf)
uniq += 1
i = j
if dropped_rows > 0: VLogger.warning(f"--traceback: 丢弃 {dropped_rows} 条无法归一化的 DWARF 行记录(地址不在影像 VA 区间,多为 COMDAT 去重副本)", "TRACEBACK")
if dropped_rows > 0: VLogger.warning(f"--traceback: dropped {dropped_rows} DWARF line records that cannot be normalised (address not in image VA range, mostly COMDAT dedup copies)", "TRACEBACK")
if uniq == 0:
VLogger.error(f"--traceback: 从 {exe_path} 得到的行记录全部无法归一到运行期 RVA 域,终止编译", "TRACEBACK")
VLogger.error(f"--traceback: all line records from {exe_path} cannot be normalised to runtime RVA range, aborting compilation", "TRACEBACK")
return 1
# ---- 3) subprograms: normalise + sort by low_rva ----
@@ -2228,7 +2228,7 @@ def WriteStackMap(mb: memhub.MemBuddy*, exe_path: str) -> int:
fi += 1
ftmp: bytes = mb.alloc(TB_FUNC_SIZE)
if ftmp is None:
VLogger.error("--traceback: 子程序排序缓冲分配失败,终止编译", "TRACEBACK")
VLogger.error("--traceback: failed to allocate subprogram sort buffer, aborting compilation", "TRACEBACK")
return 1
fj: int = 1
while fj < fkept:
@@ -2240,7 +2240,7 @@ def WriteStackMap(mb: memhub.MemBuddy*, exe_path: str) -> int:
fk -= 1
string.memcpy(d.FuncBuf + sizet(fk) * TB_FUNC_SIZE, ftmp, TB_FUNC_SIZE)
fj += 1
if dropped_funcs > 0: VLogger.warning(f"--traceback: 丢弃 {dropped_funcs} 个无法归一化的 DWARF 子程序(low_pc 不在影像 VA 区间)", "TRACEBACK")
if dropped_funcs > 0: VLogger.warning(f"--traceback: dropped {dropped_funcs} DWARF subprograms that cannot be normalised (low_pc not in image VA range)", "TRACEBACK")
# ---- 4) string pool: exact upper bound, then intern (offset 0 = sentinel) ----
pool_need: sizet = 1
@@ -2264,12 +2264,12 @@ def WriteStackMap(mb: memhub.MemBuddy*, exe_path: str) -> int:
i += 1
p: TbPool* = TbPool()
if p is None:
VLogger.error("--traceback: 字符串池对象分配失败,终止编译", "TRACEBACK")
VLogger.error("--traceback: failed to allocate string pool object, aborting compilation", "TRACEBACK")
return 1
p.Pool = mb.alloc(pool_need)
p.Offs = mb.alloc((sizet(uniq) * 2 + sizet(fkept)) * 8 + 64)
if p.Pool is None or p.Offs is None:
VLogger.error("--traceback: 字符串池缓冲分配失败,终止编译", "TRACEBACK")
VLogger.error("--traceback: failed to allocate string pool buffer, aborting compilation", "TRACEBACK")
return 1
p.Pool[0] = '\0'
p.PoolLen = 1
@@ -2278,7 +2278,7 @@ def WriteStackMap(mb: memhub.MemBuddy*, exe_path: str) -> int:
# file_off / func_off per unique row (u32 each)
ro: bytes = mb.alloc(sizet(uniq) * 8 + 8)
if ro is None:
VLogger.error("--traceback: 记录偏移缓冲分配失败,终止编译", "TRACEBACK")
VLogger.error("--traceback: failed to allocate record offset buffer, aborting compilation", "TRACEBACK")
return 1
for i in range(uniq):
pth2: str = rawptr(_tb_rd_u64(d.RowBuf, sizet(i) * TB_ROW_SIZE + TB_ROW_OFF_FILE))
@@ -2301,31 +2301,31 @@ def WriteStackMap(mb: memhub.MemBuddy*, exe_path: str) -> int:
while ci < uniq:
cv: uint64 = _tb_rd_u64(d.RowBuf, sizet(ci) * TB_ROW_SIZE)
if ci > 0 and cv <= prev_rva:
VLogger.error(f"--traceback 自检失败:记录 RVA 非严格升序(第 {ci} 条会破坏运行期二分查找),终止编译", "TRACEBACK")
VLogger.error(f"--traceback self-check failed: record RVAs not strictly ascending (record {ci} would break runtime binary search), aborting compilation", "TRACEBACK")
return 1
prev_rva = cv
if cv >= size_of_image: outside += 1
if cv >= text_rva and cv < size_of_image: plausible += 1
ci += 1
if outside > 0:
VLogger.error(f"--traceback 自检失败:有 {outside} 条记录的 RVA 落在 [0, SizeOfImage=0x{size_of_image:x}) 之外,终止编译", "TRACEBACK")
VLogger.error(f"--traceback self-check failed: {outside} records have RVA outside [0, SizeOfImage=0x{size_of_image:x}), aborting compilation", "TRACEBACK")
return 1
if plausible <= 0:
VLogger.error("--traceback 自检失败:没有任何记录的 RVA 落在运行期可能出现的区间(RVA 口径疑似错误),终止编译", "TRACEBACK")
VLogger.error("--traceback self-check failed: no record RVA falls in the range possible at runtime (RVA basis likely wrong), aborting compilation", "TRACEBACK")
return 1
# file_off must be >= 1: offset 0 is the empty-string sentinel and the runtime
# rejects a whole map whose record file_off is 0.
for ci in range(uniq):
fo: uint64 = uint64(_tb_rd_u32(ro, sizet(ci) * 8))
if fo == 0:
VLogger.error("--traceback 自检失败:存在 file_off==0 的记录(空路径,运行期会拒绝该旁文件),终止编译", "TRACEBACK")
VLogger.error("--traceback self-check failed: a record has file_off==0 (empty path; runtime would reject that sidecar file), aborting compilation", "TRACEBACK")
return 1
# ---- 6) serialise and write <exe>.stackmap ----
total: sizet = 20 + sizet(uniq) * 20 + sizet(p.PoolLen)
out: bytes = mb.alloc(total)
if out is None:
VLogger.error("--traceback: 旁文件输出缓冲分配失败,终止编译", "TRACEBACK")
VLogger.error("--traceback: failed to allocate sidecar output buffer, aborting compilation", "TRACEBACK")
return 1
out[0] = 'T'
out[1] = 'P'
@@ -2348,16 +2348,16 @@ def WriteStackMap(mb: memhub.MemBuddy*, exe_path: str) -> int:
map_len: sizet = string.strlen(exe_path) + 16
map_path: str = mb.alloc(map_len)
if map_path is None:
VLogger.error("--traceback: 旁文件路径缓冲分配失败,终止编译", "TRACEBACK")
VLogger.error("--traceback: failed to allocate sidecar path buffer, aborting compilation", "TRACEBACK")
return 1
viperlib.snprintf(map_path, map_len, "%s.stackmap", exe_path)
mf: fileio.File* = fileio.File(map_path, fileio.MODE.W)
if mf.closed:
VLogger.error(f"--traceback: 写出旁文件失败(无法打开 {map_path}),终止编译", "TRACEBACK")
VLogger.error(f"--traceback: failed to write sidecar file (cannot open {map_path}), aborting compilation", "TRACEBACK")
return 1
mf.write(out, total)
mf.close()
VLogger.success(f"--traceback: 已写出地址→源行旁文件 {map_path}({uniq} 条记录,{total} 字节)", "TRACEBACK")
VLogger.success(f"--traceback: wrote address-to-source-line sidecar {map_path} ({uniq} records, {total} bytes)", "TRACEBACK")
return 0
@@ -2374,7 +2374,7 @@ def run_pipeline(ir_buf: bytes, ir_len: sizet, temp_dir: str, output_dir: str, m
ret: int = write_ir_to_file(ir_buf, ir_len, temp_dir, module_name)
if ret != 0:
result.Success = 0
result.ErrorMsg = "写 .ll 文件失败"
result.ErrorMsg = "failed to write .ll file"
return result
# Step 2: llc-compile .ll -> .obj.
@@ -2382,20 +2382,20 @@ def run_pipeline(ir_buf: bytes, ir_len: sizet, temp_dir: str, output_dir: str, m
ir_path: str = build_sliced_path(temp_dir, module_name, "ll")
if ir_path is None:
result.Success = 0
result.ErrorMsg = "内存分配失败"
result.ErrorMsg = "memory allocation failed"
return result
ret = compile_ll_to_obj(ir_path, output_dir, module_name, cc_cmd, cc_flags)
if ret != 0:
result.Success = 0
result.ErrorMsg = "llc 编译失败"
result.ErrorMsg = "llc compilation failed"
return result
# Step 3: clang++ link .obj -> .exe (plus includes.binary prebuilt .obj and static/dynamic libs)
ret = link_obj_to_exe(output_dir, module_name, linker_cmd, linker_flags, linker_output, includes_binary_dir, linker_static_libs, linker_dynamic_libs)
if ret != 0:
result.Success = 0
result.ErrorMsg = "链接失败"
result.ErrorMsg = "link failed"
return result
result.Success = 1
+53 -53
View File
@@ -247,7 +247,7 @@ def _BuildReachableResult(mb: memhub.MemBuddy*, includes_dir: str) -> IncludesSc
def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: VLogger.Logger*) -> int:
"""Phase1: scan the includes directory and translate/generate stubs on demand"""
if includes_dir is None or temp_dir is None:
VLogger.error("includes_dir 或 temp_dir 为空,无法运行 Phase1", "Phase1")
VLogger.error("includes_dir or temp_dir is empty; cannot run Phase1", "Phase1")
return 1
# Set the global temp_dir (used for cross-module CDefine lookup)
@@ -259,7 +259,7 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: VLogg
# Ensure the temp dir exists (build_dir/temp may not exist yet)
BuildPipeline.ensure_dir(temp_dir)
log.banner("Phase1: 扫描 includes(按需翻译)")
log.banner("Phase1: scanning includes (on-demand translation)")
# Init the AST tables once, before the dependency-graph BFS: if _init_tables first runs
# after the BFS file-level mark, reset_to frees the tables and later tokenize crashes.
@@ -277,30 +277,30 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: VLogg
string.memset(reachable_set, 0, StubMerger.MAX_INCLUDES_SHA1 * 17)
reachable_count = StubMergerBuild._BuildReachableSha1Set(mb, Config.SourceDir, temp_dir, reachable_set, None, includes_dir)
if reachable_count < 0:
VLogger.error("可达集合构建失败(依赖图扫描失败),终止编译", "Phase1")
VLogger.error("failed to build the reachable set (dependency-graph scan failed); aborting compilation", "Phase1")
return 1
if reachable_count <= 0:
VLogger.warning("App 无可达 includes 依赖,跳过 Phase1", "Phase1")
VLogger.warning("App has no reachable includes dependencies; skipping Phase1", "Phase1")
stdlib.free(reachable_set)
reachable_set = None
return 0
use_reachable = 1
else:
VLogger.error("reachable_set 分配失败,终止编译", "Phase1")
VLogger.error("failed to allocate reachable_set; aborting compilation", "Phase1")
return 1
else:
VLogger.warning("SourceDir 不可用,无法构建依赖图,跳过 Phase1", "Phase1")
VLogger.warning("SourceDir unavailable; cannot build the dependency graph; skipping Phase1", "Phase1")
return 0
t_scan1: uint64 = w32.win32base.GetTickCount64()
VLogger.info(f"依赖图扫描总耗时: {t_scan1 - t_scan0} ms(可达 {reachable_count} 个 includes)", "Phase1")
VLogger.info(f"dependency-graph scan total time: {t_scan1 - t_scan0} ms (reachable {reachable_count} includes)", "Phase1")
# Build the reachable FileEntry list from the Sha1Store; 1a/1b-pre/1b iterate result
# (non-empty incdir = includes). use_reachable filtering is idempotent.
result: IncludesScanner.ScanResult* = _BuildReachableResult(mb, includes_dir)
if result is None:
VLogger.error("可达 FileEntry 列表构建失败,终止编译", "Phase1")
VLogger.error("failed to build the reachable FileEntry list; aborting compilation", "Phase1")
return 1
VLogger.info(f"可达 includes 条目: {result.Count}", "Phase1")
VLogger.info(f"reachable includes entries: {result.Count}", "Phase1")
# After scanning, clear _list/_dict injection cycle edges (e.g. _list<->memhub) so the
# graph has none and Phase A converges; deps.txt is rewritten in sync.
@@ -332,7 +332,7 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: VLogg
# dump_stats, so skip dump_stats when self_check returns 4 (cycle).
p1_sc: int = mb.self_check()
if p1_sc != 4: mb.dump_stats("phase1-pre")
else: VLogger.info("[取证] self_check=4(free list 成环),跳过 dump_stats 防死循环", "Phase1")
else: VLogger.info("[EVIDENCE] self_check=4 (free list cycle); skipping dump_stats to prevent an infinite loop", "Phase1")
p1_mo: int = mb.max_order
if p1_mo < 0 or p1_mo > 32:
p1_mo = 32 # Clamp: prevents this probe from out-of-bounds reading free_lists if max_order is corrupted
@@ -341,8 +341,8 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: VLogg
p1_base_addr: uint64 = uint64(mb.base)
p1_usable_addr: uint64 = uint64(mb.usable)
p1_chkbase_addr: uint64 = uint64(mb.chk_base)
VLogger.info(f"[取证] mb={p1_mb_addr} base={p1_base_addr} usable={p1_usable_addr} " f"usable_size={mb.usable_size} max_order={mb.max_order} self_check={p1_sc} " f"detection={mb.detection}", "Phase1")
VLogger.info(f"[取证] chk_base={p1_chkbase_addr} chk_depth={mb.chk_depth} " f"chk_cap={mb.chk_cap} free_lists[clamp={p1_mo}]={p1_fl_head}", "Phase1")
VLogger.info(f"[EVIDENCE] mb={p1_mb_addr} base={p1_base_addr} usable={p1_usable_addr} " f"usable_size={mb.usable_size} max_order={mb.max_order} self_check={p1_sc} " f"detection={mb.detection}", "Phase1")
VLogger.info(f"[EVIDENCE] chk_base={p1_chkbase_addr} chk_depth={mb.chk_depth} " f"chk_cap={mb.chk_cap} free_lists[clamp={p1_mo}]={p1_fl_head}", "Phase1")
p1_sz_cn: sizet = sizet(Phase1.MAX_CLASSES * Phase1.CLASS_NAME_LEN)
p1_sz_ds: sizet = sizet(Phase1.MAX_CLASSES * Phase1.SHA1_LEN)
p1_sz_tp: sizet = sizet(4 * Phase1.MAX_FILE_DEPS)
@@ -353,7 +353,7 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: VLogg
p1_o_bx: int = mb._order_for_size(p1_sz_bx + 16)
p1_fl_cn: uint64 = uint64(mb.free_lists[p1_o_cn])
p1_fl_ds: uint64 = uint64(mb.free_lists[p1_o_ds])
VLogger.info(f"[取证] order(class_names {p1_sz_cn}B+16)={p1_o_cn} fl={p1_fl_cn} " f"order(def_sha1s {p1_sz_ds}B+16)={p1_o_ds} fl={p1_fl_ds} " f"order(topo {p1_sz_tp}B+16)={p1_o_tp} order(box {p1_sz_bx}B+16)={p1_o_bx} " f"(fl==0 只说明该阶空、仍可由更高阶拆分;若 alloc 失败而 dump_stats 的 free>0 才是元数据损坏铁证," f"任一 order>max_order={mb.max_order} 则是 size 异常)", "Phase1")
VLogger.info(f"[EVIDENCE] order(class_names {p1_sz_cn}B+16)={p1_o_cn} fl={p1_fl_cn} " f"order(def_sha1s {p1_sz_ds}B+16)={p1_o_ds} fl={p1_fl_ds} " f"order(topo {p1_sz_tp}B+16)={p1_o_tp} order(box {p1_sz_bx}B+16)={p1_o_bx} " f"(fl==0 only means that order is empty and can still be split by a higher order; if alloc fails while dump_stats shows free>0 that is the definitive proof of metadata corruption; " f"any order>max_order={mb.max_order} means an abnormal size)", "Phase1")
# Pre-scan class inheritance and build the topo order (flat byte arrays avoid str-pointer NULL crashes)
class_names_buf: bytes = mb.alloc(Phase1.MAX_CLASSES * Phase1.CLASS_NAME_LEN)
@@ -369,9 +369,9 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: VLogg
# These three flat buffers are Phase1Scan._ScanClassInheritance's contract pre-buffers; it
# fails fast on NULL, so report and abort per buffer on allocation failure (never fall back).
if class_names_buf is None: VLogger.error("类继承预扫描扁平缓冲分配失败:class_names_buf 为 NULL,终止编译", "Phase1")
if parent_names_buf is None: VLogger.error("类继承预扫描扁平缓冲分配失败:parent_names_buf 为 NULL,终止编译", "Phase1")
if def_sha1s_buf is None: VLogger.error("类继承预扫描扁平缓冲分配失败:def_sha1s_buf 为 NULL,终止编译", "Phase1")
if class_names_buf is None: VLogger.error("class-inheritance pre-scan flat buffer allocation failed: class_names_buf is NULL; aborting compilation", "Phase1")
if parent_names_buf is None: VLogger.error("class-inheritance pre-scan flat buffer allocation failed: parent_names_buf is NULL; aborting compilation", "Phase1")
if def_sha1s_buf is None: VLogger.error("class-inheritance pre-scan flat buffer allocation failed: def_sha1s_buf is NULL; aborting compilation", "Phase1")
string.memset(class_names_buf, 0, Phase1.MAX_CLASSES * Phase1.CLASS_NAME_LEN)
string.memset(parent_names_buf, 0, Phase1.MAX_CLASSES * Phase1.CLASS_NAME_LEN)
@@ -389,12 +389,12 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: VLogg
if ret_topo == 0:
tc_ptr: t.CPtr = (int*)(topo_count_box)
if tc_ptr is not None: topo_count = tc_ptr[0]
VLogger.info(f"拓扑排序: {class_count_scanned} 个类, {topo_count} 个文件按依赖顺序处理", "Phase1")
VLogger.info(f"topological sort: {class_count_scanned} classes, {topo_count} files processed in dependency order", "Phase1")
else:
VLogger.error("拓扑排序失败(类继承依赖无法解析),终止编译", "Phase1")
VLogger.error("topological sort failed (class-inheritance dependencies could not be resolved); aborting compilation", "Phase1")
return 1
else:
VLogger.warning("类继承预扫描无结果,回退到字母序", "Phase1")
VLogger.warning("class-inheritance pre-scan produced no result; falling back to alphabetical order", "Phase1")
topo_count = 0
# ============================================================
@@ -455,7 +455,7 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: VLogg
# read_all's capacity includes the terminator (reads at most cap-1 bytes); longer files are truncated
if f_r.size() >= Phase1.SRC_BUF_SIZE:
VLogger.error(f"源文件超过 {Phase1.SRC_BUF_SIZE} 字节上限, 拒绝读取: {file_path_r}", "Phase1")
VLogger.error(f"source file exceeds the {Phase1.SRC_BUF_SIZE}-byte limit, refusing to read: {file_path_r}", "Phase1")
f_r.close()
p1a_fl += 1
continue
@@ -525,7 +525,7 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: VLogg
if mb.deep_detect != 0:
chk_rc_1a: int = mb.check_alive_blocks(p1a_mark)
if chk_rc_1a != 0:
VLogger.error(f"内存损坏(1a 每文件后续, rc={chk_rc_1a}): {file_path_r}", "Phase1")
VLogger.error(f"memory corruption (1a per-file post-check, rc={chk_rc_1a}): {file_path_r}", "Phase1")
stdlib.exit(1)
# Reclaim this file's transient AST/lexer/token/IR (registered data is in perm_pool, so
@@ -534,10 +534,10 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: VLogg
# 1a total time (struct/enum/union registration, incl. per-file read+parse+declaration)
t_a1: uint64 = w32.win32base.GetTickCount64()
VLogger.info(f"1a 总耗时: {t_a1-t_a0} ms (注册 struct/enum, reg={p1a_reg} skp={p1a_skp})", "Phase1")
VLogger.info(f"1a total time: {t_a1-t_a0} ms (registering struct/enum, reg={p1a_reg} skp={p1a_skp})", "Phase1")
if p1a_fl > 0:
VLogger.error(f"Phase 1a 有 {p1a_fl} 个文件 struct 注册失败", "Phase1")
VLogger.error(f"Phase 1a: {p1a_fl} files failed to register struct", "Phase1")
return 1
# Release topo-sort resources (topo_order/topo_count_box stay until Phase 1b ends;
@@ -576,7 +576,7 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: VLogg
vpi_path_pv: str = StubMergerCollect._sliced_path(temp_dir, dir_len_pv, sha1_pv, "vpi")
if vpi_path_pv is None:
p1bpre_failed += 1
VLogger.error(f"[Phase 1b-pre] {entry_pv.Path} | vpi 路径生成失败", "Phase1")
VLogger.error(f"[Phase 1b-pre] {entry_pv.Path} | failed to generate the vpi path", "Phase1")
continue
vf_pv: fileio.File* = fileio.File(vpi_path_pv, fileio.MODE.R)
if not vf_pv.closed:
@@ -589,33 +589,33 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: VLogg
f_pv: fileio.File* = fileio.File(file_path_pv, fileio.MODE.R)
if f_pv.closed:
p1bpre_failed += 1
VLogger.error(f"[Phase 1b-pre] {file_path_pv} | 无法打开源文件", "Phase1")
VLogger.error(f"[Phase 1b-pre] {file_path_pv} | cannot open the source file", "Phase1")
continue
# read_all's capacity includes the terminator (reads at most cap-1 bytes); longer files are truncated
if f_pv.size() >= Phase1.SRC_BUF_SIZE:
p1bpre_failed += 1
VLogger.error(f"[Phase 1b-pre] {file_path_pv} | 源文件超过 {Phase1.SRC_BUF_SIZE} 字节上限", "Phase1")
VLogger.error(f"[Phase 1b-pre] {file_path_pv} | source file exceeds the {Phase1.SRC_BUF_SIZE}-byte limit", "Phase1")
f_pv.close()
continue
src_buf_pv: bytes = stdlib.malloc(Phase1.SRC_BUF_SIZE)
if src_buf_pv is None:
f_pv.close()
p1bpre_failed += 1
VLogger.error(f"[Phase 1b-pre] {file_path_pv} | 源文件缓冲分配失败(OOM)", "Phase1")
VLogger.error(f"[Phase 1b-pre] {file_path_pv} | source buffer allocation failed (OOM)", "Phase1")
continue
bytes_read_pv: LONG = f_pv.read_all(src_buf_pv, Phase1.SRC_BUF_SIZE)
f_pv.close()
if bytes_read_pv <= 0:
stdlib.free(src_buf_pv)
p1bpre_failed += 1
VLogger.error(f"[Phase 1b-pre] {file_path_pv} | 文件读取失败或为空", "Phase1")
VLogger.error(f"[Phase 1b-pre] {file_path_pv} | file read failed or is empty", "Phase1")
continue
if bytes_read_pv < Phase1.SRC_BUF_SIZE: src_buf_pv[bytes_read_pv] = 0
lx_pv: ast.Lexer* = ast.new_lexer(mb)
if lx_pv is None:
stdlib.free(src_buf_pv)
p1bpre_failed += 1
VLogger.error(f"[Phase 1b-pre] {file_path_pv} | 词法器分配失败(OOM)", "Phase1")
VLogger.error(f"[Phase 1b-pre] {file_path_pv} | lexer allocation failed (OOM)", "Phase1")
continue
ast._lexer_init(lx_pv, src_buf_pv, mb)
tokens_pv: ast.Token* = ast.tokenize(lx_pv)
@@ -625,7 +625,7 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: VLogg
p1bpre_failed += 1
# Parse failure: the log line inlines line/col + reason, plus a full Python-style
# traceback (same reporting path as the single-file BuildPipeline).
VLogger.error(f"[Phase 1b-pre] {file_path_pv} | AST 解析失败(第 {ast.get_parse_err_line()} 行第 {ast.get_parse_err_col()} 列: {ast.get_parse_err_msg()})", "Phase1")
VLogger.error(f"[Phase 1b-pre] {file_path_pv} | AST parse failed (line {ast.get_parse_err_line()} col {ast.get_parse_err_col()}: {ast.get_parse_err_msg()})", "Phase1")
GenApi.dump_parse_traceback(file_path_pv)
continue
pyi_buf_pv: bytes = stdlib.malloc(Phase1.PYI_BUF_SIZE)
@@ -642,14 +642,14 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: VLogg
stdlib.free(pyi_buf_pv)
else:
p1bpre_failed += 1
VLogger.error(f"[Phase 1b-pre] {file_path_pv} | pyi 缓冲分配失败(OOM)", "Phase1")
VLogger.error(f"[Phase 1b-pre] {file_path_pv} | pyi buffer allocation failed (OOM)", "Phase1")
stdlib.free(src_buf_pv)
# [TEMP-DIAG] 1b-pre per-file sentinel (as above): check the tail canary of live blocks
# in [mark1, chk_depth). Must run before reset_to(mark1); only under --detection-deep.
if mb.deep_detect != 0:
chk_rc_pv: int = mb.check_alive_blocks(mark1)
if chk_rc_pv != 0:
VLogger.error(f"内存损坏(1b-pre 每文件后续, rc={chk_rc_pv}): {file_path_pv}", "Phase1")
VLogger.error(f"memory corruption (1b-pre per-file post-check, rc={chk_rc_pv}): {file_path_pv}", "Phase1")
stdlib.exit(1)
# Reclaim this file's AST (the vpi is written; nothing else persists after mark1)
@@ -657,10 +657,10 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: VLogg
# 1b-pre total time (vpi pre-generation)
t_pv1: uint64 = w32.win32base.GetTickCount64()
VLogger.info(f"1b-pre 总耗时: {t_pv1-t_pv0} ms (vpi 预生成)", "Phase1")
VLogger.info(f"1b-pre total time: {t_pv1-t_pv0} ms (vpi pre-generation)", "Phase1")
if p1bpre_failed > 0:
VLogger.error(f"Phase 1b-pre 有 {p1bpre_failed} 个文件 vpi 预生成失败(失败文件与原因见上方逐条 [Phase 1b-pre] 日志)", "Phase1")
VLogger.error(f"Phase 1b-pre: {p1bpre_failed} files failed vpi pre-generation (see the per-file [Phase 1b-pre] log lines above for failures and causes)", "Phase1")
return 1
# Iterate each file (topo order preferred so base-class files translate first and their
@@ -740,7 +740,7 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: VLogg
if force_translate == 1:
# Has class: force translation (overwrite stub/text, re-register classes)
rp: str = entry.RelPath
VLogger.info(f"含 class 定义,强制翻译: {rp}(sha1={sha1})", "Phase1")
VLogger.info(f"contains a class definition, forcing translation: {rp}(sha1={sha1})", "Phase1")
else:
# Skip already-existing stubs (on-demand translation)
sf: fileio.File* = fileio.File(stub_path, fileio.MODE.R)
@@ -777,11 +777,11 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: VLogg
stdlib.free(text_path)
# text.ll missing, re-translate
rp: str = entry.RelPath
VLogger.info(f"text.ll 不存在,重新翻译: {rp}(sha1={sha1})", "Phase1")
VLogger.info(f"text.ll not found, re-translating: {rp}(sha1={sha1})", "Phase1")
else:
# stub missing, translate
rp: str = entry.RelPath
VLogger.info(f"翻译: {rp}(sha1={sha1})", "Phase1")
VLogger.info(f"translating: {rp}(sha1={sha1})", "Phase1")
stdlib.free(stub_path)
@@ -792,17 +792,17 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: VLogg
file_path: str = entry.Path
f: fileio.File* = fileio.File(file_path, fileio.MODE.R)
if f is None:
VLogger.error(f"无法打开(f=None): {file_path}", "Phase1")
VLogger.error(f"cannot open (f=None): {file_path}", "Phase1")
failed += 1
continue
if f.closed:
VLogger.error(f"无法打开: {file_path}", "Phase1")
VLogger.error(f"cannot open: {file_path}", "Phase1")
failed += 1
continue
# read_all's capacity includes the terminator (reads at most cap-1 bytes); longer files are truncated
if f.size() >= Phase1.SRC_BUF_SIZE:
VLogger.error(f"源文件超过 {Phase1.SRC_BUF_SIZE} 字节上限, 拒绝读取: {file_path}", "Phase1")
VLogger.error(f"source file exceeds the {Phase1.SRC_BUF_SIZE}-byte limit, refusing to read: {file_path}", "Phase1")
f.close()
failed += 1
continue
@@ -816,7 +816,7 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: VLogg
bytes_read: LONG = f.read_all(src_buf, Phase1.SRC_BUF_SIZE)
f.close()
if bytes_read <= 0:
VLogger.error(f"读取失败: {file_path}", "Phase1")
VLogger.error(f"read failed: {file_path}", "Phase1")
stdlib.free(src_buf)
failed += 1
continue
@@ -834,7 +834,7 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: VLogg
if tree is None:
# Parse failure: line/col + reason go into the log line (reason supplied by parser.vp's
# _record_* / parse_tokens fallback).
VLogger.error(f"AST 解析失败: {file_path}(第 {ast.get_parse_err_line()} 行第 {ast.get_parse_err_col()} 列: {ast.get_parse_err_msg()})", "Phase1")
VLogger.error(f"AST parse failed: {file_path} (line {ast.get_parse_err_line()} col {ast.get_parse_err_col()}: {ast.get_parse_err_msg()})", "Phase1")
stdlib.free(src_buf)
failed += 1
continue
@@ -889,7 +889,7 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: VLogg
# corrupts a live block header is pinned to the current file (deterministic crash location).
pre_rc: int = mb.check_alive_blocks(mark1)
if pre_rc != 0:
VLogger.error(f"内存损坏(翻译前存活块, rc={pre_rc}): {file_path}", "Phase1")
VLogger.error(f"memory corruption (live blocks before translate, rc={pre_rc}): {file_path}", "Phase1")
stdlib.exit(1)
ret: int = tr.translate(tree)
@@ -897,9 +897,9 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: VLogg
t_f2: uint64 = w32.win32base.GetTickCount64()
# Diagnostic: translate returned normally (if it hangs inside, this line is not printed,
# distinguishing an internal hang from a later dump hang).
if ret == 0: VLogger.info(f"翻译完成(translate返回): {file_path}", "Phase1")
if ret == 0: VLogger.info(f"translation complete (translate returned): {file_path}", "Phase1")
if ret != 0:
VLogger.error(f"翻译失败: {file_path}", "Phase1") # error terminates
VLogger.error(f"translation failed: {file_path}", "Phase1") # error terminates
stdlib.free(src_buf)
if tr._global_names is not None: stdlib.free(tr._global_names)
if tr._nonlocal_names is not None: stdlib.free(tr._nonlocal_names)
@@ -913,7 +913,7 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: VLogg
# Diagnostic: the pre-translate sentinel misses write-throughs during translate, so check
# live block headers/canaries before dump (rc=6 means a tail canary was overwritten).
post_rc: int = mb.check_alive_blocks(mark1)
if post_rc != 0: VLogger.error(f"内存损坏(translate后存活块, rc={post_rc}): {file_path}", "Phase1")
if post_rc != 0: VLogger.error(f"memory corruption (live blocks after translate, rc={post_rc}): {file_path}", "Phase1")
# Dump stub IR (declarations only). 1MB is not enough: ast/parser.vp's text.ll exceeds it
# and is truncated (opt: "end of file in global variable name"), so use 8MB.
@@ -968,7 +968,7 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: VLogg
# save {sha1}.full.ll (the online FULL artifact: a self-contained single-module IR for
# Phase B to compile directly, replacing the stub + dependency text merge).
full_buf_p1: bytes = stdlib.malloc(PHASE1_FULL_SIZE)
if full_buf_p1 is None: VLogger.error("full.ll 缓冲区分配失败", "Phase1")
if full_buf_p1 is None: VLogger.error("full.ll buffer allocation failed", "Phase1")
tr.dump_ir(full_buf_p1, PHASE1_FULL_SIZE, mlirlite.OUTPUT_FULL)
full_len_p1: sizet = string.strlen(full_buf_p1)
full_path_p1: str = StubMergerCollect._sliced_path(temp_dir, dir_len_p1, sha1, "full.ll")
@@ -990,7 +990,7 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: VLogg
# Timer 3: dump (stub+text) done; print the per-file three-segment timing
t_f3: uint64 = w32.win32base.GetTickCount64()
VLogger.info(f"[{i+1}/{loop_n_b}] 翻译: {file_path} (parse={t_f1-t_f0} ms, translate={t_f2-t_f1} ms, dump={t_f3-t_f2} ms, total={t_f3-t_f0} ms)", "Phase1")
VLogger.info(f"[{i+1}/{loop_n_b}] translating: {file_path} (parse={t_f1-t_f0} ms, translate={t_f2-t_f1} ms, dump={t_f3-t_f2} ms, total={t_f3-t_f0} ms)", "Phase1")
# save dependencies(_imported_modules) for Phase B
deps_path_p1: str = StubMergerCollect._sliced_path(temp_dir, dir_len_p1, sha1, "deps.txt")
@@ -1015,25 +1015,25 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: VLogg
# OOM reclamation: free this file's transient AST/tokens/lexer/IR/dump data (persistent
# tables moved to stdlib). Memory sentinels locate overruns deterministically.
chk_rc: int = mb.check_alive_blocks(mark1)
if chk_rc != 0: VLogger.error(f"内存损坏(存活块 header, rc={chk_rc}): {file_path}", "Phase1")
if chk_rc != 0: VLogger.error(f"memory corruption (live block header, rc={chk_rc}): {file_path}", "Phase1")
# Out-of-bounds writes are the crash root cause (unrelated to reclamation/UAF, confirmed
# by A/B tests); reset_to plus the tail canary pins the offending file.
mb.reset_to(mark1)
sc_rc: int = mb.self_check()
if sc_rc != 0: VLogger.error(f"内存损坏(free list, rc={sc_rc}): {file_path}", "Phase1")
if sc_rc != 0: VLogger.error(f"memory corruption (free list, rc={sc_rc}): {file_path}", "Phase1")
# 1b total time (full translation + dump)
t_b1: uint64 = w32.win32base.GetTickCount64()
VLogger.info(f"1b 总耗时: {t_b1-t_b0} ms (全量翻译, trans={translated} skip={skipped} fail={failed})", "Phase1")
VLogger.info(f"1b total time: {t_b1-t_b0} ms (full translation, trans={translated} skip={skipped} fail={failed})", "Phase1")
# Release the topo-sort resources used by Phase 1b
if topo_order is not None: mb.free(topo_order)
if topo_count_box is not None: mb.free(topo_count_box)
VLogger.info(f"完成: 翻译={translated} 跳过={skipped} 失败={failed}", "Phase1")
VLogger.info(f"done: translated={translated} skipped={skipped} failed={failed}", "Phase1")
if failed > 0:
VLogger.error(f"Phase1 有 {failed} 个文件翻译失败", "Phase1")
VLogger.error(f"Phase1: {failed} files failed to translate", "Phase1")
return 1
# Release the reachable SHA1 set (if allocated)
+6 -6
View File
@@ -45,7 +45,7 @@ def _ScanClassInheritance(mb: memhub.MemBuddy*, result: IncludesScanner.ScanResu
if result is None: return -1
# The three flat buffers are mb.alloc'ed by the caller; a NULL buffer would make
# strcpy write the zero page, so fail-fast abort instead of returning -1 silently.
if class_names_buf is None or parent_names_buf is None or def_sha1s_buf is None: VLogger.error("类继承预扫描扁平缓冲分配失败(class_names_buf/parent_names_buf/def_sha1s_buf 为 NULL)", "Phase1")
if class_names_buf is None or parent_names_buf is None or def_sha1s_buf is None: VLogger.error("class-inheritance pre-scan flat buffer allocation failed (class_names_buf/parent_names_buf/def_sha1s_buf is NULL)", "Phase1")
entry_size: sizet = IncludesScanner.FileEntry.__sizeof__()
class_count: int = 0
@@ -74,7 +74,7 @@ def _ScanClassInheritance(mb: memhub.MemBuddy*, result: IncludesScanner.ScanResu
# read_all's capacity includes the terminator (reads at most cap-1 bytes); longer files are truncated
if f.size() >= Phase1.SRC_BUF_SIZE:
VLogger.error(f"源文件超过 {Phase1.SRC_BUF_SIZE} 字节上限, 拒绝读取: {file_path}", "Phase1")
VLogger.error(f"source file exceeds the {Phase1.SRC_BUF_SIZE}-byte limit, refusing to read: {file_path}", "Phase1")
f.close()
continue
@@ -236,7 +236,7 @@ def _PreRegisterAllGenericTemplates(mb: memhub.MemBuddy*, result: IncludesScanne
if f_pg.closed: continue
# read_all's capacity includes the terminator (reads at most cap-1 bytes); longer files are truncated
if f_pg.size() >= Phase1.SRC_BUF_SIZE:
VLogger.error(f"源文件超过 {Phase1.SRC_BUF_SIZE} 字节上限, 拒绝读取: {file_path_pg}", "Phase1")
VLogger.error(f"source file exceeds the {Phase1.SRC_BUF_SIZE}-byte limit, refusing to read: {file_path_pg}", "Phase1")
f_pg.close()
continue
src_buf_pg: bytes = stdlib.malloc(Phase1.SRC_BUF_SIZE)
@@ -290,8 +290,8 @@ def _PreRegisterAllGenericTemplates(mb: memhub.MemBuddy*, result: IncludesScanne
GenApi._register_generic_func_template(perm, fd_pg, entry_pg.Sha1)
handled_count += 1
stdlib.free(src_buf_pg)
if handled_count > 0: VLogger.info(f"mark1 前永久池预注册 {handled_count} 个 includes 泛型模板(类+函数)", "Phase1")
else: VLogger.info(f"mark1 前永久池预注册 0 个泛型模板(扫描 {result.Count} 个文件,可能 marker 未命中或全量扫描失败)", "Phase1")
if handled_count > 0: VLogger.info(f"pre-registered {handled_count} includes generic templates (classes and functions) in the permanent pool before mark1", "Phase1")
else: VLogger.info(f"pre-registered 0 generic templates in the permanent pool before mark1 (scanned {result.Count} files; the marker may not have matched or the full scan failed)", "Phase1")
return handled_count
# _FindClassDefSha1 - look up a class name's defining file SHA1 in the flat arrays;
# returns a pointer into def_sha1s_buf, or None if not found.
@@ -326,7 +326,7 @@ def _ScanTreeForContainers(node: ast.AST*, need_list_box: t.CPtr, need_dict_box:
"""Recursively scan the AST for list/dict container usage."""
# Recursion depth guard: a self-referential AST cycle would recurse until the
# stack is exhausted; fail-fast abort.
if depth > 128: VLogger.error("容器扫描递归过深(AST 环或嵌套超过 128 层)", "Phase1")
if depth > 128: VLogger.error("container scan recursion too deep (AST cycle or nesting over 128 levels)", "Phase1")
if node is None: return
k_s: int = node.kind()
if k_s == ast.ASTKind.Name:
+68 -68
View File
@@ -222,7 +222,7 @@ def _PreRegisterAppGenericTemplates(mb: memhub.MemBuddy*, entries: SrcFileEntry*
if f_pa.closed: continue
# read_all's capacity includes the terminator (reads at most cap-1 bytes); longer files are truncated
if f_pa.size() >= SRC_BUF_SIZE:
VLogger.error(f"源文件超过 {SRC_BUF_SIZE} 字节上限, 拒绝读取: {ent_pa.Path}", "PhaseA")
VLogger.error(f"source file exceeds {SRC_BUF_SIZE} byte limit, refusing to read: {ent_pa.Path}", "PhaseA")
f_pa.close()
continue
src_buf_pa: bytes = stdlib.malloc(SRC_BUF_SIZE)
@@ -273,7 +273,7 @@ def _PreRegisterAppGenericTemplates(mb: memhub.MemBuddy*, entries: SrcFileEntry*
GenApi._register_generic_func_template(perm_pa, fd_pa, ent_pa.Sha1)
handled += 1
stdlib.free(src_buf_pa)
if handled > 0: VLogger.info(f"mark2 前预注册 {handled} 个 App 泛型模板(类+函数)", "PhaseA")
if handled > 0: VLogger.info(f"pre-registered {handled} App generic templates before mark2 (classes + functions)", "PhaseA")
return handled
@@ -511,10 +511,10 @@ def _PhaseADumpFailGraph(temp_dir: str, td_len: sizet, path: str, sha1: str) ->
def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, output_dir: str, cc_cmd: str, cc_flags: str, linker_cmd: str, linker_flags: str, linker_output: str, includes_binary_dir: str, includes_dir: str, do_phase1: int, do_phase2: int, log: VLogger.Logger*, args: argparse.ParsedArgs*, linker_static_libs: str = None, linker_dynamic_libs: str = None) -> int:
"""Compile a multi-file project; return 0 on success, non-zero on failure"""
if source_dir is None:
VLogger.error("source_dir 为空", "project")
VLogger.error("source_dir is empty", "project")
return 1
VLogger.info(f"多文件项目编译: {source_dir}", "project")
VLogger.info(f"multi-file project compilation: {source_dir}", "project")
# === 1. Recursively scan .vp/.py files under source_dir (including subdirectories) ===
entry_size: sizet = SrcFileEntry.__sizeof__()
@@ -523,7 +523,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
string.memset(entries, 0, MAX_SRC_FILES * entry_size)
file_count: int = _ScanDirForPyFiles(mb, source_dir, entries, entry_size, 0, MAX_SRC_FILES)
VLogger.info(f"共 {file_count} 个源文件", "project")
VLogger.info(f"{file_count} source files in total", "project")
# Scan .s assembly files (separate array; not translated in Phase A, assembled by llvm-mc after Phase B)
asm_entries: SrcFileEntry* = stdlib.malloc(MAX_SRC_FILES * entry_size)
@@ -532,7 +532,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
return 1
string.memset(asm_entries, 0, MAX_SRC_FILES * entry_size)
asm_file_count: int = _ScanDirForAsmFiles(mb, source_dir, asm_entries, entry_size, 0, MAX_SRC_FILES)
if asm_file_count > 0: VLogger.info(f"汇编文件 {asm_file_count} 个(llvm-mc 汇编参与链接)", "project")
if asm_file_count > 0: VLogger.info(f"{asm_file_count} assembly files (assembled by llvm-mc and linked)", "project")
if file_count == 0:
stdlib.free(entries)
@@ -561,12 +561,12 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
if includes_dir is not None:
if StubMergerStore.GetSha1StoreCount() == 0:
rs_05: bytes = stdlib.malloc(StubMerger.MAX_INCLUDES_SHA1 * 17)
if rs_05 is None: VLogger.error("reachable_set 分配失败,无法填充 Sha1Store", "project")
if rs_05 is None: VLogger.error("reachable_set allocation failed, cannot fill Sha1Store", "project")
else:
string.memset(rs_05, 0, StubMerger.MAX_INCLUDES_SHA1 * 17)
rc_05: int = StubMergerBuild._BuildReachableSha1Set(mb, source_dir, temp_dir, rs_05, None, includes_dir)
if rc_05 < 0: VLogger.error("依赖图扫描失败,无法填充 Sha1Store", "project")
elif rc_05 > 0: VLogger.info(f"Phase2 依赖图按需填充 Sha1Store: {rc_05} 个", "project")
if rc_05 < 0: VLogger.error("dependency graph scan failed, cannot fill Sha1Store", "project")
elif rc_05 > 0: VLogger.info(f"Phase2 dependency graph filled Sha1Store on demand: {rc_05} entries", "project")
stdlib.free(rs_05)
# Append App source SHA1s to _sha1_map.txt (human-readable only; format {sha1}:{rel_path}) and to the
@@ -592,7 +592,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
if string.strncmp(p_str, source_dir, src_dir_len_am) == 0: rel_path_am = p_str + src_dir_len_am + 1
# Also append to the in-memory store (for cross-module CDefine lookups)
# rel_path_am looks like "lib/core/StubMerger.vp", matching _sha1_map.txt
if StubMergerStore.AppendToSha1MapStore(ent_am.Sha1, rel_path_am) != 0: VLogger.error(f"AppendToSha1MapStore 失败: {rel_path_am}", "project")
if StubMergerStore.AppendToSha1MapStore(ent_am.Sha1, rel_path_am) != 0: VLogger.error(f"AppendToSha1MapStore failed: {rel_path_am}", "project")
app_filled += 1
# Write _sha1_map.txt (human-readable output)
if not mf.closed and line_am is not None:
@@ -602,7 +602,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
if line_am is not None: stdlib.free(line_am)
if not mf.closed:
mf.close()
VLogger.info(f"已追加 {app_filled} 个 App 文件到 _sha1_map.txt 和内存存储器", "project")
VLogger.info(f"appended {app_filled} App files to _sha1_map.txt and the in-memory store", "project")
stdlib.free(map_path_am)
# The module SHA1 map is filled into the StubMerger global store by PopulateSha1MapStore +
@@ -619,13 +619,13 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
ent_vp2: SrcFileEntry* = (SrcFileEntry*)(ea_vp2)
if ent_vp2 is None or ent_vp2.Path is None or ent_vp2.Sha1 is None:
vpi_failed_p2 += 1
VLogger.error("[Phase A-pre-vpi] 源文件条目无效(Path/Sha1 缺失),跳过", "PhaseA")
VLogger.error("[Phase A-pre-vpi] invalid source entry (Path/Sha1 missing), skipping", "PhaseA")
continue
# Skip if the vpi already exists (sha1 is content-based, so a same-named vpi is valid; cross-run cache)
vpi_path_vp2: str = StubMergerCollect._sliced_path(temp_dir, td_len_vpa, ent_vp2.Sha1, "vpi")
if vpi_path_vp2 is None:
vpi_failed_p2 += 1
VLogger.error(f"[Phase A-pre-vpi] {ent_vp2.Path} | vpi 路径生成失败", "PhaseA")
VLogger.error(f"[Phase A-pre-vpi] {ent_vp2.Path} | failed to build vpi path", "PhaseA")
continue
vf_vp2: fileio.File* = fileio.File(vpi_path_vp2, fileio.MODE.R)
if not vf_vp2.closed:
@@ -638,33 +638,33 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
f_vp2: fileio.File* = fileio.File(ent_vp2.Path, fileio.MODE.R)
if f_vp2.closed:
vpi_failed_p2 += 1
VLogger.error(f"[Phase A-pre-vpi] {ent_vp2.Path} | 无法打开源文件", "PhaseA")
VLogger.error(f"[Phase A-pre-vpi] {ent_vp2.Path} | cannot open source file", "PhaseA")
continue
# read_all's capacity includes the terminator (reads at most cap-1 bytes); longer files are truncated
if f_vp2.size() >= SRC_BUF_SIZE:
vpi_failed_p2 += 1
VLogger.error(f"[Phase A-pre-vpi] {ent_vp2.Path} | 源文件超过 {SRC_BUF_SIZE} 字节上限", "PhaseA")
VLogger.error(f"[Phase A-pre-vpi] {ent_vp2.Path} | source file exceeds {SRC_BUF_SIZE} byte limit", "PhaseA")
f_vp2.close()
continue
src_buf_vp2: bytes = stdlib.malloc(SRC_BUF_SIZE)
if src_buf_vp2 is None:
f_vp2.close()
vpi_failed_p2 += 1
VLogger.error(f"[Phase A-pre-vpi] {ent_vp2.Path} | 源文件缓冲分配失败(OOM)", "PhaseA")
VLogger.error(f"[Phase A-pre-vpi] {ent_vp2.Path} | source buffer allocation failed (OOM)", "PhaseA")
continue
bytes_read_vp2: LONG = f_vp2.read_all(src_buf_vp2, SRC_BUF_SIZE)
f_vp2.close()
if bytes_read_vp2 <= 0:
stdlib.free(src_buf_vp2)
vpi_failed_p2 += 1
VLogger.error(f"[Phase A-pre-vpi] {ent_vp2.Path} | 文件读取失败或为空", "PhaseA")
VLogger.error(f"[Phase A-pre-vpi] {ent_vp2.Path} | file read failed or empty", "PhaseA")
continue
if bytes_read_vp2 < SRC_BUF_SIZE: src_buf_vp2[bytes_read_vp2] = 0
lx_vp2: ast.Lexer* = ast.new_lexer(mb)
if lx_vp2 is None:
stdlib.free(src_buf_vp2)
vpi_failed_p2 += 1
VLogger.error(f"[Phase A-pre-vpi] {ent_vp2.Path} | 词法器分配失败(OOM)", "PhaseA")
VLogger.error(f"[Phase A-pre-vpi] {ent_vp2.Path} | lexer allocation failed (OOM)", "PhaseA")
continue
ast._lexer_init(lx_vp2, src_buf_vp2, mb)
tokens_vp2: ast.Token* = ast.tokenize(lx_vp2)
@@ -674,7 +674,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
vpi_failed_p2 += 1
# Parse failure: inline the line/column + reason in the log line (same source as the full
# Python-style traceback below), since "AST parse failed" alone gives no location or cause.
VLogger.error(f"[Phase A-pre-vpi] {ent_vp2.Path} | AST 解析失败(第 {ast.get_parse_err_line()} 行第 {ast.get_parse_err_col()} 列: {ast.get_parse_err_msg()})", "PhaseA")
VLogger.error(f"[Phase A-pre-vpi] {ent_vp2.Path} | AST parse failed (line {ast.get_parse_err_line()} col {ast.get_parse_err_col()}: {ast.get_parse_err_msg()})", "PhaseA")
GenApi.dump_parse_traceback(ent_vp2.Path)
continue
pyi_buf_vp2: bytes = stdlib.malloc(PYI_BUF_SIZE_P2)
@@ -691,12 +691,12 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
stdlib.free(pyi_buf_vp2)
else:
vpi_failed_p2 += 1
VLogger.error(f"[Phase A-pre-vpi] {ent_vp2.Path} | pyi 缓冲分配失败(OOM)", "PhaseA")
VLogger.error(f"[Phase A-pre-vpi] {ent_vp2.Path} | pyi buffer allocation failed (OOM)", "PhaseA")
stdlib.free(src_buf_vp2)
# OOM cleanup: free this file's AST (the vpi is already on disk; persistent data is stdlib-allocated)
mb.reset_to(mark2)
if vpi_failed_p2 > 0:
VLogger.error(f"Phase A-pre-vpi 有 {vpi_failed_p2} 个文件 vpi 预生成失败(失败文件与原因见上方逐条 [Phase A-pre-vpi] 日志)", "PhaseA")
VLogger.error(f"Phase A-pre-vpi: {vpi_failed_p2} files failed vpi pre-generation (see the per-file [Phase A-pre-vpi] logs above for failing files and causes)", "PhaseA")
return 1
@@ -706,7 +706,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
app_deps_buf: bytes = None
app_deps_len: sizet = 0
if do_phase1 != 0:
log.banner("Phase A: 生成 stub + text")
log.banner("Phase A: generate stub + text")
# Initialize the Phase A convergence-failure diagnostic table (stdlib, survives reset)
_PhaseADiagInit(sizet(file_count) + StubMerger.MAX_INCLUDES)
@@ -772,13 +772,13 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
for si_inc_f in range(store_count_inc_pre):
if inc_fail_flags[si_inc_f] != 0:
rel_f: str = store_rel_inc_pre + sizet(si_inc_f) * StubMerger.MAX_REL_PATH_LEN
VLogger.error(f"includes 预注册失败: {rel_f}(文件无法翻译,可能是语法/解析/编译错误),终止编译", "prePhaseA")
VLogger.error(f"includes pre-registration failed: {rel_f} (file cannot be translated; possible syntax/parse/compile error), aborting compilation", "prePhaseA")
stdlib.free(inc_fail_flags)
VLogger.info(f"includes 预注册 + 依赖填充: {deps_filled_pre}/{store_count_inc_pre}", "prePhaseA")
VLogger.info(f"includes pre-registration + dependency fill: {deps_filled_pre}/{store_count_inc_pre}", "prePhaseA")
# === Phase A-pre: pre-register struct/enum/union for all sources ===
# Registers declarations only (declare_only=1), no method bodies; multi-pass resolves cross-file ordering.
VLogger.info("预注册 struct/enum/union...", "PhaseA")
VLogger.info("pre-registering struct/enum/union...", "PhaseA")
src_dir_len_pre: sizet = string.strlen(source_dir)
PHASE_A_PRE_MAX_PASSES: int = 3
for pass_i in range(PHASE_A_PRE_MAX_PASSES):
@@ -794,13 +794,13 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
# App pre-registration passes pre_allow_dep_inject=1: imports and dep vpi injection happen at
# declaration time so field annotations share Phase A's context (else struct layouts diverge).
tr_pre: HandlesTranslator.Translator* = BuildPipeline.TranslateFileGetTrans(mb, ent_pre.Path, ent_pre.Sha1, pkg_pre, 1, None, 1)
if tr_pre is None: VLogger.error(f"预注册失败: {ent_pre.Path}", "PhaseA")
if tr_pre is None: VLogger.error(f"pre-registration failed: {ent_pre.Path}", "PhaseA")
# OOM cleanup: free this App file's transient pre-registration data
mb.reset_to(mark2)
struct_count_after: int = GenApi.get_struct_count()
# Convergence check: no new struct registered this pass -> all classes are registered
if struct_count_after == struct_count_before: break
VLogger.info("预注册完成", "PhaseA")
VLogger.info("pre-registration complete", "PhaseA")
# Phase A IR buffer: 1MB is not enough, ast/parser.vp's text.ll exceeds 1MB and got truncated ->
# merged IR lost the tail (opt: "end of file in global variable name").
@@ -823,7 +823,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
# (filesystem order); failed files are retried next pass, converging in at most file_count passes.
done_flags: bytes = stdlib.malloc(file_count)
if done_flags is None:
VLogger.error("Phase A done_flags 分配失败", "PhaseA")
VLogger.error("Phase A done_flags allocation failed", "PhaseA")
stdlib.free(entries)
return 1
string.memset(done_flags, 0, file_count)
@@ -887,7 +887,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
stdlib.free(im_mod_buf)
# dump stub IR (declarations only)
stub_buf_a: bytes = stdlib.malloc(PHASE_A_IR_SIZE)
if stub_buf_a is None: VLogger.error("stub 缓冲区分配失败", "PhaseA")
if stub_buf_a is None: VLogger.error("stub buffer allocation failed", "PhaseA")
tr_a.dump_ir(stub_buf_a, PHASE_A_IR_SIZE, mlirlite.OUTPUT_STUB)
stub_len_a: sizet = string.strlen(stub_buf_a)
@@ -903,7 +903,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
# dump text IR (definitions only)
text_buf_a: bytes = stdlib.malloc(PHASE_A_IR_SIZE)
if text_buf_a is None: VLogger.error("text 缓冲区分配失败", "PhaseA")
if text_buf_a is None: VLogger.error("text buffer allocation failed", "PhaseA")
tr_a.dump_ir(text_buf_a, PHASE_A_IR_SIZE, mlirlite.OUTPUT_TEXT)
text_len_a: sizet = string.strlen(text_buf_a)
@@ -922,7 +922,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
# Save {sha1}.full.ll (in-memory FULL artifact: a self-contained single-module IR)
full_buf_a: bytes = stdlib.malloc(PHASE_A_FULL_SIZE)
if full_buf_a is None: VLogger.error("full.ll 缓冲区分配失败", "PhaseA")
if full_buf_a is None: VLogger.error("full.ll buffer allocation failed", "PhaseA")
tr_a.dump_ir(full_buf_a, PHASE_A_FULL_SIZE, mlirlite.OUTPUT_FULL)
full_len_a: sizet = string.strlen(full_buf_a)
full_path_a: str = StubMergerCollect._sliced_path(temp_dir, td_len_pa, ent.Sha1, "full.ll")
@@ -980,7 +980,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
if ent_err.Sha1 is not None: _PhaseADumpFailGraph(temp_dir, td_len_fail, ent_err.Path, ent_err.Sha1)
stdlib.free(done_flags)
_PhaseADiagFree()
VLogger.error("Phase A 多遍收敛失败,终止编译", "PhaseA")
VLogger.error("Phase A multi-pass convergence failed, aborting compilation", "PhaseA")
stdlib.free(entries)
return 1
stdlib.free(done_flags)
@@ -988,14 +988,14 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
_PhaseADiagFree()
if do_phase2 == 0:
VLogger.info("Phase A 完成(仅 stub 生成)", "project")
VLogger.info("Phase A complete (stub generation only)", "project")
if app_deps_buf is not None: stdlib.free(app_deps_buf)
stdlib.free(entries)
return 0
# === 3. Phase B: compile each file to .obj ===
if do_phase2 != 0:
log.banner("Phase B: 编译 .obj")
log.banner("Phase B: compile .obj")
# Collect .obj paths (64KB to avoid includes .obj path overflow dropping link symbols).
# reachable_set/reachable_count: filled by Phase B+, used by Phase C for on-demand linking.
@@ -1020,7 +1020,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
# only writes .ll + builds tasks, then run_llc_parallel runs llc in parallel batches.
llc_tasks: bytes = stdlib.malloc(sizet(file_count) * BuildPipeline.LlcTask.__sizeof__())
if llc_tasks is None:
VLogger.error("llc 任务表分配失败", "PhaseB")
VLogger.error("llc task table allocation failed", "PhaseB")
return 1
llc_task_count: int = 0
@@ -1033,11 +1033,11 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
# Read the in-memory FULL artifact {sha1}.full.ll -> complete IR (replaces merging stub + dep stub/text)
combined_ir: bytes = stdlib.malloc(COMBINED_IR_SIZE)
if combined_ir is None:
VLogger.error(f"combined_ir 分配失败: {ent.Path}", "PhaseB")
VLogger.error(f"combined_ir allocation failed: {ent.Path}", "PhaseB")
continue
combined_len: sizet = StubMergerBuild.LoadFullIR(temp_dir, ent.Sha1, combined_ir, COMBINED_IR_SIZE)
if combined_len == 0:
VLogger.error(f"读 full.ll 失败: {ent.Path}", "PhaseB")
VLogger.error(f"failed to read full.ll: {ent.Path}", "PhaseB")
stdlib.free(combined_ir)
continue
# Timing point 1: full.ll read done
@@ -1047,7 +1047,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
ret_w: int = BuildPipeline.write_ir_to_file(combined_ir, combined_len, temp_dir, ent.Sha1)
stdlib.free(combined_ir)
if ret_w != 0:
VLogger.error(f"写 .ll 失败: {ent.Path}", "PhaseB")
VLogger.error(f"failed to write .ll: {ent.Path}", "PhaseB")
sys.exit(1)
# Timing point 2: .ll write done
t_pb2: uint64 = w32.win32base.GetTickCount64()
@@ -1055,11 +1055,11 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
# Build the llc task (command string is stdlib.malloc'd so threads' lifetimes are not reset-reclaimed)
llc_ir_path: str = BuildPipeline.build_sliced_path(temp_dir, ent.Sha1, "ll")
if llc_ir_path is None:
VLogger.error("llc 输入路径构造失败", "PhaseB")
VLogger.error("failed to build llc input path", "PhaseB")
sys.exit(1)
tslot: BuildPipeline.LlcTask* = BuildPipeline.LlcTask*(uint64(llc_tasks) + sizet(llc_task_count) * BuildPipeline.LlcTask.__sizeof__())
if BuildPipeline.init_llc_task(tslot, llc_ir_path, output_dir, ent.Sha1, cc_cmd, cc_flags) != 0:
VLogger.error(f"llc 任务构造失败: {ent.Path}", "PhaseB")
VLogger.error(f"failed to build llc task: {ent.Path}", "PhaseB")
sys.exit(1)
# Timing point 3: llc task construction done
t_pb3: uint64 = w32.win32base.GetTickCount64()
@@ -1067,7 +1067,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
compiled_count += 1
# Show files submitted to llc (with the three timings: merge stub / write .ll / build task)
VLogger.info(f"[{i+1}/{file_count}] llc 已提交: {ent.Path} (merge={t_pb1-t_pb0} ms, write={t_pb2-t_pb1} ms, task={t_pb3-t_pb2} ms, total={t_pb3-t_pb0} ms)", "PhaseB")
VLogger.info(f"[{i+1}/{file_count}] llc submitted: {ent.Path} (merge={t_pb1-t_pb0} ms, write={t_pb2-t_pb1} ms, task={t_pb3-t_pb2} ms, total={t_pb3-t_pb0} ms)", "PhaseB")
# Build the .obj path and detect the main module (test_main.vp or main.vp; .py no longer recognized)
od_len: sizet = string.strlen(output_dir)
is_main_mod: int = 0
@@ -1081,7 +1081,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
if is_main_mod != 0:
# main module: copy to main_obj_path (so main is first at link time)
if op_sliced_len < 512: string.strcpy(main_obj_path, obj_path_sliced)
else: VLogger.error("main_obj_path 缓冲区不足,终止编译", "PhaseB")
else: VLogger.error("main_obj_path buffer too small, aborting compilation", "PhaseB")
else:
# Other modules: append to obj_paths
if obj_pos + op_sliced_len + 2 < OBJ_PATHS_SIZE:
@@ -1091,13 +1091,13 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
string.strcpy(obj_paths + obj_pos, obj_path_sliced)
obj_pos += op_sliced_len
obj_paths[obj_pos] = '\0'
else: VLogger.error(".obj 路径缓冲区不足", "PhaseB")
else: VLogger.error(".obj path buffer too small", "PhaseB")
stdlib.free(obj_path_sliced)
# Run all llc jobs in parallel (batched at LLC_MAX_THREADS threads)
ret_p: int = BuildPipeline.run_llc_parallel(llc_tasks, llc_task_count, BuildPipeline.LLC_MAX_THREADS)
if ret_p != 0:
VLogger.error("llc 编译失败,终止编译", "PhaseB")
VLogger.error("llc compilation failed, aborting compilation", "PhaseB")
sys.exit(1)
# Free the llc tasks (command strings + records)
@@ -1118,7 +1118,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
if ent_as is None or ent_as.Path is None or ent_as.Sha1 is None: continue
aret_as: int = BuildPipeline.asm_to_obj(ent_as.Path, ent_as.Sha1, output_dir, Config.TargetTriple)
if aret_as != 0:
VLogger.error(f"llvm-mc 汇编失败,终止编译: {ent_as.Path}", "PhaseB")
VLogger.error(f"llvm-mc assembly failed, aborting compilation: {ent_as.Path}", "PhaseB")
sys.exit(1)
obj_path_asm: str = StubMergerCollect._sliced_path(output_dir, od_len_as, ent_as.Sha1, "obj")
if obj_path_asm is not None:
@@ -1132,12 +1132,12 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
obj_paths[obj_pos] = '\0'
stdlib.free(obj_path_asm)
asm_compiled += 1
VLogger.info(f"[asm {asm_compiled}] 汇编完成: {ent_as.Path}", "PhaseB")
VLogger.info(f"[asm {asm_compiled}] assembly complete: {ent_as.Path}", "PhaseB")
VLogger.success(f"编译完成: {compiled_count}/{file_count}", "PhaseB")
VLogger.success(f"compilation complete: {compiled_count}/{file_count}", "PhaseB")
if compiled_count == 0:
VLogger.error("无成功编译的文件", "PhaseB")
VLogger.error("no files compiled successfully", "PhaseB")
stdlib.free(entries)
stdlib.free(asm_entries)
return 1
@@ -1149,7 +1149,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
td_len_mi: sizet = string.strlen(temp_dir)
# Diagnostic log: confirm the Phase B+ entry and Sha1Store state
VLogger.info(f"PhaseB+ 入口: store_count={StubMergerStore.GetSha1StoreCount()} includes_binary_dir={includes_binary_dir}", "PhaseB+")
VLogger.info(f"PhaseB+ entry: store_count={StubMergerStore.GetSha1StoreCount()} includes_binary_dir={includes_binary_dir}", "PhaseB+")
# Create includes_binary_dir (so .obj files can be written and collect_obj_files can scan them)
BuildPipeline.ensure_dir(includes_binary_dir)
@@ -1165,13 +1165,13 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
if reachable_set is not None:
string.memset(reachable_set, 0, REACHABLE_SET_SIZE)
reachable_count = StubMergerBuild._BuildReachableSha1Set(mb, source_dir, temp_dir, reachable_set, app_deps_buf, includes_dir)
VLogger.info(f"可达 includes SHA1: {reachable_count} 个", "PhaseB+")
VLogger.info(f"reachable includes SHA1: {reachable_count} entries", "PhaseB+")
# fast-fail: reachable-set construction failed (deps not found); never fall back, abort
if reachable_count < 0:
VLogger.error("可达集合构建失败(fast-fail),终止编译", "PhaseB+")
VLogger.error("reachable set construction failed (fast-fail), aborting compilation", "PhaseB+")
return 1
else:
VLogger.error("reachable_set 分配失败,终止编译", "PhaseB+")
VLogger.error("reachable_set allocation failed, aborting compilation", "PhaseB+")
return 1
# Free app_deps_buf (reachable_set is built, no longer needed)
@@ -1196,7 +1196,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
topo_idx: sizet* = (sizet*)(stdlib.malloc(8 * (store_count_mi + 1)))
topo_count: int = 0
if topo_idx is None:
VLogger.error("拓扑排序索引分配失败,终止编译", "PhaseB+")
VLogger.error("topological sort index allocation failed, aborting compilation", "PhaseB+")
topo_count = 0
else:
topo_count = StubMergerTopo.BuildTopoSortedIndices(store_count_mi, store_sha1_arr_mi, store_mod_arr_mi, reachable_set, reachable_count, topo_idx)
@@ -1244,7 +1244,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
if src_fp_mi is None:
stdlib.free(check_pat_mi)
if check_fd_mi is not None: stdlib.free(check_fd_mi)
VLogger.error("includes 源路径分配失败,终止编译", "PhaseB+")
VLogger.error("includes source path allocation failed, aborting compilation", "PhaseB+")
viperlib.snprintf(src_fp_mi, inc_dir_len_mi + 1 + rel_path_len_mi + 1, "%s/%s", incdir_mi, inc_rel_mi)
# Check whether it is a declaration-only file (read {sha1}.meta: no FUNC ... DEF means pure declaration)
@@ -1306,7 +1306,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
StubMergerIR.WriteModuleMeta(tr_mi.Module, temp_dir, td_len_mi, inc_sha1_mi)
# Save {sha1}.full.ll (in-memory FULL artifact)
inc_full_buf: bytes = stdlib.malloc(PBP_FULL_SIZE)
if inc_full_buf is None: VLogger.error("full.ll 缓冲区分配失败", "PhaseB+")
if inc_full_buf is None: VLogger.error("full.ll buffer allocation failed", "PhaseB+")
tr_mi.dump_ir(inc_full_buf, PBP_FULL_SIZE, mlirlite.OUTPUT_FULL)
inc_full_len: sizet = string.strlen(inc_full_buf)
inc_full_path: str = StubMergerCollect._sliced_path(temp_dir, td_len_mi, inc_sha1_mi, "full.ll")
@@ -1355,7 +1355,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
diag_text_ex = 1
df_diag_t.close()
stdlib.free(diag_text)
VLogger.error(f"读 full.ll 失败: {src_fp_mi} (full={diag_full_ex} stub={diag_stub_ex} text={diag_text_ex} is_decl={is_decl_mi})", "PhaseB+")
VLogger.error(f"failed to read full.ll: {src_fp_mi} (full={diag_full_ex} stub={diag_stub_ex} text={diag_text_ex} is_decl={is_decl_mi})", "PhaseB+")
if inc_combined_mi is not None: stdlib.free(inc_combined_mi)
stdlib.free(check_pat_mi)
if check_fd_mi is not None: stdlib.free(check_fd_mi)
@@ -1367,12 +1367,12 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
stdlib.free(inc_combined_mi)
if inc_cret_mi != 0:
VLogger.error(f"llc 编译失败,终止编译: {src_fp_mi}", "PhaseB+")
VLogger.error(f"llc compilation failed, aborting compilation: {src_fp_mi}", "PhaseB+")
sys.exit(1)
inc_compiled += 1
# Show the compiled includes file
VLogger.info(f"[includes {inc_compiled}] 编译完成: {src_fp_mi}", "PhaseB+")
VLogger.info(f"[includes {inc_compiled}] compilation complete: {src_fp_mi}", "PhaseB+")
# Add to obj_paths (sliced path, taken from includes_binary_dir)
ibd_len_mi2: sizet = string.strlen(includes_binary_dir)
@@ -1387,7 +1387,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
string.strcpy(obj_paths + obj_pos, inc_obj_sliced)
obj_pos += inc_obj_len
obj_paths[obj_pos] = '\0'
else: VLogger.error("includes .obj 路径缓冲区溢出,终止编译", "PhaseB+")
else: VLogger.error("includes .obj path buffer overflow, aborting compilation", "PhaseB+")
stdlib.free(inc_obj_sliced)
# Free this iteration's temporary memory
@@ -1402,7 +1402,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
if store_sha1_arr_mi is not None: stdlib.free(store_sha1_arr_mi)
if store_mod_arr_mi is not None: stdlib.free(store_mod_arr_mi)
VLogger.info(f"编译缺失 includes: {inc_compiled} 个", "PhaseB+")
VLogger.info(f"compiled missing includes: {inc_compiled} entries", "PhaseB+")
# Free app_deps_buf (fallback when the Phase B+ block did not run)
if app_deps_buf is not None:
@@ -1410,7 +1410,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
app_deps_buf = None
# === 4. Phase C: link all .obj -> .exe ===
log.banner("Phase C: 链接")
log.banner("Phase C: link")
od_len2: sizet = string.strlen(output_dir)
lo_len: sizet = string.strlen(linker_output)
@@ -1418,7 +1418,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
if exe_path is not None: viperlib.snprintf(exe_path, od_len2 + lo_len + 2, "%s/%s", output_dir, linker_output)
else:
exe_path = linker_output
VLogger.error("exe_path 分配失败,终止编译", "PhaseC")
VLogger.error("exe_path allocation failed, aborting compilation", "PhaseC")
# Build the final .obj path list: main_obj_path first, reachable .objs after
final_obj_paths: bytes = stdlib.malloc(OBJ_PATHS_SIZE + 512)
@@ -1490,7 +1490,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
string.strcpy(final_obj_paths + fop_pos, obj_path_pc)
fop_pos += op_len_p2
final_obj_paths[fop_pos] = '\0'
elif StubMergerIR.IsDeclOnlyModule(temp_dir, td_len_pc, rsha1_pc) != 1: VLogger.error(f"可达模块 .obj 缺失: {obj_path_pc}", "PhaseC")
elif StubMergerIR.IsDeclOnlyModule(temp_dir, td_len_pc, rsha1_pc) != 1: VLogger.error(f"reachable module .obj missing: {obj_path_pc}", "PhaseC")
stdlib.free(obj_path_pc)
final_obj_paths[fop_pos] = '\0'
@@ -1505,19 +1505,19 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
lret: int = BuildPipeline.link_objs_to_exe(final_obj_paths, obj_paths_len, linker_cmd, linker_flags, exe_path, None, linker_static_libs, linker_dynamic_libs)
if lret == 0:
VLogger.success(f"输出: {exe_path}", "PhaseC")
VLogger.success(f"output: {exe_path}", "PhaseC")
# --traceback: export <exe>.stackmap (skipped under --phase2command,
# where nothing was actually linked).
if BuildPipeline._traceback_mode != 0 and BuildPipeline._phase2_cmd_path is None: BuildPipeline.WriteStackMap(mb, exe_path)
if args.get_bool("run") and BuildPipeline._phase2_cmd_path is not None:
# phase2command mode: the exe is not linked yet (the command is in the bat); skip --run
VLogger.info("phase2command 模式:跳过 --run(exe 尚未链接)", "phase2cmd")
VLogger.info("phase2command mode: skipping --run (exe not linked yet)", "phase2cmd")
elif args.get_bool("run"):
VLogger.info(f"执行: {exe_path}", "run")
VLogger.info(f"run: {exe_path}", "run")
rp: subprocess.CompletedProcess* = subprocess.run(exe_path, False, False)
if rp is not None: VLogger.info(f"退出码: {rp.returncode}", "run")
if rp is not None: VLogger.info(f"exit code: {rp.returncode}", "run")
else:
VLogger.error("链接失败", "PhaseC")
VLogger.error("link failed", "PhaseC")
stdlib.free(entries)
stdlib.free(asm_entries)
return 1