支持 tls 关键字,全面准备清退 t, c

This commit is contained in:
TermiNexus committed 2026-10-17 23:40:06 +08:00
1 parent 6e17716a69
commit 3242560d02
74 files changed
+524 -576

No files matched your search

+138 -70
View File
@@ -1,5 +1,4 @@
import lib.core.Gen.GenApi as GenApi
import t, c
from stdint import *
import stdio
import string
@@ -262,8 +261,8 @@ class BuildResult:
OutputPath: str
ErrorMsg: str
def __new__(self) -> t.CPtr:
return t.CPtr(_mbuddy.alloc(BuildResult.__sizeof__()))
def __new__(self) -> rawptr:
return rawptr(_mbuddy.alloc(BuildResult.__sizeof__()))
def __init__(self):
self.Success = 0
@@ -576,12 +575,12 @@ def _run_cmd_wait(cmd: str) -> int:
string.memset(&sa, 0, win32.win32base.SECURITY_ATTRIBUTES.__sizeof__())
string.memset(&pi, 0, win32process.PROCESS_INFORMATION.__sizeof__())
sa.nLength = win32.win32base.SECURITY_ATTRIBUTES.__sizeof__()
sa.lpSecurityDescriptor = t.CPtr(0)
sa.lpSecurityDescriptor = None
sa.bInheritHandle = 1
si.cb = win32process.STARTUPINFOA.__sizeof__()
# create_flags=0: the child inherits the parent console (output shown, like system);
# si.dwFlags stays 0 after memset (no STARTF_USESTDHANDLES, no std handles).
ok: int = win32process.CreateProcessA(t.CPtr(0), cmd_line, t.CPtr(0), t.CPtr(0), 1, 0, t.CPtr(0), t.CPtr(0), &si, &pi)
ok: int = win32process.CreateProcessA(None, cmd_line, None, None, 1, 0, None, None, &si, &pi)
# CreateProcessA failure diagnostics: read GetLastError immediately (later calls overwrite
# it). Use warning, not error (error would sys.exit in a worker thread).
if ok == 0:
@@ -597,10 +596,10 @@ def _run_cmd_wait(cmd: str) -> int:
return int(exit_code)
def _llc_task_worker(arg: t.CPtr) -> t.CPtr:
def _llc_task_worker(arg: rawptr) -> rawptr:
"""llc thread entry: only calls _run_cmd_wait, no pool allocation (thread-safe)."""
task: LlcTask* = (LlcTask*)(arg)
if task is None: return t.CPtr(0)
if task is None: return None
# MLIR backend: validate (mlir-opt) -> lower (mlir-translate), then ASan opt -> llc
if task.MlirOptCmd is not None:
mo_rc: int = _run_cmd_wait(task.MlirOptCmd)
@@ -608,21 +607,21 @@ def _llc_task_worker(arg: t.CPtr) -> t.CPtr:
task.ExitCode = mo_rc
task.FailStage = LLC_STAGE_MLIR_OPT
task.Result = 1
return t.CPtr(0)
return None
if task.MlirTranslateCmd is not None:
mt_rc: int = _run_cmd_wait(task.MlirTranslateCmd)
if mt_rc != 0:
task.ExitCode = mt_rc
task.FailStage = LLC_STAGE_MLIR_TRANSLATE
task.Result = 1
return t.CPtr(0)
return None
if task.OptCmd is not None:
opt_rc: int = _run_cmd_wait(task.OptCmd)
if opt_rc != 0:
task.ExitCode = opt_rc
task.FailStage = LLC_STAGE_ASAN_OPT
task.Result = 1
return t.CPtr(0)
return None
llc_rc: int = _run_cmd_wait(task.LlcCmd)
task.ExitCode = llc_rc
if llc_rc != 0:
@@ -631,7 +630,7 @@ def _llc_task_worker(arg: t.CPtr) -> t.CPtr:
else:
task.FailStage = LLC_STAGE_NONE
task.Result = 0
return t.CPtr(0)
return None
def run_llc_parallel(tasks: bytes, task_count: int, max_threads: int) -> int:
@@ -650,7 +649,7 @@ def run_llc_parallel(tasks: bytes, task_count: int, max_threads: int) -> int:
return 0
if max_threads <= 0: max_threads = 1
# Thread-handle array: list[str] (str=bytes=i8*, explicit elem_size=8) stores handles;
# list[t.CPtr] append crashes reading page zero. Grab the handle right after start().
# list[rawptr] append crashes reading page zero. Grab the handle right after start().
handles: list[str]* = list[str](_mbuddy, 8)
if handles is None:
VLogger.error("run_llc_parallel: failed to allocate thread handle array", "PhaseB")
@@ -664,13 +663,13 @@ def run_llc_parallel(tasks: bytes, task_count: int, max_threads: int) -> int:
# spawn this batch
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: vthreading.Thread* = vthreading.Thread(_llc_task_worker, rawptr(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))
handles.append(rawptr(th.handle))
for j in range(int(batch_n)):
h_j: t.CPtr = t.CPtr(handles.get(batch_start + j))
h_j: rawptr = rawptr(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)
@@ -1052,8 +1051,8 @@ class TbPeLayout:
SizeOfImage: uint64
TextRva: uint64
def __new__(self) -> t.CPtr:
return t.CPtr(_mbuddy.alloc(TbPeLayout.__sizeof__()))
def __new__(self) -> rawptr:
return rawptr(_mbuddy.alloc(TbPeLayout.__sizeof__()))
def __init__(self):
self.Ok = 0
@@ -1071,8 +1070,8 @@ class TbDump:
FuncCap: int
FuncCount: int
def __new__(self) -> t.CPtr:
return t.CPtr(_mbuddy.alloc(TbDump.__sizeof__()))
def __new__(self) -> rawptr:
return rawptr(_mbuddy.alloc(TbDump.__sizeof__()))
def __init__(self):
self.RowBuf = None
@@ -1090,8 +1089,8 @@ class TbPool:
Offs: bytes
OffCount: int
def __new__(self) -> t.CPtr:
return t.CPtr(_mbuddy.alloc(TbPool.__sizeof__()))
def __new__(self) -> rawptr:
return rawptr(_mbuddy.alloc(TbPool.__sizeof__()))
def __init__(self):
self.Pool = None
@@ -1118,9 +1117,12 @@ def _tb_rd_u32(buf: bytes, off: sizet) -> uint32:
def _tb_rd_u64(buf: bytes, off: sizet) -> uint64:
lo: uint64 = uint64(_tb_rd_u32(buf, off))
hi: uint64 = uint64(_tb_rd_u32(buf, off + 4))
return lo | (hi << 32)
# Assemble via memcpy instead of u32 widening + shifts: the manual form left 0xFFFFFFFF in
# the high half (a stale register/slot), which then looked like a wild pointer in the
# stackmap tables. memcpy is exact and little-endian on x86-64.
v: uint64 = 0
string.memcpy(&v, buf + off, 8)
return v
def _tb_wr_u32(buf: bytes, off: sizet, v: uint64):
@@ -1131,8 +1133,18 @@ def _tb_wr_u32(buf: bytes, off: sizet, v: uint64):
def _tb_wr_u64(buf: bytes, off: sizet, v: uint64):
_tb_wr_u32(buf, off, v & 0xFFFFFFFF)
_tb_wr_u32(buf, off + 4, (v >> 32) & 0xFFFFFFFF)
string.memcpy(buf + off, &v, 8)
def _tb_wr_ptr(buf: bytes, off: sizet, p: str):
"""Store a string pointer into a byte table without a ptr->int cast.
Do NOT write `memcpy(buf + off, &p, 8)`: passing `&p` (i8**) to a `rawptr` parameter is
currently compiled as a dereference (it copies the string's first 8 bytes instead of the
pointer value), so go through a typed slot instead.
"""
slot: str* = str*(buf + off)
slot[0] = p
# ---- tiny string helpers (no re module) ----
@@ -1142,6 +1154,18 @@ def _tb_cc(s: str, i: sizet) -> int:
return int(s[i]) & 0xFF
def _tb_str_ok(s: str) -> int:
"""Return 1 when s is a plausible user-space string pointer (not 0, -1 or garbage).
DWARF/objdump text is parsed into pointer-bearing tables; a single bad slot would later be
dereferenced by _tb_cc/strlen and crash the compiler with ASan "unknown address 0xffff...".
Callers use this to reject such a slot at the boundary instead of storing it.
"""
if s is None: return 0
v: uint64 = uint64(s)
return 1 if v >= 65536 and v <= 0x7FFFFFFFFFFF else 0
def _tb_contains(s: str, sub: str) -> int:
if s is None or sub is None: return 0
return 1 if string.strstr(s, sub) is not None else 0
@@ -1331,7 +1355,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(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))
if int(_tb_rd_u32(dirs, sizet(i) * TB_DIR_SIZE)) == idx:
p: uint64 = _tb_rd_u64(dirs, sizet(i) * TB_DIR_SIZE + 8)
# fail-fast: a wild name pointer would crash inside _tb_is_abs_path/strlen later.
if p < 65536 or p > 0x7FFFFFFFFFFF:
VLogger.error(f"--traceback: internal error: DWARF directory record {i} has an invalid name pointer ({p})", "TRACEBACK")
return None
return rawptr(p)
return None
@@ -1345,8 +1375,20 @@ def _tb_resolve_file(mb: memhub.MemBuddy*, files: bytes, fcount: int, dirs: byte
"""Resolve a file-table entry to a path (absolute name wins, else dir join)."""
roff: sizet = sizet(frec) * TB_FILE_SIZE
cached: uint64 = _tb_rd_u64(files, roff + 24)
if cached != 0: return rawptr(cached)
name: str = rawptr(_tb_rd_u64(files, roff + 8))
if cached != 0:
# fail-fast: a wild cached path would crash inside strlen/is_abs_path below; report the
# record that carries it together with the table state instead.
if cached < 65536 or cached > 0x7FFFFFFFFFFF:
VLogger.error(f"--traceback: internal error: DWARF file record {frec} has an invalid cached path pointer ({cached})", "TRACEBACK")
return None
return rawptr(cached)
nptr: uint64 = _tb_rd_u64(files, roff + 8)
if nptr < 65536 or nptr > 0x7FFFFFFFFFFF:
ridx: uint64 = uint64(_tb_rd_u32(files, roff))
rdir: uint64 = uint64(_tb_rd_u32(files, roff + 4))
VLogger.error(f"--traceback: internal error: DWARF file record {frec} has an invalid name pointer ({nptr}); record idx={ridx} dir_index={rdir}, records in table={fcount}", "TRACEBACK")
return None
name: str = rawptr(nptr)
path: str = name
if name is not None and _tb_is_abs_path(name) == 0:
didx: int = int(_tb_rd_u32(files, roff + 4))
@@ -1357,7 +1399,7 @@ def _tb_resolve_file(mb: memhub.MemBuddy*, files: bytes, fcount: int, dirs: byte
if root is not None and _tb_is_abs_path(root) != 0: base = _tb_join(mb, root, base)
path = _tb_join(mb, base, name)
if path is None or string.strlen(path) == 0: path = "??"
_tb_wr_u64(files, roff + 24, uint64(path))
_tb_wr_ptr(files, roff + 24, path)
return path
@@ -1371,6 +1413,10 @@ def _tb_grow_rec(mb: memhub.MemBuddy*, buf: bytes, old_cap: int, rec_size: int,
def _tb_emit_row(mb: memhub.MemBuddy*, d: TbDump*, addr: uint64, path: str, line: int, fname: str = None) -> int:
if d is None: return 1
# Every row must carry a file path. A caller that could not resolve one passes None (or a
# wild sentinel); storing that as-is makes the later strlen() sizing pass fault. Normalise to
# the stackmap's existing "??" convention (the DWARF path already does the same).
if _tb_str_ok(path) == 0: path = "??"
if d.RowCount >= d.RowCap:
ncap: int = d.RowCap * 2
nb: bytes = _tb_grow_rec(mb, d.RowBuf, d.RowCap, TB_ROW_SIZE, ncap)
@@ -1381,11 +1427,10 @@ def _tb_emit_row(mb: memhub.MemBuddy*, d: TbDump*, addr: uint64, path: str, line
d.RowCap = ncap
off: sizet = sizet(d.RowCount) * TB_ROW_SIZE
_tb_wr_u64(d.RowBuf, off, addr)
_tb_wr_u64(d.RowBuf, off + TB_ROW_OFF_FILE, uint64(path))
_tb_wr_ptr(d.RowBuf, off + TB_ROW_OFF_FILE, path)
_tb_wr_u32(d.RowBuf, off + TB_ROW_OFF_LINE, uint64(line))
fv: uint64 = 0
if fname is not None: fv = uint64(fname)
_tb_wr_u64(d.RowBuf, off + TB_ROW_OFF_FUNC, fv)
if _tb_str_ok(fname) != 0: _tb_wr_ptr(d.RowBuf, off + TB_ROW_OFF_FUNC, fname)
else: _tb_wr_u64(d.RowBuf, off + TB_ROW_OFF_FUNC, 0)
d.RowCount = d.RowCount + 1
d.RawRowCount = d.RawRowCount + 1
return 0
@@ -1404,7 +1449,7 @@ def _tb_emit_func(mb: memhub.MemBuddy*, d: TbDump*, low: uint64, high: uint64, n
off: sizet = sizet(d.FuncCount) * TB_FUNC_SIZE
_tb_wr_u64(d.FuncBuf, off, low)
_tb_wr_u64(d.FuncBuf, off + 8, high)
_tb_wr_u64(d.FuncBuf, off + 16, uint64(name))
_tb_wr_ptr(d.FuncBuf, off + 16, name)
d.FuncCount = d.FuncCount + 1
return 0
@@ -1549,10 +1594,10 @@ def _tb_parse_dump(mb: memhub.MemBuddy*, dump: str) -> TbDump*:
if in_line != 0:
if _tb_startswith(st, "debug_line[") != 0:
# new CU: commit pending file, reset both tables
if pkind == 1 and pname is not None and file_count < TB_FILE_CAP:
if pkind == 1 and _tb_str_ok(pname) != 0 and file_count < TB_FILE_CAP:
_tb_wr_u32(files, sizet(file_count) * TB_FILE_SIZE, uint64(pidx))
_tb_wr_u32(files, sizet(file_count) * TB_FILE_SIZE + 4, uint64(pdir))
_tb_wr_u64(files, sizet(file_count) * TB_FILE_SIZE + 8, uint64(pname))
_tb_wr_ptr(files, sizet(file_count) * TB_FILE_SIZE + 8, pname)
_tb_wr_u64(files, sizet(file_count) * TB_FILE_SIZE + 24, uint64(0))
file_count += 1
pkind = 0
@@ -1562,10 +1607,10 @@ def _tb_parse_dump(mb: memhub.MemBuddy*, dump: str) -> TbDump*:
file_count = 0
continue
if _tb_startswith(st, "file_names[") != 0:
if pkind == 1 and pname is not None and file_count < TB_FILE_CAP:
if pkind == 1 and _tb_str_ok(pname) != 0 and file_count < TB_FILE_CAP:
_tb_wr_u32(files, sizet(file_count) * TB_FILE_SIZE, uint64(pidx))
_tb_wr_u32(files, sizet(file_count) * TB_FILE_SIZE + 4, uint64(pdir))
_tb_wr_u64(files, sizet(file_count) * TB_FILE_SIZE + 8, uint64(pname))
_tb_wr_ptr(files, sizet(file_count) * TB_FILE_SIZE + 8, pname)
_tb_wr_u64(files, sizet(file_count) * TB_FILE_SIZE + 24, uint64(0))
file_count += 1
pname = None
@@ -1574,10 +1619,10 @@ def _tb_parse_dump(mb: memhub.MemBuddy*, dump: str) -> TbDump*:
pidx = _tb_bracket_idx(st)
continue
if _tb_startswith(st, "directory_names[") != 0 or _tb_startswith(st, "include_directories[") != 0:
if pkind == 1 and pname is not None and file_count < TB_FILE_CAP:
if pkind == 1 and _tb_str_ok(pname) != 0 and file_count < TB_FILE_CAP:
_tb_wr_u32(files, sizet(file_count) * TB_FILE_SIZE, uint64(pidx))
_tb_wr_u32(files, sizet(file_count) * TB_FILE_SIZE + 4, uint64(pdir))
_tb_wr_u64(files, sizet(file_count) * TB_FILE_SIZE + 8, uint64(pname))
_tb_wr_ptr(files, sizet(file_count) * TB_FILE_SIZE + 8, pname)
_tb_wr_u64(files, sizet(file_count) * TB_FILE_SIZE + 24, uint64(0))
file_count += 1
pname = None
@@ -1586,9 +1631,13 @@ def _tb_parse_dump(mb: memhub.MemBuddy*, dump: str) -> TbDump*:
val: str = _tb_value_after_sep(_tb_after_bracket(st))
if val is not None and _tb_len_trimmed(val) > 0:
if dir_count < TB_DIR_CAP:
_tb_wr_u32(dirs, sizet(dir_count) * TB_DIR_SIZE, uint64(pidx))
_tb_wr_u64(dirs, sizet(dir_count) * TB_DIR_SIZE + 8, uint64(_tb_unquote(mb, val)))
dir_count += 1
dname: str = _tb_unquote(mb, val)
if _tb_str_ok(dname) != 0:
_tb_wr_u32(dirs, sizet(dir_count) * TB_DIR_SIZE, uint64(pidx))
_tb_wr_ptr(dirs, sizet(dir_count) * TB_DIR_SIZE + 8, dname)
dir_count += 1
else:
VLogger.warning("--traceback: ignoring a DWARF directory entry whose name could not be copied", "TRACEBACK")
pkind = 0
else: pkind = 2
continue
@@ -1596,9 +1645,9 @@ def _tb_parse_dump(mb: memhub.MemBuddy*, dump: str) -> TbDump*:
val2: str = _tb_unquote(mb, _tb_ltrim(st + 5))
if pkind == 1: pname = val2
else:
if dir_count < TB_DIR_CAP and val2 is not None:
if dir_count < TB_DIR_CAP and _tb_str_ok(val2) != 0:
_tb_wr_u32(dirs, sizet(dir_count) * TB_DIR_SIZE, uint64(pidx))
_tb_wr_u64(dirs, sizet(dir_count) * TB_DIR_SIZE + 8, uint64(val2))
_tb_wr_ptr(dirs, sizet(dir_count) * TB_DIR_SIZE + 8, val2)
dir_count += 1
pkind = 0
continue
@@ -1608,10 +1657,10 @@ def _tb_parse_dump(mb: memhub.MemBuddy*, dump: str) -> TbDump*:
pdir = _tb_parse_dec(_tb_ltrim(st + 10))
continue
if _tb_startswith(st, "Address") != 0 and _tb_contains(st, "Column") != 0:
if pkind == 1 and pname is not None and file_count < TB_FILE_CAP:
if pkind == 1 and _tb_str_ok(pname) != 0 and file_count < TB_FILE_CAP:
_tb_wr_u32(files, sizet(file_count) * TB_FILE_SIZE, uint64(pidx))
_tb_wr_u32(files, sizet(file_count) * TB_FILE_SIZE + 4, uint64(pdir))
_tb_wr_u64(files, sizet(file_count) * TB_FILE_SIZE + 8, uint64(pname))
_tb_wr_ptr(files, sizet(file_count) * TB_FILE_SIZE + 8, pname)
_tb_wr_u64(files, sizet(file_count) * TB_FILE_SIZE + 24, uint64(0))
file_count += 1
pkind = 0
@@ -1619,10 +1668,10 @@ def _tb_parse_dump(mb: memhub.MemBuddy*, dump: str) -> TbDump*:
pdir = 0
continue
if _tb_cc(linebuf, 0) == 48 and _tb_cc(linebuf, 1) == 120:
if pkind == 1 and pname is not None and file_count < TB_FILE_CAP:
if pkind == 1 and _tb_str_ok(pname) != 0 and file_count < TB_FILE_CAP:
_tb_wr_u32(files, sizet(file_count) * TB_FILE_SIZE, uint64(pidx))
_tb_wr_u32(files, sizet(file_count) * TB_FILE_SIZE + 4, uint64(pdir))
_tb_wr_u64(files, sizet(file_count) * TB_FILE_SIZE + 8, uint64(pname))
_tb_wr_ptr(files, sizet(file_count) * TB_FILE_SIZE + 8, pname)
_tb_wr_u64(files, sizet(file_count) * TB_FILE_SIZE + 24, uint64(0))
file_count += 1
pkind = 0
@@ -1657,7 +1706,7 @@ def _tb_parse_dump(mb: memhub.MemBuddy*, dump: str) -> TbDump*:
# ---- DWARF debug_info subprogram parsing ----
if _tb_contains(linebuf, "DW_TAG_") != 0:
if in_prog != 0 and cur_has_low != 0 and cname is not None and string.strlen(cname) > 0:
if in_prog != 0 and cur_has_low != 0 and _tb_str_ok(cname) != 0 and string.strlen(cname) > 0:
hi: uint64 = cur_low + 1
if cur_has_high != 0:
hi = cur_high
@@ -1734,8 +1783,8 @@ class TbCallParse:
Grp: bytes # array of TB_GRP_SIZE slots
GrpCap: int
def __new__(self) -> t.CPtr:
return t.CPtr(_mbuddy.alloc(TbCallParse.__sizeof__()))
def __new__(self) -> rawptr:
return rawptr(_mbuddy.alloc(TbCallParse.__sizeof__()))
def __init__(self):
self.Cr = None
@@ -1776,8 +1825,8 @@ def _tb_grp_get(grp: bytes, cap: int, caller: str, callee: str, do_insert: int)
if uint64(_tb_rd_u32(grp, off + TB_GRP_OFF_USED)) == 0:
if do_insert == 0: return -1
_tb_wr_u32(grp, off + TB_GRP_OFF_USED, uint64(1))
_tb_wr_u64(grp, off + TB_GRP_OFF_CALLER, uint64(caller))
_tb_wr_u64(grp, off + TB_GRP_OFF_CALLEE, uint64(callee))
_tb_wr_ptr(grp, off + TB_GRP_OFF_CALLER, caller)
_tb_wr_ptr(grp, off + TB_GRP_OFF_CALLEE, callee)
_tb_wr_u32(grp, off + TB_GRP_OFF_HEAD, uint64(TB_IDX_NONE))
_tb_wr_u32(grp, off + TB_GRP_OFF_TAIL, uint64(TB_IDX_NONE))
_tb_wr_u32(grp, off + TB_GRP_OFF_CURSOR, uint64(TB_IDX_NONE))
@@ -1811,9 +1860,9 @@ def _tb_cp_add_cr(mb: memhub.MemBuddy*, cp: TbCallParse*, caller: str, callee: s
cp.Cr = nb
cp.CrCap = ncap
off: sizet = sizet(cp.CrN) * TB_CR_SIZE
_tb_wr_u64(cp.Cr, off + TB_CR_OFF_CALLER, uint64(caller))
_tb_wr_u64(cp.Cr, off + TB_CR_OFF_CALLEE, uint64(callee))
_tb_wr_u64(cp.Cr, off + TB_CR_OFF_FILE, uint64(file))
_tb_wr_ptr(cp.Cr, off + TB_CR_OFF_CALLER, caller)
_tb_wr_ptr(cp.Cr, off + TB_CR_OFF_CALLEE, callee)
_tb_wr_ptr(cp.Cr, off + TB_CR_OFF_FILE, file)
_tb_wr_u32(cp.Cr, off + TB_CR_OFF_LINE, uint64(line))
_tb_wr_u32(cp.Cr, off + TB_CR_OFF_NEXT, uint64(TB_IDX_NONE))
cp.CrN = cp.CrN + 1
@@ -1830,8 +1879,8 @@ def _tb_cp_add_fr(mb: memhub.MemBuddy*, cp: TbCallParse*, sym: str, file: str, d
cp.Fr = nb
cp.FrCap = ncap
off: sizet = sizet(cp.FrN) * TB_FR_SIZE
_tb_wr_u64(cp.Fr, off + TB_FR_OFF_SYM, uint64(sym))
_tb_wr_u64(cp.Fr, off + TB_FR_OFF_FILE, uint64(file))
_tb_wr_ptr(cp.Fr, off + TB_FR_OFF_SYM, sym)
_tb_wr_ptr(cp.Fr, off + TB_FR_OFF_FILE, file)
_tb_wr_u32(cp.Fr, off + TB_FR_OFF_DEFLINE, uint64(defline))
cp.FrN = cp.FrN + 1
return 0
@@ -1853,6 +1902,10 @@ def _tb_read_whole(mb: memhub.MemBuddy*, path: str) -> str:
got: LONG = f.read_all(buf, sizet(sz) + 2)
f.close()
if got < 0: return None
# NUL-terminate at the actually read length. The pool buffer is NOT zero-filled (MemBuddy's
# diagnostic fill is 0xCD), so without this the caller's strlen() walks into uninitialised
# bytes and can run past the end of the arena.
buf[got] = 0
return buf
@@ -1873,7 +1926,7 @@ def _tb_parse_f_line(mb: memhub.MemBuddy*, s: str, a: sizet, b: sizet, cp: TbCal
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
if _tb_str_ok(sym) == 0 or _tb_str_ok(file) == 0: return 0
return _tb_cp_add_fr(mb, cp, sym, file, defline)
@@ -1899,7 +1952,7 @@ def _tb_parse_c_line(mb: memhub.MemBuddy*, s: str, a: sizet, b: sizet, cp: TbCal
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
if _tb_str_ok(caller) == 0 or _tb_str_ok(callee) == 0 or _tb_str_ok(file) == 0: return 0
return _tb_cp_add_cr(mb, cp, caller, callee, file, line)
@@ -1967,7 +2020,7 @@ def _tb_build_groups(mb: memhub.MemBuddy*, cp: TbCallParse*) -> int:
def _tb_emit_call_row(mb: memhub.MemBuddy*, d: TbDump*, cp: TbCallParse*, img_lo: uint64, caller: str, callee: str, ret_va: uint64) -> int:
"""Emit a row for a return address from the next unconsumed C record of (caller, callee)."""
if caller is None or callee is None or cp.Grp is None or ret_va < img_lo: return 0
if _tb_str_ok(caller) == 0 or _tb_str_ok(callee) == 0 or cp.Grp is None or ret_va < img_lo: return 0
slot: int = _tb_grp_get(cp.Grp, cp.GrpCap, caller, callee, 0)
if slot < 0: return 0
goff: sizet = sizet(slot) * TB_GRP_SIZE
@@ -1984,11 +2037,11 @@ 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
if _tb_str_ok(sym) == 0 or cp.Fr is None or cp.FrN <= 0 or addr < img_lo: return 0
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:
if _tb_str_ok(s) != 0 and string.strcmp(s, sym) == 0:
file: str = rawptr(_tb_rd_u64(cp.Fr, off + TB_FR_OFF_FILE))
dl: int = int(_tb_rd_u32(cp.Fr, off + TB_FR_OFF_DEFLINE))
return _tb_emit_row(mb, d, addr, file, dl, sym)
@@ -2066,7 +2119,7 @@ def _tb_scan_objdump(mb: memhub.MemBuddy*, dump: str, img_lo: uint64, d: TbDump*
j: sizet = lb
while j < le and _tb_cc(dump, j) != 62: j += 1
cur_func = _tb_dup_span(mb, dump + lb, j - lb)
if cur_func is not None: _tb_emit_entry_row(mb, d, cp, img_lo, laddr, cur_func)
if _tb_str_ok(cur_func) != 0: _tb_emit_entry_row(mb, d, cp, img_lo, laddr, cur_func)
continue
# ---- instruction line `ADDR: bytes mnemonic operands` ----
k: sizet = ls
@@ -2275,7 +2328,14 @@ def WriteStackMap(mb: memhub.MemBuddy*, exe_path: str) -> int:
pool_need: sizet = 1
i = 0
while i < uniq:
pth: str = rawptr(_tb_rd_u64(d.RowBuf, sizet(i) * TB_ROW_SIZE + TB_ROW_OFF_FILE))
fptr: uint64 = _tb_rd_u64(d.RowBuf, sizet(i) * TB_ROW_SIZE + TB_ROW_OFF_FILE)
# fail-fast: Windows x64 user addresses stay below 0x7FFF_FFFF_FFFF; anything else is a
# wild pointer (0, -1 or an uninitialised slot). Report the record instead of letting
# strlen walk off into unmapped memory (ASan access-violation at 0xffffffffffffffff).
if fptr < 65536 or fptr > 0x7FFFFFFFFFFF:
VLogger.error(f"--traceback: internal error: line record {i} carries an invalid file-path pointer ({fptr})", "TRACEBACK")
return 1
pth: str = rawptr(fptr)
pool_need += string.strlen(pth) + 1
i += 1
# our rows carry the function name directly (string pool); DWARF rows take it from the subprogram table.
@@ -2283,13 +2343,21 @@ def WriteStackMap(mb: memhub.MemBuddy*, exe_path: str) -> int:
while i < uniq:
fp: uint64 = _tb_rd_u64(d.RowBuf, sizet(i) * TB_ROW_SIZE + TB_ROW_OFF_FUNC)
if fp != 0:
if fp > 0x7FFFFFFFFFFF:
VLogger.error(f"--traceback: internal error: line record {i} carries an invalid function-name pointer ({fp})", "TRACEBACK")
return 1
fns: str = rawptr(fp)
if fns is not None: pool_need += string.strlen(fns) + 1
pool_need += string.strlen(fns) + 1
i += 1
i = 0
while i < fkept:
nm0: str = rawptr(_tb_rd_u64(d.FuncBuf, sizet(i) * TB_FUNC_SIZE + 16))
if nm0 is not None: pool_need += string.strlen(nm0) + 1
nmf: uint64 = _tb_rd_u64(d.FuncBuf, sizet(i) * TB_FUNC_SIZE + 16)
if nmf != 0:
if nmf > 0x7FFFFFFFFFFF:
VLogger.error(f"--traceback: internal error: subprogram record {i} carries an invalid name pointer ({nmf})", "TRACEBACK")
return 1
nm0: str = rawptr(nmf)
pool_need += string.strlen(nm0) + 1
i += 1
p: TbPool* = TbPool()
if p is None:
+5 -6
View File
@@ -1,4 +1,3 @@
import t, c
from stdint import *
import string
import stdlib
@@ -368,10 +367,10 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: VLogg
class_names_buf: bytes = mb.alloc(sizet(Phase1.MAX_CLASSES) * Phase1.CLASS_NAME_LEN)
parent_names_buf: bytes = mb.alloc(sizet(Phase1.MAX_CLASSES) * Phase1.CLASS_NAME_LEN)
def_sha1s_buf: bytes = mb.alloc(Phase1.MAX_CLASSES * Phase1.SHA1_LEN)
topo_order: t.CPtr = mb.alloc(4 * Phase1.MAX_FILE_DEPS)
topo_count_box: t.CPtr = mb.alloc(4)
generic_file_indices: t.CPtr = mb.alloc(4 * Phase1.MAX_FILE_DEPS)
generic_file_count_box: t.CPtr = mb.alloc(4)
topo_order: rawptr = mb.alloc(4 * Phase1.MAX_FILE_DEPS)
topo_count_box: rawptr = mb.alloc(4)
generic_file_indices: rawptr = mb.alloc(4 * Phase1.MAX_FILE_DEPS)
generic_file_count_box: rawptr = mb.alloc(4)
if generic_file_indices is not None: string.memset(generic_file_indices, 0, 4 * Phase1.MAX_FILE_DEPS)
if generic_file_count_box is not None: string.memset(generic_file_count_box, 0, 4)
topo_count: int = 0
@@ -427,7 +426,7 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: VLogg
file_idx: int = int(i)
if topo_count > 0:
od_addr: uint64 = uint64(topo_order) + i * 4
od_ptr: int* = (int*)(t.CPtr(od_addr))
od_ptr: int* = int*(od_addr)
if od_ptr is not None: file_idx = od_ptr[0]
entry_addr_r: uint64 = uint64(result.Entries) + file_idx * entry_size
+2 -19
View File
@@ -1,31 +1,14 @@
import t, c
from stdint import *
import string
import stdlib
import memhub
import viperlib
import sys
import win32.fileio as fileio
import win32.win32file
import win32.win32base
import ast
import mlirlite
import lib.core.VLogger as VLogger
import lib.core.Gen.Translator as HandlesTranslator
import lib.core.IncludesScanner as IncludesScanner
import lib.Stub.StubMerger as StubMerger
import lib.core.Gen.Generator as GenGenerator
import lib.core.CompilerFactory as CompilerFactory
import lib.Phase.BuildPipeline as BuildPipeline
import lib.Projectrans.Config as Config
import lib.Stub.StubGen.Converter as StubConverter
import lib.Stub.StubMerger.StubMergerBuild as StubMergerBuild
import lib.Stub.StubMerger.StubMergerCollect as StubMergerCollect
import lib.Stub.StubMerger.StubMergerIR as StubMergerIR
import lib.Stub.StubMerger.StubMergerStore as StubMergerStore
import lib.Phase.Phase1.Phase1Scan as Phase1Scan
import lib.Phase.Phase1.Phase1Topo as Phase1Topo
import lib.Phase.Phase1 as Phase1
import lib.core.Gen.GenApi as GenApi
@@ -40,7 +23,7 @@ import lib.core.Gen.GenApi as GenApi
# _ScanClassInheritance - pre-scan includes files, parse ASTs and collect ClassDef
# names/parent names into flat byte arrays; returns the class count (-1 on failure).
def _ScanClassInheritance(mb: memhub.MemBuddy*, result: IncludesScanner.ScanResult*, reachable_set: str, reachable_count: int, use_reachable: int, sha1_set: str, set_count: int, class_names_buf: bytes, parent_names_buf: bytes, def_sha1s_buf: bytes, generic_file_indices: t.CPtr, generic_file_count_box: t.CPtr) -> int:
def _ScanClassInheritance(mb: memhub.MemBuddy*, result: IncludesScanner.ScanResult*, reachable_set: str, reachable_count: int, use_reachable: int, sha1_set: str, set_count: int, class_names_buf: bytes, parent_names_buf: bytes, def_sha1s_buf: bytes, generic_file_indices: rawptr, generic_file_count_box: rawptr) -> int:
"""Pre-scan includes files and collect class inheritance info."""
if result is None: return -1
# The three flat buffers are mb.alloc'ed by the caller; a NULL buffer would make
@@ -322,7 +305,7 @@ def _AnnIsContainer(ann: ast.AST*) -> int:
return 0
def _ScanTreeForContainers(node: ast.AST*, need_list_box: t.CPtr, need_dict_box: t.CPtr, depth: int = 0):
def _ScanTreeForContainers(node: ast.AST*, need_list_box: rawptr, need_dict_box: rawptr, depth: int = 0):
"""Recursively scan the AST for list/dict container usage."""
# Recursion depth guard: a self-referential AST cycle would recurse until the
# stack is exhausted; fail-fast abort.
+4 -20
View File
@@ -1,30 +1,14 @@
import t, c
from stdint import *
import string
import stdlib
import memhub
import viperlib
import win32.fileio as fileio
import win32.win32file
import win32.win32base
import ast
import mlirlite
import lib.core.VLogger as VLogger
import lib.core.Gen.Translator as HandlesTranslator
import lib.core.IncludesScanner as IncludesScanner
import lib.Stub.StubMerger as StubMerger
import lib.core.Gen.Generator as GenGenerator
import lib.core.CompilerFactory as CompilerFactory
import lib.Phase.BuildPipeline as BuildPipeline
import lib.Projectrans.Config as Config
import lib.Stub.StubGen.Converter as StubConverter
import lib.Stub.StubMerger.StubMergerBuild as StubMergerBuild
import lib.Stub.StubMerger.StubMergerCollect as StubMergerCollect
import lib.Stub.StubMerger.StubMergerIR as StubMergerIR
import lib.Stub.StubMerger.StubMergerStore as StubMergerStore
import lib.Phase.Phase1.Phase1Scan as Phase1Scan
import lib.Phase.Phase1.Phase1Topo as Phase1Topo
import lib.Phase.Phase1 as Phase1
# Phase1Topo - topologically sort source files by class inheritance and import dependencies.
@@ -32,7 +16,7 @@ import lib.Phase.Phase1 as Phase1
# _TopoSortFiles - topologically sort files by class inheritance (Kahn).
# Builds a "file SHA1 -> dependency SHA1 set" map, computes in-degrees (parent-file first), then outputs the order; returns 0 or -1.
def _TopoSortFiles(mb: memhub.MemBuddy*, result: IncludesScanner.ScanResult*, reachable_set: str, reachable_count: int, use_reachable: int, sha1_set: str, set_count: int, class_names_buf: bytes, parent_names_buf: bytes, def_sha1s_buf: bytes, class_count: int, out_order: t.CPtr, out_count: t.CPtr, temp_dir: str, td_len_ts: sizet) -> int:
def _TopoSortFiles(mb: memhub.MemBuddy*, result: IncludesScanner.ScanResult*, reachable_set: str, reachable_count: int, use_reachable: int, sha1_set: str, set_count: int, class_names_buf: bytes, parent_names_buf: bytes, def_sha1s_buf: bytes, class_count: int, out_order: rawptr, out_count: rawptr, temp_dir: str, td_len_ts: sizet) -> int:
"""Topologically sort files by class inheritance and import dependencies."""
if result is None or out_order is None or out_count is None: return -1
@@ -44,9 +28,9 @@ def _TopoSortFiles(mb: memhub.MemBuddy*, result: IncludesScanner.ScanResult*, re
# 取消整数提升后,模块属性 CDefine(Phase1.MAX_*) 按最小位宽物化为 i16/i8;三段乘积 256*16*17=69632 会在 i16 回绕,
# 首乘数显式 sizet() 加宽到 64 位。上一行 256*17=4352 未超 i16,故无需改。
file_deps_buf: bytes = mb.alloc(sizet(Phase1.MAX_FILE_DEPS) * Phase1.MAX_DEP_EDGES * Phase1.SHA1_LEN)
file_dep_counts: t.CPtr = mb.alloc(Phase1.MAX_FILE_DEPS * 4)
file_indegrees: t.CPtr = mb.alloc(Phase1.MAX_FILE_DEPS * 4)
file_processed: t.CPtr = mb.alloc(Phase1.MAX_FILE_DEPS * 4)
file_dep_counts: rawptr = mb.alloc(Phase1.MAX_FILE_DEPS * 4)
file_indegrees: rawptr = mb.alloc(Phase1.MAX_FILE_DEPS * 4)
file_processed: rawptr = mb.alloc(Phase1.MAX_FILE_DEPS * 4)
if file_sha1s_buf is None or file_deps_buf is None or file_dep_counts is None or file_indegrees is None or file_processed is None:
if file_sha1s_buf is not None: mb.free(file_sha1s_buf)
-1
View File
@@ -1,4 +1,3 @@
import t, c
from stdint import *
import memhub
+1 -3
View File
@@ -1,5 +1,4 @@
import lib.core.Gen.GenApi as GenApi
import t, c
from stdint import *
import stdio
import string
@@ -38,8 +37,7 @@ define SRC_BUF_SIZE = 1048576
# Max source files (recursive scanning grows the count; too small truncates the list -> undefined symbol)
define MAX_SRC_FILES = 128
@t.NoVTable
class SrcFileEntry:
novirtual class SrcFileEntry:
"""Source file entry"""
Path: str # full path
Sha1: str # first 16 chars of SHA1
+11 -2
View File
@@ -1,8 +1,6 @@
import t, c
from stdint import *
import memhub
import string
import stdio
import stdlib
import viperlib
import lib.core.VLogger as VLogger
@@ -112,6 +110,17 @@ def Load_project_config(path: str) -> int:
# Read linker.flags array, join into a space-separated string
ld_flags_val: JsonValue* = linker["flags"]
LinkerFlags = _join_flags_array(ld_flags_val)
# mingw-w64:同时链接 -lmsvcrt 与 -lucrt 时,GNU ld 会从两个 import 库各拉出一个都定义
# __imp_<name> 的成员(现场:__imp_ldexp 同时出现在 libmsvcrt 与
# libapi-ms-win-crt-math),直接报 "multiple definition"。lld 按需解析 import 库不会冲突,
# 仓库默认链接命令也一直是 lld;项目配置漏写 -fuse-ld 时在这里补齐,避免逐个测试改配置。
if LinkerCmd is not None and LinkerFlags is not None and string.strstr(LinkerCmd, "clang") is not None:
if string.strstr(LinkerFlags, "-fuse-ld") is None and string.strstr(LinkerFlags, "-lucrt") is not None:
lf_cap: sizet = string.strlen(LinkerFlags) + 32
lf_new: str = _mbuddy.alloc(lf_cap)
if lf_new is not None:
viperlib.snprintf(lf_new, lf_cap, "-fuse-ld=lld %s", LinkerFlags)
LinkerFlags = lf_new
# Read linker.static_libs array: explicit static libs (.a/.obj); joined raw here,
# then resolved to abs paths against project_dir in resolve_paths.
sl_val: JsonValue* = linker["static_libs"]
-1
View File
@@ -1,4 +1,3 @@
import t, c
from stdint import *
import memhub
import string
-1
View File
@@ -1,4 +1,3 @@
import t, c
from stdint import *
from .Config import Load_project_config, resolve_paths
-1
View File
@@ -1,6 +1,5 @@
import lib.core.Gen.GenApi as GenApi
import lib.core.VLogger as VLogger
import t, c
from stdint import *
import ast
import string
-1
View File
@@ -1,7 +1,6 @@
import lib.core.Gen.GenApi as GenApi
import lib.core.Gen.GenTypes as GenTypes
import lib.core.CompilerFactory as CompilerFactory
import t, c
from stdint import *
import ast
import string
+11 -12
View File
@@ -1,5 +1,4 @@
import lib.core.Gen.GenApi as GenApi
import t, c
from stdint import *
import memhub
import string
@@ -197,9 +196,9 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str,
# 2b. --traceback: force the traceback runtime module into the reachable set (TPC: used_includes.add('traceback')).
# Its Install() symbol and the @llvm.global_ctors ctor come only from compiling this module, so a missing file is fail-fast.
if BuildPipeline._traceback_mode != 0:
tb_abs: t.CPtr = stdlib.malloc(8)
tb_rel: t.CPtr = stdlib.malloc(8)
tb_inc: t.CPtr = stdlib.malloc(8)
tb_abs: rawptr = stdlib.malloc(8)
tb_rel: rawptr = stdlib.malloc(8)
tb_inc: rawptr = stdlib.malloc(8)
tb_found: int = -1
if tb_abs is not None and tb_rel is not None and tb_inc is not None:
tb_abs_slot: sizet* = (sizet*)(tb_abs)
@@ -230,7 +229,7 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str,
processed[0] = '\0'
proc_len: sizet = 0
reachable_count: int = 0
wl_pos: t.CPtr = stdlib.malloc(8)
wl_pos: rawptr = stdlib.malloc(8)
if wl_pos is None:
stdlib.free(worklist)
stdlib.free(processed)
@@ -289,9 +288,9 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str,
continue
# includes: resolve on demand via the filesystem (unreferenced includes are never touched)
out_abs: t.CPtr = stdlib.malloc(8)
out_rel: t.CPtr = stdlib.malloc(8)
out_inc: t.CPtr = stdlib.malloc(8)
out_abs: rawptr = stdlib.malloc(8)
out_rel: rawptr = stdlib.malloc(8)
out_inc: rawptr = stdlib.malloc(8)
if out_abs is None or out_rel is None or out_inc is None:
if out_abs is not None: stdlib.free(out_abs)
if out_rel is not None: stdlib.free(out_rel)
@@ -299,10 +298,10 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str,
stdlib.free(mod_buf)
continue
# Defensive init of the output slots (the success path always writes all). The slot union must use (sizet*):
# t.CPtr* collapses to t.CPtr and [0] becomes a 1-byte access, truncating the pointer (0x2e crash root cause).
abs_slot: sizet* = (sizet*)(out_abs)
rel_slot: sizet* = (sizet*)(out_rel)
inc_slot: sizet* = (sizet*)(out_inc)
# rawptr* collapses to rawptr and [0] becomes a 1-byte access, truncating the pointer (0x2e crash root cause).
abs_slot: sizet* = sizet*(out_abs)
rel_slot: sizet* = sizet*(out_rel)
inc_slot: sizet* = sizet*(out_inc)
abs_slot[0] = 0
rel_slot[0] = 0
inc_slot[0] = 0
@@ -1,4 +1,3 @@
import t, c
from stdint import *
import memhub
import string
-1
View File
@@ -1,5 +1,4 @@
import lib.core.Gen.GenApi as GenApi
import t, c
from stdint import *
import memhub
import string
@@ -1,4 +1,3 @@
import t, c
from stdint import *
import memhub
import string
+3 -21
View File
@@ -1,29 +1,11 @@
import t, c
from stdint import *
import memhub
import string
import stdlib
import mlirlite
import win32.win32file as win32file
import win32.win32base as win32base
import win32.fileio as fileio
import viperlib
import sys
import ast
import lib.core.VLogger as VLogger
import lib.core.IncludesScanner as IncludesScanner
import lib.core.Gen.Translator as HandlesTranslator
import lib.Phase.BuildPipeline as BuildPipeline
import lib.Stub.StubGen.VpiReader as VpiReader
import lib.Projectrans.Utils as Utils
import lib.Projectrans.Config as Config
import lib.Stub.StubMerger.StubMergerCollect as StubMergerCollect
import lib.Stub.StubMerger.StubMergerStore as StubMergerStore
import lib.Stub.StubMerger.StubMergerIR as StubMergerIR
import lib.Stub.StubMerger.StubMergerBuild as StubMergerBuild
import lib.Stub.StubMerger as StubMerger
# StubMergerTopo - reverse-topological sort of reachable includes (dependencies first).
# Functions live in this module; cross-module globals are accessed via StubMerger._xxx.
@@ -33,9 +15,9 @@ import lib.Stub.StubMerger as StubMerger
def BuildTopoSortedIndices(store_count: int, store_sha1_arr: bytes, store_mod_arr: bytes, reachable_set: bytes, reachable_count: int, sorted_idx: sizet*) -> int:
"""Reverse-topologically sort reachable includes (dependencies first); store indices go to sorted_idx."""
if store_count <= 0 or store_sha1_arr is None or store_mod_arr is None or sorted_idx is None: return 0
indegree: sizet* = (sizet*)(stdlib.malloc(8 * (store_count + 1)))
queue: sizet* = (sizet*)(stdlib.malloc(8 * (store_count + 1)))
rel_idx: sizet* = (sizet*)(stdlib.malloc(8 * (store_count + 1)))
indegree: sizet* = sizet*(stdlib.malloc(8 * (store_count + 1)))
queue: sizet* = sizet*(stdlib.malloc(8 * (store_count + 1)))
rel_idx: sizet* = sizet*(stdlib.malloc(8 * (store_count + 1)))
if indegree is None or queue is None or rel_idx is None:
if indegree is not None: stdlib.free(indegree)
if queue is not None: stdlib.free(queue)
-17
View File
@@ -1,22 +1,5 @@
import t, c
from stdint import *
import memhub
import string
import stdlib
import mlirlite
import win32.win32file as win32file
import win32.win32base as win32base
import win32.fileio as fileio
import viperlib
import sys
import ast
import lib.core.VLogger as VLogger
import lib.core.IncludesScanner as IncludesScanner
import lib.core.Gen.Translator as HandlesTranslator
import lib.Phase.BuildPipeline as BuildPipeline
import lib.Stub.StubGen.VpiReader as VpiReader
import lib.Projectrans.Utils as Utils
import lib.Projectrans.Config as Config
# StubMerger - merges dependency stub declarations into the main IR (Phase2 core):
-1
View File
@@ -1,4 +1,3 @@
import t, c
import memhub
+1 -2
View File
@@ -1,4 +1,3 @@
import t, c
from stdint import *
import string
import stdio
@@ -38,7 +37,7 @@ define DIAG_MAX_FRAMES = 32 # 调用链上限(与 TypeGenMixin.TRACE_MAX 对
# (import 触发依赖加载 / 泛型特化 / 函数体翻译)时的触发点,
# 用于"从错误发生点向外展示触发过程"。
# 所有字符串都用 stdlib 深拷贝:编译期池会回绕,不能借用池内指针。
class DiagEntry:
struct DiagEntry:
# 字段名不要与"本模块 import 的类名"重名:TPC 的类型/符号表按**非限定名**索引
# (Gen.structs / SymbolTable / _mangle_func_name),曾把本字段的 `File` 与
# `win32.fileio.File` 撞在一起,导致同模块 `fileio.File(...)` 的构造调用把
+15 -6
View File
@@ -1,4 +1,3 @@
import t, c
from stdint import *
import ast
import mlirlite
@@ -922,7 +921,7 @@ novirtual class AssignGenMixin:
return 0
def _annotation_has_cptr_marker(self, annot: ast.AST*) -> int:
"""Return 1 if the annotation union carries a CPtr marker (the star form `<base> | t.CPtr`)."""
"""Return 1 if the annotation union carries a CPtr marker (the star form `<base> | rawptr`)."""
if annot is None: return 0
k: int = annot.kind()
if k == ast.ASTKind.Attribute:
@@ -2796,6 +2795,10 @@ novirtual class AssignGenMixin:
lv_target_ty: mlirlite.LLVMType* = None
if alloca.Ty is not None: lv_target_ty = alloca.Ty.Pointee
rhs_val: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, aa.value, None, 0, trans, lv_target_ty, "local variable annotation")
if rhs_val is None:
# fail-fast: 右值翻译失败时绝不能静默跳过整条声明(此前直接落到 `if rhs_val is
# not None` 之外,变量永远保持未初始化,运行期读到栈垃圾后断言崩溃)。
self.Gen.TypeGenErr.fatal_error(aa, f"the right-hand side of the declaration of '{nm.id}' could not be translated (type inference failed; see the preceding diagnostic)", "TypeError")
if rhs_val is not None:
# Fix degraded annotation: rebuild the alloca with the precise RHS struct type
precise_rhs_ty: mlirlite.LLVMType* = None
@@ -3239,10 +3242,16 @@ novirtual class AssignGenMixin:
if sub_alloca is not None and sub_alloca.Ty is not None and sub_alloca.Ty.Pointee != 0 and self.Gen.ExprGenBase.is_ptr_type(sub_alloca.Ty) != 0:
sub_pointee: mlirlite.LLVMType* = sub_alloca.Ty.Pointee
cls_nm_set: str = self.Gen.StructGen.get_class_name_by_type(pool, sub_pointee)
obj_val_set: mlirlite.Value* = mlirlite.build_load(builder, sub_pointee, sub_alloca)
# Pointer variable (X*): sub_pointee is Ptr(Struct); load it to inspect the inner struct
if cls_nm_set is None and obj_val_set is not None and self.Gen.ExprGenBase.is_ptr_type(sub_pointee) != 0 and sub_pointee.Pointee != 0:
cls_nm_set = self.Gen.StructGen.get_class_name_by_type(pool, sub_pointee.Pointee)
obj_val_set: mlirlite.Value* = None
# 值类型对象槽(`d: dict = dict(bd)`):槽地址就是 self 指针。
# 此前无条件 load,值类型会传出整个 struct 值当 self(非法),
# 指针类型才需要先 load 出对象指针。
if cls_nm_set is not None: obj_val_set = sub_alloca
else:
obj_val_set = mlirlite.build_load(builder, sub_pointee, sub_alloca)
# Pointer variable (X*): sub_pointee is Ptr(Struct); load it to inspect the inner struct
if obj_val_set is not None and self.Gen.ExprGenBase.is_ptr_type(sub_pointee) != 0 and sub_pointee.Pointee != 0:
cls_nm_set = self.Gen.StructGen.get_class_name_by_type(pool, sub_pointee.Pointee)
if cls_nm_set is not None and obj_val_set is not None:
key_val_set: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, sub_asgn.slice, None, 0, trans)
if key_val_set is not None:
-1
View File
@@ -1,5 +1,4 @@
import lib.core.Gen.GenTypes as GenTypes
import t, c
from stdint import *
import ast
import mlirlite
-1
View File
@@ -1,6 +1,5 @@
import lib.core.Gen.GenTypes as GenTypes
import lib.core.Gen.Generator as Generator
import t, c
from stdint import *
import memhub
import mlirlite
+3 -4
View File
@@ -2,7 +2,6 @@ import lib.core.Gen.GenTypes as GenTypes
import lib.core.Gen.Generator as Generator
import lib.core.Gen.GenApi as GenApi
import lib.core.Gen.Translator as HT
import t, c
from stdint import *
import ast
import mlirlite
@@ -130,9 +129,9 @@ novirtual class CallGenBase:
def _auto_addr_mode(self, name: str, idx: int) -> int:
"""Return the value-reference mode for parameter idx of a callee symbol name (0 if none/unknown)."""
if self.AutoAddrModes is None or name is None or idx < 0: return 0
v: t.CPtr = self.AutoAddrModes.__getitem__(name)
v: rawptr = self.AutoAddrModes.__getitem__(name)
if v is None: return 0
modes: str = t.CPtr(v)
modes: str = rawptr(v)
i: int = 0
while i < idx and modes[i] != char(0): i += 1
if i != idx: return 0
@@ -1298,7 +1297,7 @@ novirtual class CallGenBase:
if use_bare != 0: viperlib.snprintf(key_buf, key_len, "%s.%s", class_sha1, method_name)
else:
viperlib.snprintf(key_buf, key_len, "%s.%s.%s", class_sha1, class_name, method_name)
v: t.CPtr = ht.__getitem__(key_buf)
v: rawptr = ht.__getitem__(key_buf)
stdlib.free(key_buf)
if v is None: return None
return rawptr(v)
+2 -3
View File
@@ -1,4 +1,3 @@
import t, c
from stdint import *
import ast
import mlirlite
@@ -786,8 +785,8 @@ novirtual class CallGenCBuiltin(CallGenBase.CallGenBase):
string.memset(operands, 0, operand_size * ASM_MAX_OPERANDS)
# Clobber name array: 8 bytes/slot, capacity ASM_MAX_CLOBBERS, decoupled from
# the operand limit; a single-level t.CPtr buffer with slots cast to str*.
clobbers: t.CPtr = pool.alloc(8 * ASM_MAX_CLOBBERS)
# the operand limit; a single-level rawptr buffer with slots cast to str*.
clobbers: rawptr = pool.alloc(8 * ASM_MAX_CLOBBERS)
if clobbers is None: return None
string.memset(clobbers, 0, 8 * ASM_MAX_CLOBBERS)
+42 -43
View File
@@ -1,4 +1,3 @@
import t, c
from stdint import *
import ast
import mlirlite
@@ -165,18 +164,18 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
# 跨模块:本文件指令表为空,改查对方模块 .vpi 的 esce=/escn=
f_ci = self.Gen.CallGenBase.lookup_escape_dir_via_vpi(sha_ci, class_name, method_name, 0)
n_ci = self.Gen.CallGenBase.lookup_escape_dir_via_vpi(sha_ci, class_name, method_name, 1)
if f_ci != 0 or n_ci != 0: esc_mask_ci = (esc_mask_ci | f_ci) & (~n_ci)
# 同命裁剪(构造器路径):被构造对象的生命期由**调用方**(CallGenCtor)算出并经
# recv_life 传入 —— 这里只有 self_ptr(LLVM 值),拿不到 AST/生命期。
# recv_life == -1 = 未知 → **不做裁剪**(与接这条之前的行为完全一致)。
# 对象在本帧(裸 `T()` / `new stack` / `slife`)时,其余实参也都在本帧即可清位。
if recv_life >= 0 and esc_mask_ci != 0:
if recv_life == GenTypes.LIFE_FRAME or recv_life == GenTypes.LIFE_VALUE:
hard_ci: int = GenApi.get_param_escapes_hard_method(pool, class_name, method_name)
fld_ci: int = esc_mask_ci & (~hard_ci)
if fld_ci != 0:
can_ci2: sizet = len(arg_nodes)
cl_ci: int = self.Gen.CallGen._FrameArgsEscapeClearsMethod(trans, None, arg_nodes, can_ci2, fld_ci)
if f_ci != 0 or n_ci != 0: esc_mask_ci = (esc_mask_ci | f_ci) & (~n_ci)
# 同命裁剪(构造器路径):被构造对象的生命期由**调用方**(CallGenCtor)算出并经
# recv_life 传入 —— 这里只有 self_ptr(LLVM 值),拿不到 AST/生命期。
# recv_life == -1 = 未知 → **不做裁剪**(与接这条之前的行为完全一致)。
# 对象在本帧(裸 `T()` / `new stack` / `slife`)时,其余实参也都在本帧即可清位。
if recv_life >= 0 and esc_mask_ci != 0:
if recv_life == GenTypes.LIFE_FRAME or recv_life == GenTypes.LIFE_VALUE:
hard_ci: int = GenApi.get_param_escapes_hard_method(pool, class_name, method_name)
fld_ci: int = esc_mask_ci & (~hard_ci)
if fld_ci != 0:
can_ci2: sizet = len(arg_nodes)
cl_ci: int = self.Gen.CallGen._FrameArgsEscapeClearsMethod(trans, None, arg_nodes, can_ci2, fld_ci)
if cl_ci != 0: esc_mask_ci = esc_mask_ci & (~cl_ci)
if esc_mask_ci != 0:
dummy_ptr_ci: mlirlite.LLVMType* = mlirlite.Ptr(pool, mlirlite.Int8(pool))
@@ -343,7 +342,7 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
while tail is not None and mlirlite.value_get_next(tail) is not None: tail = mlirlite.value_get_next(tail)
# Default-argument filling: if the call site supplies fewer args than the
# definition, fill the missing ones (defined default, else zero).
mdf_defaults: t.CPtr = None
mdf_defaults: rawptr = None
mdf_default_count: int = 0
mdf_param_count: int = 0
if trans is not None and trans._funcs is not None:
@@ -555,17 +554,17 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
if uint64(self_ptr.Ty) != uint64(cmop_i8_ptr_ty): cmop_bar_ptr = mlirlite.build_bitcast(builder, self_ptr, cmop_i8_ptr_ty)
if cmop_bar_ptr is not None: mlirlite.build_load(builder, cmop_i8_ty, cmop_bar_ptr, 1)
return call_res_cmop
def _MarkWillFreeArgName(self, trans: HT.Translator*, arg_node: ast.AST*) -> int:
"""P2 willFree:把实参变量槽标为"已失效";只处理简单变量名(1=标记过 / 0=未处理)。
与模块级调用同一口径(VarEntry.Freed):该槽其后被读 / 取地址 / 再传时,由
ExprGenName.translate_name_value 或 ExprGenOps 的 AddrOf 分支报 LifetimeError。"""
if trans is None or arg_node is None: return 0
if arg_node.kind() != ast.ASTKind.Name: return 0
nm: ast.Name* = ast.Name*(arg_node)
if nm.id is None: return 0
return self.Gen.VarGen._FreedMark(self.Gen.VarGen.lookup_var(trans.SymTab, nm.id))
def _MarkWillFreeArgName(self, trans: HT.Translator*, arg_node: ast.AST*) -> int:
"""P2 willFree:把实参变量槽标为"已失效";只处理简单变量名(1=标记过 / 0=未处理)。
与模块级调用同一口径(VarEntry.Freed):该槽其后被读 / 取地址 / 再传时,由
ExprGenName.translate_name_value 或 ExprGenOps 的 AddrOf 分支报 LifetimeError。"""
if trans is None or arg_node is None: return 0
if arg_node.kind() != ast.ASTKind.Name: return 0
nm: ast.Name* = ast.Name*(arg_node)
if nm.id is None: return 0
return self.Gen.VarGen._FreedMark(self.Gen.VarGen.lookup_var(trans.SymTab, nm.id))
# _translate_method_call - translate obj.method(args) into a call with the
# object pointer (obj_ptr, an alloca of Ptr(StructTy)) as the self argument.
@@ -586,27 +585,27 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
# 跨模块回退(v3):本文件摘要表按文件重置,跨模块只能查 .vpi 落盘的摘要
sha_mc = self.Gen.StructGen.get_struct_sha1(class_name)
pk_mc: int = self.Gen.CallGenBase.lookup_escape_via_vpi(pool, sha_mc, class_name, method_name)
if pk_mc != 0: esc_mask_mc = int(pk_mc & 4294967295)
# 跨模块常量特化:用实参常量套用对方模块 .vpi 落盘的守卫记录(形参位含 self 偏移)
if esc_mask_mc != 0:
cl_mc: int = self.Gen.CallGenBase.lookup_escape_clears_via_vpi(pool, sha_mc, class_name, method_name, cargs_mc, len(cargs_mc), 1)
if pk_mc != 0: esc_mask_mc = int(pk_mc & 4294967295)
# 跨模块常量特化:用实参常量套用对方模块 .vpi 落盘的守卫记录(形参位含 self 偏移)
if esc_mask_mc != 0:
cl_mc: int = self.Gen.CallGenBase.lookup_escape_clears_via_vpi(pool, sha_mc, class_name, method_name, cargs_mc, len(cargs_mc), 1)
if cl_mc != 0: esc_mask_mc = esc_mask_mc & (~cl_mc)
# 跨过程常量特化(P3 收尾):同文件能拿到方法体时,用常量实参重算摘要(形参位含 self 偏移)
if esc_mask_mc != 0:
can_rf: sizet = len(cargs_mc)
esc_mask_mc = GenApi.refine_escape_mask_method(pool, class_name, method_name, cargs_mc, can_rf, esc_mask_mc)
# 同命裁剪(**方法路径,此前缺失**):只清"写进形参对象字段"那一类位 —— 若其余实参(含
# 接收者)也都在本帧,则接收者与本帧同命、并不真的逃逸。现场:`h.set_q(l)`,h 是
# `new heap slife S()`(生命期本就是 FRAME)、l 是栈数组 → 两都在本帧 → 不该报。
if esc_mask_mc != 0:
hard_mc: int = GenApi.get_param_escapes_hard_method(pool, class_name, method_name)
fld_mc: int = esc_mask_mc & (~hard_mc)
if fld_mc != 0:
recv_mc: ast.AST* = None
if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute:
recv_mc = ast.Attribute*(cl.func).value
can_sf: sizet = len(cargs_mc)
cl_sfm: int = self.Gen.CallGen._FrameArgsEscapeClearsMethod(trans, recv_mc, cargs_mc, can_sf, fld_mc)
esc_mask_mc = GenApi.refine_escape_mask_method(pool, class_name, method_name, cargs_mc, can_rf, esc_mask_mc)
# 同命裁剪(**方法路径,此前缺失**):只清"写进形参对象字段"那一类位 —— 若其余实参(含
# 接收者)也都在本帧,则接收者与本帧同命、并不真的逃逸。现场:`h.set_q(l)`,h 是
# `new heap slife S()`(生命期本就是 FRAME)、l 是栈数组 → 两都在本帧 → 不该报。
if esc_mask_mc != 0:
hard_mc: int = GenApi.get_param_escapes_hard_method(pool, class_name, method_name)
fld_mc: int = esc_mask_mc & (~hard_mc)
if fld_mc != 0:
recv_mc: ast.AST* = None
if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute:
recv_mc = ast.Attribute*(cl.func).value
can_sf: sizet = len(cargs_mc)
cl_sfm: int = self.Gen.CallGen._FrameArgsEscapeClearsMethod(trans, recv_mc, cargs_mc, can_sf, fld_mc)
if cl_sfm != 0: esc_mask_mc = esc_mask_mc & (~cl_sfm)
# 声明式指令最终裁决(@life(escape/noescape):unsafe 但强制),在推断/回退之后最后套用
f_mc: int = GenApi.get_param_escape_force_method(pool, class_name, method_name)
@@ -1191,7 +1190,7 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
# Default-argument filling when the call supplies fewer args than the
# definition; look up the function's defaults info.
mdf_defaults: t.CPtr = None
mdf_defaults: rawptr = None
mdf_default_count: int = 0
mdf_param_count: int = 0
if trans is not None and trans._funcs is not None:
+3 -4
View File
@@ -1,4 +1,3 @@
import t, c
from stdint import *
import ast
import mlirlite
@@ -149,7 +148,7 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
new_ptr: mlirlite.Value* = self.Gen.CallGenCall._call_method_on_ptr(pool, builder, mod, class_name, "__new__", tmp, new_arg_vals, new_arg_count, trans, diag_node_ct, is_volatile, cargs)
if new_ptr is not None:
storage_ptr = new_ptr
# __new__ declares `-> t.CPtr` (i8*) but the class type is Ptr(struct_ty).
# __new__ declares `-> rawptr` (i8*) but the class type is Ptr(struct_ty).
# Using it directly degrades method dispatch; bitcast is a zero-cost type-layer fix.
if new_ptr.Ty is not None:
nw_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, struct_ty)
@@ -711,7 +710,7 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
new_ptr: mlirlite.Value* = self.Gen.CallGenCall._call_method_on_ptr(pool, builder, mod, class_name, "__new__", tmp, new_arg_vals, new_arg_count, trans, diag_node_ct, is_volatile, cargs)
if new_ptr is not None:
storage_ptr = new_ptr
# __new__ declares `-> t.CPtr` (i8*) but the class type is Ptr(struct_ty).
# __new__ declares `-> rawptr` (i8*) but the class type is Ptr(struct_ty).
# Using it directly degrades method dispatch; bitcast is a zero-cost type-layer fix.
if new_ptr.Ty is not None:
nw_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, struct_ty)
@@ -1184,7 +1183,7 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
new_ptr: mlirlite.Value* = self.Gen.CallGenCall._call_method_on_ptr(pool, builder, mod, spec_name, "__new__", tmp, extra_args, arg_count, trans, None, gc_volatile, gc_arg_nodes)
if new_ptr is not None:
storage_ptr = new_ptr
# __new__ declares `-> t.CPtr` (i8*) but the class type is Ptr(struct_ty).
# __new__ declares `-> rawptr` (i8*) but the class type is Ptr(struct_ty).
# Using it directly degrades method dispatch; bitcast is a zero-cost type-layer fix.
if new_ptr.Ty is not None:
nw_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, struct_ty)
+20 -21
View File
@@ -2,7 +2,6 @@ import lib.core.Gen.GenTypes as GenTypes
import lib.core.Gen.Generator as Generator
import lib.core.Gen.Expr.ExprGenCls as ExprGenCls
import lib.core.Gen.Expr.ExprGenName as ExprGenName
import t, c
from stdint import *
import ast
import mlirlite
@@ -28,7 +27,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
# _tc_h0_generic_func - generic function call: infer/specialize type args, then emit the call.
# Must run before normal function/constructor dispatch; hit_box marks handled void calls.
def _tc_h0_generic_func(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cl: ast.Call*, funcs_ptr: t.CPtr, func_count: int, trans: HT.Translator*, func_name: str, cargs: list[ast.AST*]*, can: sizet, hit_box: t.CPtr = None) -> mlirlite.Value*:
def _tc_h0_generic_func(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cl: ast.Call*, funcs_ptr: rawptr, func_count: int, trans: HT.Translator*, func_name: str, cargs: list[ast.AST*]*, can: sizet, hit_box: rawptr = None) -> mlirlite.Value*:
if cl is None or trans is None: return None
if self.Gen.ClassGenGeneric.GenericFuncKeys is None: return None
if cl.func is None: return None
@@ -148,7 +147,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
if string.strcmp(kw.arg, ag.arg) == 0: return int(ki)
return -1
def _emit_generic_func_call(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, trans: HT.Translator*, cl: ast.Call*, spec_name: str, cargs: list[ast.AST*]*, can: sizet, hit_box: t.CPtr = None) -> mlirlite.Value*:
def _emit_generic_func_call(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, trans: HT.Translator*, cl: ast.Call*, spec_name: str, cargs: list[ast.AST*]*, can: sizet, hit_box: rawptr = None) -> mlirlite.Value*:
"""Emit the specialized-function call, coercing args to the specialized param types."""
func: mlirlite.Function* = self.Gen.CallGenBase.find_func_in_module(mod, spec_name)
if func is None:
@@ -189,7 +188,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
# reads its first field as a string and emits illegal IR. Use the looked-up symbol name.
return mlirlite.build_call(builder, spec_name, head, int(can), ret_ty, 0)
def _tc_h1_super_ctraits(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cl: ast.Call*, funcs_ptr: t.CPtr, func_count: int, trans: HT.Translator*, func_name: str, cargs: list[ast.AST*]*, can: sizet, hit_box: t.CPtr = None) -> mlirlite.Value*:
def _tc_h1_super_ctraits(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cl: ast.Call*, funcs_ptr: rawptr, func_count: int, trans: HT.Translator*, func_name: str, cargs: list[ast.AST*]*, can: sizet, hit_box: rawptr = None) -> mlirlite.Value*:
"""Compile-time super()/ctraits detection; sets hit_box on interception, else returns None."""
# super().method(args) -> static call to the parent method. Must intercept before attribute
# dispatch, else Call(super) is treated as a plain function call and lookup hits self/vtable.
@@ -221,7 +220,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
return mlirlite.const_int32(pool, 0)
return None
def _tc_h2_c_special(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cl: ast.Call*, funcs_ptr: t.CPtr, func_count: int, trans: HT.Translator*, func_name: str, cargs: list[ast.AST*]*, can: sizet, hit_box: t.CPtr = None) -> mlirlite.Value*:
def _tc_h2_c_special(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cl: ast.Call*, funcs_ptr: rawptr, func_count: int, trans: HT.Translator*, func_name: str, cargs: list[ast.AST*]*, can: sizet, hit_box: rawptr = None) -> mlirlite.Value*:
"""Special c.Deref/Asm/LLVMIR calls; marks hit_box for void calls, else returns None."""
# __asm__(...) / __llvmir__(...) sugar: bare calls equal c.Asm(...) / c.LLVMIR(...). Must
# intercept before the c.* attribute branch, since the bare cl.func is a Name.
@@ -301,7 +300,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
return self.Gen.AsyncGen.translate_coro_gather(builder, pool, mod, cl, trans)
return None
def _tc_h3_stdarg_cast(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cl: ast.Call*, funcs_ptr: t.CPtr, func_count: int, trans: HT.Translator*, func_name: str, cargs: list[ast.AST*]*, can: sizet) -> mlirlite.Value*:
def _tc_h3_stdarg_cast(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cl: ast.Call*, funcs_ptr: rawptr, func_count: int, trans: HT.Translator*, func_name: str, cargs: list[ast.AST*]*, can: sizet) -> mlirlite.Value*:
"""stdarg sugar and str/int/bool built-in casts; returns None when unmatched."""
# stdarg sugar (va_arg / arg / va_start / va_end): these are declared as pass bodies in
# stdarg.vp and handled here at translation time by emitting LLVM va_arg / intrinsic calls.
@@ -362,16 +361,16 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
return None
return None
def _tc_h4_type_casts(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cl: ast.Call*, funcs_ptr: t.CPtr, func_count: int, trans: HT.Translator*, func_name: str, cargs: list[ast.AST*]*, can: sizet) -> mlirlite.Value*:
def _tc_h4_type_casts(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cl: ast.Call*, funcs_ptr: rawptr, func_count: int, trans: HT.Translator*, func_name: str, cargs: list[ast.AST*]*, can: sizet) -> mlirlite.Value*:
"""t.XXX / typedef type casts; returns None when unmatched."""
# Detect t.XXX type casts (uint64(ptr), t.CPtr(val), int(val), ...): when func is t.XXX and
# Detect t.XXX type casts (uint64(ptr), rawptr(val), int(val), ...): when func is t.XXX and
# XXX is a known type, treat it as a cast; once confirmed, never fall through to cross-module calls.
if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute:
tc_at: ast.Attribute* = ast.Attribute*(cl.func)
if tc_at.value is not None and tc_at.value.kind() == ast.ASTKind.Name:
tc_mod_nm: ast.Name* = ast.Name*(tc_at.value)
if tc_mod_nm.id is not None and string.strcmp(tc_mod_nm.id, "t") == 0:
# t.CType(value, TypeName, t.CPtr) casts value to TypeName*; TypeName is a type
# t.CType(value, TypeName, rawptr) casts value to TypeName*; TypeName is a type
# name (not a value), so it must be handled explicitly or it falls through to a FATAL.
if string.strcmp(func_name, "CType") == 0 and can >= 2:
cty_node: ast.AST* = cargs.get(1)
@@ -388,7 +387,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
cty_arg3: ast.AST* = cargs.get(2)
cty_is_cptr: int = 0
if cty_arg3 is not None:
# Form 1: t.CPtr (Attribute)
# Form 1: rawptr (Attribute)
if cty_arg3.kind() == ast.ASTKind.Attribute:
cty_at3: ast.Attribute* = ast.Attribute*(cty_arg3)
if cty_at3.attr is not None and string.strcmp(cty_at3.attr, "CPtr") == 0:
@@ -413,7 +412,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
if cty_val is not None: return self.Gen.CallGenCtor._translate_t_type_cast(pool, builder, cty_val, ctype_target, cty_arg0, trans)
# t.CType confirmed: even if type resolution fails, do not fall through to a cross-module FATAL
return None
# CConst is a const marker: CConst(val, t.CPtr) -> Ptr(i8) (const void*). Without
# CConst is a const marker: CConst(val, rawptr) -> Ptr(i8) (const void*). Without
# this it emits an undefined @CConst; single-arg CConst(x) returns the value unchanged.
if string.strcmp(func_name, "CConst") == 0:
if can >= 2:
@@ -425,13 +424,13 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
target_ty: mlirlite.LLVMType* = self.Gen.TypeGenResolve.map_t_type(pool, func_name)
if target_ty is not None:
tc_effective_can: sizet = can
# Two-arg form: int(val, t.CPtr) -> Ptr(target_ty). void* is illegal in LLVM,
# Two-arg form: int(val, rawptr) -> Ptr(target_ty). void* is illegal in LLVM,
# so CVoid* must be represented as i8*.
if can == 2:
tc_arg2_node: ast.AST* = cargs.get(1)
tc_arg2_is_cptr: int = 0
if tc_arg2_node is not None:
# Form 1: t.CPtr (Attribute form)
# Form 1: rawptr (Attribute form)
if tc_arg2_node.kind() == ast.ASTKind.Attribute:
tc_arg2_at: ast.Attribute* = ast.Attribute*(tc_arg2_node)
if tc_arg2_at.attr is not None and string.strcmp(tc_arg2_at.attr, "CPtr") == 0:
@@ -513,13 +512,13 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
imp_gp = trans._imported_modules
fi_gp = trans._from_imports
bare_ty = self.Gen.TypeGenResolve.resolve_annotation_type(pool, gp_ast, imp_gp, fi_gp, trans, 0)
# Generic two-arg form T(val, t.CPtr) -> Ptr(T), aligned with int(val, t.CPtr) -> Ptr(i32);
# Generic two-arg form T(val, rawptr) -> Ptr(T), aligned with int(val, rawptr) -> Ptr(i32);
# without it the two-arg call misses the can==1 branch and misreports an undefined bare call.
if bare_ty is not None and can == 2:
bare_arg2_node: ast.AST* = cargs.get(1)
bare_arg2_is_cptr: int = 0
if bare_arg2_node is not None:
# Form 1: t.CPtr (Attribute form)
# Form 1: rawptr (Attribute form)
if bare_arg2_node.kind() == ast.ASTKind.Attribute:
bare_arg2_at: ast.Attribute* = ast.Attribute*(bare_arg2_node)
if bare_arg2_at.attr is not None and string.strcmp(bare_arg2_at.attr, "CPtr") == 0:
@@ -552,7 +551,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
return None
def _tc_h5_sizeof_len_isinst(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cl: ast.Call*, funcs_ptr: t.CPtr, func_count: int, trans: HT.Translator*, func_name: str, cargs: list[ast.AST*]*, can: sizet) -> mlirlite.Value*:
def _tc_h5_sizeof_len_isinst(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cl: ast.Call*, funcs_ptr: rawptr, func_count: int, trans: HT.Translator*, func_name: str, cargs: list[ast.AST*]*, can: sizet) -> mlirlite.Value*:
"""__sizeof__/len/isinstance built-ins; returns None when unmatched."""
# Detect ClassName.__sizeof__() and return the struct size constant (i64); it is a builtin,
# not a cross-module call. Supports e.g. Argument.__sizeof__() and module-qualified classes.
@@ -616,7 +615,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
# dot after a sha1 prefix. Union types (X*) and rawptr[X] both have pointer size 8.
if string.strstr(sz_cls_name, "|") is not None: return mlirlite.const_int64(pool, 8)
if string.strncmp(sz_cls_name, "rawptr[", 7) == 0: return mlirlite.const_int64(pool, 8)
# A trailing '*' means pointer size 8 (rawptr[T] == t.CPtr[T] == T*). Must check
# A trailing '*' means pointer size 8 (rawptr[T] == rawptr[T] == T*). Must check
# trailing, not strrchr, since containers like list[{sha}.AST*] also contain '*'.
_sz_len_s: sizet = string.strlen(sz_cls_name)
while _sz_len_s > 0 and sz_cls_name[_sz_len_s - 1] == ' ': _sz_len_s -= 1
@@ -828,7 +827,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
return mlirlite.build_call(builder, len_mangled_x, len_self_x, 1, len_ret_x, 0)
# fail-fast: the class has no __len__ (compile-time error, matching TPC
# HandlesExprBuiltin._FailFastLenUnsupported wording).
self.Gen.TypeGenErr.fatal_error(len_arg_node, f"len() argument type '{len_cls_nm}' has no __len__ method. len() only supports fixed-size arrays/array pointers (t.CArray[T, N]), i8* (str/bytes), or structs with __len__; non-i8 bare pointers/scalar pointers have no length semantics: use str/bytes for strings, record the length yourself when the element count is needed, or use t.CPtr[Element] and maintain the element count explicitly", "TypeError")
self.Gen.TypeGenErr.fatal_error(len_arg_node, f"len() argument type '{len_cls_nm}' has no __len__ method. len() only supports fixed-size arrays/array pointers (t.CArray[T, N]), i8* (str/bytes), or structs with __len__; non-i8 bare pointers/scalar pointers have no length semantics: use str/bytes for strings, record the length yourself when the element count is needed, or use rawptr[Element] and maintain the element count explicitly", "TypeError")
# Non-struct: str (Ptr(Int8)) uses strlen; other types fail fast
len_arg_ty: mlirlite.LLVMType* = mlirlite.value_get_ty(len_val)
len_is_str: int = 0
@@ -861,7 +860,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
return mlirlite.build_call(builder, "strlen", len_coerced, 1, i64_ty_ln2, 0)
# fail-fast: not a string/array/object, so length is unavailable (matching TPC
# HandlesExprBuiltin._FailFastLenUnsupported wording).
self.Gen.TypeGenErr.fatal_error(len_arg_node, "len() argument type not supported (not a string/array/object). len() only supports fixed-size arrays/array pointers (t.CArray[T, N]), i8* (str/bytes), or structs with __len__; non-i8 bare pointers/scalar pointers have no length semantics: use str/bytes for strings, record the length yourself when the element count is needed, or use t.CPtr[Element] and maintain the element count explicitly", "TypeError")
self.Gen.TypeGenErr.fatal_error(len_arg_node, "len() argument type not supported (not a string/array/object). len() only supports fixed-size arrays/array pointers (t.CArray[T, N]), i8* (str/bytes), or structs with __len__; non-i8 bare pointers/scalar pointers have no length semantics: use str/bytes for strings, record the length yourself when the element count is needed, or use rawptr[Element] and maintain the element count explicitly", "TypeError")
# Detect isinstance(): runtime vtable subclass-set comparison. obj's head vtable pointer must
# be in {Cls and all subclass vtables}; NoVTable targets fall back to a compile-time check.
@@ -873,7 +872,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
return None
def _tc_h6_method_dispatch(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cl: ast.Call*, funcs_ptr: t.CPtr, func_count: int, trans: HT.Translator*, func_name: str, cargs: list[ast.AST*]*, can: sizet, hit_box: t.CPtr = None) -> mlirlite.Value*:
def _tc_h6_method_dispatch(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cl: ast.Call*, funcs_ptr: rawptr, func_count: int, trans: HT.Translator*, func_name: str, cargs: list[ast.AST*]*, can: sizet, hit_box: rawptr = None) -> mlirlite.Value*:
"""Static class/method/receiver/self.field/list built-ins; sets hit_box, else returns None."""
# REnum.kind() compiles to a __tag read (offset 0, type from the field declaration): bitcast obj
# to __tag* + load. Four criteria detect REnum, incl. pointee __tag and X.field annotations.
@@ -917,7 +916,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
rk_ve: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, rk_nm.id)
rk_annot_raw: str = self.Gen.VarGen.var_annot_class_name(rk_ve)
if rk_annot_raw is not None:
# Truncate the union/generic suffix (" | t.CPtr" / "[...]")
# Truncate the union/generic suffix (" | rawptr" / "[...]")
rk_ae: sizet = 0
while rk_annot_raw[rk_ae] != '\0' and rk_annot_raw[rk_ae] != ' ' and rk_annot_raw[rk_ae] != '|' and rk_annot_raw[rk_ae] != '[': rk_ae += 1
rk_ab: str = pool.alloc(rk_ae + 1)
-1
View File
@@ -1,5 +1,4 @@
import lib.core.Gen.GenTypes as GenTypes
import t, c
from stdint import *
import ast
import mlirlite
+3 -4
View File
@@ -1,5 +1,4 @@
import lib.core.Gen.GenTypes as GenTypes
import t, c
from stdint import *
import ast
import mlirlite
@@ -863,7 +862,7 @@ novirtual class CallGenLeafKit(CallGenCBuiltin.CallGenCBuiltin):
return None
def add_func_to_table(self, funcs: GenTypes.FuncEntry*, func_count: int, name: str, func: mlirlite.Function*, max_count: int, defaults: t.CPtr = None, default_count: int = 0, param_count: int = 0, pool: memhub.MemBuddy* = None, def_sha1: str = None) -> int:
def add_func_to_table(self, funcs: GenTypes.FuncEntry*, func_count: int, name: str, func: mlirlite.Function*, max_count: int, defaults: rawptr = None, default_count: int = 0, param_count: int = 0, pool: memhub.MemBuddy* = None, def_sha1: str = None) -> int:
"""Add a function to a func table; returns 0 on success"""
if name is None or func is None or funcs is None: return 1
if func_count >= max_count: return 1
@@ -1021,7 +1020,7 @@ novirtual class CallGenLeafKit(CallGenCBuiltin.CallGenCBuiltin):
return 1
def _register_global_defaults(self, name: str, defaults: t.CPtr, default_count: int, param_count: int, def_sha1: str = None) -> int:
def _register_global_defaults(self, name: str, defaults: rawptr, default_count: int, param_count: int, def_sha1: str = None) -> int:
"""Register a function's default arguments in the global table; returns 0 on success"""
if name is None or defaults is None: return 1
if self._init_global_defaults_table() == 0: return 1
@@ -1066,7 +1065,7 @@ novirtual class CallGenLeafKit(CallGenCBuiltin.CallGenCBuiltin):
string.memset(kinds, 0, default_count * 4)
string.memset(ints, 0, default_count * 8)
string.memset(strs, 0, default_count * 8)
dl: list[ast.AST*]* = (list[ast.AST*] | t.CPtr)(uint64(defaults))
dl: list[ast.AST*]* = list[ast.AST*]*(uint64(defaults))
di: int
for di in range(int(default_count)):
dnode: ast.AST* = dl.get(di)
+2 -3
View File
@@ -1,4 +1,3 @@
import t, c
from stdint import *
import ast
import mlirlite
@@ -151,7 +150,7 @@ novirtual class CallGenLib(CallGenBase.CallGenBase):
i2 += 1
return new_head
# _coerce_args_to_def_params: coerce cross-module call args to vpi definition-side param
# _coerce_args_to_def_params: coerce cross-module call args to vpi definition-side param
# types so call/declare/define widths match; bare C-lib names use the C-lib path instead.
def _coerce_args_to_def_params(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, call_name: str, head: mlirlite.Value*, count: int, cargs: list[ast.AST*]* = None, self_off: int = 0, trans: HT.Translator* = None) -> mlirlite.Value*:
"""Coerce cross-module call args to vpi definition-side param types; returns the rebuilt chain"""
@@ -231,7 +230,7 @@ novirtual class CallGenLib(CallGenBase.CallGenBase):
if cls_nm is not None: stdlib.free(cls_nm)
return new_head
# _coerce_arg_value_to_ty: convert one arg Value to the target param type -- same-width ints
# _coerce_arg_value_to_ty: convert one arg Value to the target param type -- same-width ints
# pass through, narrow/wide ints trunc/extend, mismatched pointers bitcast; else unchanged.
def _coerce_arg_value_to_ty(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, val: mlirlite.Value*, tgt_ty: mlirlite.LLVMType*, node: ast.AST* = None, trans: HT.Translator* = None, tpl_src: int = 0) -> mlirlite.Value*:
"""Convert one argument Value to the target param type; leaves it unchanged when unsafe"""
+8 -11
View File
@@ -1,14 +1,11 @@
import lib.core.Gen.GenTypes as GenTypes
import t, c
from stdint import *
import ast
import mlirlite
import memhub
import string
import stdio
import stdlib
import viperlib
import lib.core.VLogger as VLogger
import lib.core.Gen.Translator as HT
import lib.core.Gen.Type.TypeSpec as TSpec
import lib.Stub.StubGen.VpiReader as VpiReader
@@ -148,7 +145,7 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
cur_param = mlirlite.param_get_next(cur_param)
return 0
def translate_call(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, node: ast.AST*, funcs_ptr: t.CPtr, func_count: int, trans: HT.Translator* = None) -> mlirlite.Value*:
def translate_call(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, node: ast.AST*, funcs_ptr: rawptr, func_count: int, trans: HT.Translator* = None) -> mlirlite.Value*:
"""Translate a Call node and return the result Value."""
cl: ast.Call* = ast.Call*(node)
# Static symbols must not be used cross-module: fail fast on mod.fn(...) hitting another
@@ -276,7 +273,7 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
if h0_hit_val != 0: return None
# super()/ctraits detection: a void method yields None but is handled, so hit_box must
# separate "handled" from "missed"; otherwise super() falls through and reports undefined.
h1_hit: t.CPtr = stdlib.malloc(4)
h1_hit: rawptr = stdlib.malloc(4)
if h1_hit is not None: h1_hit[0] = 0
_tc_h1_super_ctraits_res: mlirlite.Value* = self.Gen.CallGenDispatch._tc_h1_super_ctraits(pool, builder, mod, cl, funcs_ptr, func_count, trans, func_name, cargs, can, h1_hit)
h1_hit_val: int = 0
@@ -340,7 +337,7 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
# c.Asm/c.LLVMIR special calls return None when void but are handled, so use hit_box to
# separate "missed"; otherwise they fall through to the external-function path and mis-report.
h2_hit: t.CPtr = stdlib.malloc(4)
h2_hit: rawptr = stdlib.malloc(4)
if h2_hit is not None: h2_hit[0] = 0
_tc_h2_c_special_res: mlirlite.Value* = self.Gen.CallGenDispatch._tc_h2_c_special(pool, builder, mod, cl, funcs_ptr, func_count, trans, func_name, cargs, can, h2_hit)
h2_hit_val: int = 0
@@ -363,7 +360,7 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
# Method dispatch may succeed yet return None (e.g. void __before_init__/__init__), so hit_box
# must separate "recognized method" from "missed"; otherwise the object name is looked up as a module.
h6_hit: t.CPtr = stdlib.malloc(4)
h6_hit: rawptr = stdlib.malloc(4)
if h6_hit is not None: h6_hit[0] = 0
_tc_h6_method_dispatch_res: mlirlite.Value* = self.Gen.CallGenDispatch._tc_h6_method_dispatch(pool, builder, mod, cl, funcs_ptr, func_count, trans, func_name, cargs, can, h6_hit)
h6_hit_val: int = 0
@@ -781,7 +778,7 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
found_func = bi_decl
# Use accessors to bypass stub type restrictions.
found_ret_ty: mlirlite.LLVMType* = None
found_params: t.CPtr = None
found_params: rawptr = None
if found_func is not None:
found_ret_ty = mlirlite.function_get_ret_ty(found_func)
found_params = mlirlite.function_get_params(found_func)
@@ -912,7 +909,7 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
return self.Gen.CallGenLeafKit.translate_closure_call(pool, builder, mod, fp_alloca, cargs, can, trans)
# ---- M3: session-function table hit -> take the ctx function-pointer slot and call indirectly ----
# Placed last in the found_func-is-None block so locals/closures shadow session functions; only foreign bare names reach the table.
sfn_box: t.CPtr = None
sfn_box: rawptr = None
if trans is not None and trans.ReplCtx is not None and (cl.func is None or cl.func.kind() == ast.ASTKind.Name):
sfn_box = stdlib.malloc(4)
if sfn_box is not None:
@@ -1069,7 +1066,7 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
# Default-argument filling: when fewer args than params, fill the gaps; the defaults array is
# right-aligned to args (defaults[0] maps to args[offset], offset = param_count - default_count).
if can > 0 or (can == 0 and cargs is not None):
df_defaults: t.CPtr = None
df_defaults: rawptr = None
df_default_count: int = 0
df_param_count: int = 0
# Compute the callee module SHA1 as a qualifier key (module.func -> that module; bare -> current),
@@ -1142,7 +1139,7 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
df_arg_val: mlirlite.Value* = None
if gd_entry is not None: df_arg_val = self.Gen.CallGenLeafKit._build_const_from_snapshot(pool, mod, gd_entry, df_di, trans, builder)
if df_arg_val is None and df_defaults is not None:
df_defaults_list: list[ast.AST*]* = (list[ast.AST*] | t.CPtr)(uint64(df_defaults))
df_defaults_list: list[ast.AST*]* = list[ast.AST*]*(df_defaults)
df_default_node: ast.AST* = df_defaults_list.get(df_di)
if df_default_node is not None: df_arg_val = self.Gen.ExprGen.translate_value(builder, pool, mod, df_default_node, None, 0, trans)
if df_arg_val is not None:
+4 -5
View File
@@ -1,6 +1,5 @@
import lib.core.Gen.GenTypes as GenTypes
import lib.core.Gen.GenApi as GenApi
import t, c
from stdint import *
import ast
import mlirlite
@@ -74,10 +73,10 @@ novirtual class CallGenProp(CallGenBase.CallGenBase):
if cls_sha1_rt is None or self._dep_text_ll_exists(pool, cls_sha1_rt) == 0: call_ret_ty = mlirlite.Int32(pool)
else: call_ret_ty = self._fail_method_ret_ty(method_name, None, class_name, None, cls_sha1_rt)
# Value-reference (&T / &temp T) params: auto-address the user args before param-type
# coercion (self/obj or the classmethod cls occupies index 0, skipped via start_idx).
call_head = self._apply_auto_addr_to_call_args(builder, pool, mod, trans, call_name, arg_nodes, call_head, call_count, start_idx)
# Value-reference (&T / &temp T) params: auto-address the user args before param-type
# coercion (self/obj or the classmethod cls occupies index 0, skipped via start_idx).
call_head = self._apply_auto_addr_to_call_args(builder, pool, mod, trans, call_name, arg_nodes, call_head, call_count, start_idx)
# Coerce args to the declared param types (same as _translate_method_call); target func: found_func,
# else the mangled module declare. Otherwise call(i32) vs declare(i64) mismatches and llc errors.
tgt_func_pc: mlirlite.Function* = found_func
+6 -7
View File
@@ -1,4 +1,3 @@
import t, c
from stdint import *
import ast
import mlirlite
@@ -17,7 +16,7 @@ novirtual class CallGenVa(CallGenBase.CallGenBase):
# _func_has_eh_params - whether callee signature has try-except EH out params (main excluded)
# ============================================================
def _va_arg_get_alloca_ptr(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, arg_node: ast.AST*, funcs_ptr: t.CPtr, func_count: int, trans: HT.Translator*) -> mlirlite.Value*:
def _va_arg_get_alloca_ptr(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, arg_node: ast.AST*, funcs_ptr: rawptr, func_count: int, trans: HT.Translator*) -> mlirlite.Value*:
"""Return the alloca pointer of a va_list variable without loading it."""
if arg_node is None: return None
# Name node: look up the alloca pointer from the symbol table (no load)
@@ -37,7 +36,7 @@ novirtual class CallGenVa(CallGenBase.CallGenBase):
# Type node present but unresolvable: fail-fast, never fall back to i32 (would read the vararg slot at the wrong width)
self.Gen.TypeGenErr.fatal_error(type_node, "va_arg type resolution failed", "TypeError")
return mlirlite.Int32(pool)
def _translate_va_arg_sugar(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cl: ast.Call*, cargs: list[ast.AST*]*, can: sizet, funcs_ptr: t.CPtr, func_count: int, trans: HT.Translator*) -> mlirlite.Value*:
def _translate_va_arg_sugar(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cl: ast.Call*, cargs: list[ast.AST*]*, can: sizet, funcs_ptr: rawptr, func_count: int, trans: HT.Translator*) -> mlirlite.Value*:
"""Emit a va_arg instruction reading the next variadic argument from the va_list."""
if can == 0 or cargs is None: return mlirlite.const_int32(pool, 0)
@@ -67,7 +66,7 @@ novirtual class CallGenVa(CallGenBase.CallGenBase):
# Emit the va_arg instruction
return mlirlite.build_va_arg(builder, ap_ptr, read_ty)
def _translate_arg_sugar(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cl: ast.Call*, cargs: list[ast.AST*]*, can: sizet, funcs_ptr: t.CPtr, func_count: int, trans: HT.Translator*) -> mlirlite.Value*:
def _translate_arg_sugar(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cl: ast.Call*, cargs: list[ast.AST*]*, can: sizet, funcs_ptr: rawptr, func_count: int, trans: HT.Translator*) -> mlirlite.Value*:
"""Emit va_arg reading from the enclosing variadic's own va_list or a forwarded va_list."""
# arg() has two forms:
# (1) Own-variadic form: inside `def f(a, *args)`, trans._va_list_ptr is the i8* address of
@@ -104,7 +103,7 @@ novirtual class CallGenVa(CallGenBase.CallGenBase):
# Emit va_arg, using va_ptr as the va_list address (own buffer or forwarded pointer)
return mlirlite.build_va_arg(builder, va_ptr, read_ty)
def _translate_va_start_sugar(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cargs: list[ast.AST*]*, can: sizet, funcs_ptr: t.CPtr, func_count: int, trans: HT.Translator*) -> mlirlite.Value*:
def _translate_va_start_sugar(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cargs: list[ast.AST*]*, can: sizet, funcs_ptr: rawptr, func_count: int, trans: HT.Translator*) -> mlirlite.Value*:
"""Emit llvm.va_start to initialize the va_list (the second C-ABI argument is unused)."""
if can == 0 or cargs is None: return mlirlite.const_int32(pool, 0)
@@ -122,7 +121,7 @@ novirtual class CallGenVa(CallGenBase.CallGenBase):
void_ty_vs: mlirlite.LLVMType* = mlirlite.Void(pool)
mlirlite.build_call(builder, "llvm.va_start", ap_i8, 1, void_ty_vs, 0)
return mlirlite.const_int32(pool, 0)
def _translate_va_end_sugar(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cargs: list[ast.AST*]*, can: sizet, funcs_ptr: t.CPtr, func_count: int, trans: HT.Translator*) -> mlirlite.Value*:
def _translate_va_end_sugar(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cargs: list[ast.AST*]*, can: sizet, funcs_ptr: rawptr, func_count: int, trans: HT.Translator*) -> mlirlite.Value*:
"""Emit llvm.va_end to clean up the va_list (a no-op on most platforms)."""
if can == 0 or cargs is None: return mlirlite.const_int32(pool, 0)
@@ -180,7 +179,7 @@ novirtual class CallGenVa(CallGenBase.CallGenBase):
fixed_count: int = -1
if func is not None and func.IsVarArg == 1: fixed_count = self._get_func_fixed_param_count(func)
else:
# Fall back to the known variadic C-lib signature table (snprintf etc.) when func is None (stub not yet injected)
# Fall back to the known variadic C-lib signature table (snprintf etc.) when func is None (stub not yet injected)
# or IsVarArg=0 (ad hoc declare); otherwise i32 varargs aren't promoted and the Win64 8-byte slot keeps only 4 bytes.
fixed_count = self._get_known_vararg_cfunc_fixed_count(call_name)
if fixed_count < 0: return 0
-1
View File
@@ -1,4 +1,3 @@
import t, c
from stdint import *
import ast
import mlirlite
-1
View File
@@ -1,4 +1,3 @@
import t, c
from stdint import *
import ast
import memhub
-1
View File
@@ -1,5 +1,4 @@
import lib.core.Gen.GenTypes as GenTypes
import t, c
from stdint import *
import ast
import mlirlite
-1
View File
@@ -1,5 +1,4 @@
import lib.core.Gen.GenTypes as GenTypes
import t, c
from stdint import *
import ast
import mlirlite
+1 -2
View File
@@ -1,5 +1,4 @@
import lib.core.Gen.GenTypes as GenTypes
import t, c
from stdint import *
import ast
import mlirlite
@@ -579,7 +578,7 @@ novirtual class ClassGenGeneric(ClassGenBase.ClassGenBase):
if self.GenericClassNames is None or class_name is None: return None
# Assign the list to a local to trigger type inference (avoids field __len__ failure)
names_local: list[str]* = self.GenericClassNames
nodes_local: list[ast.ClassDef*] | t.CPtr = self.GenericClassNodes
nodes_local: list[ast.ClassDef*]* = self.GenericClassNodes
cn: sizet = len(names_local)
i: sizet
for i in range(int(cn)):
-1
View File
@@ -1,4 +1,3 @@
import t, c
from stdint import *
from . import ClassGenBase
-1
View File
@@ -1,4 +1,3 @@
import t, c
from stdint import *
import mlirlite
import memhub
-2
View File
@@ -1,11 +1,9 @@
import lib.core.Gen.GenTypes as GenTypes
import t, c
from stdint import *
import ast
import mlirlite
import memhub
import string
import stdio
import stdlib
import viperlib
import lib.core.Gen.Translator as HT
-2
View File
@@ -1,11 +1,9 @@
import lib.core.Gen.Generator as Generator
import t, c
from stdint import *
import ast
import mlirlite
import memhub
import string
import stdlib
import viperlib
import lib.core.Gen.Translator as HT
import lib.Projectrans.Utils as Utils
-1
View File
@@ -1,4 +1,3 @@
import t, c
from stdint import *
import ast
import mlirlite
-1
View File
@@ -1,4 +1,3 @@
import t, c
from stdint import *
import ast
import mlirlite
-1
View File
@@ -1,4 +1,3 @@
import t, c
from stdint import *
import ast
import mlirlite
-1
View File
@@ -1,4 +1,3 @@
import t, c
from stdint import *
import ast
import mlirlite
+2 -3
View File
@@ -1,4 +1,3 @@
import t, c
from stdint import *
import ast
import memhub
@@ -28,7 +27,7 @@ novirtual class ExprGenMixin(ExprGenBase.ExprGenBase):
"""Expression-handling mixin (pure methods, no state fields)."""
# Translate a value expression (RHS dispatch) - module-level version.
def translate_value(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, node: ast.AST*, funcs_ptr: t.CPtr = None, func_count: int = 0, trans: HT.Translator* = None, target_ty: mlirlite.LLVMType* = None, use_ctx: str = None) -> mlirlite.Value*:
def translate_value(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, node: ast.AST*, funcs_ptr: rawptr = None, func_count: int = 0, trans: HT.Translator* = None, target_ty: mlirlite.LLVMType* = None, use_ctx: str = None) -> mlirlite.Value*:
"""Translate a value expression to an LLVM Value (depth-guard wrapper; see _translate_value_impl).
target_ty = assignment/parameter type context; use_ctx = real call site for CDefine width checks."""
if trans is not None:
@@ -38,7 +37,7 @@ novirtual class ExprGenMixin(ExprGenBase.ExprGenBase):
if trans is not None: trans.TranslateDepth -= 1
return rv
def _translate_value_impl(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, node: ast.AST*, funcs_ptr: t.CPtr = None, func_count: int = 0, trans: HT.Translator* = None, target_ty: mlirlite.LLVMType* = None, use_ctx: str = None) -> mlirlite.Value*:
def _translate_value_impl(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, node: ast.AST*, funcs_ptr: rawptr = None, func_count: int = 0, trans: HT.Translator* = None, target_ty: mlirlite.LLVMType* = None, use_ctx: str = None) -> mlirlite.Value*:
"""Translate a value expression to an LLVM Value."""
if node is None: return None
k: int = node.kind()
+37 -7
View File
@@ -1,5 +1,4 @@
import lib.core.Gen.GenTypes as GenTypes
import t, c
from stdint import *
import ast
import mlirlite
@@ -44,7 +43,7 @@ novirtual class ExprGenName(ExprGenBase.ExprGenBase):
if trans is not None:
if self.Gen.VarGen._FreedCheck(self.Gen.VarGen.lookup_var(trans.SymTab, nm_id)) != 0:
self.Gen.TypeGenErr.fatal_error(node, f"cannot use '{nm_id}': already released by a @life(willFree(...)) call", "LifetimeError")
# CDefine compile-time constant: return the constant (float -> double, int -> i32).
# CDefine compile-time constant: return the constant (float -> double, int -> i32).
# Existing variables win over CDefine table entries injected from vpi stubs (lookup_var guard).
cdef_val_d: double = self.Gen.TypeGenCDef.lookup_cdefine_double(nm_id)
if self.Gen.TypeGenCDef.is_cdefine_found() != 0:
@@ -55,10 +54,10 @@ novirtual class ExprGenName(ExprGenBase.ExprGenBase):
cdef_sv: str = self.Gen.TypeGenCDef.lookup_cdefine_string(nm_id)
if cdef_sv is not None: return self.Gen.ExprGenConst.create_global_string(builder, pool, mod, cdef_sv, trans)
self.Gen.TypeGenErr.fatal_error(node, f"CDefine string value missing: {nm_id}", "InternalCompilerError")
# 浮点常量:声明为 32 位 float(`define PI: float = ...`)时直接出 f32 常量,
# 否则出 f64 —— 出 f64 会把后续 f32 字面量整体提升成 double(fpext/cvtss2sd)。
if self.Gen.TypeGenCDef.is_cdefine_double() != 0:
if self.Gen.TypeGenCDef.is_cdefine_float32() != 0: return mlirlite.ConstFloat(pool, mlirlite.Float(pool), cdef_val_d)
# 浮点常量:声明为 32 位 float(`define PI: float = ...`)时直接出 f32 常量,
# 否则出 f64 —— 出 f64 会把后续 f32 字面量整体提升成 double(fpext/cvtss2sd)。
if self.Gen.TypeGenCDef.is_cdefine_double() != 0:
if self.Gen.TypeGenCDef.is_cdefine_float32() != 0: return mlirlite.ConstFloat(pool, mlirlite.Float(pool), cdef_val_d)
return mlirlite.ConstFloat(pool, mlirlite.Double(pool), cdef_val_d)
# Integer CDefine: re-look-up the full 64-bit value (a double round-trip loses
# precision and would generate a wrong constant).
@@ -474,7 +473,7 @@ novirtual class ExprGenName(ExprGenBase.ExprGenBase):
# Is a list type: load out the list object pointer
builder: mlirlite.IRBuilder* = trans._cur_builder
return mlirlite.build_load(builder, pointee, alloca)
# _nonlocal_subscript_ptr - element pointer for a nonlocal array/pointer subscript: read the
# _nonlocal_subscript_ptr - element pointer for a nonlocal array/pointer subscript: read the
# shared heap address from env (_load_raw_var_ptr), bitcast, then GEP (the outer alloca is unusable).
def _nonlocal_subscript_ptr(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, trans: HT.Translator*, nm_id: str, idx_val: mlirlite.Value*) -> mlirlite.Value*:
"""Element pointer for a nonlocal array/pointer subscript (env heap addr -> bitcast -> GEP)."""
@@ -592,6 +591,24 @@ novirtual class ExprGenName(ExprGenBase.ExprGenBase):
elem_ptr_ty_v: mlirlite.LLVMType* = mlirlite.Ptr(pool, elem_ty_v)
casted_v: mlirlite.Value* = mlirlite.build_bitcast(builder, elem_addr_v, elem_ptr_ty_v)
if casted_v is not None: return mlirlite.build_load(builder, elem_ty_v, casted_v)
# 值类型类实例(`d: dict = dict(bd)`):槽里就是对象本身,`d[k]` 必须按
# 类的 `__getitem__` 调用编译,self 传槽地址(= &对象)。此前这里直接
# return None,调用方(AnnAssign)又对 None 静默不写,整条语句凭空消失
# ——现场:p1/p2/p3 全未赋值,随后解引用栈垃圾断言崩溃。
cls_nm_val: str = self.Gen.StructGen.get_class_name_by_type(pool, pointee)
if not (cls_nm_val is None):
gi_lookup_val: str = pool.alloc(128)
if gi_lookup_val is not None:
viperlib.snprintf(gi_lookup_val, 128, "%s.__getitem__", cls_nm_val)
if self.Gen.CallGenBase.find_func_in_module(mod, gi_lookup_val) is not None:
arg_vals_val: sizet* = pool.alloc(8)
if not (arg_vals_val is None):
arg_vals_val[0] = sizet(idx_val)
ret_val_sub: mlirlite.Value* = self.Gen.CallGenCall._call_method_on_ptr(pool, builder, mod, cls_nm_val, "__getitem__", alloca, arg_vals_val, 1, trans, sub)
if not (ret_val_sub is None): return ret_val_sub
# 非 list、无 __getitem__ 的结构体值不可下标:按对象大小 GEP 是非法 IR,
# 明确 fail-fast,绝不静默返回 None(调用方会把整条语句丢掉)。
self.Gen.TypeGenErr.fatal_error(node, f"cannot subscript the value-type object '{nm.id}' of type '{lsn_v}' (the class defines no __getitem__); use a pointer variable ('T*') or define __getitem__", "TypeError")
return None
case mlirlite.LLVMType.Ptr(inner_ty):
# Check whether inner_ty is list[T]; list subscripts go through __getitem__ (inline), not pointer traversal
@@ -876,6 +893,19 @@ novirtual class ExprGenName(ExprGenBase.ExprGenBase):
if ptr_val is None: return None
gep_r: mlirlite.Value* = mlirlite.build_gep(builder, inner_ty, ptr_val, idx_val)
return gep_r
case mlirlite.LLVMType.Struct(_fs_gsp, _fc_gsp, _lsn_gsp):
# 值类型类实例(`d: dict = dict(bd)`):槽里是对象本身,`d[k] = v`
# 只能走类的 `__setitem__`。定义了 __setitem__ 时返回 None,交由
# 赋值路径的 __setitem__ 重载分支(self 用槽地址);否则按对象数组
# GEP 会生成非法 IR,明确 fail-fast 而不是静默。
cls_nm_val_gsp: str = self.Gen.StructGen.get_class_name_by_type(pool, pointee)
if not (cls_nm_val_gsp is None):
si_lookup_val: str = pool.alloc(128)
if si_lookup_val is not None:
viperlib.snprintf(si_lookup_val, 128, "%s.__setitem__", cls_nm_val_gsp)
if self.Gen.CallGenBase.find_func_in_module(mod, si_lookup_val) is not None: return None
self.Gen.TypeGenErr.fatal_error(node, f"cannot index the value-type object '{nm.id}' of type '{_lsn_gsp}' (the class defines no __setitem__); use a pointer variable ('T*') or define __setitem__", "TypeError")
return None
case _:
# Plain scalar pointee (e.g. i64/i32/i8): GEP directly to the idx-th element; union type
# (IsPtrElement=1, pointee i8 but 8-byte pointer elements) uses an i8* stride instead.
+13 -3
View File
@@ -1,5 +1,4 @@
import lib.core.Gen.GenTypes as GenTypes
import t, c
from stdint import *
import ast
import mlirlite
@@ -208,6 +207,17 @@ novirtual class ExprGenOps(ExprGenBase.ExprGenBase):
mod_nm: ast.Name* = ast.Name*(at.value)
if mod_nm.id is not None and string.strcmp(mod_nm.id, "t") == 0 and at.attr is not None:
if self._type_name_unsigned(at.attr) != 0: return 1
return 0
# Plain function call: widen by the callee's **declared return type**. Without this,
# `uint64(f())` with `def f() -> uint32` emitted `sext i32 -> i64` (verified in the
# generated IR), so a u32 with bit 31 set grew 0xFFFFFFFF in the high half — a real
# value 0x88691F61 became 0xFFFFFFFF88691F61 (stackmap pointers were mangled this way).
if cl.func is not None and cl.func.kind() == ast.ASTKind.Name:
func_nm_u: ast.Name* = ast.Name*(cl.func)
if func_nm_u.id is not None:
fe_fn_u: GenTypes.FuncEntry* = self.Gen.CallGenLeafKit.find_func_entry_global(func_nm_u.id)
if fe_fn_u is not None and fe_fn_u.Spec is not None:
if TSpec.SpecIsUnsigned(fe_fn_u.Spec.Ret) != 0: return 1
return 0
if k == ast.ASTKind.Name:
nm: ast.Name* = ast.Name*(node)
@@ -774,7 +784,7 @@ novirtual class ExprGenOps(ExprGenBase.ExprGenBase):
case mlirlite.LLVMType.Func(_, _, _):
# 函数指针变量(嵌套函数名在符号表里是 Ptr(Func)):变量本身就是
# 那个函数地址,`&f` 应给出它的值 Ptr(Func)(一级),而不是变量
# 地址 Ptr(Ptr(Func))(二级)——否则传给 callback: t.CPtr 会因
# 地址 Ptr(Ptr(Func))(二级)——否则传给 callback: rawptr 会因
# 级数不符报 CastError(source level 2 -> target level 1)。
fp_ty_ao: mlirlite.LLVMType* = mlirlite.Ptr(pool, ao2)
return mlirlite.build_load(builder, fp_ty_ao, v_ao)
@@ -835,7 +845,7 @@ novirtual class ExprGenOps(ExprGenBase.ExprGenBase):
return tmp_slot_ao
# translate_boolop - BoolOp(op, values): short-circuit evaluation returning i1.
# and: a false short-circuits to merge (returns a's i1); or: a true short-circuits; the last value is evaluated then branches to merge; merge phis all i1 edges.
def translate_boolop(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, node: ast.AST*, funcs_ptr: t.CPtr = None, func_count: int = 0, trans: HT.Translator* = None) -> mlirlite.Value*:
def translate_boolop(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, node: ast.AST*, funcs_ptr: rawptr = None, func_count: int = 0, trans: HT.Translator* = None) -> mlirlite.Value*:
"""Translate boolean and/or with short-circuit evaluation, returning i1."""
bo: ast.BoolOp* = ast.BoolOp*(node)
if bo is None: return None
-2
View File
@@ -1,11 +1,9 @@
import t, c
from stdint import *
import ast
import mlirlite
import memhub
import string
import viperlib
import stdio
import stdlib
import lib.core.VLogger as VLogger
import lib.core.Gen.Translator as HT
+2 -3
View File
@@ -1,6 +1,5 @@
import lib.core.Gen.GenTypes as GenTypes
import lib.core.Gen.Func.FuncGenMixin as FuncGenMixin
import t, c
from stdint import *
import ast
import mlirlite
@@ -173,7 +172,7 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
# Extract default-argument info. Note: assign the attribute to an explicitly typed list[...]* local before
# calling __len__(), otherwise the list type is not recognized and the GEP base becomes i32 0 (llc error).
fd_args_node: ast.Arguments* = fd.args
fd_defaults: t.CPtr = None
fd_defaults: rawptr = None
fd_default_count: int = 0
fd_param_count: int = 0
if fd_args_node is not None:
@@ -510,7 +509,7 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
if func_section is not None: mlirlite.function_set_attr_group(func, func_section)
# Extract default-argument info
tfd_defaults: t.CPtr = None
tfd_defaults: rawptr = None
tfd_default_count: int = 0
tfd_param_count: int = 0
if args_node is not None:
+8 -9
View File
@@ -1,5 +1,4 @@
import lib.core.Gen.GenTypes as GenTypes
import t, c
from stdint import *
import ast
import mlirlite
@@ -83,7 +82,7 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
# types go into the param_tys buffer (CVoid skipped, aligning with alloca indices).
imported_modules: str = trans._imported_modules
from_imports: str = trans._from_imports
param_tys: t.CPtr = pool.alloc(8 * 64)
param_tys: rawptr = pool.alloc(8 * 64)
if param_tys is not None: string.memset(param_tys, 0, 8 * 64)
param_count: int = 0
if fd.args is not None:
@@ -192,8 +191,8 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
old_insw: int = trans._in_switch_case
# Separate nonlocal buffer: sharing [0] would overwrite the parent's set (ClosureTest2
# Test 7: level2's b clobbers level1's a). Restored after translation.
saved_nl_names: t.CPtr = trans._nonlocal_names
saved_nl_types: t.CPtr = trans._nonlocal_types
saved_nl_names: rawptr = trans._nonlocal_names
saved_nl_types: rawptr = trans._nonlocal_types
# 7. Switch to the nested function scope and clear scope names
trans._cur_func = func
@@ -778,7 +777,7 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
# ============================================================
# _collect_chain_nonlocals - recursively gather nonlocal names in the nested chain (deduped)
def _collect_chain_nonlocals(self, trans: HT.Translator*, node: ast.AST*, names: t.CPtr, count: int) -> int:
def _collect_chain_nonlocals(self, trans: HT.Translator*, node: ast.AST*, names: rawptr, count: int) -> int:
"""Recursively collect all nonlocal declaration names in a nested FunctionDef chain (deduped)"""
if node is None or names is None: return count
fd: ast.FunctionDef* = ast.FunctionDef*(node)
@@ -898,7 +897,7 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
param_names: str = pool.alloc(32 * 64) # 64 params x 32-byte names
if param_names is None: return None
string.memset(param_names, 0, 32 * 64)
param_tys: t.CPtr = pool.alloc(8 * 64) # LLVMType pointer slots
param_tys: rawptr = pool.alloc(8 * 64) # LLVMType pointer slots
if param_tys is None: return None
string.memset(param_tys, 0, 8 * 64)
param_count: int = 0
@@ -935,10 +934,10 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
cap_names: str = pool.alloc(64 * 64) # 64 captures x 64-byte names
if cap_names is None: return None
string.memset(cap_names, 0, 64 * 64)
cap_allocas: t.CPtr = pool.alloc(8 * 64) # Parent alloca pointer slots
cap_allocas: rawptr = pool.alloc(8 * 64) # Parent alloca pointer slots
if cap_allocas is None: return None
string.memset(cap_allocas, 0, 8 * 64)
cap_tys: t.CPtr = pool.alloc(8 * 64) # Capture variable type slots
cap_tys: rawptr = pool.alloc(8 * 64) # Capture variable type slots
if cap_tys is None: return None
string.memset(cap_tys, 0, 8 * 64)
cap_count: int = self._lambda_scan_body(trans, lam.body, param_names, param_count, cap_names, cap_allocas, cap_tys, 0)
@@ -1148,7 +1147,7 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
# ============================================================
# _lambda_scan_body - recursively collect outer Name refs into cap_names/cap_allocas/cap_tys
def _lambda_scan_body(self, trans: HT.Translator*, node: ast.AST*, param_names: str, param_count: int, cap_names: str, cap_allocas: t.CPtr, cap_tys: t.CPtr, cap_count: int) -> int:
def _lambda_scan_body(self, trans: HT.Translator*, node: ast.AST*, param_names: str, param_count: int, cap_names: str, cap_allocas: rawptr, cap_tys: rawptr, cap_count: int) -> int:
"""Recursively scan lambda body Name references and collect captured variables"""
if node is None or trans is None: return cap_count
k: int = node.kind()
+46 -47
View File
@@ -1,4 +1,3 @@
import t, c
from stdint import *
import ast
import mlirlite
@@ -687,52 +686,52 @@ def _WillFreeArgIndex(fn: ast.FunctionDef*, arg: ast.AST*) -> int:
return idx
def get_param_escapes_hard(name: str) -> int:
"""本文件函数"硬逃逸位"掩码(全局写 / 返回形参 / 传递闭包 / base 未定的字段写);0 = 无记录"""
if _g_gen is not None: return _g_gen.StructGen.get_param_escapes_hard(name)
return 0
def get_param_escapes_hard_method(pool: memhub.MemBuddy*, class_name: str, method_name: str) -> int:
"""方法版硬逃逸位:查 "Class.method" 并沿 MRO 回退"""
if _g_gen is not None: return _g_gen.StructGen.get_param_escapes_hard_method(pool, class_name, method_name)
return 0
# ---- 声明式指令(@life(escape/noescape))的最终裁决掩码 ----
# 契约表与推断表分开:调用点在所有推断/裁剪之后最后套用,任何推导都不能推翻。
def get_param_escape_force(name: str) -> int:
"""`@life(escape(...))` 强制逃逸位掩码;0 = 无该标注"""
if _g_gen is not None: return _g_gen.StructGen.get_param_escape_force(name)
return 0
def get_param_noescape_force(name: str) -> int:
"""`@life(noescape(...))` 强制不逃逸位掩码;0 = 无该标注"""
if _g_gen is not None: return _g_gen.StructGen.get_param_noescape_force(name)
return 0
def get_param_escape_force_method(pool: memhub.MemBuddy*, class_name: str, method_name: str) -> int:
"""方法版 `@life(escape(...))` 掩码:查 "Class.method" 并沿 MRO 回退"""
if _g_gen is not None: return _g_gen.StructGen.get_param_escape_force_method(pool, class_name, method_name)
return 0
def get_param_noescape_force_method(pool: memhub.MemBuddy*, class_name: str, method_name: str) -> int:
"""方法版 `@life(noescape(...))` 掩码:查 "Class.method" 并沿 MRO 回退"""
if _g_gen is not None: return _g_gen.StructGen.get_param_noescape_force_method(pool, class_name, method_name)
return 0
# escape_leak_line - **错误路径专用**:被调者第 want_pi 号形参的"首个逃逸写入点"行号。
# 只在 fail-fast 报错时调用(会重扫一次被调者函数体);跨模块拿不到 AST 时返回 0。
def escape_leak_line(name: str, want_pi: int) -> int:
"""形参 want_pi 的逃逸点行号;0 = 未知(含跨模块)"""
if _g_gen is not None: return _g_gen.StructGen.escape_leak_line(name, want_pi)
return 0
def get_param_escapes_hard(name: str) -> int:
"""本文件函数"硬逃逸位"掩码(全局写 / 返回形参 / 传递闭包 / base 未定的字段写);0 = 无记录"""
if _g_gen is not None: return _g_gen.StructGen.get_param_escapes_hard(name)
return 0
def get_param_escapes_hard_method(pool: memhub.MemBuddy*, class_name: str, method_name: str) -> int:
"""方法版硬逃逸位:查 "Class.method" 并沿 MRO 回退"""
if _g_gen is not None: return _g_gen.StructGen.get_param_escapes_hard_method(pool, class_name, method_name)
return 0
# ---- 声明式指令(@life(escape/noescape))的最终裁决掩码 ----
# 契约表与推断表分开:调用点在所有推断/裁剪之后最后套用,任何推导都不能推翻。
def get_param_escape_force(name: str) -> int:
"""`@life(escape(...))` 强制逃逸位掩码;0 = 无该标注"""
if _g_gen is not None: return _g_gen.StructGen.get_param_escape_force(name)
return 0
def get_param_noescape_force(name: str) -> int:
"""`@life(noescape(...))` 强制不逃逸位掩码;0 = 无该标注"""
if _g_gen is not None: return _g_gen.StructGen.get_param_noescape_force(name)
return 0
def get_param_escape_force_method(pool: memhub.MemBuddy*, class_name: str, method_name: str) -> int:
"""方法版 `@life(escape(...))` 掩码:查 "Class.method" 并沿 MRO 回退"""
if _g_gen is not None: return _g_gen.StructGen.get_param_escape_force_method(pool, class_name, method_name)
return 0
def get_param_noescape_force_method(pool: memhub.MemBuddy*, class_name: str, method_name: str) -> int:
"""方法版 `@life(noescape(...))` 掩码:查 "Class.method" 并沿 MRO 回退"""
if _g_gen is not None: return _g_gen.StructGen.get_param_noescape_force_method(pool, class_name, method_name)
return 0
# escape_leak_line - **错误路径专用**:被调者第 want_pi 号形参的"首个逃逸写入点"行号。
# 只在 fail-fast 报错时调用(会重扫一次被调者函数体);跨模块拿不到 AST 时返回 0。
def escape_leak_line(name: str, want_pi: int) -> int:
"""形参 want_pi 的逃逸点行号;0 = 未知(含跨模块)"""
if _g_gen is not None: return _g_gen.StructGen.escape_leak_line(name, want_pi)
return 0
def get_param_willfree(name: str) -> int:
"""本文件函数的 willFree 形参掩码(第 k 位=第 k 个形参在本调用后失效);无记录返回 0"""
if _g_gen is not None: return _g_gen.StructGen.get_param_willfree(name)
-1
View File
@@ -1,4 +1,3 @@
import t, c
from stdint import *
import ast
import mlirlite
-1
View File
@@ -1,6 +1,5 @@
import string
import lib.core.CompilerFactory as CompilerFactory
import t, c
from stdint import *
import stdio
import stdlib
+12 -13
View File
@@ -1,5 +1,4 @@
import lib.core.Gen.Generator as Generator
import t, c
from stdint import *
import ast
import memhub
@@ -213,7 +212,7 @@ novirtual class ImportsGenMixin:
# lookup; else fall back to scanning. orig None means a first occurrence with no alias.
if from_imports == self.CurFromImportsPtr and self.CurFromImportsOrigs is not None:
if self.CurFromImportsOrigs.__contains__(local_name) != 0:
v_o: t.CPtr = self.CurFromImportsOrigs.__getitem__(local_name)
v_o: rawptr = self.CurFromImportsOrigs.__getitem__(local_name)
if v_o is not None: return rawptr(v_o)
# Index miss or None value never returns None directly (the real string may still
# contain the name); fall through to scanning (correctness first).
@@ -275,10 +274,10 @@ novirtual class ImportsGenMixin:
name_is_star: int = 0
if string.strlen(name) == 1 and name[0] == '*': name_is_star = 1
if name_is_star == 0:
v_f: t.CPtr = self.CurFromImportsMods.__getitem__(name)
v_f: rawptr = self.CurFromImportsMods.__getitem__(name)
if v_f is not None: return rawptr(v_f)
if allow_star_fallback != 0:
sv_f: t.CPtr = self.CurFromImportsMods.__getitem__("*")
sv_f: rawptr = self.CurFromImportsMods.__getitem__("*")
if sv_f is not None: return rawptr(sv_f)
# Neither exact nor star hit in the index never returns None (an incomplete index
# misses imported names, causing _op_head NameError); fall through to scanning.
@@ -326,15 +325,15 @@ novirtual class ImportsGenMixin:
# for `from .X import Y`). Idempotent; memory is stdlib.malloc'd to persist across phases.
def register_reexport(self, mod_sha1: str, func_name: str, source_module: str) -> int:
"""Register a re-export mapping; returns 0 on success, -1 on failure."""
if mod_sha1 is None or func_name is None or source_module is None: return -1
# fail-fast: the table is a flat slab with fixed slots (17 bytes for the SHA1, 64 for the
# name/module). strncpy used to clamp silently, after which lookup_reexport's full-name
# strcmp never matched (or matched another module sharing the 63-byte prefix).
if string.strlen(mod_sha1) > 16:
self.Gen.TypeGenErr.fatal_error(None, "re-export module SHA1 is longer than its 16-byte table slot", "OverflowError")
if string.strlen(func_name) > 62:
self.Gen.TypeGenErr.fatal_error(None, f"re-exported name '{func_name}' exceeds the 62-byte table slot; shorten the identifier", "OverflowError")
if string.strlen(source_module) > 62:
if mod_sha1 is None or func_name is None or source_module is None: return -1
# fail-fast: the table is a flat slab with fixed slots (17 bytes for the SHA1, 64 for the
# name/module). strncpy used to clamp silently, after which lookup_reexport's full-name
# strcmp never matched (or matched another module sharing the 63-byte prefix).
if string.strlen(mod_sha1) > 16:
self.Gen.TypeGenErr.fatal_error(None, "re-export module SHA1 is longer than its 16-byte table slot", "OverflowError")
if string.strlen(func_name) > 62:
self.Gen.TypeGenErr.fatal_error(None, f"re-exported name '{func_name}' exceeds the 62-byte table slot; shorten the identifier", "OverflowError")
if string.strlen(source_module) > 62:
self.Gen.TypeGenErr.fatal_error(None, f"re-export source module '{source_module}' exceeds the 62-byte table slot; shorten the module name", "OverflowError")
# Lazy initialization
if self.ReexportSha1s is None:
-1
View File
@@ -1,4 +1,3 @@
import t, c
from stdint import *
import ast
import mlirlite
+33 -25
View File
@@ -1,6 +1,5 @@
import lib.core.Gen.GenTypes as GenTypes
import lib.core.Gen.Generator as Generator
import t, c
from stdint import *
import ast
import mlirlite
@@ -434,13 +433,13 @@ novirtual class StmtGenCifMixin:
if rt.value is not None:
# f-string escape (return f"..."): its buffer lives on this frame's stack and dies on
# return; copy it out explicitly (GenApi.escape_fstring_copy) before returning it.
GenApi.fail_if_fstring_escape(rt.value, "function return value")
# A2(§14.3 第 6 条,方案 A):定义端对 `return <本帧存储>` 的**声明感知** fail-fast。
# A2 移除了"一律拦"的旧判据(定义端分不清"交出本帧"与"借出调用方的存储"),
# 但也不能一律放:判据收敛到被调者自己的 `@life(ret(...))` —— `ret(frame)` 或
# `ret(borrows=<该形参>)` 放行;无声明 / 声明为非帧 → LifetimeError。
# 实现见 AssignGenMixin.check_return_frame_decl。
self.Gen.AssignGen.check_return_frame_decl(trans, rt.value)
GenApi.fail_if_fstring_escape(rt.value, "function return value")
# A2(§14.3 第 6 条,方案 A):定义端对 `return <本帧存储>` 的**声明感知** fail-fast。
# A2 移除了"一律拦"的旧判据(定义端分不清"交出本帧"与"借出调用方的存储"),
# 但也不能一律放:判据收敛到被调者自己的 `@life(ret(...))` —— `ret(frame)` 或
# `ret(borrows=<该形参>)` 放行;无声明 / 声明为非帧 → LifetimeError。
# 实现见 AssignGenMixin.check_return_frame_decl。
self.Gen.AssignGen.check_return_frame_decl(trans, rt.value)
# return a, b → pack an anonymous struct value (multi-return, return type tuple[T1,T2])
if rt.value.kind() == ast.ASTKind.Tuple:
rtp: ast.Tuple* = ast.Tuple*(rt.value)
@@ -511,6 +510,15 @@ novirtual class StmtGenCifMixin:
ret_val_ty: mlirlite.LLVMType* = None
cur_func_rv: mlirlite.Function* = trans._cur_func
if cur_func_rv is not None: ret_val_ty = mlirlite.function_get_ret_ty(cur_func_rv)
# `return <闭包变量>` 且返回注解是 t.Callable:闭包对象是 {fn_ptr, env_ptr},
# 不是裸函数指针。调用方把结果存进 t.Callable 变量后 `f()` 会按裸函数指针直调,
# 跳到堆地址崩溃(ClosureCallTest 现场)。这里明确 fail-fast,要求用 rawptr/t.CPtr。
if rt.value.kind() == ast.ASTKind.Name and trans is not None and trans.CurFuncRetAnnot is not None:
clo_nm_rv: ast.Name* = ast.Name*(rt.value)
if clo_nm_rv.id is not None and self.Gen.CallGenLeafKit.is_closure_name(clo_nm_rv.id) != 0:
clo_ret_rv: mlirlite.LLVMType* = self.Gen.TypeGenErr.try_extract_callable_ret(pool, trans.CurFuncRetAnnot, trans._imported_modules, trans._from_imports, trans)
if clo_ret_rv is not None:
self.Gen.TypeGenErr.fatal_error(rt, f"the return annotation is t.Callable (a bare function pointer) but the returned value is the closure variable '{clo_nm_rv.id}'. A closure is a (fn_ptr, env_ptr) object; storing it where a t.Callable is expected makes the caller jump into heap data. Annotate the return as rawptr / t.CPtr instead", "TypeError")
# [C1] return-annotation context: record this return's direct value node so a bare
# generic ctor (`return Stack()` in `-> Stack[int]`) specializes via CurFuncRetAnnot.
trans.CurRetNode = rt.value
@@ -770,23 +778,23 @@ novirtual class StmtGenCifMixin:
mlirlite.build_br(builder, merge_bb)
else_branches_merge = 1
# 生命期汇合:有 else → 与 then 末状态 Meet;无 else → 与 if 前状态 Meet(else 路径=跳过)
if has_else == 1: self.Gen.VarGen.life_join(life_snap1)
else: self.Gen.VarGen.life_join(life_snap0)
# willFree 失效槽表合流:只有"到达合流的路径"参与(否则会把"分支体内 free 后 return"
# 的失效态错误地带到合流之后——fileio.vp 的 `if …: free(buf); return None` 即此现场)。
# 两分支都到达 → 求交(curr=else 末态 ∩ then 末态);
# 仅 then 到达 → 取 then 末态;
# 仅 else/无 else → 取 else 末态(无 else 时即 if 前状态)。
if has_else == 1:
if then_branches_merge != 0 and else_branches_merge != 0:
self.Gen.VarGen.freed_join(freed_snap1)
elif then_branches_merge != 0:
self.Gen.VarGen.freed_restore(freed_snap1)
else:
self.Gen.VarGen.freed_restore(freed_snap0)
# 生命期汇合:有 else → 与 then 末状态 Meet;无 else → 与 if 前状态 Meet(else 路径=跳过)
if has_else == 1: self.Gen.VarGen.life_join(life_snap1)
else: self.Gen.VarGen.life_join(life_snap0)
# willFree 失效槽表合流:只有"到达合流的路径"参与(否则会把"分支体内 free 后 return"
# 的失效态错误地带到合流之后——fileio.vp 的 `if …: free(buf); return None` 即此现场)。
# 两分支都到达 → 求交(curr=else 末态 ∩ then 末态);
# 仅 then 到达 → 取 then 末态;
# 仅 else/无 else → 取 else 末态(无 else 时即 if 前状态)。
if has_else == 1:
if then_branches_merge != 0 and else_branches_merge != 0:
self.Gen.VarGen.freed_join(freed_snap1)
elif then_branches_merge != 0:
self.Gen.VarGen.freed_restore(freed_snap1)
else:
self.Gen.VarGen.freed_restore(freed_snap0)
# 7. merge reachability (used by tail-flow checks): without else cond_br always reaches
# merge; with else, any branch br reaching merge suffices; only all-return is unreachable.
trans._ctrl_merge_reachable = 1
@@ -1,5 +1,4 @@
import lib.core.Gen.GenTypes as GenTypes
import t, c
from stdint import *
import ast
import mlirlite
+43 -44
View File
@@ -1,7 +1,6 @@
import lib.core.Gen.GenTypes as GenTypes
import lib.core.Gen.Generator as Generator
import lib.core.Gen.Call.CallGenFd as CallGenFd
import t, c
from stdint import *
import ast
import mlirlite
@@ -177,49 +176,49 @@ novirtual class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
mlirlite.build_call(builder, dl_fn_name, dl_self, 2, dl_ret, 0)
return 0
# translate_assert - translate `assert cond[, msg]` into a single testcheck.check(...) call:
# a true condition is silent (ok_msg=None), a false one reports FAIL via the standard path
# (no printf/exit/exception; the checker prints nothing when the condition holds).
def translate_assert(self, trans: HT.Translator*, node: ast.AST*) -> int:
"""Translate an assert statement; returns 0"""
if trans is None or node is None: return 0
asn: ast.Assert* = ast.Assert*(node)
if asn is None or asn.test is None: return 0
pool: memhub.MemBuddy* = trans.Pool
builder: mlirlite.IRBuilder* = trans._cur_builder
mod: mlirlite.LLVMModule* = trans.Module
if pool is None or builder is None or mod is None: return 0
# Resolve the message argument: omitted -> "assertion failed". A non-string literal is
# rejected here (clear report); a non-string expression is caught by the call's int->ptr rule.
msg_node: ast.AST* = asn.msg
if msg_node is None:
msg_node = ast.Constant(pool, ast.CONST_STR, 0, 0.0, "assertion failed", node.lineno, node.col_offset)
if msg_node is None: return 0
elif msg_node.kind() == ast.ASTKind.Constant:
msg_c: ast.Constant* = ast.Constant*(msg_node)
if msg_c is None or msg_c.const_kind != ast.CONST_STR:
self.Gen.TypeGenErr.fatal_error(msg_node, "assert message must be a string (str)", "TypeError")
return 0
# Synthesize `testcheck.check(<test>, None, <msg>)` and reuse the normal call translation
# (cross-module name resolution + argument coercion stay in one place).
none_node: ast.AST* = ast.Constant(pool, ast.CONST_NONE, 0, 0.0, None, node.lineno, node.col_offset)
if none_node is None: return 0
args_list: list[ast.AST*]* = list[ast.AST*](pool, 8)
if args_list is None: return 0
args_list.append(asn.test)
args_list.append(none_node)
args_list.append(msg_node)
mod_name_node: ast.AST* = ast.Name(pool, "testcheck", ast.ASTCtx.Load)
if mod_name_node is None: return 0
check_attr: ast.AST* = ast.Attribute(pool, mod_name_node, "check", ast.ASTCtx.Load)
if check_attr is None: return 0
call_node: ast.AST* = ast.Call(pool, check_attr, args_list, None)
if call_node is None: return 0
self.Gen.ExprGen.translate_value(builder, pool, mod, call_node, trans._funcs, trans._func_count, trans)
return 0
# translate_assert - translate `assert cond[, msg]` into a single testcheck.check(...) call:
# a true condition is silent (ok_msg=None), a false one reports FAIL via the standard path
# (no printf/exit/exception; the checker prints nothing when the condition holds).
def translate_assert(self, trans: HT.Translator*, node: ast.AST*) -> int:
"""Translate an assert statement; returns 0"""
if trans is None or node is None: return 0
asn: ast.Assert* = ast.Assert*(node)
if asn is None or asn.test is None: return 0
pool: memhub.MemBuddy* = trans.Pool
builder: mlirlite.IRBuilder* = trans._cur_builder
mod: mlirlite.LLVMModule* = trans.Module
if pool is None or builder is None or mod is None: return 0
# Resolve the message argument: omitted -> "assertion failed". A non-string literal is
# rejected here (clear report); a non-string expression is caught by the call's int->ptr rule.
msg_node: ast.AST* = asn.msg
if msg_node is None:
msg_node = ast.Constant(pool, ast.CONST_STR, 0, 0.0, "assertion failed", node.lineno, node.col_offset)
if msg_node is None: return 0
elif msg_node.kind() == ast.ASTKind.Constant:
msg_c: ast.Constant* = ast.Constant*(msg_node)
if msg_c is None or msg_c.const_kind != ast.CONST_STR:
self.Gen.TypeGenErr.fatal_error(msg_node, "assert message must be a string (str)", "TypeError")
return 0
# Synthesize `testcheck.check(<test>, None, <msg>)` and reuse the normal call translation
# (cross-module name resolution + argument coercion stay in one place).
none_node: ast.AST* = ast.Constant(pool, ast.CONST_NONE, 0, 0.0, None, node.lineno, node.col_offset)
if none_node is None: return 0
args_list: list[ast.AST*]* = list[ast.AST*](pool, 8)
if args_list is None: return 0
args_list.append(asn.test)
args_list.append(none_node)
args_list.append(msg_node)
mod_name_node: ast.AST* = ast.Name(pool, "testcheck", ast.ASTCtx.Load)
if mod_name_node is None: return 0
check_attr: ast.AST* = ast.Attribute(pool, mod_name_node, "check", ast.ASTCtx.Load)
if check_attr is None: return 0
call_node: ast.AST* = ast.Call(pool, check_attr, args_list, None)
if call_node is None: return 0
self.Gen.ExprGen.translate_value(builder, pool, mod, call_node, trans._funcs, trans._func_count, trans)
return 0
# translate_try - try/except translation. Exception = (i32 code, i8* msg);
# except X statically expands X and all descendant exception codes.
def translate_try(self, trans: HT.Translator*, node: ast.AST*) -> int:
+5 -6
View File
@@ -1,6 +1,5 @@
import lib.core.Gen.GenApi as GenApi
import lib.core.Gen.GenTypes as GenTypes
import t, c
from stdint import *
import ast
import mlirlite
@@ -204,7 +203,7 @@ novirtual class Translator:
_lambda_count: int
# Set of nonlocal variables across the function chain (collected by pre-scan).
# The outermost function allocates their heaps and stores addresses in @nlheap.{sha1}.{name}.
_nl_chain_names: t.CPtr
_nl_chain_names: rawptr
_nl_chain_count: int
# Chain-nonlocal heap built flag (built once per function): reset to 0 on entering a function,
# set to 1 by _prepare_chain_nonlocals. Shared by nested definitions to avoid double malloc.
@@ -694,8 +693,8 @@ novirtual class Translator:
"""Detect list/dict container usage and inject container-class visibility."""
if tree is None or tree.children is None: return
# Use stack buffers to pass detection results (4-byte int pointers across helper calls)
need_list_box: t.CPtr = stdlib.malloc(4)
need_dict_box: t.CPtr = stdlib.malloc(4)
need_list_box: rawptr = stdlib.malloc(4)
need_dict_box: rawptr = stdlib.malloc(4)
if need_list_box is None or need_dict_box is None:
if need_list_box is not None: stdlib.free(need_list_box)
if need_dict_box is not None: stdlib.free(need_dict_box)
@@ -809,7 +808,7 @@ novirtual class Translator:
stdlib.free(nm_buf)
return 0
def _scan_container_usage(self, node: ast.AST*, need_list_box: t.CPtr, need_dict_box: t.CPtr):
def _scan_container_usage(self, node: ast.AST*, need_list_box: rawptr, need_dict_box: rawptr):
"""Recursively scan AST statement structure for list/dict container usage."""
if node is None: return
k: int = node.kind()
@@ -843,7 +842,7 @@ novirtual class Translator:
n_s: sizet = len(ch_s)
for ci_s in range(int(n_s)): self._scan_container_usage(ch_s.get(ci_s), need_list_box, need_dict_box)
def _check_container_type_node(self, node: ast.AST*, need_list_box: t.CPtr, need_dict_box: t.CPtr):
def _check_container_type_node(self, node: ast.AST*, need_list_box: rawptr, need_dict_box: rawptr):
"""Check whether a type-position node is list/dict, recursing through Name/Subscript/BinOp."""
if node is None: return
k: int = node.kind()
-1
View File
@@ -1,5 +1,4 @@
import lib.core.Gen.Generator as Generator
import t, c
from stdint import *
import ast
import mlirlite
+4 -8
View File
@@ -1,15 +1,11 @@
import lib.core.Gen.Type.TypeGenMixin as TypeGenMixin
import lib.core.Gen.GenTypes as GenTypes
import lib.core.Gen.Generator as Generator
import t, c
from stdint import *
import ast
import mlirlite
import memhub
import hashtable
import string
import viperlib
import stdio
import stdlib
import lib.core.VLogger as VLogger
import lib.Stub.StubMerger as StubMerger
@@ -49,9 +45,9 @@ novirtual class TypeGenBase:
# 判定"是不是浮点常量"一律用 `!= 0`(既有调用处口径不变),只有浮点常量物化处区分 64/32:
# `define PI: float = 3.14159...` 必须直接出 f32 常量,否则与 f32 字面量混算会多出 fpext。
CDefineDoubleFlags: list[int]*
CDefineStrFlags: list[int]* # 1=string value (e.g. ZLIB_VERSION: t.CDefine = "1.3.2")
# 值表达式的**位宽**(bit);0=未知(按值推断)。来源:右侧显式强转(如 WORD(0x0080))优先,
# 否则声明注解(define X: WORD = ... 的 T)。物化时据此定宽,避免小位宽常量被提升为 i32。
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
@@ -126,7 +122,7 @@ 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
+9 -15
View File
@@ -1,15 +1,9 @@
import t, c
from stdint import *
import ast
import mlirlite
import memhub
import string
import viperlib
import stdio
import stdlib
import lib.core.VLogger as VLogger
import lib.core.Gen.Translator as HT
import lib.Stub.StubMerger as StubMerger
import lib.core.CompilerFactory as CompilerFactory
@@ -21,14 +15,14 @@ define CDEFINE_IDX_CAP = 4096
# Index hash constants (32-bit FNV-1a)
define CDEF_IDX_FNV_OFFSET = 2166136261
define CDEF_IDX_FNV_PRIME = 16777619
define CDEF_IDX_FNV_MASK = 4294967295
# CDefine 浮点常量的宽度种类(CDefineDoubleFlags / XmodCdefDoubleFlags 条目编码)。
# 1=f64(double,默认),2=f32(float);0 表示非浮点。既有读取处一律按 `!= 0` 判定
# "是浮点常量",故 f32 天然兼容;只有浮点常量的物化处需要区分 64/32。
# 必须写明 `: int`:无注解 define 按**值的最小可存宽度**物化(1/2 → i8),
# 直接当实参调用 `list[int].append(x: int)` 会落成 `i8 != i32`(mlir-opt 校验失败)。
define CDEFINE_FLOAT_KIND_F64: int = 1
define CDEF_IDX_FNV_MASK = 4294967295
# CDefine 浮点常量的宽度种类(CDefineDoubleFlags / XmodCdefDoubleFlags 条目编码)。
# 1=f64(double,默认),2=f32(float);0 表示非浮点。既有读取处一律按 `!= 0` 判定
# "是浮点常量",故 f32 天然兼容;只有浮点常量的物化处需要区分 64/32。
# 必须写明 `: int`:无注解 define 按**值的最小可存宽度**物化(1/2 → i8),
# 直接当实参调用 `list[int].append(x: int)` 会落成 `i8 != i32`(mlir-opt 校验失败)。
define CDEFINE_FLOAT_KIND_F64: int = 1
define CDEFINE_FLOAT_KIND_F32: int = 2
@@ -454,7 +448,7 @@ 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).
-6
View File
@@ -1,15 +1,9 @@
import t, c
from stdint import *
import ast
import mlirlite
import memhub
import string
import viperlib
import stdio
import stdlib
import lib.core.VLogger as VLogger
import lib.core.Gen.Translator as HT
import lib.Stub.StubMerger as StubMerger
import lib.core.CompilerFactory as CompilerFactory
+8 -16
View File
@@ -1,6 +1,4 @@
import lib.core.Gen.GenTypes as GenTypes
import lib.core.Gen.Generator as Generator
import t, c
from stdint import *
import ast
import mlirlite
@@ -8,13 +6,7 @@ import memhub
import string
import viperlib
import stdio
import stdlib
import sys
import win32.fileio as fileio
import lib.core.VLogger as VLogger
import lib.core.Diag as Diag
import lib.Stub.StubMerger as StubMerger
import lib.core.CompilerFactory as CompilerFactory
import lib.core.Gen.Translator as HT
import lib.core.Gen.Type.TypeGenBase as TypeGenBase
@@ -85,14 +77,14 @@ novirtual class TypeGenErr(TypeGenBase.TypeGenBase):
Diag.finish()
return 0
# [TEMP-DIAG] 定位“报错位置”问题用(定位完删除):把决策输入打出来
# node=节点原始行号(0=合成) curarg=当前实参行号 trace=栈深 top=栈最内层行号
ti_top: int = -1
if self.Gen.TypeGenBase.TraceCount > 0:
tf_d: GenTypes.TraceFrame* = self.Gen.TypeGenBase._get_trace_frame(self.Gen.TypeGenBase.TraceCount - 1)
if tf_d is not None: ti_top = tf_d.lineno
stdio.printf("[TEMP-DIAG] node=%d curarg=%d trace=%d top=%d -> shown=%d\n",
raw_line, self.Gen.TypeGenBase.CurArgLine, self.Gen.TypeGenBase.TraceCount, ti_top, lineno)
# [TEMP-DIAG] 定位“报错位置”问题用(定位完删除):把决策输入打出来
# node=节点原始行号(0=合成) curarg=当前实参行号 trace=栈深 top=栈最内层行号
ti_top: int = -1
if self.Gen.TypeGenBase.TraceCount > 0:
tf_d: GenTypes.TraceFrame* = self.Gen.TypeGenBase._get_trace_frame(self.Gen.TypeGenBase.TraceCount - 1)
if tf_d is not None: ti_top = tf_d.lineno
stdio.printf("[TEMP-DIAG] node=%d curarg=%d trace=%d top=%d -> shown=%d\n",
raw_line, self.Gen.TypeGenBase.CurArgLine, self.Gen.TypeGenBase.TraceCount, ti_top, lineno)
Diag.begin(ent, err_type, msg)
# 调用链帧(按"自外向内"顺序)**由本类遍历**(只有它拿得到 TraceFrame),填入 entry。
for i in range(int(self.Gen.TypeGenBase.TraceCount)):
-13
View File
@@ -1,17 +1,4 @@
import t, c
from stdint import *
import ast
import mlirlite
import memhub
import string
import stdio
import sys
import stdlib
import viperlib
import win32.fileio as fileio
import lib.core.CompilerFactory as CompilerFactory
import lib.core.VLogger as VLogger
import lib.core.Gen.Translator as HT
import lib.core.Gen.Type.TypeGenBase as TypeGenBase
+3 -9
View File
@@ -1,9 +1,6 @@
import lib.core.Gen.GenTypes as GenTypes
import lib.core.Gen.GenApi as GenApi
import lib.core.Gen.Generator as Generator
import lib.core.Gen.Type.TypeGenCType as TypeGenCType
import lib.core.Gen.Type.TypeGenMixin as TypeGenMixin
import t, c
from stdint import *
import ast
import mlirlite
@@ -11,21 +8,18 @@ import memhub
import hashtable
import string
import viperlib
import stdio
import stdlib
import lib.core.VLogger as VLogger
import lib.core.Gen.Translator as HT
import lib.Stub.StubMerger as StubMerger
import lib.Stub.StubMerger.StubMergerStore as StubMergerStore
import lib.Stub.StubGen.VpiReader as VpiReader
import lib.core.CompilerFactory as CompilerFactory
import lib.Projectrans.Config as Config
import lib.core.Gen.Type.TypeGenBase as TypeGenBase
import lib.core.Gen.Type.TypeGenCDef as TypeGenCDef
import win32.fileio as fileio
# ============================================================
# TypeGenResolve - split out of TypeGenMixin (stage D): type mapping / annotation resolution.
novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
@@ -1198,10 +1192,10 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
key_buf[mod_len] = '|'
string.strncpy(key_buf + mod_len + 1, attr, attr_len)
key_buf[key_len] = '\0'
v: t.CPtr = self.LibTypedefTypeMap.__getitem__(key_buf)
v: rawptr = self.LibTypedefTypeMap.__getitem__(key_buf)
stdlib.free(key_buf)
if v is None: return None
return t.CPtr(v)
return rawptr(v)
def _lookup_library_typedef(self, pool: memhub.MemBuddy*, mod: str, attr: str) -> str:
"""Look up "attr: t.CTypedef = TYPE" in a library module source, returning TYPE or None."""
if mod is None or attr is None: return None
+2 -5
View File
@@ -1,4 +1,3 @@
import t, c
from stdint import *
import mlirlite
import memhub
@@ -115,8 +114,7 @@ def SpecSignFromTypeName(name: str) -> int:
# TypeSpec - type descriptor. PtrLevel alone is ambiguous for Ptr(i8), so element
# semantics use the (ElemBits, IsElemPtr, DerefBase) triple replacing IsPtrElement.
@t.NoVTable
class TypeSpec:
novirtual class TypeSpec:
"""Unified type descriptor"""
Kind: int # one of TS_*
Bits: int # bit width; 0 for non-int/float
@@ -140,8 +138,7 @@ class TypeSpec:
# TypeSpecArena - interning table (object-owned, not global): Keys/Specs/Count/Cap.
@t.NoVTable
class TypeSpecArena:
novirtual class TypeSpecArena:
"""TypeSpec interning table; dedups by canonical text key (one instance per type)"""
Keys: bytes
Specs: sizet*
+3 -4
View File
@@ -3,7 +3,6 @@ import lib.core.Gen.Generator as Generator
import lib.core.Gen.Type.TypeSpec as TSpec
from MatchGenMixin import SCOPE_MATCH
import lib.core.Gen.Translator as HT
import t, c
from stdint import *
import ast
import mlirlite
@@ -934,7 +933,7 @@ novirtual class VarGenMixin:
# ---- repl_try_session_func_call - indirect call to a session function (M3 core) ----
# On a hit, load the address from the ctx slot, prepend ctx, coerce args to the table signature, and call_indirect (hit=1).
def repl_try_session_func_call(self, trans: HT.Translator*, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, func_name: str, cargs: list[ast.AST*]*, can: int, kw_count: int, node: ast.AST*, hit: t.CPtr) -> mlirlite.Value*:
def repl_try_session_func_call(self, trans: HT.Translator*, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, func_name: str, cargs: list[ast.AST*]*, can: int, kw_count: int, node: ast.AST*, hit: rawptr) -> mlirlite.Value*:
"""Session function call: return the call result on hit (void returns None but hit is set)"""
if trans is None or trans.ReplCtx is None or pool is None or builder is None: return None
if func_name is None or hit is None: return None
@@ -965,7 +964,7 @@ novirtual class VarGenMixin:
self.Gen.TypeGenErr.fatal_error(node, f"REPL: failed to read the address of session function '{func_name}'", "InternalCompilerError")
return None
# (2) Resolve param types before building the signature (token -> LLVM type; unsupported -> error)
ptypes: t.CPtr = pool.alloc(8 * GenTypes.REPL_FUNC_PARAM_MAX)
ptypes: rawptr = pool.alloc(8 * GenTypes.REPL_FUNC_PARAM_MAX)
if ptypes is None: return None
string.memset(ptypes, 0, 8 * GenTypes.REPL_FUNC_PARAM_MAX)
ai_chk: int
@@ -1347,7 +1346,7 @@ novirtual class VarGenMixin:
i8_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, mlirlite.Int8(pool))
pv: mlirlite.Value* = val
if val.Ty is not None and self.Gen.ExprGenBase.is_ptr_type(val.Ty) != 0 and self.Gen.AssignGen._stg_ty_equal(pool, val.Ty, i8_ptr_ty) == 0:
# Skip when already an i8 pointer (str/bytes/t.CPtr) - LLVM rejects same-type bitcast
# Skip when already an i8 pointer (str/bytes/rawptr) - LLVM rejects same-type bitcast
pv = mlirlite.build_bitcast(builder, val, i8_ptr_ty)
if pv is None: return 1
# Pointer -> i64 (ptrtoint): the host reads it as an integer then inttoptrs it back to an address
+4 -7
View File
@@ -1,8 +1,6 @@
import t, c
from stdint import *
import memhub
import string
import stdio
import stdlib
import win32.win32file as win32file
import win32.win32base as win32base
@@ -11,7 +9,6 @@ import hashlib
import viperlib
import lib.Projectrans.Utils as Utils
import lib.core.VLogger as VLogger
import lib.Projectrans.Config as Config
# IncludesScanner - recursive scan of the includes directory (Win32 FindFirstFileA/
@@ -311,8 +308,8 @@ def _PathExistsA(path: str) -> int:
# _TryResolveModuleFile - try one candidate path and write outputs (1=hit, 0=miss);
# out_abs_path/out_rel_path/out_inc_dir are t.CPtr output slots.
def _TryResolveModuleFile(inc_dir: str, rel_seg: bytes, rel_len: sizet, suffix: str, out_abs_path: t.CPtr, out_rel_path: t.CPtr, out_inc_dir: t.CPtr) -> int:
# out_abs_path/out_rel_path/out_inc_dir are rawptr output slots.
def _TryResolveModuleFile(inc_dir: str, rel_seg: bytes, rel_len: sizet, suffix: str, out_abs_path: rawptr, out_rel_path: rawptr, out_inc_dir: rawptr) -> int:
if inc_dir is None or rel_seg is None or suffix is None: return 0
inc_len: sizet = string.strlen(inc_dir)
suf_len: sizet = string.strlen(suffix)
@@ -346,7 +343,7 @@ def _TryResolveModuleFile(inc_dir: str, rel_seg: bytes, rel_len: sizet, suffix:
return 0
string.strcpy(inc_buf, inc_dir)
# Write output slots (stdlib deep copies; caller frees). Slots must be cast to
# (sizet*): t.CPtr* folds to t.CPtr and [0] becomes a 1-byte access.
# (sizet*): rawptr* folds to rawptr and [0] becomes a 1-byte access.
if out_abs_path is not None:
abs_slot: sizet* = (sizet*)(out_abs_path)
abs_slot[0] = sizet(cand)
@@ -363,7 +360,7 @@ def _TryResolveModuleFile(inc_dir: str, rel_seg: bytes, rel_len: sizet, suffix:
# ResolveModuleToPath - module name -> file path, resolved on demand (no full scan).
# Tries each includes dir, preferring file modules over packages. Returns 0 found, 1 not.
def ResolveModuleToPath(includes_dirs: str, mod_name: str, out_abs_path: t.CPtr, out_rel_path: t.CPtr, out_inc_dir: t.CPtr) -> int:
def ResolveModuleToPath(includes_dirs: str, mod_name: str, out_abs_path: rawptr, out_rel_path: rawptr, out_inc_dir: rawptr) -> int:
if includes_dirs is None or mod_name is None: return 1
mn_len: sizet = string.strlen(mod_name)
if mn_len == 0: return 1
+2 -4
View File
@@ -1,7 +1,5 @@
import t, c
from stdint import *
import stdio
import stdlib
import memhub
import sys
import win32.win32console as win32cmd
@@ -33,7 +31,7 @@ def repl_abort() -> int:
return 0
def set_repl_jmpbuf(buf: t.CPtr) -> int:
def set_repl_jmpbuf(buf: rawptr) -> int:
"""Register the REPL jmp_buf; idempotent at each fragment entry."""
global _repl_jmpbuf
_repl_jmpbuf = buf
@@ -196,7 +194,7 @@ log: Logger*
def init_logger():
"""Initialize the global logger instance."""
global logger, log
raw: t.CPtr = _mbuddy.alloc(Logger.__sizeof__())
raw: rawptr = _mbuddy.alloc(Logger.__sizeof__())
# if raw is None: return None (memory management still unclear)
logger = raw
logger.__before_init__()
+3 -6
View File
@@ -1,12 +1,9 @@
"""Compiler-side implementation of the `viperbuild` built-in macro library (--src Pass 0)."""
import t, c
from stdint import *
from win32.win32base import *
import memhub
import string
import stdio
import stdlib
import viperlib
import ast
import win32.win32file as win32file
import lib.Projectrans.Utils as Utils
@@ -796,9 +793,9 @@ def _ResolveModuleToFile(module_name: str) -> str:
"""Resolve a module name to a local project file path; None if not found."""
if module_name is None or _search_dirs is None: return None
if string.strlen(module_name) == 0: return None
out_abs: t.CPtr = stdlib.malloc(8)
out_rel: t.CPtr = stdlib.malloc(8)
out_inc: t.CPtr = stdlib.malloc(8)
out_abs: rawptr = stdlib.malloc(8)
out_rel: rawptr = stdlib.malloc(8)
out_inc: rawptr = stdlib.malloc(8)
if out_abs is None or out_rel is None or out_inc is None:
if out_abs is not None: stdlib.free(out_abs)
if out_rel is not None: stdlib.free(out_rel)