fix(gen): 修 REnum match case 静默丢弃与 CDefine 负 64 位常量截断,补 void 注解放行、移植类型定义前移

- MatchGenMixin: 2a 路径查变体 tag 改用裸类名(注解书写形态带模块前缀,与变体表裸名注册口径不一致致 tag=-1、case 被静默丢弃、match 全走 default),并在 build_switch 前硬校验 应提交 case 数==sw_count,不再静默丢

- ExprGenCdef: _const_int_for_cdefine 补 value < -2147483648 下限判据,修正 (0xA5A5A5A5<<32)|0xA5A5A5A5 等负 64 位模式被物化为 i32(写入 inttoptr 零扩展 vs 比较 sext 不一致,致 MemBuddy canary 假阳性 rc=6)

- FuncGenDef/ClassGenVTable: 掉尾检查新增注解级 void 判据(t.CVoid/裸名 CVoid/None),不再因 REnum match 退化误判 void 函数缺 return

- StubMergerBuild: 移植 TPC 的 类型定义前移 遍,满足 llc 对 by-value 具名类型需先定义的要求(invalid type for function argument)

- main.vp/Phase1Main.vp/project.vpj: 新增 --detection-deep 深诊断开关与 canary 哨兵、ASan 静态库声明
This commit is contained in:
TermiNexus committed 2026-09-15 19:11:54 +08:00
1 parent 60100fa6af
commit 31df298341
8 files changed
+345 -6

No files matched your search

+142
View File
@@ -1231,6 +1231,141 @@ def _BuildReachableSha1Set(mb: rowptr[memhub.MemBuddy], source_dir: str,
VLogger.debug(f"可达 SHA1: {reachable_count} 个", "Reachable")
return reachable_count
# ============================================================
# 类型定义前移(llc: "invalid type for function argument")
#
# 来源:TransPyC/lib/Projectrans/Phase2Translator.py:3056-3081(TPC 同语义实现,
# 注释原文即 llc 的该要求)。触发条件:by-value 结构体参数引用的具名类型必须在
# declare 点之前定义,否则 llc 报 invalid type for function argument。
#
# 与 TPC 的差异(唯一一处):TPC 额外做一次同名去重(seen_type_names_inc)。
# 本侧合并阶段 _seen_types(本文件 L1646-1667)已按类型名保证"活跃定义唯一",
# 重复行不会进入 out_buf,故本遍是纯置换(长度不变),天然幂等且行内容逐字不变。
# ============================================================
def _IsTypeDefLine(buf: bytes, ls: sizet, le: sizet) -> int:
"""buf[ls, le)(不含换行)是否为 `%name = type ...` 类型定义行
对齐 TPC L3064 的 `stripped.startswith('%') and '= type' in stripped`,
但把裸子串判定收紧为词判定:'= type' 也会出现在函数体字符串常量里
(如 c"= type\\00"),裸子串判定会把普通指令行当类型定义搬走 → 破坏 IR。
"""
k: sizet = ls
while k < le and (buf[k] == ' ' or buf[k] == '\t'):
k += 1
if k >= le or buf[k] != '%':
return 0
j: sizet = k
while j + 6 <= le:
if string.strncmp(buf + j, "= type", 6) == 0:
if j + 6 == le:
return 1
e: char = buf[j + 6]
# 'type' 后只能是空白 / '{' / '<'(= type { ... } / opaque / <{...}>)
if e == ' ' or e == '\t' or e == '{' or e == '<':
return 1
j += 1
return 0
def _IsIrHeaderLine(buf: bytes, ls: sizet, le: sizet) -> int:
"""buf[ls, le)(不含换行)是否属于 ll header(类型定义插在其后)
对齐 TPC L3076 的 header 判据:空行 / ';' 注释 / 'target ' /
'source_filename' / '!' 元数据(行首空白先跳过,等价 TPC 的 stripped)。
"""
k: sizet = ls
while k < le and (buf[k] == ' ' or buf[k] == '\t' or buf[k] == '\r'):
k += 1
if k >= le:
return 1
if buf[k] == ';' or buf[k] == '!':
return 1
if le - k >= 7 and string.strncmp(buf + k, "target ", 7) == 0:
return 1
if le - k >= 15 and string.strncmp(buf + k, "source_filename", 15) == 0:
return 1
return 0
def HoistTypeDefsBeforeDeclares(buf: bytes, length: sizet) -> sizet:
"""把所有 `%name = type ...` 行上移到 IR header 之后(见上方注释)
纯文本行级重排:行内容逐字不变、类型定义之间相对顺序不变、非类型定义行
相对顺序不变;插入点与 TPC 一致(header 之后第一行;若首个非 header 行
就在文件首行或无 header 行,按 TPC 同规则追加末尾)。
幂等:已是"定义在 header 之后"时结果与输入逐字节相同。
返回重排后的长度(有类型定义行时恒为 length)。
"""
if buf is None or length == 0:
return length
# 1. 统计类型定义行总长(整行含换行)
types_len: sizet = 0
p: sizet = 0
while p < length:
le: sizet = p
while le < length and buf[le] != '\n':
le += 1
step: sizet = le - p
if le < length:
step += 1
if _IsTypeDefLine(buf, p, le) != 0:
types_len += step
p += step
if types_len == 0:
return length
tmp: bytes = stdlib.malloc(types_len + 1)
if tmp is None:
# VLogger.error 直接终止进程(不静默降级为"未前移")
VLogger.error("类型定义前移暂存缓冲分配失败", "StubMerger")
return length
# 2. 类型定义行抽到 tmp、其余行原地压缩(只删行不增行,w <= p 恒成立)
tmp_pos: sizet = 0
w: sizet = 0
p = 0
while p < length:
le = p
while le < length and buf[le] != '\n':
le += 1
step = le - p
if le < length:
step += 1
if _IsTypeDefLine(buf, p, le) != 0:
string.memmove(tmp + tmp_pos, buf + p, step)
tmp_pos += step
else:
if w != p:
string.memmove(buf + w, buf + p, step)
w += step
p += step
tmp[tmp_pos] = '\0'
# 3. 求插入点:过滤后文本里 header 之后的首行起始偏移
ins: sizet = 0
hit: int = 0
q: sizet = 0
while q < w:
qe: sizet = q
while qe < w and buf[qe] != '\n':
qe += 1
if _IsIrHeaderLine(buf, q, qe) == 0:
ins = q
hit = 1
break
if qe >= w:
break
q = qe + 1
if hit == 0 or ins == 0:
# 对齐 TPC L3079-3080:insert_pos == len(other_lines)
ins = w
# 4. 腾出 tmp_pos 字节空隙并拷入(长度不变;右移用 memmove 保证重叠安全)
tail: sizet = w - ins
if tail > 0:
string.memmove(buf + ins + tmp_pos, buf + ins, tail)
string.memmove(buf + ins, tmp, tmp_pos)
new_len: sizet = w + tmp_pos
buf[new_len] = '\0'
stdlib.free(tmp)
return new_len
# ============================================================
# BuildCombinedIR - 组合本地 stub + 依赖 stubs + 本地 text → 完整 IR
#
@@ -1674,6 +1809,13 @@ def BuildCombinedIR(temp_dir: str, local_sha1: str,
if mod_arr is not None:
stdlib.free(mod_arr)
# 类型定义前移:llc 要求 by-value 结构体参数引用的具名类型在 declare 点之前
# 定义(否则 "invalid type for function argument")。TPC 侧同一遍见
# TransPyC/lib/Projectrans/Phase2Translator.py:3056-3081。
# 落点说明:Phase2 的 write_ir_to_file 是逐字节写本缓冲区(BuildPipeline.vp
# L289-314,无任何文本加工),故在此完成即覆盖 llc 的全部输入,无需在
# Phase2 再复制一份 4MB 缓冲区重排。
out_pos = HoistTypeDefsBeforeDeclares(out_buf, out_pos)
return out_pos
# ============================================================
# LoadDepVpiIntoModule - 加载依赖模块的 .vpi 文件,注入 declare 到当前模块