Commit Graph
30 Commits
Author SHA1 Message Date
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 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 2e7bac1816 fix(HandlesImports/HandlesClassDef): 剥掉"祖先块前缀的陈旧半截副本",消除成员表重名条目
类别:a(编译器缺陷)

现场(用户构建日志):
  WARN[StructGen]: [结构体布局] 类 'ClassGenDef' 的成员表含**重名条目**:
  表 50 项 / 去重 42 项,现有 body 42 槽恰等于「去重名数 + vtable」

判据(磁盘实证 + 结构推导,非猜测):
  · ClassGenDef 自身 **0** 个字段(全是方法),父类 ClassGenBase 恰 **42** 个字段;
    temp / output 两份 stub 的 ClassGenDef body 都是 **42** 槽,且槽序 = ClassGenBase
    的字段声明序 ⇒ body 正确、**表多出 8 条**(50 = 42 + 8)。
  · ClassGenBase 自己的 Phase-1 stub 就是 42 槽(不是 8)⇒ 多出的 8 条并非"父类 stub 短",
    只能来自**内存里父类表尚未收集完就被展平**(写入了半截祖先块 Block[:k],k=8)——
    正是 PrependInheritedBlock 文档里写明的"已知残留"。
  · 重名条目不是布局槽位(见 FuncGen._get_member_offset 的重名段),却让
    _member_base 的 _Delta = 42 - 50 = -8 走进类型校准,并让回退路径的
    base + 表内下标 整体偏斜(同族现场:MatchCase / TypeGenResolve 的偏移算错)。

修复(判据全部为正向事实,且与插入方向无关):
  · 新增 NormalizeAncestralCopies(Table, Block, InsertAtHead) -> (新表, 是否已含完整块):
      ① 完整块在**头**、尾部挂前缀副本 ⇒ 剥尾(副本是被后来的整块前置推过去的);
      ② 完整块在**尾**、头部挂前缀副本 ⇒ 剥头(副本先写入、整块随后追加);
      ③ 完整块尚未出现 ⇒ 剥插入边(仅当这些名字已另有正本,见下)。
    剥掉的每一项在(表里已有的 / 随后插入的)完整块里同名同型 ⇒ **不丢布局信息**,
    与 PrependInheritedBlock 性质①"不删块内条目"不冲突(剥的是副本)。
    第一版曾"两端都剥",离线冒烟立刻暴露会把**正本**当副本剥掉([A,B,C,D,A,B] →
    [C,D,A,B],再前置整块反而多出 C,D 两份)⇒ 改为先定位完整块在哪一端、再剥另一端。
  · TrimStalePrefixCopy 判据:插入边连续游程与 Block[:j] 逐项同 name 同 IR 类型,
    1 ≤ j < len(Block),且这 j 个名字在表里**各另有一份**(计数 ≥ 2)——最后一条把
    "子类自有字段恰好与祖先块前缀同名同型"的误判排除。
  · 两个插入点(HandlesImports.PrependInheritedBlock 前置 / HandlesClassDef 展平追加)
    统一走该函数,判据对称;真正剥除时各留一条 WARN(含条数与前后表长)。
  · PrependInheritedBlock 原有两条幂等提前返回("表头等于块" / InheritedBlockAlreadyPresent)
    合并为 _HasFull 一条(语义等价),并更新其"已知残留"段落。
  · StructGen 的重名 WARN 追加**逐条明细**(名@表内下标):单看"表 50 / 去重 42"无法区分
    "祖先块前缀的陈旧副本"与"两条收集路径各登记一次"(两种成因修法完全不同)。

核验:py_compile + compileall 全 lib 通过;离线冒烟(调用真实函数,8 组用例 × 两个插入方向):
①/② 均 6→4 正确剥除,"无重复 / 带自有字段 / 类型不同 / 无正本 / 空表"均不改动。
未放宽任何 fail-fast。
2026-09-30 18:41:24 +08:00
TermiNexus 3efe83f1ea fix(编译期名称解析): 修 StructGen 缺 _vlog 导入与 HandlesImports 的 node/Node 误用
两处均为 a(编译器缺陷),且都被 HandlesImports.py:1055 的
`except Exception -> WARN 忽略异常` 吞掉,表现为模块声明静默退化
(本次现场:VpiReader.vp 阶段 WARN + 结构体声明路径中断)。非源码违规、
非机制漏豁免。

1) lib/core/LLVMCG/StructGen.py
   - `_generate_structs` 的「成员表含重名条目、body 已是权威布局」提示调用
     `_vlog().warning(...)`,但本文件从未定义/导入 `_vlog` ⇒ NameError。
     该分支一旦命中(astaux.ExceptHandler 一类),`_generate_structs` 整体中断。
   - 补 `from lib.core.VLogger import get_logger as _vlog`,与同目录
     FuncGen.py / TypeConvert.py 同口径。

2) lib/core/Handles/HandlesImports.py:1317
   - `_EmitExternalGlobalDeclLlvm` 形参名为 `Node`,该 elif 分支误写成小写 `node`
     ⇒ 只要传入的是**非带注解**的跨模块全局量(`ast.Assign`)就恒抛 NameError,
     分支整体不可达:外部全局变量从未声明 external,消费侧表现为
     unknown global / 类型占位。
   - 判据改回 `isinstance(Node, ast.Assign)`。

静态核验:对 TransPyC 全部 .py 做 symtable 未定义名扫描,结果 0 条;
`python -m py_compile` 通过。
2026-09-30 14:55:40 +08:00
TermiNexus bd92dfdadf fix(LLVMCG): 成员表重名/联合类型字段不再打断布局配对,ExceptHandler.name 取到槽 9
类别:a 编译器缺陷(TPC 侧)

现象:翻译 TransPyV/App/lib/core/Gen/AsyncGenMixin.vp 时 `self._add_local(trans, hd.name)`
(:667)的第 2 个实参求值失败并终止编译。基址诊断给出的事实链:类 ExceptHandler
(%"831ce86a2fd82c0..." 即 astaux.vp)成员表 10 项 =
['parent','pool','lineno','col_offset','end_lineno','end_col_offset','children','children','type','name']
('children' 重复一次),struct body 10 槽 =
[i8*, AST*, MemManager*, i32×4, list*, AST*(type), i8*(name)],字段 'name' 计算偏移 = 10
⇒ 越界(正确槽 = 9)。

判据与修法(两条,均已写进代码注释与告警报文):
1) 布局配对断链:成员表里 'children' 的声明类型是**联合类型折叠后的通用指针**
   (`list[rawptr[AST]] | t.CPtr` ⇒ i8*),而 body 槽是具体指针(%"...list[...]"*)⇒
   原配对只比 `is` / `str()`,在这一条断链,其后所有条目不再消费槽位 ⇒ 目标字段退化为
   "base + 表内下标"('name' ⇒ 1 + 9 = 10)。修:两侧都是指针时复用存储/建 body 侧同一
   判据 `_MemberSlotAccepts`(通用指针 i8* 放行、具体指针按被指类型),标量仍走严格比较
   ——不引入新的过配对。修后 type = 8、name = 9 ✓。
2) 重名条目使长度判据失真:`_expected_count_pre = len(class_members) + vtable` 把重复项
   也算一槽 ⇒ 误判"body 不完整"而用**带重复项的表**重建 body(本模块布局比其它模块多一
   槽 ⇒ 跨模块 ABI 撕裂)。新增判据:表有重名**且** body 槽数恰等于「去重名数 + vtable」
   ⇒ 认定 body 已是权威布局(来自定义模块 stub),只报事实、不重建。
   另在"未被 body 配对命中"的回退路径上,把"成员表含重名条目 + 表项数/去重项数 + 该
   返回值取自回退路径可能失准"作为事实告警报出(每类一次)。

验证:冒烟复刻本现场(表含重名 'children' 且其类型为联合 i8*;body 10 槽)——逐字段
1..9 全部命中(parent=1 … children=7、type=8、name=9),无重名的自洽表行为不变;
FuncGen / StructGen 两文件 py_compile 通过。
2026-09-29 23:48:52 +08:00
TermiNexus b06dac1d6b fix(LLVMCG): 字段偏移改用「冻结表有效性锚点 + 尾部对齐」,AnnAssign.value 不再读到 i32
类别:a 编译器缺陷(TPC 侧)

现象:编译 TranslatorGenMixin.vp 时 fail-fast —— getelementptr(GEP) 的指针操作数不是
指针类型:操作数类型=i32、操作数名='value.9'、GEP 名='vtable_slot_AST'。即
`aa.value.kind()`(TranslatorGenMixin.vp:788)的接收者读成了整数:AnnAssign.value 落到
槽 3(lineno, i32),随后虚分派取 vtable 时把 i32 当指针用。

根因(两条独立判据,均写进代码注释与告警报文):
1) 冻结表陈旧:StructGen 建 body 时把「成员名→槽位」挂在 struct 实例上,而 body 之后会被
   「不完整即清除」(StructGen 预检 existing_st.elements = None)与 stub 加载
   (HandlesImports._TryLoadStructFromStub 的 set_body)清空后重设,重设路径不更新该表
   ⇒ 陈旧表按旧布局取槽(AnnAssign 在「自有 4 字段 + vtable」时冻结 {'value': 3},body
   被重设为 12 槽正确布局后仍取槽 3)。修:建表时同时记下 body 对象身份
   (_tpc_member_body,set_body 每次赋新 tuple),查询侧用 `is` 判同一性,失效即不再采用。
2) 未展平成员表:表只含自有字段(父类前缀未补齐)时,「base(0/1) + 表内下标」会把整类字段
   整体前移(target/annotation 落到指针槽 ⇒ 类型巧合、静默读错对象;value 落到 i32 槽 ⇒
   本次崩溃)。修:表短于 body 字段区时按**尾部对齐**取槽(展平规则 = 父类前缀 + 自有字段
   ⇒ 自有字段恒为 body 尾部 len(表) 项),并用 _MemberSlotAccepts 逐项类型比对自校验;
   表自洽(长度 == 字段区)时本路径不介入 ⇒ 对既有正确行为零变化;REnum 是 max-variant
   镜像布局,明确排除。命中时每类留一次告警,报出表长/字段区/取槽结果。

验证:8 例冒烟(冻结表新鲜;陈旧×展平表 / ×自有表;无冻结×自有表 / ×展平表;无 vtable;
整类逐字段 = 8/9/10/11;尾部类型不符时落回旧路径)全部通过;两文件 py_compile 通过。
2026-09-29 23:18:37 +08:00
TermiNexus f28ff1b414 fix(tpc): 字段偏移改为「布局真源优先」,成员表漂移不再整体平移
类别 a(编译器缺陷):_get_member_offset 返回 `base + 成员表下标`,但 class_members
是**可变**表(继承展平会在父类就绪后前置前缀、跨模块可按需重载/重跑),而 struct
body 一旦 set_body 即**永久冻结** ⇒ 表与布局的序位一旦漂移,整类字段偏移整体平移。
现场(IR 实证):Converter 模块读 `Alias.name` 落在 struct 槽 9(= asname),而定义
模块 Alias.__init__ 写入槽 8;`import t` 无 as 子句 ⇒ asname=NULL ⇒ TransPyV StubGen
误报「import 别名 name 为空(_copy_str 返回 None)」并终止 includes/stdint.vp 的 vpi
生成。同族旧现场:ast.Constant.lineno 取到 pool 槽(已由成员偏移同源修复过)。

- StructGen._generate_structs:set_body 时把「成员名 → 槽位」映射冻结到 struct
  实例上(_tpc_member_slots / _tpc_member_base)。用**实例属性**而非全局表:同一
  类名可能存在不止一个 struct 实例,偏移必须与被 GEP 的那一份布局同源;仅当
  member_types 恰为 `[vtable?] + 全部成员` 时记录(位域类折叠为单一 storage 槽,
  其字段位置另由 class_member_bitoffsets 表达;stub 已置 body 的情形不记录)。
- FuncGen._get_member_offset:新增两条「只看布局」的路径,优先于原下标法——
  ① 冻结映射命中即返回;
  ② 用**类型**把成员表逐项配对到 elements(类型不匹配、重名的条目视为方法/占位/
     漂移项,不占槽位),配对中类型相等的槽位即字段真实槽位。
  表与布局自洽时两者给出与旧下标法逐字相同的结果;都覆盖不到才落回下标法(对既有
  正确行为零变化)。另在「配对结果 ≠ 下标法」时每类每字段告警一次,报出成员表项数
  与 struct 槽数,把漂移来源变成可见事实。

本地冒烟(三类,均通过):自洽表 8 / 漂移表(类型不匹配)8(旧下标法 9)/
全同型漂移且命中冻结映射 8。
2026-09-29 19:57:57 +08:00
TermiNexus aa1b536517 perf(tpc): _generate_structs 去掉两处"纯 no-op 重建",不改任何判定语义
类别:b(机制漏缓存)—— 都是"结果等价却重算"的浪费,不是判定逻辑改动。

证据(PROF\main_prof_72472.prof 的 `_generate_structs` 子调用归属,self=39.07s/8,493 次):
  isinstance 6.195s(6,249 万次)· `HandlesBase.__get__` 3.596s · len 2.892s
  `_TypedPointerType.__init__` 2.600s(375 万次)· `_patched_new` 2.030s · dict.get 2.555s
  `_MemberSlotAccepts` 2.437s · SymbolTable.lookup 2.105s · rsplit 1.123s

改动(均为**恒等变换**,任意中断/交错下语义不变,不引入失效判定):
1. Step 3 成员类型重绑:新增 `if pointee is not struct:` / `if member_type is not struct:`
   短路。原实现对"pointee 本来就是该 struct 实例"的成员也**现建一个 ir.PointerType
   再覆盖写**——llvmlite 类型是值语义(`_TypedPointerType.__eq__` 按 pointee 比较、
   打印只取决于结构),覆盖写是纯 no-op。该分支每次调用遍历全部类的全部成员
   ⇒ 这是 375 万次指针构造(约占本函数 9s,含 _patched_new/_patched_init/_IsBadPointee)
   的主源。
2. T* 占位扫描改为 `_StructBodyHasPlaceholderT`(按 body 对象记忆):Step 4 的
   "全类×全元素"扫描与 set_body 前的复核扫描,每次调用都全量重扫,是本函数
   6,249 万次 isinstance 与 498 万次 rsplit 的主源。判据精确性:该扫描是
   `st.elements` 的纯函数,而 `elements` 在 llvmlite 里**整体替换**(set_body 赋新
   tuple、作废置 None,从不原地改元素)⇒ 用 `elements` 的对象身份当版本号即可精确
   判定"body 未变";缓存条目强引用 struct 与 elements,二者存活 ⇒ id 不会被回收
   复用 ⇒ 不会误判命中。
3. vtable 槽 `i8*` 改用模块级共享实例 `_I8_PTR`(原来每类每次现建)。

**刻意不做**:不加"全量状态签名 early-out"。理由(写在此处备查):签名只能覆盖
"每类成员个数"这类可 O(类数) 取得的量,覆盖不到"成员类型原地替换";而那次替换正是
本函数 Step 3 自己做的 ⇒ 一旦某轮在中途抛异常,签名与上一次成功轮相同,early-out
会跳过**必要的**重建。要做到完备就得给约 25 处 `class_members` 变更点埋版本号,
收益不确定、风险实在,故不做。

验证:py 层 import + GetDiagnostics 干净;未跑编译器。
2026-09-29 16:37:03 +08:00
TermiNexus ac0d0f2cc8 perf(tpc): _MemberSlotAccepts 身份短路 + _generate_structs 热路径局部化(profile 第二梯队)
依据 PROF/main_prof_10152.prof(tottime 2288.9s / 72 文件顺序翻译):

  _generate_structs        自身 268.6s   cum 1893.2s   15762 基本/35240 总调用
    └ 子调用:_TryLoadStructFromStub 1589.5s×418k、_MemberSlotAccepts 61.1s×54.2M
              isinstance 39.7s×300M、PointerType 构造 68.7s×15.9M、
              SymbolTable.lookup 16.7s×15.9M、__len__ 6.8s×15.9M

本次两处(均等价改写):
1. StructGen._MemberSlotAccepts:在 `Declared == Slot or str(Declared) == str(Slot)`
   之前加**身份短路** `Declared is Slot`。该函数被 _generate_structs 调 54.2M 次,
   其中 llvmlite `Type.__eq__` 合计 ~30s(types.py:178/303/550/720)+
   `str()` ~3.3s;同一对象(body 与 class_members 引用同一类型对象)是其常见形态。
2. StructGen._generate_structs:把 `self.SymbolTable` / `.lookup` 提升为函数级局部
   (真值语义逐字保持:SymbolTable 定义了 __len__,故先取 `bool(_symtab)`),
   消掉逐类重复的属性查找与 15.9M 次 `__len__`。

说明:本提交只做等价微优化;本函数的大头(35,240 次全量 O(类数×成员数) 扫描、
429k 次结构体 stub 加载、1407 次泛型特化整类发射)需要结构性改动,另行评估。
2026-09-29 14:41:48 +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 a6c9003a19 fix(tpc): struct body 解冻改用 llvmlite 公开属性 elements(原 _elements 是空操作)
类别:a 编译器缺陷(机制漏豁免:错误的"解冻"字段名使所有 body 重建路径失效)。

现场(连续多轮复现):TransPyV Generator.vp:200
`self.ExprGenFstr = SubExprGenFstr` 报「指针隐式转换已禁止(层数不同):
%"a7b68c79f39cb4d3.ExprGenFstr"* → i8**」,[成员槽来源] 显示
class_members[Generator] 下标 11 声明槽类型**正确**,而布局
'47382dc8277c9d28.Generator' 的 elements[11]=i8**。

取证(已核对全部产物与共享符号表,排除"来源侧退化"):
- TransPyV/output/*.stub.ll 与 temp/47382dc8277c9d28.stub.ll 里
  %"47382dc8277c9d28.Generator" 的 slot11 全部是 %"a7b68c79f39cb4d3.ExprGenFstr"*,
  **没有任何 stub 含 i8**;
- 共享符号表(temp/_shared_sym.json → symbol_table_bytes)里
  Generator.Members['ExprGenFstr'] = CTypeInfo(BaseType=CStruct('ExprGenFstr'),
  PtrCount=1, IsStruct=True),也**正确**。

真根因:llvmlite 本版本 IdentifiedStructType 的字段是**公开属性 `elements`**
(types.py: `self.elements = None`;`is_opaque` = `elements is None`;
`set_body` 仅在 `elements is not None` 时抛 "already defined")。而本仓所有
"清除 body 以便重建"的路径都写 `st._elements = None`(不存在的属性)⇒ **空操作** ⇒
⇒ `if struct_type.elements is None` 恒假 ⇒ set_body 永不执行 ⇒ 早期冻结的错误布局
(如 i8**)无法被任何后续权威重建覆盖:
  · StructGen Step 4 逐元素校验已判定不一致(_slot_types_ok=False),清空失败 ⇒
    既不能 skip 也不能重建 ⇒ 错布局被静默保留;
  · HandlesClassDef 的本地定义权威重建点(class_members 已重建为正确类型后)
    同样无法刷新 body。
两处叠加正是"class_members 正确、布局却是 i8**"的稳定现象,也解释了此前
TypeGenCDef(槽3) / ExprGenFstr(槽11) 在不同构建里交替出现(取决于谁先被冻结)。

修复:四处解冻改为写公开属性 `elements`:
  · StructGen.py Step 4 前置校验失败后的清空;
  · StructGen.py sha1 全名 key 的 struct 清空;
  · StructGen.py T* 占位清空;
  · HandlesClassDef.py @t.NoVTable 本地定义点的清空。
清空后 is_opaque 恢复为真 ⇒ _TryLoadStructFromStub(declare 侧权威 body)或
class_members 重建可真正 set_body。
2026-09-29 13:13:17 +08:00
TermiNexus 7167703ca7 fix(tpc): 降级判据改用「注解形态 + 解析结果形状」,放在字段类型唯一漏斗
上一版拦截失效的原因(实测):`class_member_annotations` 全仓**只被声明与读取、从未被
写入**(Step 2 一直是空转),故依赖它的 `_HasDegradedMemberTypes` 恒为 False。改法:

1) SymbolUtils 新增两个**形状判据**(纯判定,不做类型解析):
   - IsI8DoublePtrType(T):是否 i8 系两层指针(`i8**`)= `T | t.CPtr` 在 T 类型丢失后
     退化成的形状;
   - IsClassRefPtrAnnotation(Ann):注解是否「指针包装的类引用」(rawptr[Mod.Class] /
     Mod.Class | t.CPtr;内层为非 t/c 模块的 Attribute 或首字母大写裸名)。
2) HandlesBase.CollectAnnAssignMember(字段类型的**唯一漏斗**,覆盖本地类 /
   跨模块 .vpi / 嵌套类三条写入链)新增硬判据:注解是类引用指针 **且** 解析结果正是
   `i8**` ⇒ 必属类型信息丢失,当场 fail-fast,并打出 CTypeInfo 全字段(BaseType/
   PtrCount/IsStruct/IsTypedef/IsEnum/Name)与内层名的符号表条目标志。此前的
   `_CheckPtrAnnotationKeepsClass` 只看"void 基座且无类身份",而现场这条链**带 Name/
   IsTypedef**(Attribute 分支的 typedef 出口 → BaseType=CVoid 但 Name=类名),正好漏过。
3) StructGen 删除依赖那张空表的 `_HasDegradedMemberTypes` / `_IsI8DoublePtr` /
   `_IsClassRefPtrAnnotation` 及 set_body 前的拦截(改由漏斗处兜住,不留死代码)。

现场:Generator.vp:200 `self.ExprGenFstr = SubExprGenFstr`
  → 布局槽 elements[11]=i8** 而声明 %"a7b68c79f39cb4d3.ExprGenFstr"*
2026-09-29 12:04:20 +08:00
TermiNexus 66de269ebf fix(tpc): 撤回失效的"事后修复",改为 set_body 前拦截降级布局(不冻结)
a 编译器缺陷。实测证据(07ea8e2f 的探针回执):对本版本 llvmlite,
`st._elements = None` 之后 `st.set_body(*elems)` 仍以
`RuntimeError: <name> is already defined` 拒绝 —— 说明 **`_elements` 不是 set_body 的
判定存储**,即"清 _elements 以便重载/重设 body"这一全仓惯用手段在本版本是**空操作**
(结构体 body 一经设定即不可改)。因此:

  - 撤回 7fb0e498 加的 `_SyncStructBodyTypes`(使用点就地修复)及其在
    `_get_member_offset` 的调用 —— 它在本版本永不可能成功,只会把编译卡死;
  - 改为**事前拦截**:`_generate_structs` 在 `set_body` 前调用
    `_HasDegradedMemberTypes`,若本次布局里存在「注解是类引用(`Mod.Class` /
    首字母大写裸名,排除 t/c 模块)**却**解析成 `i8**`」的成员,说明该 pass 类型尚未
    就绪 ⇒ **不设 body、保持 opaque**,等类型就绪的 pass 再设。这样从根上避免
    "降级布局被永久冻结"(现场:Generator 的槽 i8** 而声明 %"…ExprGenFstr"*,
    赋值时报「层数不同」)。始终不就绪则在使用点响亮失败(opaqueness / 层数不同),
    不再静默产出错布局。
  - 新增 `_IsI8DoublePtr` / `_IsClassRefPtrAnnotation` 两个判据助手(保守:只在
    "指针包装 + 类引用内层 + i8 系两层指针"三条同时成立时判降级)。
2026-09-29 11:56:08 +08:00
TermiNexus 07ea8e2fd9 fix(tpc): 布局校验/修复排除 REnum 镜像布局 + set_body 失败带回滚与上下文
a 编译器缺陷(承接 b8ed33a2 / 7fb0e498):REnum 的 struct body 是 **max-variant
镜像布局** —— class_members 里的名字是*变体*而非字段,变体嵌套结构体也必须逐字
镜像主结构体。此前两处按"声明类型 vs 槽类型"的校验/修复会把这类布局当成错位:
  - Step 4 前置校验可能据此清 _elements 重建;
  - _SyncStructBodyTypes 可能据此改写布局(跨模块 GEP 偏移随即与定义端失配)。
新增 _IsRenumLayoutClass(主 REnum 自身 / `<REnum>_<Variant>`,判据走 SymbolTable 的
IsRenum)并在两处排除。

另外:就地修复的 `_elements = None` + `set_body` 一旦被 llvmlite 拒绝
(`<name> is already defined`,现场 fd4cd0ec583f42b9.Generator),原来会抛出这句
毫无上下文的裸 RuntimeError。现改为:捕获后**直接写回原 _elements 回滚**,再抛
FailFastError 带上类名 / struct / 原异常 / 元素数前后值,既不留中间状态也不再含糊。
2026-09-29 11:34:17 +08:00
TermiNexus 7fb0e49854 fix(tpc): 成员偏移查询点就地修复错位 struct body(修正晚于冻结的时序)
a 编译器缺陷(承接 b8ed33a2):Step 4 的前置校验只在**当次** _generate_structs 生效,
漏掉"class_members 被 .vpi/stub 修正在后、struct body 冻结在前"的时序 —— 于是
一次只修好一部分槽(实测:第 3 槽修好、第 11 槽仍错),错误逐个前移。

证据链(成员槽来源明细 + 全盘 grep):
  class_members[Generator] 项数=47 base=0 下标=11
      声明槽类型=%"a7b68c79f39cb4d3.ExprGenFstr"*
  布局 struct name='fd4cd0ec583f42b9.Generator' 元素数=47 elements[11]=i8**
  且**没有任何** stub 文本里含 i8** ⇒ body 是从"当时已降级的 class_members"冻住的,
  后来重载只修 class_members,改不了已冻结的 body(llvmlite set_body 只能设一次)。

修法:新增 StructGen._SyncStructBodyTypes —— 在**使用点**(_get_member_offset,
即任何 GEP 之前、成员表已在上方补齐/重载之后)按 class_members 逐槽比对,
只把**存储时必被拒绝**的槽(判据同 _MemberSlotAccepts)改回声明类型,其余元素
(含通用指针 i8* 槽)原样保留;_elements 置空后 set_body 重设(命名类型对象不变,
故已发出的 getelementptr %"name"* 不受影响)。同名类 sha1 冲突路径
(_ResolvedClass != ClassName)跳过,避免用别的同名类成员表写坏布局。
2026-09-29 00:19:06 +08:00
TermiNexus b8ed33a200 fix(tpc): struct body 冻结前逐元素校验类型一致性(错位 body 不再被当"完整")
a 编译器缺陷:_generate_structs Step 4 判定既有 struct body 是否"完整"时只比
「元素个数 / 首元素是 vtable 指针 / 是否含 T 占位」—— **类型错位**的 body
(个数命中、元素名也全对,但某槽被冻结成通用多层指针)会被直接 continue;
而 llvmlite 的 set_body 只能设一次 ⇒ 错布局被永久冻结,后续 .vpi / .stub 重载
只修 class_members、改不了 body ⇒ 赋值时报「层数不同」。

现场(成员槽来源明细实锤:声明侧正确、布局侧坏):
  class_members[Generator] 项数=47 base=0 下标=3
      声明槽类型=%"d827f3045bd28467.TypeGenCDef"*
  布局 struct name='fd4cd0ec583f42b9.Generator' 元素数=47 elements[3]=i8**
  → self.TypeGenCDef = SubTypeGenCDef
    指针隐式转换已禁止(层数不同):不能把 %…TypeGenCDef"* 隐式转为 i8**

修法:新增 _MemberSlotAccepts(判据与存储侧同一套 _ClassifyPtrImplicitConvert),
把 class_members 声明类型与 elements[base+i] 逐项比对;只有**存储时必被拒绝**的
组合(层数不同 / 指针与非指针混位)判为不一致 ⇒ 走既有「清 _elements + 清 stub
缓存」路径重建布局(通用指针槽 i8*、继承上转、数组退让照旧放行,不误清)。
2026-09-29 00:06:11 +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 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 a775ec9932 fix(coro): 帧结构体定义输出到 IR,修复 GEP base element must be sized
协程帧类型 %Coro_<name> 只存 _coro_frame_types 未注册进 Gen.structs,
StructGen.finalize 不输出其定义 → resume_<name> 的 GEP 引用 opaque
类型,opt ASan 插桩报 "base element of getelementptr must be sized"。
修复:_GetOrCreateFrameType 注册帧到 Gen.structs;finalize 追加
_coro_frame_types 兜底输出(防 _generate_structs 清空时丢定义)。
2026-08-21 12:41:10 +08:00
TermiNexus 12207e91d8 fix(phase2): 并行编译 worker 数按可用内存动态限制,修复内存不足 OOM
系统可用内存低(Trae CN/Java 占用后仅剩 ~1GB)时,ProcessPoolExecutor
按 cpu_count 开满 worker,多进程编译内存叠加在 StructGen.finalize 拼接
module IR 时 MemoryError(编译 StubMergerTopo.vp 报错即此)。新增
_PickWorkerCount:GlobalMemoryStatusEx 读 ullAvailPhys,按 per_worker_mb
估算 worker 上限(翻译 700MB / llc 400MB),内存不足自动降为单 worker
串行,内存充足回落 cpu_count。StructGen.finalize 拼接改中间变量拆分,
降低单 worker 峰值副本。
2026-08-21 09:04:40 +08:00
TermiNexus 267e9881b4 perf(structgen): finalize 特殊字符 struct 名加引号合并为单次 re.sub
逐 name 全文本 re.sub 改为单正则 alternation 一次扫描,消除 N 次 O(text) 扫描(lookahead 边界保证备选互斥,语义等价)
2026-08-19 00:32:21 +08:00
TermiNexus fbd132284b perf(phase2): 按 PROF 热点优化编译耗时
- HandlesImports: _TryLoadClassMembersFromPyi 复用 _pyi_cache 跳过重复 isfile(-12.8s);_LookupStubFuncType 类级缓存 + 签名增量扫描新增 stub,去掉每文件 isfile(-6.5s+)
- Phase2Translator: pyi cache 仅主进程构建(worker 只读 pkl,-23s+pickle 10.4s);include 源文件 sha1/存在性缓存消除 _build_current_ModuleSha1Map 重复磁盘 I/O
- StructGen.finalize: 行过滤改 startswith(tuple) 预计算,消除 O(lines×structs×patterns) 嵌套循环(tottime 84.8s → 预期数秒)
- 附带 fail-fast(任意文件出错立即终止)+ 子进程 PROF 硬编码
2026-08-19 00:28:40 +08:00
TermiNexus 3b5f2af838 fix(structgen): 跨模块 struct 兜底命名改查 _struct_sha1_map,修复 7cadfe07e983fd90.Translator 前缀错位
泛型特化 _specialize_generic_class 把 Gen.module_sha1 切换到模板定义模块
(_list=7cadfe07e983fd90)的窗口内,_generate_structs Step 1 对不在
class_sha1_map 的类(因 LlvmGenerator 共享 class_methods 跨文件泄漏而
缺条目)用 _mangle_name 兜底,错误注册为 7cadfe07e983fd90.Translator 等
跨模块类型 → Translator.__before_init__ self 类型错前缀,与构造 result
(6ccfee1e5bd522ed.Translator*)不匹配,llvm 报 Type of #1 arg mismatch。
兜底改为优先查 _struct_sha1_map(Phase2 从 vpi 扫描构建的正确类→模块映射),
特化类自身仍走 _mangle_name(用模板 sha1 命名是设计意图)。
2026-08-17 20:55:47 +08:00
TermiNexus 6fc9e19e24 feat(vast): 新增自研 Viper/Python 语法解析器取代标准库 ast
TPC 侧取缔对 Python 标准库 ast 模块的依赖,改用 lib/vast 包:
- Nodes: 与 CPython 3.12 ast 完全同构的 131 个节点类(含抽象基类继承)
- Tokenizer: 词法分析(缩进/字符串/f-string/数字/运算符/软关键字)
- ExprParser: 表达式递归下降解析(优先级/lambda/推导式/f-string/切片)
- Parser: 语句解析(def/class/if/for/try/with/match/import/注解)
- Utils/Unparser: walk/dump/unparse/copy_location/literal_eval 等辅助
- 软关键字 fn/fun/func/function 并入解析器, 废除 ExpandSoftKeywords 预处理
- lib/_bootstrap 将 lib 目录加入 sys.path, import vast as ast 零修改替换
- 52 个文件 import ast 替换为 import vast as ast; 94 文件差分测试 0 失败
2026-08-17 09:36:38 +08:00
TermiNexus 1b89225130 feat(TPC): 拓展 isinstance 跨模块支持
vtable linkage 改默认外部强定义(含 initializer 自动归一为空),跨模块可引用 sha1.Class_Vtable;外部引用声明为 i8* 避免 array 类型不匹配;_ResolveVtableAddr 增加 CVTable 状态检查,NoVTable 类回退编译时静态判断避免坏引用。新增 geom_lib.vp 跨模块测试库,main.vp 增加跨模块继承/运行时多态/NoVTable 场景。
2026-08-10 19:16:27 +08:00
TermiNexus 87b9dec1ef feat(core): TPC 多继承同步 M5 - MRO 查找/字段展平/vtable合并/this调整
BaseGen: 新增 class_parents + _get_mro_chain(与 TPV 语义一致: 深度优先
+声明顺序+去重) 与 _get_parent_classes 兼容旧单链
HandlesClassDef: 停用 is_base_of_any 自动 vtable; 多父字段展平去重;
方法合并用定义类名; vtable 槽沿 MRO 取定义类
HandlesExprCall: 方法调用沿 MRO 回退 + stub(无函数体)判定修复
undefined reference; 继承方法 this 调整(GEP+bitcast 成父类指针)
HandlesExprUtils/HandlesImports/HandlesWith/StructGen: MRO 查找同步
2026-08-10 00:29:05 +08:00
TermiNexus ca7c2120b8 修正了种子编译器的错误 2026-07-22 21:55:36 +08:00
TermiNexus 4e66207ba1 重置上传,新增了多个标准库,开始 TransPyV 自举实验 2026-07-19 11:38:15 +08:00
t 796222a300 snapshot before regression test 2026-07-18 19:25:40 +08:00