fix(gen): define 位宽/整数提升/移位/AugAssign 规则收敛,修 va_list 与 traceback 卡死

- define 位宽:cdefine_decl_bits 单一裁决(注解 → 右侧强转 → 值最小可存宽度),定义方与 VpiReader 两处跨模块注册共用;lookup_cdefine_bits 取最后一条非零记录;存储即截断;使用侧改按声明位宽;通用求值器加编译期除零/移位越界 fail-fast
- 消除对无符号强转加宽方向的依赖:define_min_width 改 (value>>32)==0,截断改左移+算术右移,修 TPC/TPV 判定不一致导致 0x80000000 误判 64 位
- 整数提升:算术/移位、比较、一元 +/-/~ 提升到 i32;位运算保持同宽不提升
- AugAssign:y 有效宽度须 <= x、禁 y 显式有符号而 x 无符号;移位计数静态范围检查
- 函数入口复位 va_list 状态与 willFree 失效槽表(FuncGenDef/FuncGenMixin/VarGenMixin 等)
- BuildPipeline:traceback 逐行扫描改 span 判定,去掉对全文 strlen 的 O(n^2)
This commit is contained in:
TermiNexus committed 2026-10-12 12:37:48 +08:00
1 parent be4db7b5d1
commit 4e46a25ab1
18 files changed
+1247 -843

No files matched your search

+53 -27
View File
@@ -291,7 +291,7 @@ def ensure_dir(path: str) -> int:
# Temporarily truncate at each separator and create every directory level.
# CreateDirectoryA returns 0 when the dir exists (failure); ignore it.
plen: int = int(path_len)
for i in range(plen):
for i in range(int(plen)):
ch: int = buf[i]
if ch == 47 or ch == 92: # '/' = 47, '\\' = 92
saved: char = char(ch)
@@ -639,7 +639,7 @@ def run_llc_parallel(tasks: bytes, task_count: int, max_threads: int) -> int:
if tasks is None or task_count <= 0: return 0
# --phase2command mode: do not spawn threads; write all opt (ASan) + llc commands to the bat
if _phase2_cmd_path is not None:
for pi2 in range(task_count):
for pi2 in range(int(task_count)):
tsk_p: LlcTask* = (LlcTask*)(uint64(tasks) + sizet(pi2) * LlcTask.__sizeof__())
if tsk_p is None: continue
if tsk_p.MlirOptCmd is not None: _phase2_write_cmd(tsk_p.MlirOptCmd)
@@ -658,24 +658,24 @@ def run_llc_parallel(tasks: bytes, task_count: int, max_threads: int) -> int:
slot_size: sizet = LlcTask.__sizeof__()
ret: int = 0
# Batched execution: batch_start steps by max_threads (each batch spawn+join+close).
for batch_start in range(0, task_count, max_threads):
for batch_start in range(0, int(task_count), int(max_threads)):
batch_n: int = max_threads
if batch_start + batch_n > task_count: batch_n = task_count - batch_start
# spawn this batch
for j in range(batch_n):
for j in range(int(batch_n)):
tsk: LlcTask* = (LlcTask*)(uint64(tasks) + sizet(batch_start + j) * slot_size)
th: vthreading.Thread* = vthreading.Thread(_llc_task_worker, t.CPtr(tsk))
th.start()
# Read the handle right after start() (the Thread object's stack slot may be
# reused; reading self.handle at join could see a cleared/overwritten value).
handles.append(t.CPtr(th.handle))
for j in range(batch_n):
for j in range(int(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 thread join timeout/failure (not finished within 600 seconds)", "PhaseB")
win32.win32base.CloseHandle(h_j)
# check this batch's results
for j in range(batch_n):
for j in range(int(batch_n)):
tsk2: LlcTask* = (LlcTask*)(uint64(tasks) + sizet(batch_start + j) * slot_size)
if tsk2 is not None and tsk2.Result != 0:
# Failure: use FailStage to pinpoint the step and print that step's command.
@@ -942,7 +942,7 @@ def link_objs_to_exe(obj_paths: str, obj_paths_len: sizet, linker_cmd: str, link
dt2len: sizet = dj - ds2
if dt2len == dtlen:
deq: int = 1
for dk in range(dtlen):
for dk in range(int(dtlen)):
if dedup[ds2 + dk] != obj_paths[dstart + dk]:
deq = 0
break
@@ -1147,6 +1147,20 @@ def _tb_contains(s: str, sub: str) -> int:
return 1 if string.strstr(s, sub) is not None else 0
def _tb_contains_span(s: str, ls: sizet, le: sizet, sub: str) -> int:
"""Span-aware substring test: only s[ls, le) is searched.
A tail-wide strstr would both scan the whole remaining objdump text (hundreds of MB) and
match a substring belonging to a LATER line (e.g. ".text" found past a ".rdata" header)."""
if s is None or sub is None: return 0
sl: sizet = string.strlen(sub)
if sl == 0 or le < ls or le - ls < sl: return 0
i: sizet = ls
while i + sl <= le:
if string.strncmp(s + i, sub, sl) == 0: return 1
i += 1
return 0
def _tb_startswith(s: str, pfx: str) -> int:
if s is None or pfx is None: return 0
pl: sizet = string.strlen(pfx)
@@ -1154,6 +1168,18 @@ def _tb_startswith(s: str, pfx: str) -> int:
return 1 if string.strncmp(s, pfx, pl) == 0 else 0
def _tb_startswith_span(s: str, ls: sizet, le: sizet, pfx: str) -> int:
"""Span-aware prefix test: only s[ls, le) is considered, never the tail.
_tb_startswith on `s + ls` would run strlen over the WHOLE remaining text; llvm-objdump -d
output is hundreds of MB for a large exe, so a per-line strlen makes the whole scan O(n^2)
and the compilation appears to hang (it spins inside strlen for hours)."""
if s is None or pfx is None: return 0
if le < ls: return 0
pl: sizet = string.strlen(pfx)
if le - ls < pl: return 0
return 1 if string.strncmp(s + ls, pfx, pl) == 0 else 0
def _tb_ltrim(s: str) -> str:
if s is None: return None
i: sizet = 0
@@ -1165,7 +1191,7 @@ def _tb_len_trimmed(s: str) -> sizet:
if s is None: return 0
n: sizet = string.strlen(s)
while n > 0:
c: int = _tb_cc(s, n - 1)
c: int = int(_tb_cc(s, n - 1))
if c == 32 or c == 9 or c == 13: n -= 1
else: break
return n
@@ -1206,7 +1232,7 @@ def _tb_join(mb: memhub.MemBuddy*, base: str, name: str) -> str:
nl: sizet = string.strlen(name)
buf: str = mb.alloc(bl + nl + 2)
if buf is None: return name
last: int = _tb_cc(base, bl - 1)
last: int = int(_tb_cc(base, bl - 1))
if last == 47 or last == 92: viperlib.snprintf(buf, bl + nl + 2, "%s%s", base, name)
else: viperlib.snprintf(buf, bl + nl + 2, "%s/%s", base, name)
return buf
@@ -1304,13 +1330,13 @@ def _tb_quoted_after(mb: memhub.MemBuddy*, s: str, key: str) -> str:
def _tb_dir_lookup(dirs: bytes, count: int, idx: int) -> str:
for i in range(count):
for i in range(int(count)):
if int(_tb_rd_u32(dirs, sizet(i) * TB_DIR_SIZE)) == idx: return rawptr(_tb_rd_u64(dirs, sizet(i) * TB_DIR_SIZE + 8))
return None
def _tb_file_rec(files: bytes, count: int, idx: int) -> int:
for i in range(count):
for i in range(int(count)):
if int(_tb_rd_u32(files, sizet(i) * TB_FILE_SIZE)) == idx: return i
return -1
@@ -1403,7 +1429,7 @@ def _tb_func_name_for(fbuf: bytes, fcount: int, rva: uint64) -> str:
def _tb_intern(p: TbPool*, s: str) -> uint64:
"""Intern s into the pool (linear scan; keeps offset 0 as the empty sentinel)."""
if p is None or s is None: return 0
for k in range(p.OffCount):
for k in range(int(p.OffCount)):
o: uint64 = _tb_rd_u64(p.Offs, sizet(k) * 8)
if string.strcmp(p.Pool + sizet(o), s) == 0: return o
off: uint64 = uint64(p.PoolLen)
@@ -1450,7 +1476,7 @@ def _tb_read_pe_layout(mb: memhub.MemBuddy*, exe_path: str) -> TbPeLayout*:
min_rva: uint64 = 0xFFFFFFFF
found_text: int = 0
have_sec: int = 0
for si in range(nsec):
for si in range(int(nsec)):
base: sizet = sec_off + sizet(si) * 40
if uint64(base) + 40 > uint64(got): break
va: uint64 = uint64(_tb_rd_u32(head, base + 12))
@@ -1744,7 +1770,7 @@ def _tb_grp_get(grp: bytes, cap: int, caller: str, callee: str, do_insert: int)
if grp is None or cap <= 0 or caller is None or callee is None: return -1
mask: uint64 = uint64(cap - 1)
hv: uint64 = (_tb_strhash(caller) * 0x100000001B3 + _tb_strhash(callee)) & mask
for i in range(cap):
for i in range(int(cap)):
slot: int = int((hv + uint64(i)) & mask)
off: sizet = sizet(slot) * TB_GRP_SIZE
if uint64(_tb_rd_u32(grp, off + TB_GRP_OFF_USED)) == 0:
@@ -1823,7 +1849,7 @@ def _tb_read_whole(mb: memhub.MemBuddy*, path: str) -> str:
if buf is None:
f.close()
return None
# 容量全程用 sizet(与 sz 同宽):旧写法 ULONG(sz) + ULONG(2) 会在 sz >= 4GiB 时静默回绕
# 容量全程用 sizet(与 sz 同宽):旧写法 ULONG(sz) + ULONG(2) 会在 sz >= 4GiB 时静默回绕
got: LONG = f.read_all(buf, sizet(sz) + 2)
f.close()
if got < 0: return None
@@ -1844,7 +1870,7 @@ def _tb_parse_f_line(mb: memhub.MemBuddy*, s: str, a: sizet, b: sizet, cp: TbCal
f3: sizet = p
while f3 < b and _tb_cc(s, f3) != 9: f3 += 1
if f3 >= b: return 0
defline: int = _tb_parse_dec(s + p)
defline: int = int(_tb_parse_dec(s + p))
p = f3 + 1
file: str = _tb_dup_span(mb, s + p, b - p)
if sym is None or file is None: return 0
@@ -1870,7 +1896,7 @@ def _tb_parse_c_line(mb: memhub.MemBuddy*, s: str, a: sizet, b: sizet, cp: TbCal
e4: sizet = p
while e4 < b and _tb_cc(s, e4) != 9: e4 += 1
if e4 >= b: return 0
line: int = _tb_parse_dec(s + p)
line: int = int(_tb_parse_dec(s + p))
p = e4 + 1
file: str = _tb_dup_span(mb, s + p, b - p)
if caller is None or callee is None or file is None: return 0
@@ -1901,7 +1927,7 @@ def _tb_parse_vpcall_file(mb: memhub.MemBuddy*, path: str, cp: TbCallParse*) ->
def _tb_load_vpcalls(mb: memhub.MemBuddy*, cp: TbCallParse*) -> int:
if _tb_vpcall_buf is None or _tb_vpcall_n <= 0: return 0
for k in range(_tb_vpcall_n):
for k in range(int(_tb_vpcall_n)):
slot: str* = (str*)(uint64(_tb_vpcall_buf) + sizet(k) * 8)
path: str = slot[0]
if path is not None:
@@ -1918,7 +1944,7 @@ def _tb_build_groups(mb: memhub.MemBuddy*, cp: TbCallParse*) -> int:
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):
for i in range(int(cp.CrN)):
off: sizet = sizet(i) * TB_CR_SIZE
caller: str = rawptr(_tb_rd_u64(cp.Cr, off + TB_CR_OFF_CALLER))
callee: str = rawptr(_tb_rd_u64(cp.Cr, off + TB_CR_OFF_CALLEE))
@@ -1959,7 +1985,7 @@ def _tb_emit_call_row(mb: memhub.MemBuddy*, d: TbDump*, cp: TbCallParse*, img_lo
def _tb_emit_entry_row(mb: memhub.MemBuddy*, d: TbDump*, cp: TbCallParse*, img_lo: uint64, addr: uint64, sym: str) -> int:
"""Emit a fallback function-entry row for an objdump label sym (file/line from the F record)."""
if sym is None or cp.Fr is None or cp.FrN <= 0 or addr < img_lo: return 0
for i in range(cp.FrN):
for i in range(int(cp.FrN)):
off: sizet = sizet(i) * TB_FR_SIZE
s: str = rawptr(_tb_rd_u64(cp.Fr, off + TB_FR_OFF_SYM))
if s is not None and string.strcmp(s, sym) == 0:
@@ -2016,10 +2042,10 @@ def _tb_scan_objdump(mb: memhub.MemBuddy*, dump: str, img_lo: uint64, d: TbDump*
if pos < dlen: pos += 1
if le > ls and _tb_cc(dump, le - 1) == 13: le -= 1
if le <= ls: continue
if _tb_startswith(dump + ls, "Disassembly of section") != 0:
if _tb_startswith_span(dump, ls, le, "Disassembly of section") != 0:
# section change: a pending callq at the previous section's end has no valid return address
prev_is_call = 0
if _tb_contains(dump + ls, ".text") != 0: in_text = 1
if _tb_contains_span(dump, ls, le, ".text") != 0: in_text = 1
else: in_text = 0
continue
if in_text == 0: continue
@@ -2188,7 +2214,7 @@ def WriteStackMap(mb: memhub.MemBuddy*, exe_path: str) -> int:
bl: uint64 = uint64(_tb_rd_u32(d.RowBuf, sizet(i) * TB_ROW_SIZE + TB_ROW_OFF_LINE))
bp: uint64 = _tb_rd_u64(d.RowBuf, sizet(i) * TB_ROW_SIZE + TB_ROW_OFF_FILE)
bf: uint64 = _tb_rd_u64(d.RowBuf, sizet(i) * TB_ROW_SIZE + TB_ROW_OFF_FUNC)
j: int = i + 1
j: int = int(i + 1)
while j < kept and _tb_rd_u64(d.RowBuf, sizet(j) * TB_ROW_SIZE) == rv:
lj: uint64 = uint64(_tb_rd_u32(d.RowBuf, sizet(j) * TB_ROW_SIZE + TB_ROW_OFF_LINE))
if lj > bl:
@@ -2209,7 +2235,7 @@ def WriteStackMap(mb: memhub.MemBuddy*, exe_path: str) -> int:
return 1
# ---- 3) subprograms: normalise + sort by low_rva ----
fcount: int = d.FuncCount
fcount: int = int(d.FuncCount)
fkept: int = 0
dropped_funcs: int = 0
fi: int = 0
@@ -2283,7 +2309,7 @@ def WriteStackMap(mb: memhub.MemBuddy*, exe_path: str) -> int:
if ro is None:
VLogger.error("--traceback: failed to allocate record offset buffer, aborting compilation", "TRACEBACK")
return 1
for i in range(uniq):
for i in range(int(uniq)):
pth2: str = rawptr(_tb_rd_u64(d.RowBuf, sizet(i) * TB_ROW_SIZE + TB_ROW_OFF_FILE))
foff: uint64 = _tb_intern(p, pth2)
noff: uint64 = 0
@@ -2318,7 +2344,7 @@ def WriteStackMap(mb: memhub.MemBuddy*, exe_path: str) -> int:
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):
for ci in range(int(uniq)):
fo: uint64 = uint64(_tb_rd_u32(ro, sizet(ci) * 8))
if fo == 0:
VLogger.error("--traceback self-check failed: a record has file_off==0 (empty path; runtime would reject that sidecar file), aborting compilation", "TRACEBACK")
@@ -2338,7 +2364,7 @@ def WriteStackMap(mb: memhub.MemBuddy*, exe_path: str) -> int:
_tb_wr_u32(out, 8, uint64(uniq))
_tb_wr_u32(out, 12, uint64(p.OffCount))
_tb_wr_u32(out, 16, uint64(p.PoolLen))
for ri2 in range(uniq):
for ri2 in range(int(uniq)):
roff2: sizet = 20 + sizet(ri2) * 20
rv3: uint64 = _tb_rd_u64(d.RowBuf, sizet(ri2) * TB_ROW_SIZE)
ln3: uint64 = uint64(_tb_rd_u32(d.RowBuf, sizet(ri2) * TB_ROW_SIZE + TB_ROW_OFF_LINE))