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:
1 parent
be4db7b5d1
commit
4e46a25ab1
18 files changed
+1247
-843
No files matched your search
@@ -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))
|
||||
|
||||
+298
-295
File diff suppressed because it is too large.
Load diff
+426
-202
@@ -7,7 +7,7 @@ import string
|
||||
import stdio
|
||||
import stdlib
|
||||
import viperlib
|
||||
import w32.fileio as fileio
|
||||
import win32.fileio as fileio
|
||||
import lib.core.VLogger as VLogger
|
||||
import lib.core.Gen.Translator as HT
|
||||
import lib.Stub.StubMerger.StubMergerStore as StubMergerStore
|
||||
@@ -40,8 +40,25 @@ novirtual class AssignGenMixin:
|
||||
return 0
|
||||
|
||||
# extract_cdefine_int_value - extract an integer constant from AnnAssign.value
|
||||
# ---- 统一编译期常量表达式求值(值 + 位宽) ----
|
||||
# 所有 define/常量折叠入口都走这里,避免各写一套(此前 extract_cdefine_int_value /
|
||||
# _eval_const_int_expr / _cif_eval_node 三套互相独立,导致位宽信息在 define 上丢失)。
|
||||
def extract_cdefine_int_value(self, val_node: ast.AST*) -> int64:
|
||||
"""Extract an integer constant from an AnnAssign value node."""
|
||||
"""仅取值(丢弃位宽)的薄封装;需要位宽请用 extract_cdefine_int_value_bits。"""
|
||||
b_legacy: int = 0
|
||||
return self.extract_cdefine_int_value_bits(val_node, None, &b_legacy)
|
||||
|
||||
def extract_cdefine_int_value_bits(self, val_node: ast.AST*, pool: memhub.MemBuddy*, bits_out: int*) -> int64:
|
||||
"""编译期常量表达式求值:返回值,并把**位宽**写入 bits_out[0](0=未知)。
|
||||
|
||||
位宽规则(供 define 物化用):
|
||||
- 显式强转(Call 的 callee 是整型/typedef,如 WORD / int / t.CUInt16T)→ 该位宽;
|
||||
- Name 引用其它 define → 沿用其位宽;
|
||||
- BinOp:两侧位宽**相等** → 保持该位宽(**不提升**);不等 → 取较宽;任一未知 → 未知;
|
||||
- 字面量/一元 → 未知(物化时按值推断)。
|
||||
pool 为 None 时无法解析 typedef(仅旧调用方走此路),强转按"取实参值、位宽未知"处理。
|
||||
"""
|
||||
if bits_out is not None: bits_out[0] = 0
|
||||
if val_node is None: return 0
|
||||
k: int = val_node.kind()
|
||||
|
||||
@@ -58,19 +75,33 @@ novirtual class AssignGenMixin:
|
||||
# Case 2: BinOp - e.g. FOREGROUND_RED | FOREGROUND_GREEN, (1 << N)
|
||||
if k == ast.ASTKind.BinOp:
|
||||
bop: ast.BinOp* = ast.BinOp*(val_node)
|
||||
left_val: int64 = self.extract_cdefine_int_value(bop.left)
|
||||
right_val: int64 = self.extract_cdefine_int_value(bop.right)
|
||||
lb: int = 0
|
||||
rb: int = 0
|
||||
left_val: int64 = self.extract_cdefine_int_value_bits(bop.left, pool, &lb)
|
||||
right_val: int64 = self.extract_cdefine_int_value_bits(bop.right, pool, &rb)
|
||||
if bits_out is not None:
|
||||
if lb != 0 and rb != 0: bits_out[0] = lb if lb > rb else rb
|
||||
elif lb != 0: bits_out[0] = lb
|
||||
elif rb != 0: bits_out[0] = rb
|
||||
if bop.op == ast.OpKind.BitOr: return left_val | right_val
|
||||
if bop.op == ast.OpKind.BitAnd: return left_val & right_val
|
||||
if bop.op == ast.OpKind.BitXor: return left_val ^ right_val
|
||||
if bop.op == ast.OpKind.LShift: return left_val << right_val
|
||||
if bop.op == ast.OpKind.RShift: return left_val >> right_val
|
||||
# 移位:求值器内部按 64 位算,计数为负或 >= 64 时结果无意义(宿主侧是 poison/未定义),
|
||||
# 与 require_shift_count_in_range 同一口径直接 fail-fast,不产出垃圾常量。
|
||||
if bop.op == ast.OpKind.LShift or bop.op == ast.OpKind.RShift:
|
||||
if right_val < 0 or right_val >= 64:
|
||||
self.Gen.TypeGenErr.fatal_error(val_node, f"constant expression shift count out of range: the shift count evaluates to {right_val} at compile time, " "but the evaluator works on 64-bit integers (valid range 0..63); the result would be undefined. " "Fix: use a count in 0..63, or restructure the expression.", "ValueError")
|
||||
if bop.op == ast.OpKind.LShift: return left_val << right_val
|
||||
return left_val >> right_val
|
||||
if bop.op == ast.OpKind.Add: return left_val + right_val
|
||||
if bop.op == ast.OpKind.Sub: return left_val - right_val
|
||||
if bop.op == ast.OpKind.Mult: return left_val * right_val
|
||||
if bop.op == ast.OpKind.Div: return left_val // right_val
|
||||
if bop.op == ast.OpKind.FloorDiv: return left_val // right_val
|
||||
if bop.op == ast.OpKind.Mod: return left_val % right_val
|
||||
# 编译期除零:绝不把除法交给宿主(会直接把编译器打挂),fail-fast 给出可读诊断。
|
||||
if bop.op == ast.OpKind.Div or bop.op == ast.OpKind.FloorDiv or bop.op == ast.OpKind.Mod:
|
||||
if right_val == 0:
|
||||
self.Gen.TypeGenErr.fatal_error(val_node, "constant expression division by zero: the right operand of '/', '//' or '%' evaluates to 0 at compile time.", "ValueError")
|
||||
if bop.op == ast.OpKind.Mod: return left_val % right_val
|
||||
return left_val // right_val
|
||||
# Unknown BinOp operator: fail fast
|
||||
self.Gen.TypeGenErr.fatal_error(val_node, "CDefine value extraction failed: cannot parse a compile-time constant from the expression", "TypeError")
|
||||
|
||||
@@ -82,18 +113,29 @@ novirtual class AssignGenMixin:
|
||||
if string.strcmp(nm.id, "True") == 0: return 1
|
||||
if string.strcmp(nm.id, "False") == 0: return 0
|
||||
looked_up: int64 = self.Gen.TypeGenCDef.lookup_cdefine_constant(nm.id)
|
||||
if self.Gen.TypeGenCDef.is_cdefine_found() != 0: return looked_up
|
||||
if self.Gen.TypeGenCDef.is_cdefine_found() != 0:
|
||||
# 沿用被引用 define 的位宽(宽度随宏传播,不再丢)
|
||||
if bits_out is not None: bits_out[0] = self.Gen.TypeGenCDef.lookup_cdefine_bits(nm.id)
|
||||
return looked_up
|
||||
# Unregistered reference: fail fast
|
||||
self.Gen.TypeGenErr.fatal_error(val_node, "CDefine value extraction failed: cannot parse a compile-time constant from the expression", "TypeError")
|
||||
|
||||
# Case 4: Call - e.g. t.CUnsignedLong(-11)
|
||||
# Case 4: Call - 整型/typedef 强转(如 WORD(0x0080) / t.CUnsignedLong(-11)):
|
||||
# 取实参值,并按**目标类型的位宽**规范化(这赋予常量"已知宽度")。
|
||||
if k == ast.ASTKind.Call:
|
||||
cl: ast.Call* = ast.Call*(val_node)
|
||||
t_bits: int = 0
|
||||
if pool is not None: t_bits = self._annot_int_bits(pool, cl.func)
|
||||
# Bind the property to a list[...]* local before calling len() (else GEP base breaks)
|
||||
cl_args_list: list[ast.AST*]* = cl.args
|
||||
if cl_args_list is not None and len(cl_args_list) > 0:
|
||||
first_arg: ast.AST* = cl_args_list.get(0)
|
||||
arg_val: int64 = self.extract_cdefine_int_value(first_arg)
|
||||
ib: int = 0
|
||||
arg_val: int64 = self.extract_cdefine_int_value_bits(first_arg, pool, &ib)
|
||||
if t_bits != 0:
|
||||
if bits_out is not None: bits_out[0] = t_bits
|
||||
return self._normalize_cdefine_bits(arg_val, t_bits)
|
||||
if bits_out is not None: bits_out[0] = ib
|
||||
return arg_val
|
||||
# Call with no args: fail fast
|
||||
self.Gen.TypeGenErr.fatal_error(val_node, "CDefine value extraction failed: cannot parse a compile-time constant from the expression", "TypeError")
|
||||
@@ -101,7 +143,9 @@ novirtual class AssignGenMixin:
|
||||
# Case 5: UnaryOp - e.g. -11
|
||||
if k == ast.ASTKind.UnaryOp:
|
||||
uop: ast.UnaryOp* = ast.UnaryOp*(val_node)
|
||||
operand_val: int64 = self.extract_cdefine_int_value(uop.operand)
|
||||
ob: int = 0
|
||||
operand_val: int64 = self.extract_cdefine_int_value_bits(uop.operand, pool, &ob)
|
||||
if bits_out is not None: bits_out[0] = ob
|
||||
if uop.op == ast.OpKind.USub: return -operand_val
|
||||
if uop.op == ast.OpKind.UAdd: return operand_val
|
||||
if uop.op == ast.OpKind.Invert: return ~operand_val
|
||||
@@ -113,6 +157,37 @@ novirtual class AssignGenMixin:
|
||||
# After fatal_error add an explicit return so the block is properly terminated
|
||||
return 0
|
||||
|
||||
def _normalize_cdefine_bits(self, value: int64, bits: int) -> int64:
|
||||
"""把值按位宽**截断**后再重解释为该位宽的有符号表示(与 ExprGenCdef._cdefine_bits_value 同口径)。
|
||||
|
||||
「左侧只会看到截断了的数据」:超宽的值先按 bits 取低位(如 WORD(0x80000000) → 0),
|
||||
不是直接把原值搬过来、也不是只对"刚好越界一点"的值做重解释。
|
||||
实现用左移后算术右移,而不是 `value & 0xFFFFFFFF` 这类大掩码字面量:
|
||||
掩码字面量(如 4294967295)超出 int32 范围,其加宽方向在 TPC/TPV 上可能判成 sext,
|
||||
会得到 0xFFFFFFFFFFFFFFFF 而不是 0xFFFFFFFF,导致截断失效。
|
||||
"""
|
||||
if bits == 8: return (value << 56) >> 56
|
||||
if bits == 16: return (value << 48) >> 48
|
||||
if bits == 32: return (value << 32) >> 32
|
||||
return value
|
||||
|
||||
def cdefine_decl_bits(self, pool: memhub.MemBuddy*, annot: ast.AST*, value_node: ast.AST*) -> int:
|
||||
"""define 声明的位宽裁决(定义方与跨模块使用方**共用同一套**,避免两边位宽不一致)。
|
||||
|
||||
① 有声明注解 T(define X: T = ...)→ 按注解 T 的位宽;
|
||||
② 无注解但右侧表达式自身已定宽(显式强转,如 WORD(0x0080))→ 用该位宽;
|
||||
③ 都没有 → 按**值的最小可存宽度**(0x80000000 属 2^31..2^32-1 取 32;小值取 8/16,
|
||||
窄宽度参与算术时由"算术整数提升"先升到 i32,不会溢出)。
|
||||
返回 0 表示该 define 不是整型(调用方不应据此注册整型常量)。
|
||||
"""
|
||||
annot_bits: int = self._define_annot_bits(pool, annot)
|
||||
if annot_bits != 0: return annot_bits
|
||||
if value_node is None: return 0
|
||||
rhs_bits: int = 0
|
||||
val: int64 = self.extract_cdefine_int_value_bits(value_node, pool, &rhs_bits)
|
||||
if rhs_bits != 0: return rhs_bits
|
||||
return self.define_min_width(val)
|
||||
|
||||
# define / t.CDefine integer constant storable and width checks
|
||||
# Auto-derived => 64-bit pattern; explicit type T => T's width range.
|
||||
def define_min_width(self, value: int64) -> int:
|
||||
@@ -126,10 +201,89 @@ novirtual class AssignGenMixin:
|
||||
if value <= 255: return 8
|
||||
if value <= 65535: return 16
|
||||
if value <= 2147483647: return 32
|
||||
# 2^31..2^32-1: fits an unsigned 32-bit bit pattern
|
||||
if uint64(uint32(value)) == value: return 32
|
||||
# 2^31..2^32-1:无符号 32 位位型即可容纳 → 32 位。
|
||||
# 用 (value >> 32) == 0 判断,**不要**写成 uint64(uint32(value)) == value:uint32(...) 之后的
|
||||
# 加宽方向(zext/sext)由 should_zext_widen 按节点无符号性决定,TPC 与 TPV 自举的判定可能不一致;
|
||||
# 一旦判成 sext,uint64(0x80000000) 会变成 0xFFFFFFFF80000000 ≠ value → 误判 64 位 →
|
||||
# `GENERIC_READ | GENERIC_WRITE` 被放大到 i64 → 赋给 32 位目标报隐式窄化。
|
||||
if (value >> 32) == 0: return 32
|
||||
return 64
|
||||
|
||||
# _is_static_int_expr - 该节点是否属于通用求值器一定能折叠的整型表达式(只判定,不求值)
|
||||
def _binop_is_foldable(self, op: int) -> int:
|
||||
"""1=通用求值器支持的整型二元运算(与 extract_cdefine_int_value_bits 的 BinOp 分支一致)"""
|
||||
if op == ast.OpKind.BitOr or op == ast.OpKind.BitAnd or op == ast.OpKind.BitXor: return 1
|
||||
if op == ast.OpKind.LShift or op == ast.OpKind.RShift: return 1
|
||||
if op == ast.OpKind.Add or op == ast.OpKind.Sub or op == ast.OpKind.Mult: return 1
|
||||
if op == ast.OpKind.Div or op == ast.OpKind.FloorDiv or op == ast.OpKind.Mod: return 1
|
||||
return 0
|
||||
|
||||
def _is_static_int_expr(self, node: ast.AST*) -> int:
|
||||
"""1=通用求值器(extract_cdefine_int_value_bits)一定能求出整数;0=不确定,**不得调用求值器**。
|
||||
|
||||
判定范围与求值器严格对齐(否则求值器会 fail-fast,变成误报):
|
||||
整型/布尔字面量、True/False、**已注册的** CDefine 名、其 +/-/~、求值器支持的整型二元运算、
|
||||
以及带实参的强转调用。变量、普通函数调用、求值器不支持的算子一律判 0 —— 即"求不出来就不检查"。
|
||||
"""
|
||||
if node is None: return 0
|
||||
k: int = node.kind()
|
||||
if k == ast.ASTKind.Constant:
|
||||
cn: ast.Constant* = ast.Constant*(node)
|
||||
if cn.const_kind == ast.CONST_INT or cn.const_kind == ast.CONST_BOOL: return 1
|
||||
return 0
|
||||
if k == ast.ASTKind.Name:
|
||||
nm: ast.Name* = ast.Name*(node)
|
||||
if nm.id is None: return 0
|
||||
if string.strcmp(nm.id, "True") == 0 or string.strcmp(nm.id, "False") == 0: return 1
|
||||
self.Gen.TypeGenCDef.lookup_cdefine_constant(nm.id)
|
||||
if self.Gen.TypeGenCDef.is_cdefine_found() == 0: return 0
|
||||
return 1
|
||||
if k == ast.ASTKind.BinOp:
|
||||
bop: ast.BinOp* = ast.BinOp*(node)
|
||||
if self._binop_is_foldable(bop.op) == 0: return 0
|
||||
if self._is_static_int_expr(bop.left) == 0: return 0
|
||||
return self._is_static_int_expr(bop.right)
|
||||
if k == ast.ASTKind.UnaryOp:
|
||||
uop: ast.UnaryOp* = ast.UnaryOp*(node)
|
||||
if uop.op != ast.OpKind.USub and uop.op != ast.OpKind.UAdd and uop.op != ast.OpKind.Invert: return 0
|
||||
return self._is_static_int_expr(uop.operand)
|
||||
if k == ast.ASTKind.Call:
|
||||
cl: ast.Call* = ast.Call*(node)
|
||||
cl_args_st: list[ast.AST*]* = cl.args
|
||||
if cl_args_st is None or len(cl_args_st) == 0: return 0
|
||||
return self._is_static_int_expr(cl_args_st.get(0))
|
||||
return 0
|
||||
|
||||
# _static_int_of - 静态求值整数节点:复用 define 的**通用求值器**(单一实现,避免两套规则)
|
||||
def _static_int_of(self, node: ast.AST*, out: int64*) -> int:
|
||||
"""1=静态可确定(值写入 out[0]),0=无法确定(调用方不做检查,也不报错)。
|
||||
|
||||
先由 _is_static_int_expr 判定"求值器一定能求出来",再走同一个求值器
|
||||
(extract_cdefine_int_value_bits,位宽丢弃),因此支持任意编译期可折叠表达式
|
||||
(`(SHIFT / 2) + 1`、`WORD(3)`、`A | B` 等),且求不出来时不会触发求值器的 fail-fast。
|
||||
"""
|
||||
if node is None or out is None: return 0
|
||||
if self._is_static_int_expr(node) == 0: return 0
|
||||
b_na: int = 0
|
||||
out[0] = self.extract_cdefine_int_value_bits(node, None, &b_na)
|
||||
return 1
|
||||
|
||||
# require_shift_count_in_range - 移位计数的静态范围检查(x << y / x >> y 与 <<= / >>= 共用)
|
||||
def require_shift_count_in_range(self, pool: memhub.MemBuddy*, trans: HT.Translator*, node: ast.AST*, count_node: ast.AST*, lhs_bits: int) -> None:
|
||||
"""fail-fast:移位计数可静态确定且为负、或 >= 左操作数位宽时直接报错。
|
||||
|
||||
移位计数**豁免**"宽度 <= 目标宽度"与符号一致性规则(计数不是写回目标的值),
|
||||
改为在编译期把能确定的无意义移位拦下来:x << y / x >> y 与 x <<= y / x >>= y 同一套。
|
||||
计数无法静态确定时不检查(留给运行期)。lhs_bits 应传**提升后的有效宽度**(C 语义下
|
||||
u8 << 8 合法,因为 u8 先提升到 i32)。"""
|
||||
if count_node is None or lhs_bits <= 0: return
|
||||
v: int64 = 0
|
||||
if self._static_int_of(count_node, &v) == 0: return
|
||||
if v < 0:
|
||||
self.Gen.TypeGenErr.fatal_error(node, f"[TPV] shift count is negative ({v}): shifting by a negative amount is undefined. " "Fix: use a non-negative count.", "ValueError")
|
||||
if uint64(v) >= uint64(lhs_bits):
|
||||
self.Gen.TypeGenErr.fatal_error(node, f"[TPV] shift count out of range: the count {v} is >= the left operand width {lhs_bits} bits; " f"the result would be 0 or undefined (not a value the compiler may guess). " f"Fix: use a count < {lhs_bits}, or widen the left operand.", "ValueError")
|
||||
|
||||
def _is_cdefine_marker_node(self, node: ast.AST*) -> int:
|
||||
"""Return 1 if the node is a t.CDefine marker node."""
|
||||
if node is None: return 0
|
||||
@@ -164,23 +318,49 @@ novirtual class AssignGenMixin:
|
||||
n2: ast.Name* = ast.Name*(annot)
|
||||
nm = n2.id
|
||||
if nm is None: return 0
|
||||
return self._int_bits_of_type_name(pool, nm, 0)
|
||||
|
||||
def _int_bits_of_type_name(self, pool: memhub.MemBuddy*, nm: str, depth: int) -> int:
|
||||
"""类型名 → 整数位宽(0=未知/非整数)。depth 防 typedef 环。
|
||||
|
||||
覆盖:内建名(int/bool/int8../uint64/sizet)、`t.CXxx`(map_t_type)、
|
||||
以及**用户 typedef**(如 stdint 的 `WORD: t.CTypedef = uint16` —— 经 lookup_ctypedef
|
||||
取其底层类型文本再递归定宽)。此前用户 typedef 一律返回 0,导致
|
||||
`define X: WORD = ...` 的声明宽度不参与校验/物化。
|
||||
"""
|
||||
if nm is None or depth > 10: return 0
|
||||
# 去掉模块/类限定前缀:`t.CUInt16T` → `CUInt16T`;`stdint.uint16` → `uint16`
|
||||
dot: str = string.strrchr(nm, 46)
|
||||
if dot is not None and dot[1] != '\0':
|
||||
return self._int_bits_of_type_name(pool, dot + 1, depth + 1)
|
||||
if string.strcmp(nm, "int") == 0: return 32
|
||||
if string.strcmp(nm, "bool") == 0: return 8
|
||||
# Fixed-width lowercase aliases from stdint/builtins (not t module names)
|
||||
if string.strcmp(nm, "int8") == 0 or string.strcmp(nm, "uint8") == 0: return 8
|
||||
if string.strcmp(nm, "int16") == 0 or string.strcmp(nm, "uint16") == 0: return 16
|
||||
if string.strcmp(nm, "int32") == 0 or string.strcmp(nm, "uint32") == 0: return 32
|
||||
if string.strcmp(nm, "int64") == 0 or string.strcmp(nm, "uint64") == 0: return 64
|
||||
if string.strcmp(nm, "sizet") == 0 or string.strcmp(nm, "size_t") == 0: return 64
|
||||
ty: mlirlite.LLVMType* = self.Gen.TypeGenResolve.map_t_type(pool, nm)
|
||||
if ty is None: return 0
|
||||
match ty:
|
||||
case mlirlite.LLVMType.Int(bits):
|
||||
if bits == 1: return 8
|
||||
if bits == 8 or bits == 16 or bits == 32 or bits == 64: return bits
|
||||
return 0
|
||||
case _:
|
||||
return 0
|
||||
if ty is not None:
|
||||
match ty:
|
||||
case mlirlite.LLVMType.Int(bits):
|
||||
if bits == 1: return 8
|
||||
if bits == 8 or bits == 16 or bits == 32 or bits == 64: return bits
|
||||
return 0
|
||||
case _:
|
||||
return 0
|
||||
# 用户/include typedef(如 stdint.vp 的 `WORD: t.CTypedef = uint16`):
|
||||
# 表内值可能是模块限定 ref(stdint.uint16),先用 _expand_typedef_ref 展开;
|
||||
# 本地表未命中再走 builtins 源扫描兜底。
|
||||
underlying: str = self.Gen.TypeGenCType.lookup_ctypedef(nm)
|
||||
if underlying is None:
|
||||
underlying = self.Gen.TypeGenResolve._lookup_builtins_typedef(pool, nm)
|
||||
if underlying is not None:
|
||||
expanded: str = self.Gen.TypeGenResolve._expand_typedef_ref(pool, underlying)
|
||||
if expanded is not None: underlying = expanded
|
||||
if underlying is None: return 0
|
||||
if string.strcmp(underlying, nm) == 0: return 0
|
||||
return self._int_bits_of_type_name(pool, underlying, depth + 1)
|
||||
|
||||
def _define_annot_bits(self, pool: memhub.MemBuddy*, annot: ast.AST*) -> int:
|
||||
"""Return the bit width of a CDefine declaration's explicit type T."""
|
||||
@@ -216,7 +396,7 @@ novirtual class AssignGenMixin:
|
||||
if line_len < name_len + 1: continue
|
||||
ok: int = 1
|
||||
ki: sizet
|
||||
for ki in range(name_len):
|
||||
for ki in range(int(name_len)):
|
||||
if buf[line_start + ki] != name[ki]:
|
||||
ok = 0
|
||||
break
|
||||
@@ -236,7 +416,7 @@ novirtual class AssignGenMixin:
|
||||
txt: str = stdlib.malloc(eq - vstart + 1)
|
||||
if txt is None: continue
|
||||
vi: sizet
|
||||
for vi in range(eq - vstart): txt[vi] = buf[vstart + vi]
|
||||
for vi in range(int(eq - vstart)): txt[vi] = buf[vstart + vi]
|
||||
txt[eq - vstart] = 0
|
||||
result = txt
|
||||
break
|
||||
@@ -303,8 +483,13 @@ novirtual class AssignGenMixin:
|
||||
d += 1
|
||||
return 0
|
||||
|
||||
def require_cdefine_storable(self, pool: memhub.MemBuddy*, node: ast.AST*, name: str, value: int64, annot: ast.AST*) -> None:
|
||||
"""Fail fast if a CDefine value is not storable in its declared type."""
|
||||
def require_cdefine_storable(self, pool: memhub.MemBuddy*, node: ast.AST*, name: str, value: int64, annot: ast.AST*, rhs_bits: int = 0) -> None:
|
||||
"""Fail fast if a CDefine value is not storable in its declared type.
|
||||
|
||||
rhs_bits: 右侧表达式自身的位宽(0=未定宽)。右侧带显式强转(如 WORD(...))且不宽于声明注解时,
|
||||
截断是**显式**的(左侧只看到截断后的数据),此时不再按"存不下"报错——这正是
|
||||
"注解存不下且没有强转截断的需要报错"里被豁免的那一半。
|
||||
"""
|
||||
text: str = self._cdefine_int_literal_text(name)
|
||||
bits: int = self._define_annot_bits(pool, annot)
|
||||
wmin: int = self.define_min_width(value)
|
||||
@@ -312,6 +497,9 @@ novirtual class AssignGenMixin:
|
||||
self.Gen.TypeGenErr.fatal_error(node, f"[TPV] define constant value not storable: the initial value {text} of constant '{name}' exceeds 64 bits " f"(the widest integer type in this language is 64 bits; the storable range under both signed and unsigned interpretations is " f"-9223372036854775808..18446744073709551615; no wider integer type exists => widening is required). " f"Silent truncation/downgrade is forbidden; compilation aborted. Available integer types: int8/uint8, " f"int16/uint16, int32/uint32, int64/uint64 " f"(or int8/uint8/.../uint64, char, sizet). " f"Fix: move the value into the 64-bit range, or introduce a larger integer type for this number (widen).", "TypeError")
|
||||
return
|
||||
if bits == 0: return
|
||||
# Explicit truncation exemption: the RHS narrowed itself to rhs_bits (a cast such as WORD(...)),
|
||||
# so the value the declaration stores is already the truncated one.
|
||||
if rhs_bits != 0 and rhs_bits <= bits: return
|
||||
# Build the 32-bit bounds from overflow-safe literals (see define_min_width)
|
||||
m31: int64 = 2147483647
|
||||
lo: int64 = -m31 - 1
|
||||
@@ -327,7 +515,11 @@ novirtual class AssignGenMixin:
|
||||
self.Gen.TypeGenErr.fatal_error(node, f"[TPV] define constant value not storable: the initial value {value} of constant '{name}' is not storable. " f"Minimal storable width W_min: {wmin} bits; declared type width {bits} bits, " f"storable range of this declaration: {lo}..{hi}. " f"Silent truncation/downgrade is forbidden; compilation aborted. " f"Fix: widen the explicit type to a wider type (such as int32 / int64 / uint64), " f"or correct the literal.", "TypeError")
|
||||
|
||||
def require_cdefine_use_width(self, pool: memhub.MemBuddy*, trans: HT.Translator*, target_annot: ast.AST*, value_node: ast.AST*) -> None:
|
||||
"""Fail fast when a define constant is wider than its use-site type."""
|
||||
"""Fail fast when a define constant is wider than its use-site type.
|
||||
|
||||
比较基准是 define 的**声明位宽**(注解 → 右侧强转 → 值的最小宽度,见 cdefine_decl_bits),
|
||||
不是值本身的最小宽度:define 声明成 64 位,写进 uint8 位置就是隐式窄化,必须报错。
|
||||
"""
|
||||
if value_node is None or target_annot is None: return
|
||||
if value_node.kind() != ast.ASTKind.Name: return
|
||||
rn: ast.Name* = ast.Name*(value_node)
|
||||
@@ -338,9 +530,11 @@ novirtual class AssignGenMixin:
|
||||
if self.Gen.TypeGenCDef.is_cdefine_found() == 0: return
|
||||
bits: int = self._annot_int_bits(pool, target_annot)
|
||||
if bits == 0: return
|
||||
wmin: int = self.define_min_width(cdef_val)
|
||||
if bits >= wmin: return
|
||||
self.Gen.TypeGenErr.fatal_error(value_node, f"[TPV] define constant use-site width insufficient: the actual value {cdef_val} of constant '{rn.id}' " f"needs at least {wmin} bits (minimal storable width W_min), " f"but the use site (variable declaration annotation) provides only {bits} bits. " f"Silent truncation/downgrade is forbidden; compilation aborted. " f"Fix: widen the variable declaration annotation to at least {wmin} bits (such as int{wmin}T / CUInt{wmin}T), " f"or use a smaller constant value.", "TypeError")
|
||||
# 声明位宽优先;未登记位宽(0)时才回退到推断位宽(值的最小可存宽度)
|
||||
dw: int = self.Gen.TypeGenCDef.lookup_cdefine_bits(rn.id)
|
||||
if dw == 0: dw = self.define_min_width(cdef_val)
|
||||
if bits >= dw: return
|
||||
self.Gen.TypeGenErr.fatal_error(value_node, f"[TPV] define constant use-site width insufficient: constant '{rn.id}' is {dw} bits wide " f"(declared width; actual value {cdef_val}), but the use site (variable declaration annotation) provides only {bits} bits. " f"Silent truncation/downgrade is forbidden; compilation aborted. " f"Fix: widen the variable declaration annotation to at least {dw} bits (such as int{dw}T / CUInt{dw}T), " f"or narrow the constant at its define with an explicit cast.", "TypeError")
|
||||
|
||||
# _register_cdefine_from_value - register a CDefine constant (float or int)
|
||||
def _register_cdefine_from_value(self, pool: memhub.MemBuddy*, name: str, val_node: ast.AST*, annot: ast.AST* = None) -> None:
|
||||
@@ -381,10 +575,15 @@ novirtual class AssignGenMixin:
|
||||
# CDefine declaration without a value node: fail fast
|
||||
self.Gen.TypeGenErr.fatal_error(None, f"CDefine value extraction failed: '{name}' is missing an initial value", "TypeError")
|
||||
return
|
||||
val_fp: int64 = self.extract_cdefine_int_value(val_node)
|
||||
# 位宽裁决:与跨模块使用方共用 cdefine_decl_bits(注解 → 右侧显式强转 → 值的最小可存宽度)。
|
||||
# 有注解而存不下时:右侧带显式强转截断则不报错(左侧只看到截断值),否则由
|
||||
# require_cdefine_storable 报"存不下"。
|
||||
rhs_bits: int = 0
|
||||
val_fp: int64 = self.extract_cdefine_int_value_bits(val_node, pool, &rhs_bits)
|
||||
vb: int = self.cdefine_decl_bits(pool, annot, val_node)
|
||||
# Check storability before registering (source text >64-bit or explicit T too narrow => fail)
|
||||
self.require_cdefine_storable(pool, val_node, name, val_fp, annot)
|
||||
self.Gen.TypeGenCDef.register_cdefine_constant(pool, name, val_fp)
|
||||
self.require_cdefine_storable(pool, val_node, name, val_fp, annot, rhs_bits)
|
||||
self.Gen.TypeGenCDef.register_cdefine_constant(pool, name, val_fp, vb)
|
||||
|
||||
# Recover a CDefine float value by re-reading its source line with atof
|
||||
|
||||
@@ -435,7 +634,7 @@ novirtual class AssignGenMixin:
|
||||
if ns + name_len + 1 > line_start + line_len: continue
|
||||
match_ok: int = 1
|
||||
ki: sizet
|
||||
for ki in range(name_len):
|
||||
for ki in range(int(name_len)):
|
||||
if buf[ns + ki] != name[ki]:
|
||||
match_ok = 0
|
||||
break
|
||||
@@ -482,7 +681,7 @@ novirtual class AssignGenMixin:
|
||||
stdlib.free(buf)
|
||||
return 0
|
||||
vi: sizet
|
||||
for vi in range(eq - val_start): val_txt[vi] = buf[val_start + vi]
|
||||
for vi in range(int(eq - val_start)): val_txt[vi] = buf[val_start + vi]
|
||||
val_txt[eq - val_start] = 0
|
||||
stdlib.free(buf)
|
||||
# Use atof_out (an out param) to avoid the cross-module double return bug
|
||||
@@ -589,105 +788,105 @@ novirtual class AssignGenMixin:
|
||||
# ============================================================
|
||||
|
||||
# ── A2(§14)`@life(ret(...))`:调用点解析被调者的返回值生命期契约 ──
|
||||
# _RecvClassName - 从方法的接收者表达式取它静态类型指向的类名(去 sha1 前缀);判不定返回 None。
|
||||
# 只认「名字变量」接收者:变量表里查不到(如 `m.f()` 里的模块名 `m`)返回 None —— 调用方据此
|
||||
# 把"模块限定函数调用"与"方法调用"分开(二者在 AST 里都是 Attribute)。
|
||||
def _RecvClassName(self, trans: HT.Translator*, recv: ast.AST*) -> str:
|
||||
"""接收者静态类型 → 类名(无 sha1 前缀);判不定返回 None"""
|
||||
if trans is None or recv is None: return None
|
||||
if recv.kind() != ast.ASTKind.Name: return None
|
||||
rn: ast.Name* = ast.Name*(recv)
|
||||
if rn.id is None: return None
|
||||
v: mlirlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, rn.id)
|
||||
if v is None: return None
|
||||
ty: mlirlite.LLVMType* = v.Ty # 变量槽类型 = <变量类型>*
|
||||
if ty is None or self.Gen.ExprGenBase.is_ptr_type(ty) == 0: return None
|
||||
p1: mlirlite.LLVMType* = ty.Pointee # 变量类型本身
|
||||
if p1 is None: return None
|
||||
# 类变量通常是指针(`S*`);值语义的类变量(`s: S`,罕见)直接取本类型。
|
||||
elem: mlirlite.LLVMType* = p1.Pointee
|
||||
if self.Gen.ExprGenBase.is_ptr_type(p1) == 0: elem = p1
|
||||
sname: str = mlirlite.get_struct_name(elem)
|
||||
if sname is None: return None
|
||||
pl: int = GenApi.sha1_prefix_len(sname)
|
||||
if pl > 0: sname = sname + pl
|
||||
return sname
|
||||
|
||||
# _CallArgNodeForParam - `@life(ret(borrows=<形参 pi>))` 在调用点对应的实参节点。
|
||||
# **方法**调用的形参表含 self(pi=0 即接收者),故 is_method=1 时偏移一格;
|
||||
# **模块限定函数**(`m.f(...)`:callee 也是 Attribute,但不是方法)形参 0 就是第一个实参,
|
||||
# 不能偏移 —— 否则 pi=0 会被解析成"模块名"这个非实参节点(现场:本函数把
|
||||
# `retdep2.IdentBorrow(l)` 的形参 0 取成了 `retdep2` → life=STATIC → 跨模块 ret 契约静默失效)。
|
||||
def _CallArgNodeForParam(self, cl: ast.Call*, pi: int, is_method: int) -> ast.AST*:
|
||||
"""第 pi 号形参对应的实参表达式;越界/判不定返回 None"""
|
||||
if cl is None or pi < 0: return None
|
||||
cargs: list[ast.AST*]* = cl.args
|
||||
if is_method != 0:
|
||||
if pi == 0:
|
||||
at: ast.Attribute* = ast.Attribute*(cl.func)
|
||||
return at.value
|
||||
if cargs is None or sizet(pi - 1) >= len(cargs): return None
|
||||
return cargs.get(sizet(pi - 1))
|
||||
if cargs is None or sizet(pi) >= len(cargs): return None
|
||||
return cargs.get(sizet(pi))
|
||||
|
||||
# _CallIsMethod - 该调用是否"接收者是对象变量"的方法调用(决定形参表是否含 self)。
|
||||
def _CallIsMethod(self, trans: HT.Translator*, cl: ast.Call*) -> int:
|
||||
"""1 = 方法调用(形参表含 self);0 = 模块限定函数 / 裸函数"""
|
||||
if cl is None or cl.func is None: return 0
|
||||
if cl.func.kind() != ast.ASTKind.Attribute: return 0
|
||||
at: ast.Attribute* = ast.Attribute*(cl.func)
|
||||
if self._RecvClassName(trans, at.value) is None: return 0
|
||||
return 1
|
||||
|
||||
# _CallRetKind - 解析被调者的 `@life(ret(...))` 契约(本文件表 → 跨模块 .vpi);-1 = 无声明/判不定。
|
||||
def _CallRetKind(self, trans: HT.Translator*, cl: ast.Call*) -> int:
|
||||
"""被调者 ret_kind 编码(GenApi.RET_KIND_*);-1 = 无声明/判不定"""
|
||||
if cl is None or cl.func is None: return -1
|
||||
fnm: str = self.Gen.ExprGenBase.get_func_name(cl.func)
|
||||
if fnm is None: return -1
|
||||
pool: memhub.MemBuddy* = None
|
||||
if trans is not None: pool = trans.Pool
|
||||
if pool is None: return -1
|
||||
# 方法调用:接收者是本帧变量 → 取它静态类型指向的类名;接收者是模块名 → 不当方法。
|
||||
if cl.func.kind() == ast.ASTKind.Attribute:
|
||||
at: ast.Attribute* = ast.Attribute*(cl.func)
|
||||
cls: str = self._RecvClassName(trans, at.value)
|
||||
if cls is not None:
|
||||
rk_m: int = GenApi.get_ret_kind_method(pool, cls, fnm)
|
||||
if rk_m != 0: return rk_m
|
||||
sha_m: str = self.Gen.StructGen.get_struct_sha1(cls)
|
||||
if sha_m is not None:
|
||||
rk_m = self.Gen.CallGenBase.lookup_ret_kind_via_vpi(sha_m, cls, fnm)
|
||||
if rk_m != 0: return rk_m
|
||||
return -1
|
||||
# 模块级函数(裸名,或模块限定 `m.f()`):本文件表优先,再跨模块 .vpi 回退。
|
||||
rk: int = GenApi.get_ret_kind(fnm)
|
||||
if rk != 0: return rk
|
||||
mod_sha: str = None
|
||||
if cl.func.kind() == ast.ASTKind.Attribute:
|
||||
at2: ast.Attribute* = ast.Attribute*(cl.func)
|
||||
# 只认裸模块名接收者(`m.f()`);`self.h.f()` 之类链式接收者判不定 → 不猜。
|
||||
if at2.value is None or at2.value.kind() != ast.ASTKind.Name: return -1
|
||||
mn2: ast.Name* = ast.Name*(at2.value)
|
||||
mod_p: str = None
|
||||
if mn2.id is not None: mod_p = mn2.id
|
||||
if mod_p is not None:
|
||||
fi2: str = None
|
||||
if trans is not None: fi2 = trans._from_imports
|
||||
mod_sha = self.Gen.StructGen._resolve_module_sha1(mod_p, fi2)
|
||||
# 接收者既不是模块(解析不出 sha1)也不是变量(否则上面早按方法走了)→ 判不定。
|
||||
if mod_sha is None: return -1
|
||||
if mod_sha is None and trans is not None and trans._from_imports is not None:
|
||||
mod_sha = self.Gen.CallGenBase._lookup_mod_sha1(pool, trans._from_imports, fnm, 0)
|
||||
if mod_sha is not None:
|
||||
rk = self.Gen.CallGenBase.lookup_ret_kind_via_vpi(mod_sha, None, fnm)
|
||||
if rk != 0: return rk
|
||||
return -1
|
||||
|
||||
# life_of_expr - 求表达式当前值的生命期标签。
|
||||
# target_ty 参与判定:裸本帧数组名在**指针**目标位置 = 数组退让(交出本帧栈地址),
|
||||
# 在同型聚合目标位置 = 按值拷贝(LIFE_VALUE)。
|
||||
# _RecvClassName - 从方法的接收者表达式取它静态类型指向的类名(去 sha1 前缀);判不定返回 None。
|
||||
# 只认「名字变量」接收者:变量表里查不到(如 `m.f()` 里的模块名 `m`)返回 None —— 调用方据此
|
||||
# 把"模块限定函数调用"与"方法调用"分开(二者在 AST 里都是 Attribute)。
|
||||
def _RecvClassName(self, trans: HT.Translator*, recv: ast.AST*) -> str:
|
||||
"""接收者静态类型 → 类名(无 sha1 前缀);判不定返回 None"""
|
||||
if trans is None or recv is None: return None
|
||||
if recv.kind() != ast.ASTKind.Name: return None
|
||||
rn: ast.Name* = ast.Name*(recv)
|
||||
if rn.id is None: return None
|
||||
v: mlirlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, rn.id)
|
||||
if v is None: return None
|
||||
ty: mlirlite.LLVMType* = v.Ty # 变量槽类型 = <变量类型>*
|
||||
if ty is None or self.Gen.ExprGenBase.is_ptr_type(ty) == 0: return None
|
||||
p1: mlirlite.LLVMType* = ty.Pointee # 变量类型本身
|
||||
if p1 is None: return None
|
||||
# 类变量通常是指针(`S*`);值语义的类变量(`s: S`,罕见)直接取本类型。
|
||||
elem: mlirlite.LLVMType* = p1.Pointee
|
||||
if self.Gen.ExprGenBase.is_ptr_type(p1) == 0: elem = p1
|
||||
sname: str = mlirlite.get_struct_name(elem)
|
||||
if sname is None: return None
|
||||
pl: int = GenApi.sha1_prefix_len(sname)
|
||||
if pl > 0: sname = sname + pl
|
||||
return sname
|
||||
|
||||
# _CallArgNodeForParam - `@life(ret(borrows=<形参 pi>))` 在调用点对应的实参节点。
|
||||
# **方法**调用的形参表含 self(pi=0 即接收者),故 is_method=1 时偏移一格;
|
||||
# **模块限定函数**(`m.f(...)`:callee 也是 Attribute,但不是方法)形参 0 就是第一个实参,
|
||||
# 不能偏移 —— 否则 pi=0 会被解析成"模块名"这个非实参节点(现场:本函数把
|
||||
# `retdep2.IdentBorrow(l)` 的形参 0 取成了 `retdep2` → life=STATIC → 跨模块 ret 契约静默失效)。
|
||||
def _CallArgNodeForParam(self, cl: ast.Call*, pi: int, is_method: int) -> ast.AST*:
|
||||
"""第 pi 号形参对应的实参表达式;越界/判不定返回 None"""
|
||||
if cl is None or pi < 0: return None
|
||||
cargs: list[ast.AST*]* = cl.args
|
||||
if is_method != 0:
|
||||
if pi == 0:
|
||||
at: ast.Attribute* = ast.Attribute*(cl.func)
|
||||
return at.value
|
||||
if cargs is None or sizet(pi - 1) >= len(cargs): return None
|
||||
return cargs.get(sizet(pi - 1))
|
||||
if cargs is None or sizet(pi) >= len(cargs): return None
|
||||
return cargs.get(sizet(pi))
|
||||
|
||||
# _CallIsMethod - 该调用是否"接收者是对象变量"的方法调用(决定形参表是否含 self)。
|
||||
def _CallIsMethod(self, trans: HT.Translator*, cl: ast.Call*) -> int:
|
||||
"""1 = 方法调用(形参表含 self);0 = 模块限定函数 / 裸函数"""
|
||||
if cl is None or cl.func is None: return 0
|
||||
if cl.func.kind() != ast.ASTKind.Attribute: return 0
|
||||
at: ast.Attribute* = ast.Attribute*(cl.func)
|
||||
if self._RecvClassName(trans, at.value) is None: return 0
|
||||
return 1
|
||||
|
||||
# _CallRetKind - 解析被调者的 `@life(ret(...))` 契约(本文件表 → 跨模块 .vpi);-1 = 无声明/判不定。
|
||||
def _CallRetKind(self, trans: HT.Translator*, cl: ast.Call*) -> int:
|
||||
"""被调者 ret_kind 编码(GenApi.RET_KIND_*);-1 = 无声明/判不定"""
|
||||
if cl is None or cl.func is None: return -1
|
||||
fnm: str = self.Gen.ExprGenBase.get_func_name(cl.func)
|
||||
if fnm is None: return -1
|
||||
pool: memhub.MemBuddy* = None
|
||||
if trans is not None: pool = trans.Pool
|
||||
if pool is None: return -1
|
||||
# 方法调用:接收者是本帧变量 → 取它静态类型指向的类名;接收者是模块名 → 不当方法。
|
||||
if cl.func.kind() == ast.ASTKind.Attribute:
|
||||
at: ast.Attribute* = ast.Attribute*(cl.func)
|
||||
cls: str = self._RecvClassName(trans, at.value)
|
||||
if cls is not None:
|
||||
rk_m: int = GenApi.get_ret_kind_method(pool, cls, fnm)
|
||||
if rk_m != 0: return rk_m
|
||||
sha_m: str = self.Gen.StructGen.get_struct_sha1(cls)
|
||||
if sha_m is not None:
|
||||
rk_m = self.Gen.CallGenBase.lookup_ret_kind_via_vpi(sha_m, cls, fnm)
|
||||
if rk_m != 0: return rk_m
|
||||
return -1
|
||||
# 模块级函数(裸名,或模块限定 `m.f()`):本文件表优先,再跨模块 .vpi 回退。
|
||||
rk: int = GenApi.get_ret_kind(fnm)
|
||||
if rk != 0: return rk
|
||||
mod_sha: str = None
|
||||
if cl.func.kind() == ast.ASTKind.Attribute:
|
||||
at2: ast.Attribute* = ast.Attribute*(cl.func)
|
||||
# 只认裸模块名接收者(`m.f()`);`self.h.f()` 之类链式接收者判不定 → 不猜。
|
||||
if at2.value is None or at2.value.kind() != ast.ASTKind.Name: return -1
|
||||
mn2: ast.Name* = ast.Name*(at2.value)
|
||||
mod_p: str = None
|
||||
if mn2.id is not None: mod_p = mn2.id
|
||||
if mod_p is not None:
|
||||
fi2: str = None
|
||||
if trans is not None: fi2 = trans._from_imports
|
||||
mod_sha = self.Gen.StructGen._resolve_module_sha1(mod_p, fi2)
|
||||
# 接收者既不是模块(解析不出 sha1)也不是变量(否则上面早按方法走了)→ 判不定。
|
||||
if mod_sha is None: return -1
|
||||
if mod_sha is None and trans is not None and trans._from_imports is not None:
|
||||
mod_sha = self.Gen.CallGenBase._lookup_mod_sha1(pool, trans._from_imports, fnm, 0)
|
||||
if mod_sha is not None:
|
||||
rk = self.Gen.CallGenBase.lookup_ret_kind_via_vpi(mod_sha, None, fnm)
|
||||
if rk != 0: return rk
|
||||
return -1
|
||||
|
||||
# life_of_expr - 求表达式当前值的生命期标签。
|
||||
# target_ty 参与判定:裸本帧数组名在**指针**目标位置 = 数组退让(交出本帧栈地址),
|
||||
# 在同型聚合目标位置 = 按值拷贝(LIFE_VALUE)。
|
||||
def life_of_expr(self, trans: HT.Translator*, node: ast.AST*, target_ty: mlirlite.LLVMType*) -> int:
|
||||
"""表达式的 LifeTag;无法判定时 LIFE_UNKNOWN(保守放行)"""
|
||||
if node is None or trans is None: return GenTypes.LIFE_VALUE
|
||||
@@ -862,64 +1061,64 @@ novirtual class AssignGenMixin:
|
||||
if n3.id is not None:
|
||||
nm_txt = n3.id
|
||||
nm_pre = "&"
|
||||
# 泄漏点(可选,只有"确实报错"才去算):被调者那边**造成逃逸的写入点**在同一源文件里,
|
||||
# 追加一帧 "File lifetime leak ..."。"先过 life 判定再采集"是刻意的:escape_leak_line
|
||||
# 会重扫被调者函数体并动 EscConstEnv/EscIndCount,不能对"其实不报错"的实参白白跑一遍。
|
||||
leak_f: str = None
|
||||
leak_l: int = 0
|
||||
if leak_func is not None and leak_param >= 0:
|
||||
leak_l = self.Gen.StructGen.escape_leak_line(leak_func, leak_param)
|
||||
if leak_l > 0: leak_f = self.Gen.TypeGenResolve.get_current_file()
|
||||
self.Gen.TypeGenErr.fatal_error(node, f"stack address escapes: `{nm_pre}{nm_txt}` is frame-local; using it at {where} hands out a dangling pointer once this frame returns. Copy the value, or allocate on the heap (`new heap hlife ...`).", "LifetimeError", 0, 0, leak_f, leak_l)
|
||||
return 0
|
||||
|
||||
# check_return_frame_decl - **定义端** `return <本帧存储>` 的**声明感知**检查(A2 / §14.3 第 6 条)。
|
||||
# A2 移除了"一律拦"的旧判据:定义端分不清"交出本帧"与"借出调用方的存储"(后者是
|
||||
# `return <借用形参>`,一律拦会误报)。但也不能一律放 —— 无声明的函数返回本帧存储会让调用点
|
||||
# 永远只拿到"未知"(悬垂指针一路静默)。判据收敛到被调者自己的 `@life(ret(...))`:
|
||||
# · `ret(frame)` → 放行(去向由调用点负责);
|
||||
# · `ret(borrows=<该形参>)` → 放行(借出的正是那个形参);
|
||||
# · 无声明 / `ret(heap|static|borrows=none)` → `LifetimeError`(提示补声明 / 改声明)。
|
||||
# 拿不到当前函数定义(非三条函数体翻译路径)时**不判**:宁可少报,也不误报。
|
||||
def check_return_frame_decl(self, trans: HT.Translator*, value: ast.AST*) -> int:
|
||||
"""返回值是本帧存储且当前函数的 ret 声明不允许 → LifetimeError;0=放行"""
|
||||
if trans is None or value is None: return 0
|
||||
fd: ast.AST* = self.Gen.StructGen.NllCurFunc
|
||||
if fd is None: return 0
|
||||
rv_ty: mlirlite.LLVMType* = None
|
||||
if trans._cur_func is not None: rv_ty = mlirlite.function_get_ret_ty(trans._cur_func)
|
||||
# target_ty 必须传返回类型:裸本帧数组名在**指针**返回类型下是数组退让(交出栈地址),
|
||||
# 在同型聚合返回类型下是按值拷贝(LIFE_VALUE,不在此列)。
|
||||
if self.life_of_expr(trans, value, rv_ty) != GenTypes.LIFE_FRAME: return 0
|
||||
rk: int = GenApi.get_ret_kind_by_func(fd)
|
||||
if rk == GenApi.RET_KIND_FRAME: return 0
|
||||
is_borrow_kind: int = 0
|
||||
pi: int = -1
|
||||
if rk >= GenApi.RET_KIND_BORROW_BASE:
|
||||
is_borrow_kind = 1
|
||||
pi = rk - GenApi.RET_KIND_BORROW_BASE
|
||||
# 只有"返回的正是被声明的那个形参"才算借出;返回别的本帧存储仍是悬垂
|
||||
if self.Gen.StructGen._ParamIndex(ast.FunctionDef*(fd), value) == pi: return 0
|
||||
# 报文里给出名字(&x / x),便于定位
|
||||
nm_txt: str = "expression"
|
||||
nm_pre: str = ""
|
||||
if value.kind() == ast.ASTKind.Name:
|
||||
n2: ast.Name* = ast.Name*(value)
|
||||
if n2.id is not None: nm_txt = n2.id
|
||||
elif value.kind() == ast.ASTKind.UnaryOp:
|
||||
u2: ast.UnaryOp* = ast.UnaryOp*(value)
|
||||
if u2.operand is not None and u2.operand.kind() == ast.ASTKind.Name:
|
||||
n3: ast.Name* = ast.Name*(u2.operand)
|
||||
if n3.id is not None:
|
||||
nm_txt = n3.id
|
||||
nm_pre = "&"
|
||||
if rk == 0:
|
||||
self.Gen.TypeGenErr.fatal_error(value, f"stack address escapes: `{nm_pre}{nm_txt}` is frame-local and this function has no `@life(ret(...))` declaration, so callers cannot know the result's lifetime. Declare `@life(ret(frame))` (the result may point into this frame; callers must keep it in-frame) or `@life(ret(borrows=<param>))` (the result borrows a parameter), or return a value copy / a heap allocation (`new heap hlife ...`).", "LifetimeError")
|
||||
return 0
|
||||
if is_borrow_kind != 0:
|
||||
self.Gen.TypeGenErr.fatal_error(value, f"stack address escapes: `{nm_pre}{nm_txt}` is frame-local but this function declares `@life(ret(borrows=<param>))` (parameter #{pi}), which only permits returning that parameter. Return that parameter, or declare `@life(ret(frame))`.", "LifetimeError")
|
||||
return 0
|
||||
self.Gen.TypeGenErr.fatal_error(value, f"stack address escapes: `{nm_pre}{nm_txt}` is frame-local but this function is declared as non-frame (`@life(ret(heap))` / `ret(static)` / `ret(borrows=none)`); the declaration contradicts the returned value. Fix the declaration, or return a non-frame value.", "LifetimeError")
|
||||
# 泄漏点(可选,只有"确实报错"才去算):被调者那边**造成逃逸的写入点**在同一源文件里,
|
||||
# 追加一帧 "File lifetime leak ..."。"先过 life 判定再采集"是刻意的:escape_leak_line
|
||||
# 会重扫被调者函数体并动 EscConstEnv/EscIndCount,不能对"其实不报错"的实参白白跑一遍。
|
||||
leak_f: str = None
|
||||
leak_l: int = 0
|
||||
if leak_func is not None and leak_param >= 0:
|
||||
leak_l = self.Gen.StructGen.escape_leak_line(leak_func, leak_param)
|
||||
if leak_l > 0: leak_f = self.Gen.TypeGenResolve.get_current_file()
|
||||
self.Gen.TypeGenErr.fatal_error(node, f"stack address escapes: `{nm_pre}{nm_txt}` is frame-local; using it at {where} hands out a dangling pointer once this frame returns. Copy the value, or allocate on the heap (`new heap hlife ...`).", "LifetimeError", 0, 0, leak_f, leak_l)
|
||||
return 0
|
||||
|
||||
# check_return_frame_decl - **定义端** `return <本帧存储>` 的**声明感知**检查(A2 / §14.3 第 6 条)。
|
||||
# A2 移除了"一律拦"的旧判据:定义端分不清"交出本帧"与"借出调用方的存储"(后者是
|
||||
# `return <借用形参>`,一律拦会误报)。但也不能一律放 —— 无声明的函数返回本帧存储会让调用点
|
||||
# 永远只拿到"未知"(悬垂指针一路静默)。判据收敛到被调者自己的 `@life(ret(...))`:
|
||||
# · `ret(frame)` → 放行(去向由调用点负责);
|
||||
# · `ret(borrows=<该形参>)` → 放行(借出的正是那个形参);
|
||||
# · 无声明 / `ret(heap|static|borrows=none)` → `LifetimeError`(提示补声明 / 改声明)。
|
||||
# 拿不到当前函数定义(非三条函数体翻译路径)时**不判**:宁可少报,也不误报。
|
||||
def check_return_frame_decl(self, trans: HT.Translator*, value: ast.AST*) -> int:
|
||||
"""返回值是本帧存储且当前函数的 ret 声明不允许 → LifetimeError;0=放行"""
|
||||
if trans is None or value is None: return 0
|
||||
fd: ast.AST* = self.Gen.StructGen.NllCurFunc
|
||||
if fd is None: return 0
|
||||
rv_ty: mlirlite.LLVMType* = None
|
||||
if trans._cur_func is not None: rv_ty = mlirlite.function_get_ret_ty(trans._cur_func)
|
||||
# target_ty 必须传返回类型:裸本帧数组名在**指针**返回类型下是数组退让(交出栈地址),
|
||||
# 在同型聚合返回类型下是按值拷贝(LIFE_VALUE,不在此列)。
|
||||
if self.life_of_expr(trans, value, rv_ty) != GenTypes.LIFE_FRAME: return 0
|
||||
rk: int = GenApi.get_ret_kind_by_func(fd)
|
||||
if rk == GenApi.RET_KIND_FRAME: return 0
|
||||
is_borrow_kind: int = 0
|
||||
pi: int = -1
|
||||
if rk >= GenApi.RET_KIND_BORROW_BASE:
|
||||
is_borrow_kind = 1
|
||||
pi = rk - GenApi.RET_KIND_BORROW_BASE
|
||||
# 只有"返回的正是被声明的那个形参"才算借出;返回别的本帧存储仍是悬垂
|
||||
if self.Gen.StructGen._ParamIndex(ast.FunctionDef*(fd), value) == pi: return 0
|
||||
# 报文里给出名字(&x / x),便于定位
|
||||
nm_txt: str = "expression"
|
||||
nm_pre: str = ""
|
||||
if value.kind() == ast.ASTKind.Name:
|
||||
n2: ast.Name* = ast.Name*(value)
|
||||
if n2.id is not None: nm_txt = n2.id
|
||||
elif value.kind() == ast.ASTKind.UnaryOp:
|
||||
u2: ast.UnaryOp* = ast.UnaryOp*(value)
|
||||
if u2.operand is not None and u2.operand.kind() == ast.ASTKind.Name:
|
||||
n3: ast.Name* = ast.Name*(u2.operand)
|
||||
if n3.id is not None:
|
||||
nm_txt = n3.id
|
||||
nm_pre = "&"
|
||||
if rk == 0:
|
||||
self.Gen.TypeGenErr.fatal_error(value, f"stack address escapes: `{nm_pre}{nm_txt}` is frame-local and this function has no `@life(ret(...))` declaration, so callers cannot know the result's lifetime. Declare `@life(ret(frame))` (the result may point into this frame; callers must keep it in-frame) or `@life(ret(borrows=<param>))` (the result borrows a parameter), or return a value copy / a heap allocation (`new heap hlife ...`).", "LifetimeError")
|
||||
return 0
|
||||
if is_borrow_kind != 0:
|
||||
self.Gen.TypeGenErr.fatal_error(value, f"stack address escapes: `{nm_pre}{nm_txt}` is frame-local but this function declares `@life(ret(borrows=<param>))` (parameter #{pi}), which only permits returning that parameter. Return that parameter, or declare `@life(ret(frame))`.", "LifetimeError")
|
||||
return 0
|
||||
self.Gen.TypeGenErr.fatal_error(value, f"stack address escapes: `{nm_pre}{nm_txt}` is frame-local but this function is declared as non-frame (`@life(ret(heap))` / `ret(static)` / `ret(borrows=none)`); the declaration contradicts the returned value. Fix the declaration, or return a non-frame value.", "LifetimeError")
|
||||
return 0
|
||||
|
||||
# _SlifeDropOwned - 该槽已被登记为 slife 绑定时交还所有权:释放它当前拥有的对象、影子清 0。
|
||||
@@ -1148,7 +1347,7 @@ novirtual class AssignGenMixin:
|
||||
n_s: sizet = string.strlen(sv_s)
|
||||
if sizet(arr_count) < n_s:
|
||||
self.Gen.TypeGenErr.fatal_error(list_node, f"string literal length {n_s} exceeds the declared char array length {arr_count}; out-of-bounds writes are forbidden", "OverflowError")
|
||||
for bi_s in range(n_s):
|
||||
for bi_s in range(int(n_s)):
|
||||
bidx_s: mlirlite.Value* = mlirlite.const_int32(pool, bi_s)
|
||||
bp_s: mlirlite.Value* = mlirlite.build_gep_array(builder, arr_ty, elem_ty, array_alloca, bidx_s)
|
||||
if bp_s is None: break
|
||||
@@ -1162,7 +1361,7 @@ novirtual class AssignGenMixin:
|
||||
self.Gen.TypeGenErr.fatal_error(list_node, f"array initializer element count {elts_count} exceeds the declared array length {arr_count}; out-of-bounds writes are forbidden", "OverflowError")
|
||||
|
||||
# Emit GEP + store for each element
|
||||
for i in range(elts_count):
|
||||
for i in range(int(elts_count)):
|
||||
elem_node: ast.AST* = elts.get(i)
|
||||
# Explicit null check (repo convention: is None); an empty slot means an element count mismatch.
|
||||
# When the slot is null, fail fast with class/field/index context.
|
||||
@@ -1247,10 +1446,35 @@ novirtual class AssignGenMixin:
|
||||
# Double-pointer arithmetic: delegate the check to ExprGenOps.fail_if_double_ptr_binop
|
||||
self.Gen.ExprGenOps.fail_if_double_ptr_binop(pool, op, lhs, rhs, node, 1)
|
||||
|
||||
# x op= y 的操作数约束(fail-fast,先于运算与提升):
|
||||
# ① y 的有效宽度必须 <= x 的宽度。变量按其 LLVM 类型宽度;常量按其值的**最小可存宽度**
|
||||
# (所以 `u8 += 1` 合法、`u8 += 300` 报错)。y 更宽 = 隐式收窄,必须先显式收窄再写。
|
||||
# ② 不允许 y 显式有符号而 x 无符号:负值经符号扩展会变成巨大的无符号值,属静默语义改变。
|
||||
# 移位(<<= / >>=)**豁免** ①②:计数不是写回目标的值,改用静态范围检查(见下方)。
|
||||
y_node_aa: ast.AST* = None
|
||||
if node is not None and node.kind() == ast.ASTKind.AugAssign: y_node_aa = ast.AugAssign*(node).value
|
||||
if lhs_bits != 0 and rhs_bits != 0 and op != ast.OpKind.LShift and op != ast.OpKind.RShift:
|
||||
y_bits_eff: int = rhs_bits
|
||||
if y_node_aa is not None and y_node_aa.kind() == ast.ASTKind.Constant:
|
||||
cn_aa: ast.Constant* = ast.Constant*(y_node_aa)
|
||||
if cn_aa.const_kind == ast.CONST_INT: y_bits_eff = self.define_min_width(cn_aa.int_val)
|
||||
if y_bits_eff > lhs_bits:
|
||||
self.Gen.TypeGenErr.fatal_error(node, f"[TPV] augmented assignment operand too wide: the right-hand side needs {y_bits_eff} bits, " f"but the target provides only {lhs_bits} bits; an implicit narrowing is forbidden. " f"Fix: narrow the right-hand side explicitly (e.g. uint{lhs_bits}(...) / int{lhs_bits}(...)) before the operation.", "CastError")
|
||||
x_uns_aa: int = 0
|
||||
if lhs_node is not None: x_uns_aa = self.Gen.ExprGenOps._node_type_unsigned(pool, lhs_node, trans)
|
||||
y_sgn_aa: int = 0
|
||||
if y_node_aa is not None: y_sgn_aa = self.Gen.ExprGenOps._node_type_signed(pool, y_node_aa, trans)
|
||||
if x_uns_aa != 0 and y_sgn_aa != 0:
|
||||
self.Gen.TypeGenErr.fatal_error(node, "[TPV] augmented assignment operand signedness conflict: the right-hand side is explicitly signed " "while the target is unsigned; sign extension would turn negative values into huge unsigned values. " "Fix: use an unsigned right-hand side (e.g. uint32(...)), or make the target signed.", "CastError")
|
||||
|
||||
# Integer op: type promotion
|
||||
if lhs_bits > rhs_bits and rhs_bits > 0: rhs = self.Gen.ExprGenBase.coerce_to_type(builder, rhs, lhs.Ty, node, trans)
|
||||
elif rhs_bits > lhs_bits and lhs_bits > 0: lhs = self.Gen.ExprGenBase.coerce_to_type(builder, lhs, rhs.Ty, node, trans)
|
||||
|
||||
# 移位:计数本身豁免宽度/符号规则,改按**提升后**的左操作数位宽做静态范围检查
|
||||
if op == ast.OpKind.LShift or op == ast.OpKind.RShift:
|
||||
self.require_shift_count_in_range(pool, trans, node, y_node_aa, self.Gen.ExprGenBase.get_llvm_type_bits(lhs.Ty))
|
||||
|
||||
# Unsigned operands use udiv/urem; signed use sdiv/srem
|
||||
aug_unsigned: int = 0
|
||||
if lhs_node is not None:
|
||||
@@ -1612,7 +1836,7 @@ novirtual class AssignGenMixin:
|
||||
elem_ty_arr: mlirlite.LLVMType* = resolved_ty.Pointee
|
||||
lst_arr: ast.List* = ast.List*(value)
|
||||
cnt_arr: int = 0
|
||||
if lst_arr.elts is not None: cnt_arr = len(lst_arr.elts)
|
||||
if lst_arr.elts is not None: cnt_arr = int(len(lst_arr.elts))
|
||||
# Special case: a single string literal means length strlen+1 (include the NUL)
|
||||
if elem_ty_arr is not None and cnt_arr == 1 and lst_arr.elts is not None:
|
||||
e0_arr: ast.AST* = lst_arr.elts.get(0)
|
||||
@@ -2296,7 +2520,7 @@ novirtual class AssignGenMixin:
|
||||
mod: mlirlite.LLVMModule* = trans.Module
|
||||
|
||||
# const target: fail fast before assignment (Name/Attribute/Subscript)
|
||||
for ti_c in range(len(targets)):
|
||||
for ti_c in range(int(len(targets))):
|
||||
tgt_c: ast.AST* = targets.get(ti_c)
|
||||
if tgt_c is None: continue
|
||||
if tgt_c.kind() == ast.ASTKind.Name:
|
||||
@@ -2326,7 +2550,7 @@ novirtual class AssignGenMixin:
|
||||
self.const_assign_fail(tgt_c, "element assignment", at_c2.attr, self.Gen.ExprGenAttr.attr_field_decl_src(pool, trans, sub_c.value))
|
||||
|
||||
# f-string escape: results are function-local temporaries; writing them out is dangling
|
||||
for ti_f in range(len(targets)):
|
||||
for ti_f in range(int(len(targets))):
|
||||
tgt_f: ast.AST* = targets.get(ti_f)
|
||||
if tgt_f is None: continue
|
||||
tk_f: int = tgt_f.kind()
|
||||
@@ -2415,7 +2639,7 @@ novirtual class AssignGenMixin:
|
||||
|
||||
new_vars: int = 0
|
||||
tn: sizet = len(targets)
|
||||
for ti in range(tn):
|
||||
for ti in range(int(tn)):
|
||||
target: ast.AST* = targets.get(ti)
|
||||
if target is None: continue
|
||||
|
||||
@@ -2436,7 +2660,7 @@ novirtual class AssignGenMixin:
|
||||
fchain: mlirlite.ParamNode* = mlirlite.get_struct_fields(rhs_val.Ty)
|
||||
fcur: mlirlite.ParamNode* = fchain
|
||||
fti: sizet
|
||||
for fti in range(tgt_n):
|
||||
for fti in range(int(tgt_n)):
|
||||
tgt_elt: ast.AST* = tgt_elts.get(fti)
|
||||
if tgt_elt is None or fcur is None: break
|
||||
fty: mlirlite.LLVMType* = mlirlite.paramnode_get_ty(fcur)
|
||||
@@ -2875,7 +3099,7 @@ novirtual class AssignGenMixin:
|
||||
if seg is None: return None
|
||||
start: sizet = 0
|
||||
i: sizet
|
||||
for i in range(alen):
|
||||
for i in range(int(alen)):
|
||||
if annot[i] == '|':
|
||||
seglen: sizet = i - start
|
||||
# Segment [start, i): trim spaces/quotes then look up the registry
|
||||
|
||||
@@ -87,7 +87,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
|
||||
cnt: int = 0
|
||||
if ags.args is None: return 0
|
||||
bn: sizet = len(ags.args)
|
||||
for i in range(bn):
|
||||
for i in range(int(bn)):
|
||||
a0: ast.AST* = ags.args.get(i)
|
||||
if a0 is None: continue
|
||||
aname: str = None
|
||||
@@ -130,7 +130,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
|
||||
if stmts is None or trans._coro_uses_sleep != 0: return
|
||||
bn: sizet = len(stmts)
|
||||
i: sizet = 0
|
||||
for i in range(bn): self._scan_sleep_stmt(trans, stmts.get(i))
|
||||
for i in range(int(bn)): self._scan_sleep_stmt(trans, stmts.get(i))
|
||||
|
||||
def _scan_sleep_stmt(self, trans: HT.Translator*, stmt: ast.AST*):
|
||||
if stmt is None or trans._coro_uses_sleep != 0: return
|
||||
@@ -159,7 +159,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
|
||||
if trn.handlers is not None:
|
||||
nh: sizet = len(trn.handlers)
|
||||
hh: sizet = 0
|
||||
for hh in range(nh):
|
||||
for hh in range(int(nh)):
|
||||
h0: ast.AST* = trn.handlers.get(hh)
|
||||
if h0 is not None: self._scan_sleep_stmts(trans, h0.children)
|
||||
self._scan_sleep_stmts(trans, trn.orelse)
|
||||
@@ -202,7 +202,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
|
||||
if cl.args is not None:
|
||||
nb: sizet = len(cl.args)
|
||||
j: sizet = 0
|
||||
for j in range(nb): self._scan_sleep_expr(trans, cl.args.get(j))
|
||||
for j in range(int(nb)): self._scan_sleep_expr(trans, cl.args.get(j))
|
||||
return
|
||||
if k == ast.ASTKind.BinOp:
|
||||
b: ast.BinOp* = ast.BinOp*(node)
|
||||
@@ -219,14 +219,14 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
|
||||
if cm.comparators is not None:
|
||||
nb2: sizet = len(cm.comparators)
|
||||
j2: sizet = 0
|
||||
for j2 in range(nb2): self._scan_sleep_expr(trans, cm.comparators.get(j2))
|
||||
for j2 in range(int(nb2)): self._scan_sleep_expr(trans, cm.comparators.get(j2))
|
||||
return
|
||||
if k == ast.ASTKind.BoolOp:
|
||||
bo: ast.BoolOp* = ast.BoolOp*(node)
|
||||
if bo.values is not None:
|
||||
nb3: sizet = len(bo.values)
|
||||
j3: sizet = 0
|
||||
for j3 in range(nb3): self._scan_sleep_expr(trans, bo.values.get(j3))
|
||||
for j3 in range(int(nb3)): self._scan_sleep_expr(trans, bo.values.get(j3))
|
||||
return
|
||||
if k == ast.ASTKind.IfExp:
|
||||
ie: ast.IfExp* = ast.IfExp*(node)
|
||||
@@ -253,7 +253,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
|
||||
if stmts is None: return
|
||||
bn: sizet = len(stmts)
|
||||
i: sizet = 0
|
||||
for i in range(bn): self._scan_stmt(trans, stmts.get(i))
|
||||
for i in range(int(bn)): self._scan_stmt(trans, stmts.get(i))
|
||||
|
||||
def _scan_stmt(self, trans: HT.Translator*, stmt: ast.AST*):
|
||||
if stmt is None: return
|
||||
@@ -300,7 +300,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
|
||||
if tr2.handlers is not None:
|
||||
nh2: sizet = len(tr2.handlers)
|
||||
hh2: sizet = 0
|
||||
for hh2 in range(nh2):
|
||||
for hh2 in range(int(nh2)):
|
||||
h2: ast.AST* = tr2.handlers.get(hh2)
|
||||
if h2 is not None: self._scan_stmts(trans, h2.children)
|
||||
self._scan_stmts(trans, tr2.orelse)
|
||||
@@ -368,7 +368,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
|
||||
if cl.args is not None:
|
||||
nb: sizet = len(cl.args)
|
||||
j: sizet = 0
|
||||
for j in range(nb): self._scan_expr(trans, cl.args.get(j))
|
||||
for j in range(int(nb)): self._scan_expr(trans, cl.args.get(j))
|
||||
return
|
||||
if k == ast.ASTKind.UnaryOp:
|
||||
u: ast.UnaryOp* = ast.UnaryOp*(node)
|
||||
@@ -380,14 +380,14 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
|
||||
if cm.comparators is not None:
|
||||
nb2: sizet = len(cm.comparators)
|
||||
j2: sizet = 0
|
||||
for j2 in range(nb2): self._scan_expr(trans, cm.comparators.get(j2))
|
||||
for j2 in range(int(nb2)): self._scan_expr(trans, cm.comparators.get(j2))
|
||||
return
|
||||
if k == ast.ASTKind.BoolOp:
|
||||
bo: ast.BoolOp* = ast.BoolOp*(node)
|
||||
if bo.values is not None:
|
||||
nb3: sizet = len(bo.values)
|
||||
j3: sizet = 0
|
||||
for j3 in range(nb3): self._scan_expr(trans, bo.values.get(j3))
|
||||
for j3 in range(int(nb3)): self._scan_expr(trans, bo.values.get(j3))
|
||||
return
|
||||
if k == ast.ASTKind.IfExp:
|
||||
ie: ast.IfExp* = ast.IfExp*(node)
|
||||
@@ -415,12 +415,12 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
|
||||
if stmt.children is not None:
|
||||
bn4: sizet = len(stmt.children)
|
||||
i4: sizet = 0
|
||||
for i4 in range(bn4):
|
||||
for i4 in range(int(bn4)):
|
||||
if self._stmt_deep_await(stmt.children.get(i4)) != 0: return 1
|
||||
if fn3.orelse is not None:
|
||||
bn5: sizet = len(fn3.orelse)
|
||||
i5: sizet = 0
|
||||
for i5 in range(bn5):
|
||||
for i5 in range(int(bn5)):
|
||||
if self._stmt_deep_await(fn3.orelse.get(i5)) != 0: return 1
|
||||
return 0
|
||||
|
||||
@@ -445,7 +445,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
|
||||
if tr3.handlers is not None:
|
||||
nh3: sizet = len(tr3.handlers)
|
||||
hh3: sizet = 0
|
||||
for hh3 in range(nh3):
|
||||
for hh3 in range(int(nh3)):
|
||||
h3: ast.AST* = tr3.handlers.get(hh3)
|
||||
if h3 is not None and self._stmts_deep_await(h3.children) != 0: return 1
|
||||
if self._stmts_deep_await(tr3.orelse) != 0: return 1
|
||||
@@ -456,7 +456,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
|
||||
if stmts is None: return 0
|
||||
bn6: sizet = len(stmts)
|
||||
i6: sizet = 0
|
||||
for i6 in range(bn6):
|
||||
for i6 in range(int(bn6)):
|
||||
if self._stmt_deep_await(stmts.get(i6)) != 0: return 1
|
||||
return 0
|
||||
|
||||
@@ -474,7 +474,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
|
||||
nb7: sizet = len(cl4.args)
|
||||
if nb7 != 1 and nb7 != 2: return 0
|
||||
j7: sizet = 0
|
||||
for j7 in range(nb7):
|
||||
for j7 in range(int(nb7)):
|
||||
if self._find_expr_await(cl4.args.get(j7)) is not None: return 0
|
||||
return 1
|
||||
|
||||
@@ -492,7 +492,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
|
||||
if stmts is None: return
|
||||
bn: sizet = len(stmts)
|
||||
i: sizet = 0
|
||||
for i in range(bn): self._collect_locals_stmt(trans, stmts.get(i))
|
||||
for i in range(int(bn)): self._collect_locals_stmt(trans, stmts.get(i))
|
||||
|
||||
def _collect_locals_stmt(self, trans: HT.Translator*, stmt: ast.AST*):
|
||||
if stmt is None: return
|
||||
@@ -536,7 +536,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
|
||||
if tr.handlers is not None:
|
||||
nh: sizet = len(tr.handlers)
|
||||
hh: sizet = 0
|
||||
for hh in range(nh):
|
||||
for hh in range(int(nh)):
|
||||
h0: ast.AST* = tr.handlers.get(hh)
|
||||
if h0 is None: continue
|
||||
hd: ast.ExceptHandler* = ast.ExceptHandler*(h0)
|
||||
@@ -569,7 +569,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
|
||||
head: mlirlite.ParamNode* = None
|
||||
prev: mlirlite.ParamNode* = None
|
||||
fi: int = 0
|
||||
for fi in range(total):
|
||||
for fi in range(int(total)):
|
||||
fty: mlirlite.LLVMType* = i32_ty
|
||||
if fi == 4: fty = i8_ptr_ty
|
||||
elif fi >= trans._coro_extra_base:
|
||||
@@ -613,7 +613,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
|
||||
# so names are built with snprintf ("%%arg" -> "%arg", matching resume's "%frame").
|
||||
pi: int = 0
|
||||
argn_buf: str = pool.alloc(16)
|
||||
for pi in range(nparams):
|
||||
for pi in range(int(nparams)):
|
||||
if argn_buf is not None: viperlib.snprintf(argn_buf, 16, "%%arg%d", pi)
|
||||
mlirlite.add_param(pool, func, i32_ty, argn_buf)
|
||||
# register in the function table (bare name create_<name>)
|
||||
@@ -686,7 +686,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
|
||||
if slot is not None: mlirlite.build_store(builder, zero, slot)
|
||||
# store params into frame slots (index 5+i)
|
||||
pj: int = 0
|
||||
for pj in range(nparams):
|
||||
for pj in range(int(nparams)):
|
||||
pslot: mlirlite.Value* = mlirlite.build_gep_struct(builder, frame_ty, i32_ty, frame_ref, 5 + pj)
|
||||
if pslot is not None:
|
||||
arnb: str = pool.alloc(16)
|
||||
@@ -724,7 +724,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
|
||||
"""Return the wake-slot index for an async name, or -1 if none"""
|
||||
if trans is None or name is None or trans._coro_wake_names is None: return -1
|
||||
i: int = 0
|
||||
for i in range(trans._coro_wake_count):
|
||||
for i in range(int(trans._coro_wake_count)):
|
||||
addr: uint64 = uint64(trans._coro_wake_names) + i * 8
|
||||
slot: str* = (str*)(addr)
|
||||
if slot[0] is not None and string.strcmp(slot[0], name) == 0: return trans._coro_wake_idxs[i]
|
||||
@@ -769,7 +769,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
|
||||
|
||||
# GEP param slots -> register in the symbol table (index 5+i)
|
||||
pj: int = 0
|
||||
for pj in range(nparams):
|
||||
for pj in range(int(nparams)):
|
||||
pname: str = self._read_param_name(trans, pj)
|
||||
if pname is None: continue
|
||||
pslot: mlirlite.Value* = mlirlite.build_gep_struct(builder, frame_ty, i32_ty, frame_arg, 5 + pj)
|
||||
@@ -777,7 +777,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
|
||||
|
||||
# GEP local slots -> register in the symbol table
|
||||
li: int = 0
|
||||
for li in range(nlocals):
|
||||
for li in range(int(nlocals)):
|
||||
lname: str = self._read_local_name(trans, li)
|
||||
if lname is None: continue
|
||||
slot: mlirlite.Value* = mlirlite.build_gep_struct(builder, frame_ty, i32_ty, frame_arg, trans._coro_local_base + li)
|
||||
@@ -786,7 +786,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
|
||||
# M3-M7: register extra slots (wake i64 / __f_idx/__f_len/__await i32).
|
||||
# Name reverse lookup: wake=uses_sleep && ei==0; for=_coro_for_slots; else __await_M.
|
||||
ei: int = 0
|
||||
for ei in range(nextra):
|
||||
for ei in range(int(nextra)):
|
||||
ety: int = 1
|
||||
if trans._coro_extra_types is not None: ety = trans._coro_extra_types[ei]
|
||||
ety_llvm: mlirlite.LLVMType* = i32_ty
|
||||
@@ -802,7 +802,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
|
||||
is_for: int = 0
|
||||
is_len: int = 0
|
||||
kf: int = 0
|
||||
for kf in range(trans._coro_scan_for_count):
|
||||
for kf in range(int(trans._coro_scan_for_count)):
|
||||
if trans._coro_for_slots[kf] == ei:
|
||||
is_for = 1
|
||||
break
|
||||
@@ -818,7 +818,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
|
||||
# __await_M: reverse lookup the position array
|
||||
found: int = 0
|
||||
km: int = 0
|
||||
for km in range(nextra):
|
||||
for km in range(int(nextra)):
|
||||
if trans._coro_await_slots[km] == ei:
|
||||
found = 1
|
||||
break
|
||||
@@ -861,7 +861,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
|
||||
sw_count: int = 0
|
||||
bb_name: str = pool.alloc(32)
|
||||
si: int = 0
|
||||
for si in range(nseg):
|
||||
for si in range(int(nseg)):
|
||||
if bb_name is not None: viperlib.snprintf(bb_name, 32, "state%d", si)
|
||||
sbb: mlirlite.BasicBlock* = mlirlite.create_block(pool, func, bb_name)
|
||||
if sbb is None: continue
|
||||
@@ -881,6 +881,8 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
|
||||
# Set trans._cur_builder/_cur_func here; later position_at_end reuse the same builder object.
|
||||
trans._cur_func = func
|
||||
trans._cur_builder = builder
|
||||
# P2 willFree 失效表:协程体是独立帧 → 入口同样复位
|
||||
self.Gen.VarGen.freed_clear()
|
||||
seg_bb: mlirlite.BasicBlock* = mlirlite.BasicBlock*(state_bbs[0])
|
||||
mlirlite.position_at_end(builder, seg_bb)
|
||||
# M3 硬约束锚点:登记"正在翻译其 body 的协程函数",CallGenCtor._translate_new_expr 据此对
|
||||
@@ -911,7 +913,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
|
||||
if stmts is None: return
|
||||
bn: sizet = len(stmts)
|
||||
i: sizet = 0
|
||||
for i in range(bn):
|
||||
for i in range(int(bn)):
|
||||
stmt: ast.AST* = stmts.get(i)
|
||||
if stmt is None: continue
|
||||
# current block is terminated (return/suspend) -> stop this segment
|
||||
@@ -1123,7 +1125,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
|
||||
if cl.args is not None:
|
||||
nb: sizet = len(cl.args)
|
||||
j: sizet = 0
|
||||
for j in range(nb):
|
||||
for j in range(int(nb)):
|
||||
r3: ast.Await* = self._find_expr_await(cl.args.get(j))
|
||||
if r3 is not None: return r3
|
||||
return None
|
||||
@@ -1137,7 +1139,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
|
||||
if cm.comparators is not None:
|
||||
nb2: sizet = len(cm.comparators)
|
||||
j2: sizet = 0
|
||||
for j2 in range(nb2):
|
||||
for j2 in range(int(nb2)):
|
||||
r5: ast.Await* = self._find_expr_await(cm.comparators.get(j2))
|
||||
if r5 is not None: return r5
|
||||
return None
|
||||
@@ -1146,7 +1148,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
|
||||
if bo.values is not None:
|
||||
nb3: sizet = len(bo.values)
|
||||
j3: sizet = 0
|
||||
for j3 in range(nb3):
|
||||
for j3 in range(int(nb3)):
|
||||
r6: ast.Await* = self._find_expr_await(bo.values.get(j3))
|
||||
if r6 is not None: return r6
|
||||
return None
|
||||
@@ -1218,7 +1220,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
|
||||
if cl.args is not None:
|
||||
nb: sizet = len(cl.args)
|
||||
j: sizet = 0
|
||||
for j in range(nb):
|
||||
for j in range(int(nb)):
|
||||
r4: int = self._replace_expr_await(pool, cl.args.get(j), temp_name)
|
||||
if r4 == 1: return 1
|
||||
if r4 == 2:
|
||||
@@ -1243,7 +1245,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
|
||||
if cm.comparators is not None:
|
||||
nb2: sizet = len(cm.comparators)
|
||||
j2: sizet = 0
|
||||
for j2 in range(nb2):
|
||||
for j2 in range(int(nb2)):
|
||||
r7: int = self._replace_expr_await(pool, cm.comparators.get(j2), temp_name)
|
||||
if r7 == 1: return 1
|
||||
if r7 == 2:
|
||||
@@ -1255,7 +1257,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
|
||||
if bo.values is not None:
|
||||
nb3: sizet = len(bo.values)
|
||||
j3: sizet = 0
|
||||
for j3 in range(nb3):
|
||||
for j3 in range(int(nb3)):
|
||||
r8: int = self._replace_expr_await(pool, bo.values.get(j3), temp_name)
|
||||
if r8 == 1: return 1
|
||||
if r8 == 2:
|
||||
@@ -1430,7 +1432,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
|
||||
i32_ty: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
||||
tr: ast.Try* = ast.Try*(stmt)
|
||||
if tr is None: return
|
||||
nhandlers: int = 0
|
||||
nhandlers: sizet = 0
|
||||
if tr.handlers is not None: nhandlers = len(tr.handlers)
|
||||
if nhandlers == 0:
|
||||
err_t: str = pool.alloc(256)
|
||||
@@ -1459,7 +1461,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
|
||||
if check_bbs is None or handler_bbs is None: return
|
||||
check_bbs[0] = sizet(except_bb)
|
||||
hi: int = 0
|
||||
for hi in range(nhandlers):
|
||||
for hi in range(int(nhandlers)):
|
||||
# [FIX 2026-08-31] build block names with snprintf: "%" is not string formatting here,
|
||||
# so the old "%d" form produced i32 garbage and triggered a false int->ptr cast error.
|
||||
chk_name: str = pool.alloc(64)
|
||||
@@ -1472,7 +1474,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
|
||||
unmatched_bb: mlirlite.BasicBlock* = mlirlite.create_block(pool, func, "coro.try_reraise")
|
||||
codes_buf: int* = pool.alloc(256)
|
||||
# per handler: the check chain
|
||||
for hi in range(nhandlers):
|
||||
for hi in range(int(nhandlers)):
|
||||
h_node: ast.AST* = tr.handlers.get(hi)
|
||||
check_bb: mlirlite.BasicBlock* = (mlirlite.BasicBlock*)(check_bbs[hi])
|
||||
mlirlite.position_at_end(builder, check_bb)
|
||||
@@ -1493,7 +1495,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
|
||||
else:
|
||||
match_cond: mlirlite.Value* = None
|
||||
ci: int = 0
|
||||
for ci in range(ncodes):
|
||||
for ci in range(int(ncodes)):
|
||||
eq_v: mlirlite.Value* = mlirlite.build_icmp(builder, mlirlite.ICMP_EQ, exc_val, mlirlite.const_int32(pool, codes_buf[ci]))
|
||||
if match_cond is None: match_cond = eq_v
|
||||
else: match_cond = mlirlite.build_or(builder, match_cond, eq_v)
|
||||
@@ -1505,7 +1507,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
|
||||
mlirlite.build_store(builder, done_u, trans._coro_done_ptr)
|
||||
mlirlite.build_br(builder, trans._coro_exit_bb)
|
||||
# ---- each except handler ----
|
||||
for hi in range(nhandlers):
|
||||
for hi in range(int(nhandlers)):
|
||||
h_node2: ast.AST* = tr.handlers.get(hi)
|
||||
handler_bb2: mlirlite.BasicBlock* = (mlirlite.BasicBlock*)(handler_bbs[hi])
|
||||
mlirlite.position_at_end(builder, handler_bb2)
|
||||
@@ -1595,7 +1597,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
|
||||
cl: ast.Call* = ast.Call*(fn.iter)
|
||||
start_val: mlirlite.Value* = None
|
||||
end_val: mlirlite.Value* = None
|
||||
nargs: int = len(cl.args)
|
||||
nargs: int = int(len(cl.args))
|
||||
if nargs == 1:
|
||||
start_val = mlirlite.const_int32(pool, 0)
|
||||
end_val = self.Gen.ExprGen.translate_value(builder, pool, mod, cl.args.get(0), None, 0, trans)
|
||||
@@ -1710,7 +1712,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
|
||||
if cl is None or trans is None or cl.args is None: return mlirlite.const_int32(pool, 0)
|
||||
nb: sizet = len(cl.args)
|
||||
j: sizet = 0
|
||||
for j in range(nb):
|
||||
for j in range(int(nb)):
|
||||
arg0: ast.AST* = cl.args.get(j)
|
||||
async_name: str = self._var_to_async(trans, arg0)
|
||||
if async_name is None: continue
|
||||
@@ -1778,7 +1780,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
|
||||
if cl_a.args is not None:
|
||||
nb: sizet = len(cl_a.args)
|
||||
j: sizet = 0
|
||||
for j in range(nb):
|
||||
for j in range(int(nb)):
|
||||
av: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, cl_a.args.get(j), None, 0, trans)
|
||||
if av is None: av = mlirlite.const_int32(pool, 0)
|
||||
av.Next = None
|
||||
@@ -1910,7 +1912,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
|
||||
if trans is None or name is None or trans._async_funcs is None: return 0
|
||||
cnt: int = trans._async_func_count
|
||||
i: int = 0
|
||||
for i in range(cnt):
|
||||
for i in range(int(cnt)):
|
||||
addr: uint64 = uint64(trans._async_funcs) + i * 8
|
||||
slot: str* = (str*)(addr)
|
||||
if slot[0] is not None and string.strcmp(slot[0], name) == 0: return 1
|
||||
@@ -1922,7 +1924,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
|
||||
if trans._coro_var_async is None: return 1
|
||||
cnt: int = trans._coro_var_async_count
|
||||
i: int = 0
|
||||
for i in range(cnt):
|
||||
for i in range(int(cnt)):
|
||||
addr: uint64 = uint64(trans._coro_var_async) + i * 16
|
||||
pair: str* = (str*)(addr)
|
||||
if pair[0] is not None and string.strcmp(pair[0], var_name) == 0: return 0
|
||||
@@ -1939,7 +1941,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
|
||||
if trans is None or name is None or trans._async_funcs is None: return 1
|
||||
cnt: int = trans._async_func_count
|
||||
i: int = 0
|
||||
for i in range(cnt):
|
||||
for i in range(int(cnt)):
|
||||
addr: uint64 = uint64(trans._async_funcs) + i * 8
|
||||
slot: str* = (str*)(addr)
|
||||
if slot[0] is not None and string.strcmp(slot[0], name) == 0: return 0
|
||||
@@ -1956,7 +1958,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
|
||||
if trans._coro_frames is None: return 1
|
||||
cnt: int = trans._coro_frame_count
|
||||
i: int = 0
|
||||
for i in range(cnt):
|
||||
for i in range(int(cnt)):
|
||||
addr: uint64 = uint64(trans._coro_frames) + i * 16
|
||||
pair: str* = (str*)(addr)
|
||||
if pair[0] is not None and string.strcmp(pair[0], name) == 0:
|
||||
@@ -1975,7 +1977,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
|
||||
if trans is None or name is None or trans._coro_frames is None: return None
|
||||
cnt: int = trans._coro_frame_count
|
||||
i: int = 0
|
||||
for i in range(cnt):
|
||||
for i in range(int(cnt)):
|
||||
addr: uint64 = uint64(trans._coro_frames) + i * 16
|
||||
pair: str* = (str*)(addr)
|
||||
if pair[0] is not None and string.strcmp(pair[0], name) == 0:
|
||||
@@ -1990,7 +1992,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
|
||||
if trans is None or var_name is None or trans._coro_var_async is None: return None
|
||||
cnt: int = trans._coro_var_async_count
|
||||
i: int = 0
|
||||
for i in range(cnt):
|
||||
for i in range(int(cnt)):
|
||||
addr: uint64 = uint64(trans._coro_var_async) + i * 16
|
||||
pair: str* = (str*)(addr)
|
||||
if pair[0] is not None and string.strcmp(pair[0], var_name) == 0: return pair[1]
|
||||
|
||||
@@ -80,7 +80,7 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase):
|
||||
# Multiple inheritance: walk parents in declaration order, dedupe by short name (first wins).
|
||||
parent_count: int = self.Gen.StructGen.get_parent_count(class_name)
|
||||
if parent_count > 0:
|
||||
for pi in range(parent_count):
|
||||
for pi in range(int(parent_count)):
|
||||
pname: str = self.Gen.StructGen.get_parent_name_at(class_name, pi)
|
||||
if pname is None: continue
|
||||
# Look up the parent entry (prefer same-module SHA1 match).
|
||||
@@ -91,7 +91,7 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase):
|
||||
parent_vt_count: int = 0
|
||||
if vt_parent_entry is not None: parent_vt_count = vt_parent_entry.VTableMethodCount
|
||||
if parent_vt_count > 0:
|
||||
for pvi in range(parent_vt_count):
|
||||
for pvi in range(int(parent_vt_count)):
|
||||
if method_count >= VTBL_METHOD_MAX: break
|
||||
pmethod_name: str = None
|
||||
if vt_parent_entry is not None: pmethod_name = self.Gen.StructGen.get_vtable_method_name(pname, pvi)
|
||||
@@ -101,7 +101,7 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase):
|
||||
# Check whether the subclass overrides the method and whether it is @t.NoVTable.
|
||||
child_overrides: int = 0
|
||||
child_nvt: int = 0
|
||||
for ci2 in range(cn):
|
||||
for ci2 in range(int(cn)):
|
||||
stmt2: ast.AST* = children.get(ci2)
|
||||
if stmt2 is None: continue
|
||||
if stmt2.kind() != ast.ASTKind.FunctionDef: continue
|
||||
@@ -154,7 +154,7 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase):
|
||||
method_count += 1
|
||||
|
||||
# 2. Collect this class's virtual methods (skip ones already inherited).
|
||||
for ci in range(cn):
|
||||
for ci in range(int(cn)):
|
||||
stmt: ast.AST* = children.get(ci)
|
||||
if stmt is None: continue
|
||||
if stmt.kind() != ast.ASTKind.FunctionDef: continue
|
||||
@@ -171,7 +171,7 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase):
|
||||
if self.Gen.ClassGenBase._should_method_be_virtual(cd, fd, trans) == 0: continue
|
||||
# Skip if already inherited (avoid duplicates).
|
||||
already_in_vt: int = 0
|
||||
for mi in range(method_count):
|
||||
for mi in range(int(method_count)):
|
||||
exist_addr: sizet = method_names_buf[mi]
|
||||
if exist_addr != 0:
|
||||
exist_name: str = rawptr(exist_addr)
|
||||
@@ -233,7 +233,7 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase):
|
||||
|
||||
first_node: mlirlite.ParamNode* = None
|
||||
tail_node: mlirlite.ParamNode* = None
|
||||
for i in range(method_count):
|
||||
for i in range(int(method_count)):
|
||||
pnode: mlirlite.ParamNode* = mlirlite.new_param_node(pool, i8_ptr_ty)
|
||||
if pnode is None: continue
|
||||
tail_node = mlirlite.param_list_append(first_node, tail_node, pnode)
|
||||
@@ -248,7 +248,7 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase):
|
||||
init_buf[0] = '\0'
|
||||
self._vtable_strcat(init_buf, 16384, "{ ")
|
||||
|
||||
for i in range(method_count):
|
||||
for i in range(int(method_count)):
|
||||
if i > 0: self._vtable_strcat(init_buf, 16384, ", ")
|
||||
|
||||
full_name_addr: sizet = full_names_buf[i]
|
||||
@@ -335,7 +335,7 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase):
|
||||
oop_entry: GenTypes.StructEntry* = self.Gen.StructGen.find_struct_by_type(struct_ty)
|
||||
|
||||
# Pass 1: detect methods, mark OOP, and register decorator metadata.
|
||||
for ci in range(cn):
|
||||
for ci in range(int(cn)):
|
||||
stmt: ast.AST* = children.get(ci)
|
||||
if stmt is None: continue
|
||||
if stmt.kind() == ast.ASTKind.FunctionDef:
|
||||
@@ -390,14 +390,14 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase):
|
||||
|
||||
# Declare-before-define: forward-declare every method so lookups no longer depend on
|
||||
# source order (e.g. FileW.__init__ calling self.seek defined later in the class).
|
||||
for ci_pd in range(cn):
|
||||
for ci_pd in range(int(cn)):
|
||||
stmt_pd: ast.AST* = children.get(ci_pd)
|
||||
if stmt_pd is None: continue
|
||||
if stmt_pd.kind() == ast.ASTKind.FunctionDef:
|
||||
fd_pd: ast.FunctionDef* = ast.FunctionDef*(stmt_pd)
|
||||
if fd_pd is not None and fd_pd.name is not None: self._translate_method(trans, fd_pd, struct_ty, class_name, 1)
|
||||
|
||||
for ci in range(cn):
|
||||
for ci in range(int(cn)):
|
||||
stmt: ast.AST* = children.get(ci)
|
||||
if stmt is None: continue
|
||||
if stmt.kind() == ast.ASTKind.FunctionDef:
|
||||
@@ -568,7 +568,7 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase):
|
||||
an: sizet = len(alist)
|
||||
start_idx: int = 0
|
||||
if is_static_m == 0: start_idx = 1
|
||||
for ai in range(start_idx, an):
|
||||
for ai in range(int(start_idx), int(an)):
|
||||
arg: ast.Arg* = ast.Arg*(alist.get(ai))
|
||||
if arg is not None and arg.arg is not None:
|
||||
param_ty: mlirlite.LLVMType* = i32_ty
|
||||
@@ -603,7 +603,7 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase):
|
||||
if is_static_m == 0:
|
||||
mode_buf[mode_cnt] = char(48)
|
||||
mode_cnt += 1
|
||||
for ai_m in range(start_idx, an):
|
||||
for ai_m in range(int(start_idx), int(an)):
|
||||
arg_m: ast.Arg* = ast.Arg*(alist.get(ai_m))
|
||||
mm: int = 0
|
||||
if arg_m is not None and arg_m.annotation is not None:
|
||||
@@ -722,7 +722,7 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase):
|
||||
an2: sizet = len(alist2)
|
||||
start_idx2: int = 0
|
||||
if is_static_m == 0: start_idx2 = 1
|
||||
for ai2 in range(start_idx2, an2):
|
||||
for ai2 in range(int(start_idx2), int(an2)):
|
||||
arg2: ast.Arg* = ast.Arg*(alist2.get(ai2))
|
||||
if arg2 is not None and arg2.arg is not None:
|
||||
param_ty2: mlirlite.LLVMType* = i32_ty
|
||||
@@ -781,6 +781,8 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase):
|
||||
trans._cur_func = func
|
||||
trans._cur_builder = func_builder
|
||||
GenApi.clear_scope_names(trans)
|
||||
# P2 willFree 失效表:方法体也走独立路径 → 同样做函数作用域复位(栈槽地址会被后续函数复用)
|
||||
self.Gen.VarGen.freed_clear()
|
||||
|
||||
# M3 owned slife:方法体也走**独立**的转译路径(与 FuncGenMixin 的嵌套函数同款),
|
||||
# 必须自己抬升/弹回登记区间,否则方法内登记会落进外层的区间 → 内层出口 free 外层对象,
|
||||
@@ -792,17 +794,17 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase):
|
||||
body: list[ast.AST*]* = fd.children
|
||||
if body is not None:
|
||||
bn: sizet = len(body)
|
||||
for bi in range(bn):
|
||||
for bi in range(int(bn)):
|
||||
stmt: ast.AST* = body.get(bi)
|
||||
if stmt is not None: self.Gen.StmtGenDecl.pre_scan_scope_decls(trans, stmt)
|
||||
for bi in range(bn):
|
||||
for bi in range(int(bn)):
|
||||
stmt: ast.AST* = body.get(bi)
|
||||
if stmt is not None: self.Gen.StmtGenDecl.pre_scan_allocas(trans, stmt)
|
||||
|
||||
# Translate the method body.
|
||||
if body is not None:
|
||||
bn2: sizet = len(body)
|
||||
for bi2 in range(bn2):
|
||||
for bi2 in range(int(bn2)):
|
||||
stmt2: ast.AST* = body.get(bi2)
|
||||
if stmt2 is not None: self.Gen.StmtGen.translate_stmt(trans, stmt2)
|
||||
|
||||
@@ -973,7 +975,7 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase):
|
||||
|
||||
vt_first_node: mlirlite.ParamNode* = None
|
||||
vt_prev_node: mlirlite.ParamNode* = None
|
||||
for vti in range(vt_method_count):
|
||||
for vti in range(int(vt_method_count)):
|
||||
vtnode: mlirlite.ParamNode* = mlirlite.new_param_node(pool, vt_i8_ptr_ty)
|
||||
if vtnode is None: continue
|
||||
if vt_first_node is None: vt_first_node = vtnode
|
||||
@@ -1012,7 +1014,7 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase):
|
||||
|
||||
# 2. Store per-field defaults (via bi_entry, avoiding cross-module same-name lookups).
|
||||
if bi_entry is not None:
|
||||
for fi in range(bi_entry.FieldCount):
|
||||
for fi in range(int(bi_entry.FieldCount)):
|
||||
fe: GenTypes.FieldEntry* = self.Gen.StructGen._get_field_entry(bi_entry, fi)
|
||||
if fe is None or fe.DefaultVal is None: continue
|
||||
# Static-array field with a list-literal default: zero it then GEP+store each element, matching
|
||||
|
||||
@@ -42,7 +42,7 @@ novirtual class ExprGenAttr(ExprGenBase.ExprGenBase):
|
||||
suffix_start: sizet = cur_len - name_len
|
||||
if cur.Name[suffix_start - 1] == '.':
|
||||
Matched: int = 1
|
||||
for i in range(name_len):
|
||||
for i in range(int(name_len)):
|
||||
if cur.Name[suffix_start + i] != name[i]:
|
||||
Matched = 0
|
||||
break
|
||||
@@ -412,7 +412,7 @@ novirtual class ExprGenAttr(ExprGenBase.ExprGenBase):
|
||||
cd_local: int = self.Gen.TypeGenCDef.is_cdefine_found()
|
||||
if cd_local == 0:
|
||||
# determine the real module name: prefer resolving the alias from _from_imports,
|
||||
# whose entries look like "local_name:real_module" (e.g. win32base:w32.win32base)
|
||||
# whose entries look like "local_name:real_module" (e.g. win32base:win32.win32base)
|
||||
real_mod_name: str = nm_ma.id
|
||||
if trans._from_imports is not None:
|
||||
resolved_mod: str = self.Gen.ImportsGen.lookup_from_import(trans._from_imports, nm_ma.id, 0)
|
||||
@@ -422,7 +422,7 @@ novirtual class ExprGenAttr(ExprGenBase.ExprGenBase):
|
||||
while resolved_mod[rm_len] != ' ' and resolved_mod[rm_len] != '\0' and resolved_mod[rm_len] != ':': rm_len += 1
|
||||
rm_buf: str = pool.alloc(rm_len + 1)
|
||||
if rm_buf is not None:
|
||||
for rm_i in range(rm_len): rm_buf[rm_i] = resolved_mod[rm_i]
|
||||
for rm_i in range(int(rm_len)): rm_buf[rm_i] = resolved_mod[rm_i]
|
||||
rm_buf[rm_len] = '\0'
|
||||
real_mod_name = rm_buf
|
||||
# even when _from_imports is None (e.g. a plain `import ast`), still attempt the
|
||||
@@ -442,8 +442,8 @@ novirtual class ExprGenAttr(ExprGenBase.ExprGenBase):
|
||||
# cross-module float CDefine (e.g. vipermath.U_M_PI) → float constant
|
||||
dchk_ma: double = self.Gen.TypeGenCDef.lookup_cdefine_double(at.attr)
|
||||
if self.Gen.TypeGenCDef.is_cdefine_double() != 0: return mlirlite.ConstFloat(pool, mlirlite.Double(pool), dchk_ma)
|
||||
return self.Gen.ExprGenCdef._const_int_for_cdefine(pool, cdef_ma_val)
|
||||
# check whether nm_ma.id.at.attr is an imported submodule (e.g. w32.win32file);
|
||||
return self.Gen.ExprGenCdef._const_int_for_cdefine(pool, at.attr, cdef_ma_val)
|
||||
# check whether nm_ma.id.at.attr is an imported submodule (e.g. win32.win32file);
|
||||
# for a submodule do not create a global, let the nested attribute path handle it
|
||||
is_submod: int = 0
|
||||
if trans._imported_modules is not None:
|
||||
@@ -452,7 +452,7 @@ novirtual class ExprGenAttr(ExprGenBase.ExprGenBase):
|
||||
if submod_buf is not None:
|
||||
viperlib.snprintf(submod_buf, submod_len, "%s.%s", nm_ma.id, at.attr)
|
||||
if self.Gen.ImportsGen.is_module_imported(trans._imported_modules, submod_buf) == 1: is_submod = 1
|
||||
# also check from_imports: the at.attr alias maps to the real module w32.win32file
|
||||
# also check from_imports: the at.attr alias maps to the real module win32.win32file
|
||||
if is_submod == 0 and trans._from_imports is not None:
|
||||
fi_mod_raw: str = self.Gen.ImportsGen.lookup_from_import(trans._from_imports, at.attr, 0)
|
||||
if fi_mod_raw is not None:
|
||||
@@ -461,14 +461,14 @@ novirtual class ExprGenAttr(ExprGenBase.ExprGenBase):
|
||||
while fi_mod_raw[fm_len] != ' ' and fi_mod_raw[fm_len] != '\0' and fi_mod_raw[fm_len] != ':': fm_len += 1
|
||||
fi_mod: str = pool.alloc(fm_len + 1)
|
||||
if fi_mod is not None:
|
||||
for fm_i in range(fm_len): fi_mod[fm_i] = fi_mod_raw[fm_i]
|
||||
for fm_i in range(int(fm_len)): fi_mod[fm_i] = fi_mod_raw[fm_i]
|
||||
fi_mod[fm_len] = '\0'
|
||||
# check whether fi_mod starts with "nm_ma.id." (e.g. w32.win32file with w32.)
|
||||
# check whether fi_mod starts with "nm_ma.id." (e.g. win32.win32file with win32.)
|
||||
prefix_len: sizet = string.strlen(nm_ma.id)
|
||||
fi_mod_len: sizet = string.strlen(fi_mod)
|
||||
if fi_mod_len > prefix_len + 1:
|
||||
match_prefix: int = 1
|
||||
for pi in range(prefix_len):
|
||||
for pi in range(int(prefix_len)):
|
||||
if fi_mod[pi] != nm_ma.id[pi]:
|
||||
match_prefix = 0
|
||||
break
|
||||
@@ -504,7 +504,7 @@ novirtual class ExprGenAttr(ExprGenBase.ExprGenBase):
|
||||
pass
|
||||
return mlirlite.build_load(builder, load_ty_mod, mod_gv_ref)
|
||||
|
||||
# nested module attribute access (w32.win32file.GENERIC_READ → global @GENERIC_READ): when
|
||||
# nested module attribute access (win32.win32file.GENERIC_READ → global @GENERIC_READ): when
|
||||
# at.value is an Attribute resolving to an imported submodule, look up at.attr as a global constant
|
||||
if at.value.kind() == ast.ASTKind.Attribute and trans is not None:
|
||||
inner_at: ast.Attribute* = ast.Attribute*(at.value)
|
||||
@@ -512,12 +512,12 @@ novirtual class ExprGenAttr(ExprGenBase.ExprGenBase):
|
||||
inner_nm: ast.Name* = ast.Name*(inner_at.value)
|
||||
if inner_nm.id is not None and inner_at.attr is not None:
|
||||
if self.Gen.VarGen.lookup_var(trans.SymTab, inner_nm.id) is None:
|
||||
# build the qualified module name: "w32.win32file"
|
||||
# build the qualified module name: "win32.win32file"
|
||||
qual_len: sizet = string.strlen(inner_nm.id) + string.strlen(inner_at.attr) + 2
|
||||
qual_buf: str = pool.alloc(qual_len)
|
||||
if qual_buf is not None:
|
||||
viperlib.snprintf(qual_buf, qual_len, "%s.%s", inner_nm.id, inner_at.attr)
|
||||
# module knownness: an explicit import (w32.win32file) or the global module
|
||||
# module knownness: an explicit import (win32.win32file) or the global module
|
||||
# registry (pkg.X registered via `from . import X` in __init__.vp, e.g. intr.idt)
|
||||
mod_known_n: int = self.Gen.ImportsGen.is_module_imported(trans._imported_modules, qual_buf)
|
||||
if mod_known_n == 0 and self._lookup_module_sha1(qual_buf) is not None:
|
||||
@@ -547,7 +547,7 @@ novirtual class ExprGenAttr(ExprGenBase.ExprGenBase):
|
||||
pass
|
||||
return mlirlite.build_load(builder, load_ty_nested, gv_ref_nested)
|
||||
# global not found: try a CDefine constant lookup; for nested module attributes
|
||||
# like w32.win32base.INVALID_HANDLE_VALUE, resolve the CDefine from the .vpi
|
||||
# like win32.win32base.INVALID_HANDLE_VALUE, resolve the CDefine from the .vpi
|
||||
cdef_val_nested: int64 = self.Gen.TypeGenCDef.lookup_cdefine_constant(at.attr)
|
||||
if self.Gen.TypeGenCDef.is_cdefine_found() == 0:
|
||||
# build a temporary from_imports "attr:module" for the cross-module lookup
|
||||
@@ -560,7 +560,7 @@ novirtual class ExprGenAttr(ExprGenBase.ExprGenBase):
|
||||
# cross-module float CDefine → float constant
|
||||
dchk_nst: double = self.Gen.TypeGenCDef.lookup_cdefine_double(at.attr)
|
||||
if self.Gen.TypeGenCDef.is_cdefine_double() != 0: return mlirlite.ConstFloat(pool, mlirlite.Double(pool), dchk_nst)
|
||||
return self.Gen.ExprGenCdef._const_int_for_cdefine(pool, cdef_val_nested)
|
||||
return self.Gen.ExprGenCdef._const_int_for_cdefine(pool, at.attr, cdef_val_nested)
|
||||
# CDefine not found: fail-fast and stop compiling (no more undefined global
|
||||
# references, which would make llc report "use of undefined value").
|
||||
if self.Gen.ExprGenCdef._is_cdefine_name_pattern(at.attr) != 0:
|
||||
@@ -915,7 +915,7 @@ novirtual class ExprGenAttr(ExprGenBase.ExprGenBase):
|
||||
if bare is None: return None
|
||||
st: GenTypes.StructEntry* = self.Gen.StructGen.find_struct(bare)
|
||||
if st is None: return None
|
||||
for fi in range(st.FieldCount):
|
||||
for fi in range(int(st.FieldCount)):
|
||||
fe_u: GenTypes.FieldEntry* = self.Gen.StructGen._get_field_entry(st, fi)
|
||||
if fe_u is not None and fe_u.Name is not None and string.strcmp(fe_u.Name, at.attr) == 0: return fe_u
|
||||
return None
|
||||
|
||||
@@ -7,7 +7,7 @@ import string
|
||||
import stdio
|
||||
import stdlib
|
||||
import viperlib
|
||||
import w32.fileio as fileio
|
||||
import win32.fileio as fileio
|
||||
import lib.Stub.StubMerger as StubMerger
|
||||
import lib.core.VLogger as VLogger
|
||||
import lib.Projectrans.Config as Config
|
||||
@@ -36,7 +36,7 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
if sha1_arr_ptr is None: return None
|
||||
# iterate to find the matching SHA1
|
||||
idx_s: int = -1
|
||||
for i in range(count):
|
||||
for i in range(int(count)):
|
||||
sidx: sizet = sizet(i) * 17
|
||||
cur_sha1: str = sha1_arr_ptr + sidx
|
||||
if cur_sha1[0] == '\0': continue
|
||||
@@ -129,7 +129,7 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
mb_buf: str = pool.alloc(mbl + 1)
|
||||
if mb_buf is not None:
|
||||
mi2: sizet
|
||||
for mi2 in range(mbl): mb_buf[mi2] = mod_name_raw[mi2]
|
||||
for mi2 in range(int(mbl)): mb_buf[mi2] = mod_name_raw[mi2]
|
||||
mb_buf[mbl] = '\0'
|
||||
base_mod = mb_buf
|
||||
while base_mod is not None and base_mod[0] == '.': base_mod = base_mod + 1
|
||||
@@ -175,14 +175,14 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
if im_nlen > 0 and im_nlen < 256:
|
||||
im_buf: str = pool.alloc(im_nlen + 1)
|
||||
if im_buf is not None:
|
||||
for mk in range(im_nlen):
|
||||
for mk in range(int(im_nlen)):
|
||||
im_buf[mk] = imported_modules[im_start + mk]
|
||||
im_buf[im_nlen] = '\0'
|
||||
sub_fi: str = pool.alloc(im_nlen + 3)
|
||||
if sub_fi is not None:
|
||||
sub_fi[0] = '*'
|
||||
sub_fi[1] = ':'
|
||||
for mk2 in range(im_nlen):
|
||||
for mk2 in range(int(im_nlen)):
|
||||
sub_fi[2 + mk2] = im_buf[mk2]
|
||||
sub_fi[2 + im_nlen] = '\0'
|
||||
sub_val: int64 = self._lookup_cross_module_cdefine(pool, name, sub_fi, None, depth + 1, node)
|
||||
@@ -245,7 +245,7 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
while cs_line_start > 0 and pyi_buf[cs_line_start - 1] != '\n': cs_line_start -= 1
|
||||
if cs_i - cs_line_start >= cs_name_len + 1:
|
||||
nm_ok: int = 1
|
||||
for ki2 in range(cs_name_len):
|
||||
for ki2 in range(int(cs_name_len)):
|
||||
if pyi_buf[cs_line_start + ki2] != name[ki2]:
|
||||
nm_ok = 0
|
||||
break
|
||||
@@ -299,7 +299,7 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
# check whether the line starts with "name:"
|
||||
if line_len < name_len + 1: continue
|
||||
match_ok: int = 1
|
||||
for ki in range(name_len):
|
||||
for ki in range(int(name_len)):
|
||||
if pyi_buf[line_start + ki] != name[ki]:
|
||||
match_ok = 0
|
||||
break
|
||||
@@ -407,7 +407,7 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
# copy the left token into a buffer
|
||||
val_buf: str = pool.alloc(val_str_len + 1)
|
||||
if val_buf is None: continue
|
||||
for vi in range(val_str_len): val_buf[vi] = pyi_buf[val_str_start + vi]
|
||||
for vi in range(int(val_str_len)): val_buf[vi] = pyi_buf[val_str_start + vi]
|
||||
val_buf[val_str_len] = '\0'
|
||||
|
||||
# evaluate the left token
|
||||
@@ -436,7 +436,7 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
# 0xFFFFFFFF=4294967295 keeps full precision (constant side truncates to i32 -1).
|
||||
hex_result: int64 = 0
|
||||
hex_buf: str = val_buf + 2 # skip "0x"
|
||||
for hi in range(val_str_len - 2):
|
||||
for hi in range(int(val_str_len - 2)):
|
||||
hc: char = hex_buf[hi]
|
||||
hd: int = 0
|
||||
if hc >= '0' and hc <= '9': hd = hc - '0'
|
||||
@@ -530,7 +530,7 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
continue
|
||||
r_buf: str = pool.alloc(r_len + 1)
|
||||
if r_buf is None: continue
|
||||
for r_i in range(r_len): r_buf[r_i] = pyi_buf[r_start + r_i]
|
||||
for r_i in range(int(r_len)): r_buf[r_i] = pyi_buf[r_start + r_i]
|
||||
r_buf[r_len] = '\0'
|
||||
rval: int64 = 0
|
||||
if r_name != 0:
|
||||
@@ -545,7 +545,7 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
elif r_hex != 0:
|
||||
rhex_v: int64 = 0
|
||||
rh_buf: str = r_buf + 2
|
||||
for rh_i in range(r_len - 2):
|
||||
for rh_i in range(int(r_len - 2)):
|
||||
rc: char = rh_buf[rh_i]
|
||||
rd: int = 0
|
||||
if rc >= '0' and rc <= '9': rd = rc - '0'
|
||||
@@ -666,7 +666,7 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
if rc_cpos + name_len_rc <= rc_scan_end:
|
||||
rc_match: int = 1
|
||||
rc_ki: sizet
|
||||
for rc_ki in range(name_len_rc):
|
||||
for rc_ki in range(int(name_len_rc)):
|
||||
if pyi_buf[rc_cpos + rc_ki] != name[rc_ki]:
|
||||
rc_match = 0
|
||||
break
|
||||
@@ -693,7 +693,7 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
rc_sub_buf: str = pool.alloc(rc_sub_len + 1)
|
||||
if rc_sub_buf is None: continue
|
||||
rc_si: sizet
|
||||
for rc_si in range(rc_sub_len): rc_sub_buf[rc_si] = pyi_buf[rc_sub_start + rc_si]
|
||||
for rc_si in range(int(rc_sub_len)): rc_sub_buf[rc_si] = pyi_buf[rc_sub_start + rc_si]
|
||||
rc_sub_buf[rc_sub_len] = '\0'
|
||||
|
||||
# build the full module name: relative import -> package + "." + token (the base
|
||||
@@ -726,7 +726,7 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
viperlib.snprintf(rc_fi, rc_fi_len, "%s:%s", name, rc_full_mod)
|
||||
|
||||
# recursive call to search the submodule
|
||||
rc_sub_val: int = self._lookup_cross_module_cdefine(pool, name, rc_fi, None, depth + 1, node)
|
||||
rc_sub_val: int64 = self._lookup_cross_module_cdefine(pool, name, rc_fi, None, depth + 1, node)
|
||||
if self.Gen.TypeGenCDef.is_cdefine_found() != 0:
|
||||
result_val = rc_sub_val
|
||||
result_found = 1
|
||||
@@ -734,7 +734,7 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
stdlib.free(pyi_buf)
|
||||
|
||||
# 9.6 star-fallback module had no match: fall back to walking imported_modules,
|
||||
# e.g. `from stdint import *` names stdint but SHARE_READ lives in `import w32.fileio`.
|
||||
# e.g. `from stdint import *` names stdint but SHARE_READ lives in `import win32.fileio`.
|
||||
if result_found == 0 and imported_modules is not None:
|
||||
im2_len: sizet = string.strlen(imported_modules)
|
||||
im2_ci: sizet = 0
|
||||
@@ -748,14 +748,14 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
if im2_nlen > 0 and im2_nlen < 256:
|
||||
im2_buf: str = pool.alloc(im2_nlen + 1)
|
||||
if im2_buf is not None:
|
||||
for mk3 in range(im2_nlen):
|
||||
for mk3 in range(int(im2_nlen)):
|
||||
im2_buf[mk3] = imported_modules[im2_start + mk3]
|
||||
im2_buf[im2_nlen] = '\0'
|
||||
sub_fi2: str = pool.alloc(im2_nlen + 3)
|
||||
if sub_fi2 is not None:
|
||||
sub_fi2[0] = '*'
|
||||
sub_fi2[1] = ':'
|
||||
for mk4 in range(im2_nlen):
|
||||
for mk4 in range(int(im2_nlen)):
|
||||
sub_fi2[2 + mk4] = im2_buf[mk4]
|
||||
sub_fi2[2 + im2_nlen] = '\0'
|
||||
sub_val2: int64 = self._lookup_cross_module_cdefine(pool, name, sub_fi2, None, depth + 1, node)
|
||||
@@ -820,24 +820,39 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
return has_upper
|
||||
# ============================================================
|
||||
# Translate a variable reference (Name node) -> load
|
||||
def _const_int_for_cdefine(self, pool: memhub.MemBuddy*, value: int64) -> mlirlite.Value*:
|
||||
"""Materialize a CDefine integer constant as an LLVM constant (TPC TypeConvert aligned)"""
|
||||
if (value >= 0 and uint64(uint32(value)) != value) or value < -2147483648: return mlirlite.const_int64(pool, value)
|
||||
return mlirlite.const_int32(pool, int(value))
|
||||
def _const_int_for_cdefine(self, pool: memhub.MemBuddy*, name: str, value: int64) -> mlirlite.Value*:
|
||||
"""Materialize a CDefine integer constant as an LLVM constant (TPC TypeConvert aligned)
|
||||
|
||||
位宽优先用 define 的**已知位宽**(右侧显式强转或声明注解,见 register_cdefine_constant 的 bits);
|
||||
没有位宽记录时按**推断位宽**物化(值的最小可存宽度;0x80000000 → 32)。
|
||||
这样 `define X: WORD = WORD(...)` 的 X 保持 u16、`X | Y` 里两个同宽 u16 不会被提升成
|
||||
i32;无注解的 `define A = 0x80000000` 也不会退化成 i64 把 `A | B` 放大到 64 位,
|
||||
而小值 define 说成 8/16 也在算术提升下不会溢出。
|
||||
"""
|
||||
dbits: int = 0
|
||||
if name is not None: dbits = self.Gen.TypeGenCDef.lookup_cdefine_bits(name)
|
||||
if dbits == 8 or dbits == 16 or dbits == 32 or dbits == 64:
|
||||
return mlirlite.const_int(pool, dbits, self._cdefine_bits_value(value, dbits))
|
||||
# 无位宽记录(例如只从 .vpi 跨模块引入、未登记 bits)→ 按**推断位宽**物化:
|
||||
# 值的最小可存宽度(2^31..2^32-1 取 32 位无符号位型,不退化成 i64;
|
||||
# 小值取 8/16,参与算术时由整数提升先升到 i32,不会溢出)。
|
||||
wmin: int = self.Gen.AssignGen.define_min_width(value)
|
||||
return mlirlite.const_int(pool, wmin, self._cdefine_bits_value(value, wmin))
|
||||
|
||||
# ============================================================
|
||||
# cdefine_use_width_value - materialize CDefine constants at the use-site width (TPC-aligned)
|
||||
def _cdefine_bits_value(self, value: int64, bits: int) -> int64:
|
||||
"""Normalize a CDefine value to the signed representation of the given bit width"""
|
||||
if bits == 8:
|
||||
if value > 127: return value - 256
|
||||
return value
|
||||
if bits == 16:
|
||||
if value > 32767: return value - 65536
|
||||
return value
|
||||
if bits == 32:
|
||||
if value > 2147483647: return value - 4294967296
|
||||
return value
|
||||
"""把值按位宽**截断**后再重解释为该位宽的有符号表示(与 AssignGenMixin._normalize_cdefine_bits 同口径)。
|
||||
|
||||
「左侧只会看到截断了的数据」:超宽的值先按 bits 取低位(WORD(0x80000000) → 0),
|
||||
而不是只对"刚好越界一点"的值做重解释。
|
||||
实现用左移后算术右移,而不是 `value & 0xFFFFFFFF` 这类大掩码字面量:
|
||||
掩码字面量(如 4294967295)超出 int32 范围,其加宽方向在 TPC/TPV 上可能判成 sext,
|
||||
会得到 0xFFFFFFFFFFFFFFFF 而不是 0xFFFFFFFF,导致截断失效。
|
||||
"""
|
||||
if bits == 8: return (value << 56) >> 56
|
||||
if bits == 16: return (value << 48) >> 48
|
||||
if bits == 32: return (value << 32) >> 32
|
||||
return value
|
||||
|
||||
def cdefine_use_width_value(self, pool: memhub.MemBuddy*, node: ast.AST*, name: str, value: int64, target_ty: mlirlite.LLVMType*, use_ctx: str = None) -> mlirlite.Value*:
|
||||
|
||||
@@ -63,7 +63,7 @@ novirtual class ExprGenName(ExprGenBase.ExprGenBase):
|
||||
# wider -> widen in place; narrower -> fail-fast; unknown -> the existing 32/64 default.
|
||||
uw_val: mlirlite.Value* = self.Gen.ExprGenCdef.cdefine_use_width_value(pool, node, nm_id, cdef_val_c, target_ty, use_ctx)
|
||||
if uw_val is not None: return uw_val
|
||||
return self.Gen.ExprGenCdef._const_int_for_cdefine(pool, cdef_val_c)
|
||||
return self.Gen.ExprGenCdef._const_int_for_cdefine(pool, nm_id, cdef_val_c)
|
||||
|
||||
# Not found locally: try cross-module .vpi lookup, only for ALL_CAPS names so ordinary
|
||||
# variable names never trigger the costly cross-module source read.
|
||||
@@ -85,7 +85,7 @@ novirtual class ExprGenName(ExprGenBase.ExprGenBase):
|
||||
# Same use-site width rule (cross-module CDefine)
|
||||
uw_x: mlirlite.Value* = self.Gen.ExprGenCdef.cdefine_use_width_value(pool, node, nm_id, cdef_val, target_ty, use_ctx)
|
||||
if uw_x is not None: return uw_x
|
||||
return self.Gen.ExprGenCdef._const_int_for_cdefine(pool, cdef_val)
|
||||
return self.Gen.ExprGenCdef._const_int_for_cdefine(pool, nm_id, cdef_val)
|
||||
|
||||
# CDefine lookup failed: from-import module-level globals (e.g. `from Phase2 import
|
||||
# PA_DEPS_MAX_STR`, an i64 global) are located by suffix after confirming the source module.
|
||||
|
||||
@@ -219,7 +219,7 @@ novirtual class ExprGenOps(ExprGenBase.ExprGenBase):
|
||||
if bare_u is not None:
|
||||
st_u: GenTypes.StructEntry* = self.Gen.StructGen.find_struct(bare_u)
|
||||
if st_u is not None:
|
||||
for fi_u in range(st_u.FieldCount):
|
||||
for fi_u in range(int(st_u.FieldCount)):
|
||||
fe_u: GenTypes.FieldEntry* = self.Gen.StructGen._get_field_entry(st_u, fi_u)
|
||||
if fe_u is not None and fe_u.Name is not None and string.strcmp(fe_u.Name, at_u.attr) == 0:
|
||||
# [S4 read] An explicit unsigned field Spec wins
|
||||
@@ -483,6 +483,18 @@ novirtual class ExprGenOps(ExprGenBase.ExprGenBase):
|
||||
# Type alignment: icmp requires matching types on both sides. Only numeric types are aligned (same-level pointers icmp directly); i1 uses zext (1 stays 1).
|
||||
lhs_bits: int = self.get_llvm_type_bits(lhs.Ty)
|
||||
rhs_bits: int = self.get_llvm_type_bits(rhs.Ty)
|
||||
# C 语义整数提升(与算术同一口径):两侧都窄于 32 位时先提升到 i32 再比较,避免 8/16 位
|
||||
# 直接比较在符号/边界组合下语义模糊(如 signed i8 与 unsigned u8 直接比)。
|
||||
# 提升后把 bits 记为 32,下面的等宽化不再重复处理。
|
||||
if lhs_bits != 0 and rhs_bits != 0 and lhs_bits < 32 and rhs_bits < 32:
|
||||
cmp_i32_pr: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
||||
cmp_probe: mlirlite.Value* = mlirlite.const_int32(pool, 0)
|
||||
if self.should_zext_widen(pool, lhs, cmp_probe, cmp.left, trans) != 0: lhs = mlirlite.build_zext(builder, lhs, cmp_i32_pr)
|
||||
else: lhs = mlirlite.build_sext(builder, lhs, cmp_i32_pr)
|
||||
if self.should_zext_widen(pool, rhs, cmp_probe, rhs_node, trans) != 0: rhs = mlirlite.build_zext(builder, rhs, cmp_i32_pr)
|
||||
else: rhs = mlirlite.build_sext(builder, rhs, cmp_i32_pr)
|
||||
lhs_bits = 32
|
||||
rhs_bits = 32
|
||||
if lhs_bits != 0 and rhs_bits != 0 and lhs_bits != rhs_bits:
|
||||
if lhs_bits < rhs_bits:
|
||||
if self.should_zext_widen(pool, lhs, rhs, cmp.left, trans) != 0: lhs = mlirlite.build_zext(builder, lhs, rhs.Ty)
|
||||
@@ -542,6 +554,17 @@ novirtual class ExprGenOps(ExprGenBase.ExprGenBase):
|
||||
ovl_u: mlirlite.Value* = self.try_unary_operator_overload(pool, builder, mod, operand, uo.op, trans)
|
||||
if ovl_u is not None: return ovl_u
|
||||
|
||||
# C 语义整数提升(与算术/比较同一口径):一元 +/-/~ 先把窄于 32 位的整数操作数提升到 i32,
|
||||
# 这样 `0 - x` / `x ^ -1` 的常量与操作数同宽(否则是 i32 常量对 i8/i16 操作数)。
|
||||
# `not x` 不需要:结果为 i1,操作数宽度不影响语义。
|
||||
if uo.op == ast.OpKind.USub or uo.op == ast.OpKind.UAdd or uo.op == ast.OpKind.Invert:
|
||||
u_bits: int = self.get_llvm_type_bits(operand.Ty)
|
||||
if u_bits != 0 and u_bits < 32:
|
||||
u_i32_ty: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
||||
u_probe: mlirlite.Value* = mlirlite.const_int32(pool, 0)
|
||||
if self.should_zext_widen(pool, operand, u_probe, uo.operand, trans) != 0: operand = mlirlite.build_zext(builder, operand, u_i32_ty)
|
||||
else: operand = mlirlite.build_sext(builder, operand, u_i32_ty)
|
||||
|
||||
if uo.op == ast.OpKind.USub:
|
||||
# -x
|
||||
# Float type uses fneg (emit fneg directly to avoid 0.0 - x type mismatch)
|
||||
@@ -700,10 +723,10 @@ novirtual class ExprGenOps(ExprGenBase.ExprGenBase):
|
||||
# phi incoming list head/tail
|
||||
phi_head: mlirlite.PhiIncoming* = None
|
||||
phi_tail: mlirlite.PhiIncoming* = None
|
||||
phi_count: int = count - 1 # loop iteration count
|
||||
phi_count: int = int(count - 1) # loop iteration count
|
||||
|
||||
# Evaluate and short-circuit each value except the last
|
||||
for i in range(count - 1):
|
||||
for i in range(int(count - 1)):
|
||||
val_node: ast.AST* = values.get(i)
|
||||
val: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, val_node, funcs_ptr, func_count, trans)
|
||||
if val is None: return None
|
||||
@@ -1075,6 +1098,15 @@ novirtual class ExprGenOps(ExprGenBase.ExprGenBase):
|
||||
if node is not None: col_s = node.col_offset + 1
|
||||
self.Gen.TypeGenErr.fatal_error(node, f"[TPV] illegal double-pointer arithmetic type: op = '{op_lbl}'{aug_note}, " f"left operand type = {lty_lbl}, right operand type = {rty_lbl}. " f"in this language str is i8*, with no `+` concatenation semantics (`str + str` and pointer +/- pointer " f"both degrade to raw pointer add/sub => illegal IR: opt reports " f"invalid operand type for instruction). " f"silent degradation is forbidden, compilation aborted. " f"fix: build strings with f-string interpolation, string.strcat, or viperlib.snprintf.", "TypeError", col_s, col_s)
|
||||
return 1
|
||||
# binop_needs_int_promotion - whether this op takes C-style integer promotion (to >= 32 bits)
|
||||
def binop_needs_int_promotion(self, op: int) -> int:
|
||||
"""C 语义整数提升的适用范围:**算术与移位**为 1(窄于 32 位先提升再算,避免 8/16 位回绕),
|
||||
**位运算(& | ^)为 0**——按既有约定"两侧同宽就不提升"(WORD | WORD 保持 u16)。"""
|
||||
if op == ast.OpKind.Add or op == ast.OpKind.Sub or op == ast.OpKind.Mult: return 1
|
||||
if op == ast.OpKind.Div or op == ast.OpKind.FloorDiv or op == ast.OpKind.Mod: return 1
|
||||
if op == ast.OpKind.LShift or op == ast.OpKind.RShift: return 1
|
||||
return 0
|
||||
|
||||
# translate_binop - binary operations with automatic type promotion
|
||||
# ============================================================
|
||||
def translate_binop(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, node: ast.AST*, trans: HT.Translator* = None) -> mlirlite.Value*:
|
||||
@@ -1170,6 +1202,23 @@ novirtual class ExprGenOps(ExprGenBase.ExprGenBase):
|
||||
if binop_unsigned == 0 and rhs_node is not None:
|
||||
if self._node_type_unsigned(pool, rhs_node, trans) != 0: binop_unsigned = 1
|
||||
|
||||
# C 语义整数提升(仅算术/移位;位运算 & | ^ 保持"同宽不提升"):
|
||||
# 两侧都窄于 32 位时先提升到 32 位再算,避免 8/16 位回绕(如两个 16 位 define 的
|
||||
# `MAX_INCLUDES * MAX_REL_PATH_LEN` 会算成 0)。提升后把 lhs_bits/rhs_bits 记为 32,
|
||||
# 让下面的等宽化逻辑不再重复处理(否则会对同宽 i32 再做一次无意义转换)。
|
||||
if self.binop_needs_int_promotion(op) != 0:
|
||||
if lhs_bits != 0 and rhs_bits != 0 and lhs_bits < 32 and rhs_bits < 32:
|
||||
i32_ty_pr: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
||||
# 加宽方向与下面的等宽化同一判据(i1/i8 默认 zext、带符号注解 sext、无符号 typedef zext);
|
||||
# probe 只是给 should_zext_widen 提供"宽侧类型",其常量值不参与判据。
|
||||
i32_probe: mlirlite.Value* = mlirlite.const_int32(pool, 0)
|
||||
if self.should_zext_widen(pool, lhs, i32_probe, lhs_node, trans) != 0: lhs = mlirlite.build_zext(builder, lhs, i32_ty_pr)
|
||||
else: lhs = self.coerce_to_type(builder, lhs, i32_ty_pr, node, trans)
|
||||
if self.should_zext_widen(pool, rhs, i32_probe, rhs_node, trans) != 0: rhs = mlirlite.build_zext(builder, rhs, i32_ty_pr)
|
||||
else: rhs = self.coerce_to_type(builder, rhs, i32_ty_pr, node, trans)
|
||||
lhs_bits = 32
|
||||
rhs_bits = 32
|
||||
|
||||
if lhs_bits > rhs_bits:
|
||||
if self.should_zext_widen(pool, rhs, lhs, rhs_node, trans) != 0: rhs = mlirlite.build_zext(builder, rhs, lhs.Ty)
|
||||
else: rhs = self.coerce_to_type(builder, rhs, lhs.Ty, node, trans)
|
||||
@@ -1184,6 +1233,11 @@ novirtual class ExprGenOps(ExprGenBase.ExprGenBase):
|
||||
# fail-fast: illegal bitwise operand types; abort compilation (expose the upstream root cause)
|
||||
self.Gen.TypeGenErr.fatal_error(node, "illegal bitwise operand types: both lhs/rhs are non-integer types (pointer or struct), cannot perform bitwise operations", "TypeError")
|
||||
|
||||
# 移位:计数静态可确定时做范围检查(与 AugAssign 的 <<= / >>= 共用);
|
||||
# 计数宽度本身豁免,按提升/等宽化后的左操作数有效位宽比较(C 语义下 u8 << 8 合法)。
|
||||
if op == ast.OpKind.LShift or op == ast.OpKind.RShift:
|
||||
self.Gen.AssignGen.require_shift_count_in_range(pool, trans, node, rhs_node, self.get_llvm_type_bits(lhs.Ty))
|
||||
|
||||
if op == ast.OpKind.Add: return mlirlite.build_add(builder, lhs, rhs)
|
||||
elif op == ast.OpKind.Sub: return mlirlite.build_sub(builder, lhs, rhs)
|
||||
elif op == ast.OpKind.Mult: return mlirlite.build_mul(builder, lhs, rhs)
|
||||
|
||||
@@ -35,7 +35,7 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
|
||||
cnt: int = 0
|
||||
has_ref: int = 0
|
||||
imp: str = trans._imported_modules
|
||||
for i in range(an):
|
||||
for i in range(int(an)):
|
||||
arg: ast.Arg* = ast.Arg*(alist.get(i))
|
||||
if arg is None or arg.arg is None: continue
|
||||
# CVoid params are not emitted into the signature, so they must not occupy a mode slot.
|
||||
@@ -81,7 +81,7 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
|
||||
# Pre-register this module's ClassDefs in declare-only mode (struct+fields+method declares, no bodies):
|
||||
# stage 1a cannot resolve dependent CTypedef (vpi not yet injected); re-registering here keeps annotations resolvable.
|
||||
pre_i: sizet
|
||||
for pre_i in range(cn_count):
|
||||
for pre_i in range(int(cn_count)):
|
||||
pre_child: ast.AST* = ch.get(pre_i)
|
||||
if pre_child is None: continue
|
||||
if pre_child.kind() != ast.ASTKind.ClassDef: continue
|
||||
@@ -91,7 +91,7 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
|
||||
if pre_ex is not None and pre_ex.FieldCount > 0: continue
|
||||
self.Gen.ClassGenDef.translate_class_def(trans, pre_child)
|
||||
|
||||
for ci in range(cn_count):
|
||||
for ci in range(int(cn_count)):
|
||||
child: ast.AST* = ch.get(ci)
|
||||
if child is None: continue
|
||||
if child.kind() != ast.ASTKind.FunctionDef: continue
|
||||
@@ -202,7 +202,7 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
|
||||
if ags.args is not None:
|
||||
alist: list[ast.AST*]* = ags.args
|
||||
an: sizet = len(alist)
|
||||
for ai in range(an):
|
||||
for ai in range(int(an)):
|
||||
arg: ast.Arg* = ast.Arg*(alist.get(ai))
|
||||
if arg is None or arg.arg is None: continue
|
||||
# CVoid means empty param: skip
|
||||
@@ -343,7 +343,7 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
|
||||
decor_arg0: int64 = 0
|
||||
if fd.decorator_list is not None:
|
||||
ddn: sizet = len(fd.decorator_list)
|
||||
for ddi in range(ddn):
|
||||
for ddi in range(int(ddn)):
|
||||
deco: ast.AST* = fd.decorator_list.get(ddi)
|
||||
if deco is None: continue
|
||||
dk: int = deco.kind()
|
||||
@@ -409,7 +409,7 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
|
||||
if ags_e.args is not None:
|
||||
alist_e: list[ast.AST*]* = ags_e.args
|
||||
an_e: sizet = len(alist_e)
|
||||
for ai_e in range(an_e):
|
||||
for ai_e in range(int(an_e)):
|
||||
arg_e: ast.Arg* = ast.Arg*(alist_e.get(ai_e))
|
||||
if arg_e is not None and arg_e.arg is not None:
|
||||
# CVoid means empty param: skip, do not add to the function signature
|
||||
@@ -507,7 +507,7 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
|
||||
if ags.args is not None:
|
||||
alist: list[ast.AST*]* = ags.args
|
||||
an: sizet = len(alist)
|
||||
for ai in range(an):
|
||||
for ai in range(int(an)):
|
||||
arg: ast.Arg* = ast.Arg*(alist.get(ai))
|
||||
if arg is not None and arg.arg is not None:
|
||||
# CVoid means empty param: skip, do not add to the function signature
|
||||
@@ -624,13 +624,26 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
|
||||
# Generic function templates already returned early (see the top of this function); here fd.type_params is always
|
||||
# None (specialization clones clear it). The generic context is set by _specialize_generic_function before the call.
|
||||
|
||||
# arg()/va_list scope reset: save the ENCLOSING function's va_list state and start this body with
|
||||
# none of it. A non-variadic function must not inherit the previous function's buffer: that Value
|
||||
# belongs to another function while the printer emits it by NAME, so the name re-binds to
|
||||
# whatever local happens to carry it here (scene: viperlib vsnprintf emitted `va_arg ptr %9`
|
||||
# where %9 is `count_mode`'s i32 alloca -> va_arg loaded a garbage va_list pointer ->
|
||||
# access-violation inside vsnprintf). The *args setup below re-sets both for a variadic callee,
|
||||
# and old_va_list_ptr/old_vararg_name are restored after the body so that nested functions still
|
||||
# see the enclosing variadic function's buffer.
|
||||
old_va_list_ptr: mlirlite.Value* = trans._va_list_ptr
|
||||
old_vararg_name: str = trans._vararg_name
|
||||
trans._va_list_ptr = None
|
||||
trans._vararg_name = None
|
||||
|
||||
# Create allocas for the parameters
|
||||
if args_node is not None:
|
||||
ags2: ast.Arguments* = ast.Arguments*(args_node)
|
||||
if ags2.args is not None:
|
||||
alist2: list[ast.AST*]* = ags2.args
|
||||
an2: sizet = len(alist2)
|
||||
for ai2 in range(an2):
|
||||
for ai2 in range(int(an2)):
|
||||
arg2: ast.Arg* = ast.Arg*(alist2.get(ai2))
|
||||
if arg2 is not None and arg2.arg is not None:
|
||||
# CVoid means empty param: skip, do not create an alloca
|
||||
@@ -736,8 +749,8 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
|
||||
old_global_count: int = trans._global_name_count
|
||||
old_nonlocal_count: int = trans._nonlocal_name_count
|
||||
old_env_count: int = trans._closure_env_count
|
||||
old_va_list_ptr: mlirlite.Value* = trans._va_list_ptr
|
||||
old_vararg_name: str = trans._vararg_name
|
||||
# old_va_list_ptr / old_vararg_name were saved above (before the *args setup) so that this body
|
||||
# could start from a clean va_list state; the restore below hands the enclosing value back.
|
||||
# Return-annotation context ([C①], one source of type-argument fallback for bare generic construction):
|
||||
# record this function's return annotation before entering the body and restore the outer value on exit.
|
||||
old_ret_annot: ast.AST* = trans.CurFuncRetAnnot
|
||||
@@ -750,6 +763,8 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
|
||||
trans._cur_func = func
|
||||
trans._cur_builder = func_builder
|
||||
trans.CurFuncRetAnnot = fd.returns
|
||||
# P2 willFree 失效表:**函数作用域复位**(表按栈槽地址记录,槽地址会被后续函数复用)
|
||||
self.Gen.VarGen.freed_clear()
|
||||
trans._break_bb = None
|
||||
trans._continue_bb = None
|
||||
trans._in_switch_case = 0
|
||||
@@ -764,10 +779,10 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
|
||||
body: list[ast.AST*]* = fd.children
|
||||
if body is not None:
|
||||
bn: sizet = len(body)
|
||||
for bi in range(bn):
|
||||
for bi in range(int(bn)):
|
||||
stmt: ast.AST* = body.get(bi)
|
||||
if stmt is not None: self.Gen.StmtGenDecl.pre_scan_scope_decls(trans, stmt)
|
||||
for bi in range(bn):
|
||||
for bi in range(int(bn)):
|
||||
stmt: ast.AST* = body.get(bi)
|
||||
if stmt is not None: self.Gen.StmtGenDecl.pre_scan_allocas(trans, stmt)
|
||||
|
||||
@@ -780,7 +795,7 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
|
||||
# Translate the function body
|
||||
if body is not None:
|
||||
bn2: sizet = len(body)
|
||||
for bi2 in range(bn2):
|
||||
for bi2 in range(int(bn2)):
|
||||
stmt2: ast.AST* = body.get(bi2)
|
||||
if stmt2 is not None:
|
||||
# Before a nested function definition: pre-scan its in-chain nonlocals and build the heap
|
||||
@@ -937,7 +952,7 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
|
||||
wrapper: mlirlite.Function* = mlirlite.create_function(pool, mod, wrapper_name, ret_ty)
|
||||
if wrapper is None: return None
|
||||
pi: int = 0
|
||||
for pi in range(n_params):
|
||||
for pi in range(int(n_params)):
|
||||
pname_w: str = pool.alloc(16)
|
||||
if pname_w is not None:
|
||||
viperlib.snprintf(pname_w, 16, "%%a%d", pi)
|
||||
@@ -987,7 +1002,7 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
|
||||
mlirlite.position_at_end(wb, rec_bb)
|
||||
rec_args: mlirlite.Value* = None
|
||||
ri: int = 0
|
||||
for ri in range(n_params):
|
||||
for ri in range(int(n_params)):
|
||||
an_r: str = pool.alloc(16)
|
||||
if an_r is None: continue
|
||||
viperlib.snprintf(an_r, 16, "%%a%d", ri)
|
||||
@@ -1025,7 +1040,7 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
|
||||
if n_params > 0:
|
||||
fields_head: mlirlite.ParamNode* = None
|
||||
fk: int = 0
|
||||
for fk in range(n_params - 1, -1, -1):
|
||||
for fk in range(int(n_params - 1), -1, -1):
|
||||
pn_n: mlirlite.ParamNode* = mlirlite.new_param_node(pool, (mlirlite.LLVMType*)(param_tys[fk]))
|
||||
if pn_n is not None:
|
||||
pn_n.Next = fields_head
|
||||
@@ -1034,7 +1049,7 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
|
||||
if args_ty is not None:
|
||||
args_alloca = mlirlite.build_alloca(wb, args_ty)
|
||||
fi2: int = 0
|
||||
for fi2 in range(n_params):
|
||||
for fi2 in range(int(n_params)):
|
||||
fld_ty: mlirlite.LLVMType* = (mlirlite.LLVMType*)(param_tys[fi2])
|
||||
fld_ptr: mlirlite.Value* = mlirlite.build_gep_struct(wb, args_ty, fld_ty, args_alloca, fi2)
|
||||
if fld_ptr is not None:
|
||||
@@ -1105,7 +1120,7 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
|
||||
call_args: mlirlite.Value* = None
|
||||
if n_params > 0:
|
||||
k: int = 0
|
||||
for k in range(n_params):
|
||||
for k in range(int(n_params)):
|
||||
fld_ty_k: mlirlite.LLVMType* = (mlirlite.LLVMType*)(param_tys[k])
|
||||
fld_pk: mlirlite.Value* = mlirlite.build_gep_struct(wb, args_ty, fld_ty_k, args_alloca, k)
|
||||
if fld_pk is None: continue
|
||||
|
||||
@@ -91,7 +91,7 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
if ags_n.args is not None:
|
||||
alist_n: list[ast.AST*]* = ags_n.args
|
||||
an_n: sizet = len(alist_n)
|
||||
for ai_n in range(an_n):
|
||||
for ai_n in range(int(an_n)):
|
||||
arg_n: ast.Arg* = ast.Arg*(alist_n.get(ai_n))
|
||||
if arg_n is None or arg_n.arg is None: continue
|
||||
if arg_n.annotation is not None:
|
||||
@@ -144,7 +144,7 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
alist_p: list[ast.AST*]* = ags_p.args
|
||||
idx_p: int = 0
|
||||
apn: sizet = len(alist_p)
|
||||
for api in range(apn):
|
||||
for api in range(int(apn)):
|
||||
arg_p: ast.Arg* = ast.Arg*(alist_p.get(api))
|
||||
if arg_p is None or arg_p.arg is None: continue
|
||||
if arg_p.annotation is not None:
|
||||
@@ -199,6 +199,8 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
trans._cur_func = func
|
||||
trans._cur_builder = func_builder
|
||||
trans._cur_func_name = func_name
|
||||
# P2 willFree 失效表:函数作用域复位(栈槽地址会被后续函数复用,跨函数保留会误报)
|
||||
self.Gen.VarGen.freed_clear()
|
||||
GenApi.clear_scope_names(trans)
|
||||
trans._break_bb = None
|
||||
trans._continue_bb = None
|
||||
@@ -214,10 +216,10 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
body: list[ast.AST*]* = fd.children
|
||||
if body is not None:
|
||||
bn: sizet = len(body)
|
||||
for bi in range(bn):
|
||||
for bi in range(int(bn)):
|
||||
stmt: ast.AST* = body.get(bi)
|
||||
if stmt is not None: self.Gen.StmtGenDecl.pre_scan_scope_decls(trans, stmt)
|
||||
for bi in range(bn):
|
||||
for bi in range(int(bn)):
|
||||
stmt: ast.AST* = body.get(bi)
|
||||
if stmt is not None: self.Gen.StmtGenDecl.pre_scan_allocas(trans, stmt)
|
||||
|
||||
@@ -225,7 +227,7 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
# (Python semantics). Branch AnnAssign locals are already pre-registered and excluded.
|
||||
if body is not None:
|
||||
bnc: sizet = len(body)
|
||||
for bic in range(bnc):
|
||||
for bic in range(int(bnc)):
|
||||
stmtc: ast.AST* = body.get(bic)
|
||||
if stmtc is not None: self._closure_scan_captures(trans, stmtc)
|
||||
|
||||
@@ -238,7 +240,7 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
# 9. Translate the body (Nonlocal statements fill _nonlocal_names)
|
||||
if body is not None:
|
||||
bn2: sizet = len(body)
|
||||
for bi2 in range(bn2):
|
||||
for bi2 in range(int(bn2)):
|
||||
stmt2: ast.AST* = body.get(bi2)
|
||||
if stmt2 is not None: self.Gen.StmtGen.translate_stmt(trans, stmt2)
|
||||
|
||||
@@ -302,7 +304,7 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
# compile-time map): parent local -> new heap + redirect; parent nonlocal -> reuse heap.
|
||||
if env_ptr is not None:
|
||||
i8_ptr_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, i8_ptr_ty)
|
||||
for ni in range(nested_nonlocal_count):
|
||||
for ni in range(int(nested_nonlocal_count)):
|
||||
# Read the name from the _nonlocal_names buffer
|
||||
name_addr: uint64 = uint64(trans._nonlocal_names) + ni * 8
|
||||
nl_slot: str* = (str*)(name_addr)
|
||||
@@ -467,10 +469,10 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
if ifn.test is not None: self._closure_scan_captures(trans, ifn.test)
|
||||
if ifn.children is not None:
|
||||
bn_if: sizet = len(ifn.children)
|
||||
for bi_if in range(bn_if): self._closure_scan_captures(trans, ifn.children.get(bi_if))
|
||||
for bi_if in range(int(bn_if)): self._closure_scan_captures(trans, ifn.children.get(bi_if))
|
||||
if ifn.orelse is not None:
|
||||
bn_el: sizet = len(ifn.orelse)
|
||||
for bi_el in range(bn_el): self._closure_scan_captures(trans, ifn.orelse.get(bi_el))
|
||||
for bi_el in range(int(bn_el)): self._closure_scan_captures(trans, ifn.orelse.get(bi_el))
|
||||
return 1
|
||||
if k == ast.ASTKind.For:
|
||||
for_n: ast.For* = ast.For*(node)
|
||||
@@ -478,20 +480,20 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
if for_n.iter is not None: self._closure_scan_captures(trans, for_n.iter)
|
||||
if for_n.children is not None:
|
||||
bn_f: sizet = len(for_n.children)
|
||||
for bi_f in range(bn_f): self._closure_scan_captures(trans, for_n.children.get(bi_f))
|
||||
for bi_f in range(int(bn_f)): self._closure_scan_captures(trans, for_n.children.get(bi_f))
|
||||
if for_n.orelse is not None:
|
||||
bn_fe: sizet = len(for_n.orelse)
|
||||
for bi_fe in range(bn_fe): self._closure_scan_captures(trans, for_n.orelse.get(bi_fe))
|
||||
for bi_fe in range(int(bn_fe)): self._closure_scan_captures(trans, for_n.orelse.get(bi_fe))
|
||||
return 1
|
||||
if k == ast.ASTKind.While:
|
||||
wh_n: ast.While* = ast.While*(node)
|
||||
if wh_n.test is not None: self._closure_scan_captures(trans, wh_n.test)
|
||||
if wh_n.children is not None:
|
||||
bn_w: sizet = len(wh_n.children)
|
||||
for bi_w in range(bn_w): self._closure_scan_captures(trans, wh_n.children.get(bi_w))
|
||||
for bi_w in range(int(bn_w)): self._closure_scan_captures(trans, wh_n.children.get(bi_w))
|
||||
if wh_n.orelse is not None:
|
||||
bn_we: sizet = len(wh_n.orelse)
|
||||
for bi_we in range(bn_we): self._closure_scan_captures(trans, wh_n.orelse.get(bi_we))
|
||||
for bi_we in range(int(bn_we)): self._closure_scan_captures(trans, wh_n.orelse.get(bi_we))
|
||||
return 1
|
||||
if k == ast.ASTKind.Return:
|
||||
rt_n: ast.Return* = ast.Return*(node)
|
||||
@@ -505,14 +507,14 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
wth: ast.With* = ast.With*(node)
|
||||
if wth.items is not None:
|
||||
bn_wi: sizet = len(wth.items)
|
||||
for bi_wi in range(bn_wi):
|
||||
for bi_wi in range(int(bn_wi)):
|
||||
wi_n: ast.AST* = wth.items.get(bi_wi)
|
||||
if wi_n is not None and wi_n.kind() == ast.ASTKind.WithItem:
|
||||
wi_c: ast.WithItem* = ast.WithItem*(wi_n)
|
||||
if wi_c.context_expr is not None: self._closure_scan_captures(trans, wi_c.context_expr)
|
||||
if wth.children is not None:
|
||||
bn_wb: sizet = len(wth.children)
|
||||
for bi_wb in range(bn_wb): self._closure_scan_captures(trans, wth.children.get(bi_wb))
|
||||
for bi_wb in range(int(bn_wb)): self._closure_scan_captures(trans, wth.children.get(bi_wb))
|
||||
return 1
|
||||
if k == ast.ASTKind.Raise:
|
||||
rs_n: ast.Raise* = ast.Raise*(node)
|
||||
@@ -528,36 +530,36 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
tr_n: ast.Try* = ast.Try*(node)
|
||||
if tr_n.children is not None:
|
||||
bn_t: sizet = len(tr_n.children)
|
||||
for bi_t in range(bn_t): self._closure_scan_captures(trans, tr_n.children.get(bi_t))
|
||||
for bi_t in range(int(bn_t)): self._closure_scan_captures(trans, tr_n.children.get(bi_t))
|
||||
if tr_n.handlers is not None:
|
||||
bn_th: sizet = len(tr_n.handlers)
|
||||
for bi_th in range(bn_th):
|
||||
for bi_th in range(int(bn_th)):
|
||||
eh_n: ast.AST* = tr_n.handlers.get(bi_th)
|
||||
if eh_n is not None and eh_n.kind() == ast.ASTKind.ExceptHandler:
|
||||
eh_c: ast.ExceptHandler* = ast.ExceptHandler*(eh_n)
|
||||
if eh_c.children is not None:
|
||||
bn_ehb: sizet = len(eh_c.children)
|
||||
for bi_ehb in range(bn_ehb): self._closure_scan_captures(trans, eh_c.children.get(bi_ehb))
|
||||
for bi_ehb in range(int(bn_ehb)): self._closure_scan_captures(trans, eh_c.children.get(bi_ehb))
|
||||
if tr_n.orelse is not None:
|
||||
bn_to: sizet = len(tr_n.orelse)
|
||||
for bi_to in range(bn_to): self._closure_scan_captures(trans, tr_n.orelse.get(bi_to))
|
||||
for bi_to in range(int(bn_to)): self._closure_scan_captures(trans, tr_n.orelse.get(bi_to))
|
||||
if tr_n.finalbody is not None:
|
||||
bn_tf: sizet = len(tr_n.finalbody)
|
||||
for bi_tf in range(bn_tf): self._closure_scan_captures(trans, tr_n.finalbody.get(bi_tf))
|
||||
for bi_tf in range(int(bn_tf)): self._closure_scan_captures(trans, tr_n.finalbody.get(bi_tf))
|
||||
return 1
|
||||
if k == ast.ASTKind.Match:
|
||||
mt_n: ast.Match* = ast.Match*(node)
|
||||
if mt_n.subject is not None: self._closure_scan_captures(trans, mt_n.subject)
|
||||
if mt_n.cases is not None:
|
||||
bn_mc: sizet = len(mt_n.cases)
|
||||
for bi_mc in range(bn_mc):
|
||||
for bi_mc in range(int(bn_mc)):
|
||||
mc_n: ast.AST* = mt_n.cases.get(bi_mc)
|
||||
if mc_n is not None and mc_n.kind() == ast.ASTKind.MatchCase:
|
||||
mc_c: ast.MatchCase* = ast.MatchCase*(mc_n)
|
||||
if mc_c.guard is not None: self._closure_scan_captures(trans, mc_c.guard)
|
||||
if mc_c.children is not None:
|
||||
bn_mcb: sizet = len(mc_c.children)
|
||||
for bi_mcb in range(bn_mcb): self._closure_scan_captures(trans, mc_c.children.get(bi_mcb))
|
||||
for bi_mcb in range(int(bn_mcb)): self._closure_scan_captures(trans, mc_c.children.get(bi_mcb))
|
||||
return 1
|
||||
# ---- Expressions ----
|
||||
if k == ast.ASTKind.BinOp:
|
||||
@@ -573,24 +575,24 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
bop2: ast.BoolOp* = ast.BoolOp*(node)
|
||||
if bop2.values is not None:
|
||||
bn_bo: sizet = len(bop2.values)
|
||||
for bi_bo in range(bn_bo): self._closure_scan_captures(trans, bop2.values.get(bi_bo))
|
||||
for bi_bo in range(int(bn_bo)): self._closure_scan_captures(trans, bop2.values.get(bi_bo))
|
||||
return 1
|
||||
if k == ast.ASTKind.Compare:
|
||||
cmp: ast.Compare* = ast.Compare*(node)
|
||||
if cmp.left is not None: self._closure_scan_captures(trans, cmp.left)
|
||||
if cmp.comparators is not None:
|
||||
bn_cmp: sizet = len(cmp.comparators)
|
||||
for bi_cmp in range(bn_cmp): self._closure_scan_captures(trans, cmp.comparators.get(bi_cmp))
|
||||
for bi_cmp in range(int(bn_cmp)): self._closure_scan_captures(trans, cmp.comparators.get(bi_cmp))
|
||||
return 1
|
||||
if k == ast.ASTKind.Call:
|
||||
cal: ast.Call* = ast.Call*(node)
|
||||
if cal.func is not None: self._closure_scan_captures(trans, cal.func)
|
||||
if cal.args is not None:
|
||||
bn_ca: sizet = len(cal.args)
|
||||
for bi_ca in range(bn_ca): self._closure_scan_captures(trans, cal.args.get(bi_ca))
|
||||
for bi_ca in range(int(bn_ca)): self._closure_scan_captures(trans, cal.args.get(bi_ca))
|
||||
if cal.keywords is not None:
|
||||
bn_ck: sizet = len(cal.keywords)
|
||||
for bi_ck in range(bn_ck):
|
||||
for bi_ck in range(int(bn_ck)):
|
||||
kw_n: ast.AST* = cal.keywords.get(bi_ck)
|
||||
if kw_n is not None and kw_n.kind() == ast.ASTKind.Keyword:
|
||||
kw_c: ast.Keyword* = ast.Keyword*(kw_n)
|
||||
@@ -615,28 +617,28 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
dc: ast.Dict* = ast.Dict*(node)
|
||||
if dc.keys is not None:
|
||||
bn_dk: sizet = len(dc.keys)
|
||||
for bi_dk in range(bn_dk): self._closure_scan_captures(trans, dc.keys.get(bi_dk))
|
||||
for bi_dk in range(int(bn_dk)): self._closure_scan_captures(trans, dc.keys.get(bi_dk))
|
||||
if dc.values is not None:
|
||||
bn_dv: sizet = len(dc.values)
|
||||
for bi_dv in range(bn_dv): self._closure_scan_captures(trans, dc.values.get(bi_dv))
|
||||
for bi_dv in range(int(bn_dv)): self._closure_scan_captures(trans, dc.values.get(bi_dv))
|
||||
return 1
|
||||
if k == ast.ASTKind.Set:
|
||||
st_n: ast.Set* = ast.Set*(node)
|
||||
if st_n.elts is not None:
|
||||
bn_set: sizet = len(st_n.elts)
|
||||
for bi_set in range(bn_set): self._closure_scan_captures(trans, st_n.elts.get(bi_set))
|
||||
for bi_set in range(int(bn_set)): self._closure_scan_captures(trans, st_n.elts.get(bi_set))
|
||||
return 1
|
||||
if k == ast.ASTKind.List:
|
||||
ls_n: ast.List* = ast.List*(node)
|
||||
if ls_n.elts is not None:
|
||||
bn_ls: sizet = len(ls_n.elts)
|
||||
for bi_ls in range(bn_ls): self._closure_scan_captures(trans, ls_n.elts.get(bi_ls))
|
||||
for bi_ls in range(int(bn_ls)): self._closure_scan_captures(trans, ls_n.elts.get(bi_ls))
|
||||
return 1
|
||||
if k == ast.ASTKind.Tuple:
|
||||
tp_n: ast.Tuple* = ast.Tuple*(node)
|
||||
if tp_n.elts is not None:
|
||||
bn_tp: sizet = len(tp_n.elts)
|
||||
for bi_tp in range(bn_tp): self._closure_scan_captures(trans, tp_n.elts.get(bi_tp))
|
||||
for bi_tp in range(int(bn_tp)): self._closure_scan_captures(trans, tp_n.elts.get(bi_tp))
|
||||
return 1
|
||||
if k == ast.ASTKind.Starred:
|
||||
sr_n: ast.Starred* = ast.Starred*(node)
|
||||
@@ -656,7 +658,7 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
js_n: ast.JoinedStr* = ast.JoinedStr*(node)
|
||||
if js_n.values is not None:
|
||||
bn_js: sizet = len(js_n.values)
|
||||
for bi_js in range(bn_js): self._closure_scan_captures(trans, js_n.values.get(bi_js))
|
||||
for bi_js in range(int(bn_js)): self._closure_scan_captures(trans, js_n.values.get(bi_js))
|
||||
return 1
|
||||
if k == ast.ASTKind.FormattedValue:
|
||||
fv_n: ast.FormattedValue* = ast.FormattedValue*(node)
|
||||
@@ -684,14 +686,14 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
if lc_n.elt is not None: self._closure_scan_captures(trans, lc_n.elt)
|
||||
if lc_n.generators is not None:
|
||||
bn_gen: sizet = len(lc_n.generators)
|
||||
for bi_gen in range(bn_gen):
|
||||
for bi_gen in range(int(bn_gen)):
|
||||
cp_n: ast.AST* = lc_n.generators.get(bi_gen)
|
||||
if cp_n is not None and cp_n.kind() == ast.ASTKind.Comprehension:
|
||||
cp_c: ast.Comprehension* = ast.Comprehension*(cp_n)
|
||||
if cp_c.iter is not None: self._closure_scan_captures(trans, cp_c.iter)
|
||||
if cp_c.ifs is not None:
|
||||
bn_cpi: sizet = len(cp_c.ifs)
|
||||
for bi_cpi in range(bn_cpi): self._closure_scan_captures(trans, cp_c.ifs.get(bi_cpi))
|
||||
for bi_cpi in range(int(bn_cpi)): self._closure_scan_captures(trans, cp_c.ifs.get(bi_cpi))
|
||||
return 1
|
||||
if k == ast.ASTKind.DictComp:
|
||||
dcn: ast.DictComp* = ast.DictComp*(node)
|
||||
@@ -699,14 +701,14 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
if dcn.value is not None: self._closure_scan_captures(trans, dcn.value)
|
||||
if dcn.generators is not None:
|
||||
bn_dgen: sizet = len(dcn.generators)
|
||||
for bi_dgen in range(bn_dgen):
|
||||
for bi_dgen in range(int(bn_dgen)):
|
||||
cp_dn: ast.AST* = dcn.generators.get(bi_dgen)
|
||||
if cp_dn is not None and cp_dn.kind() == ast.ASTKind.Comprehension:
|
||||
cp_dc: ast.Comprehension* = ast.Comprehension*(cp_dn)
|
||||
if cp_dc.iter is not None: self._closure_scan_captures(trans, cp_dc.iter)
|
||||
if cp_dc.ifs is not None:
|
||||
bn_dcpi: sizet = len(cp_dc.ifs)
|
||||
for bi_dcpi in range(bn_dcpi): self._closure_scan_captures(trans, cp_dc.ifs.get(bi_dcpi))
|
||||
for bi_dcpi in range(int(bn_dcpi)): self._closure_scan_captures(trans, cp_dc.ifs.get(bi_dcpi))
|
||||
return 1
|
||||
return 1
|
||||
|
||||
@@ -780,7 +782,7 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
if fd is None or fd.children is None: return count
|
||||
ch: list[ast.AST*]* = fd.children
|
||||
cn: sizet = len(ch)
|
||||
for ci in range(cn):
|
||||
for ci in range(int(cn)):
|
||||
st: ast.AST* = ch.get(ci)
|
||||
if st is None: continue
|
||||
k: int = st.kind()
|
||||
@@ -790,13 +792,13 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
if nl_node is not None and nl_node.names is not None:
|
||||
nl_list: list[ast.AST*]* = nl_node.names
|
||||
nn: sizet = len(nl_list)
|
||||
for ni in range(nn):
|
||||
for ni in range(int(nn)):
|
||||
nm_node: ast.AST* = nl_list.get(ni)
|
||||
if nm_node is None: continue
|
||||
nm_x: ast.Name* = ast.Name*(nm_node)
|
||||
if nm_x is None or nm_x.id is None: continue
|
||||
dup: int = 0
|
||||
for di in range(count):
|
||||
for di in range(int(count)):
|
||||
if string.strcmp((str)(uint64(names) + di * 8), nm_x.id) == 0:
|
||||
dup = 1
|
||||
break
|
||||
@@ -820,7 +822,7 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
if trans._nl_chain_names is None: return 0
|
||||
trans._nl_chain_count = self._collect_chain_nonlocals(trans, node, trans._nl_chain_names, 0)
|
||||
trans._nl_chain_done = 1
|
||||
for ci in range(trans._nl_chain_count):
|
||||
for ci in range(int(trans._nl_chain_count)):
|
||||
nm_slot: str* = str*(uint64(trans._nl_chain_names) + ci * 8)
|
||||
nl_name: str = nm_slot[0]
|
||||
if nl_name is None: continue
|
||||
@@ -902,7 +904,7 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
if ags.args is not None:
|
||||
alist: list[ast.AST*]* = ags.args
|
||||
pn: sizet = len(alist)
|
||||
for pi in range(pn):
|
||||
for pi in range(int(pn)):
|
||||
if param_count >= 64: break
|
||||
arg_n: ast.Arg* = ast.Arg*(alist.get(pi))
|
||||
if arg_n is None or arg_n.arg is None: continue
|
||||
@@ -942,7 +944,7 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
func: mlirlite.Function* = mlirlite.create_function(pool, mod, mangled_promoted, i32_ty)
|
||||
if func is None: return None
|
||||
mlirlite.add_param(pool, func, i8_ptr_ty, "%env")
|
||||
for pi2 in range(param_count):
|
||||
for pi2 in range(int(param_count)):
|
||||
ty_addr2: uint64 = uint64(param_tys) + pi2 * 8
|
||||
ty_slot2: str* = str*(ty_addr2)
|
||||
p_ty2: mlirlite.LLVMType* = (mlirlite.LLVMType*)(ty_slot2[0])
|
||||
@@ -987,13 +989,15 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
trans._cur_func = func
|
||||
trans._cur_builder = func_builder
|
||||
trans._cur_func_name = mangled_promoted
|
||||
# P2 willFree 失效表:函数作用域复位(栈槽地址会被后续函数复用,跨函数保留会误报)
|
||||
self.Gen.VarGen.freed_clear()
|
||||
GenApi.clear_scope_names(trans)
|
||||
trans._break_bb = None
|
||||
trans._continue_bb = None
|
||||
trans._in_switch_case = 0
|
||||
|
||||
# 9. Captured variables: load values from env heap slots into local allocas
|
||||
for ci in range(cap_count):
|
||||
for ci in range(int(cap_count)):
|
||||
# Take the capture name via str pointer offset (like param_names + pi*32). Do not use
|
||||
# (str)(cap_addr): TPV compiles that to itoa, corrupting define_var keys.
|
||||
cap_slot: str = cap_names + ci * 64
|
||||
@@ -1017,7 +1021,7 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
self.Gen.VarGen.define_var(trans.SymTab, cap_slot, var_alloca)
|
||||
|
||||
# 10. Parameter allocas
|
||||
for pi3 in range(param_count):
|
||||
for pi3 in range(int(param_count)):
|
||||
ty_addr4: uint64 = uint64(param_tys) + pi3 * 8
|
||||
ty_slot4: str* = (str*)(ty_addr4)
|
||||
p_ty3: mlirlite.LLVMType* = (mlirlite.LLVMType*)(ty_slot4[0])
|
||||
@@ -1076,7 +1080,7 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
if env_size_val is not None: env_size_val.Next = None
|
||||
env_ptr = mlirlite.build_call(builder, "malloc", env_size_val, 1, i8_ptr_ty, 0)
|
||||
if env_ptr is not None:
|
||||
for ci3 in range(cap_count):
|
||||
for ci3 in range(int(cap_count)):
|
||||
# Same as step 9: null-check via str pointer offset, avoiding itoa semantics
|
||||
cap_slot3: str = cap_names + ci3 * 64
|
||||
if cap_slot3[0] == '\0': continue
|
||||
@@ -1149,10 +1153,10 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
nm: ast.Name* = ast.Name*(node)
|
||||
if nm.id is None: return cap_count
|
||||
# Skip lambda parameters
|
||||
for pi in range(param_count):
|
||||
for pi in range(int(param_count)):
|
||||
if string.strcmp(param_names + pi * 32, nm.id) == 0: return cap_count
|
||||
# Skip already-captured names
|
||||
for ci in range(cap_count):
|
||||
for ci in range(int(cap_count)):
|
||||
if string.strcmp(cap_names + ci * 64, nm.id) == 0: return cap_count
|
||||
# Look up the current scope chain
|
||||
alloca: mlirlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, nm.id)
|
||||
@@ -1184,7 +1188,7 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
if bop2.values is not None:
|
||||
vs: list[ast.AST*]* = bop2.values
|
||||
vn: sizet = len(vs)
|
||||
for vi in range(vn):
|
||||
for vi in range(int(vn)):
|
||||
cc2 = self._lambda_scan_body(trans, vs.get(vi), param_names, param_count, cap_names, cap_allocas, cap_tys, cc2)
|
||||
return cc2
|
||||
if k == ast.ASTKind.Compare:
|
||||
@@ -1193,7 +1197,7 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
if cmp.comparators is not None:
|
||||
cl: list[ast.AST*]* = cmp.comparators
|
||||
cn: sizet = len(cl)
|
||||
for cvi in range(cn):
|
||||
for cvi in range(int(cn)):
|
||||
cc3 = self._lambda_scan_body(trans, cl.get(cvi), param_names, param_count, cap_names, cap_allocas, cap_tys, cc3)
|
||||
return cc3
|
||||
if k == ast.ASTKind.Call:
|
||||
@@ -1202,12 +1206,12 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
if cal.args is not None:
|
||||
als: list[ast.AST*]* = cal.args
|
||||
an: sizet = len(als)
|
||||
for ai in range(an):
|
||||
for ai in range(int(an)):
|
||||
cc4 = self._lambda_scan_body(trans, als.get(ai), param_names, param_count, cap_names, cap_allocas, cap_tys, cc4)
|
||||
if cal.keywords is not None:
|
||||
kws: list[ast.AST*]* = cal.keywords
|
||||
kn: sizet = len(kws)
|
||||
for ki in range(kn):
|
||||
for ki in range(int(kn)):
|
||||
kw: ast.Keyword* = ast.Keyword*(kws.get(ki))
|
||||
if kw is not None and kw.value is not None:
|
||||
cc4 = self._lambda_scan_body(trans, kw.value, param_names, param_count, cap_names, cap_allocas, cap_tys, cc4)
|
||||
@@ -1232,7 +1236,7 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
if ls.elts is not None:
|
||||
es: list[ast.AST*]* = ls.elts
|
||||
en: sizet = len(es)
|
||||
for ei in range(en):
|
||||
for ei in range(int(en)):
|
||||
cc7 = self._lambda_scan_body(trans, es.get(ei), param_names, param_count, cap_names, cap_allocas, cap_tys, cc7)
|
||||
return cc7
|
||||
if k == ast.ASTKind.Lambda:
|
||||
|
||||
@@ -164,9 +164,17 @@ def is_cdefine_found() -> int:
|
||||
|
||||
|
||||
# ---------------- register_cdefine_constant (delegates to TypeGenCDef.register_cdefine_constant) ----------------
|
||||
def register_cdefine_constant(pool: memhub.MemBuddy*, name: str, value: int64):
|
||||
"""Delegate to gen.register_cdefine_constant"""
|
||||
if _g_gen is not None: _g_gen.TypeGenCDef.register_cdefine_constant(pool, name, value)
|
||||
def register_cdefine_constant(pool: memhub.MemBuddy*, name: str, value: int64, bits: int = 0):
|
||||
"""Delegate to gen.register_cdefine_constant (bits: 0=未知;8/16/32/64=已知位宽)"""
|
||||
if _g_gen is not None: _g_gen.TypeGenCDef.register_cdefine_constant(pool, name, value, bits)
|
||||
|
||||
|
||||
# ---------------- cdefine_decl_bits (delegates to AssignGenMixin.cdefine_decl_bits) ----------------
|
||||
def cdefine_decl_bits(pool: memhub.MemBuddy*, annot: ast.AST*, value_node: ast.AST*) -> int:
|
||||
"""define 声明的位宽裁决(注解 → 右侧显式强转 → 值的最小可存宽度);0=非整型/无法裁决。
|
||||
跨模块(.vpi 重解析 define 的 AST)注册时必须用它,否则使用方位宽与定义方不一致。"""
|
||||
if _g_gen is not None: return _g_gen.AssignGen.cdefine_decl_bits(pool, annot, value_node)
|
||||
return 0
|
||||
|
||||
|
||||
# ---------------- register_cdefine_double (delegates to TypeGenCDef.register_cdefine_double) ----------------
|
||||
@@ -341,7 +349,7 @@ def compute_willfree_mask(fd: ast.AST*) -> int:
|
||||
dn: sizet = len(dl)
|
||||
if dn == 0: return 0
|
||||
mask: int = 0
|
||||
for i in range(dn):
|
||||
for i in range(int(dn)):
|
||||
deco: ast.AST* = dl.get(i)
|
||||
if deco is None or deco.kind() != ast.ASTKind.Call: continue
|
||||
dc: ast.Call* = ast.Call*(deco)
|
||||
@@ -352,7 +360,7 @@ def compute_willfree_mask(fd: ast.AST*) -> int:
|
||||
# @life(<tag>, <tag>, ...):逐个标签并入
|
||||
if dc.args is not None:
|
||||
an: sizet = len(dc.args)
|
||||
for j in range(an):
|
||||
for j in range(int(an)):
|
||||
mask = _WillFreeMergeTag(fn, dc.args.get(j), mask)
|
||||
elif string.strcmp(fname.id, "willFree") == 0:
|
||||
# 兼容形式:@willFree(0) 直接叠加(与 @life 等价、可混用)
|
||||
@@ -636,7 +644,7 @@ def _WillFreeArgIndex(fn: ast.FunctionDef*, arg: ast.AST*) -> int:
|
||||
if arg.kind() == ast.ASTKind.Name:
|
||||
nma: ast.Name* = ast.Name*(arg)
|
||||
if nma.id is None: return -1
|
||||
for k in range(pn):
|
||||
for k in range(int(pn)):
|
||||
pa: ast.AST* = pl.get(k)
|
||||
if pa is None or pa.kind() != ast.ASTKind.Arg: continue
|
||||
aa: ast.Arg* = ast.Arg*(pa)
|
||||
@@ -901,7 +909,7 @@ def fail_if_fstring_escape(node: ast.AST*, where: str) -> int:
|
||||
tp: ast.Tuple* = ast.Tuple*(node)
|
||||
if tp.elts is not None:
|
||||
en: sizet = len(tp.elts)
|
||||
for i in range(en):
|
||||
for i in range(int(en)):
|
||||
e: ast.AST* = tp.elts.get(i)
|
||||
if e is not None:
|
||||
ek: int = e.kind()
|
||||
|
||||
@@ -49,7 +49,7 @@ novirtual class TranslatorGenMixin:
|
||||
if ch is None: return 0
|
||||
cn_count: sizet = len(ch)
|
||||
added_total: int = 0
|
||||
for ci in range(cn_count):
|
||||
for ci in range(int(cn_count)):
|
||||
child: ast.AST* = ch.get(ci)
|
||||
if child is None: continue
|
||||
kd: int = child.kind()
|
||||
@@ -123,7 +123,7 @@ novirtual class TranslatorGenMixin:
|
||||
al: list[ast.AST*]* = ags.args
|
||||
an: sizet = len(al)
|
||||
ai: sizet
|
||||
for ai in range(an):
|
||||
for ai in range(int(an)):
|
||||
ag: ast.Arg* = ast.Arg*(al.get(ai))
|
||||
if ag is not None and ag.annotation is not None: self.Gen.TypeGenResolve.resolve_annotation_type(pool, ag.annotation, imp, fi, trans, 0)
|
||||
|
||||
@@ -194,7 +194,7 @@ novirtual class TranslatorGenMixin:
|
||||
elem_ty_str_n: str = pool.alloc_tagged(4096, memhub.DETSITE_TRANSLATORGEN_185)
|
||||
if elem_ty_str_n is None: return None
|
||||
mlirlite.TypePrint(elem_ty_str_n, 4096, elem_ty, pool)
|
||||
for i_n in range(cnt_total_n):
|
||||
for i_n in range(int(cnt_total_n)):
|
||||
sub_n: str = None
|
||||
if i_n < elts_count:
|
||||
en_n: ast.AST* = elts.get(i_n)
|
||||
@@ -216,7 +216,7 @@ novirtual class TranslatorGenMixin:
|
||||
pos_n += sizet(written_n)
|
||||
viperlib.snprintf(buf_n + pos_n, buf_size_n - pos_n, "]")
|
||||
return buf_n
|
||||
for zi in range(elts_count):
|
||||
for zi in range(int(elts_count)):
|
||||
ze: ast.AST* = elts.get(zi)
|
||||
if ze is None: continue
|
||||
zk: int = ze.kind()
|
||||
@@ -245,7 +245,7 @@ novirtual class TranslatorGenMixin:
|
||||
|
||||
# Write elements up to the declared length arr_count, zero-padding short lists so the
|
||||
# initializer element count always matches the type llc expects.
|
||||
for i in range(cnt_total):
|
||||
for i in range(int(cnt_total)):
|
||||
elem_val: int64 = 0
|
||||
if i < elts_count:
|
||||
elem_node: ast.AST* = elts.get(i)
|
||||
@@ -291,7 +291,7 @@ novirtual class TranslatorGenMixin:
|
||||
if buf is None: return None
|
||||
written: int = viperlib.snprintf(buf, buf_size, "[")
|
||||
pos: sizet = sizet(written)
|
||||
for i in range(sizet(arr_count)):
|
||||
for i in range(int(sizet(arr_count))):
|
||||
if i > 0:
|
||||
written = viperlib.snprintf(buf + pos, buf_size - pos, ", ")
|
||||
pos += sizet(written)
|
||||
@@ -841,7 +841,7 @@ novirtual class TranslatorGenMixin:
|
||||
pos_v += sizet(written_v)
|
||||
else:
|
||||
fc_v: int = resolved_stg_entry.FieldCount
|
||||
for fi_v in range(1, fc_v):
|
||||
for fi_v in range(1, int(fc_v)):
|
||||
fe_v: GenTypes.FieldEntry* = self.Gen.StructGen._get_field_entry(resolved_stg_entry, fi_v)
|
||||
if fe_v is None or fe_v.Ty is None: continue
|
||||
ty_buf_v: str = pool.alloc_tagged(4096, memhub.DETSITE_TRANSLATORGEN_832)
|
||||
@@ -951,13 +951,15 @@ novirtual class TranslatorGenMixin:
|
||||
# Set the current translation context (Handles access it via self.Trans._cur_builder)
|
||||
trans._cur_func = main_func
|
||||
trans._cur_builder = builder
|
||||
# P2 willFree 失效表:进入 main 体前复位(避免上一翻译单元残留的栈槽失效态泄漏进来)
|
||||
self.Gen.VarGen.freed_clear()
|
||||
|
||||
# Pre-process module-level variables (create LLVM globals) before pre_scan_allocas, or
|
||||
# PreScan would create local allocas that other functions reference by SSA number.
|
||||
ch: list[ast.AST*]* = tree.children
|
||||
if ch is not None:
|
||||
cn_count: sizet = len(ch)
|
||||
for ci in range(cn_count):
|
||||
for ci in range(int(cn_count)):
|
||||
child: ast.AST* = ch.get(ci)
|
||||
if child is not None:
|
||||
kd: int = child.kind()
|
||||
@@ -966,7 +968,7 @@ novirtual class TranslatorGenMixin:
|
||||
# Prescan top-level statements to pre-create allocas for in-function AnnAssign; module-level
|
||||
# variables are already registered so PreScan's lookup_current skips them.
|
||||
if ch is not None:
|
||||
for ci in range(cn_count):
|
||||
for ci in range(int(cn_count)):
|
||||
child: ast.AST* = ch.get(ci)
|
||||
if child is not None: self.Gen.StmtGenDecl.pre_scan_allocas(trans, child)
|
||||
|
||||
@@ -1003,7 +1005,7 @@ novirtual class TranslatorGenMixin:
|
||||
def is_global_name(self, trans: HT.Translator*, name: str) -> int:
|
||||
"""Return 1 when name is in the current function's global set."""
|
||||
if trans is None or name is None or trans._global_names is None: return 0
|
||||
for i in range(trans._global_name_count):
|
||||
for i in range(int(trans._global_name_count)):
|
||||
entry_addr: uint64 = uint64(trans._global_names) + i * 8
|
||||
slot_ptr: str* = (str*)(entry_addr)
|
||||
entry: str = slot_ptr[0]
|
||||
@@ -1062,7 +1064,7 @@ novirtual class TranslatorGenMixin:
|
||||
def is_nonlocal_name(self, trans: HT.Translator*, name: str) -> int:
|
||||
"""Return 1 when name is in the current function's nonlocal set."""
|
||||
if trans is None or name is None or trans._nonlocal_names is None: return 0
|
||||
for i in range(trans._nonlocal_name_count):
|
||||
for i in range(int(trans._nonlocal_name_count)):
|
||||
entry_addr: uint64 = uint64(trans._nonlocal_names) + i * 8
|
||||
slot_ptr: str* = (str*)(entry_addr)
|
||||
entry: str = slot_ptr[0]
|
||||
@@ -1073,7 +1075,7 @@ novirtual class TranslatorGenMixin:
|
||||
def get_nonlocal_index(self, trans: HT.Translator*, name: str) -> int:
|
||||
"""Return the 0-based env index of a nonlocal variable; -1 when not found."""
|
||||
if trans is None or name is None or trans._nonlocal_names is None: return -1
|
||||
for i in range(trans._nonlocal_name_count):
|
||||
for i in range(int(trans._nonlocal_name_count)):
|
||||
entry_addr: uint64 = uint64(trans._nonlocal_names) + i * 8
|
||||
slot_ptr: str* = (str*)(entry_addr)
|
||||
entry: str = slot_ptr[0]
|
||||
|
||||
@@ -41,7 +41,10 @@ novirtual class TypeGenBase:
|
||||
CDefineNames: list[str]*
|
||||
CDefineValues: list[str]*
|
||||
CDefineDoubleFlags: list[int]*
|
||||
CDefineStrFlags: list[int]* # 1=string value (e.g. ZLIB_VERSION: t.CDefine = "1.3.2")
|
||||
CDefineStrFlags: list[int]* # 1=string value (e.g. ZLIB_VERSION: t.CDefine = "1.3.2")
|
||||
# 值表达式的**位宽**(bit);0=未知(按值推断)。来源:右侧显式强转(如 WORD(0x0080))优先,
|
||||
# 否则声明注解(define X: WORD = ... 的 T)。物化时据此定宽,避免小位宽常量被提升为 i32。
|
||||
CDefineBits: list[int]*
|
||||
CDefineIsDouble: int
|
||||
CDefineIsStr: int
|
||||
CDefineFound: int
|
||||
@@ -111,7 +114,8 @@ novirtual class TypeGenBase:
|
||||
self.CDefineNames = None
|
||||
self.CDefineValues = None
|
||||
self.CDefineDoubleFlags = None
|
||||
self.CDefineStrFlags = None
|
||||
self.CDefineStrFlags = None
|
||||
self.CDefineBits = None
|
||||
self.CDefineIsDouble = 0
|
||||
self.CDefineIsStr = 0
|
||||
self.CDefineFound = 0
|
||||
@@ -197,7 +201,7 @@ novirtual class TypeGenBase:
|
||||
frame.col_end = 0
|
||||
def clear_trace(self) -> None:
|
||||
"""Clear the trace stack (called before translating each file)."""
|
||||
for i in range(self.TraceCount):
|
||||
for i in range(int(self.TraceCount)):
|
||||
frame: GenTypes.TraceFrame* = self._get_trace_frame(i)
|
||||
if frame is not None:
|
||||
frame.file_name = None
|
||||
@@ -249,7 +253,7 @@ novirtual class TypeGenBase:
|
||||
if self.GenericTpNames is not None and self.GenericTypeArgs is not None:
|
||||
rg_n: sizet = len(self.GenericTpNames)
|
||||
rg_i: sizet
|
||||
for rg_i in range(rg_n):
|
||||
for rg_i in range(int(rg_n)):
|
||||
rg_tpn: str = self.GenericTpNames.get(rg_i)
|
||||
if rg_tpn is not None and string.strcmp(name, rg_tpn) == 0:
|
||||
rg_ta: str = self.GenericTypeArgs.get(rg_i)
|
||||
|
||||
@@ -64,7 +64,7 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
|
||||
h: uint64 = self._cdef_idx_hash(name)
|
||||
mask: sizet = sizet(self.CDefineNameIdxCap - 1)
|
||||
si: sizet = sizet(h) & mask
|
||||
for i in range(self.CDefineNameIdxCap):
|
||||
for i in range(int(self.CDefineNameIdxCap)):
|
||||
slot: sizet* = self._cdef_idx_slot(si)
|
||||
sv: sizet = slot[0]
|
||||
if sv == 0: return -1
|
||||
@@ -80,7 +80,7 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
|
||||
h: uint64 = self._cdef_idx_hash(name)
|
||||
mask: sizet = sizet(self.CDefineNameIdxCap - 1)
|
||||
si: sizet = sizet(h) & mask
|
||||
for i in range(self.CDefineNameIdxCap):
|
||||
for i in range(int(self.CDefineNameIdxCap)):
|
||||
slot: sizet* = self._cdef_idx_slot(si)
|
||||
if slot[0] == 0:
|
||||
slot[0] = sizet(slot_index + 1)
|
||||
@@ -101,8 +101,11 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
|
||||
if CompilerFactory.factory is not None and CompilerFactory.factory.PermPool is not None: return CompilerFactory.factory.PermPool
|
||||
return pool
|
||||
|
||||
def register_cdefine_constant(self, pool: memhub.MemBuddy*, name: str, value: int64) -> None:
|
||||
"""Register an integer CDefine constant; store the value as a decimal string."""
|
||||
def register_cdefine_constant(self, pool: memhub.MemBuddy*, name: str, value: int64, bits: int = 0) -> None:
|
||||
"""Register an integer CDefine constant; store the value as a decimal string.
|
||||
|
||||
bits: 值表达式的位宽(0=未知,按值推断;8/16/32/64 = 显式强转或声明注解给出的位宽)。
|
||||
"""
|
||||
if name is None: return
|
||||
if self.CDefineNames is None:
|
||||
tp: memhub.MemBuddy* = self._cdefine_table_pool(pool)
|
||||
@@ -110,19 +113,21 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
|
||||
tv: list[str]* = list[str](tp, 64)
|
||||
td: list[int]* = list[int](tp, 64)
|
||||
tsf: list[int]* = list[int](tp, 64)
|
||||
tbits: list[int]* = list[int](tp, 64)
|
||||
# Table build failed (pool unusable/exhausted): report and return, never let the
|
||||
# append below hit None or an uninitialized address (would write NULL/0xCD).
|
||||
if tn is None or tv is None or td is None or tsf is None:
|
||||
if tn is None or tv is None or td is None or tsf is None or tbits is None:
|
||||
self.Gen.TypeGenErr.fatal_error(None, "CDefine table allocation failed (table body pool unavailable or exhausted)", "MemoryError")
|
||||
return
|
||||
self.CDefineNames = tn
|
||||
self.CDefineValues = tv
|
||||
self.CDefineDoubleFlags = td
|
||||
self.CDefineStrFlags = tsf
|
||||
self.CDefineBits = tbits
|
||||
self._cdef_idx_init(tp)
|
||||
# Table-ready guard: missing/half-initialized body -> fail-fast, no garbage writes
|
||||
# (assert non-null before append).
|
||||
if self.CDefineNames is None or self.CDefineValues is None or self.CDefineDoubleFlags is None or self.CDefineStrFlags is None:
|
||||
if self.CDefineNames is None or self.CDefineValues is None or self.CDefineDoubleFlags is None or self.CDefineStrFlags is None or self.CDefineBits is None:
|
||||
self.Gen.TypeGenErr.fatal_error(None, "CDefine table not ready (CDefineNames/Values/Flags is empty)", "MemoryError")
|
||||
return
|
||||
name_copy: str = pool.alloc(string.strlen(name) + 1)
|
||||
@@ -142,6 +147,7 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
|
||||
if val_buf is None: self.Gen.TypeGenErr.fatal_error(None, f"CDefine '{name}' value buffer allocation failed", "MemoryError")
|
||||
if self.CDefineDoubleFlags is not None: self.CDefineDoubleFlags.append(0)
|
||||
if self.CDefineStrFlags is not None: self.CDefineStrFlags.append(0)
|
||||
if self.CDefineBits is not None: self.CDefineBits.append(bits)
|
||||
def register_cdefine_double(self, pool: memhub.MemBuddy*, name: str, value: double) -> None:
|
||||
"""Register a float CDefine constant; store the value as %f text and flag it."""
|
||||
if name is None: return
|
||||
@@ -151,16 +157,18 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
|
||||
tv: list[str]* = list[str](tp, 64)
|
||||
td: list[int]* = list[int](tp, 64)
|
||||
tsf: list[int]* = list[int](tp, 64)
|
||||
if tn is None or tv is None or td is None or tsf is None:
|
||||
tbits: list[int]* = list[int](tp, 64)
|
||||
if tn is None or tv is None or td is None or tsf is None or tbits is None:
|
||||
self.Gen.TypeGenErr.fatal_error(None, "CDefine float table allocation failed (table body pool unavailable or exhausted)", "MemoryError")
|
||||
return
|
||||
self.CDefineNames = tn
|
||||
self.CDefineValues = tv
|
||||
self.CDefineDoubleFlags = td
|
||||
self.CDefineStrFlags = tsf
|
||||
self.CDefineBits = tbits
|
||||
self._cdef_idx_init(tp)
|
||||
# Table-ready guard (same as register_cdefine_constant): assert non-null before append
|
||||
if self.CDefineNames is None or self.CDefineValues is None or self.CDefineDoubleFlags is None or self.CDefineStrFlags is None:
|
||||
if self.CDefineNames is None or self.CDefineValues is None or self.CDefineDoubleFlags is None or self.CDefineStrFlags is None or self.CDefineBits is None:
|
||||
self.Gen.TypeGenErr.fatal_error(None, "CDefine float table not ready (CDefineNames/Values/Flags is empty)", "MemoryError")
|
||||
return
|
||||
name_copy_d: str = pool.alloc(string.strlen(name) + 1)
|
||||
@@ -179,6 +187,7 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
|
||||
else: self.CDefineValues.append("0")
|
||||
if self.CDefineDoubleFlags is not None: self.CDefineDoubleFlags.append(1)
|
||||
if self.CDefineStrFlags is not None: self.CDefineStrFlags.append(0)
|
||||
if self.CDefineBits is not None: self.CDefineBits.append(0)
|
||||
# fail-fast: a failed CDefine value buffer must abort compilation
|
||||
if val_buf is None:
|
||||
VLogger.error("CDefine float value buffer allocation failed", "CDEF-DBG")
|
||||
@@ -195,16 +204,18 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
|
||||
tv: list[str]* = list[str](tp, 64)
|
||||
td: list[int]* = list[int](tp, 64)
|
||||
tsf: list[int]* = list[int](tp, 64)
|
||||
if tn is None or tv is None or td is None or tsf is None:
|
||||
tbits: list[int]* = list[int](tp, 64)
|
||||
if tn is None or tv is None or td is None or tsf is None or tbits is None:
|
||||
self.Gen.TypeGenErr.fatal_error(None, "CDefine string table allocation failed (table body pool unavailable or exhausted)", "MemoryError")
|
||||
return
|
||||
self.CDefineNames = tn
|
||||
self.CDefineValues = tv
|
||||
self.CDefineDoubleFlags = td
|
||||
self.CDefineStrFlags = tsf
|
||||
self.CDefineBits = tbits
|
||||
self._cdef_idx_init(tp)
|
||||
# Table-ready guard (same as register_cdefine_constant): assert non-null before append
|
||||
if self.CDefineNames is None or self.CDefineValues is None or self.CDefineDoubleFlags is None or self.CDefineStrFlags is None:
|
||||
if self.CDefineNames is None or self.CDefineValues is None or self.CDefineDoubleFlags is None or self.CDefineStrFlags is None or self.CDefineBits is None:
|
||||
self.Gen.TypeGenErr.fatal_error(None, "CDefine string table not ready (CDefineNames/Values/Flags is empty)", "MemoryError")
|
||||
return
|
||||
ncopy_s: str = pool.alloc(string.strlen(name) + 1)
|
||||
@@ -223,6 +234,7 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
|
||||
self.CDefineValues.append(vcopy)
|
||||
if self.CDefineDoubleFlags is not None: self.CDefineDoubleFlags.append(0)
|
||||
if self.CDefineStrFlags is not None: self.CDefineStrFlags.append(1)
|
||||
if self.CDefineBits is not None: self.CDefineBits.append(0)
|
||||
# Also set the "last result is a string" flag (read by is_cdefine_str()).
|
||||
# Cross-module string CDefine reads call this + set_cdefine_found; missing the flag degrades to int 0 -> NULL crash.
|
||||
self.CDefineIsStr = 1
|
||||
@@ -235,7 +247,7 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
|
||||
if self.XmodCdefNames is not None:
|
||||
x_n: sizet = len(self.XmodCdefNames)
|
||||
x_i: sizet
|
||||
for x_i in range(x_n):
|
||||
for x_i in range(int(x_n)):
|
||||
x_nm: str = self.XmodCdefNames.get(x_i)
|
||||
if x_nm is not None and string.strcmp(x_nm, name) == 0: return
|
||||
perm: memhub.MemBuddy* = CompilerFactory.factory.PermPool
|
||||
@@ -266,7 +278,7 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
|
||||
if name is None or self.XmodCdefNames is None: return None
|
||||
n: sizet = len(self.XmodCdefNames)
|
||||
i: sizet
|
||||
for i in range(n):
|
||||
for i in range(int(n)):
|
||||
nm: str = self.XmodCdefNames.get(i)
|
||||
if nm is not None and string.strcmp(nm, name) == 0:
|
||||
val_str: str = self.XmodCdefValues.get(i)
|
||||
@@ -293,7 +305,7 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
|
||||
if cd_hit < 0: return 0
|
||||
cd_start = sizet(cd_hit)
|
||||
cd_end = sizet(cd_hit + 1)
|
||||
for i in range(cd_start, cd_end):
|
||||
for i in range(int(cd_start), int(cd_end)):
|
||||
nm: str = self.CDefineNames.get(i)
|
||||
if nm is not None and string.strcmp(nm, name) == 0:
|
||||
val_str: str = self.CDefineValues.get(i)
|
||||
@@ -309,6 +321,26 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
|
||||
return string.atoll(val_str)
|
||||
return string.atoll(val_str)
|
||||
return 0
|
||||
def lookup_cdefine_bits(self, name: str) -> int:
|
||||
"""Look up a CDefine constant's value-expression bit width; 0 = unknown (width inferred by value).
|
||||
|
||||
同一名字可能被注册**多次**:本模块注册带位宽(如 WORD(...) 或声明注解),而 stub/.vpi
|
||||
等路径以 bits=0 注册。若用「命中即返回」,哈希索引先命中的可能是那条 0 位宽记录,
|
||||
于是已知位宽被丢掉、常量被物化成 i32。故这里**全表扫描并优先返回非 0 位宽**。
|
||||
"""
|
||||
if self.CDefineNames is None or self.CDefineBits is None or name is None: return 0
|
||||
n: sizet = len(self.CDefineNames)
|
||||
# 同一名字可能被注册多次:stub/.vpi 等早期路径先注册(位宽可能未知 → 兜底值),
|
||||
# 本模块自己的 define 翻译在后(注解/强转齐全,最权威)。故取**最后**一条非零记录,
|
||||
# 而不是"命中即返回"或"第一条非零"——否则早退的 stub 记录会盖掉真实位宽。
|
||||
found: int = 0
|
||||
for i in range(int(n)):
|
||||
nm: str = self.CDefineNames.get(i)
|
||||
if nm is None or string.strcmp(nm, name) != 0: continue
|
||||
if sizet(i) >= len(self.CDefineBits): continue
|
||||
b: int = self.CDefineBits.get(i)
|
||||
if b != 0: found = b
|
||||
return found
|
||||
def lookup_cdefine_double(self, name: str) -> double:
|
||||
"""Look up a CDefine constant and return its double value (float or int)."""
|
||||
self.CDefineFound = 0
|
||||
@@ -326,7 +358,7 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
|
||||
if cd_hit < 0: return 0.0
|
||||
cd_start = sizet(cd_hit)
|
||||
cd_end = sizet(cd_hit + 1)
|
||||
for i in range(cd_start, cd_end):
|
||||
for i in range(int(cd_start), int(cd_end)):
|
||||
nm: str = self.CDefineNames.get(i)
|
||||
# Fast path: strcmp equality implies equal first char, so skip strcmp when it differs.
|
||||
if nm is not None and nm[0] == name[0] and string.strcmp(nm, name) == 0:
|
||||
@@ -360,7 +392,7 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
|
||||
if cd_hit < 0: return None
|
||||
cd_start = sizet(cd_hit)
|
||||
cd_end = sizet(cd_hit + 1)
|
||||
for i in range(cd_start, cd_end):
|
||||
for i in range(int(cd_start), int(cd_end)):
|
||||
nm: str = self.CDefineNames.get(i)
|
||||
if nm is not None and string.strcmp(nm, name) == 0:
|
||||
val_str: str = self.CDefineValues.get(i)
|
||||
@@ -388,7 +420,8 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
|
||||
if self.CDefineNames is not None: self.CDefineNames.clear()
|
||||
if self.CDefineValues is not None: self.CDefineValues.clear()
|
||||
if self.CDefineDoubleFlags is not None: self.CDefineDoubleFlags.clear()
|
||||
if self.CDefineStrFlags is not None: self.CDefineStrFlags.clear()
|
||||
if self.CDefineStrFlags is not None: self.CDefineStrFlags.clear()
|
||||
if self.CDefineBits is not None: self.CDefineBits.clear()
|
||||
# Clear index slots (store index+1; 0=empty) and reset Built to 0 (empty table and index
|
||||
# in sync, matching a freshly built table before its first register).
|
||||
if self.CDefineNameIdx is not None and self.CDefineNameIdxCap > 0:
|
||||
|
||||
@@ -82,7 +82,7 @@ novirtual class VarGenMixin:
|
||||
"""Look up by name in the variable table"""
|
||||
if name is None or vars_ptr is None: return None
|
||||
entry_size: sizet = GenTypes.VarEntry.__sizeof__()
|
||||
for i in range(var_count):
|
||||
for i in range(int(var_count)):
|
||||
entry_addr: uint64 = uint64(vars_ptr) + i * entry_size
|
||||
entry: GenTypes.VarEntry* = GenTypes.VarEntry*(entry_addr)
|
||||
if entry.Name is not None and entry.Used != 0:
|
||||
@@ -96,7 +96,7 @@ novirtual class VarGenMixin:
|
||||
"""Look up by name in the variable table, return the VarEntry or None"""
|
||||
if name is None or vars_ptr is None: return None
|
||||
entry_size: sizet = GenTypes.VarEntry.__sizeof__()
|
||||
for i in range(var_count):
|
||||
for i in range(int(var_count)):
|
||||
entry_addr: uint64 = uint64(vars_ptr) + i * entry_size
|
||||
entry: GenTypes.VarEntry* = GenTypes.VarEntry*(entry_addr)
|
||||
if entry.Name is not None and entry.Used != 0:
|
||||
@@ -403,6 +403,18 @@ novirtual class VarGenMixin:
|
||||
_g_freed_count = int(n)
|
||||
return 0
|
||||
|
||||
def freed_clear(self) -> None:
|
||||
"""进入新函数体:清空失效槽表(函数作用域复位)。
|
||||
|
||||
表按 **alloca 槽地址** 记录(_FreedCheck 比较槽指针),而栈槽地址会被后续函数复用
|
||||
(前帧已销毁)⇒ 若跨函数保留,上一函数的失效态会误加到新函数的同名/同址变量上
|
||||
(现场:ExprGenCls._resolve_doc_symbol_attr 首行读形参 out_sym 即报 "already released",
|
||||
而该函数体内根本没有 willFree 调用——是上一个函数的栈槽地址被复用)。
|
||||
故每个函数体入口清零;嵌套函数体结束后不恢复外层失效态(仅为少报,绝非误报)。
|
||||
"""
|
||||
global _g_freed_count
|
||||
_g_freed_count = 0
|
||||
|
||||
def freed_join(self, buf: sizet*) -> int:
|
||||
"""合流:当前失效槽表与快照**求交**(只有两路都失效才算失效);0=完成 / 1=无快照"""
|
||||
global _g_freed_slots, _g_freed_count
|
||||
@@ -753,7 +765,7 @@ novirtual class VarGenMixin:
|
||||
|
||||
def _SlifeSlotFirstSeen(self, idx: int, slot: uint64) -> int:
|
||||
"""槽 slot 在 (idx, _g_slife_top) 里是否已经出现过;1=本次首次出现(该释放)/ 0=重复(跳过)"""
|
||||
j: int = idx + 1
|
||||
j: int = int(idx + 1)
|
||||
while j < _g_slife_top:
|
||||
if uint64(_g_slife_owned[j * 2]) == slot: return 0
|
||||
j += 1
|
||||
@@ -769,7 +781,7 @@ novirtual class VarGenMixin:
|
||||
while i >= _g_slife_count:
|
||||
slot: uint64 = uint64(_g_slife_owned[i * 2])
|
||||
sh: mlirlite.Value* = mlirlite.Value*(_g_slife_owned[i * 2 + 1])
|
||||
if sh is not None and self._SlifeSlotFirstSeen(i, slot) != 0:
|
||||
if sh is not None and self._SlifeSlotFirstSeen(int(i), slot) != 0:
|
||||
self._SlifeEmitGuardedFree(pool, builder, mod, trans, mlirlite.Value*(_g_slife_owned[i * 2]), sh)
|
||||
i -= 1
|
||||
return 0
|
||||
@@ -830,7 +842,7 @@ novirtual class VarGenMixin:
|
||||
if ctx.Marker != GenTypes.REPL_CTX_MARKER: return None
|
||||
if ctx.Table is None or ctx.Count <= 0: return None
|
||||
entry_size: sizet = GenTypes.ReplVarEntry.__sizeof__()
|
||||
for i in range(ctx.Count):
|
||||
for i in range(int(ctx.Count)):
|
||||
entry_addr: uint64 = uint64(ctx.Table) + i * entry_size
|
||||
entry: GenTypes.ReplVarEntry* = GenTypes.ReplVarEntry*(entry_addr)
|
||||
if entry.Name is not None and string.strcmp(entry.Name, name) == 0: return entry
|
||||
@@ -879,7 +891,7 @@ novirtual class VarGenMixin:
|
||||
if ctx.Marker != GenTypes.REPL_CTX_MARKER: return None
|
||||
if ctx.FuncTable is None or ctx.FuncCount <= 0: return None
|
||||
entry_size: sizet = GenTypes.ReplFuncEntry.__sizeof__()
|
||||
for i in range(ctx.FuncCount):
|
||||
for i in range(int(ctx.FuncCount)):
|
||||
entry_addr: uint64 = uint64(ctx.FuncTable) + i * entry_size
|
||||
entry: GenTypes.ReplFuncEntry* = GenTypes.ReplFuncEntry*(entry_addr)
|
||||
if entry.Name is not None and string.strcmp(entry.Name, name) == 0: return entry
|
||||
@@ -938,7 +950,7 @@ novirtual class VarGenMixin:
|
||||
if ptypes is None: return None
|
||||
string.memset(ptypes, 0, 8 * GenTypes.REPL_FUNC_PARAM_MAX)
|
||||
ai_chk: int
|
||||
for ai_chk in range(entry.ParamCount):
|
||||
for ai_chk in range(int(entry.ParamCount)):
|
||||
pty_chk: mlirlite.LLVMType* = self.repl_func_ty(pool, int(entry.ParamTags[ai_chk]), int(entry.ParamBits[ai_chk]))
|
||||
if pty_chk is None:
|
||||
self.Gen.TypeGenErr.fatal_error(node, f"REPL: type token of parameter {ai_chk + 1} of session function '{func_name}' is not supported (M3 supports iN/float/double/i8*)", "TypeError")
|
||||
@@ -951,7 +963,7 @@ novirtual class VarGenMixin:
|
||||
tail: mlirlite.Value* = head
|
||||
arg_count: int = 1
|
||||
ai: int
|
||||
for ai in range(entry.ParamCount):
|
||||
for ai in range(int(entry.ParamCount)):
|
||||
arg: ast.AST* = cargs.get(ai)
|
||||
if arg is None:
|
||||
self.Gen.TypeGenErr.fatal_error(node, f"REPL: argument {ai + 1} of session function '{func_name}' is missing", "SyntaxError")
|
||||
@@ -983,7 +995,7 @@ novirtual class VarGenMixin:
|
||||
fp_first: mlirlite.ParamNode* = None
|
||||
fp_prev: mlirlite.ParamNode* = None
|
||||
pi: int
|
||||
for pi in range(arg_count):
|
||||
for pi in range(int(arg_count)):
|
||||
cur_ty: mlirlite.LLVMType* = i8_ptr_ty
|
||||
if pi > 0:
|
||||
pty_slot3: str* = (str*)(uint64(ptypes) + (pi - 1) * 8)
|
||||
|
||||
+82
-83
@@ -1,4 +1,3 @@
|
||||
import t
|
||||
from stdint import *
|
||||
import stdio
|
||||
import stdlib
|
||||
@@ -7,10 +6,10 @@ import viperlib
|
||||
import mlirlite
|
||||
import memhub
|
||||
import ast
|
||||
import w32.fileio as fileio
|
||||
import w32.win32file as w32file
|
||||
import w32.win32base as win32base
|
||||
import w32.win32process as win32process
|
||||
import win32.fileio as fileio
|
||||
import win32.win32file as win32file
|
||||
import win32.win32base as win32base
|
||||
import win32.win32process as win32process
|
||||
import lib.core.VLogger as VLogger
|
||||
import lib.core.Gen.GenApi as GenApi
|
||||
import lib.core.Gen.GenTypes as GenTypes
|
||||
@@ -159,7 +158,7 @@ def ReplFindProject(Out: bytes, Cap: int, Diag: bytes, DiagCap: int) -> int:
|
||||
c1[0] = 0
|
||||
c2[0] = 0
|
||||
# ① cwd/repl.vpj
|
||||
n_cwd: ULONG = w32file.GetCurrentDirectoryA(ULONG(Cap), c1)
|
||||
n_cwd: ULONG = win32file.GetCurrentDirectoryA(ULONG(Cap), c1)
|
||||
if n_cwd > 0:
|
||||
cl: int = int(n_cwd)
|
||||
# Defensive: GetCurrentDirectoryA writes are undefined on overflow, so force a NUL.
|
||||
@@ -173,7 +172,7 @@ def ReplFindProject(Out: bytes, Cap: int, Diag: bytes, DiagCap: int) -> int:
|
||||
return 1
|
||||
else: string.strcpy(c1, "(GetCurrentDirectoryA failed)")
|
||||
# 2) exe-dir/repl.vpj (GetModuleFileNameA(NULL) returns our own full path).
|
||||
n_exe: ULONG = w32file.GetModuleFileNameA(None, c2, ULONG(Cap))
|
||||
n_exe: ULONG = win32file.GetModuleFileNameA(None, c2, ULONG(Cap))
|
||||
if n_exe > 0:
|
||||
el: int = int(n_exe)
|
||||
# Defensive: on truncation the return equals Cap and the string may lack a NUL.
|
||||
@@ -321,10 +320,10 @@ ReplIncClsMissing: int = 0
|
||||
def _is_file(path: str) -> int:
|
||||
"""1=exists and is not a directory; 0=missing, a directory, or bad args."""
|
||||
if path is None: return 0
|
||||
attr: ULONG = w32file.GetFileAttributesA(path)
|
||||
attr: ULONG = win32file.GetFileAttributesA(path)
|
||||
if attr == 0xFFFFFFFF: # INVALID_FILE_ATTRIBUTES
|
||||
return 0
|
||||
if (attr & w32file.FILE_ATTRIBUTE_DIRECTORY) != 0: return 0
|
||||
if (attr & win32file.FILE_ATTRIBUTE_DIRECTORY) != 0: return 0
|
||||
return 1
|
||||
|
||||
|
||||
@@ -340,7 +339,7 @@ def ReplFindHostImplib() -> int:
|
||||
if exe_buf is None or cand is None: return 0
|
||||
exe_buf[0] = 0
|
||||
cand[0] = 0
|
||||
n_exe: ULONG = w32file.GetModuleFileNameA(None, exe_buf, ULONG(_REPL_PATH_CAP))
|
||||
n_exe: ULONG = win32file.GetModuleFileNameA(None, exe_buf, ULONG(_REPL_PATH_CAP))
|
||||
if n_exe <= 0: return 0
|
||||
el: int = int(n_exe)
|
||||
# Defensive: on truncation the write may lack a NUL (same as ReplFindProject).
|
||||
@@ -379,7 +378,7 @@ def _read_line(h: win32base.HANDLE, buf: bytes, cap: int) -> int:
|
||||
eof: int = 0
|
||||
while total < cap - 1:
|
||||
bytes_read = 0
|
||||
ok = w32file.ReadFile(h, buf + total, 1, &bytes_read, None)
|
||||
ok = win32file.ReadFile(h, buf + total, 1, &bytes_read, None)
|
||||
if ok == 0 or bytes_read == 0:
|
||||
eof = 1
|
||||
break
|
||||
@@ -415,7 +414,7 @@ def _line_starts_block(line: str, n: int) -> int:
|
||||
while s < n and (line[s] == ' ' or line[s] == '\t'): s += 1
|
||||
if s >= n: return 0
|
||||
if line[s] == '@': return 1
|
||||
i: int = n
|
||||
i: int = int(n)
|
||||
while i > 0:
|
||||
ch: char = line[i - 1]
|
||||
if ch != ' ' and ch != '\t': break
|
||||
@@ -521,7 +520,7 @@ def _repl_base_cdefine_find(name: str) -> int:
|
||||
"""Find a baseline CDefine entry index by name; -1 when not found."""
|
||||
if name is None or ReplBaseCDefineNames is None: return -1
|
||||
i: int
|
||||
for i in range(ReplBaseCDefineCount):
|
||||
for i in range(int(ReplBaseCDefineCount)):
|
||||
nm: str = rawptr(ReplBaseCDefineNames[i])
|
||||
if nm is not None and string.strcmp(nm, name) == 0: return i
|
||||
return -1
|
||||
@@ -532,7 +531,7 @@ def ReplBaselineRestore(pool: memhub.MemBuddy*) -> int:
|
||||
if pool is None or ReplBaseCDefineNames is None: return 1
|
||||
if GenApi._g_gen is None: return 1
|
||||
i: int
|
||||
for i in range(ReplBaseCDefineCount):
|
||||
for i in range(int(ReplBaseCDefineCount)):
|
||||
nm: str = rawptr(ReplBaseCDefineNames[i])
|
||||
val: str = rawptr(ReplBaseCDefineValues[i])
|
||||
kind: int = int(ReplBaseCDefineKinds[i])
|
||||
@@ -543,7 +542,7 @@ def ReplBaselineRestore(pool: memhub.MemBuddy*) -> int:
|
||||
# String CDefine: GenApi has no wrapper, so call gen directly (same shape as
|
||||
# the clear_cdefine_constants wrapper).
|
||||
GenApi._g_gen.TypeGenCDef.register_cdefine_string(pool, nm, val)
|
||||
for i in range(ReplBaseCTypedefCount):
|
||||
for i in range(int(ReplBaseCTypedefCount)):
|
||||
tn: str = rawptr(ReplBaseCTypedefNames[i])
|
||||
tv: str = rawptr(ReplBaseCTypedefTypeStrs[i])
|
||||
if tn is None or tv is None: continue
|
||||
@@ -562,7 +561,7 @@ def ReplBaselineCapture() -> int:
|
||||
cdef_str: list[int]* = GenApi._g_gen.TypeGenCDef.CDefineStrFlags
|
||||
n: sizet = len(cdef_names)
|
||||
i: sizet
|
||||
for i in range(n):
|
||||
for i in range(int(n)):
|
||||
nm: str = cdef_names.get(i)
|
||||
if nm is None: continue
|
||||
if _repl_base_cdefine_find(nm) >= 0: continue
|
||||
@@ -586,13 +585,13 @@ def ReplBaselineCapture() -> int:
|
||||
ct_vals: list[str]* = GenApi._g_gen.TypeGenCType.CTypedefTypeStrs
|
||||
cn2: sizet = len(ct_names)
|
||||
i2: sizet
|
||||
for i2 in range(cn2):
|
||||
for i2 in range(int(cn2)):
|
||||
tn: str = ct_names.get(i2)
|
||||
if tn is None: continue
|
||||
if ReplBaseCTypedefCount >= _REPL_BASE_CTYPEDEF_MAX: break
|
||||
dup_hit: int = 0
|
||||
k: int
|
||||
for k in range(ReplBaseCTypedefCount):
|
||||
for k in range(int(ReplBaseCTypedefCount)):
|
||||
ex: str = rawptr(ReplBaseCTypedefNames[k])
|
||||
if ex is not None and string.strcmp(ex, tn) == 0:
|
||||
dup_hit = 1
|
||||
@@ -637,12 +636,12 @@ def ReplFragImplibRecord(implib_path: str) -> int:
|
||||
def _repl_cmd_append_implibs(cmd: bytes, cap: int, pos: int) -> int:
|
||||
if cmd is None or ReplFragImplibs is None: return pos
|
||||
i: int
|
||||
for i in range(ReplFragImplibCount):
|
||||
for i in range(int(ReplFragImplibCount)):
|
||||
p: str = rawptr(ReplFragImplibs[i])
|
||||
if p is None: continue
|
||||
pos = _repl_cat_str(cmd, cap, pos, " ")
|
||||
pos = _repl_cat_str(cmd, cap, int(pos), " ")
|
||||
if pos < 0: return -1
|
||||
pos = _repl_cat_str(cmd, cap, pos, p)
|
||||
pos = _repl_cat_str(cmd, cap, int(pos), p)
|
||||
if pos < 0: return -1
|
||||
return pos
|
||||
|
||||
@@ -688,10 +687,10 @@ def ReplInitIncludesClasses(mb: memhub.MemBuddy*, sr: IncludesScanner.ScanResult
|
||||
budget_t0: uint64 = win32base.GetTickCount64()
|
||||
budget_hit: int = 0
|
||||
p_i: int
|
||||
for p_i in range(_REPL_INC_CLS_PASSES):
|
||||
for p_i in range(int(_REPL_INC_CLS_PASSES)):
|
||||
pass_ok: int = 0
|
||||
i: int
|
||||
for i in range(n_mod):
|
||||
for i in range(int(n_mod)):
|
||||
if int(st[i]) != 0: continue
|
||||
# Startup budget (one time check per module; GetTickCount64 is cheap): on overrun
|
||||
# stop, leaving the remaining modules to the on-demand import path.
|
||||
@@ -758,7 +757,7 @@ def ReplInitIncludesClasses(mb: memhub.MemBuddy*, sr: IncludesScanner.ScanResult
|
||||
# Summary (aggregated warnings): per-module warnings were capped, so print full counts.
|
||||
skip_cnt: int = 0
|
||||
si: int
|
||||
for si in range(n_mod):
|
||||
for si in range(int(n_mod)):
|
||||
if int(st[si]) == 0: skip_cnt += 1
|
||||
stdio.printf("includes classes: registered vpi classes for %d modules (failed %d, missing stub %d, " "not attempted/to retry %d, warnings dropped %d)\n", ReplIncClsMods, ReplIncClsFail, ReplIncClsMissing, skip_cnt, warn_dropped)
|
||||
if budget_hit != 0:
|
||||
@@ -786,7 +785,7 @@ def ReplInitSession() -> int:
|
||||
# 1) %TEMP% prefix (GetTempPathA returns a trailing backslash; 0 means failure).
|
||||
tmp_buf: bytes = _perm_pool.alloc(_REPL_TMP_CAP)
|
||||
if tmp_buf is None: return 1
|
||||
tmp_len: ULONG = w32file.GetTempPathA(_REPL_TMP_CAP, tmp_buf)
|
||||
tmp_len: ULONG = win32file.GetTempPathA(_REPL_TMP_CAP, tmp_buf)
|
||||
if tmp_len == 0: return 1
|
||||
if int(tmp_len) >= _REPL_TMP_CAP: return 1
|
||||
# 2) Session dir <TEMP>TransPyV_repl/<pid> (pid isolates sessions; paths use '/').
|
||||
@@ -919,7 +918,7 @@ def _repl_gen_includes_vpi(mb: memhub.MemBuddy*, sr: IncludesScanner.ScanResult*
|
||||
es: sizet = IncludesScanner.FileEntry.__sizeof__()
|
||||
dl: sizet = string.strlen(vpi_dir)
|
||||
n: int
|
||||
for n in range(sr.Count):
|
||||
for n in range(int(sr.Count)):
|
||||
ent: IncludesScanner.FileEntry* = IncludesScanner.FileEntry*(uint64(sr.Entries) + uint64(n) * es)
|
||||
if ent is None or ent.Sha1 is None or ent.Path is None:
|
||||
ReplIncVpiFail += 1
|
||||
@@ -1061,7 +1060,7 @@ def ReplInitIncludes(mb: memhub.MemBuddy*) -> int:
|
||||
def _repl_show_line(p: bytes, n: int) -> str:
|
||||
"""Copy a line fragment into a session buffer as a NUL-terminated string (truncated to 118 bytes)."""
|
||||
if ReplLineShow is None or p is None: return "(?)"
|
||||
m: int = n
|
||||
m: int = int(n)
|
||||
if m > 118: m = 118
|
||||
if m < 0: m = 0
|
||||
string.memcpy(ReplLineShow, p, sizet(m))
|
||||
@@ -1106,7 +1105,7 @@ def _repl_cat(dst: bytes, cap: int, pos: int, src: bytes, n: int) -> int:
|
||||
# Append a NUL-terminated string to dst; returns new length, -1 = out of capacity.
|
||||
def _repl_cat_str(dst: bytes, cap: int, pos: int, s: str) -> int:
|
||||
if s is None: return pos
|
||||
return _repl_cat(dst, cap, pos, s, int(string.strlen(s)))
|
||||
return _repl_cat(dst, cap, int(pos), s, int(string.strlen(s)))
|
||||
|
||||
|
||||
# Record a lifted function name (reserved-prefix check + same-fragment dedup + limit); 0=ok, 1=reject.
|
||||
@@ -1121,7 +1120,7 @@ def _repl_lift_add_name(name: str) -> int:
|
||||
stdio.printf("M3: session function name cannot start with the reserved prefix '%s': %s\n", pfx, name)
|
||||
return 1
|
||||
i: int
|
||||
for i in range(ReplLiftNameCount):
|
||||
for i in range(int(ReplLiftNameCount)):
|
||||
if string.strcmp(ReplLiftNames + i * _REPL_FN_NAME_MAX, name) == 0:
|
||||
stdio.printf("M3: session function '%s' defined twice in the same fragment (define it only once)\n", name)
|
||||
return 1
|
||||
@@ -1149,10 +1148,10 @@ def _repl_lift_def_header(line: bytes, n: int, name_out: bytes, name_cap: int) -
|
||||
p += 1
|
||||
nlen: int = p - ns
|
||||
if nlen <= 0:
|
||||
stdio.printf("M3: def header is missing the function name: %s\n", _repl_show_line(line, n))
|
||||
stdio.printf("M3: def header is missing the function name: %s\n", _repl_show_line(line, int(n)))
|
||||
return 1
|
||||
if nlen >= name_cap:
|
||||
stdio.printf("M3: session function name too long (limit %d bytes): %s\n", name_cap - 1, _repl_show_line(line, n))
|
||||
stdio.printf("M3: session function name too long (limit %d bytes): %s\n", name_cap - 1, _repl_show_line(line, int(n)))
|
||||
return 1
|
||||
string.memcpy(name_out, line + ns, sizet(nlen))
|
||||
name_out[nlen] = 0
|
||||
@@ -1168,10 +1167,10 @@ def _repl_lift_def_header(line: bytes, n: int, name_out: bytes, name_cap: int) -
|
||||
break
|
||||
p += 1
|
||||
if d != 0:
|
||||
stdio.printf("M3: unclosed generic-parameter bracket in def header: %s\n", _repl_show_line(line, n))
|
||||
stdio.printf("M3: unclosed generic-parameter bracket in def header: %s\n", _repl_show_line(line, int(n)))
|
||||
return 1
|
||||
if p >= n or line[p] != '(':
|
||||
stdio.printf("M3: def header is missing '(' after the function name: %s\n", _repl_show_line(line, n))
|
||||
stdio.printf("M3: def header is missing '(' after the function name: %s\n", _repl_show_line(line, int(n)))
|
||||
return 1
|
||||
lp: int = p
|
||||
# Matching ')'.
|
||||
@@ -1188,7 +1187,7 @@ def _repl_lift_def_header(line: bytes, n: int, name_out: bytes, name_cap: int) -
|
||||
break
|
||||
q += 1
|
||||
if rp < 0:
|
||||
stdio.printf("M3: def header is missing ')' (the signature must be on one line): %s\n", _repl_show_line(line, n))
|
||||
stdio.printf("M3: def header is missing ')' (the signature must be on one line): %s\n", _repl_show_line(line, int(n)))
|
||||
return 1
|
||||
# ':' must be on the same line (as must the return annotation).
|
||||
has_colon: int = 0
|
||||
@@ -1199,7 +1198,7 @@ def _repl_lift_def_header(line: bytes, n: int, name_out: bytes, name_cap: int) -
|
||||
break
|
||||
q2 += 1
|
||||
if has_colon == 0:
|
||||
stdio.printf("M3: def header ':' is not on the same line (the signature must be on one line): %s\n", _repl_show_line(line, n))
|
||||
stdio.printf("M3: def header ':' is not on the same line (the signature must be on one line): %s\n", _repl_show_line(line, int(n)))
|
||||
return 1
|
||||
# Whether the parameter list is empty.
|
||||
empty: int = 1
|
||||
@@ -1219,12 +1218,12 @@ def _repl_lift_def_header(line: bytes, n: int, name_out: bytes, name_cap: int) -
|
||||
seg += 1
|
||||
# Emit: "export " + original header [0,lp] + injection + original header [lp+1,n) + '\n'.
|
||||
pos: int = int(string.strlen(ReplLiftBuf))
|
||||
pos = _repl_cat_str(ReplLiftBuf, _REPL_LIFT_CAP, pos, "export ")
|
||||
pos = _repl_cat(ReplLiftBuf, _REPL_LIFT_CAP, pos, line, lp + 1)
|
||||
if empty != 0: pos = _repl_cat_str(ReplLiftBuf, _REPL_LIFT_CAP, pos, "__repl_ctx__: bytes")
|
||||
else: pos = _repl_cat_str(ReplLiftBuf, _REPL_LIFT_CAP, pos, "__repl_ctx__: bytes, ")
|
||||
pos = _repl_cat(ReplLiftBuf, _REPL_LIFT_CAP, pos, line + lp + 1, n - lp - 1)
|
||||
pos = _repl_cat_str(ReplLiftBuf, _REPL_LIFT_CAP, pos, "\n")
|
||||
pos = _repl_cat_str(ReplLiftBuf, _REPL_LIFT_CAP, int(pos), "export ")
|
||||
pos = _repl_cat(ReplLiftBuf, _REPL_LIFT_CAP, int(pos), line, lp + 1)
|
||||
if empty != 0: pos = _repl_cat_str(ReplLiftBuf, _REPL_LIFT_CAP, int(pos), "__repl_ctx__: bytes")
|
||||
else: pos = _repl_cat_str(ReplLiftBuf, _REPL_LIFT_CAP, int(pos), "__repl_ctx__: bytes, ")
|
||||
pos = _repl_cat(ReplLiftBuf, _REPL_LIFT_CAP, int(pos), line + lp + 1, n - lp - 1)
|
||||
pos = _repl_cat_str(ReplLiftBuf, _REPL_LIFT_CAP, int(pos), "\n")
|
||||
if pos < 0:
|
||||
stdio.printf("M3: lift source buffer too small (%d bytes)\n", _REPL_LIFT_CAP)
|
||||
return 2
|
||||
@@ -1244,28 +1243,28 @@ def _repl_split_body(body: bytes) -> int:
|
||||
in_body: int = 0 # past the def header (indented lines are the body; check raise)
|
||||
name_buf: bytes = ReplMsgName
|
||||
while i < total:
|
||||
n: int = _repl_line_len(body + i)
|
||||
ind: int = _repl_indent_len(body + i, n)
|
||||
n: int = int(_repl_line_len(body + i))
|
||||
ind: int = int(_repl_indent_len(body + i, int(n)))
|
||||
blank: int = 0
|
||||
if ind >= n: blank = 1
|
||||
if in_lift != 0:
|
||||
if blank != 0:
|
||||
# Blank line: stays in the lift block (blank body lines are indentation-neutral).
|
||||
lp0: int = int(string.strlen(ReplLiftBuf))
|
||||
lp0 = _repl_cat(ReplLiftBuf, _REPL_LIFT_CAP, lp0, body + i, n)
|
||||
lp0 = _repl_cat(ReplLiftBuf, _REPL_LIFT_CAP, lp0, body + i, int(n))
|
||||
lp0 = _repl_cat_str(ReplLiftBuf, _REPL_LIFT_CAP, lp0, "\n")
|
||||
if lp0 < 0: return 2
|
||||
i += n + 1
|
||||
continue
|
||||
if ind > 0:
|
||||
if need_header != 0:
|
||||
stdio.printf("M3: a def header must immediately follow the decorator: %s\n", _repl_show_line(body + i, n))
|
||||
stdio.printf("M3: a def header must immediately follow the decorator: %s\n", _repl_show_line(body + i, int(n)))
|
||||
return 1
|
||||
if in_body != 0 and _repl_line_has_kw(body + i, n, ind, "raise") != 0:
|
||||
stdio.printf("M3: session function '%s' has raise in its body: %s\n" " M3 does not support cross-fragment exception propagation (raise appends the " "__eh_msg_out__/__eh_code_out__ out params to the function, " "but indirect calls through the function pointer do not pass them) -- the whole fragment is skipped\n", name_buf, _repl_show_line(body + i, n))
|
||||
if in_body != 0 and _repl_line_has_kw(body + i, int(n), ind, "raise") != 0:
|
||||
stdio.printf("M3: session function '%s' has raise in its body: %s\n" " M3 does not support cross-fragment exception propagation (raise appends the " "__eh_msg_out__/__eh_code_out__ out params to the function, " "but indirect calls through the function pointer do not pass them) -- the whole fragment is skipped\n", name_buf, _repl_show_line(body + i, int(n)))
|
||||
return 1
|
||||
lp1: int = int(string.strlen(ReplLiftBuf))
|
||||
lp1 = _repl_cat(ReplLiftBuf, _REPL_LIFT_CAP, lp1, body + i, n)
|
||||
lp1 = _repl_cat(ReplLiftBuf, _REPL_LIFT_CAP, lp1, body + i, int(n))
|
||||
lp1 = _repl_cat_str(ReplLiftBuf, _REPL_LIFT_CAP, lp1, "\n")
|
||||
if lp1 < 0: return 2
|
||||
i += n + 1
|
||||
@@ -1277,9 +1276,9 @@ def _repl_split_body(body: bytes) -> int:
|
||||
continue
|
||||
if blank != 0 or ind > 0:
|
||||
# Into the fragment body (4 spaces per line, same as the block path).
|
||||
pos = _repl_cat_str(ReplSrcBuf, _REPL_FRAG_SRC_CAP, pos, " ")
|
||||
pos = _repl_cat(ReplSrcBuf, _REPL_FRAG_SRC_CAP, pos, body + i, n)
|
||||
pos = _repl_cat_str(ReplSrcBuf, _REPL_FRAG_SRC_CAP, pos, "\n")
|
||||
pos = _repl_cat_str(ReplSrcBuf, _REPL_FRAG_SRC_CAP, int(pos), " ")
|
||||
pos = _repl_cat(ReplSrcBuf, _REPL_FRAG_SRC_CAP, int(pos), body + i, int(n))
|
||||
pos = _repl_cat_str(ReplSrcBuf, _REPL_FRAG_SRC_CAP, int(pos), "\n")
|
||||
if pos < 0: return 2
|
||||
i += n + 1
|
||||
continue
|
||||
@@ -1289,13 +1288,13 @@ def _repl_split_body(body: bytes) -> int:
|
||||
need_header = 1
|
||||
in_body = 0
|
||||
lp2: int = int(string.strlen(ReplLiftBuf))
|
||||
lp2 = _repl_cat(ReplLiftBuf, _REPL_LIFT_CAP, lp2, body + i, n)
|
||||
lp2 = _repl_cat(ReplLiftBuf, _REPL_LIFT_CAP, lp2, body + i, int(n))
|
||||
lp2 = _repl_cat_str(ReplLiftBuf, _REPL_LIFT_CAP, lp2, "\n")
|
||||
if lp2 < 0: return 2
|
||||
i += n + 1
|
||||
continue
|
||||
if _repl_line_has_kw(body + i, n, 0, "def") != 0:
|
||||
rc_h: int = _repl_lift_def_header(body + i, n, name_buf, _REPL_FN_NAME_MAX)
|
||||
if _repl_line_has_kw(body + i, int(n), 0, "def") != 0:
|
||||
rc_h: int = int(_repl_lift_def_header(body + i, int(n), name_buf, _REPL_FN_NAME_MAX))
|
||||
if rc_h != 0: return rc_h
|
||||
if _repl_lift_add_name(name_buf) != 0: return 1
|
||||
in_lift = 1
|
||||
@@ -1303,12 +1302,12 @@ def _repl_split_body(body: bytes) -> int:
|
||||
in_body = 1
|
||||
i += n + 1
|
||||
continue
|
||||
if _repl_line_has_kw(body + i, n, 0, "async") != 0 and _repl_line_has_kw(body + i, n, 6, "def") != 0:
|
||||
stdio.printf("M3: async def session functions are not supported (coroutine ABI is incompatible with " "the M3 signature token; rewrite as a plain def): %s\n", _repl_show_line(body + i, n))
|
||||
if _repl_line_has_kw(body + i, int(n), 0, "async") != 0 and _repl_line_has_kw(body + i, int(n), 6, "def") != 0:
|
||||
stdio.printf("M3: async def session functions are not supported (coroutine ABI is incompatible with " "the M3 signature token; rewrite as a plain def): %s\n", _repl_show_line(body + i, int(n)))
|
||||
return 1
|
||||
pos = _repl_cat_str(ReplSrcBuf, _REPL_FRAG_SRC_CAP, pos, " ")
|
||||
pos = _repl_cat(ReplSrcBuf, _REPL_FRAG_SRC_CAP, pos, body + i, n)
|
||||
pos = _repl_cat_str(ReplSrcBuf, _REPL_FRAG_SRC_CAP, pos, "\n")
|
||||
pos = _repl_cat_str(ReplSrcBuf, _REPL_FRAG_SRC_CAP, int(pos), " ")
|
||||
pos = _repl_cat(ReplSrcBuf, _REPL_FRAG_SRC_CAP, int(pos), body + i, int(n))
|
||||
pos = _repl_cat_str(ReplSrcBuf, _REPL_FRAG_SRC_CAP, int(pos), "\n")
|
||||
if pos < 0: return 2
|
||||
i += n + 1
|
||||
if need_header != 0:
|
||||
@@ -1330,12 +1329,12 @@ def _repl_build_frag_src_multi(name: str, body: bytes) -> int:
|
||||
rc: int = _repl_split_body(body)
|
||||
if rc != 0: return rc
|
||||
pos: int = ReplWrapPos
|
||||
pos = _repl_cat_str(ReplSrcBuf, _REPL_FRAG_SRC_CAP, pos, " return 0\n")
|
||||
pos = _repl_cat_str(ReplSrcBuf, _REPL_FRAG_SRC_CAP, int(pos), " return 0\n")
|
||||
if pos < 0: return 2
|
||||
# Append lifted module-level defs after the fragment entry function (module scope, order-free).
|
||||
lift_len: int = int(string.strlen(ReplLiftBuf))
|
||||
if lift_len > 0:
|
||||
pos = _repl_cat(ReplSrcBuf, _REPL_FRAG_SRC_CAP, pos, ReplLiftBuf, lift_len)
|
||||
pos = _repl_cat(ReplSrcBuf, _REPL_FRAG_SRC_CAP, int(pos), ReplLiftBuf, lift_len)
|
||||
if pos < 0: return 2
|
||||
ReplWrapPos = pos
|
||||
return 0
|
||||
@@ -1350,7 +1349,7 @@ def _repl_func_entry_find(name: str) -> GenTypes.ReplFuncEntry*:
|
||||
if ReplCtxObj is None or ReplCtxObj.FuncTable is None or name is None: return None
|
||||
es: sizet = GenTypes.ReplFuncEntry.__sizeof__()
|
||||
i: int
|
||||
for i in range(ReplCtxObj.FuncCount):
|
||||
for i in range(int(ReplCtxObj.FuncCount)):
|
||||
e: GenTypes.ReplFuncEntry* = GenTypes.ReplFuncEntry*(uint64(ReplCtxObj.FuncTable) + uint64(i) * uint64(es))
|
||||
if e.Name is not None and string.strcmp(e.Name, name) == 0: return e
|
||||
return None
|
||||
@@ -1377,11 +1376,11 @@ def _repl_sig_to_text(tag: int, bits: int, pc: int) -> str:
|
||||
"""Render ReplSigResult + ReplSigTags/Bits as "(P1, P2) -> R" (PermPool buffer)."""
|
||||
if ReplSigText is None: return "(?)"
|
||||
pos: int = _repl_cat_str(ReplSigText, _REPL_SIG_TEXT_CAP, 0, "(")
|
||||
for i in range(pc):
|
||||
if i > 0: pos = _repl_cat_str(ReplSigText, _REPL_SIG_TEXT_CAP, pos, ", ")
|
||||
pos = _repl_cat_str(ReplSigText, _REPL_SIG_TEXT_CAP, pos, _repl_tag_text(int(ReplSigTags[i]), int(ReplSigBits[i])))
|
||||
pos = _repl_cat_str(ReplSigText, _REPL_SIG_TEXT_CAP, pos, ") -> ")
|
||||
pos = _repl_cat_str(ReplSigText, _REPL_SIG_TEXT_CAP, pos, _repl_tag_text(tag, bits))
|
||||
for i in range(int(pc)):
|
||||
if i > 0: pos = _repl_cat_str(ReplSigText, _REPL_SIG_TEXT_CAP, int(pos), ", ")
|
||||
pos = _repl_cat_str(ReplSigText, _REPL_SIG_TEXT_CAP, int(pos), _repl_tag_text(int(ReplSigTags[i]), int(ReplSigBits[i])))
|
||||
pos = _repl_cat_str(ReplSigText, _REPL_SIG_TEXT_CAP, int(pos), ") -> ")
|
||||
pos = _repl_cat_str(ReplSigText, _REPL_SIG_TEXT_CAP, int(pos), _repl_tag_text(tag, bits))
|
||||
if pos < 0: return "(?)"
|
||||
return ReplSigText
|
||||
|
||||
@@ -1395,7 +1394,7 @@ def _repl_precheck_session_funcs(mod: mlirlite.LLVMModule*) -> int:
|
||||
return 1
|
||||
new_cnt: int = 0
|
||||
idx: int
|
||||
for idx in range(ReplLiftNameCount):
|
||||
for idx in range(int(ReplLiftNameCount)):
|
||||
nm: str = _repl_lift_name(idx)
|
||||
if nm is None: return 1
|
||||
rc: int = _repl_model_func_sig(mod, nm, ReplSigResult)
|
||||
@@ -1426,7 +1425,7 @@ def _repl_precheck_session_funcs(mod: mlirlite.LLVMModule*) -> int:
|
||||
sig_same: int = 1
|
||||
if old.RetTag != ReplSigResult.RetTag or old.RetBits != ReplSigResult.RetBits or old.ParamCount != ReplSigResult.ParamCount: sig_same = 0
|
||||
if sig_same != 0:
|
||||
for pi in range(old.ParamCount):
|
||||
for pi in range(int(old.ParamCount)):
|
||||
if int(old.ParamTags[pi]) != int(ReplSigTags[pi]) or int(old.ParamBits[pi]) != int(ReplSigBits[pi]):
|
||||
sig_same = 0
|
||||
break
|
||||
@@ -1443,11 +1442,11 @@ def _repl_precheck_session_funcs(mod: mlirlite.LLVMModule*) -> int:
|
||||
def _repl_entry_sig_text(e: GenTypes.ReplFuncEntry*) -> str:
|
||||
if e is None or ReplSigText2 is None: return "(?)"
|
||||
pos: int = _repl_cat_str(ReplSigText2, _REPL_SIG_TEXT_CAP, 0, "(")
|
||||
for i in range(e.ParamCount):
|
||||
if i > 0: pos = _repl_cat_str(ReplSigText2, _REPL_SIG_TEXT_CAP, pos, ", ")
|
||||
pos = _repl_cat_str(ReplSigText2, _REPL_SIG_TEXT_CAP, pos, _repl_tag_text(int(e.ParamTags[i]), int(e.ParamBits[i])))
|
||||
pos = _repl_cat_str(ReplSigText2, _REPL_SIG_TEXT_CAP, pos, ") -> ")
|
||||
pos = _repl_cat_str(ReplSigText2, _REPL_SIG_TEXT_CAP, pos, _repl_tag_text(e.RetTag, e.RetBits))
|
||||
for i in range(int(e.ParamCount)):
|
||||
if i > 0: pos = _repl_cat_str(ReplSigText2, _REPL_SIG_TEXT_CAP, int(pos), ", ")
|
||||
pos = _repl_cat_str(ReplSigText2, _REPL_SIG_TEXT_CAP, int(pos), _repl_tag_text(int(e.ParamTags[i]), int(e.ParamBits[i])))
|
||||
pos = _repl_cat_str(ReplSigText2, _REPL_SIG_TEXT_CAP, int(pos), ") -> ")
|
||||
pos = _repl_cat_str(ReplSigText2, _REPL_SIG_TEXT_CAP, int(pos), _repl_tag_text(e.RetTag, e.RetBits))
|
||||
if pos < 0: return "(?)"
|
||||
return ReplSigText2
|
||||
|
||||
@@ -1460,7 +1459,7 @@ def _repl_register_session_funcs(h_lib: win32base.HANDLE, frag_no: int, mod: mli
|
||||
stdio.printf("M3: session function table/ctx not initialized\n")
|
||||
return 1
|
||||
idx: int
|
||||
for idx in range(ReplLiftNameCount):
|
||||
for idx in range(int(ReplLiftNameCount)):
|
||||
nm: str = _repl_lift_name(idx)
|
||||
if nm is None: return 1
|
||||
# Exported symbol is the bare name: `export def` sets has_export without a SHA1 prefix,
|
||||
@@ -1476,7 +1475,7 @@ def _repl_register_session_funcs(h_lib: win32base.HANDLE, frag_no: int, mod: mli
|
||||
if rc != 0:
|
||||
stdio.printf("M3: failed to re-parse session function '%s' signature (rc=%d)\n", nm, rc)
|
||||
return 1
|
||||
j: int = ReplCtxObj.FuncCount
|
||||
j: int = int(ReplCtxObj.FuncCount)
|
||||
es: sizet = GenTypes.ReplFuncEntry.__sizeof__()
|
||||
ent = GenTypes.ReplFuncEntry*(uint64(ReplCtxObj.FuncTable) + uint64(j) * uint64(es))
|
||||
nm_c: str = _repl_perm_dup(nm)
|
||||
@@ -1625,8 +1624,8 @@ def _repl_model_func_sig(mod: mlirlite.LLVMModule*, name: str, out: ReplSigOut*)
|
||||
out.Bad = 1
|
||||
out.BadType = _repl_ty_name(p_ty)
|
||||
return 2
|
||||
ReplSigTags[out.ParamCount] = tk // 1000
|
||||
ReplSigBits[out.ParamCount] = tk % 1000
|
||||
ReplSigTags[out.ParamCount] = char(tk // 1000)
|
||||
ReplSigBits[out.ParamCount] = char(tk % 1000)
|
||||
out.ParamCount = out.ParamCount + 1
|
||||
pi += 1
|
||||
p_cur = mlirlite.param_get_next(p_cur)
|
||||
@@ -1900,7 +1899,7 @@ def ReplExecFragment(mb: memhub.MemBuddy*, src: str) -> int:
|
||||
|
||||
# 12') Session function registration (M3), before calling the entry so a failure skips this
|
||||
# fragment's statements; in-fragment calls to the same name still use direct calls.
|
||||
if _repl_register_session_funcs(h_lib, n, tr.Module) != 0:
|
||||
if _repl_register_session_funcs(h_lib, int(n), tr.Module) != 0:
|
||||
if ReplCtxObj is not None: ReplCtxObj.FuncCount = ReplSavedFuncCount
|
||||
return 1
|
||||
|
||||
@@ -1971,7 +1970,7 @@ def run(mb: memhub.MemBuddy*) -> int:
|
||||
if lg is not None: lg.set_jit_flag(False)
|
||||
return 1
|
||||
|
||||
h_in: win32base.HANDLE = w32file.GetStdHandle(w32file.STD_INPUT_HANDLE)
|
||||
h_in: win32base.HANDLE = win32file.GetStdHandle(win32file.STD_INPUT_HANDLE)
|
||||
line: bytes = stdlib.malloc(_REPL_LINE_CAP)
|
||||
if line is None:
|
||||
stdio.printf("REPL: line buffer allocation failed\n")
|
||||
@@ -2079,7 +2078,7 @@ def run(mb: memhub.MemBuddy*) -> int:
|
||||
mb.reset_to(mark)
|
||||
continue
|
||||
# In continuation: every line is buffered; no state flips on ':' and no indent analysis.
|
||||
if _block_append(line, n) != 0:
|
||||
if _block_append(line, int(n)) != 0:
|
||||
stdio.printf("REPL: block input exceeds the block buffer cap of %d bytes: this block was discarded, " "returning to >>> (reduce the block size or run it in chunks)\n", _REPL_BLOCK_CAP)
|
||||
ReplInBlock = 0
|
||||
ReplBlockLen = 0
|
||||
@@ -2089,10 +2088,10 @@ def run(mb: memhub.MemBuddy*) -> int:
|
||||
if n == 0: # blank line: skip
|
||||
mb.reset_to(mark)
|
||||
continue
|
||||
if _line_starts_block(line, n) != 0:
|
||||
if _line_starts_block(line, int(n)) != 0:
|
||||
# Trailing ':' enters continuation: buffer this line without executing it. The first line
|
||||
# cannot overflow (line cap < block cap), so append failure falls through below.
|
||||
if _block_append(line, n) == 0:
|
||||
if _block_append(line, int(n)) == 0:
|
||||
ReplInBlock = 1
|
||||
mb.reset_to(mark)
|
||||
continue
|
||||
|
||||
Reference in new issue
Block a user