Commit Graph
80 Commits
Author SHA1 Message Date
TermiNexus 2beb5d724f debug(traceback): worker stderr 输出并按 fail-fast 注入 main
并行 worker 的 _vlog 不汇总;改为 stderr 直接输出 enabled/InstallSymbol;main 找不到 Install 时明确报错。
2026-10-19 18:31:45 +08:00
TermiNexus 2051c2237e debug(traceback): 输出 InstallSymbol 赋值日志
每个文件打印 TracebackInstallSymbol,确认 main 模块是否拿到 3fe9e5dc351f3d26.Install。
2026-10-19 18:30:38 +08:00
TermiNexus 1f03fc1977 fix(traceback): 通过 .vpi 内容定位 traceback 模块 SHA1
sig_files/source_module_sig_files 的值是 .vpi 路径,basename 不含 traceback;改为读取前 512 字节匹配 'Module: traceback'。
2026-10-19 18:19:31 +08:00
TermiNexus 3da522a549 feat(traceback): 在 main 入口直接调用 Install()
MinGW 下 .ctors.65534 不被 CRT 执行,导致 Ctrl+C 无栈;改为编译期把 traceback_sha1.Install() 注入 main 入口,不依赖 .ctors/.init_array。
2026-10-19 17:31:12 +08:00
TermiNexus 0903491df3 fix(extern): 从 temp_dir 全量 .vpi 收集 extern
sig_files 键集合不含 include 模块(win32process 等);直接扫描 temp_dir/*.vpi 补全 export_extern_funcs,确保 ReadProcessMemory/StackWalk64 裸名。
2026-10-19 16:12:30 +08:00
TermiNexus 5a56198937 fix(extern): sig_files 不再按 App sha1_map 过滤
sha1_map 只含 App 源文件,按它过滤会丢弃 include 模块的 .vpi,导致 ReadProcessMemory/StackWalk64 等跨模块 extern 无法识别为裸名。
2026-10-19 15:45:58 +08:00
TermiNexus d312d97bca fix(extern): 初始化并同步 LlvmGen._export_extern_funcs
translator 可能尚无该属性;getattr 初始化后 clear+update,并把同一 dict 引用同步到 LlvmGen,确保 _mangle_func_name 看到跨模块 extern。
2026-10-19 15:29:38 +08:00
TermiNexus f91fb7dcca fix(extern): 原地更新 export_extern_funcs
translator 与 LlvmGen 共享同一 dict 引用;替换属性导致 Gen 仍指向旧空 dict。改为 clear+update,确保跨模块 extern 裸名生效。
2026-10-19 15:07:54 +08:00
TermiNexus ed76f77218 fix(extern): worker 合并 sig_files 补全 extern 集合
共享 export_extern_funcs 可能因 sig_files 过滤缺少 include 模块(win32process.ReadProcessMemory 等);worker 内从 sig_files 合并补全。
2026-10-19 14:55:48 +08:00
TermiNexus d4fc66fe3c fix(link): _merged.obj 存在时仍链接 include 的 .obj
_merged.obj 只合并非 include 模块;此前命中该分支后跳过 include 的 .obj,导致 argparse.print_help 等符号未链接进 exe。
2026-10-18 22:13:50 +08:00
TermiNexus de89b26bb7 fix(generic): 特化实参统一 mangling,失败立即终止
泛型实参在特化名/spec_key/type_map 中统一为 i32/指针形态,list[int].get 返回 i32;特化与翻译失败不再吞异常,include 缺失 .ll 直接终止。
2026-10-18 20:40:23 +08:00
TermiNexus 369afb5011 fix(generic): 恢复泛型特化发射主路径
补回 _specialize_generic_class 的 deepcopy/改名/字段与方法 T 替换/_EmitClassLlvm 发射逻辑,list[...].__len__ 与 get 正常生成;源码不再支持 CPtr[...],统一写 T*;编译错误只由主进程输出一次。
2026-10-18 19:50:32 +08:00
TermiNexus 109e71a477 refactor: 删除保留性兼容兜底,仅保留新语法主路径
移除 len(list) 直读 __count__、list 方法裸模板回退、旧 C* 文本标记兼容、旧联合兼容;vast 扩展过期直接报错,已重建 _parser.pyd。
2026-10-18 19:22:32 +08:00
TermiNexus 92e467ae4c fix(cli): 翻译失败只输出单行错误
worker 与 batch 异常不再打印 traceback.format_exc,避免与主进程汇总重复输出。
2026-10-18 19:09:51 +08:00
TermiNexus 0bded58999 refactor(type): 全面清理旧联合与 t/c 标记判断
审计并修复 23 个文件:类型注解中的 T1 | T2 一律 fail-fast;typedef/define/export/extern/static/const/volatile/attribute 优先读取 Markers/Attributes/ByteOrder/BitWidth;指针统一 CPtr/rawptr/CRef/CTempRef/CArrayPtr/CAutoPtr Subscript;stub 输出统一新语法。
2026-10-18 18:01:20 +08:00
TermiNexus f19d25782f refactor(tpc): 退役 t/c 模块并将定宽类型升级为内建
删除 lib/includes/t.py、c.py,类型模型迁入 lib/core/TypeSystem.py;移除 IsTModule/_T_MODULE_NAMES 等名称判据,语法糖与内部标记统一为裸名;builtins 的 sizet/intN/uintN/fast/least/uchar/short/long 等改为编译器内建短名。
2026-10-18 13:00:58 +08:00
TermiNexus 2b4679073e 维护基线 2026-10-17 23:27:00 +08:00
TermiNexus 15f77da9d3 chore: 改名同步 TransPyV -> ViperC(源码/配置内的纯替换) 2026-10-12 15:06:48 +08:00
TermiNexus d5b4e9a8b9 feat(cdef): define 常量带类型注册——按声明类型物化(DWORD(-11) → i32 位型)
方案 C:CDefine 不再只存无类型 int,而是把**声明整数类型**(位宽 + 符号性)一并登记,使用点按该类型物化,而不是按数值大小猜 i32/i64。链路:① SymbolUtils 新增 DefineIntSignednessOf / DefineDeclIntTypeFromAnnotation(由 T | t.CDefine 注解经符号表 typedef 链解析宽度与符号性)/ DefineDeclTypeValue(按声明类型把值归一成 (位宽, 位型值));② 同模块注册(LlvmGenerator._register_cdefine_constant)与跨模块提取(Phase2Translator._ExtractCDefineConstantsFromPyi,含 _shared_all_decl_types 的 pickle 传递)分别写 Gen._define_decl_types / translator._define_decl_types;③ 物化处(HandlesExpr 裸名两处、HandlesExprAttr._make_define_constant,及 from-import 注册)优先按声明类型出常量。这样 win32file 的 STD_*_HANDLE(DWORD(-10/-11/-12))解析为 i32 位型 0xFFFFFFF5 等,与 GetStdHandle(ULONG) 使用点一致。
2026-10-10 21:20:30 +08:00
TermiNexus e1a0fbf9c0 fix(cdef): 裸类型别名构造的 CDefine(如 DWORD(-11))也能提取进 _all_define_constants
根因:ConstEvaluator 只认 t.X(...) / from t import X 的类型构造,裸 typedef 别名(DWORD(-11))既不在 _t_module 也不在 mock ctx 的 _t_type_symbols → eval_full 返回 None → _ExtractCDefineConstantsFromPyi 整条丢弃该常量。win32file.vp 的 STD_INPUT/OUTPUT/ERROR_HANDLE 是全仓唯一用别名构造写的 define,正对应链接期三个 undefined symbol(模块属性访问 wf.STD_*_HANDLE 解析落空后退化成未定义全局)。补:eval_full 失败且值为单参 Call 时,回退求值其实参(CDefine 只要常量值,位型一致)。
2026-10-10 20:57:26 +08:00
TermiNexus 2514cee4c9 feat(lang): 同步 cif/celif/celse、值引用注解与 assert 改译 testcheck.check
vast 双路径(Python Parser/ExprParser 与 C 扩展 vast_c.c)新增 cif-celif-celse 语句族与 &T / &temp T 参数注解解析,_parser.pyd 已同步重编。HandlesIf 改认 @CIf 标记并做编译期常量求值,含运行时变量一律 fail-fast,同时删除 c.CIf 家族(c.py)。HandlesAssert 合成 testcheck.check(cond, None, msg) 调用,非字符串字面量消息报 TypeError。FuncGen 调用点按形参模式自动取址(ref 仅接受左值,右值 fail-fast;temp 物化临时栈),SymbolUtils/TypeAnnotationResolver 提供模式识别;t.py 删除 CNeedPtr。
2026-10-07 19:03:29 +08:00
TermiNexus 64f3d79b64 refactor(asan): ASan 运行库目录改为按 PATH 自动探测,去除本机硬编码路径
- _FindAsanRuntimeDir() 重写为 _FindAsanRuntime():遍历 PATH 定位

- clang64/bin 由前置改为追加到 PATH,避免裸名 clang++/llc/opt 被解析到 clang64 的 libc++ 版本

- 缺失 ASan 运行库时的报错文案改用占位符路径
2026-10-07 11:25:37 +08:00
TermiNexus 07ba7a4fe5 fix(codegen): 新规范迁移暴露的 4 类解析/代码生成缺陷
1) HandlesExpr._HandleConstantLlvm 引号判定用 AST lineno 直查磁盘行,漏了 _inject_auto_imports 注入行的偏移 N,取到错行;改用 _DiskLineno 还原,并把 col_offset(UTF-8 字节偏移)转字符偏移后再切片。2) Phase2Translator 新增 _DedupComdatDecls:_dso.ll 由各依赖片段拼接,每片段各带同名 comdat 声明,llc 报 redefinition of comdat;在 _AddWeakComdat 入口与两处落盘点去重。3) LLVMTypeMapper._resolve_name 补存储类/类型限定符标记分支(CExport/CExtern/CConst… 返回空串,由 _resolve_bitor 取另一侧真实类型),与 _resolve_attribute 同口径。4) 短别名(int/int64/sizet/char…)在 typedef 右值与类型转换调用处解析不到:SymbolTypeResolver._resolve_typedef_from_value 与 LLVMTypeMapper.resolve_typedef_value_type 的 ast.Name 分支补 builtins typedef / BuiltinTypeMap 兜底(BaseType 可能是 CType 实例,须取 type(x).__name__),HandlesExprCall 的类型转换求值补 _lookup_builtins_typedef 回退并保留双参数 CPtr 指针包装。验证:TransPyV 自举 includes 52/52 + App 76/76 全通过;TPV 顺序回归 77/77。
2026-10-05 09:25:02 +08:00
TermiNexus 5eef845e5b feat(struct): 嵌套 class/struct 发射为独立结构体 {外类}_{内类}
用户 2026-10-01 定:struct 内允许叠加 struct(与 class 内写 class 同口径)。
此前类体里的嵌套 ClassDef 在两个编译器里都被**静默丢弃**(字段遍历只认 AnnAssign、
方法遍历只认 FunctionDef),现按既有 REnum/union 嵌套命名先例发射为独立结构体。

命名与引用口径(双端同源):内层结构体 = {外类}_{内类}(多层递归 A_B_C);
引用两种写法都可用 —— 限定名 `外类.内类`、扁平名 `外类_内类`。

- HandlesClassDef.py:新增 _EmitNestedClassDefs(deepcopy + 改名后递归
  _EmitClassLlvm,deepcopy 避免污染源码 AST)+ _RegisterNestedClassName
  (SymbolTable 登记裸名/点号名/扁平名,对齐 REnum 变体的三键先例)。
- TypeAnnotationResolver.py:_from_node_attribute 增加嵌套分支 —— 限定名按**扁平名**
  取条目并让 Name/BaseType 都是扁平名(否则会拼出 t.CStruct(name='内类'),
  与声明端 `%"{sha}.外类_内类"` 不同源 ⇒ 布局分叉)。
- DeclarationGenerator.py:_get_type_str 支持 `外类.内类` → `%"{sha}.外类_内类"`;
  _generate_class_decl 递归产出嵌套类型的完整声明(否则跨模块只拿到 opaque,
  指针可用但字段偏移全失效)。
- Phase1Generator.py / Phase2Translator.py:跨模块注册表按扁平名登记嵌套类。
  Phase2Translator 原先用 ast.walk 把嵌套类按**短名**登记(占住平坦命名空间、
  与定义端不同源),改为递归扁平名。

实测(TPC 侧):嵌套类名字登记 {A_B, A_C, A_C_D};`x: A.B` → CTypeInfo(A_B);
声明端 `%"cafe.A" = type { i32, %"cafe.A_B" }` + `%"cafe.A_B" = type { i32 }`
+ 多层 `%"cafe.A_C_D" = type { i32 }`。

已知缺口(下一步):TransPyV 侧跨模块走 .vpi,VpiReader 只登记顶层类 ⇒ 跨模块引用
嵌套类型仍需在 VpiReader 侧补登记(同模块路径已通)。
2026-10-01 23:29:54 +08:00
TermiNexus f0e630457f chore(Phase2Translator): 自动并行度上限 8 worker,并精简并行提示文案
- 新增 _MAX_WORKERS=8(用户定:16 worker 太多,收益不足而内存压力翻倍);
  只约束自动取值,显式 TPC_JOBS=N 仍按硬件/文件数上限
- 并行相关的 RuntimeError 与日志精简为一行数字 + 出路,去掉"拒绝降级为串行 /
  串行会让编译时间从 ~400s 掉到 ~1180s 档"等长说明
2026-10-01 20:58:08 +08:00
TermiNexus 2ba38ae842 fix(Phase2Translator): 并行 worker 数不再按可用内存降级串行,改为"预分配校验 + 强制预留"(TPC_JOBS 可显式覆盖)
现象:同一条命令的编译时间在 ~400s 与 ~1180s 两档之间随机跳(PROF 目录当日
366s~2239s),用户反馈"耗时不降反增"。

根因(三份 profile 实证):
- 旧 _PickWorkerCount 用 `by_mem = max(1, avail_mb // per_worker_mb)` 按**可用**
  物理内存夹 worker 数:可用稍紧(<~2.8GB)就整体降为 1 ⇒ **静默退回串行**。
  其文档字符串自己就写着"76 文件逐个翻译 1180s,并行时 ~400s,波动 2-3 倍"。
- PROF/20260930_224857(438.40s):主进程 _translate_file **0 次**、
  StructGen/HandlesImports/HandlesClassDef **0 条记录**、总调用 7.28 亿
  ⇒ 翻译在 worker 进程里做完了(未 profile),主进程只在等 → **并行档**。
- PROF/20260930_231916(1048.55s)与 20260930_235354(1168.15s):
  _translate_file **75 次全在主进程**、_parallel_translate_worker/_batch 0 次、
  总调用 19.6 亿 ⇒ **串行档**(1180s 档)。
⇒ 拿 438s 当基线比 1168s 是"并行 vs 串行"。本次 A/B(同为串行、总调用
  19.616 亿 vs 19.631 亿)里各项优化均为正收益(_RewritePlain −5.0s、
  __exit__ −1.5s、re.match+_compile −1.3s、_RewriteQuoted −0.75s…);
  +12% 属机器波动(同一批 C 函数在调用次数不变下变慢 25%~73%)。

改动(口径由用户定:不降级 + 预分配内存避免波动 + 强制要求预留内存):
1. **不降级**:删掉"内存紧就退串行"。worker 数 = min(cpu_count, n_files),
   不再因可用内存抖动而变化。
2. **预分配校验 + 强制预留**:新增 _MemStatus()(读可用/总量)与
   _ProbeReserveMb()——按 base×per_worker_mb(翻译 350 / 编译 400)分块**真正
   提交**该预算(峰值=预算),成功后立即整体释放,紧接其后创建 worker 进程
   由 OS 复用这批页。等于先向 OS 确认"这笔预算确实拿得到",而不是只看含大量
   可回收页的 ullAvailPhys。
3. **fail-fast**:可用内存 < 预算、或预分配校验提交失败 ⇒ RuntimeError 报出
   "需要 X MB(N worker × M MB)/ 当前可用 Y MB / 总量 Z MB",由使用者释放内存
   或**显式** TPC_JOBS=N 重跑(N=1 即显式接受串行)。不再静默降级。
4. 新增 TPC_JOBS=N 显式覆盖(跳过校验,使用者自担内存风险)。
5. 每次决策都打日志:显式指定 / 读不到内存状态 / 预分配校验耗时 / 最终 workers。

已知取舍:内存确实不足时编译会**直接失败**(这是"不降级"的应有语义),出路是
释放内存或 TPC_JOBS=N;预分配校验会短暂占用峰值=预算的内存(约 1GB/秒级耗时)。

未动(下一步候选):串行档的真热点 StructGen._generate_structs(151s 自身 /
480s 累计,被调 36,389 次≈每类 22 次,O(类数²))。
2026-10-01 00:23:16 +08:00
TermiNexus b18d57fa38 perf(全仓库/正则): 字面量正则全部改模块级预编译常量(15 文件 / 115 个调用点)
背景(prof 基线 PROF/20260930_224857,主进程 433.69s):正则这一族合计 ≈45s ——
`re.match`(未预编译入口)被调 1970 万次 / 自身 10.70s、它内部 `Pattern.match` 占 8.06s,
`re._compile`(缓存查找)1975 万次 / 7.19s,`re._make`(真正编译)274 万次 / 7.62s
(说明 re 的 512 条缓存已被打穿,每 7 次调用就有 1 次重新编译)。
每次 `re.match(字面量, s)` 都要做"模式串哈希 → 缓存查找";预编译后这层全省掉。

改动:把 15 个文件里 97 处"字面量当模式"的 `re.match/search/sub/subn/fullmatch/
findall/finditer/split(...)` 提升为模块级 `_RE_*: re.Pattern[str] = re.compile(...)`,
共 115 个调用点(含同字面量跨处共用)。模式串与 flags 一字未改(flags 从调用处
搬进 re.compile,含原先以**位置参数**传入的 flags)。4 处拼接型动态模式按设计不动
(FuncGen 2 处、HandlesImports 2 处)。

核验(不靠肉眼):写了 AST 指纹对拍脚本,对每个文件取 HEAD 版与新工作区版,把
"旧 re.<m>(字面量, 其余实参, 关键字) + flags" 与 "新 _RE_X.<m>(实参, 关键字) +
常量 (模式, flags)" 做多重集比对 —— 15 个文件、115 个调用点全部逐一等价(0 失败),
另查无重名常量、无遗留字面量调用、GetDiagnostics 全空。
对拍过程中查出一处子代理抄写错误:`_RE_GLOBAL_DSOLOCAL_MULTILINE` 丢了行首锚 `^`
(原文注释明确"使用 ^ + MULTILINE 避免匹配字符串常量内容中的 define/declare"),已修正。

同轮经查证后**未做改动**的两项(避免把假热点当真事修):
- VLogger 的 `re.sub` 6.43s:profile 当时的 VLogger 版本里根本没有 `import re`
  (`git show 3658ab06:lib/core/VLogger.py` 已确认),属 C 帧错挂,不做。
- tokenize 274 万次:那是 `_generate_tokens_from_c_tokenizer` 的 **token 产出次数**
  (≈350 个整文件的词法量),SymbolUtils 的 4 个 tokenize 调用点 nc 仅数百,且已有
  `_EXPAND_CACHE` 去重;再加一层 token 缓存无收益,不做。
2026-09-30 23:47:36 +08:00
TermiNexus 4641777db1 perf(Phase2Translator/_split_ll): 括号计数走 C 级 str.count 快路径 + 调用点预筛
prof 基线(PROF/20260930_224857,主进程 433.69s)实测 _split_ll:
ncalls 53 / tottime 0.263s / cumtime 3.347s,其中
_count_braces_outside_strings 102,397 次 / tottime 1.999s / cumtime 2.769s
(占其 cumtime 的 83%);同函数内 strip 0.078s、startswith 0.084s 可忽略。

- _count_braces_outside_strings:行内不含 c" 时退化为 C 级
  str.count('{') - str.count('}'),不再逐字符跑 Python 循环。
  语义逐字等价:逐字符版唯一的额外分支就是"跳过 c" 字面量",而该分支
  只在出现子串 c" 时进入 ⇒ 无 c" 时等于纯括号计数。
- 两个调用点(in_define 函数体续行 / in_global_def 全局初始化器续行)加预筛
  `'{' in line or '}' in line`:无花括号的行必然返回 0,省掉一次函数调用。
- `stripped == '{'` 分支的 3 处 re.sub 字面量改用同模式串的预编译常量
  (_RE_DEFINE_PREFIX / _RE_DEFINE_LINKAGE / _RE_ALWAYSINLINE),与上面
  define 分支保持同一口径(模式串一字未改)。

顺带实测(供下一步参考):str.strip 179M 次 / 22.8s 是全流程最大项,但
cProfile 归属帧几乎全在 stdlib(re.match 5.25M、threading.wait 4.88M、
futures.as_completed 3.63M、json.load 0.66M、subprocess 各 ~0.4M);
本项目内只有 _extract_llvm_type_name 1.37M、_ParseDwarfLineRows 0.34M、
_split_ll 0.35M。
2026-09-30 23:18:29 +08:00
TermiNexus e292e2390d perf(Phase2Translator): 文本读缓存改按路径直存(零 stat),正确性靠写/删点显式失效
- _ReadTextCached 不再 os.stat:原实现每次调用都要 stat 判新旧,等于把省下的
  1.2 万次 open 换成 1.2 万次 stat(同为系统调用热点),命中路径仍有系统开销。
  改为 OrderedDict[路径 → 文本],命中零系统调用;newline 非 None 的逐字节读不进缓存。
- _InvalidateTextCache 改 O(1)(按路径直接 pop),并在 14 处文本写点 + 1 处 stale
  文件删除(os.remove)处调用 —— 读侧不再 stat 后,写/删点显式失效是唯一正确性保证。
- 失效为何必须显式(时间戳判据在同 tick 内会失效):_compile_one 前的 type 定义
  重排(行集合不变仅次序变,长度逐字节相同)、_recombine_ll 的旧 sha1 前缀替换
  (16 位 hex 等长替换)。
- 安全性前提:TPC 单次运行内被读文件内容不变。已核对:self.sig_files /
  self.stub_files 指向的都是 Phase1 产物(temp_dir / _GLOBAL_CACHE_DIR,Phase2
  期间不再改写);Phase2 自产的 .ll/.stub.ll/.deps.json/.vpi 每次写、删都走上面的失效。

基线 PROF/20260930_224857:_io.open 12,740 次/self 28.8s、read 11,902 次/30.4s、
TextIOWrapper.__exit__ 16.6s(128 个模块却上万次读,同一份产物被多阶段反复打开)。
2026-09-30 23:15:22 +08:00
TermiNexus 1748940d30 perf(Phase2Translator): 文本读全走 LRU 缓存 + 写侧显式失效,削重复文件 I/O
- 新增 _ReadTextCached:键 =(路径, st_mtime_ns, st_size),命中即免 open/read/close;
  按总字节预算(64MB)LRU 淘汰,单文件 >32MB 不入缓存;newline 非 None 时退化为直读。
- 21 处文本读点全部改走缓存(.vpi/.pyi 解析、.ll/.stub.ll/.text.ll、deps.json、
  doc.json、include 源、pyi 缓存预热等),json.load(f) 统一改 json.loads(_ReadTextCached(...))。
- 新增 _InvalidateTextCache:14 处本进程文本写点(原地改写/新落盘)后显式失效。
  Windows 文件时间为系统时钟派生,粒度约 15.6ms,"先读 → 同 tick 内原地改写 → 再读"
  且改写前后字节数相同时 (mtime, size) 键完全一致(实测两例:_compile_one 前的 type
  定义重排——行集合不变仅次序变;_recombine_ll 的旧 sha1 前缀替换——16 位 hex 等长替换),
  只靠 stat 判新旧会读到过期文本,故写侧必须显式失效。

基线 PROF/20260930_224857:_io.open 12,740 次/self 28.8s、read 11,902 次/30.4s、
TextIOWrapper.__exit__ 16.6s(128 个模块却上万次读,同一份产物被多阶段反复打开)。
2026-09-30 23:13:01 +08:00
TermiNexus 3658ab0637 perf(Phase2Translator/IRMetadataFix): 削逐行热点——跳过无谓 strip、预编译逐行正则、C 级数字扫描
prof 基线(PROF/20260930_224857/main_time.txt=438.40s,7.28 亿次调用)显示全流程最大的 Python 侧热点是逐行扫描产生的内置字符串/正则调用:str.startswith 2.372 亿次(32.1s)、str.strip 1.791 亿次(22.8s)、re.Pattern.match 3130 万次(11.4s)、re.match 模块级 1971 万次(10.7s)+re._compile 1975 万次(7.2s)。四处等价改动:
1) _split_ll 第二遍:in_define/in_global_def 续行(.ll 里占比最高)不再 str.strip——这两支只用行内容与花括号计数,前导/尾随空白不含花括号,_count_braces_outside_strings 结果逐字不变;
2) _split_ll 第一遍:加 "define" not in line 的 C 级快路径,绝大多数行连 strip 都不做;
3) Phase2Translator 逐行正则全部预编译为模块级 _RE_* 常量(11 处 re.match + 4 处 re.sub,模式串一字未改)——消掉每次调用的模式串哈希+缓存查找;
4) IRMetadataFix:_RewritePlain 的前导数字扫描由逐字符 Python 循环改为 str.lstrip 的 C 级扫描(该函数 prof 5.8M 次调用,是项目内 self time 最大者);SanitizeIRMetadata 的逐行 startswith("!") 改为 [:1] 索引切片。
离线自测:数字扫描与旧实现逐例一致;SanitizeIRMetadata 幂等性保持;_split_ll 冒烟输出与预期语义一致(global/多行初始化器/define→declare/constant/metadata 分流正确)。
2026-09-30 23:04:25 +08:00
TermiNexus 52ea21f082 fix(LlvmGenerator/Phase2Translator): 本模块声明的类钉死本模块 SHA1 前缀,消除同名类前缀漂移
类别:a(编译器缺陷)

现象(上一版 ab906f2c 之后的下一步):
  includes/ast/parser.vp:1774 `kw: rawptr[AST] = KwNode(ps.pool, None, val)` 报
  TypeError: Type of #1 arg mismatch:
    %"fef783da4d24330d.Keyword"* != %"8312ce86a2fd82c0.Keyword"*
  (llvmlite 该报文是「形参 != 实参」且下标 1-based ⇒ 实际就是**第 0 个形参 self**)

根因(磁盘实证,非推断):TransPyV/output/8312ce86a2fd82c0.stub.ll 内自相矛盾
  - `%"fef783da4d24330d.Keyword" = type {…}` 与 `%"8312ce86a2fd82c0.Keyword" = type {…}`
    两份同名类的 struct 同时存在;
  - `declare %"fef783da4d24330d.Keyword"* @"8312ce86a2fd82c0.Keyword.__new__"(%"8312ce86a2fd82c0.Keyword"* %self, …)`
  - `declare void @"8312ce86a2fd82c0.Keyword.__before_init__(%"fef783da4d24330d.Keyword"* %".1")`
  其中 8312ce86a2fd82c0 = includes/ast/astaux.vp、fef783da4d24330d = includes/ast/tokens.vp
  (TransPyV/temp/_sha1_map.txt),两者各自定义了一个 `class Keyword`。
  机制:`_build_struct_sha1_map` 建的是「**类名** → 定义模块 SHA1」,但去重判据写成
  `if sha1_key in struct_sha1_map` —— 用模块 SHA1 去查一张按类名建的表,判据恒假 ⇒
  实际是「后扫到的覆盖先扫到的」,同名类归属随 os.listdir 顺序漂移。定义端
  `StructGen._generate_structs` 在 `class_sha1_map` 为空时用此表取前缀,于是 astaux
  **自己**的 Keyword 被命名成 `fef783da4d24330d.Keyword`(另一个模块的 sha1);而按本
  模块前缀另建的 `8312ce86a2fd82c0.Keyword` 被 `__new__`/`__init__` 当作 self,
  `HandlesClassDef._EmitNewFunctionLlvm` 生成的 `__before_init__` 却取
  `Gen.structs['Keyword']`(错名前缀那份)⇒ 声明与调用点分叉。

修复(两点,均为正向事实判据):
  1) LlvmGenerator(Gen 构造处,**先于** setup_from_symbol_table 内部的 _generate_structs):
     把本文件顶层 ClassDef 名单钉死为本模块 SHA1,写进 Gen.class_sha1_map(其优先级高于
     _struct_sha1_map)。"本文件声明的类"就在本次要翻译的 AST 里;class_sha1_map 是 Gen
     实例级、每模块重建 ⇒ 非本模块声明的类不受影响。
  2) Phase2Translator._build_struct_sha1_map:去重判据改为**类名**(先扫到的为准),并另按
     模块 SHA1 去重"同一模块文件被两处目录重复解析"——归属不再随 listdir 顺序漂移。

未放宽任何 fail-fast;未改动 __new__/__before_init__ 的调用点。
2026-09-30 18:12:24 +08:00
TermiNexus 030d0fd149 feat(tpc): PROF 输出按编译启动时间分目录,不再清理旧目录
需求(2026-09-29):PROF 按启动时间分组新建文件夹生成,不清理旧的 PROF。

改动:
- Phase2Translator:新增 `StartProfRun()`(在 PROF 根目录下按 `YYYYmmdd_HHMMSS`
  新建本次编译专属子目录,同名补 `_N`;失败回落 PROF 根目录)与
  `GetProfRunDir()`(主进程用已选定目录,worker 用继承的环境变量
  `TPC_PROF_RUN_DIR` 复用同一目录)。原 `_CleanProfFiles()` 删除——它的语义
  是"每次编译删掉 PROF 下全部 *.prof",与"保留历次"直接冲突。
- worker_prof_*.prof 落 `PROF/<启动时间>/`。
- main_prof_{pid}.prof 与 main_time.txt 同样落该目录(Projectrans.py)。
- Config.main 启动时调用 StartProfRun 并打印本次输出目录。

子目录名经环境变量传子进程:ProcessPoolExecutor 用 spawn 起新解释器,进程内
模块态不共享,环境变量随进程继承 ⇒ 同一次编译的所有 worker 落在同一目录,
不会各建各的。
2026-09-29 16:26:44 +08:00
TermiNexus 4fc067e3bf perf(tpc): 按 profile 消掉两处主热点(stub 索引每 miss 重扫目录 / vtable 每类全扫函数)
依据 PROF/main_prof_10152.prof(main_time.txt: 失败 2362.46s,72 个文件全部走
顺序 _translate_file,tottime 合计 2288.9s):

  tottime      calls   函数
  491.7s      723782   nt._path_isdir            ← 总时长 21%
  268.6s       15762   StructGen._generate_structs
  182.9s      479819   nt.listdir                ← 8%
  87.2s    657640754   builtins.isinstance
  78.6s     41514263   ntpath.join
  75.9s     83047878   ntpath.splitroot
  75.7s         1631   StructGen._create_Vtable_globals   (46ms/次)
  66.6s       238007   HandlesImports._build_stub_index
  63.0s    317868145   str.startswith
  38.4s    175751378   str.endswith
  ...
  ── 仅 isdir + listdir + join/splitroot/endswith ≈ 850s ≈ 总时长的 37%

一、_build_stub_index 的零 syscall 快路径(HandlesImports + Phase2Translator)
    它由 _TryLoadStructFromStub 的**每次索引 miss** 调用(238k 次),每次都要
    2× os.path.isdir + 2× os.listdir + 全量 endswith/join 重拼文件集合 ——
    这正是上面 isdir 723k / listdir 479k 的主要来源。
    改为:写入方显式置脏(ImportHandle.MarkStubSetDirty),未置脏时直接返回。
    置脏点:① 每个文件翻译入口(_translate_file,顺序与 worker 模式皆按文件粒度
    覆盖)② 写 temp/<sha1>.stub.ll 之后 ③ 注册 include 写 output/<sha1>.stub.ll
    之后。索引建完即清脏。语义:增量重扫、output 覆盖 temp 全部不变,只是把
    "集合是否变化"由目录扫描改为写入方免费告知(编译进程内 stub 只增不改)。

二、_create_Vtable_globals 的 O(类数 × 函数数) → O(函数数)
    原实现对每个候选类都遍历一遍 self.functions 找 "{ClassName}." 前缀且非
    declare 的函数;改为一次性预计算"非 declare 函数名的全部点分前缀"集合,
    每类只做一次集合查询。判据等价(startswith(prefix) ⟺ prefix 在点分前缀集)。

重编后请再抓一次 profile 复核收益;下一梯队热点是 _generate_structs
(268.6s tottime / 35k 次调用)与 _parse_llvm_type(3.5M 次调用)。
2026-09-29 14:36:57 +08:00
TermiNexus 74ccd31c19 fix(tpc): 裸名解析处理同名冲突 + 声明阶段多轮,消除跨模块类型解析残余失败
承接上一条(两阶段加载)后仍有 11 个模块签名解析失败,两类根因:

① 同名冲突:平坦符号表(无前缀)一个名字只能留一个条目,不同模块可用不同类型

   种类声明同名符号,且谁后插入谁占住裸名 —— stdint.vp:9 `INT: t.CTypedef = t.CInt`

   与 argparse.vp:33 `define INT = 1`(sig: `INT: t.CDefine = 1`)撞名 ⇒ posix 的

   `oflag: INT` 解析失败;llvmlite/__function.vp:77 `class Function` 与

   ast/tokens.vp:95 枚举成员 `Function: t.State` 撞名 ⇒ 5 个模块的 `func: rawptr[Function]`

   失败;ast/exprs.vp:18 `class Name` 同型(ast/parser.vp)。

   修法:_resolve_name 在平坦条目不是 typedef/类/枚举时,跨 _namespaces 找回同名

   typedef / 类 / 枚举(注解位置只可能是这三种)。

② 声明阶段的顺序依赖:typedef 右侧可引用其它模块的 typedef(win32base 的

   `HANDLE: t.CTypedef = VOIDPTR`,VOIDPTR 在 stdint)⇒ 单轮仍与顺序有关。

   修法:Phase2Translator 声明阶段多轮(未解析 typedef 的模块下一轮重 stage,

   无进展即停,_CountUnresolvedTypedefs 判定),签名阶段仍是全部声明入表后统一跑。

③ 顺带修:两阶段是两个 TypeResolver 实例,签名阶段也须注入本模块类型名

   (TypeResolver.inject_known_type_names 供两阶段共用)。

验证(TransPyV 全量 127 个 sig 模拟 ①→②):commit 失败数 11 → 0;

posix.open ret=i32 params=[i8*,i32]、read ret=i64 params=[i32,i8*,i64] 解析正确。
2026-09-28 18:25:57 +08:00
TermiNexus 05f4adb233 fix(tpc): 符号表加载改两阶段,消除模块加载顺序依赖
a 类(编译器缺陷/机制漏豁免):签名解析要通过符号表查其它模块的类型名

(from X import * 的裸名如 posix 的 INT;跨模块类如 rawptr[Constant]),而加载器

按 os.listdir 字典序逐个「提取→解析→插入」⇒ posix 排在 stdint 之前时 INT 查不到。

旧口径静默降级 i32 掩盖了它,fail-fast 后暴露。

修法:加载拆两趟 —— ① StageModuleSymbols:提取 + 只解析/插入声明

(类/枚举/typedef/匿名类型)② CommitModuleSymbols:解析签名 + 插入函数/define +

命名空间重导出。批量加载器(Phase2Translator._build_shared_symbol_data)先收集全部

模块、跑完所有①再统一跑② ⇒ 任一模块的签名都能看到全部声明,与顺序无关。

单模块 LoadModuleSymbols 语义不变(①紧接②)。

验证(探针,posix 先 stage、stdint 后 stage 的“坏顺序”):

  posix.open ret=i32 params=[i8*,i32](INT=stdint 的 typedef);read ret=i64 params=[i32,i8*,i64];

  同样顺序走旧的单模块路径则 RAISE(对照组)。
2026-09-28 18:14:45 +08:00
TermiNexus 96d8e13c5a fix(err): 成员查找 fail-fast 改用可读异常 + 报错语句栈不再重复长报文
① 新增 MemberLookupFailFast(KeyError):_get_member_offset 的 fail-fast 原用 raise KeyError(消息),而 KeyError.__str__ = repr(消息) ⇒ 报文被加上引号、换行被转义成字面 \n(含"源代码 (文件, 行号): 行号 | 源码"的多行报文彻底不可读)。新类继承 KeyError(保住 HandlesExprAttr 两处 except KeyError 兜底点)但 __str__ 返回裸消息。

② 顺带删除该处的 traceback.print_stack 调试转储(遗留调试语句,输出噪音)。

③ Phase2Translator 语句栈渲染:原格式把同一条异常消息贴在每一层帧后面 ⇒ 一句话被打印 N 次;改为外层→内层的调用路径逐帧印位置、异常消息只在最内层印一次。
2026-09-28 16:48:58 +08:00
TermiNexus c83fc752c6 fix(sizeof,parse): 尺寸链去静默兜底,解析报错可定位,补 C 解析器缺口
StructGen: _get_struct_size/_get_align 去 opaque→8/ptr_size、未知→4 兜底并补 VectorType;LiteralStructType.fields→.elements(llvmlite 无 fields,必然 AttributeError)

HandlesClassDef: union 载荷尺寸经不存在的 ir.DataLayout 静默退化成 8 字节;HandlesExprCall/ExprBuiltin: sizeof 各静默 0 分支改 fail-fast,数组元素 sizeof 统一走 _get_struct_size

HandlesFor: nonlocal struct/array 堆分配不再退化成 malloc(8)

vast/vast_c: 补一元 * / & 解析,装饰器后 extern/export 分派修复,C 侧报错带 token 与行号;StubGen.Converter: 宏展开丢尾随空行修 .pyi 缩进非法

其余为 rawptr 迁移 / fail-fast 口径等历史改动一并入库
2026-09-26 19:27:22 +08:00
TermiNexus 53a345a59a feat(ctrlc-stack): 新增 --ctrlc-stack 编译期开关(按键打印 .vp 调用栈)
Config/Phase2Translator: --ctrlc-stack 自动开 DWARF;缓存失效;链接后 llvm-dwarfdump 导出 <exe>.stackmap(逐条归一化为 RVA——旧的整体判据会被 lld 去重产生的 0 地址行带偏,导致全表差一个 ImageBase);链接参数追加 -ldbghelp;include 也生成 DWARF 并隔离到 includes.binary_ctrlcstack(_asan);llc 追加 -frame-pointer=all。

BaseGen._RegisterDbgStackInstallViaCtor + StructGen.finalize 钩子: 注入 llvm.global_ctors(priority 65535) 在启动期调用 dbgstack.Install(),写盘前核对 global_ctors 否则 fail-fast。

FuncGen._adjust_args: 指针到整数的 fail-fast 补逐项类型诊断。
2026-09-25 13:11:13 +08:00
TermiNexus 6af2dea917 修复了一些问题 2026-09-17 14:52:37 +08:00
TermiNexus 43e7cdd2cc feat(tpc): 函数级 DWARF 调试元数据 + 合并 IR 元数据规整
DWARF: 新增 LLVMCG/DebugInfo.py(DebugInfoEmitter)——每模块一个 DIFile/DICompileUnit、每个 define 一个 DISubprogram 并在函数入口首条指令挂 DILocation,模块落盘前补 !llvm.dbg.cu 与 !llvm.module.flags(Debug Info Version 3)。开关:新增 --dwarf(默认关闭),dwarf_enabled = args.dwarf or args.asan,沿 Phase2Translator → _translate_file → Translator.DwarfEnabled → LlvmCodeGenerator.dwarf_enabled 下传。define 之外的 declare 不挂。include 库刻意不给 DI(其 .obj 进跨项目共享的 includes.binary 缓存,注入会污染缓存)

关键约束(踩过的坑,已在探针中实测):DICompileUnit 与 DISubprogram 必须 is_distinct=True;emissionKind 用 FullDebug 而非 Full;spFlags 用 DISPFlagDefinition;DISubprogram 必带 type=DISubroutineType,缺了 llc 会崩 0xC0000005;模块级必须有 !llvm.dbg.cu + Debug Info Version 3

IRMetadataFix: 新增 SanitizeIRMetadata,在 _llc_compile 入口统一规整 IR——BuildCombinedIR 是多模块 IR 文本直接拼接,--dwarf 后每个片段自带 !0..!N 与两份命名元数据,22383 行样本里 !0/!1 各重复 67 次导致 opt/llc 报 Metadata id is already used。实现:按块重编号 !N 并改写 !dbg/命名元数据引用、!llvm.dbg.cu 全量聚合、其它命名元数据全同去重;无元数据文件逐字节 no-op、幂等(1.7MB/0.30s)

顺带修 _AddWeakComdat:函数头正则跨行吞换行,--dwarf 把 !dbg 放到 define 行尾后 comdat 被插到 ! 附着之后,llc 报 expected { in function body(现场 52 个 define 中 36 个中招);改为插在附着之前,无 !dbg 时产物逐字节不变

已自证:llc/opt RC=0、行表含 .vp 文件名与行号、opt -passes=forceattrs,asan 与 llvm-link/always-inline 均不丢 !dbg。已知待办:llc 现报 inlinable function call ... must have a !dbg location ⇒ 需补语句/调用点级 !dbg(否则警告 ignoring invalid debug info 会丢弃整份 DI)
2026-09-16 19:29:51 +08:00
TermiNexus d0f69eb90a fix(phase2): 热点插桩时强制失效 include 模块的旧目标文件
include 模块同样会被插桩(hotspot_sites_file 经 worker 透传),但 _compile_include_py_files 对预编译缓存 precompiled_obj 与 output 缓存 obj_path 存在即跳过,且没有 hotspot_on 强制重译分支,导致复用上一次未插桩的 .obj,使 include 内热点模块(MemBuddy.alloc、ast.tokens、ast.base、llvmlite 窥孔等)的计数/计时 IR 静默缺失、其耗时被计入调用者 self。

启用热点时先删除这两类旧 .obj(沿用 App 主循环同一写法:用未缓存的 os.path.isfile 判定,避免把已存在写入只写存在性缓存后被删除导致后续判定读到过期值而漏编),删除失败仅告警不中断、不静默吞错。

include 路径本就每次无条件重译 .ll(无跳过逻辑),故只需处理 .obj;.dso.ll/.stub.ll/.text.ll/.vpi/.deps.json 经核对无同类隐患。未启用热点时行为逐字节不变。
2026-09-14 14:42:01 +08:00
TermiNexus ac77287f20 fix(phase2): 热点插桩强制重译时同步失效旧目标文件
启用 --hotspot-sites 时会跳过 .ll 与 .stub.ll+.text.ll 两个缓存分支而强制重译,但既有的 {sha1}.obj 未被清理:_compile_ll_files 的三处跳过判定只按 obj 是否存在(收集阶段、_compile_one、need_compile 二次过滤),会复用上一次未插桩的目标文件,导致部分模块的热点计时/计数静默缺失、其耗时被计入调用者 self。

在 App 源文件主循环内,hotspot_on 为真时先删除对应 {sha1}.obj;用未缓存的 os.path.isfile 判定而非 _CachedIsFile,避免把已存在写入只写存在性缓存后被删除、使后续三处判定读到过期值而漏编;删除失败仅告警不中断、不静默吞错。

该循环遍历全部可达源文件且本轮所有文件都进入 need_translate(串行与批处理共享同一列表),故覆盖本轮全部被强制重译的模块。未启用时整块被跳过,行为逐字节不变。
2026-09-14 14:40:30 +08:00
TermiNexus fc5a6134ca feat(hotspot)!: 下线 --func-hotspot 并为 --hotspot-sites 增加每函数计时器
移除 --func-hotspot 全链路(CLI 解析与绑定、Config argparse 与注入、Translator 属性、LlvmGenerator 收口与透传、BaseGen 专属实现、Phase2Translator 透传、帮助文本);全仓已无 FuncHotspot/func-hotspot 残留引用。

通用截断逻辑保留并改名为 _InstallHotspotTruncate,供 --hotspot-sites 使用(分别清空 hotspot_sites.txt 与 hotspot_time.txt);ctor 注册改为直接新建并接收处理器列表(sites 与 time 两个处理器在同一 ctor 内注册)。

--hotspot-sites 现附带每函数计时:入口读一次 @llvm.readcyclecounter() 存起始 tick,在函数所有 ret 之前再读一次并累加差值到 internal i64 __fhs_ftime_N,同时 __fhs_ftime_enter_N 记录进入次数;单位为 tick(x86_64 即 CPU 周期),语义为 inclusive(包含内层调用)。

计时结果按耗时降序写入同目录 hotspot_time.txt(# time <module_sha1> 分段 + 函数名/累计 tick/进入次数),不改变 hotspot_sites.txt 的既有格式与字段。不传 --hotspot-sites 时零影响。
2026-09-14 13:10:06 +08:00
TermiNexus 2021c0edb7 feat(hotspot): 新增 --hotspot-sites 逐调用点计数并带源码 file:line
为匹配白名单的直接 call 点插桩 internal i64 __fhs_site_<K>(call 前 +1),计数天然包含循环内放大的真实执行次数,可把热点定位到具体调用点。

新增 --hotspot-callees 覆盖白名单、--hotspot-sites-all 全量;按去 16 位 sha1 前缀后的基名匹配。Ctrl+C 落盘独立文件 hotspot_sites.txt(# sites 分段,含调用方/被调方/计数/源码位置),按计数降序;不破坏既有 hotspot.txt 格式。

在 AST->IR 生成期由 _HotspotSiteBuilder 为每条新生成的 call 指令轻量挂载 _src_file/_src_line,插桩时作为可选尾字段输出;取不到则留空不伪造。开关关闭时改用原生 ir.IRBuilder,零影响。
2026-09-13 23:29:32 +08:00
TermiNexus 5680592f37 feat(tpc): 新增 --func-hotspot 函数热点统计,Ctrl+C 时落盘
启用后为每个函数定义生成 internal i64 计数器并在入口递增;各模块 Ctrl+C 处理器以追加方式写最终文件(含 main 的模块启动时清空一次),经 llvm.global_ctors 自注册;未传参数时零影响。
2026-09-13 17:50:01 +08:00
TermiNexus cc2f5d34eb feat(tpc): includes 只认项目配置;ASan 改为显式链接 clang64 运行库
includes 目录不再回退仓库硬编码 TransPyCRoot/includes:Phase2Translator 新增 PROJECT_INCLUDE_DIRS(INCLUDE_DIRS 置空以兼容旧引用),Config.py 从 project.vpj 的 includes 同步;HandlesImports 的模块搜索、TypeAnnotationResolver._builtins_include_dirs 一律只读项目目录。

ASan:mingw64 clang 没有 x86_64-w64-windows-gnu 目标的 ASan runtime,新增 _FindAsanRuntimeDir 定位 clang64 运行库并按 -l: 显式链接(asan_dynamic-x86_64.dll.a + runtime_thunk),定位失败即报错退出。HandlesExprFormat:i32 不再写 'l' 长度修饰符(LP64 下 printf 会按 64 位读实参)。
2026-09-11 22:47:21 +08:00
TermiNexus b02bedae9a fix(link): 为 weak/linkonce 函数与全局补 COMDAT,修复 COFF 下跨模块重复符号
COFF 下无 COMDAT 的 weak/linkonce 定义会被 llc 展开成强符号 .weak.<name>.default(weak external 实现副本)。rowptr 新一代 includes 让泛型特化(list[rowptr[AST]].__before_init__)与跨模块共享常量(PYI_BUF_SIZE)在多个模块重复定义 → ld.lld duplicate symbol。

在 Phase2Translator 三处 dso_local 后处理簇后调用 _AddWeakComdat:函数定义补 ' comdat'、全局补 ', comdat'(置于 align/section 前),并在模块头加 comdat any 声明。llc/ld.lld 端到端已验证(双 obj 同符号链接无重复报错)。
2026-09-05 12:02:48 +08:00
TermiNexus 49631ccf9b feat(linker): TPC 支持 project.vpj linker.dynamic_libs 动态链接 .dll/.so(解析为绝对路径,作为链接输入文件) 2026-09-01 22:01:18 +08:00
TermiNexus 5ba5fb5ff3 fix(linker): include 模块自动扫描支持 .no_autobuild 标记与构建目录排除
- _scan_dir_for_libs:模块目录含 .no_autobuild 时跳过 C 源编译与库文件自动发现

- 递归时跳过 build/cmake-build*/.git/__pycache__/. 开头目录

- 防止 fltk 等内嵌完整第三方源码的 include 模块被误编译
2026-09-01 20:16:34 +08:00