Compare commits
94
Commits
3384941cd1
...
3242560d02
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3242560d02 | ||
|
|
6e17716a69 | ||
|
|
eff88dd591 | ||
|
|
bf0bbcc89b | ||
|
|
d46fdf152a | ||
|
|
3e1e1fc1b3 | ||
|
|
15952d3852 | ||
|
|
201c881b43 | ||
|
|
19feb40e8c | ||
|
|
2f4031b3d6 | ||
|
|
d8648176e9 | ||
|
|
e796314a24 | ||
|
|
7e9ed22dff | ||
|
|
c57c931099 | ||
|
|
832b0a5706 | ||
|
|
6196bdfe56 | ||
|
|
d06e665ebd | ||
|
|
5460d78ff1 | ||
|
|
9d0a43d5e6 | ||
|
|
15a210bede | ||
|
|
59c032f135 | ||
|
|
23236c88ea | ||
|
|
d1dd6de070 | ||
|
|
cbadc356f9 | ||
|
|
5dc483e256 | ||
|
|
6b86f846ca | ||
|
|
217f5f021d | ||
|
|
5ea0ce094e | ||
|
|
3e52baa209 | ||
|
|
508428bbc9 | ||
|
|
3a687db0bb | ||
|
|
38a277a914 | ||
|
|
dc4468593c | ||
|
|
a143c2de0c | ||
|
|
7da9d7cb0d | ||
|
|
d09b79995d | ||
|
|
61f23d99a1 | ||
|
|
813b59e25f | ||
|
|
ee3337f3c4 | ||
|
|
a060e1d76e | ||
|
|
3c6041c97c | ||
|
|
ca81c1185c | ||
|
|
40ea5d6513 | ||
|
|
4f45b391ea | ||
|
|
d6b87da063 | ||
|
|
0f2675e794 | ||
|
|
0678cc8c48 | ||
|
|
70c271d2a4 | ||
|
|
9d02a3ac0b | ||
|
|
6b180c8f59 | ||
|
|
a23c1bda3e | ||
|
|
dc24970af2 | ||
|
|
ea54d5685d | ||
|
|
99a021ad67 | ||
|
|
12da1cbfc1 | ||
|
|
bc1eeb6e38 | ||
|
|
c1e5a11962 | ||
|
|
d2138678ef | ||
|
|
1c3cdf3493 | ||
|
|
377c0db5d3 | ||
|
|
6cb1f525d9 | ||
|
|
a01e9280cc | ||
|
|
14d8c05a2a | ||
|
|
97c0a1c6fd | ||
|
|
d7c5af9b80 | ||
|
|
cb2dc4cb3a | ||
|
|
444e87dd7d | ||
|
|
d3997e0746 | ||
|
|
32599a08c1 | ||
|
|
37c4b0da3b | ||
|
|
5d15d01e21 | ||
|
|
3ca2165fd9 | ||
|
|
b859f8a136 | ||
|
|
22475e0fda | ||
|
|
c0ff5c9d96 | ||
|
|
c9562a6e04 | ||
|
|
d206abf3e1 | ||
|
|
ea2af739dd | ||
|
|
00bf7ebf38 | ||
|
|
e3743b185f | ||
|
|
54b239747e | ||
|
|
ae12ae8581 | ||
|
|
001f8afc37 | ||
|
|
e7b05c6a3d | ||
|
|
3d638927d5 | ||
|
|
4a66aa7fd2 | ||
|
|
9d02cc4684 | ||
|
|
65c72fd55c | ||
|
|
8453698f66 | ||
|
|
74fafb3b56 | ||
|
|
f044d94e90 | ||
|
|
87914b0a51 | ||
|
|
d7a5f7e68b | ||
|
|
7c11c589f7 |
No files matched your search
+138
-70
@@ -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:
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import t, c
|
||||
from stdint import *
|
||||
import string
|
||||
import stdlib
|
||||
import memhub
|
||||
import viperlib
|
||||
import w32.fileio as fileio
|
||||
import w32.win32base
|
||||
import win32.fileio as fileio
|
||||
import win32.win32base
|
||||
import ast
|
||||
import mlirlite
|
||||
import lib.core.VLogger as VLogger
|
||||
@@ -133,7 +132,7 @@ def _BreakContainerCyclesInDeps(temp_dir: str) -> int:
|
||||
if StubMerger._includes_deps_sha1 is None or StubMerger._includes_deps_str is None: return 0
|
||||
cnt: sizet = StubMerger._includes_deps_count
|
||||
ci: sizet
|
||||
for ci in range(cnt):
|
||||
for ci in range(int(cnt)):
|
||||
sha1_m: str = StubMerger._includes_deps_sha1 + sizet(ci) * 17
|
||||
deps_m: str = StubMerger._includes_deps_str + sizet(ci) * StubMerger.MAX_DEPS_STR_LEN
|
||||
if sha1_m is None or deps_m is None: continue
|
||||
@@ -195,7 +194,7 @@ def _BuildReachableResult(mb: memhub.MemBuddy*, includes_dir: str) -> IncludesSc
|
||||
if result is None: return None
|
||||
entry_size: sizet = IncludesScanner.FileEntry.__sizeof__()
|
||||
i: sizet
|
||||
for i in range(store_count):
|
||||
for i in range(int(store_count)):
|
||||
sidx: sizet = sizet(i) * 17
|
||||
if StubMerger._sha1_store_arr[sidx] == '\0': continue
|
||||
# Only includes entries (non-empty incdir); App entries do not enter result
|
||||
@@ -274,7 +273,7 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: VLogg
|
||||
|
||||
# On-demand dependency scan (App-first): seed the queue from App imports, then BFS-resolve
|
||||
# reachable includes only (read + SHA1 + import parse); unreferenced ones are never read.
|
||||
t_scan0: uint64 = w32.win32base.GetTickCount64()
|
||||
t_scan0: uint64 = win32.win32base.GetTickCount64()
|
||||
reachable_set: str = None
|
||||
reachable_count: int = 0
|
||||
use_reachable: int = 0
|
||||
@@ -298,7 +297,7 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: VLogg
|
||||
else:
|
||||
VLogger.warning("SourceDir unavailable; cannot build the dependency graph; skipping Phase1", "Phase1")
|
||||
return 0
|
||||
t_scan1: uint64 = w32.win32base.GetTickCount64()
|
||||
t_scan1: uint64 = win32.win32base.GetTickCount64()
|
||||
VLogger.info(f"dependency-graph scan total time: {t_scan1 - t_scan0} ms (reachable {reachable_count} includes)", "Phase1")
|
||||
|
||||
# Build the reachable FileEntry list from the Sha1Store; 1a/1b-pre/1b iterate result
|
||||
@@ -350,26 +349,28 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: VLogg
|
||||
p1_chkbase_addr: uint64 = uint64(mb.chk_base)
|
||||
VLogger.info(f"[EVIDENCE] mb={p1_mb_addr} base={p1_base_addr} usable={p1_usable_addr} " f"usable_size={mb.usable_size} max_order={mb.max_order} self_check={p1_sc} " f"detection={mb.detection}", "Phase1")
|
||||
VLogger.info(f"[EVIDENCE] chk_base={p1_chkbase_addr} chk_depth={mb.chk_depth} " f"chk_cap={mb.chk_cap} free_lists[clamp={p1_mo}]={p1_fl_head}", "Phase1")
|
||||
p1_sz_cn: sizet = sizet(Phase1.MAX_CLASSES * Phase1.CLASS_NAME_LEN)
|
||||
p1_sz_cn: sizet = sizet(Phase1.MAX_CLASSES) * Phase1.CLASS_NAME_LEN
|
||||
p1_sz_ds: sizet = sizet(Phase1.MAX_CLASSES * Phase1.SHA1_LEN)
|
||||
p1_sz_tp: sizet = sizet(4 * Phase1.MAX_FILE_DEPS)
|
||||
p1_sz_bx: sizet = sizet(4)
|
||||
p1_o_cn: int = mb._order_for_size(p1_sz_cn + 16)
|
||||
p1_o_ds: int = mb._order_for_size(p1_sz_ds + 16)
|
||||
p1_o_tp: int = mb._order_for_size(p1_sz_tp + 16)
|
||||
p1_o_bx: int = mb._order_for_size(p1_sz_bx + 16)
|
||||
p1_o_cn: int = int(mb._order_for_size(p1_sz_cn + 16))
|
||||
p1_o_ds: int = int(mb._order_for_size(p1_sz_ds + 16))
|
||||
p1_o_tp: int = int(mb._order_for_size(p1_sz_tp + 16))
|
||||
p1_o_bx: int = int(mb._order_for_size(p1_sz_bx + 16))
|
||||
p1_fl_cn: uint64 = uint64(mb.free_lists[p1_o_cn])
|
||||
p1_fl_ds: uint64 = uint64(mb.free_lists[p1_o_ds])
|
||||
VLogger.info(f"[EVIDENCE] order(class_names {p1_sz_cn}B+16)={p1_o_cn} fl={p1_fl_cn} " f"order(def_sha1s {p1_sz_ds}B+16)={p1_o_ds} fl={p1_fl_ds} " f"order(topo {p1_sz_tp}B+16)={p1_o_tp} order(box {p1_sz_bx}B+16)={p1_o_bx} " f"(fl==0 only means that order is empty and can still be split by a higher order; if alloc fails while dump_stats shows free>0 that is the definitive proof of metadata corruption; " f"any order>max_order={mb.max_order} means an abnormal size)", "Phase1")
|
||||
|
||||
# Pre-scan class inheritance and build the topo order (flat byte arrays avoid str-pointer NULL crashes)
|
||||
class_names_buf: bytes = mb.alloc(Phase1.MAX_CLASSES * Phase1.CLASS_NAME_LEN)
|
||||
parent_names_buf: bytes = mb.alloc(Phase1.MAX_CLASSES * Phase1.CLASS_NAME_LEN)
|
||||
# 取消整数提升后,模块属性 CDefine 按最小位宽物化为 i16/i8;512*64=32768 恰好越出 i16 有符号上限而回绕成负值,
|
||||
# 首乘数显式 sizet() 加宽到 64 位。下面 def_sha1s_buf 的 512*17=8704 未溢出,无需改。
|
||||
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
|
||||
@@ -380,8 +381,8 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: VLogg
|
||||
if parent_names_buf is None: VLogger.error("class-inheritance pre-scan flat buffer allocation failed: parent_names_buf is NULL; aborting compilation", "Phase1")
|
||||
if def_sha1s_buf is None: VLogger.error("class-inheritance pre-scan flat buffer allocation failed: def_sha1s_buf is NULL; aborting compilation", "Phase1")
|
||||
|
||||
string.memset(class_names_buf, 0, Phase1.MAX_CLASSES * Phase1.CLASS_NAME_LEN)
|
||||
string.memset(parent_names_buf, 0, Phase1.MAX_CLASSES * Phase1.CLASS_NAME_LEN)
|
||||
string.memset(class_names_buf, 0, sizet(Phase1.MAX_CLASSES) * Phase1.CLASS_NAME_LEN)
|
||||
string.memset(parent_names_buf, 0, sizet(Phase1.MAX_CLASSES) * Phase1.CLASS_NAME_LEN)
|
||||
string.memset(def_sha1s_buf, 0, Phase1.MAX_CLASSES * Phase1.SHA1_LEN)
|
||||
if topo_order is not None: string.memset(topo_order, 0, 4 * Phase1.MAX_FILE_DEPS)
|
||||
if topo_count_box is not None: string.memset(topo_count_box, 0, 4)
|
||||
@@ -394,7 +395,7 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: VLogg
|
||||
td_len_ts_call: sizet = string.strlen(temp_dir)
|
||||
ret_topo: int = Phase1Topo._TopoSortFiles(mb, result, reachable_set, reachable_count, use_reachable, sha1_set, set_count, class_names_buf, parent_names_buf, def_sha1s_buf, class_count_scanned, topo_order, topo_count_box, temp_dir, td_len_ts_call)
|
||||
if ret_topo == 0:
|
||||
tc_ptr: t.CPtr = (int*)(topo_count_box)
|
||||
tc_ptr: int* = (int*)(topo_count_box)
|
||||
if tc_ptr is not None: topo_count = tc_ptr[0]
|
||||
VLogger.info(f"topological sort: {class_count_scanned} classes, {topo_count} files processed in dependency order", "Phase1")
|
||||
else:
|
||||
@@ -415,17 +416,17 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: VLogg
|
||||
GenApi.set_stdlib_alloc(1)
|
||||
|
||||
# Iterate files: topo order preferred, falling back to alphabetical
|
||||
iter_count: int = topo_count if topo_count > 0 else result.Count
|
||||
iter_count: int = int(topo_count if topo_count > 0 else result.Count)
|
||||
# 1a total-time start (struct/enum/union registration)
|
||||
t_a0: uint64 = w32.win32base.GetTickCount64()
|
||||
for i in range(iter_count):
|
||||
t_a0: uint64 = win32.win32base.GetTickCount64()
|
||||
for i in range(int(iter_count)):
|
||||
# Per-file mark: free this file's AST after translating (registered data is in perm_pool)
|
||||
p1a_mark: sizet = mb.mark()
|
||||
# Get the file index: topo order or alphabetical
|
||||
file_idx: int = i
|
||||
file_idx: int = int(i)
|
||||
if topo_count > 0:
|
||||
od_addr: uint64 = uint64(topo_order) + i * 4
|
||||
od_ptr: t.CPtr = (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
|
||||
@@ -451,7 +452,7 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: VLogg
|
||||
continue
|
||||
|
||||
# Per-file timer start (read + parse, to locate 1a stalls)
|
||||
t_fa0: uint64 = w32.win32base.GetTickCount64()
|
||||
t_fa0: uint64 = win32.win32base.GetTickCount64()
|
||||
|
||||
# Read file contents
|
||||
file_path_r: str = entry_r.Path
|
||||
@@ -496,7 +497,7 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: VLogg
|
||||
continue
|
||||
|
||||
# Timer 1: read + parse done
|
||||
t_fa1: uint64 = w32.win32base.GetTickCount64()
|
||||
t_fa1: uint64 = win32.win32base.GetTickCount64()
|
||||
|
||||
# Create Translator with _declare_only=1 (register struct/enum/union)
|
||||
tr_r: HandlesTranslator.Translator* = HandlesTranslator.Translator()
|
||||
@@ -515,7 +516,7 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: VLogg
|
||||
GenApi.reset_visible_structs(mb, 0)
|
||||
ret_r: int = tr_r.translate(tree_r)
|
||||
# Timer 2: translate(declare=1) returned
|
||||
t_fa2: uint64 = w32.win32base.GetTickCount64()
|
||||
t_fa2: uint64 = win32.win32base.GetTickCount64()
|
||||
if ret_r != 0: p1a_fl += 1
|
||||
else: p1a_reg += 1
|
||||
|
||||
@@ -540,7 +541,7 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: VLogg
|
||||
mb.reset_to(p1a_mark)
|
||||
|
||||
# 1a total time (struct/enum/union registration, incl. per-file read+parse+declaration)
|
||||
t_a1: uint64 = w32.win32base.GetTickCount64()
|
||||
t_a1: uint64 = win32.win32base.GetTickCount64()
|
||||
VLogger.info(f"1a total time: {t_a1-t_a0} ms (registering struct/enum, reg={p1a_reg} skp={p1a_skp})", "Phase1")
|
||||
|
||||
if p1a_fl > 0:
|
||||
@@ -567,8 +568,8 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: VLogg
|
||||
p1bpre_failed: int = 0
|
||||
dir_len_pv: sizet = string.strlen(temp_dir)
|
||||
# 1b-pre total-time start (vpi pre-generation)
|
||||
t_pv0: uint64 = w32.win32base.GetTickCount64()
|
||||
for i_pv in range(result.Count):
|
||||
t_pv0: uint64 = win32.win32base.GetTickCount64()
|
||||
for i_pv in range(int(result.Count)):
|
||||
entry_addr_pv: uint64 = uint64(result.Entries) + i_pv * entry_size
|
||||
entry_pv: IncludesScanner.FileEntry* = IncludesScanner.FileEntry*(entry_addr_pv)
|
||||
if entry_pv is None or entry_pv.Sha1 is None:
|
||||
@@ -663,7 +664,7 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: VLogg
|
||||
mb.reset_to(mark1)
|
||||
|
||||
# 1b-pre total time (vpi pre-generation)
|
||||
t_pv1: uint64 = w32.win32base.GetTickCount64()
|
||||
t_pv1: uint64 = win32.win32base.GetTickCount64()
|
||||
VLogger.info(f"1b-pre total time: {t_pv1-t_pv0} ms (vpi pre-generation)", "Phase1")
|
||||
|
||||
if p1bpre_failed > 0:
|
||||
@@ -676,16 +677,16 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: VLogg
|
||||
skipped: int = 0
|
||||
failed: int = 0
|
||||
# 1b total-time start (full translation)
|
||||
t_b0: uint64 = w32.win32base.GetTickCount64()
|
||||
t_b0: uint64 = win32.win32base.GetTickCount64()
|
||||
|
||||
loop_n_b: int = result.Count
|
||||
loop_n_b: int = int(result.Count)
|
||||
if topo_count > 0: loop_n_b = topo_count
|
||||
for i in range(loop_n_b):
|
||||
for i in range(int(loop_n_b)):
|
||||
# Get the entry (topo: file_idx from topo_order, else the scan index)
|
||||
file_idx_b: int = i
|
||||
file_idx_b: int = int(i)
|
||||
if topo_count > 0:
|
||||
od_addr_b: uint64 = uint64(topo_order) + sizet(i) * 4
|
||||
od_ptr_b: t.CPtr = (int*)(od_addr_b)
|
||||
od_ptr_b: int* = (int*)(od_addr_b)
|
||||
if od_ptr_b is not None: file_idx_b = od_ptr_b[0]
|
||||
entry_addr: uint64 = uint64(result.Entries) + sizet(file_idx_b) * entry_size
|
||||
entry: IncludesScanner.FileEntry* = IncludesScanner.FileEntry*(entry_addr)
|
||||
@@ -793,7 +794,7 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: VLogg
|
||||
stdlib.free(stub_path)
|
||||
|
||||
# Per-file timer start: read (parse/translate/dump segments, matching Phase2 instrumentation)
|
||||
t_f0: uint64 = w32.win32base.GetTickCount64()
|
||||
t_f0: uint64 = win32.win32base.GetTickCount64()
|
||||
|
||||
# Read file contents
|
||||
file_path: str = entry.Path
|
||||
@@ -847,7 +848,7 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: VLogg
|
||||
continue
|
||||
|
||||
# Timer 1: read + lexer/tokenize/parse done
|
||||
t_f1: uint64 = w32.win32base.GetTickCount64()
|
||||
t_f1: uint64 = win32.win32base.GetTickCount64()
|
||||
|
||||
# Register CTypedef by scanning source text: AST string fields (attr/id) point into src_buf
|
||||
# and dangle across phases (reading attr faults), so match "NAME: t.CTypedef = TYPE" lines.
|
||||
@@ -901,7 +902,7 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: VLogg
|
||||
|
||||
ret: int = tr.translate(tree)
|
||||
# Timer 2: translate returned
|
||||
t_f2: uint64 = w32.win32base.GetTickCount64()
|
||||
t_f2: uint64 = win32.win32base.GetTickCount64()
|
||||
# Diagnostic: translate returned normally (if it hangs inside, this line is not printed,
|
||||
# distinguishing an internal hang from a later dump hang).
|
||||
if ret == 0: VLogger.info(f"translation complete (translate returned): {file_path}", "Phase1")
|
||||
@@ -996,7 +997,7 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: VLogg
|
||||
stdlib.free(vpcall_text_p1)
|
||||
|
||||
# Timer 3: dump (stub+text) done; print the per-file three-segment timing
|
||||
t_f3: uint64 = w32.win32base.GetTickCount64()
|
||||
t_f3: uint64 = win32.win32base.GetTickCount64()
|
||||
VLogger.info(f"[{i+1}/{loop_n_b}] translating: {file_path} (parse={t_f1-t_f0} ms, translate={t_f2-t_f1} ms, dump={t_f3-t_f2} ms, total={t_f3-t_f0} ms)", "Phase1")
|
||||
|
||||
# save dependencies(_imported_modules) for Phase B
|
||||
@@ -1023,14 +1024,14 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: VLogg
|
||||
# tables moved to stdlib). Memory sentinels locate overruns deterministically.
|
||||
chk_rc: int = mb.check_alive_blocks(mark1)
|
||||
if chk_rc != 0: VLogger.error(f"memory corruption (live block header, rc={chk_rc}): {file_path}", "Phase1")
|
||||
# Out-of-bounds writes are the crash root cause (unrelated to reclamation/UAF, confirmed
|
||||
# Out-of-bounds writes are the crash root cause (unrelated to reclamation/UAF, confirmed
|
||||
# by A/B tests); reset_to plus the tail canary pins the offending file.
|
||||
mb.reset_to(mark1)
|
||||
sc_rc: int = mb.self_check()
|
||||
if sc_rc != 0: VLogger.error(f"memory corruption (free list, rc={sc_rc}): {file_path}", "Phase1")
|
||||
|
||||
# 1b total time (full translation + dump)
|
||||
t_b1: uint64 = w32.win32base.GetTickCount64()
|
||||
t_b1: uint64 = win32.win32base.GetTickCount64()
|
||||
VLogger.info(f"1b total time: {t_b1-t_b0} ms (full translation, trans={translated} skip={skipped} fail={failed})", "Phase1")
|
||||
|
||||
# Release the topo-sort resources used by Phase 1b
|
||||
|
||||
@@ -1,31 +1,14 @@
|
||||
import t, c
|
||||
from stdint import *
|
||||
import string
|
||||
import stdlib
|
||||
import memhub
|
||||
import viperlib
|
||||
import sys
|
||||
import w32.fileio as fileio
|
||||
import w32.win32file
|
||||
import w32.win32base
|
||||
import win32.fileio as fileio
|
||||
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
|
||||
@@ -50,7 +33,7 @@ def _ScanClassInheritance(mb: memhub.MemBuddy*, result: IncludesScanner.ScanResu
|
||||
entry_size: sizet = IncludesScanner.FileEntry.__sizeof__()
|
||||
class_count: int = 0
|
||||
|
||||
for i in range(result.Count):
|
||||
for i in range(int(result.Count)):
|
||||
# Per-file mark: reclaim this file's AST right after collecting inheritance
|
||||
# info (keeping all ASTs alive until mark1 would OOM the 1GB main pool).
|
||||
scan_mark: sizet = mb.mark()
|
||||
@@ -106,7 +89,7 @@ def _ScanClassInheritance(mb: memhub.MemBuddy*, result: IncludesScanner.ScanResu
|
||||
ch: list[ast.AST*]* = tree.children
|
||||
if ch is not None:
|
||||
cn: sizet = len(ch)
|
||||
for ci in range(cn):
|
||||
for ci in range(int(cn)):
|
||||
child: ast.AST* = ch.get(ci)
|
||||
if child is None: continue
|
||||
kd: int = child.kind()
|
||||
@@ -121,12 +104,12 @@ def _ScanClassInheritance(mb: memhub.MemBuddy*, result: IncludesScanner.ScanResu
|
||||
if tp_scan is not None:
|
||||
file_has_generic = 1
|
||||
if generic_file_count_box is not None:
|
||||
gfc_ptr_scan: t.CPtr = (int*)(generic_file_count_box)
|
||||
gfc_ptr_scan: int* = (int*)(generic_file_count_box)
|
||||
if gfc_ptr_scan is not None:
|
||||
gfc_scan: int = gfc_ptr_scan[0]
|
||||
if gfc_scan < Phase1.MAX_FILE_DEPS and generic_file_indices is not None:
|
||||
gi_addr_scan: uint64 = uint64(generic_file_indices) + gfc_scan * 4
|
||||
gi_ptr_scan: t.CPtr = (int*)(gi_addr_scan)
|
||||
gi_ptr_scan: int* = (int*)(gi_addr_scan)
|
||||
if gi_ptr_scan is not None: gi_ptr_scan[0] = i
|
||||
gfc_ptr_scan[0] = gfc_scan + 1
|
||||
|
||||
@@ -226,7 +209,7 @@ def _PreRegisterAllGenericTemplates(mb: memhub.MemBuddy*, result: IncludesScanne
|
||||
if perm is None: return 0
|
||||
p1e_size: sizet = IncludesScanner.FileEntry.__sizeof__()
|
||||
handled_count: int = 0
|
||||
for i in range(result.Count):
|
||||
for i in range(int(result.Count)):
|
||||
entry_addr_pg: uint64 = uint64(result.Entries) + i * p1e_size
|
||||
entry_pg: IncludesScanner.FileEntry* = (IncludesScanner.FileEntry*)(entry_addr_pg)
|
||||
if entry_pg is None or entry_pg.Sha1 is None: continue
|
||||
@@ -269,7 +252,7 @@ def _PreRegisterAllGenericTemplates(mb: memhub.MemBuddy*, result: IncludesScanne
|
||||
ch_pg: list[ast.AST*]* = tree_pg.children
|
||||
if ch_pg is not None:
|
||||
cn_pg: sizet = len(ch_pg)
|
||||
for ci_pg in range(cn_pg):
|
||||
for ci_pg in range(int(cn_pg)):
|
||||
child_pg: ast.AST* = ch_pg.get(ci_pg)
|
||||
if child_pg is None: continue
|
||||
ck_pg: int = child_pg.kind()
|
||||
@@ -298,7 +281,7 @@ def _PreRegisterAllGenericTemplates(mb: memhub.MemBuddy*, result: IncludesScanne
|
||||
def _FindClassDefSha1(class_names_buf: bytes, def_sha1s_buf: bytes, class_count: int, class_name: str) -> str:
|
||||
"""Find a class name's defining file SHA1 in the flat arrays."""
|
||||
if class_names_buf is None or def_sha1s_buf is None or class_name is None: return None
|
||||
for i in range(class_count):
|
||||
for i in range(int(class_count)):
|
||||
name_ptr: str = class_names_buf + i * Phase1.CLASS_NAME_LEN
|
||||
if string.strcmp(name_ptr, class_name) == 0: return def_sha1s_buf + i * Phase1.SHA1_LEN
|
||||
return None
|
||||
@@ -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.
|
||||
@@ -349,12 +332,12 @@ def _ScanTreeForContainers(node: ast.AST*, need_list_box: t.CPtr, need_dict_box:
|
||||
if cl_args_s is not None:
|
||||
na_s: sizet = len(cl_args_s)
|
||||
i_a2: sizet
|
||||
for i_a2 in range(na_s): _ScanTreeForContainers(cl_args_s.get(i_a2), need_list_box, need_dict_box, depth + 1)
|
||||
for i_a2 in range(int(na_s)): _ScanTreeForContainers(cl_args_s.get(i_a2), need_list_box, need_dict_box, depth + 1)
|
||||
cl_kws_s: list[ast.AST*]* = cl_s2.keywords
|
||||
if cl_kws_s is not None:
|
||||
nk_s: sizet = len(cl_kws_s)
|
||||
i_k2: sizet
|
||||
for i_k2 in range(nk_s):
|
||||
for i_k2 in range(int(nk_s)):
|
||||
kw_s: ast.Keyword* = ast.Keyword*(cl_kws_s.get(i_k2))
|
||||
if kw_s is not None: _ScanTreeForContainers(kw_s.value, need_list_box, need_dict_box, depth + 1)
|
||||
elif k_s == ast.ASTKind.Assign:
|
||||
@@ -364,7 +347,7 @@ def _ScanTreeForContainers(node: ast.AST*, need_list_box: t.CPtr, need_dict_box:
|
||||
if tgts_s is not None:
|
||||
nt_s: sizet = len(tgts_s)
|
||||
i_t2: sizet
|
||||
for i_t2 in range(nt_s): _ScanTreeForContainers(tgts_s.get(i_t2), need_list_box, need_dict_box, depth + 1)
|
||||
for i_t2 in range(int(nt_s)): _ScanTreeForContainers(tgts_s.get(i_t2), need_list_box, need_dict_box, depth + 1)
|
||||
elif k_s == ast.ASTKind.AnnAssign:
|
||||
aa_s2: ast.AnnAssign* = ast.AnnAssign*(node)
|
||||
_ScanTreeForContainers(aa_s2.annotation, need_list_box, need_dict_box, depth + 1)
|
||||
@@ -378,7 +361,7 @@ def _ScanTreeForContainers(node: ast.AST*, need_list_box: t.CPtr, need_dict_box:
|
||||
if fa_s is not None:
|
||||
nf_s: sizet = len(fa_s)
|
||||
i_f2: sizet
|
||||
for i_f2 in range(nf_s):
|
||||
for i_f2 in range(int(nf_s)):
|
||||
arg_s: ast.Arg* = ast.Arg*(fa_s.get(i_f2))
|
||||
if arg_s is not None: _ScanTreeForContainers(arg_s.annotation, need_list_box, need_dict_box, depth + 1)
|
||||
elif k_s == ast.ASTKind.ClassDef:
|
||||
@@ -387,7 +370,7 @@ def _ScanTreeForContainers(node: ast.AST*, need_list_box: t.CPtr, need_dict_box:
|
||||
if bases_s is not None:
|
||||
nb_s: sizet = len(bases_s)
|
||||
i_b2: sizet
|
||||
for i_b2 in range(nb_s): _ScanTreeForContainers(bases_s.get(i_b2), need_list_box, need_dict_box, depth + 1)
|
||||
for i_b2 in range(int(nb_s)): _ScanTreeForContainers(bases_s.get(i_b2), need_list_box, need_dict_box, depth + 1)
|
||||
elif k_s == ast.ASTKind.Attribute:
|
||||
at_s: ast.Attribute* = ast.Attribute*(node)
|
||||
_ScanTreeForContainers(at_s.value, need_list_box, need_dict_box, depth + 1)
|
||||
@@ -397,7 +380,7 @@ def _ScanTreeForContainers(node: ast.AST*, need_list_box: t.CPtr, need_dict_box:
|
||||
if elts_s is not None:
|
||||
ne_s: sizet = len(elts_s)
|
||||
i_e2: sizet
|
||||
for i_e2 in range(ne_s): _ScanTreeForContainers(elts_s.get(i_e2), need_list_box, need_dict_box, depth + 1)
|
||||
for i_e2 in range(int(ne_s)): _ScanTreeForContainers(elts_s.get(i_e2), need_list_box, need_dict_box, depth + 1)
|
||||
elif k_s == ast.ASTKind.UnaryOp:
|
||||
uop_s: ast.UnaryOp* = ast.UnaryOp*(node)
|
||||
_ScanTreeForContainers(uop_s.operand, need_list_box, need_dict_box, depth + 1)
|
||||
@@ -414,18 +397,18 @@ def _ScanTreeForContainers(node: ast.AST*, need_list_box: t.CPtr, need_dict_box:
|
||||
if cps_s is not None:
|
||||
nc_s: sizet = len(cps_s)
|
||||
i_c2: sizet
|
||||
for i_c2 in range(nc_s): _ScanTreeForContainers(cps_s.get(i_c2), need_list_box, need_dict_box, depth + 1)
|
||||
for i_c2 in range(int(nc_s)): _ScanTreeForContainers(cps_s.get(i_c2), need_list_box, need_dict_box, depth + 1)
|
||||
elif k_s == ast.ASTKind.BoolOp:
|
||||
bo_s: ast.BoolOp* = ast.BoolOp*(node)
|
||||
bvs_s: list[ast.AST*]* = bo_s.values
|
||||
if bvs_s is not None:
|
||||
nv_s: sizet = len(bvs_s)
|
||||
i_v2: sizet
|
||||
for i_v2 in range(nv_s): _ScanTreeForContainers(bvs_s.get(i_v2), need_list_box, need_dict_box, depth + 1)
|
||||
for i_v2 in range(int(nv_s)): _ScanTreeForContainers(bvs_s.get(i_v2), need_list_box, need_dict_box, depth + 1)
|
||||
# `[...]`/`{}` literals do not trigger _list/_dict imports (compile-time constant lists, array init);
|
||||
# recurse into statement-block children.
|
||||
if node.children is not None:
|
||||
ch_s: list[ast.AST*]* = node.children
|
||||
n_s: sizet = len(ch_s)
|
||||
ci_s: sizet
|
||||
for ci_s in range(n_s): _ScanTreeForContainers(ch_s.get(ci_s), need_list_box, need_dict_box, depth + 1)
|
||||
for ci_s in range(int(n_s)): _ScanTreeForContainers(ch_s.get(ci_s), need_list_box, need_dict_box, depth + 1)
|
||||
@@ -1,30 +1,14 @@
|
||||
import t, c
|
||||
from stdint import *
|
||||
import string
|
||||
import stdlib
|
||||
import memhub
|
||||
import viperlib
|
||||
import w32.fileio as fileio
|
||||
import w32.win32file
|
||||
import w32.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
|
||||
|
||||
@@ -41,10 +25,12 @@ def _TopoSortFiles(mb: memhub.MemBuddy*, result: IncludesScanner.ScanResult*, re
|
||||
# 1. Allocate flat arrays: file SHA1, dependency SHA1 list, dep counts, in-degrees, processed flags.
|
||||
# Flat arrays replace a FileDepEntry struct to avoid NULL str-pointer fields crashing.
|
||||
file_sha1s_buf: bytes = mb.alloc(Phase1.MAX_FILE_DEPS * Phase1.SHA1_LEN)
|
||||
file_deps_buf: bytes = mb.alloc(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)
|
||||
# 取消整数提升后,模块属性 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: 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)
|
||||
@@ -55,14 +41,14 @@ def _TopoSortFiles(mb: memhub.MemBuddy*, result: IncludesScanner.ScanResult*, re
|
||||
return -1
|
||||
|
||||
string.memset(file_sha1s_buf, 0, Phase1.MAX_FILE_DEPS * Phase1.SHA1_LEN)
|
||||
string.memset(file_deps_buf, 0, Phase1.MAX_FILE_DEPS * Phase1.MAX_DEP_EDGES * Phase1.SHA1_LEN)
|
||||
string.memset(file_deps_buf, 0, sizet(Phase1.MAX_FILE_DEPS) * Phase1.MAX_DEP_EDGES * Phase1.SHA1_LEN)
|
||||
string.memset(file_dep_counts, 0, Phase1.MAX_FILE_DEPS * 4)
|
||||
string.memset(file_indegrees, 0, Phase1.MAX_FILE_DEPS * 4)
|
||||
string.memset(file_processed, 0, Phase1.MAX_FILE_DEPS * 4)
|
||||
|
||||
file_count: int = 0
|
||||
# sha1 -> file_index map (simple linear search; file count is small)
|
||||
for i in range(result.Count):
|
||||
for i in range(int(result.Count)):
|
||||
entry_addr: uint64 = uint64(result.Entries) + i * entry_size
|
||||
entry: IncludesScanner.FileEntry* = IncludesScanner.FileEntry*(entry_addr)
|
||||
if entry is None or entry.Sha1 is None: continue
|
||||
@@ -83,7 +69,7 @@ def _TopoSortFiles(mb: memhub.MemBuddy*, result: IncludesScanner.ScanResult*, re
|
||||
file_count += 1
|
||||
|
||||
# 2. Build dependency edges: for each class, find the parent's defining file and add a child->parent edge
|
||||
for ci in range(class_count):
|
||||
for ci in range(int(class_count)):
|
||||
# Read parent class name (offset = ci * Phase1.CLASS_NAME_LEN)
|
||||
parent_ptr: str = parent_names_buf + ci * Phase1.CLASS_NAME_LEN
|
||||
if parent_ptr[0] == '\0': continue
|
||||
@@ -100,19 +86,19 @@ def _TopoSortFiles(mb: memhub.MemBuddy*, result: IncludesScanner.ScanResult*, re
|
||||
if string.strcmp(parent_sha1, child_def_sha1) == 0: continue
|
||||
|
||||
# Find the child file's index and add the parent file SHA1 to its dependency list
|
||||
for fi in range(file_count):
|
||||
for fi in range(int(file_count)):
|
||||
fsha1: str = file_sha1s_buf + fi * Phase1.SHA1_LEN
|
||||
if string.strcmp(fsha1, child_def_sha1) != 0: continue
|
||||
|
||||
# Read the current dependency count
|
||||
cnt_addr: uint64 = uint64(file_dep_counts) + fi * 4
|
||||
cnt_ptr: t.CPtr = (int*)(cnt_addr)
|
||||
cnt_ptr: int* = (int*)(cnt_addr)
|
||||
if cnt_ptr is None: break
|
||||
cur_cnt: int = cnt_ptr[0]
|
||||
|
||||
# Check whether the dependency is already present (dedupe)
|
||||
already: int = 0
|
||||
for di in range(cur_cnt):
|
||||
for di in range(int(cur_cnt)):
|
||||
dep_slot: str = file_deps_buf + fi * Phase1.MAX_DEP_EDGES * Phase1.SHA1_LEN + di * Phase1.SHA1_LEN
|
||||
if string.strcmp(dep_slot, parent_sha1) == 0:
|
||||
already = 1
|
||||
@@ -132,7 +118,7 @@ def _TopoSortFiles(mb: memhub.MemBuddy*, result: IncludesScanner.ScanResult*, re
|
||||
map_count_ts: int = 0
|
||||
if sha1_arr_ts is not None and mod_arr_ts is not None:
|
||||
map_count_ts = StubMergerIR._BuildIncludesSha1Map(temp_dir, td_len_ts, sha1_arr_ts, mod_arr_ts)
|
||||
for fi_imp in range(file_count):
|
||||
for fi_imp in range(int(file_count)):
|
||||
if map_count_ts <= 0: break
|
||||
fsha1_imp: str = file_sha1s_buf + fi_imp * Phase1.SHA1_LEN
|
||||
deps_imp: str = StubMergerStore.LookupIncludesDeps(fsha1_imp)
|
||||
@@ -153,16 +139,16 @@ def _TopoSortFiles(mb: memhub.MemBuddy*, result: IncludesScanner.ScanResult*, re
|
||||
stdlib.free(dname_imp)
|
||||
if dsha1_imp is None: continue
|
||||
# Find the dependency file's index in the list (skip self)
|
||||
for fj_imp in range(file_count):
|
||||
for fj_imp in range(int(file_count)):
|
||||
if string.strcmp(file_sha1s_buf + fj_imp * Phase1.SHA1_LEN, dsha1_imp) != 0: continue
|
||||
if fj_imp == fi_imp: break
|
||||
# Dedupe and add the edge: fi_imp depends on fj_imp (InDegree[fi_imp]++)
|
||||
cnt_addr_imp: uint64 = uint64(file_dep_counts) + fi_imp * 4
|
||||
cnt_ptr_imp: t.CPtr = (int*)(cnt_addr_imp)
|
||||
cnt_ptr_imp: int* = (int*)(cnt_addr_imp)
|
||||
if cnt_ptr_imp is None: break
|
||||
cur_cnt_imp: int = cnt_ptr_imp[0]
|
||||
already_imp: int = 0
|
||||
for di_imp in range(cur_cnt_imp):
|
||||
for di_imp in range(int(cur_cnt_imp)):
|
||||
dep_slot_imp: str = file_deps_buf + fi_imp * Phase1.MAX_DEP_EDGES * Phase1.SHA1_LEN + di_imp * Phase1.SHA1_LEN
|
||||
if string.strcmp(dep_slot_imp, dsha1_imp) == 0:
|
||||
already_imp = 1
|
||||
@@ -179,11 +165,11 @@ def _TopoSortFiles(mb: memhub.MemBuddy*, result: IncludesScanner.ScanResult*, re
|
||||
|
||||
# 3. Kahn: InDegree = DepCount (number of parent files depended on).
|
||||
# Process zero-dependency files first, then decrement the in-degree of dependents.
|
||||
for fi in range(file_count):
|
||||
for fi in range(int(file_count)):
|
||||
cnt_addr3: uint64 = uint64(file_dep_counts) + fi * 4
|
||||
cnt_ptr3: t.CPtr = (int*)(cnt_addr3)
|
||||
cnt_ptr3: int* = (int*)(cnt_addr3)
|
||||
ind_addr3: uint64 = uint64(file_indegrees) + fi * 4
|
||||
ind_ptr3: t.CPtr = (int*)(ind_addr3)
|
||||
ind_ptr3: int* = (int*)(ind_addr3)
|
||||
if cnt_ptr3 is not None and ind_ptr3 is not None: ind_ptr3[0] = cnt_ptr3[0]
|
||||
|
||||
# 4. Topological sort output
|
||||
@@ -191,27 +177,27 @@ def _TopoSortFiles(mb: memhub.MemBuddy*, result: IncludesScanner.ScanResult*, re
|
||||
changed: int = 1
|
||||
while changed != 0:
|
||||
changed = 0
|
||||
for fi in range(file_count):
|
||||
for fi in range(int(file_count)):
|
||||
# Check processed flag
|
||||
proc_addr4: uint64 = uint64(file_processed) + fi * 4
|
||||
proc_ptr4: t.CPtr = (int*)(proc_addr4)
|
||||
proc_ptr4: int* = (int*)(proc_addr4)
|
||||
if proc_ptr4 is None or proc_ptr4[0] != 0: continue
|
||||
# Check in-degree
|
||||
ind_addr4: uint64 = uint64(file_indegrees) + fi * 4
|
||||
ind_ptr4: t.CPtr = (int*)(ind_addr4)
|
||||
ind_ptr4: int* = (int*)(ind_addr4)
|
||||
if ind_ptr4 is None or ind_ptr4[0] > 0: continue
|
||||
|
||||
# In-degree 0: can be processed
|
||||
# Find the matching result.Entries index
|
||||
target_sha1: str = file_sha1s_buf + fi * Phase1.SHA1_LEN
|
||||
for ri in range(result.Count):
|
||||
for ri in range(int(result.Count)):
|
||||
ea_r: uint64 = uint64(result.Entries) + ri * entry_size
|
||||
ent_r: IncludesScanner.FileEntry* = IncludesScanner.FileEntry*(ea_r)
|
||||
if ent_r is not None and ent_r.Sha1 is not None:
|
||||
if string.strcmp(ent_r.Sha1, target_sha1) == 0:
|
||||
# Write into the output index array
|
||||
out_idx_addr: uint64 = uint64(out_order) + out_idx * 4
|
||||
out_int_ptr: t.CPtr = (int*)(out_idx_addr)
|
||||
out_int_ptr: int* = (int*)(out_idx_addr)
|
||||
if out_int_ptr is not None: out_int_ptr[0] = ri
|
||||
out_idx += 1
|
||||
break
|
||||
@@ -221,56 +207,56 @@ def _TopoSortFiles(mb: memhub.MemBuddy*, result: IncludesScanner.ScanResult*, re
|
||||
changed = 1
|
||||
|
||||
# Decrement the in-degree of files that depend on this one
|
||||
for fj in range(file_count):
|
||||
for fj in range(int(file_count)):
|
||||
proc_addr5: uint64 = uint64(file_processed) + fj * 4
|
||||
proc_ptr5: t.CPtr = (int*)(proc_addr5)
|
||||
proc_ptr5: int* = (int*)(proc_addr5)
|
||||
if proc_ptr5 is None or proc_ptr5[0] != 0: continue
|
||||
# Read fj's dependency count
|
||||
cnt_addr5: uint64 = uint64(file_dep_counts) + fj * 4
|
||||
cnt_ptr5: t.CPtr = (int*)(cnt_addr5)
|
||||
cnt_ptr5: int* = (int*)(cnt_addr5)
|
||||
if cnt_ptr5 is None: continue
|
||||
fj_dep_cnt: int = cnt_ptr5[0]
|
||||
for di in range(fj_dep_cnt):
|
||||
for di in range(int(fj_dep_cnt)):
|
||||
dep_slot5: str = file_deps_buf + fj * Phase1.MAX_DEP_EDGES * Phase1.SHA1_LEN + di * Phase1.SHA1_LEN
|
||||
if string.strcmp(dep_slot5, target_sha1) == 0:
|
||||
ind_addr5: uint64 = uint64(file_indegrees) + fj * 4
|
||||
ind_ptr5: t.CPtr = (int*)(ind_addr5)
|
||||
ind_ptr5: int* = (int*)(ind_addr5)
|
||||
if ind_ptr5 is not None: ind_ptr5[0] = ind_ptr5[0] - 1
|
||||
break
|
||||
|
||||
# Handle cycles: within a cycle emit the more-depended-upon files first.
|
||||
# On-demand container injection (e.g. string -> _list) plus real imports form pseudo-cycles Kahn cannot fully drain; scan order would translate dependents first -> unregistered cross-module vtable TypeStr.
|
||||
if out_idx < file_count:
|
||||
rem_idx: t.CPtr = stdlib.malloc(4 * file_count)
|
||||
rem_depcnt: t.CPtr = stdlib.malloc(4 * file_count)
|
||||
rem_idx: int* = (int*)(stdlib.malloc(4 * file_count))
|
||||
rem_depcnt: int* = (int*)(stdlib.malloc(4 * file_count))
|
||||
rem_count: int = 0
|
||||
if rem_idx is not None and rem_depcnt is not None:
|
||||
for fi in range(file_count):
|
||||
for fi in range(int(file_count)):
|
||||
proc_addr7: uint64 = uint64(file_processed) + fi * 4
|
||||
proc_ptr7: t.CPtr = (int*)(proc_addr7)
|
||||
proc_ptr7: int* = (int*)(proc_addr7)
|
||||
if proc_ptr7 is not None and proc_ptr7[0] == 0:
|
||||
rem_idx[rem_count] = fi
|
||||
rem_depcnt[rem_count] = 0
|
||||
rem_count += 1
|
||||
# Compute each remaining file's dependents count (among the remaining files)
|
||||
for i_a in range(rem_count):
|
||||
for i_a in range(int(rem_count)):
|
||||
fi_a: int = rem_idx[i_a]
|
||||
target_sha1_a: str = file_sha1s_buf + fi_a * Phase1.SHA1_LEN
|
||||
for i_b in range(rem_count):
|
||||
for i_b in range(int(rem_count)):
|
||||
fi_b: int = rem_idx[i_b]
|
||||
if fi_b == fi_a: continue
|
||||
cnt_addr_b: uint64 = uint64(file_dep_counts) + fi_b * 4
|
||||
cnt_ptr_b: t.CPtr = (int*)(cnt_addr_b)
|
||||
cnt_ptr_b: int* = (int*)(cnt_addr_b)
|
||||
if cnt_ptr_b is None: continue
|
||||
cnt_b: int = cnt_ptr_b[0]
|
||||
for di_b in range(cnt_b):
|
||||
for di_b in range(int(cnt_b)):
|
||||
dep_slot_b: str = file_deps_buf + fi_b * Phase1.MAX_DEP_EDGES * Phase1.SHA1_LEN + di_b * Phase1.SHA1_LEN
|
||||
if string.strcmp(dep_slot_b, target_sha1_a) == 0:
|
||||
rem_depcnt[i_a] = rem_depcnt[i_a] + 1
|
||||
break
|
||||
# Bubble sort descending (more dependents first)
|
||||
for i_a in range(rem_count):
|
||||
for j_a in range(rem_count - 1):
|
||||
for i_a in range(int(rem_count)):
|
||||
for j_a in range(int(rem_count - 1)):
|
||||
if rem_depcnt[j_a] < rem_depcnt[j_a + 1]:
|
||||
tmp_i: int = rem_idx[j_a]
|
||||
rem_idx[j_a] = rem_idx[j_a + 1]
|
||||
@@ -279,19 +265,19 @@ def _TopoSortFiles(mb: memhub.MemBuddy*, result: IncludesScanner.ScanResult*, re
|
||||
rem_depcnt[j_a] = rem_depcnt[j_a + 1]
|
||||
rem_depcnt[j_a + 1] = tmp_d
|
||||
# Emit the remaining files in order
|
||||
for i_a in range(rem_count):
|
||||
for i_a in range(int(rem_count)):
|
||||
fi_a2: int = rem_idx[i_a]
|
||||
proc_addr8: uint64 = uint64(file_processed) + fi_a2 * 4
|
||||
proc_ptr8: t.CPtr = (int*)(proc_addr8)
|
||||
proc_ptr8: int* = (int*)(proc_addr8)
|
||||
if proc_ptr8 is None or proc_ptr8[0] != 0: continue
|
||||
target_sha1_2: str = file_sha1s_buf + fi_a2 * Phase1.SHA1_LEN
|
||||
for ri in range(result.Count):
|
||||
for ri in range(int(result.Count)):
|
||||
ea_r2: uint64 = uint64(result.Entries) + ri * entry_size
|
||||
ent_r2: IncludesScanner.FileEntry* = IncludesScanner.FileEntry*(ea_r2)
|
||||
if ent_r2 is not None and ent_r2.Sha1 is not None:
|
||||
if string.strcmp(ent_r2.Sha1, target_sha1_2) == 0:
|
||||
out_idx_addr2: uint64 = uint64(out_order) + out_idx * 4
|
||||
out_int_ptr2: t.CPtr = (int*)(out_idx_addr2)
|
||||
out_int_ptr2: int* = (int*)(out_idx_addr2)
|
||||
if out_int_ptr2 is not None: out_int_ptr2[0] = ri
|
||||
out_idx += 1
|
||||
break
|
||||
@@ -300,7 +286,7 @@ def _TopoSortFiles(mb: memhub.MemBuddy*, result: IncludesScanner.ScanResult*, re
|
||||
stdlib.free(rem_depcnt)
|
||||
|
||||
# Write the output count
|
||||
out_count_ptr: t.CPtr = (int*)(out_count)
|
||||
out_count_ptr: int* = (int*)(out_count)
|
||||
if out_count_ptr is not None: out_count_ptr[0] = out_idx
|
||||
|
||||
# Free the flat arrays
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import t, c
|
||||
from stdint import *
|
||||
import memhub
|
||||
|
||||
|
||||
+1535
-1537
File diff suppressed because it is too large.
Load diff
+609
-600
File diff suppressed because it is too large.
Load diff
@@ -1,13 +1,12 @@
|
||||
import t, c
|
||||
from stdint import *
|
||||
import memhub
|
||||
import string
|
||||
import stdlib
|
||||
import viperlib
|
||||
import hashlib
|
||||
import w32.win32file
|
||||
import w32.win32base
|
||||
import w32.fileio as fileio
|
||||
import win32.win32file
|
||||
import win32.win32base
|
||||
import win32.fileio as fileio
|
||||
|
||||
|
||||
# Utils - project utility functions (SHA1 hashing, directory cleanup)
|
||||
@@ -19,9 +18,9 @@ _mbuddy: memhub.MemBuddy*
|
||||
# nibble_to_hex_char - convert a nibble to a hex character
|
||||
def nibble_to_hex_char(n: int, uppercase: int) -> char:
|
||||
"""Convert a 0-15 nibble to a hex character (uppercase=1 uses 'A'-'F')."""
|
||||
if n < 10: return '0' + n
|
||||
if uppercase != 0: return 'A' + (n - 10)
|
||||
return 'a' + (n - 10)
|
||||
if n < 10: return char('0' + n)
|
||||
if uppercase != 0: return char('A' + (n - 10))
|
||||
return char('a' + (n - 10))
|
||||
|
||||
|
||||
# bytes_to_hex - convert a byte sequence to a hex string
|
||||
@@ -33,7 +32,7 @@ def bytes_to_hex(pool: memhub.MemBuddy*, digest: bytes, nbytes: sizet, uppercase
|
||||
if use_malloc != 0: hex_buf = stdlib.malloc(hex_len)
|
||||
else: hex_buf = pool.alloc(hex_len)
|
||||
if hex_buf is None: return None
|
||||
for i in range(nbytes):
|
||||
for i in range(int(nbytes)):
|
||||
hi: int = (digest[i] >> 4) & 0xF
|
||||
lo: int = digest[i] & 0xF
|
||||
hex_buf[i * 2] = nibble_to_hex_char(hi, uppercase)
|
||||
@@ -129,7 +128,7 @@ def PathToModuleName(path: str, strip_includes_prefix: int) -> str:
|
||||
# Copy and replace / with .
|
||||
result: str = stdlib.malloc(plen + 1)
|
||||
if result is None: return None
|
||||
for ri in range(plen):
|
||||
for ri in range(int(plen)):
|
||||
ch: char = path[path_start + ri]
|
||||
if ch == '/' or ch == '\\': result[ri] = '.'
|
||||
else: result[ri] = ch
|
||||
@@ -146,14 +145,14 @@ def CleanDir(dir_path: str) -> int:
|
||||
if pattern is None: return -1
|
||||
viperlib.snprintf(pattern, dir_len + 8, "%s/*", dir_path)
|
||||
|
||||
find_data: w32.win32file.WIN32_FIND_DATAA* = stdlib.malloc(w32.win32file.WIN32_FIND_DATAA.__sizeof__())
|
||||
find_data: win32.win32file.WIN32_FIND_DATAA* = stdlib.malloc(win32.win32file.WIN32_FIND_DATAA.__sizeof__())
|
||||
if find_data is None:
|
||||
stdlib.free(pattern)
|
||||
return -1
|
||||
string.memset(find_data, 0, w32.win32file.WIN32_FIND_DATAA.__sizeof__())
|
||||
string.memset(find_data, 0, win32.win32file.WIN32_FIND_DATAA.__sizeof__())
|
||||
|
||||
handle: w32.win32base.HANDLE = w32.win32file.FindFirstFileA(pattern, find_data)
|
||||
if handle == w32.win32base.INVALID_HANDLE_VALUE:
|
||||
handle: win32.win32base.HANDLE = win32.win32file.FindFirstFileA(pattern, find_data)
|
||||
if handle == win32.win32base.INVALID_HANDLE_VALUE:
|
||||
stdlib.free(pattern)
|
||||
stdlib.free(find_data)
|
||||
return 0
|
||||
@@ -170,21 +169,21 @@ def CleanDir(dir_path: str) -> int:
|
||||
full_path: bytes = JoinPath(dir_path, fname)
|
||||
if full_path is not None:
|
||||
# Check whether this is a directory
|
||||
is_dir: uint32 = find_data.dwFileAttributes & w32.win32file.FILE_ATTRIBUTE_DIRECTORY
|
||||
is_dir: uint32 = find_data.dwFileAttributes & win32.win32file.FILE_ATTRIBUTE_DIRECTORY
|
||||
if is_dir != 0:
|
||||
# Recurse into the subdirectory
|
||||
sub_deleted: int = CleanDir(full_path)
|
||||
if sub_deleted > 0: deleted += sub_deleted
|
||||
# Remove the now-empty subdirectory
|
||||
w32.win32file.RemoveDirectoryA(full_path)
|
||||
win32.win32file.RemoveDirectoryA(full_path)
|
||||
else:
|
||||
# Delete the file
|
||||
if w32.win32file.DeleteFileA(full_path) != 0: deleted += 1
|
||||
if win32.win32file.DeleteFileA(full_path) != 0: deleted += 1
|
||||
stdlib.free(full_path)
|
||||
|
||||
if w32.win32file.FindNextFileA(handle, find_data) == 0: break
|
||||
if win32.win32file.FindNextFileA(handle, find_data) == 0: break
|
||||
|
||||
w32.win32file.FindClose(handle)
|
||||
win32.win32file.FindClose(handle)
|
||||
stdlib.free(pattern)
|
||||
stdlib.free(find_data)
|
||||
return deleted
|
||||
@@ -1,4 +1,3 @@
|
||||
import t, c
|
||||
from stdint import *
|
||||
|
||||
from .Config import Load_project_config, resolve_paths
|
||||
|
||||
+1012
-1004
File diff suppressed because it is too large.
Load diff
@@ -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
|
||||
@@ -400,6 +399,11 @@ def _cache_vpi_return_annots_text(vpi_buf: bytes, src_sha1: str) -> int:
|
||||
cls_len2: sizet = 0
|
||||
if cur_cls is not None: cls_len2 = string.strlen(cur_cls)
|
||||
key_len: sizet = sha1_len + 1 + cls_len2 + 1 + mname_len
|
||||
# fail-fast: cache entries are fixed-size slots. Skipping a too-long key or
|
||||
# return text leaves the method without a cached return type, and callers
|
||||
# then fall back to i32 (pointer returns get truncated).
|
||||
if key_len >= VPI_KEY_LEN or rtype_len >= VPI_RET_LEN:
|
||||
VLogger.error(f"return-annotation cache slot too small (key {key_len}/{VPI_KEY_LEN} bytes, return {rtype_len}/{VPI_RET_LEN} bytes); shorten the class/method name or the return annotation", "VpiReader")
|
||||
if key_len < VPI_KEY_LEN and rtype_len < VPI_RET_LEN:
|
||||
# copy method name to a temp buffer (vpi_buf+dstart is not NUL-terminated)
|
||||
mname_buf: bytes = stdlib.malloc(mname_len + 1)
|
||||
@@ -575,6 +579,10 @@ def _cache_vpi_param_annots_text(vpi_buf: bytes, src_sha1: str) -> int:
|
||||
while a_start < s_end and vpi_buf[a_start] == ' ': a_start += 1
|
||||
a_end: sizet = s_end
|
||||
while a_end > a_start and vpi_buf[a_end - 1] == ' ': a_end -= 1
|
||||
# fail-fast: dropping a segment shifts every later parameter's annotation onto
|
||||
# the wrong parameter (declare/define type mismatch), which is worse than a miss.
|
||||
if plist_pos + (a_end - a_start) + 1 >= VPI_PARAM_LEN:
|
||||
VLogger.error(f"parameter annotation list exceeds the {VPI_PARAM_LEN}-byte cache slot; shorten this method's annotations", "VpiReader")
|
||||
if plist_pos + (a_end - a_start) + 1 < VPI_PARAM_LEN:
|
||||
if seg_written > 0:
|
||||
plist[plist_pos] = '@'
|
||||
@@ -592,6 +600,10 @@ def _cache_vpi_param_annots_text(vpi_buf: bytes, src_sha1: str) -> int:
|
||||
cls_len2: sizet = 0
|
||||
if cur_cls is not None: cls_len2 = string.strlen(cur_cls)
|
||||
key_len: sizet = sha1_len + 1 + cls_len2 + 1 + mname_len
|
||||
if key_len >= VPI_PARAM_KEY_LEN:
|
||||
# fail-fast: a skipped key silently loses the whole parameter annotation
|
||||
# list (the caller then rebuilds declare params from the degraded text).
|
||||
VLogger.error(f"parameter-annotation cache key exceeds {VPI_PARAM_KEY_LEN} bytes (class/method name too long)", "VpiReader")
|
||||
if key_len < VPI_PARAM_KEY_LEN:
|
||||
mname_buf: bytes = stdlib.malloc(mname_len + 1)
|
||||
if mname_buf is not None:
|
||||
@@ -2090,14 +2102,36 @@ def _vpi_register_typedef_closure(pool: memhub.MemBuddy*, import_mods: str, temp
|
||||
if aa_r.target.kind() != ast.ASTKind.Name: continue
|
||||
cdef_t: ast.Name* = ast.Name*(aa_r.target)
|
||||
if cdef_t.id is None: continue
|
||||
if aa_r.value.kind() == ast.ASTKind.Constant:
|
||||
cn_v: ast.Constant* = ast.Constant*(aa_r.value)
|
||||
if cn_v.const_kind == ast.CONST_FLOAT: GenApi.register_cdefine_double(pool, cdef_t.id, cn_v.float_val)
|
||||
# 与定义端同口径:剥掉 `float(...)`/`double(...)` 包裹与负浮点字面量
|
||||
# `UnaryOp(USub, Constant(float))`;否则 `X: double = -100.0` 既不注册
|
||||
# 浮点(静默丢失),又可能被误当整型去走常驻提取。
|
||||
vnode_r: ast.AST* = aa_r.value
|
||||
neg_r: int = 0
|
||||
if vnode_r.kind() == ast.ASTKind.Call:
|
||||
cl_r: ast.Call* = ast.Call*(vnode_r)
|
||||
if cl_r.args is not None and len(cl_r.args) > 0:
|
||||
fa_r: ast.AST* = cl_r.args.get(0)
|
||||
if fa_r is not None and fa_r.kind() == ast.ASTKind.Constant: vnode_r = fa_r
|
||||
if vnode_r.kind() == ast.ASTKind.UnaryOp:
|
||||
uu_r: ast.UnaryOp* = ast.UnaryOp*(vnode_r)
|
||||
if uu_r.op == ast.OpKind.USub and uu_r.operand is not None and uu_r.operand.kind() == ast.ASTKind.Constant:
|
||||
ic_r: ast.Constant* = ast.Constant*(uu_r.operand)
|
||||
if ic_r.const_kind == ast.CONST_FLOAT:
|
||||
vnode_r = uu_r.operand
|
||||
neg_r = 1
|
||||
if vnode_r.kind() == ast.ASTKind.Constant:
|
||||
cn_v: ast.Constant* = ast.Constant*(vnode_r)
|
||||
if cn_v.const_kind == ast.CONST_FLOAT:
|
||||
fv_r: double = cn_v.float_val
|
||||
if neg_r != 0: fv_r = -fv_r
|
||||
# 浮点宽度同样由声明注解裁决(与定义方同一套),
|
||||
# 否则 `define PI: float = ...` 的使用方会把 f32 当 double
|
||||
GenApi.register_cdefine_double(pool, cdef_t.id, fv_r, GenApi.cdefine_decl_float_bits(pool, aa_r.annotation))
|
||||
elif cn_v.const_kind == ast.CONST_INT:
|
||||
cdef_val: int64 = GenApi.extract_cdefine_int_value(aa_r.value)
|
||||
cdef_val: int64 = GenApi.extract_cdefine_int_value(vnode_r)
|
||||
# 位宽与定义方同一套裁决(注解 → 右侧强转 → 值的最小可存宽度),
|
||||
# 否则使用方拿 bits=0 只能兜底,跨模块位宽会与定义方不一致
|
||||
GenApi.register_cdefine_constant(pool, cdef_t.id, cdef_val, GenApi.cdefine_decl_bits(pool, aa_r.annotation, aa_r.value))
|
||||
GenApi.register_cdefine_constant(pool, cdef_t.id, cdef_val, GenApi.cdefine_decl_bits(pool, aa_r.annotation, vnode_r))
|
||||
# recurse into child imports (cycle guarded by the visited marks)
|
||||
if sub_imports[0] != '\0': _vpi_register_typedef_closure(pool, sub_imports, temp_dir, depth + 1)
|
||||
stdlib.free(sub_imports)
|
||||
@@ -2409,23 +2443,42 @@ def _ParseVpiIntoModuleImpl(pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, v
|
||||
if cdef_tgt.id is not None:
|
||||
# Skip string CDefines (version numbers etc.): extract_cdefine_int_value
|
||||
# fails fast on string constants, and count refs only need integer CDefines.
|
||||
# 与定义端 `_register_cdefine_from_value` 同口径:先剥掉 `float(...)`/`double(...)`
|
||||
# 强转包裹与负浮点字面量的 `UnaryOp(USub, Constant(float))`,否则
|
||||
# `X: double = -100.0` 会被判成"非 float"而落到整型提取 ⇒ fatal。
|
||||
vnode: ast.AST* = aa.value
|
||||
cdef_neg_f: int = 0
|
||||
if vnode.kind() == ast.ASTKind.Call:
|
||||
cl_w2: ast.Call* = ast.Call*(vnode)
|
||||
if cl_w2.args is not None and len(cl_w2.args) > 0:
|
||||
fa2: ast.AST* = cl_w2.args.get(0)
|
||||
if fa2 is not None and fa2.kind() == ast.ASTKind.Constant: vnode = fa2
|
||||
if vnode.kind() == ast.ASTKind.UnaryOp:
|
||||
uu2: ast.UnaryOp* = ast.UnaryOp*(vnode)
|
||||
if uu2.op == ast.OpKind.USub and uu2.operand is not None and uu2.operand.kind() == ast.ASTKind.Constant:
|
||||
ic2: ast.Constant* = ast.Constant*(uu2.operand)
|
||||
if ic2.const_kind == ast.CONST_FLOAT:
|
||||
vnode = uu2.operand
|
||||
cdef_neg_f = 1
|
||||
cdef_is_str: int = 0
|
||||
cdef_is_float: int = 0
|
||||
cdef_float_val: double = 0.0
|
||||
if aa.value.kind() == ast.ASTKind.Constant:
|
||||
cn_v: ast.Constant* = ast.Constant*(aa.value)
|
||||
if vnode.kind() == ast.ASTKind.Constant:
|
||||
cn_v: ast.Constant* = ast.Constant*(vnode)
|
||||
if cn_v.const_kind == ast.CONST_STR: cdef_is_str = 1
|
||||
elif cn_v.const_kind == ast.CONST_FLOAT:
|
||||
cdef_is_float = 1
|
||||
cdef_float_val = cn_v.float_val
|
||||
if cdef_neg_f != 0: cdef_float_val = -cdef_float_val
|
||||
if cdef_is_str == 0 and cdef_is_float != 0:
|
||||
# Float CDefine (e.g. FRAGMENTATION_THRESHOLD: t.CDefine = 0.7): register via
|
||||
# register_cdefine_double (integer extraction would fail fast).
|
||||
GenApi.register_cdefine_double(pool, cdef_tgt.id, cdef_float_val)
|
||||
# 注解声明 float 时带上 f32 种类(与定义方同一套裁决)。
|
||||
GenApi.register_cdefine_double(pool, cdef_tgt.id, cdef_float_val, GenApi.cdefine_decl_float_bits(pool, aa.annotation))
|
||||
elif cdef_is_str == 0:
|
||||
cdef_val: int64 = GenApi.extract_cdefine_int_value(aa.value)
|
||||
cdef_val: int64 = GenApi.extract_cdefine_int_value(vnode)
|
||||
# 位宽与定义方同一套裁决(注解 → 右侧强转 → 值的最小可存宽度)
|
||||
GenApi.register_cdefine_constant(pool, cdef_tgt.id, cdef_val, GenApi.cdefine_decl_bits(pool, aa.annotation, aa.value))
|
||||
GenApi.register_cdefine_constant(pool, cdef_tgt.id, cdef_val, GenApi.cdefine_decl_bits(pool, aa.annotation, vnode))
|
||||
else:
|
||||
_inject_global_variable_declare(pool, mod, aa, trans, stub_imported_modules, stub_from_imports, i32_ty, i8_ptr_ty, src_sha1)
|
||||
continue
|
||||
@@ -2628,6 +2681,12 @@ def _inject_class_struct_declare(pool: memhub.MemBuddy*, mod: mlirlite.LLVMModul
|
||||
if existing_c is not None and existing_c.Ty is not None:
|
||||
if existing_c.IsForwardDecl != 0: fill_fwd_c = 1
|
||||
else:
|
||||
# entry 已在全局 StructTable(可能由别的模块先注入/注册),但**本模块**的 NamedTypeHead
|
||||
# 不保证有它的完整定义:只有被按值使用(值返回类型 / 值字段 / 数组元素)时打印才回查登记
|
||||
# 表,而指针引用只会登记 opaque 前向声明 → 打印签名/类型时报 "still opaque"。故这里补齐
|
||||
# 本模块的完整定义(幂等:add_named_type 同名去重,完整定义替换 opaque)。
|
||||
mlirlite.module_add_named_type(mod, pool, existing_c.Ty)
|
||||
if existing_c.IsPacked != 0: mlirlite.module_set_named_type_packed(mod, pool, existing_c.Ty, 1)
|
||||
GenApi.add_visible_struct(pool, real_name)
|
||||
return 1
|
||||
# Skip REnum classes (class X(t.REnum)): their layout is computed from variants by
|
||||
@@ -2645,9 +2704,25 @@ def _inject_class_struct_declare(pool: memhub.MemBuddy*, mod: mlirlite.LLVMModul
|
||||
# 2. collect field types (AnnAssign children, mirroring ClassGenDef.translate_class_def)
|
||||
field_names_buf: sizet* = pool.alloc(8 * GenTypes.FIELD_MAX)
|
||||
field_types_buf: sizet* = pool.alloc(8 * GenTypes.FIELD_MAX)
|
||||
# Per-field marker buffers, index-aligned with field_names_buf / add_field order. The .vpi text
|
||||
# keeps each field's raw annotation (Converter._PyiTypeStr renders BinOp/Attribute verbatim, so
|
||||
# `X | t.BigEndian` / `t.CVolatile | int` survive), so the same markers ClassGenDef extracts on
|
||||
# the definition side must be re-extracted here; otherwise a struct referenced across a module
|
||||
# boundary loses its volatile/const/byte-order semantics and the same field compiles differently
|
||||
# per module (the bug this fixes).
|
||||
field_volatile_buf: sizet* = pool.alloc(8 * GenTypes.FIELD_MAX)
|
||||
field_pointee_volatile_buf: sizet* = pool.alloc(8 * GenTypes.FIELD_MAX)
|
||||
field_const_buf: sizet* = pool.alloc(8 * GenTypes.FIELD_MAX)
|
||||
field_byteorder_buf: sizet* = pool.alloc(8 * GenTypes.FIELD_MAX)
|
||||
if field_names_buf is None or field_types_buf is None: return 0
|
||||
if field_volatile_buf is None or field_pointee_volatile_buf is None: return 0
|
||||
if field_const_buf is None or field_byteorder_buf is None: return 0
|
||||
string.memset(field_names_buf, 0, 8 * GenTypes.FIELD_MAX)
|
||||
string.memset(field_types_buf, 0, 8 * GenTypes.FIELD_MAX)
|
||||
string.memset(field_volatile_buf, 0, 8 * GenTypes.FIELD_MAX)
|
||||
string.memset(field_pointee_volatile_buf, 0, 8 * GenTypes.FIELD_MAX)
|
||||
string.memset(field_const_buf, 0, 8 * GenTypes.FIELD_MAX)
|
||||
string.memset(field_byteorder_buf, 0, 8 * GenTypes.FIELD_MAX)
|
||||
field_count: int = 0
|
||||
cls_children_c: list[ast.AST*]* = class_cd.children
|
||||
if cls_children_c is not None:
|
||||
@@ -2668,6 +2743,18 @@ def _inject_class_struct_declare(pool: memhub.MemBuddy*, mod: mlirlite.LLVMModul
|
||||
if field_count < GenTypes.FIELD_MAX:
|
||||
field_names_buf[field_count] = sizet(m_tgt_c.id)
|
||||
field_types_buf[field_count] = sizet(field_ty_c)
|
||||
# Re-extract the field-level markers from the raw annotation, exactly as
|
||||
# ClassGenDef.translate_class_def does on the definition side (same predicates, same
|
||||
# precedence), so a cross-module reference gets identical field semantics.
|
||||
annot_fc: ast.AST* = m_aa_c.annotation
|
||||
if annot_fc is not None:
|
||||
if GenApi.has_decorator_marker(annot_fc, "CVolatile", 0) != 0: field_volatile_buf[field_count] = 1
|
||||
if GenApi.has_pointee_volatile_marker(annot_fc) != 0: field_pointee_volatile_buf[field_count] = 1
|
||||
if GenApi.is_const_object_marker(annot_fc, field_ty_c) != 0: field_const_buf[field_count] = 1
|
||||
# Byte order: 1 = t.BigEndian / 2 = t.LittleEndian (0 stays native);
|
||||
# annotation `|` means "and", so `X | t.BigEndian` is a BigEndian X.
|
||||
if GenApi.has_decorator_marker(annot_fc, "BigEndian", 0) != 0: field_byteorder_buf[field_count] = 1
|
||||
elif GenApi.has_decorator_marker(annot_fc, "LittleEndian", 0) != 0: field_byteorder_buf[field_count] = 2
|
||||
field_count += 1
|
||||
# 3. The vpi stub has no class fields -> field_count is 0; registering an empty class would make
|
||||
# the source module hit an empty struct and raise InternalCompilerError, so do not register.
|
||||
@@ -2734,7 +2821,17 @@ def _inject_class_struct_declare(pool: memhub.MemBuddy*, mod: mlirlite.LLVMModul
|
||||
for afi_c in range(int(field_count)):
|
||||
af_name_c: str = str(field_names_buf[afi_c])
|
||||
af_ty_c: mlirlite.LLVMType* = (mlirlite.LLVMType*)(field_types_buf[afi_c])
|
||||
GenApi.add_field(pool, entry_c, af_name_c, af_ty_c)
|
||||
af_idx_c: int = GenApi.add_field(pool, entry_c, af_name_c, af_ty_c)
|
||||
# Restore the per-field markers AFTER add_field, because StructGenMixin.add_field always
|
||||
# resets them to 0 (it cannot see the annotation). Buffers are index-aligned with add_field
|
||||
# order, so afi_c indexes them directly (mirrors ClassGenDef's post-add_field set).
|
||||
if af_idx_c >= 0 and GenApi._g_gen is not None:
|
||||
af_fe_c: GenTypes.FieldEntry* = GenApi._g_gen.StructGen._get_field_entry(entry_c, af_idx_c)
|
||||
if af_fe_c is not None:
|
||||
if field_volatile_buf[afi_c] != 0: af_fe_c.IsVolatile = 1
|
||||
if field_pointee_volatile_buf[afi_c] != 0: af_fe_c.PointeeVolatile = 1
|
||||
if field_const_buf[afi_c] != 0: af_fe_c.IsConst = 1
|
||||
if field_byteorder_buf[afi_c] != 0: af_fe_c.ByteOrder = int(field_byteorder_buf[afi_c])
|
||||
GenApi.add_visible_struct(pool, real_name)
|
||||
return 1
|
||||
|
||||
@@ -2837,10 +2934,10 @@ def _inject_global_variable_declare(pool: memhub.MemBuddy*, mod: mlirlite.LLVMMo
|
||||
gv_ty_v: mlirlite.LLVMType* = resolved_v
|
||||
match resolved_v:
|
||||
case mlirlite.LLVMType.Struct(_, _, _):
|
||||
# Object classes (@t.Object/@t.CVTable: object-as-pointer) -> Ptr(struct), matching
|
||||
# handle_module_level_var; plain t.CStruct value classes keep a value instance.
|
||||
# 与 handle_module_level_var 保持同一口径:只有 `__new__`(自定义分配)的类才以指针表示;
|
||||
# 仅"有方法 / 带 vtable"(IsOOP / HasVTable)的类,模块级全局同样按值本位存储。
|
||||
gv_entry_v: GenTypes.StructEntry* = GenApi.find_struct_by_type(resolved_v)
|
||||
if gv_entry_v is not None and (gv_entry_v.HasNew != 0 or gv_entry_v.HasVTable != 0 or gv_entry_v.IsOOP != 0): gv_ty_v = mlirlite.Ptr(pool, resolved_v)
|
||||
if gv_entry_v is not None and gv_entry_v.HasNew != 0: gv_ty_v = mlirlite.Ptr(pool, resolved_v)
|
||||
else: gv_ty_v = resolved_v
|
||||
case mlirlite.LLVMType.Array(aelem_v, acount_v):
|
||||
# Fixed-size arrays (CArray[T, N]) keep the full [N x T] type, matching
|
||||
@@ -3074,6 +3171,11 @@ def _inject_method_declare(pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, m_
|
||||
if ret_ty is None and string.strcmp(m_fd.name, "__init__") == 0: ret_ty = mlirlite.Void(pool)
|
||||
if ret_ty is None: ret_ty = i32_ty
|
||||
|
||||
# 跨模块方法**按值返回类**时,引用模块必须登记该具名结构体的完整定义:否则打印方法签名回查登记表,
|
||||
# 命中的只是指针引用登记的 opaque 前向声明 → MLIR 报 "still opaque at print time"。与
|
||||
# _inject_function_declare 同口径(那里对值返回类型调 _ensure_full_value_types)。
|
||||
_ensure_full_value_types(mod, pool, ret_ty)
|
||||
|
||||
# create the declare
|
||||
func: mlirlite.Function* = mlirlite.create_declare(pool, mod, mangled_name, ret_ty)
|
||||
if func is None: return
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import lib.core.Gen.GenApi as GenApi
|
||||
import t, c
|
||||
from stdint import *
|
||||
import memhub
|
||||
import string
|
||||
import stdlib
|
||||
import mlirlite
|
||||
import w32.win32file as win32file
|
||||
import w32.win32base as win32base
|
||||
import w32.fileio as fileio
|
||||
import win32.win32file as win32file
|
||||
import win32.win32base as win32base
|
||||
import win32.fileio as fileio
|
||||
import viperlib
|
||||
import sys
|
||||
import ast
|
||||
@@ -31,7 +30,7 @@ def _IsAppModule(mod_name: str) -> int:
|
||||
"""Return whether mod_name is an App source file (store hit with empty incdir)."""
|
||||
if mod_name is None: return 0
|
||||
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None: return 0
|
||||
for i in range(StubMerger._sha1_store_count):
|
||||
for i in range(int(StubMerger._sha1_store_count)):
|
||||
midx: sizet = sizet(i) * 64
|
||||
if string.strcmp(StubMerger._sha1_store_mod + midx, mod_name) == 0:
|
||||
iidx: sizet = sizet(i) * StubMerger.MAX_INC_DIR_LEN
|
||||
@@ -100,7 +99,7 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str,
|
||||
scan_res_r: IncludesScanner.ScanResult* = IncludesScanner.scan_includes(mb, source_dir)
|
||||
if scan_res_r is not None:
|
||||
es_r: sizet = IncludesScanner.FileEntry.__sizeof__()
|
||||
for e_i_r in range(scan_res_r.Count):
|
||||
for e_i_r in range(int(scan_res_r.Count)):
|
||||
e_r: IncludesScanner.FileEntry* = IncludesScanner.FileEntry*(uint64(scan_res_r.Entries) + uint64(e_i_r) * es_r)
|
||||
if e_r is None or e_r.Path is None: continue
|
||||
# File-level mark: reclaim the AST right after parsing (keeping all App ASTs resident would OOM)
|
||||
@@ -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,125 +1,124 @@
|
||||
import t, c
|
||||
from stdint import *
|
||||
import memhub
|
||||
import string
|
||||
import stdlib
|
||||
import mlirlite
|
||||
import w32.win32file as win32file
|
||||
import w32.win32base as win32base
|
||||
import w32.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.StubMergerStore as StubMergerStore
|
||||
import lib.Stub.StubMerger.StubMergerTopo as StubMergerTopo
|
||||
import lib.Stub.StubMerger.StubMergerIR as StubMergerIR
|
||||
import lib.Stub.StubMerger.StubMergerBuild as StubMergerBuild
|
||||
import lib.Stub.StubMerger as StubMerger
|
||||
|
||||
# StubMergerCollect - slice-path construction and stub-file SHA1 collection.
|
||||
# Functions live in this module; cross-module globals are accessed via StubMerger._xxx.
|
||||
|
||||
# _sliced_path - build a slice path (stdlib.malloc; caller frees; creates subdir when level>0).
|
||||
# Shards by Config.Sha1SliceLevel into SHA1-prefix subdirs; returns the full path or None on failure.
|
||||
def _sliced_path(temp_dir: str, td_len: sizet, sha1: str, ext: str) -> str:
|
||||
"""Build a SHA1-sliced path (stdlib.malloc; caller frees; creates the subdir)."""
|
||||
if temp_dir is None or sha1 is None or ext is None: return None
|
||||
sha1_len: sizet = string.strlen(sha1)
|
||||
ext_len: sizet = string.strlen(ext)
|
||||
|
||||
# Subdir prefix: reuse Config.slice_subdir (the authoritative SHA1 slice impl; mbuddy-allocated, no free)
|
||||
subdir_buf: str = Config.slice_subdir(sha1, Config.Sha1SliceLevel)
|
||||
sub_len: sizet = 0
|
||||
if subdir_buf is not None:
|
||||
sub_len = string.strlen(subdir_buf)
|
||||
|
||||
# Ensure subdir {temp_dir}/{subdir} exists.
|
||||
# Otherwise fileio.File(MODE.W) fails silently (file.closed==True) and the reader returns 0.
|
||||
dir_path_len: sizet = td_len + sub_len + 2
|
||||
dir_path: bytes = stdlib.malloc(dir_path_len)
|
||||
if dir_path is not None:
|
||||
viperlib.snprintf(dir_path, dir_path_len, "%s/%s", temp_dir, subdir_buf)
|
||||
BuildPipeline.ensure_dir(dir_path)
|
||||
stdlib.free(dir_path)
|
||||
|
||||
# Build the full path
|
||||
path: str = None
|
||||
path_len: sizet = 0
|
||||
if sub_len > 0:
|
||||
path_len = td_len + sub_len + sha1_len + ext_len + 4
|
||||
path = stdlib.malloc(path_len)
|
||||
if path is not None: viperlib.snprintf(path, path_len, "%s/%s/%s.%s", temp_dir, subdir_buf, sha1, ext)
|
||||
else:
|
||||
path_len = td_len + sha1_len + ext_len + 3
|
||||
path = stdlib.malloc(path_len)
|
||||
if path is not None: viperlib.snprintf(path, path_len, "%s/%s.%s", temp_dir, sha1, ext)
|
||||
|
||||
return path
|
||||
|
||||
# _load_includes_sha1_set - collect includes SHA1 from the global in-memory store.
|
||||
# Source is PopulateSha1MapStore (not _sha1_map.txt); filters stubs to avoid linking TransPyV's own lib/* stubs.
|
||||
def _load_includes_sha1_set(pool: memhub.MemBuddy*, temp_dir: str, sha1_set: str) -> int:
|
||||
"""Copy includes SHA1s from the global store into sha1_set; returns the count, -1 on error."""
|
||||
if sha1_set is None: return -1
|
||||
if StubMerger._sha1_store_arr is None: return -1
|
||||
count: int = 0
|
||||
for i in range(StubMerger._sha1_store_count):
|
||||
if count >= StubMerger.MAX_INCLUDES_SHA1: break
|
||||
idx: sizet = sizet(i) * 17
|
||||
string.strcpy(sha1_set + sizet(count) * 17, StubMerger._sha1_store_arr + idx)
|
||||
count += 1
|
||||
return count
|
||||
# _is_in_sha1_set - check whether a SHA1 is in the set.
|
||||
def _is_in_sha1_set(sha1: str, sha1_set: str, set_count: int) -> int:
|
||||
"""Whether sha1 is present in the set."""
|
||||
if sha1 is None or sha1_set is None: return 0
|
||||
for i in range(set_count):
|
||||
if string.strcmp(sha1_set + i * 17, sha1) == 0: return 1
|
||||
return 0
|
||||
# WriteIncludesSha1Map - write includes scan results to _sha1_map.txt (human-readable debug output only).
|
||||
# Internally the in-memory store filled by PopulateSha1MapStore is used; returns 0 on success, non-zero on failure.
|
||||
def WriteIncludesSha1Map(mb: memhub.MemBuddy*, temp_dir: str, scan_result: IncludesScanner.ScanResult*, filter_set: str, filter_count: int) -> int:
|
||||
"""Write the includes scan result to _sha1_map.txt (human-readable debug output)."""
|
||||
if temp_dir is None or scan_result is None: return 1
|
||||
|
||||
# Build the path temp_dir/_sha1_map.txt
|
||||
dir_len: sizet = string.strlen(temp_dir)
|
||||
map_path: bytes = stdlib.malloc(dir_len + 32)
|
||||
if map_path is None: return 1
|
||||
viperlib.snprintf(map_path, dir_len + 32, "%s/_sha1_map.txt", temp_dir)
|
||||
|
||||
# Open the file for writing (CREATE_ALWAYS)
|
||||
f: fileio.File* = fileio.File(map_path, fileio.MODE.W)
|
||||
if f.closed: return 1
|
||||
|
||||
# Write each include entry: {sha1}:includes/{rel_path}\n
|
||||
entry_size_w: sizet = IncludesScanner.FileEntry.__sizeof__()
|
||||
line_buf: bytes = stdlib.malloc(512)
|
||||
if line_buf is None:
|
||||
f.close()
|
||||
return 1
|
||||
|
||||
written_count: int = 0
|
||||
for i in range(scan_result.Count):
|
||||
ea_w: uint64 = uint64(scan_result.Entries) + i * entry_size_w
|
||||
ent_w: IncludesScanner.FileEntry* = (IncludesScanner.FileEntry*)(ea_w)
|
||||
if ent_w is None or ent_w.Sha1 is None or ent_w.RelPath is None: continue
|
||||
# When a filter set is provided, write only its entries
|
||||
if filter_set is not None and filter_count > 0:
|
||||
if _is_in_sha1_set(ent_w.Sha1, filter_set, filter_count) == 0: continue
|
||||
viperlib.snprintf(line_buf, 512, "%s:includes/%s\n", ent_w.Sha1, ent_w.RelPath)
|
||||
line_len_w: sizet = string.strlen(line_buf)
|
||||
f.write(line_buf, line_len_w)
|
||||
written_count += 1
|
||||
|
||||
f.close()
|
||||
return 0
|
||||
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.StubMergerStore as StubMergerStore
|
||||
import lib.Stub.StubMerger.StubMergerTopo as StubMergerTopo
|
||||
import lib.Stub.StubMerger.StubMergerIR as StubMergerIR
|
||||
import lib.Stub.StubMerger.StubMergerBuild as StubMergerBuild
|
||||
import lib.Stub.StubMerger as StubMerger
|
||||
|
||||
# StubMergerCollect - slice-path construction and stub-file SHA1 collection.
|
||||
# Functions live in this module; cross-module globals are accessed via StubMerger._xxx.
|
||||
|
||||
# _sliced_path - build a slice path (stdlib.malloc; caller frees; creates subdir when level>0).
|
||||
# Shards by Config.Sha1SliceLevel into SHA1-prefix subdirs; returns the full path or None on failure.
|
||||
def _sliced_path(temp_dir: str, td_len: sizet, sha1: str, ext: str) -> str:
|
||||
"""Build a SHA1-sliced path (stdlib.malloc; caller frees; creates the subdir)."""
|
||||
if temp_dir is None or sha1 is None or ext is None: return None
|
||||
sha1_len: sizet = string.strlen(sha1)
|
||||
ext_len: sizet = string.strlen(ext)
|
||||
|
||||
# Subdir prefix: reuse Config.slice_subdir (the authoritative SHA1 slice impl; mbuddy-allocated, no free)
|
||||
subdir_buf: str = Config.slice_subdir(sha1, Config.Sha1SliceLevel)
|
||||
sub_len: sizet = 0
|
||||
if subdir_buf is not None:
|
||||
sub_len = string.strlen(subdir_buf)
|
||||
|
||||
# Ensure subdir {temp_dir}/{subdir} exists.
|
||||
# Otherwise fileio.File(MODE.W) fails silently (file.closed==True) and the reader returns 0.
|
||||
dir_path_len: sizet = td_len + sub_len + 2
|
||||
dir_path: bytes = stdlib.malloc(dir_path_len)
|
||||
if dir_path is not None:
|
||||
viperlib.snprintf(dir_path, dir_path_len, "%s/%s", temp_dir, subdir_buf)
|
||||
BuildPipeline.ensure_dir(dir_path)
|
||||
stdlib.free(dir_path)
|
||||
|
||||
# Build the full path
|
||||
path: str = None
|
||||
path_len: sizet = 0
|
||||
if sub_len > 0:
|
||||
path_len = td_len + sub_len + sha1_len + ext_len + 4
|
||||
path = stdlib.malloc(path_len)
|
||||
if path is not None: viperlib.snprintf(path, path_len, "%s/%s/%s.%s", temp_dir, subdir_buf, sha1, ext)
|
||||
else:
|
||||
path_len = td_len + sha1_len + ext_len + 3
|
||||
path = stdlib.malloc(path_len)
|
||||
if path is not None: viperlib.snprintf(path, path_len, "%s/%s.%s", temp_dir, sha1, ext)
|
||||
|
||||
return path
|
||||
|
||||
# _load_includes_sha1_set - collect includes SHA1 from the global in-memory store.
|
||||
# Source is PopulateSha1MapStore (not _sha1_map.txt); filters stubs to avoid linking ViperC's own lib/* stubs.
|
||||
def _load_includes_sha1_set(pool: memhub.MemBuddy*, temp_dir: str, sha1_set: str) -> int:
|
||||
"""Copy includes SHA1s from the global store into sha1_set; returns the count, -1 on error."""
|
||||
if sha1_set is None: return -1
|
||||
if StubMerger._sha1_store_arr is None: return -1
|
||||
count: int = 0
|
||||
for i in range(int(StubMerger._sha1_store_count)):
|
||||
if count >= StubMerger.MAX_INCLUDES_SHA1: break
|
||||
idx: sizet = sizet(i) * 17
|
||||
string.strcpy(sha1_set + sizet(count) * 17, StubMerger._sha1_store_arr + idx)
|
||||
count += 1
|
||||
return count
|
||||
# _is_in_sha1_set - check whether a SHA1 is in the set.
|
||||
def _is_in_sha1_set(sha1: str, sha1_set: str, set_count: int) -> int:
|
||||
"""Whether sha1 is present in the set."""
|
||||
if sha1 is None or sha1_set is None: return 0
|
||||
for i in range(int(set_count)):
|
||||
if string.strcmp(sha1_set + i * 17, sha1) == 0: return 1
|
||||
return 0
|
||||
# WriteIncludesSha1Map - write includes scan results to _sha1_map.txt (human-readable debug output only).
|
||||
# Internally the in-memory store filled by PopulateSha1MapStore is used; returns 0 on success, non-zero on failure.
|
||||
def WriteIncludesSha1Map(mb: memhub.MemBuddy*, temp_dir: str, scan_result: IncludesScanner.ScanResult*, filter_set: str, filter_count: int) -> int:
|
||||
"""Write the includes scan result to _sha1_map.txt (human-readable debug output)."""
|
||||
if temp_dir is None or scan_result is None: return 1
|
||||
|
||||
# Build the path temp_dir/_sha1_map.txt
|
||||
dir_len: sizet = string.strlen(temp_dir)
|
||||
map_path: bytes = stdlib.malloc(dir_len + 32)
|
||||
if map_path is None: return 1
|
||||
viperlib.snprintf(map_path, dir_len + 32, "%s/_sha1_map.txt", temp_dir)
|
||||
|
||||
# Open the file for writing (CREATE_ALWAYS)
|
||||
f: fileio.File* = fileio.File(map_path, fileio.MODE.W)
|
||||
if f.closed: return 1
|
||||
|
||||
# Write each include entry: {sha1}:includes/{rel_path}\n
|
||||
entry_size_w: sizet = IncludesScanner.FileEntry.__sizeof__()
|
||||
line_buf: bytes = stdlib.malloc(512)
|
||||
if line_buf is None:
|
||||
f.close()
|
||||
return 1
|
||||
|
||||
written_count: int = 0
|
||||
for i in range(int(scan_result.Count)):
|
||||
ea_w: uint64 = uint64(scan_result.Entries) + i * entry_size_w
|
||||
ent_w: IncludesScanner.FileEntry* = (IncludesScanner.FileEntry*)(ea_w)
|
||||
if ent_w is None or ent_w.Sha1 is None or ent_w.RelPath is None: continue
|
||||
# When a filter set is provided, write only its entries
|
||||
if filter_set is not None and filter_count > 0:
|
||||
if _is_in_sha1_set(ent_w.Sha1, filter_set, filter_count) == 0: continue
|
||||
viperlib.snprintf(line_buf, 512, "%s:includes/%s\n", ent_w.Sha1, ent_w.RelPath)
|
||||
line_len_w: sizet = string.strlen(line_buf)
|
||||
f.write(line_buf, line_len_w)
|
||||
written_count += 1
|
||||
|
||||
f.close()
|
||||
return 0
|
||||
@@ -1,13 +1,12 @@
|
||||
import lib.core.Gen.GenApi as GenApi
|
||||
import t, c
|
||||
from stdint import *
|
||||
import memhub
|
||||
import string
|
||||
import stdlib
|
||||
import mlirlite
|
||||
import w32.win32file as win32file
|
||||
import w32.win32base as win32base
|
||||
import w32.fileio as fileio
|
||||
import win32.win32file as win32file
|
||||
import win32.win32base as win32base
|
||||
import win32.fileio as fileio
|
||||
import viperlib
|
||||
import sys
|
||||
import ast
|
||||
@@ -186,7 +185,7 @@ def _BuildIncludesSha1Map(temp_dir: str, td_len: sizet, sha1_list: str, mod_list
|
||||
if sha1_list is None or mod_list is None: return 0
|
||||
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None: return 0
|
||||
count: int = 0
|
||||
for i in range(StubMerger._sha1_store_count):
|
||||
for i in range(int(StubMerger._sha1_store_count)):
|
||||
if count >= StubMerger.MAX_INCLUDES: break
|
||||
idx: sizet = sizet(i) * 17
|
||||
idx2: sizet = sizet(i) * 64
|
||||
@@ -201,7 +200,7 @@ def _FindSha1ByModName(sha1_arr: str, mod_arr: str, count: int, mod_name: str) -
|
||||
if sha1_arr is None or mod_arr is None or mod_name is None: return None
|
||||
if count <= 0: return None
|
||||
# Pass 1: exact match
|
||||
for i in range(count):
|
||||
for i in range(int(count)):
|
||||
idx: sizet = sizet(i) * 64
|
||||
# Fast path: strcmp equality implies equal first char, so skip strcmp when it differs
|
||||
# (hot spot: this function strcmp-scans the whole table per lookup).
|
||||
@@ -213,7 +212,7 @@ def _FindSha1ByModName(sha1_arr: str, mod_arr: str, count: int, mod_name: str) -
|
||||
init_buf: bytes = stdlib.malloc(nm_len + 12)
|
||||
if init_buf is not None:
|
||||
viperlib.snprintf(init_buf, nm_len + 12, "%s.__init__", mod_name)
|
||||
for i in range(count):
|
||||
for i in range(int(count)):
|
||||
idx2: sizet = sizet(i) * 64
|
||||
if mod_arr[idx2] == init_buf[0] and string.strcmp(mod_arr + idx2, init_buf) == 0:
|
||||
sidx2: sizet = sizet(i) * 17
|
||||
@@ -226,7 +225,7 @@ def _FindSha1ByModName(sha1_arr: str, mod_arr: str, count: int, mod_name: str) -
|
||||
def _AddSha1ToSet(set_buf: str, count: int, sha1: str) -> int:
|
||||
"""Add a SHA1 to the set (deduped) and return the new count."""
|
||||
if set_buf is None or sha1 is None: return count
|
||||
for i in range(count):
|
||||
for i in range(int(count)):
|
||||
idx: sizet = sizet(i) * 17
|
||||
if string.strcmp(set_buf + idx, sha1) == 0: return count
|
||||
if count < StubMerger.MAX_INCLUDES_SHA1:
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import t, c
|
||||
from stdint import *
|
||||
import memhub
|
||||
import string
|
||||
import stdlib
|
||||
import mlirlite
|
||||
import w32.win32file as win32file
|
||||
import w32.win32base as win32base
|
||||
import w32.fileio as fileio
|
||||
import win32.win32file as win32file
|
||||
import win32.win32base as win32base
|
||||
import win32.fileio as fileio
|
||||
import viperlib
|
||||
import sys
|
||||
import ast
|
||||
@@ -136,17 +135,19 @@ def PopulateSha1MapStore(scan_result: IncludesScanner.ScanResult*) -> int:
|
||||
if StubMerger._sha1_store_incdir is not None: stdlib.free(StubMerger._sha1_store_incdir)
|
||||
StubMerger._sha1_store_arr = stdlib.malloc(StubMerger.MAX_INCLUDES * 17)
|
||||
StubMerger._sha1_store_mod = stdlib.malloc(StubMerger.MAX_INCLUDES * 64)
|
||||
StubMerger._sha1_store_rel = stdlib.malloc(StubMerger.MAX_INCLUDES * StubMerger.MAX_REL_PATH_LEN)
|
||||
StubMerger._sha1_store_incdir = stdlib.malloc(StubMerger.MAX_INCLUDES * StubMerger.MAX_INC_DIR_LEN)
|
||||
# 取消 C 整数提升后:MAX_INCLUDES/MAX_REL_PATH_LEN/MAX_INC_DIR_LEN 这些模块属性 CDefine 按最小位宽物化为 i16,
|
||||
# 两侧都 i16 的乘积(1024*256 / 1024*512)会静默回绕成 0;显式 sizet() 把乘数加宽到 64 位,结果与旧「提升到 int」一致。
|
||||
StubMerger._sha1_store_rel = stdlib.malloc(sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_REL_PATH_LEN)
|
||||
StubMerger._sha1_store_incdir = stdlib.malloc(sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_INC_DIR_LEN)
|
||||
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None or StubMerger._sha1_store_rel is None or StubMerger._sha1_store_incdir is None: return -1
|
||||
string.memset(StubMerger._sha1_store_arr, 0, StubMerger.MAX_INCLUDES * 17)
|
||||
string.memset(StubMerger._sha1_store_mod, 0, StubMerger.MAX_INCLUDES * 64)
|
||||
string.memset(StubMerger._sha1_store_rel, 0, StubMerger.MAX_INCLUDES * StubMerger.MAX_REL_PATH_LEN)
|
||||
string.memset(StubMerger._sha1_store_incdir, 0, StubMerger.MAX_INCLUDES * StubMerger.MAX_INC_DIR_LEN)
|
||||
string.memset(StubMerger._sha1_store_rel, 0, sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_REL_PATH_LEN)
|
||||
string.memset(StubMerger._sha1_store_incdir, 0, sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_INC_DIR_LEN)
|
||||
StubMerger._sha1_store_count = 0
|
||||
p_bad: int = 0
|
||||
entry_size_p: sizet = IncludesScanner.FileEntry.__sizeof__()
|
||||
for i in range(scan_result.Count):
|
||||
for i in range(int(scan_result.Count)):
|
||||
if StubMerger._sha1_store_count >= StubMerger.MAX_INCLUDES: VLogger.error("includes SHA1 store exceeded the limit; aborting", "StubMerger")
|
||||
ea: uint64 = uint64(scan_result.Entries) + i * entry_size_p
|
||||
ent: IncludesScanner.FileEntry* = IncludesScanner.FileEntry*(ea)
|
||||
@@ -198,13 +199,14 @@ def AppendToSha1MapStore(sha1: str, rel_path: str) -> int:
|
||||
if StubMerger._sha1_store_arr is None:
|
||||
StubMerger._sha1_store_arr = stdlib.malloc(StubMerger.MAX_INCLUDES * 17)
|
||||
StubMerger._sha1_store_mod = stdlib.malloc(StubMerger.MAX_INCLUDES * 64)
|
||||
StubMerger._sha1_store_rel = stdlib.malloc(StubMerger.MAX_INCLUDES * StubMerger.MAX_REL_PATH_LEN)
|
||||
StubMerger._sha1_store_incdir = stdlib.malloc(StubMerger.MAX_INCLUDES * StubMerger.MAX_INC_DIR_LEN)
|
||||
# sizet(): 取消整数提升后 i16(MAX_INCLUDES)*i16(长度) 会回绕,显式加宽到 64 位
|
||||
StubMerger._sha1_store_rel = stdlib.malloc(sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_REL_PATH_LEN)
|
||||
StubMerger._sha1_store_incdir = stdlib.malloc(sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_INC_DIR_LEN)
|
||||
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None or StubMerger._sha1_store_rel is None or StubMerger._sha1_store_incdir is None: return 1
|
||||
string.memset(StubMerger._sha1_store_arr, 0, StubMerger.MAX_INCLUDES * 17)
|
||||
string.memset(StubMerger._sha1_store_mod, 0, StubMerger.MAX_INCLUDES * 64)
|
||||
string.memset(StubMerger._sha1_store_rel, 0, StubMerger.MAX_INCLUDES * StubMerger.MAX_REL_PATH_LEN)
|
||||
string.memset(StubMerger._sha1_store_incdir, 0, StubMerger.MAX_INCLUDES * StubMerger.MAX_INC_DIR_LEN)
|
||||
string.memset(StubMerger._sha1_store_rel, 0, sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_REL_PATH_LEN)
|
||||
string.memset(StubMerger._sha1_store_incdir, 0, sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_INC_DIR_LEN)
|
||||
StubMerger._sha1_store_count = 0
|
||||
if StubMerger._sha1_store_count >= StubMerger.MAX_INCLUDES: return 1
|
||||
# module name: rel_path "lib/core/StubMerger.vp" -> "lib.core.StubMerger"
|
||||
@@ -216,7 +218,7 @@ def AppendToSha1MapStore(sha1: str, rel_path: str) -> int:
|
||||
if mod_name is None: return 1
|
||||
# Dedup only when both SHA1 and module name match: the same SHA1 may be registered first
|
||||
# under a full-path name; the App short name must stay or exact lookup breaks.
|
||||
for i in range(StubMerger._sha1_store_count):
|
||||
for i in range(int(StubMerger._sha1_store_count)):
|
||||
sidx_d: sizet = sizet(i) * 17
|
||||
if string.strcmp(StubMerger._sha1_store_arr + sidx_d, sha1) == 0:
|
||||
midx_d: sizet = sizet(i) * 64
|
||||
@@ -255,17 +257,18 @@ def AddIncludeToSha1MapStore(sha1: str, rel_path: str, inc_dir: str, mod_name: s
|
||||
if StubMerger._sha1_store_arr is None:
|
||||
StubMerger._sha1_store_arr = stdlib.malloc(StubMerger.MAX_INCLUDES * 17)
|
||||
StubMerger._sha1_store_mod = stdlib.malloc(StubMerger.MAX_INCLUDES * 64)
|
||||
StubMerger._sha1_store_rel = stdlib.malloc(StubMerger.MAX_INCLUDES * StubMerger.MAX_REL_PATH_LEN)
|
||||
StubMerger._sha1_store_incdir = stdlib.malloc(StubMerger.MAX_INCLUDES * StubMerger.MAX_INC_DIR_LEN)
|
||||
# sizet(): 取消整数提升后 i16(MAX_INCLUDES)*i16(长度) 会回绕,显式加宽到 64 位
|
||||
StubMerger._sha1_store_rel = stdlib.malloc(sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_REL_PATH_LEN)
|
||||
StubMerger._sha1_store_incdir = stdlib.malloc(sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_INC_DIR_LEN)
|
||||
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None or StubMerger._sha1_store_rel is None or StubMerger._sha1_store_incdir is None: return 1
|
||||
string.memset(StubMerger._sha1_store_arr, 0, StubMerger.MAX_INCLUDES * 17)
|
||||
string.memset(StubMerger._sha1_store_mod, 0, StubMerger.MAX_INCLUDES * 64)
|
||||
string.memset(StubMerger._sha1_store_rel, 0, StubMerger.MAX_INCLUDES * StubMerger.MAX_REL_PATH_LEN)
|
||||
string.memset(StubMerger._sha1_store_incdir, 0, StubMerger.MAX_INCLUDES * StubMerger.MAX_INC_DIR_LEN)
|
||||
string.memset(StubMerger._sha1_store_rel, 0, sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_REL_PATH_LEN)
|
||||
string.memset(StubMerger._sha1_store_incdir, 0, sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_INC_DIR_LEN)
|
||||
StubMerger._sha1_store_count = 0
|
||||
if StubMerger._sha1_store_count >= StubMerger.MAX_INCLUDES: return 1
|
||||
# dedup when both SHA1 and module name match (aligned with AppendToSha1MapStore)
|
||||
for i in range(StubMerger._sha1_store_count):
|
||||
for i in range(int(StubMerger._sha1_store_count)):
|
||||
sidx_d: sizet = sizet(i) * 17
|
||||
if string.strcmp(StubMerger._sha1_store_arr + sidx_d, sha1) == 0:
|
||||
midx_d: sizet = sizet(i) * 64
|
||||
@@ -343,7 +346,7 @@ def RegisterGenericOwner(cls_name: str, mod_sha1: str) -> int:
|
||||
string.memset(_generic_owner_sha1s, 0, GENERIC_OWNER_MAX * 17)
|
||||
_generic_owner_count = 0
|
||||
i_go: int
|
||||
for i_go in range(_generic_owner_count):
|
||||
for i_go in range(int(_generic_owner_count)):
|
||||
if string.strcmp(_generic_owner_names + sizet(i_go) * 64, cls_name) == 0:
|
||||
if string.strcmp(_generic_owner_sha1s + sizet(i_go) * 17, mod_sha1) == 0: return 1
|
||||
return 0
|
||||
@@ -358,7 +361,7 @@ def LookupGenericOwnerSha1(cls_name: str) -> str:
|
||||
"""Look up the defining-module SHA1 by generic class name (internal pointer, no free)"""
|
||||
if cls_name is None or _generic_owner_names is None: return None
|
||||
i_lg: int
|
||||
for i_lg in range(_generic_owner_count):
|
||||
for i_lg in range(int(_generic_owner_count)):
|
||||
if string.strcmp(_generic_owner_names + sizet(i_lg) * 64, cls_name) == 0: return _generic_owner_sha1s + sizet(i_lg) * 17
|
||||
return None
|
||||
# ============================================================
|
||||
@@ -371,7 +374,7 @@ def LookupSha1ByModNameSuffix(mod_suffix: str) -> str:
|
||||
suffix_len_sf: sizet = string.strlen(mod_suffix)
|
||||
if suffix_len_sf == 0: return None
|
||||
i_sf: int
|
||||
for i_sf in range(StubMerger._sha1_store_count):
|
||||
for i_sf in range(int(StubMerger._sha1_store_count)):
|
||||
idx_sf: sizet = sizet(i_sf) * 64
|
||||
mod_name_sf: str = StubMerger._sha1_store_mod + idx_sf
|
||||
mod_len_sf: sizet = string.strlen(mod_name_sf)
|
||||
@@ -416,7 +419,7 @@ def LookupSha1ByAlias(from_imports: str, alias: str) -> str:
|
||||
if colon < total_len and from_imports[colon] == ':' and local_len == alias_len:
|
||||
# compare local_name == alias
|
||||
Matched: int = 1
|
||||
for li in range(alias_len):
|
||||
for li in range(int(alias_len)):
|
||||
if from_imports[ci + li] != alias[li]:
|
||||
Matched = 0
|
||||
break
|
||||
@@ -457,7 +460,7 @@ def LookupModNameByAlias(from_imports: str, alias: str) -> str:
|
||||
if colon < total_len and from_imports[colon] == ':' and local_len == alias_len:
|
||||
# compare local_name == alias
|
||||
Matched: int = 1
|
||||
for li in range(alias_len):
|
||||
for li in range(int(alias_len)):
|
||||
if from_imports[ci + li] != alias[li]:
|
||||
Matched = 0
|
||||
break
|
||||
@@ -484,14 +487,15 @@ def PopulateIncludesDeps(sha1: str, deps_str: str) -> int:
|
||||
# lazy init
|
||||
if StubMerger._includes_deps_sha1 is None:
|
||||
StubMerger._includes_deps_sha1 = stdlib.malloc(StubMerger.MAX_INCLUDES * 17)
|
||||
StubMerger._includes_deps_str = stdlib.malloc(StubMerger.MAX_INCLUDES * StubMerger.MAX_DEPS_STR_LEN)
|
||||
# sizet(): 取消整数提升后 i16(MAX_INCLUDES)*i16(MAX_DEPS_STR_LEN) 会回绕成 0,显式加宽到 64 位
|
||||
StubMerger._includes_deps_str = stdlib.malloc(sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_DEPS_STR_LEN)
|
||||
if StubMerger._includes_deps_sha1 is None or StubMerger._includes_deps_str is None: return 1
|
||||
string.memset(StubMerger._includes_deps_sha1, 0, StubMerger.MAX_INCLUDES * 17)
|
||||
string.memset(StubMerger._includes_deps_str, 0, StubMerger.MAX_INCLUDES * StubMerger.MAX_DEPS_STR_LEN)
|
||||
string.memset(StubMerger._includes_deps_str, 0, sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_DEPS_STR_LEN)
|
||||
StubMerger._includes_deps_count = 0
|
||||
if StubMerger._includes_deps_count >= StubMerger.MAX_INCLUDES: return 1
|
||||
# dedup: skip if SHA1 already present
|
||||
for i in range(StubMerger._includes_deps_count):
|
||||
for i in range(int(StubMerger._includes_deps_count)):
|
||||
sidx: sizet = sizet(i) * 17
|
||||
if string.strcmp(StubMerger._includes_deps_sha1 + sidx, sha1) == 0: return 0
|
||||
# write SHA1
|
||||
@@ -512,7 +516,7 @@ def LookupIncludesDeps(sha1: str) -> str:
|
||||
"""Look up includes-file dependencies in memory"""
|
||||
if sha1 is None or StubMerger._includes_deps_sha1 is None: return None
|
||||
if StubMerger._includes_deps_count <= 0: return None
|
||||
for i in range(StubMerger._includes_deps_count):
|
||||
for i in range(int(StubMerger._includes_deps_count)):
|
||||
sidx: sizet = sizet(i) * 17
|
||||
if string.strcmp(StubMerger._includes_deps_sha1 + sidx, sha1) == 0:
|
||||
idx2: sizet = sizet(i) * StubMerger.MAX_DEPS_STR_LEN
|
||||
@@ -550,7 +554,7 @@ def LookupSha1RelPath(sha1: str) -> str:
|
||||
"""Return the read-only rel-path pointer for a SHA1, or None if missing"""
|
||||
if sha1 is None: return None
|
||||
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_rel is None: return None
|
||||
for i in range(StubMerger._sha1_store_count):
|
||||
for i in range(int(StubMerger._sha1_store_count)):
|
||||
sidx: sizet = sizet(i) * 17
|
||||
if string.strcmp(StubMerger._sha1_store_arr + sidx, sha1) == 0:
|
||||
ridx: sizet = sizet(i) * StubMerger.MAX_REL_PATH_LEN
|
||||
@@ -564,7 +568,7 @@ def LookupModNameBySha1(sha1: str) -> str:
|
||||
"""Return the read-only module-name pointer for a SHA1, or None if missing"""
|
||||
if sha1 is None or sha1[0] == '\0': return None
|
||||
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None: return None
|
||||
for i in range(StubMerger._sha1_store_count):
|
||||
for i in range(int(StubMerger._sha1_store_count)):
|
||||
sidx_mb: sizet = sizet(i) * 17
|
||||
if string.strcmp(StubMerger._sha1_store_arr + sidx_mb, sha1) == 0:
|
||||
midx_mb: sizet = sizet(i) * 64
|
||||
|
||||
@@ -1,29 +1,11 @@
|
||||
import t, c
|
||||
from stdint import *
|
||||
import memhub
|
||||
import string
|
||||
import stdlib
|
||||
import mlirlite
|
||||
import w32.win32file as win32file
|
||||
import w32.win32base as win32base
|
||||
import w32.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)
|
||||
@@ -43,7 +25,7 @@ def BuildTopoSortedIndices(store_count: int, store_sha1_arr: bytes, store_mod_ar
|
||||
return 0
|
||||
# Zero indegree/queue/rel_idx (malloc memory is uninitialized)
|
||||
zi: int = 0
|
||||
for zi in range(store_count + 1):
|
||||
for zi in range(int(store_count + 1)):
|
||||
indegree[zi] = 0
|
||||
queue[zi] = 0
|
||||
rel_idx[zi] = 0
|
||||
@@ -51,18 +33,18 @@ def BuildTopoSortedIndices(store_count: int, store_sha1_arr: bytes, store_mod_ar
|
||||
rel_count: int = 0
|
||||
i_t: int
|
||||
if reachable_set is None or reachable_count <= 0:
|
||||
for i_t in range(store_count):
|
||||
for i_t in range(int(store_count)):
|
||||
rel_idx[rel_count] = i_t
|
||||
rel_count += 1
|
||||
else:
|
||||
for i_t in range(store_count):
|
||||
for i_t in range(int(store_count)):
|
||||
if StubMergerCollect._is_in_sha1_set(store_sha1_arr + sizet(i_t) * 17, reachable_set, reachable_count) != 0:
|
||||
rel_idx[rel_count] = i_t
|
||||
rel_count += 1
|
||||
|
||||
# Compute indegree: number of direct deps of m within the reachable set
|
||||
for i_r in range(rel_count):
|
||||
si: int = rel_idx[i_r]
|
||||
for i_r in range(int(rel_count)):
|
||||
si: int = int(rel_idx[i_r])
|
||||
deps_si: str = StubMergerStore.LookupIncludesDeps(store_sha1_arr + sizet(si) * 17)
|
||||
if deps_si is None: continue
|
||||
dl_si: sizet = string.strlen(deps_si)
|
||||
@@ -81,13 +63,13 @@ def BuildTopoSortedIndices(store_count: int, store_sha1_arr: bytes, store_mod_ar
|
||||
stdlib.free(dname_buf)
|
||||
if dsha1 is not None and StubMergerCollect._is_in_sha1_set(dsha1, reachable_set, reachable_count) != 0:
|
||||
# Use an intermediate int to work around a union-pointer arithmetic codegen bug
|
||||
indg: int = indegree[si]
|
||||
indg: int = int(indegree[si])
|
||||
indg = indg + 1
|
||||
indegree[si] = indg
|
||||
# Kahn: nodes with indegree 0 (no reachable deps) are output first
|
||||
q_tail: int = 0
|
||||
for i_r in range(rel_count):
|
||||
si2: int = rel_idx[i_r]
|
||||
for i_r in range(int(rel_count)):
|
||||
si2: int = int(rel_idx[i_r])
|
||||
if indegree[si2] == 0:
|
||||
queue[q_tail] = si2
|
||||
q_tail += 1
|
||||
@@ -96,14 +78,14 @@ def BuildTopoSortedIndices(store_count: int, store_sha1_arr: bytes, store_mod_ar
|
||||
# and a for range bound is evaluated once, so newly queued modules would be skipped.
|
||||
q_head: int = 0
|
||||
while q_head < q_tail:
|
||||
m: int = queue[q_head]
|
||||
m: int = int(queue[q_head])
|
||||
sorted_idx[out_count] = m
|
||||
out_count += 1
|
||||
# Decrement indegree of reachable modules that depend on m
|
||||
mod_m: str = store_mod_arr + sizet(m) * 64
|
||||
mod_m_len: sizet = string.strlen(mod_m)
|
||||
for i_r in range(rel_count):
|
||||
si3: int = rel_idx[i_r]
|
||||
for i_r in range(int(rel_count)):
|
||||
si3: int = int(rel_idx[i_r])
|
||||
if indegree[si3] == 0: continue
|
||||
deps_si3: str = StubMergerStore.LookupIncludesDeps(store_sha1_arr + sizet(si3) * 17)
|
||||
if deps_si3 is None: continue
|
||||
@@ -119,7 +101,7 @@ def BuildTopoSortedIndices(store_count: int, store_sha1_arr: bytes, store_mod_ar
|
||||
if dlen3 == mod_m_len and string.strncmp(deps_si3 + dstart3, mod_m, mod_m_len) == 0: found_dep = 1
|
||||
if found_dep == 1:
|
||||
# Use an intermediate int decrement to work around a union-pointer arithmetic codegen bug
|
||||
indg3: int = indegree[si3]
|
||||
indg3: int = int(indegree[si3])
|
||||
indg3 = indg3 - 1
|
||||
indegree[si3] = indg3
|
||||
if indegree[si3] == 0:
|
||||
@@ -127,8 +109,8 @@ def BuildTopoSortedIndices(store_count: int, store_sha1_arr: bytes, store_mod_ar
|
||||
q_tail += 1
|
||||
q_head += 1
|
||||
# On cycles, append remaining entries in store order to avoid skipping compilation
|
||||
for i_r in range(rel_count):
|
||||
si4: int = rel_idx[i_r]
|
||||
for i_r in range(int(rel_count)):
|
||||
si4: int = int(rel_idx[i_r])
|
||||
if indegree[si4] > 0:
|
||||
sorted_idx[out_count] = si4
|
||||
out_count += 1
|
||||
|
||||
@@ -1,22 +1,5 @@
|
||||
import t, c
|
||||
from stdint import *
|
||||
import memhub
|
||||
import string
|
||||
import stdlib
|
||||
import mlirlite
|
||||
import w32.win32file as win32file
|
||||
import w32.win32base as win32base
|
||||
import w32.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,4 +1,3 @@
|
||||
import t, c
|
||||
import memhub
|
||||
|
||||
|
||||
|
||||
+25
-9
@@ -1,9 +1,8 @@
|
||||
import t, c
|
||||
from stdint import *
|
||||
import string
|
||||
import stdio
|
||||
import stdlib
|
||||
import w32.fileio as fileio
|
||||
import win32.fileio as fileio
|
||||
import lib.core.VLogger as VLogger
|
||||
|
||||
|
||||
@@ -38,10 +37,10 @@ define DIAG_MAX_FRAMES = 32 # 调用链上限(与 TypeGenMixin.TRACE_MAX 对
|
||||
# (import 触发依赖加载 / 泛型特化 / 函数体翻译)时的触发点,
|
||||
# 用于"从错误发生点向外展示触发过程"。
|
||||
# 所有字符串都用 stdlib 深拷贝:编译期池会回绕,不能借用池内指针。
|
||||
class DiagEntry:
|
||||
struct DiagEntry:
|
||||
# 字段名不要与"本模块 import 的类名"重名:TPC 的类型/符号表按**非限定名**索引
|
||||
# (Gen.structs / SymbolTable / _mangle_func_name),曾把本字段的 `File` 与
|
||||
# `w32.fileio.File` 撞在一起,导致同模块 `fileio.File(...)` 的构造调用把
|
||||
# `win32.fileio.File` 撞在一起,导致同模块 `fileio.File(...)` 的构造调用把
|
||||
# `File.__before_init__` 的 self 形参解析成 `i8**`(llvmlite: "Type of #1 arg
|
||||
# mismatch: i8** != %"...fileio.File"*")。故此处用 FileName 而非 File。
|
||||
FileName: str # 当前帧(错误点)文件
|
||||
@@ -138,19 +137,36 @@ def add_frame(ent: DiagEntry*, file_name: str, lineno: int) -> int:
|
||||
|
||||
|
||||
# emit - 打印一条诊断:头 → 调用链(按 direction) → 当前帧(caret) → 类别: 消息
|
||||
# 去重:① 连续两个位置完全相同的栈帧只印一次;② 与“错误点帧”位置相同的栈帧不再重复印
|
||||
#(此前常见 `_start` 定义行被打印两遍:栈内最内层帧与错误点帧落在同一行)。
|
||||
def emit(ent: DiagEntry*, direction: int) -> int:
|
||||
if ent is None: return 0
|
||||
emit_header()
|
||||
last_file: str = ""
|
||||
last_line: int = -1
|
||||
if ent.FrameFiles is not None and ent.FrameLines is not None:
|
||||
if direction == DIAG_DIR_DOWN:
|
||||
i_d: int = ent.FrameCount - 1
|
||||
while i_d >= 0:
|
||||
emit_frame(str(ent.FrameFiles[i_d]), int(ent.FrameLines[i_d]))
|
||||
ff: str = str(ent.FrameFiles[i_d])
|
||||
ll: int = int(ent.FrameLines[i_d])
|
||||
dup: int = 1 if last_file != "" and string.strcmp(ff, last_file) == 0 and ll == last_line else 0
|
||||
if dup == 0:
|
||||
emit_frame(ff, ll)
|
||||
last_file = ff
|
||||
last_line = ll
|
||||
i_d -= 1
|
||||
else:
|
||||
i_u: int = 0
|
||||
while i_u < ent.FrameCount:
|
||||
emit_frame(str(ent.FrameFiles[i_u]), int(ent.FrameLines[i_u]))
|
||||
ff2: str = str(ent.FrameFiles[i_u])
|
||||
ll2: int = int(ent.FrameLines[i_u])
|
||||
dup2: int = 1 if last_file != "" and string.strcmp(ff2, last_file) == 0 and ll2 == last_line else 0
|
||||
cur_same: int = 1 if ent.FileName is not None and string.strcmp(ff2, ent.FileName) == 0 and ll2 == ent.Line else 0
|
||||
if dup2 == 0 and cur_same == 0:
|
||||
emit_frame(ff2, ll2)
|
||||
last_file = ff2
|
||||
last_line = ll2
|
||||
i_u += 1
|
||||
emit_current(ent.FileName, ent.Line, ent.ColStart, ent.ColEnd)
|
||||
# 泄漏点帧紧跟在"错误点"之后(往下再一行),再印类别: 消息
|
||||
@@ -203,7 +219,7 @@ def read_source_line(file_path: str, lineno: int) -> str:
|
||||
# Scan line by line for the target lineno
|
||||
cur_line: int = 1
|
||||
line_start: sizet = 0
|
||||
for i in range(bytes_read):
|
||||
for i in range(int(bytes_read)):
|
||||
if buf[i] == '\n':
|
||||
if cur_line == lineno:
|
||||
line_len: sizet = i - line_start
|
||||
@@ -273,7 +289,7 @@ def emit_current(file_name: str, lineno: int, col_start: int, col_end: int) -> i
|
||||
if col_start > 0 and col_end >= col_start:
|
||||
stdio.printf(" ")
|
||||
j: int
|
||||
for j in range(col_end):
|
||||
for j in range(int(col_end)):
|
||||
if j >= col_start - 1 and j < col_end: stdio.printf("^")
|
||||
else: stdio.printf(" ")
|
||||
stdio.printf("\n")
|
||||
@@ -304,7 +320,7 @@ def emit_parse(file_path: str, err_line: int, err_col: int, err_msg: str) -> int
|
||||
# 0-based, so 4-space indent + err_col spaces lands on the token's first char)
|
||||
if err_col > 0:
|
||||
stdio.printf(" ")
|
||||
for _ in range(err_col): stdio.printf(" ")
|
||||
for _ in range(int(err_col)): stdio.printf(" ")
|
||||
stdio.printf("^\n")
|
||||
stdlib.free(src)
|
||||
else: stdio.printf(' File "%s"\n', file_path)
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,5 +1,4 @@
|
||||
import lib.core.Gen.GenTypes as GenTypes
|
||||
import t, c
|
||||
from stdint import *
|
||||
import ast
|
||||
import mlirlite
|
||||
@@ -635,8 +634,8 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
|
||||
# M7 frame pool: i64 head (0=empty); first 8 bytes hold next (state/done zeroed on reuse)
|
||||
pool_gv: mlirlite.GlobalVariable* = self._get_coro_pool(pool, mod, trans, name)
|
||||
# GlobalVariable is not a Value: build a Value view (Ty=Ptr(i64), Name="@coro_pool_<name>")
|
||||
pool_ref_name: str = pool.alloc(96)
|
||||
if pool_ref_name is not None: viperlib.snprintf(pool_ref_name, 96, "@coro_pool_%s", name)
|
||||
pool_ref_name: str = pool.alloc(string.strlen(name) + 16)
|
||||
if pool_ref_name is not None: viperlib.snprintf(pool_ref_name, string.strlen(name) + 16, "@coro_pool_%s", name)
|
||||
pool_ref: mlirlite.Value* = mlirlite.SSAValue(pool, i64_ptr_ty, pool_ref_name)
|
||||
pool_head: mlirlite.Value* = mlirlite.build_load(builder, i64_ty, pool_ref)
|
||||
if pool_head is None: return 0
|
||||
@@ -700,8 +699,8 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
|
||||
# M7: per-frame-type global free-list pool head (i64, 0 = empty)
|
||||
def _get_coro_pool(self, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, trans: HT.Translator*, name: str) -> mlirlite.GlobalVariable*:
|
||||
"""Get or create the coro_pool_<name> global (i64 pool head)"""
|
||||
pn_buf: str = pool.alloc(128)
|
||||
if pn_buf is not None: viperlib.snprintf(pn_buf, 128, "coro_pool_%s", name)
|
||||
pn_buf: str = pool.alloc(string.strlen(name) + 16)
|
||||
if pn_buf is not None: viperlib.snprintf(pn_buf, string.strlen(name) + 16, "coro_pool_%s", name)
|
||||
# idempotent: create/destroy both call this, so scan the module global list (no find_global API)
|
||||
g: mlirlite.GlobalVariable* = mod.GlobalHead
|
||||
while g is not None:
|
||||
@@ -1664,8 +1663,8 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
|
||||
pool_gv: mlirlite.GlobalVariable* = self._get_coro_pool(pool, mod, trans, async_name)
|
||||
# GlobalVariable is not a Value: build a Value view (Ty=Ptr(i64), Name="@coro_pool_<name>")
|
||||
i64_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, i64_ty)
|
||||
pool_ref_name: str = pool.alloc(96)
|
||||
if pool_ref_name is not None: viperlib.snprintf(pool_ref_name, 96, "@coro_pool_%s", async_name)
|
||||
pool_ref_name: str = pool.alloc(string.strlen(async_name) + 16)
|
||||
if pool_ref_name is not None: viperlib.snprintf(pool_ref_name, string.strlen(async_name) + 16, "@coro_pool_%s", async_name)
|
||||
pool_ref: mlirlite.Value* = mlirlite.SSAValue(pool, i64_ptr_ty, pool_ref_name)
|
||||
pool_head: mlirlite.Value* = mlirlite.build_load(builder, i64_ty, pool_ref)
|
||||
node_next: mlirlite.Value* = mlirlite.build_bitcast(builder, frame_ptr, i64_ptr_ty)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -12,8 +11,8 @@ import string
|
||||
import stdio
|
||||
import stdlib
|
||||
import viperlib
|
||||
import w32.fileio as fileio
|
||||
import w32.win32file as win32file
|
||||
import win32.fileio as fileio
|
||||
import win32.win32file as win32file
|
||||
import lib.core.VLogger as VLogger
|
||||
import lib.Stub.StubMerger as StubMerger
|
||||
import lib.core.CompilerFactory as CompilerFactory
|
||||
@@ -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
|
||||
@@ -146,6 +145,10 @@ novirtual class CallGenBase:
|
||||
trans: HT.Translator*, callee_name: str, arg_idx: int, arg_node: ast.AST*,
|
||||
fpty: mlirlite.LLVMType*, use_ctx: str = "param") -> mlirlite.Value*:
|
||||
"""Translate a call argument; value-reference params take the address (ref) or a temp copy (temp)."""
|
||||
# 记录“当前实参位置”:被调者内部若因**合成节点**(lineno==0,如构造器内联出的参数赋值)
|
||||
# 报错,可据此把报错精确定位到这个实参表达式(如 `&_on_submit`),而不是退回函数定义行。
|
||||
if arg_node is not None:
|
||||
self.Gen.TypeGenBase.set_cur_arg(self.Gen.TypeGenResolve.get_current_file(), arg_node.lineno, arg_node.col_offset + 1)
|
||||
mode: int = self._auto_addr_mode(callee_name, arg_idx)
|
||||
if mode == 0:
|
||||
return self.Gen.ExprGen.translate_value(builder, pool, mod, arg_node, None, 0, trans, fpty, use_ctx)
|
||||
@@ -246,7 +249,7 @@ novirtual class CallGenBase:
|
||||
mode: int = self.Gen.CallGenBase._auto_addr_mode(callee_name, idx)
|
||||
if mode == 0: mode = self.Gen.CallGenBase._vpi_auto_addr_mode(callee_name, trans.ModuleSha1, idx)
|
||||
if mode != 0:
|
||||
arg_i: int = idx - start_idx
|
||||
arg_i: int = int(idx - start_idx)
|
||||
arg_node: ast.AST* = None
|
||||
if arg_nodes is not None and arg_i < len(arg_nodes): arg_node = arg_nodes.get(arg_i)
|
||||
node_v = self.Gen.CallGenBase._apply_auto_addr_arg(builder, pool, mod, trans, mode, arg_node, cur)
|
||||
@@ -275,7 +278,7 @@ novirtual class CallGenBase:
|
||||
sha1_arr_ptr: bytes = StubMergerStore.GetSha1StoreArrPtr()
|
||||
count: int = StubMergerStore.GetSha1StoreCount()
|
||||
if rel_arr_ptr is None or sha1_arr_ptr is None or count <= 0: return None
|
||||
for i in range(count):
|
||||
for i in range(int(count)):
|
||||
sidx: sizet = sizet(i) * 17
|
||||
if string.strcmp(sha1_arr_ptr + sidx, mod_sha1) != 0: continue
|
||||
ridx: sizet = sizet(i) * StubMerger.MAX_REL_PATH_LEN
|
||||
@@ -286,7 +289,7 @@ novirtual class CallGenBase:
|
||||
if pkg_len == 0: return None
|
||||
pkg_buf: str = pool.alloc(pkg_len + 1)
|
||||
if pkg_buf is None: return None
|
||||
for pi in range(pkg_len):
|
||||
for pi in range(int(pkg_len)):
|
||||
if rel[pi] == '/': pkg_buf[pi] = '.'
|
||||
else: pkg_buf[pi] = rel[pi]
|
||||
pkg_buf[pkg_len] = '\0'
|
||||
@@ -344,7 +347,7 @@ novirtual class CallGenBase:
|
||||
fl2_od: sizet = fend_od - fstart_od
|
||||
if fl2_od == name_len:
|
||||
fd_eq_od: int = 1
|
||||
for fdi_od in range(fl2_od):
|
||||
for fdi_od in range(int(fl2_od)):
|
||||
if buf[fstart_od + fdi_od] != func_name[fdi_od]:
|
||||
fd_eq_od = 0
|
||||
break
|
||||
@@ -418,7 +421,7 @@ novirtual class CallGenBase:
|
||||
as_pos += 1
|
||||
if cand_len == name_len:
|
||||
m_ok: int = 1
|
||||
for ki in range(name_len):
|
||||
for ki in range(int(name_len)):
|
||||
if buf[cand_start + ki] != func_name[ki]:
|
||||
m_ok = 0
|
||||
break
|
||||
@@ -490,7 +493,7 @@ novirtual class CallGenBase:
|
||||
"""Whether (sha1, func_name) is a CExport function (reads the Gen.CallGenBase source of truth), falling back to the vpi return annotation on a registry miss."""
|
||||
if self.Gen is None or sha1 is None or func_name is None: return 0
|
||||
if self.Gen.CallGenBase.CexportSha1Arr is not None:
|
||||
for i in range(self.Gen.CallGenBase.CexportCount):
|
||||
for i in range(int(self.Gen.CallGenBase.CexportCount)):
|
||||
idx: sizet = sizet(i) * 17
|
||||
if string.strcmp(self.Gen.CallGenBase.CexportSha1Arr + idx, sha1) == 0:
|
||||
idx2: sizet = sizet(i) * 64
|
||||
@@ -510,7 +513,7 @@ novirtual class CallGenBase:
|
||||
string.memset(self.Gen.CallGenBase.StaticSha1Arr, 0, _CEXPORT_MAX * 17)
|
||||
string.memset(self.Gen.CallGenBase.StaticNameArr, 0, _CEXPORT_MAX * 64)
|
||||
# idempotent: repeated (sha1, name) registration returns a hit
|
||||
for i in range(self.Gen.CallGenBase.StaticCount):
|
||||
for i in range(int(self.Gen.CallGenBase.StaticCount)):
|
||||
si: sizet = sizet(i) * 17
|
||||
if string.strcmp(self.Gen.CallGenBase.StaticSha1Arr + si, sha1) == 0:
|
||||
sn: sizet = sizet(i) * 64
|
||||
@@ -532,7 +535,7 @@ novirtual class CallGenBase:
|
||||
"""Whether (sha1, name) is a registered static symbol, the cross-module reference blocker; pairs (sha1, name) exactly to avoid same-name mistakes."""
|
||||
if self.Gen is None or sha1 is None or name is None: return 0
|
||||
if self.Gen.CallGenBase.StaticSha1Arr is None: return 0
|
||||
for i in range(self.Gen.CallGenBase.StaticCount):
|
||||
for i in range(int(self.Gen.CallGenBase.StaticCount)):
|
||||
si: sizet = sizet(i) * 17
|
||||
if string.strcmp(self.Gen.CallGenBase.StaticSha1Arr + si, sha1) == 0:
|
||||
sn: sizet = sizet(i) * 64
|
||||
@@ -576,7 +579,7 @@ novirtual class CallGenBase:
|
||||
while mod_name_raw[mod_len] != ' ' and mod_name_raw[mod_len] != '\0' and mod_name_raw[mod_len] != ':': mod_len += 1
|
||||
mod_name_buf: str = pool.alloc(mod_len + 1)
|
||||
if mod_name_buf is None: return None
|
||||
for mi in range(mod_len): mod_name_buf[mi] = mod_name_raw[mi]
|
||||
for mi in range(int(mod_len)): mod_name_buf[mi] = mod_name_raw[mi]
|
||||
mod_name_buf[mod_len] = '\0'
|
||||
# strip leading dots of relative import
|
||||
base_mod: str = mod_name_buf
|
||||
@@ -628,7 +631,7 @@ novirtual class CallGenBase:
|
||||
suffix_start_o: sizet = cur_len - name_len
|
||||
if cur_name[suffix_start_o - 1] == '.':
|
||||
match_o: int = 1
|
||||
for i in range(name_len):
|
||||
for i in range(int(name_len)):
|
||||
if cur_name[suffix_start_o + i] != name[i]:
|
||||
match_o = 0
|
||||
break
|
||||
@@ -638,7 +641,7 @@ novirtual class CallGenBase:
|
||||
suffix_start: sizet = cur_len - name_len
|
||||
if cur_name[suffix_start - 1] == '.':
|
||||
Matched: int = 1
|
||||
for i in range(name_len):
|
||||
for i in range(int(name_len)):
|
||||
if cur_name[suffix_start + i] != name[i]:
|
||||
Matched = 0
|
||||
break
|
||||
@@ -794,110 +797,110 @@ novirtual class CallGenBase:
|
||||
if annot is None: return None
|
||||
return VpiReader._resolve_vpi_ret_annot_str(pool, annot, i32_ty, i8_ptr_ty)
|
||||
|
||||
def _vpi_escape_packed(self, sha1: str, cls_name: str, method_name: str) -> int:
|
||||
"""跨模块逃逸摘要查询(v3):按 (定义模块 sha1, Class?, method) 查 .vpi 缓存;0=无摘要。
|
||||
|
||||
用"本模块已加载"备忘包住 _ensure_vpi_ret_annot_loaded(其内部是 O(N) 前缀判定):
|
||||
调用点每次都要查摘要,不做备忘会把编译拖慢一个量级。"""
|
||||
if sha1 is None or method_name is None: return 0
|
||||
if VpiReader._vpi_escape_module_loaded(sha1) == 0:
|
||||
if self.Gen.CallGenBase.TempDir is None or self.Gen.CallGenBase.TempDirLen == 0: return 0
|
||||
# ⚠ 同 lookup_ret_kind_via_vpi:**加载成功才记备忘**。失败也记会把该模块毒化成"已加载",
|
||||
# 使之后所有跨模块查询(含 willFree 的 _vpi_willfree,它共用这条备忘)永远查空。
|
||||
if VpiReader._ensure_vpi_ret_annot_loaded(sha1, self.Gen.CallGenBase.TempDir) == 0: return 0
|
||||
VpiReader._vpi_escape_mark_module(sha1)
|
||||
return VpiReader._lookup_vpi_escape_packed(sha1, cls_name, method_name)
|
||||
|
||||
# lookup_escape_dir_via_vpi - 跨模块声明式指令契约(@life(escape(...)) / @life(noescape(...)))。
|
||||
# 本文件指令表按文件重置,跨模块调用点必为空,只能靠 .vpi 行尾的 esce=/escn= 传递;
|
||||
# noescape != 0 取"强制不逃逸位",否则取"强制逃逸位"。0 = 无该标注(含该模块未加载过)。
|
||||
def lookup_escape_dir_via_vpi(self, cls_sha1: str, class_name: str, func_name: str, noescape: int) -> int:
|
||||
"""对方模块 .vpi 里落的契约掩码;0 = 无该标注"""
|
||||
if func_name is None or cls_sha1 is None: return 0
|
||||
return VpiReader._lookup_vpi_escape_dir(cls_sha1, class_name, func_name, noescape)
|
||||
|
||||
# lookup_ret_kind_via_vpi - 跨模块 `@life(ret(...))` 返回值生命期契约(A2,§14)。
|
||||
# 与 escape/noescape 同一条 .vpi 通道(行尾 escr=);0 = 无声明(含该模块未加载过)。
|
||||
# 注意:本查询可能在调用点**之前**(life_of_expr 判右值生命期)发生,不能假定调用点的
|
||||
# 逃逸查询已把对方 vpi 载入,故这里自己走一次"本模块已加载"备忘 + 按需加载。
|
||||
def lookup_ret_kind_via_vpi(self, cls_sha1: str, class_name: str, func_name: str) -> int:
|
||||
"""对方模块 .vpi 里落的 ret_kind;0 = 无声明"""
|
||||
if func_name is None or cls_sha1 is None: return 0
|
||||
if VpiReader._vpi_escape_module_loaded(cls_sha1) == 0:
|
||||
if self.Gen.CallGenBase.TempDir is None or self.Gen.CallGenBase.TempDirLen == 0: return 0
|
||||
# ⚠ 只有**加载成功**才能记入"已加载"备忘。_ensure 失败(该模块的 vpi 此刻还没生成 /
|
||||
# 打不开)时若也记上,后续所有跨模块查询(逃逸摘要 / willFree / ret)都会**跳过真正的
|
||||
# 加载** → 缓存恒空 → 判据静默失效。现场:本查询可能在极早的翻译阶段先跑(life_of_expr
|
||||
# 判右值生命期),把尚无 vpi 的模块毒化成"已加载" → EscapeXmodTest(esc)/
|
||||
# NewLifetimeWillFreeTest(wf)双双漏检。
|
||||
if VpiReader._ensure_vpi_ret_annot_loaded(cls_sha1, self.Gen.CallGenBase.TempDir) == 0: return 0
|
||||
VpiReader._vpi_escape_mark_module(cls_sha1)
|
||||
return VpiReader._lookup_vpi_escape_dir(cls_sha1, class_name, func_name, 2)
|
||||
|
||||
def lookup_escape_via_vpi(self, pool: memhub.MemBuddy*, cls_sha1: str, cls_name: str, method_name: str) -> int:
|
||||
"""跨模块逃逸摘要(v3):本类 + MRO 祖先各自的模块 sha1 依次查 .vpi,返回打包值(0=无摘要)。
|
||||
|
||||
本文件的摘要表按文件重置,跨模块调用点上必为空,只能靠 .vpi 传递;
|
||||
键与方法路径一致:"Class.method"(方法)/ 裸名(模块级函数,此时 cls_name=None)。"""
|
||||
if method_name is None: return 0
|
||||
if cls_sha1 is not None:
|
||||
p0: int = self._vpi_escape_packed(cls_sha1, cls_name, method_name)
|
||||
if p0 != 0: return p0
|
||||
if cls_name is not None:
|
||||
mro: sizet* = self.Gen.StructGen.get_mro_chain(pool, cls_name)
|
||||
mi: int = 1
|
||||
while mro is not None and mro[mi] != 0:
|
||||
anc: str = rawptr(mro[mi])
|
||||
if anc is not None and string.strcmp(anc, cls_name) != 0:
|
||||
sha_a: str = self.Gen.StructGen.get_struct_sha1(anc)
|
||||
if sha_a is not None:
|
||||
pa: int = self._vpi_escape_packed(sha_a, anc, method_name)
|
||||
if pa != 0: return pa
|
||||
mi += 1
|
||||
return 0
|
||||
|
||||
def _vpi_willfree(self, sha1: str, cls_name: str, method_name: str) -> int:
|
||||
"""跨模块 willFree 标注查询(P2):按 (定义模块 sha1, Class?, method) 查 .vpi 缓存;0=无标注。
|
||||
|
||||
与逃逸摘要共用"本模块已加载"备忘:_ensure_vpi_ret_annot_loaded 的单一入口会把
|
||||
ret/param/esc/wf 一起喂齐,所以这里只需保证模块被加载过一次。"""
|
||||
if sha1 is None or method_name is None: return 0
|
||||
if VpiReader._vpi_escape_module_loaded(sha1) == 0:
|
||||
if self.Gen.CallGenBase.TempDir is None or self.Gen.CallGenBase.TempDirLen == 0: return 0
|
||||
VpiReader._ensure_vpi_ret_annot_loaded(sha1, self.Gen.CallGenBase.TempDir)
|
||||
VpiReader._vpi_escape_mark_module(sha1)
|
||||
return VpiReader._lookup_vpi_willfree(sha1, cls_name, method_name)
|
||||
|
||||
def lookup_escape_clears_via_vpi(self, pool: memhub.MemBuddy*, cls_sha1: str, class_name: str, func_name: str, cargs: list[ast.AST*]*, can: sizet, param_base: int) -> int:
|
||||
"""跨模块常量特化:把实参字面量装进常量环境,再向 vpi 缓存要"该清掉的位掩码";0 = 无命中
|
||||
|
||||
跨模块拿不到被调者函数体(只有 .vpi),所以用落盘的守卫表(`escg=`)而不是重算摘要。
|
||||
没有常量实参、或该函数没落过守卫记录 → 返回 0(调用方原样保留掩码,保守)。"""
|
||||
if cls_sha1 is None or func_name is None or cargs is None or can == 0: return 0
|
||||
if GenApi.build_const_env(cargs, can, param_base) == 0: return 0
|
||||
fenv: sizet* = GenApi.const_env_ptr()
|
||||
if fenv is None: return 0
|
||||
return VpiReader._lookup_vpi_escape_clears(cls_sha1, class_name, func_name, fenv)
|
||||
|
||||
def lookup_willfree_via_vpi(self, pool: memhub.MemBuddy*, cls_sha1: str, cls_name: str, method_name: str) -> int:
|
||||
"""跨模块 willFree 掩码(P2):本类 + MRO 祖先各自的模块 sha1 依次查 .vpi;0=无标注"""
|
||||
if method_name is None: return 0
|
||||
if cls_sha1 is not None:
|
||||
p0: int = self._vpi_willfree(cls_sha1, cls_name, method_name)
|
||||
if p0 != 0: return p0
|
||||
if cls_name is not None:
|
||||
mro: sizet* = self.Gen.StructGen.get_mro_chain(pool, cls_name)
|
||||
mi: int = 1
|
||||
while mro is not None and mro[mi] != 0:
|
||||
anc: str = rawptr(mro[mi])
|
||||
if anc is not None and string.strcmp(anc, cls_name) != 0:
|
||||
sha_a: str = self.Gen.StructGen.get_struct_sha1(anc)
|
||||
if sha_a is not None:
|
||||
pa: int = self._vpi_willfree(sha_a, anc, method_name)
|
||||
if pa != 0: return pa
|
||||
mi += 1
|
||||
return 0
|
||||
|
||||
def _vpi_escape_packed(self, sha1: str, cls_name: str, method_name: str) -> int:
|
||||
"""跨模块逃逸摘要查询(v3):按 (定义模块 sha1, Class?, method) 查 .vpi 缓存;0=无摘要。
|
||||
|
||||
用"本模块已加载"备忘包住 _ensure_vpi_ret_annot_loaded(其内部是 O(N) 前缀判定):
|
||||
调用点每次都要查摘要,不做备忘会把编译拖慢一个量级。"""
|
||||
if sha1 is None or method_name is None: return 0
|
||||
if VpiReader._vpi_escape_module_loaded(sha1) == 0:
|
||||
if self.Gen.CallGenBase.TempDir is None or self.Gen.CallGenBase.TempDirLen == 0: return 0
|
||||
# ⚠ 同 lookup_ret_kind_via_vpi:**加载成功才记备忘**。失败也记会把该模块毒化成"已加载",
|
||||
# 使之后所有跨模块查询(含 willFree 的 _vpi_willfree,它共用这条备忘)永远查空。
|
||||
if VpiReader._ensure_vpi_ret_annot_loaded(sha1, self.Gen.CallGenBase.TempDir) == 0: return 0
|
||||
VpiReader._vpi_escape_mark_module(sha1)
|
||||
return VpiReader._lookup_vpi_escape_packed(sha1, cls_name, method_name)
|
||||
|
||||
# lookup_escape_dir_via_vpi - 跨模块声明式指令契约(@life(escape(...)) / @life(noescape(...)))。
|
||||
# 本文件指令表按文件重置,跨模块调用点必为空,只能靠 .vpi 行尾的 esce=/escn= 传递;
|
||||
# noescape != 0 取"强制不逃逸位",否则取"强制逃逸位"。0 = 无该标注(含该模块未加载过)。
|
||||
def lookup_escape_dir_via_vpi(self, cls_sha1: str, class_name: str, func_name: str, noescape: int) -> int:
|
||||
"""对方模块 .vpi 里落的契约掩码;0 = 无该标注"""
|
||||
if func_name is None or cls_sha1 is None: return 0
|
||||
return VpiReader._lookup_vpi_escape_dir(cls_sha1, class_name, func_name, noescape)
|
||||
|
||||
# lookup_ret_kind_via_vpi - 跨模块 `@life(ret(...))` 返回值生命期契约(A2,§14)。
|
||||
# 与 escape/noescape 同一条 .vpi 通道(行尾 escr=);0 = 无声明(含该模块未加载过)。
|
||||
# 注意:本查询可能在调用点**之前**(life_of_expr 判右值生命期)发生,不能假定调用点的
|
||||
# 逃逸查询已把对方 vpi 载入,故这里自己走一次"本模块已加载"备忘 + 按需加载。
|
||||
def lookup_ret_kind_via_vpi(self, cls_sha1: str, class_name: str, func_name: str) -> int:
|
||||
"""对方模块 .vpi 里落的 ret_kind;0 = 无声明"""
|
||||
if func_name is None or cls_sha1 is None: return 0
|
||||
if VpiReader._vpi_escape_module_loaded(cls_sha1) == 0:
|
||||
if self.Gen.CallGenBase.TempDir is None or self.Gen.CallGenBase.TempDirLen == 0: return 0
|
||||
# ⚠ 只有**加载成功**才能记入"已加载"备忘。_ensure 失败(该模块的 vpi 此刻还没生成 /
|
||||
# 打不开)时若也记上,后续所有跨模块查询(逃逸摘要 / willFree / ret)都会**跳过真正的
|
||||
# 加载** → 缓存恒空 → 判据静默失效。现场:本查询可能在极早的翻译阶段先跑(life_of_expr
|
||||
# 判右值生命期),把尚无 vpi 的模块毒化成"已加载" → EscapeXmodTest(esc)/
|
||||
# NewLifetimeWillFreeTest(wf)双双漏检。
|
||||
if VpiReader._ensure_vpi_ret_annot_loaded(cls_sha1, self.Gen.CallGenBase.TempDir) == 0: return 0
|
||||
VpiReader._vpi_escape_mark_module(cls_sha1)
|
||||
return VpiReader._lookup_vpi_escape_dir(cls_sha1, class_name, func_name, 2)
|
||||
|
||||
def lookup_escape_via_vpi(self, pool: memhub.MemBuddy*, cls_sha1: str, cls_name: str, method_name: str) -> int:
|
||||
"""跨模块逃逸摘要(v3):本类 + MRO 祖先各自的模块 sha1 依次查 .vpi,返回打包值(0=无摘要)。
|
||||
|
||||
本文件的摘要表按文件重置,跨模块调用点上必为空,只能靠 .vpi 传递;
|
||||
键与方法路径一致:"Class.method"(方法)/ 裸名(模块级函数,此时 cls_name=None)。"""
|
||||
if method_name is None: return 0
|
||||
if cls_sha1 is not None:
|
||||
p0: int = self._vpi_escape_packed(cls_sha1, cls_name, method_name)
|
||||
if p0 != 0: return p0
|
||||
if cls_name is not None:
|
||||
mro: sizet* = self.Gen.StructGen.get_mro_chain(pool, cls_name)
|
||||
mi: int = 1
|
||||
while mro is not None and mro[mi] != 0:
|
||||
anc: str = rawptr(mro[mi])
|
||||
if anc is not None and string.strcmp(anc, cls_name) != 0:
|
||||
sha_a: str = self.Gen.StructGen.get_struct_sha1(anc)
|
||||
if sha_a is not None:
|
||||
pa: int = self._vpi_escape_packed(sha_a, anc, method_name)
|
||||
if pa != 0: return pa
|
||||
mi += 1
|
||||
return 0
|
||||
|
||||
def _vpi_willfree(self, sha1: str, cls_name: str, method_name: str) -> int:
|
||||
"""跨模块 willFree 标注查询(P2):按 (定义模块 sha1, Class?, method) 查 .vpi 缓存;0=无标注。
|
||||
|
||||
与逃逸摘要共用"本模块已加载"备忘:_ensure_vpi_ret_annot_loaded 的单一入口会把
|
||||
ret/param/esc/wf 一起喂齐,所以这里只需保证模块被加载过一次。"""
|
||||
if sha1 is None or method_name is None: return 0
|
||||
if VpiReader._vpi_escape_module_loaded(sha1) == 0:
|
||||
if self.Gen.CallGenBase.TempDir is None or self.Gen.CallGenBase.TempDirLen == 0: return 0
|
||||
VpiReader._ensure_vpi_ret_annot_loaded(sha1, self.Gen.CallGenBase.TempDir)
|
||||
VpiReader._vpi_escape_mark_module(sha1)
|
||||
return VpiReader._lookup_vpi_willfree(sha1, cls_name, method_name)
|
||||
|
||||
def lookup_escape_clears_via_vpi(self, pool: memhub.MemBuddy*, cls_sha1: str, class_name: str, func_name: str, cargs: list[ast.AST*]*, can: sizet, param_base: int) -> int:
|
||||
"""跨模块常量特化:把实参字面量装进常量环境,再向 vpi 缓存要"该清掉的位掩码";0 = 无命中
|
||||
|
||||
跨模块拿不到被调者函数体(只有 .vpi),所以用落盘的守卫表(`escg=`)而不是重算摘要。
|
||||
没有常量实参、或该函数没落过守卫记录 → 返回 0(调用方原样保留掩码,保守)。"""
|
||||
if cls_sha1 is None or func_name is None or cargs is None or can == 0: return 0
|
||||
if GenApi.build_const_env(cargs, can, param_base) == 0: return 0
|
||||
fenv: sizet* = GenApi.const_env_ptr()
|
||||
if fenv is None: return 0
|
||||
return VpiReader._lookup_vpi_escape_clears(cls_sha1, class_name, func_name, fenv)
|
||||
|
||||
def lookup_willfree_via_vpi(self, pool: memhub.MemBuddy*, cls_sha1: str, cls_name: str, method_name: str) -> int:
|
||||
"""跨模块 willFree 掩码(P2):本类 + MRO 祖先各自的模块 sha1 依次查 .vpi;0=无标注"""
|
||||
if method_name is None: return 0
|
||||
if cls_sha1 is not None:
|
||||
p0: int = self._vpi_willfree(cls_sha1, cls_name, method_name)
|
||||
if p0 != 0: return p0
|
||||
if cls_name is not None:
|
||||
mro: sizet* = self.Gen.StructGen.get_mro_chain(pool, cls_name)
|
||||
mi: int = 1
|
||||
while mro is not None and mro[mi] != 0:
|
||||
anc: str = rawptr(mro[mi])
|
||||
if anc is not None and string.strcmp(anc, cls_name) != 0:
|
||||
sha_a: str = self.Gen.StructGen.get_struct_sha1(anc)
|
||||
if sha_a is not None:
|
||||
pa: int = self._vpi_willfree(sha_a, anc, method_name)
|
||||
if pa != 0: return pa
|
||||
mi += 1
|
||||
return 0
|
||||
|
||||
# _vpi_method_param_annot: on-demand lookup of the vpi param annotation string
|
||||
def _vpi_method_param_annot(self, pool: memhub.MemBuddy*, cls_sha1: str, cls_name: str, method_name: str) -> str:
|
||||
"""Load and query the vpi parameter annotation string on demand; None on miss"""
|
||||
@@ -1021,7 +1024,7 @@ novirtual class CallGenBase:
|
||||
# trim trailing whitespace
|
||||
e: sizet = end
|
||||
while e > s:
|
||||
c2: int = buf[e - 1]
|
||||
c2: int = int(buf[e - 1])
|
||||
if c2 != ' ' and c2 != '\t': break
|
||||
e -= 1
|
||||
|
||||
@@ -1083,7 +1086,7 @@ novirtual class CallGenBase:
|
||||
stdlib.free(nm_buf)
|
||||
if nm_ty is not None:
|
||||
nm_wrap: mlirlite.LLVMType* = nm_ty
|
||||
for nm_i in range(nm_star):
|
||||
for nm_i in range(int(nm_star)):
|
||||
nm_wrap = mlirlite.Ptr(pool, nm_wrap)
|
||||
return nm_wrap
|
||||
|
||||
@@ -1294,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)
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import t, c
|
||||
from stdint import *
|
||||
import ast
|
||||
import mlirlite
|
||||
@@ -20,12 +19,18 @@ define ASM_MAX_OPERANDS = 16
|
||||
# Clobber (~{...}) capacity is decoupled from the operand limit: vipersimd x8
|
||||
# needs 21 entries, so ASM_MAX_OPERANDS=16 silently truncated it to 16.
|
||||
define ASM_MAX_CLOBBERS = 32
|
||||
define ASM_TEXT_BUF_SIZE = 2048
|
||||
# ASM_TEXT_BUF_SIZE holds the whole inline-asm template (a kernel switch statement
|
||||
# template is already 2245 bytes). Overflow is fail-fast in _asm_append_cstr, never
|
||||
# silently truncated: a cut-in-half template used to surface as a bogus llc parse
|
||||
# error (e.g. a dangling `mov` with its operands chopped off).
|
||||
define ASM_TEXT_BUF_SIZE = 8192
|
||||
# Constraint buffer base size; the real capacity scales with operand/clobber count.
|
||||
define ASM_CONST_BUF_SIZE = 512
|
||||
define ASM_ARGS_BUF_SIZE = 512
|
||||
define ASM_LINE_BUF_SIZE = 4096
|
||||
define LLVMIR_MAX_OPS = 16
|
||||
define LLVMIR_TEMPLATE_BUF = 512
|
||||
# Raw LLVM IR templates (c.LLVMIR) are multi-instruction text, not one-liners.
|
||||
define LLVMIR_TEMPLATE_BUF = 4096
|
||||
|
||||
|
||||
# CallGenCBuiltin - c.*/asm/llvmir builtins
|
||||
@@ -441,15 +446,22 @@ novirtual class CallGenCBuiltin(CallGenBase.CallGenBase):
|
||||
return loaded
|
||||
|
||||
|
||||
def _asm_append_cstr(self, dst: str, dst_size: sizet, src: str):
|
||||
"""Append the C string src to the end of dst."""
|
||||
def _asm_append_cstr(self, dst: str, dst_size: sizet, src: str, what: str = "inline asm"):
|
||||
"""Append the C string src to the end of dst; fail-fast when it does not fit.
|
||||
|
||||
Silent truncation corrupts the generated text (a template cut in the middle of an
|
||||
instruction yields a bogus llc/verifier error much later), so overflow is reported
|
||||
here at the real cause.
|
||||
"""
|
||||
if dst is None or src is None: return
|
||||
dlen: sizet = string.strlen(dst)
|
||||
slen: sizet = string.strlen(src)
|
||||
remain: sizet = dst_size - dlen
|
||||
if remain <= 0: return
|
||||
# Capacity includes the terminating NUL.
|
||||
if dlen + slen + 1 > dst_size:
|
||||
self.Gen.TypeGenErr.fatal_error(None, f"{what} text does not fit the fixed buffer: {dlen} byte(s) already used + {slen} byte(s) to append + 1 NUL > capacity {dst_size} byte(s); shorten or split the template (silent truncation would generate broken code)", "TypeError")
|
||||
return
|
||||
i: sizet = 0
|
||||
while i < slen and i + 1 < remain:
|
||||
while i < slen:
|
||||
dst[dlen + i] = src[i]
|
||||
i += 1
|
||||
dst[dlen + i] = '\0'
|
||||
@@ -492,9 +504,14 @@ novirtual class CallGenCBuiltin(CallGenBase.CallGenBase):
|
||||
return asm_text
|
||||
cur = cur.Next
|
||||
if matched == 0:
|
||||
if out_pos + 1 < asm_size * 2:
|
||||
out[out_pos] = asm_text[i]
|
||||
out_pos += 1
|
||||
# The output buffer is 2x the input, so this cannot trigger for a plain copy;
|
||||
# keep it fail-fast so a future longer rewrite (mangled expansion) can never
|
||||
# silently drop characters.
|
||||
if out_pos + 1 >= asm_size * 2:
|
||||
self.Gen.TypeGenErr.fatal_error(None, f"inline asm text overflows the symbol-mangling buffer (capacity {asm_size * 2} byte(s)); shorten the template", "TypeError")
|
||||
return asm_text
|
||||
out[out_pos] = asm_text[i]
|
||||
out_pos += 1
|
||||
i += 1
|
||||
out[out_pos] = '\0'
|
||||
return out
|
||||
@@ -647,7 +664,7 @@ novirtual class CallGenCBuiltin(CallGenBase.CallGenBase):
|
||||
buf: str = pool.alloc(llen + rlen + 1)
|
||||
if buf is None: return ""
|
||||
string.strcpy(buf, left_str)
|
||||
for i in range(rlen): buf[llen + i] = right_str[i]
|
||||
for i in range(int(rlen)): buf[llen + i] = right_str[i]
|
||||
buf[llen + rlen] = '\0'
|
||||
return buf
|
||||
return ""
|
||||
@@ -670,6 +687,16 @@ novirtual class CallGenCBuiltin(CallGenBase.CallGenBase):
|
||||
if mod_nm2.id is not None and string.strcmp(mod_nm2.id, "ASM_DESCR") == 0: return self._resolve_register(at.attr)
|
||||
return ""
|
||||
|
||||
# 裸枚举名(不带 ASM_DESCR. / t. 前缀):`op=[CLOBBER_MEMORY, CLOBBER_RAX]`。
|
||||
# 只有确实解析成 ASM_DESCR 成员时才接受(_resolve_register 对未知名给不出非空串),
|
||||
# 否则返回空串交由调用方按"未知约束"处理,避免把普通变量名误当约束串。
|
||||
if k == ast.ASTKind.Name:
|
||||
nm_b: ast.Name* = ast.Name*(node)
|
||||
if nm_b.id is not None:
|
||||
rs_b: str = self._resolve_register(nm_b.id)
|
||||
if rs_b is not None and rs_b[0] != '\0': return rs_b
|
||||
return ""
|
||||
|
||||
# Constant: string constant
|
||||
if k == ast.ASTKind.Constant:
|
||||
cn: ast.Constant* = ast.Constant*(node)
|
||||
@@ -719,13 +746,17 @@ novirtual class CallGenCBuiltin(CallGenBase.CallGenBase):
|
||||
|
||||
def _asm_add_operand(self, operands: GenTypes.AsmOperand*, count: int, val: mlirlite.Value*, constraint: str, is_output: int) -> int:
|
||||
"""Append an operand, dedup by value and direction, and return the new count."""
|
||||
if count >= ASM_MAX_OPERANDS: return count
|
||||
# fail-fast: the operand array is fixed at ASM_MAX_OPERANDS slots; dropping the
|
||||
# excess operand here would silently change the generated constraint/argument lists.
|
||||
if count >= ASM_MAX_OPERANDS:
|
||||
self.Gen.TypeGenErr.fatal_error(None, f"inline asm has more than {ASM_MAX_OPERANDS} operands (compiler limit); split the asm block into several ones", "TypeError")
|
||||
return count
|
||||
# fail-fast: AsmInp/AsmOut operands must not carry SHA1-mangled symbols;
|
||||
# LLVM MC cannot parse digit-leading names, so mark them t.CExport.
|
||||
if val is not None and val.Name is not None:
|
||||
if self._asm_name_is_mangled(val.Name) != 0: self.Gen.TypeGenErr.fatal_error(None, f"__asm__ operand (AsmInp/AsmOut) does not allow a symbol with a SHA1 mangling prefix '{val.Name}'; mark the corresponding function/global definition as t.CExport (bare name)", "TypeError")
|
||||
if val is not None:
|
||||
for ei in range(count):
|
||||
for ei in range(int(count)):
|
||||
eop: GenTypes.AsmOperand* = self._asm_get_operand(operands, ei)
|
||||
if eop is not None and eop.Value == val and eop.IsOutput == is_output: return count
|
||||
op: GenTypes.AsmOperand* = self._asm_get_operand(operands, count)
|
||||
@@ -754,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)
|
||||
|
||||
@@ -792,7 +823,7 @@ novirtual class CallGenCBuiltin(CallGenBase.CallGenBase):
|
||||
jvalues: list[ast.AST*]* = js.values
|
||||
if jvalues is not None:
|
||||
jvlen: sizet = len(jvalues)
|
||||
for vi in range(jvlen):
|
||||
for vi in range(int(jvlen)):
|
||||
part: ast.AST* = jvalues.get(vi)
|
||||
if part is None: continue
|
||||
pk: int = part.kind()
|
||||
@@ -878,7 +909,7 @@ novirtual class CallGenCBuiltin(CallGenBase.CallGenBase):
|
||||
keywords: list[ast.AST*]* = cl.keywords
|
||||
if keywords is not None:
|
||||
kwlen: sizet = len(keywords)
|
||||
for ki in range(kwlen):
|
||||
for ki in range(int(kwlen)):
|
||||
kw: ast.Keyword* = ast.Keyword*(keywords.get(ki))
|
||||
if kw is None or kw.arg is None or kw.value is None: continue
|
||||
kwk: int = kw.value.kind()
|
||||
@@ -890,7 +921,7 @@ novirtual class CallGenCBuiltin(CallGenBase.CallGenBase):
|
||||
out_elts: list[ast.AST*]* = out_lst.elts
|
||||
if out_elts is None: continue
|
||||
oelen: sizet = len(out_elts)
|
||||
for oi in range(oelen):
|
||||
for oi in range(int(oelen)):
|
||||
oelt: ast.AST* = out_elts.get(oi)
|
||||
if oelt is None or oelt.kind() != ast.ASTKind.Call: continue
|
||||
ocall: ast.Call* = ast.Call*(oelt)
|
||||
@@ -919,7 +950,7 @@ novirtual class CallGenCBuiltin(CallGenBase.CallGenBase):
|
||||
in_elts: list[ast.AST*]* = in_lst.elts
|
||||
if in_elts is None: continue
|
||||
ielen: sizet = len(in_elts)
|
||||
for ii in range(ielen):
|
||||
for ii in range(int(ielen)):
|
||||
ielt: ast.AST* = in_elts.get(ii)
|
||||
if ielt is None or ielt.kind() != ast.ASTKind.Call: continue
|
||||
icall: ast.Call* = ast.Call*(ielt)
|
||||
@@ -941,16 +972,20 @@ novirtual class CallGenCBuiltin(CallGenBase.CallGenBase):
|
||||
cl_elts: list[ast.AST*]* = cl_lst.elts
|
||||
if cl_elts is None: continue
|
||||
clen: sizet = len(cl_elts)
|
||||
for ci in range(clen):
|
||||
for ci in range(int(clen)):
|
||||
celt: ast.AST* = cl_elts.get(ci)
|
||||
if celt is None: continue
|
||||
cstr: str = self._resolve_asm_descr(pool, celt)
|
||||
if cstr is None or cstr[0] == '\0': continue
|
||||
if clobber_count < ASM_MAX_CLOBBERS:
|
||||
cl_addr: uint64 = uint64(clobbers) + clobber_count * 8
|
||||
cl_ptr: str* = (str*)(cl_addr)
|
||||
cl_ptr[0] = cstr
|
||||
clobber_count += 1
|
||||
# fail-fast: the clobber slot array is fixed at ASM_MAX_CLOBBERS;
|
||||
# dropping the excess would silently weaken the clobber list.
|
||||
if clobber_count >= ASM_MAX_CLOBBERS:
|
||||
self.Gen.TypeGenErr.fatal_error(celt, f"inline asm has more than {ASM_MAX_CLOBBERS} clobbers (compiler limit); remove duplicates or split the asm block", "TypeError")
|
||||
return None
|
||||
cl_addr: uint64 = uint64(clobbers) + clobber_count * 8
|
||||
cl_ptr: str* = (str*)(cl_addr)
|
||||
cl_ptr[0] = cstr
|
||||
clobber_count += 1
|
||||
|
||||
# 3. Parse the second positional arg as the clobber list
|
||||
if len(cargs) >= 2:
|
||||
@@ -960,22 +995,24 @@ novirtual class CallGenCBuiltin(CallGenBase.CallGenBase):
|
||||
pos_elts: list[ast.AST*]* = pos_lst.elts
|
||||
if pos_elts is not None:
|
||||
plen: sizet = len(pos_elts)
|
||||
for pi in range(plen):
|
||||
for pi in range(int(plen)):
|
||||
pelt: ast.AST* = pos_elts.get(pi)
|
||||
if pelt is None: continue
|
||||
pstr: str = self._resolve_asm_descr(pool, pelt)
|
||||
if pstr is None or pstr[0] == '\0': continue
|
||||
if clobber_count < ASM_MAX_CLOBBERS:
|
||||
pcl_addr: uint64 = uint64(clobbers) + clobber_count * 8
|
||||
pcl_ptr: str* = (str*)(pcl_addr)
|
||||
pcl_ptr[0] = pstr
|
||||
clobber_count += 1
|
||||
if clobber_count >= ASM_MAX_CLOBBERS:
|
||||
self.Gen.TypeGenErr.fatal_error(pelt, f"inline asm has more than {ASM_MAX_CLOBBERS} clobbers (compiler limit); remove duplicates or split the asm block", "TypeError")
|
||||
return None
|
||||
pcl_addr: uint64 = uint64(clobbers) + clobber_count * 8
|
||||
pcl_ptr: str* = (str*)(pcl_addr)
|
||||
pcl_ptr[0] = pstr
|
||||
clobber_count += 1
|
||||
|
||||
# 4. Count outputs/inputs and determine the return type
|
||||
output_count: int = 0
|
||||
input_count: int = 0
|
||||
first_output_idx: int = -1
|
||||
for oi2 in range(operand_count):
|
||||
for oi2 in range(int(operand_count)):
|
||||
op2: GenTypes.AsmOperand* = self._asm_get_operand(operands, oi2)
|
||||
if op2.IsOutput != 0:
|
||||
if first_output_idx < 0: first_output_idx = oi2
|
||||
@@ -993,7 +1030,7 @@ novirtual class CallGenCBuiltin(CallGenBase.CallGenBase):
|
||||
out_pn_last: mlirlite.ParamNode* = None
|
||||
out_pn_n: int = 0
|
||||
out_seq: int = 0
|
||||
for oi5 in range(operand_count):
|
||||
for oi5 in range(int(operand_count)):
|
||||
op5: GenTypes.AsmOperand* = self._asm_get_operand(operands, oi5)
|
||||
if op5 is None or op5.IsOutput == 0: continue
|
||||
pty5: mlirlite.LLVMType* = None
|
||||
@@ -1033,7 +1070,7 @@ novirtual class CallGenCBuiltin(CallGenBase.CallGenBase):
|
||||
match ret_ty:
|
||||
case mlirlite.LLVMType.Void():
|
||||
ret_re: mlirlite.LLVMType* = None
|
||||
for re_i in range(operand_count):
|
||||
for re_i in range(int(operand_count)):
|
||||
re_op: GenTypes.AsmOperand* = self._asm_get_operand(operands, re_i)
|
||||
if re_op is None or re_op.IsOutput == 0: continue
|
||||
if re_op.Value is not None and re_op.Value.Ty is not None and re_op.Value.Ty.Pointee != 0:
|
||||
@@ -1052,19 +1089,19 @@ novirtual class CallGenCBuiltin(CallGenBase.CallGenBase):
|
||||
|
||||
new_idx: int = 0
|
||||
# Outputs first
|
||||
for oi3 in range(operand_count):
|
||||
for oi3 in range(int(operand_count)):
|
||||
op3: GenTypes.AsmOperand* = self._asm_get_operand(operands, oi3)
|
||||
if op3.IsOutput != 0:
|
||||
new_order[new_idx] = oi3
|
||||
new_idx += 1
|
||||
# Then inputs
|
||||
for oi4 in range(operand_count):
|
||||
for oi4 in range(int(operand_count)):
|
||||
op4: GenTypes.AsmOperand* = self._asm_get_operand(operands, oi4)
|
||||
if op4.IsOutput == 0:
|
||||
new_order[new_idx] = oi4
|
||||
new_idx += 1
|
||||
# Build the orig -> new mapping
|
||||
for ni2 in range(operand_count): orig_to_new[new_order[ni2]] = ni2
|
||||
for ni2 in range(int(operand_count)): orig_to_new[new_order[ni2]] = ni2
|
||||
|
||||
# 4.6 Generate asm text (second f-string pass, $N from reordered indices).
|
||||
# The project uses Intel syntax via the IR inteldialect keyword.
|
||||
@@ -1076,7 +1113,7 @@ novirtual class CallGenCBuiltin(CallGenBase.CallGenBase):
|
||||
if jvalues2 is not None:
|
||||
jvlen2: sizet = len(jvalues2)
|
||||
fv_fi: int = 0
|
||||
for vi2 in range(jvlen2):
|
||||
for vi2 in range(int(jvlen2)):
|
||||
part2: ast.AST* = jvalues2.get(vi2)
|
||||
if part2 is None: continue
|
||||
pk2: int = part2.kind()
|
||||
@@ -1096,47 +1133,53 @@ novirtual class CallGenCBuiltin(CallGenBase.CallGenBase):
|
||||
cn4: ast.Constant* = ast.Constant*(tmpl_node)
|
||||
if cn4.const_kind == ast.CONST_STR and cn4.str_val is not None: self._asm_append_cstr(asm_text, ASM_TEXT_BUF_SIZE, cn4.str_val)
|
||||
|
||||
# 5. Build the constraint string in reordered order (outputs first)
|
||||
const_buf: str = pool.alloc(ASM_CONST_BUF_SIZE)
|
||||
# 5. Build the constraint string in reordered order (outputs first).
|
||||
# Capacity scales with the operand/clobber count: a 32-entry clobber list alone is
|
||||
# ~300 bytes, so the old fixed 512-byte cap could silently cut it. Overflow of this
|
||||
# (or any asm text buffer) is fail-fast in _asm_append_cstr.
|
||||
const_cap: sizet = ASM_CONST_BUF_SIZE + sizet(operand_count + clobber_count + 1) * 64
|
||||
const_buf: str = pool.alloc(const_cap)
|
||||
if const_buf is None: return None
|
||||
const_buf[0] = '\0'
|
||||
|
||||
first_const: int = 1
|
||||
for ci2 in range(operand_count):
|
||||
for ci2 in range(int(operand_count)):
|
||||
orig_i: int = new_order[ci2]
|
||||
cop: GenTypes.AsmOperand* = self._asm_get_operand(operands, orig_i)
|
||||
cons_val: str = cop.Constraint
|
||||
if cons_val is None: cons_val = "r"
|
||||
|
||||
if first_const == 0: self._asm_append_cstr(const_buf, ASM_CONST_BUF_SIZE, ",")
|
||||
if first_const == 0: self._asm_append_cstr(const_buf, const_cap, ",")
|
||||
first_const = 0
|
||||
|
||||
if cop.IsOutput != 0:
|
||||
# Output constraint: ensure it starts with =
|
||||
if cons_val[0] != '=': self._asm_append_cstr(const_buf, ASM_CONST_BUF_SIZE, "=")
|
||||
self._asm_append_cstr(const_buf, ASM_CONST_BUF_SIZE, cons_val)
|
||||
else: self._asm_append_cstr(const_buf, ASM_CONST_BUF_SIZE, cons_val)
|
||||
if cons_val[0] != '=': self._asm_append_cstr(const_buf, const_cap, "=")
|
||||
self._asm_append_cstr(const_buf, const_cap, cons_val)
|
||||
else: self._asm_append_cstr(const_buf, const_cap, cons_val)
|
||||
|
||||
# Append clobber constraints ~{name}
|
||||
for cidx in range(clobber_count):
|
||||
for cidx in range(int(clobber_count)):
|
||||
cl_addr2: uint64 = uint64(clobbers) + cidx * 8
|
||||
cname: str = (str*)(cl_addr2)[0]
|
||||
if cname is None or cname[0] == '\0': continue
|
||||
if first_const == 0: self._asm_append_cstr(const_buf, ASM_CONST_BUF_SIZE, ",")
|
||||
if first_const == 0: self._asm_append_cstr(const_buf, const_cap, ",")
|
||||
first_const = 0
|
||||
self._asm_append_cstr(const_buf, ASM_CONST_BUF_SIZE, "~{")
|
||||
self._asm_append_cstr(const_buf, ASM_CONST_BUF_SIZE, cname)
|
||||
self._asm_append_cstr(const_buf, ASM_CONST_BUF_SIZE, "}")
|
||||
self._asm_append_cstr(const_buf, const_cap, "~{")
|
||||
self._asm_append_cstr(const_buf, const_cap, cname)
|
||||
self._asm_append_cstr(const_buf, const_cap, "}")
|
||||
|
||||
# 6. Build the argument list text (input operands only): each arg is type text (an
|
||||
# inlined named struct can reach 512 bytes) plus the value name, so scale the buffer.
|
||||
asm_arg_cap: sizet = sizet(operand_count + 1) * 544 + 64
|
||||
# 6. Build the argument list text (input operands only): each arg is type text
|
||||
# (TypePrint is given a 4096-byte buffer below) plus the value name, so size the
|
||||
# buffer from that worst case instead of underestimating it (544 bytes/operand
|
||||
# silently cut wide inlined named structs).
|
||||
asm_arg_cap: sizet = sizet(operand_count + 1) * 4224 + 64
|
||||
args_buf: str = pool.alloc(asm_arg_cap)
|
||||
if args_buf is None: return None
|
||||
args_buf[0] = '\0'
|
||||
|
||||
first_arg: int = 1
|
||||
for ai3 in range(operand_count):
|
||||
for ai3 in range(int(operand_count)):
|
||||
orig_i2: int = new_order[ai3]
|
||||
aop: GenTypes.AsmOperand* = self._asm_get_operand(operands, orig_i2)
|
||||
if aop.IsOutput != 0: continue
|
||||
@@ -1197,7 +1240,7 @@ novirtual class CallGenCBuiltin(CallGenBase.CallGenBase):
|
||||
if is_void == 0 and first_output_idx >= 0:
|
||||
result_val: mlirlite.Value* = mlirlite.SSAValue(pool, ret_ty, ssa_name)
|
||||
out_emit_idx: int = 0
|
||||
for oi_out in range(operand_count):
|
||||
for oi_out in range(int(operand_count)):
|
||||
orig_o: int = new_order[oi_out]
|
||||
oo: GenTypes.AsmOperand* = self._asm_get_operand(operands, orig_o)
|
||||
if oo is None or oo.IsOutput == 0: continue
|
||||
@@ -1205,12 +1248,17 @@ novirtual class CallGenCBuiltin(CallGenBase.CallGenBase):
|
||||
# Multi-output: extractvalue index out_emit_idx, then store
|
||||
if oo.Value is not None and oo.Value.Ty is not None and oo.Value.Ty.Pointee != 0:
|
||||
ev_name: str = pool.alloc(32)
|
||||
ev_line: str = pool.alloc(ASM_LINE_BUF_SIZE)
|
||||
# Size the line from its actual parts: ret_ty_buf alone can be ~4096
|
||||
# bytes, so the old fixed cap could cut the instruction in half.
|
||||
ev_cap: sizet = ASM_LINE_BUF_SIZE + 64
|
||||
if ret_ty_buf is not None: ev_cap += string.strlen(ret_ty_buf)
|
||||
if ssa_name is not None: ev_cap += string.strlen(ssa_name)
|
||||
ev_line: str = pool.alloc(ev_cap)
|
||||
if ev_name is not None and ev_line is not None:
|
||||
# ssa_name is "%N"; build a unique "%asm.N.e{i}" (letter-
|
||||
# initial) so %N.e{i} is not parsed as id plus a bad suffix.
|
||||
viperlib.snprintf(ev_name, 32, "%%asm.%s.e%d", ssa_name + 1, out_emit_idx)
|
||||
viperlib.snprintf(ev_line, ASM_LINE_BUF_SIZE, "%s = extractvalue %s %s, %d", ev_name, ret_ty_buf, ssa_name, out_emit_idx)
|
||||
viperlib.snprintf(ev_line, ev_cap, "%s = extractvalue %s %s, %d", ev_name, ret_ty_buf, ssa_name, out_emit_idx)
|
||||
mlirlite.block_append_text(pool, builder.CurBlock, ev_line)
|
||||
ev_val: mlirlite.Value* = mlirlite.SSAValue(pool, oo.Value.Ty.Pointee, ev_name)
|
||||
mlirlite.build_store(builder, ev_val, oo.Value)
|
||||
@@ -1253,7 +1301,7 @@ novirtual class CallGenCBuiltin(CallGenBase.CallGenBase):
|
||||
jvalues: list[ast.AST*]* = js.values
|
||||
if jvalues is not None:
|
||||
jvlen: sizet = len(jvalues)
|
||||
for vi in range(jvlen):
|
||||
for vi in range(int(jvlen)):
|
||||
part: ast.AST* = jvalues.get(vi)
|
||||
if part is None: continue
|
||||
pk: int = part.kind()
|
||||
@@ -1261,7 +1309,7 @@ novirtual class CallGenCBuiltin(CallGenBase.CallGenBase):
|
||||
if pk == ast.ASTKind.Constant:
|
||||
# String literal fragment
|
||||
cn: ast.Constant* = ast.Constant*(part)
|
||||
if cn.str_val is not None: self._asm_append_cstr(text, LLVMIR_TEMPLATE_BUF, cn.str_val)
|
||||
if cn.str_val is not None: self._asm_append_cstr(text, LLVMIR_TEMPLATE_BUF, cn.str_val, "c.LLVMIR")
|
||||
|
||||
elif pk == ast.ASTKind.FormattedValue:
|
||||
fv: ast.FormattedValue* = ast.FormattedValue*(part)
|
||||
@@ -1279,7 +1327,7 @@ novirtual class CallGenCBuiltin(CallGenBase.CallGenBase):
|
||||
if string.strcmp(attr_nm, "LInp") == 0:
|
||||
# Input operand: translate to an LLVM value reference text (%x or constant)
|
||||
inp_val: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, val_node, None, 0, trans)
|
||||
if inp_val is not None and inp_val.Name is not None: self._asm_append_cstr(text, LLVMIR_TEMPLATE_BUF, inp_val.Name)
|
||||
if inp_val is not None and inp_val.Name is not None: self._asm_append_cstr(text, LLVMIR_TEMPLATE_BUF, inp_val.Name, "c.LLVMIR")
|
||||
|
||||
elif string.strcmp(attr_nm, "LOut") == 0:
|
||||
# Output operand: allocate a unique SSA register
|
||||
@@ -1293,28 +1341,28 @@ novirtual class CallGenCBuiltin(CallGenBase.CallGenBase):
|
||||
reg_buf: str = pool.alloc(32)
|
||||
if reg_buf is not None:
|
||||
viperlib.snprintf(reg_buf, 32, "%%llvmir_%d", self.Gen.CallGenBase.LLVMIRRegSeq)
|
||||
self._asm_append_cstr(text, LLVMIR_TEMPLATE_BUF, reg_buf)
|
||||
self._asm_append_cstr(text, LLVMIR_TEMPLATE_BUF, reg_buf, "c.LLVMIR")
|
||||
out_alloca[out_count] = sizet(alloca)
|
||||
out_regname[out_count] = sizet(reg_buf)
|
||||
out_count += 1
|
||||
continue
|
||||
# Non-LInp/LOut f-string expression: translate and embed the value text
|
||||
ival: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, fexpr, None, 0, trans)
|
||||
if ival is not None and ival.Name is not None: self._asm_append_cstr(text, LLVMIR_TEMPLATE_BUF, ival.Name)
|
||||
if ival is not None and ival.Name is not None: self._asm_append_cstr(text, LLVMIR_TEMPLATE_BUF, ival.Name, "c.LLVMIR")
|
||||
|
||||
elif tk == ast.ASTKind.Constant:
|
||||
cn2: ast.Constant* = ast.Constant*(first_arg)
|
||||
if cn2.str_val is not None:
|
||||
# A bare strcpy is unbounded and could overflow the pool block, so
|
||||
# use the same bounded append as the JoinedStr branch.
|
||||
self._asm_append_cstr(text, LLVMIR_TEMPLATE_BUF, cn2.str_val)
|
||||
self._asm_append_cstr(text, LLVMIR_TEMPLATE_BUF, cn2.str_val, "c.LLVMIR")
|
||||
|
||||
# 2. Embed the template text directly (no instruction parsing) + LOut writeback
|
||||
mlirlite.block_append_text(pool, builder.CurBlock, text)
|
||||
|
||||
# LOut writeback: store <ty> %llvmirN, <ty>* %var
|
||||
oi: int
|
||||
for oi in range(out_count):
|
||||
for oi in range(int(out_count)):
|
||||
oa: mlirlite.Value* = mlirlite.Value*(out_alloca[oi])
|
||||
rn: str = str(out_regname[oi])
|
||||
if oa is None or oa.Ty is None or oa.Ty.Pointee == 0 or rn is None: continue
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import t, c
|
||||
from stdint import *
|
||||
import ast
|
||||
import mlirlite
|
||||
@@ -53,7 +52,7 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
|
||||
method_index: int = -1
|
||||
if vc_entry is not None and vc_entry.VTableMethods is not None:
|
||||
methods_arr: sizet* = (sizet*)(vc_entry.VTableMethods)
|
||||
for mi in range(vc_entry.VTableMethodCount):
|
||||
for mi in range(int(vc_entry.VTableMethodCount)):
|
||||
mname_addr: sizet = methods_arr[mi]
|
||||
if mname_addr == 0: continue
|
||||
mname: str = rawptr(mname_addr)
|
||||
@@ -87,7 +86,7 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
|
||||
# rawptr[ParamNode]); an earlier Function* annotation violated ptr rules.
|
||||
vt_first: mlirlite.ParamNode* = None
|
||||
vt_prev: mlirlite.ParamNode* = None
|
||||
for vi in range(method_count):
|
||||
for vi in range(int(method_count)):
|
||||
pn: mlirlite.ParamNode* = mlirlite.new_param_node(pool, i8_ptr_ty)
|
||||
if pn is None: continue
|
||||
if vt_first is None: vt_first = pn
|
||||
@@ -116,7 +115,7 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
|
||||
fp_prev: mlirlite.ParamNode* = None
|
||||
fp_count: int = 0
|
||||
cur_val: mlirlite.Value* = args_head
|
||||
for vi in range(args_count):
|
||||
for vi in range(int(args_count)):
|
||||
if cur_val is None: break
|
||||
cur_ty: mlirlite.LLVMType* = mlirlite.value_get_ty(cur_val)
|
||||
fpn: mlirlite.ParamNode* = mlirlite.new_param_node(pool, cur_ty)
|
||||
@@ -165,23 +164,23 @@ 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))
|
||||
can_ci: sizet = len(arg_nodes)
|
||||
for ai_ci in range(can_ci):
|
||||
for ai_ci in range(int(can_ci)):
|
||||
if ((esc_mask_ci >> int(ai_ci + 1)) & 1) != 0:
|
||||
GenApi.fail_if_stack_addr_escape(arg_nodes.get(ai_ci), "a constructor parameter stored into a field", trans, dummy_ptr_ci)
|
||||
|
||||
@@ -323,7 +322,7 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
|
||||
tail: mlirlite.Value* = self_ptr
|
||||
|
||||
if extra_args is not None and extra_count > 0:
|
||||
for ei in range(extra_count):
|
||||
for ei in range(int(extra_count)):
|
||||
arg_addr: sizet = extra_args[ei]
|
||||
if arg_addr == 0: continue
|
||||
av: mlirlite.Value* = mlirlite.Value*(arg_addr)
|
||||
@@ -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:
|
||||
@@ -485,19 +484,27 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
|
||||
pc_node: mlirlite.Value* = head
|
||||
new_head_pc: mlirlite.Value* = head
|
||||
prev_pc: mlirlite.Value* = None
|
||||
pc_idx: int = 0
|
||||
while pc_node is not None:
|
||||
pc_next_save: mlirlite.Value* = pc_node.Next
|
||||
coerced_pc: mlirlite.Value* = pc_node
|
||||
# 每个实参带上**它自己的 AST 节点**(arg_nodes 与链上 slot 1.. 对应,slot 0 是
|
||||
# self):转换失败时报错才能定位到这个实参表达式(如 `&_on_submit`),而不是
|
||||
# 因 node=None 退回"函数定义行"、只剩一帧。
|
||||
pc_arg_node: ast.AST* = node
|
||||
if pc_idx >= 1 and arg_nodes is not None and (pc_idx - 1) < len(arg_nodes):
|
||||
pc_arg_node = arg_nodes.get(pc_idx - 1)
|
||||
if pc_param is not None:
|
||||
pty_pc: mlirlite.LLVMType* = mlirlite.param_get_ty(pc_param)
|
||||
if pty_pc is not None:
|
||||
c_pc: mlirlite.Value* = self.Gen.ExprGenBase.coerce_to_type(builder, pc_node, pty_pc, None)
|
||||
c_pc: mlirlite.Value* = self.Gen.ExprGenBase.coerce_to_type(builder, pc_node, pty_pc, pc_arg_node)
|
||||
if c_pc is not None: coerced_pc = c_pc
|
||||
pc_param = mlirlite.param_get_next(pc_param)
|
||||
coerced_pc.Next = None
|
||||
if prev_pc is None: new_head_pc = coerced_pc
|
||||
else: prev_pc.Next = coerced_pc
|
||||
prev_pc = coerced_pc
|
||||
pc_idx += 1
|
||||
pc_node = pc_next_save
|
||||
head = new_head_pc
|
||||
|
||||
@@ -510,7 +517,7 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
|
||||
cmop_has_vt = cmop_entry.HasVTable
|
||||
if cmop_has_vt == 1 and cmop_entry.VTableMethods is not None:
|
||||
cmop_methods_arr: sizet* = (sizet*)(cmop_entry.VTableMethods)
|
||||
for cmop_mi in range(cmop_entry.VTableMethodCount):
|
||||
for cmop_mi in range(int(cmop_entry.VTableMethodCount)):
|
||||
cmop_mname_addr: sizet = cmop_methods_arr[cmop_mi]
|
||||
if cmop_mname_addr == 0: continue
|
||||
cmop_mname: str = rawptr(cmop_mname_addr)
|
||||
@@ -547,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.
|
||||
|
||||
@@ -578,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)
|
||||
@@ -611,7 +618,7 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
|
||||
if esc_mask_mc != 0:
|
||||
dummy_ptr_mc: mlirlite.LLVMType* = mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||||
can_mc: sizet = len(cargs_mc)
|
||||
for ai_mc in range(can_mc):
|
||||
for ai_mc in range(int(can_mc)):
|
||||
if ((esc_mask_mc >> int(ai_mc + 1)) & 1) != 0:
|
||||
GenApi.fail_if_stack_addr_escape(cargs_mc.get(ai_mc), "an escaping method parameter", trans, dummy_ptr_mc)
|
||||
|
||||
@@ -1002,7 +1009,7 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
|
||||
# fields, so this needs no byte adjust; pass it through unchanged.
|
||||
if anc_entry_th is not None and anc_entry_th.FieldCount > 0:
|
||||
anc_first_name_th: str = None
|
||||
for afi_th in range(anc_entry_th.FieldCount):
|
||||
for afi_th in range(int(anc_entry_th.FieldCount)):
|
||||
afe_th: GenTypes.FieldEntry* = self.Gen.StructGen._get_field_entry(anc_entry_th, afi_th)
|
||||
if afe_th is not None:
|
||||
afn_th: str = self.Gen.StructGen.get_field_name_ptr(afe_th)
|
||||
@@ -1090,7 +1097,7 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
|
||||
pname_k: str = mlirlite.param_get_name(cur_param_k)
|
||||
if pname_k is not None:
|
||||
ki_k: int
|
||||
for ki_k in range(kwc):
|
||||
for ki_k in range(int(kwc)):
|
||||
kw_k: ast.Keyword* = ast.Keyword*(kw_calls.get(ki_k))
|
||||
if kw_k is None or kw_k.arg is None or kw_k.value is None: continue
|
||||
if self._kwarg_name_matches(kw_k.arg, pname_k) == 1:
|
||||
@@ -1127,7 +1134,7 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
|
||||
cur_param_k = mlirlite.param_get_next(cur_param_k)
|
||||
else:
|
||||
# Positional arguments (original logic).
|
||||
for ai in range(can):
|
||||
for ai in range(int(can)):
|
||||
arg_node: ast.AST* = cargs.get(ai)
|
||||
if arg_node is None:
|
||||
# (a) Missing arg node (AST list hole): fail fast, never skip silently
|
||||
@@ -1183,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:
|
||||
@@ -1313,7 +1320,7 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
|
||||
mc_has_vt = mc_entry.HasVTable
|
||||
if mc_has_vt == 1 and mc_entry.VTableMethods is not None:
|
||||
mc_methods_arr: sizet* = (sizet*)(mc_entry.VTableMethods)
|
||||
for mc_mi in range(mc_entry.VTableMethodCount):
|
||||
for mc_mi in range(int(mc_entry.VTableMethodCount)):
|
||||
mc_mname_addr: sizet = mc_methods_arr[mc_mi]
|
||||
if mc_mname_addr == 0: continue
|
||||
mc_mname: str = rawptr(mc_mname_addr)
|
||||
@@ -1569,7 +1576,7 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
|
||||
anc_entry_sp: GenTypes.StructEntry* = self.Gen.StructGen.find_struct(anc)
|
||||
if anc_entry_sp is not None and anc_entry_sp.FieldCount > 0:
|
||||
first_name_sp: str = None
|
||||
for afi_sp in range(anc_entry_sp.FieldCount):
|
||||
for afi_sp in range(int(anc_entry_sp.FieldCount)):
|
||||
afe_sp: GenTypes.FieldEntry* = self.Gen.StructGen._get_field_entry(anc_entry_sp, afi_sp)
|
||||
if afe_sp is not None:
|
||||
afn_sp: str = self.Gen.StructGen.get_field_name_ptr(afe_sp)
|
||||
@@ -1620,7 +1627,7 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
|
||||
head: mlirlite.Value* = self_arg
|
||||
tail: mlirlite.Value* = self_arg
|
||||
arg_count_sp: int = 1
|
||||
for ai_sp in range(can_sp):
|
||||
for ai_sp in range(int(can_sp)):
|
||||
arg_node_sp: ast.AST* = cargs_sp.get(ai_sp)
|
||||
if arg_node_sp is None: continue
|
||||
# Fetch the param type first (char literal -> str param needs a materialized string).
|
||||
@@ -1687,11 +1694,11 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
|
||||
if class_name is None: return 0
|
||||
if self.Gen is None: return 0
|
||||
n: int = self.Gen.StructGen.get_struct_count()
|
||||
for j in range(n):
|
||||
for j in range(int(n)):
|
||||
e: GenTypes.StructEntry* = self.Gen.StructGen._get_struct_entry(j)
|
||||
if e is not None and e.Name is not None:
|
||||
pc: int = self.Gen.StructGen.get_parent_count(e.Name)
|
||||
for pj in range(pc):
|
||||
for pj in range(int(pc)):
|
||||
pn: str = self.Gen.StructGen.get_parent_name_at(e.Name, pj)
|
||||
if pn is not None:
|
||||
if string.strcmp(pn, class_name) == 0: return 1
|
||||
@@ -1704,7 +1711,7 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
|
||||
if class_name is None or cand_name is None or pool is None: return 0
|
||||
if self.Gen is None: return 0
|
||||
n: int = self.Gen.StructGen.get_struct_count()
|
||||
for j in range(n):
|
||||
for j in range(int(n)):
|
||||
k_entry: GenTypes.StructEntry* = self.Gen.StructGen._get_struct_entry(j)
|
||||
if k_entry is not None and k_entry.Name is not None:
|
||||
mro: sizet* = self.Gen.StructGen.get_mro_chain(pool, k_entry.Name)
|
||||
|
||||
+1009
-951
File diff suppressed because it is too large.
Load diff
+1531
-1527
File diff suppressed because it is too large.
Load diff
@@ -1,5 +1,4 @@
|
||||
import lib.core.Gen.GenTypes as GenTypes
|
||||
import t, c
|
||||
from stdint import *
|
||||
import ast
|
||||
import mlirlite
|
||||
@@ -91,7 +90,7 @@ novirtual class CallGenFd(CallGenBase.CallGenBase):
|
||||
fd_args: list[ast.AST*]* = cl.args
|
||||
fd_can: int = 0
|
||||
if fd_args is not None: fd_can = int(len(fd_args))
|
||||
for fd_ai in range(fd_can):
|
||||
for fd_ai in range(int(fd_can)):
|
||||
fd_arg: ast.AST* = fd_args.get(fd_ai)
|
||||
if fd_arg is not None:
|
||||
fd_av: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, fd_arg, None, 0, trans)
|
||||
@@ -153,7 +152,7 @@ novirtual class CallGenFd(CallGenBase.CallGenBase):
|
||||
cur_func: mlirlite.Function* = trans._cur_func
|
||||
if cur_func is None: return 0
|
||||
injected: int = 0
|
||||
for ji in range(cnt):
|
||||
for ji in range(int(cnt)):
|
||||
fname: str = self.Gen.ClassGenBase.class_meta_field_at(GenTypes.CLASS_META_REQUIRES, class_name, ji)
|
||||
if fname is None: continue
|
||||
fe: GenTypes.FieldEntry* = self.Gen.StructGen.lookup_field(sty, fname)
|
||||
@@ -218,7 +217,7 @@ novirtual class CallGenFd(CallGenBase.CallGenBase):
|
||||
# Bitcast the ctx value to i8* (provider stack stores void*)
|
||||
fd_ctx_i8: mlirlite.Value* = mlirlite.build_bitcast(builder, ctx_val, i8_ptr_ty_p)
|
||||
if fd_ctx_i8 is None: return 0
|
||||
for pi in range(fd_pc):
|
||||
for pi in range(int(fd_pc)):
|
||||
fname: str = self.Gen.ClassGenBase.class_meta_field_at(GenTypes.CLASS_META_PROVIDES, cls_name, pi)
|
||||
if fname is None: continue
|
||||
fd_fstr: mlirlite.Value* = self.Gen.ExprGenConst.create_global_string(builder, pool, mod, fname, trans)
|
||||
@@ -243,5 +242,5 @@ novirtual class CallGenFd(CallGenBase.CallGenBase):
|
||||
ret_i32_q: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
||||
if ret_i32_q is None: return 0
|
||||
self._ensure_method_declare(pool, mod, fd_pon, ret_i32_q, None, 0)
|
||||
for _pi in range(fd_pc): mlirlite.build_call(builder, fd_pon, None, 0, ret_i32_q, 0)
|
||||
for _pi in range(int(fd_pc)): mlirlite.build_call(builder, fd_pon, None, 0, ret_i32_q, 0)
|
||||
return fd_pc
|
||||
@@ -1,5 +1,4 @@
|
||||
import lib.core.Gen.GenTypes as GenTypes
|
||||
import t, c
|
||||
from stdint import *
|
||||
import ast
|
||||
import mlirlite
|
||||
@@ -65,7 +64,7 @@ def _gfn_idx_find(funcs: GenTypes.FuncEntry*, idx_base: sizet*, cap: int, name:
|
||||
mask: sizet = sizet(cap - 1)
|
||||
si: sizet = sizet(h) & mask
|
||||
entry_size: sizet = GenTypes.FuncEntry.__sizeof__()
|
||||
for i in range(cap):
|
||||
for i in range(int(cap)):
|
||||
slot: sizet* = _gfn_idx_slot(idx_base, si)
|
||||
sv: sizet = slot[0]
|
||||
if sv == 0: return -1
|
||||
@@ -84,7 +83,7 @@ def _gfn_idx_insert(idx_base: sizet*, cap: int, slot_index: int, name: str) -> i
|
||||
h: uint64 = _gfn_hash(name)
|
||||
mask: sizet = sizet(cap - 1)
|
||||
si: sizet = sizet(h) & mask
|
||||
for i in range(cap):
|
||||
for i in range(int(cap)):
|
||||
slot: sizet* = _gfn_idx_slot(idx_base, si)
|
||||
if slot[0] == 0:
|
||||
slot[0] = sizet(slot_index + 1)
|
||||
@@ -117,6 +116,23 @@ novirtual class CallGenLeafKit(CallGenCBuiltin.CallGenCBuiltin):
|
||||
# ============================================================
|
||||
def _emit_eh_guarded_call(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, trans: HT.Translator*, func: mlirlite.Function*, call_name: str, head: mlirlite.Value*, count: int, ret_ty: mlirlite.LLVMType*, is_variadic: int) -> mlirlite.Value*:
|
||||
"""Call a function with try-except EH detection; returns the call result (positioned at ok block)"""
|
||||
# Argument-count check (fail-fast at the front end instead of leaking to the LLVM verifier).
|
||||
# Callee params include the injected EH out-params (%__eh_msg_out__/__eh_code_out__), which the
|
||||
# caller does not pass explicitly (they are appended later in this function), so subtract them.
|
||||
if func is not None and is_variadic == 0:
|
||||
n_declared: int = 0
|
||||
p_chk: mlirlite.Param* = mlirlite.function_get_param_head(func)
|
||||
while p_chk is not None:
|
||||
n_declared += 1
|
||||
p_chk = mlirlite.param_get_next(p_chk)
|
||||
if self._func_has_eh_params(func) == 1: n_declared = n_declared - 2
|
||||
if n_declared >= 0 and count != n_declared:
|
||||
fname_chk: str = mlirlite.function_get_name(func)
|
||||
if fname_chk is None: fname_chk = call_name
|
||||
self.Gen.TypeGenErr.fatal_error(
|
||||
None,
|
||||
f"Argument count mismatch calling '{fname_chk}': the callee declares {n_declared} parameter(s) but the call passes {count} argument(s)",
|
||||
"TypeError")
|
||||
if trans is None or self._func_has_eh_params(func) == 0: return mlirlite.build_call(builder, call_name, head, count, ret_ty, is_variadic)
|
||||
# ---- 1. determine output pointers ----
|
||||
i8_ty_g: mlirlite.LLVMType* = mlirlite.Int8(pool)
|
||||
@@ -289,7 +305,7 @@ novirtual class CallGenLeafKit(CallGenCBuiltin.CallGenCBuiltin):
|
||||
elts: list[ast.AST*]* = tp.elts
|
||||
if elts is not None:
|
||||
n: sizet = len(elts)
|
||||
for i in range(n):
|
||||
for i in range(int(n)):
|
||||
if count >= 15: break
|
||||
elt: ast.AST* = elts.get(i)
|
||||
if elt is None: continue
|
||||
@@ -328,7 +344,7 @@ novirtual class CallGenLeafKit(CallGenCBuiltin.CallGenCBuiltin):
|
||||
cur: str = rawptr(cur_addr)
|
||||
if cur is None: continue
|
||||
dup: int = 0
|
||||
for vi in range(vcount):
|
||||
for vi in range(int(vcount)):
|
||||
va: sizet = visited[vi]
|
||||
if va != 0:
|
||||
vn: str = rawptr(va)
|
||||
@@ -347,12 +363,12 @@ novirtual class CallGenLeafKit(CallGenCBuiltin.CallGenCBuiltin):
|
||||
# enqueue subclasses: full scan over registered structs whose ParentNames
|
||||
# contain cur (by ordinal, not by name -- do not switch to find_struct).
|
||||
scnt: int = self.Gen.StructGen.get_struct_count()
|
||||
for i in range(scnt):
|
||||
for i in range(int(scnt)):
|
||||
e2: GenTypes.StructEntry* = self.Gen.StructGen._get_struct_entry(i)
|
||||
if e2 is None or e2.Name is None: continue
|
||||
if e2.ParentCount > 0 and e2.ParentNames is not None:
|
||||
parr: sizet* = (sizet*)(e2.ParentNames)
|
||||
for j in range(e2.ParentCount):
|
||||
for j in range(int(e2.ParentCount)):
|
||||
pa: sizet = parr[j]
|
||||
if pa != 0:
|
||||
pn: str = rawptr(pa)
|
||||
@@ -456,7 +472,7 @@ novirtual class CallGenLeafKit(CallGenCBuiltin.CallGenCBuiltin):
|
||||
param_node: mlirlite.ParamNode* = mlirlite.new_param_node(pool, i8_ptr_ty)
|
||||
pn_tail: mlirlite.ParamNode* = param_node
|
||||
if can > 0 and cargs is not None and trans is not None:
|
||||
for ai in range(can):
|
||||
for ai in range(int(can)):
|
||||
arg: ast.AST* = cargs.get(ai)
|
||||
if arg is None: continue
|
||||
arg_val: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, arg, None, 0, trans)
|
||||
@@ -516,7 +532,7 @@ novirtual class CallGenLeafKit(CallGenCBuiltin.CallGenCBuiltin):
|
||||
"""Return whether name is registered as a closure variable name (1 = yes, 0 = no)"""
|
||||
if name is None or self.ClosureNames is None: return 0
|
||||
i: sizet
|
||||
for i in range(self.ClosureCount):
|
||||
for i in range(int(self.ClosureCount)):
|
||||
if string.strcmp(self.ClosureNames + i * 64, name) == 0: return 1
|
||||
return 0
|
||||
|
||||
@@ -555,7 +571,7 @@ novirtual class CallGenLeafKit(CallGenCBuiltin.CallGenCBuiltin):
|
||||
return 1
|
||||
# idempotence/conflict check: linear scan of registered bare names (cap 64, cheap)
|
||||
i: sizet
|
||||
for i in range(self.FuncptrCount):
|
||||
for i in range(int(self.FuncptrCount)):
|
||||
idx_hit: sizet = sizet(i) * 64
|
||||
if string.strcmp(self.FuncptrNames + idx_hit, name) == 0:
|
||||
prev_ret: mlirlite.LLVMType* = (mlirlite.LLVMType*)(self.FuncptrRets[i])
|
||||
@@ -580,7 +596,7 @@ novirtual class CallGenLeafKit(CallGenCBuiltin.CallGenCBuiltin):
|
||||
def lookup_func_ptr_ret(self, name: str) -> mlirlite.LLVMType*:
|
||||
"""Look up a function-pointer variable's return type; None = unregistered (defaults to i32)"""
|
||||
if name is None or self.FuncptrNames is None: return None
|
||||
for i in range(self.FuncptrCount):
|
||||
for i in range(int(self.FuncptrCount)):
|
||||
idx: sizet = sizet(i) * 64
|
||||
if string.strcmp(self.FuncptrNames + idx, name) == 0: return (mlirlite.LLVMType*)(self.FuncptrRets[i])
|
||||
return None
|
||||
@@ -602,7 +618,7 @@ novirtual class CallGenLeafKit(CallGenCBuiltin.CallGenCBuiltin):
|
||||
tail: mlirlite.Value* = None
|
||||
arg_count: int = 0
|
||||
ai: sizet
|
||||
for ai in range(can):
|
||||
for ai in range(int(can)):
|
||||
arg: ast.AST* = cargs.get(ai)
|
||||
if arg is not None:
|
||||
arg_val: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, arg, None, 0, trans)
|
||||
@@ -621,7 +637,7 @@ novirtual class CallGenLeafKit(CallGenCBuiltin.CallGenCBuiltin):
|
||||
fp_first: mlirlite.ParamNode* = None
|
||||
fp_prev: mlirlite.ParamNode* = None
|
||||
cur_v: mlirlite.Value* = head
|
||||
for pi in range(arg_count):
|
||||
for pi in range(int(arg_count)):
|
||||
if cur_v is None: break
|
||||
cur_ty: mlirlite.LLVMType* = mlirlite.value_get_ty(cur_v)
|
||||
pn2: mlirlite.ParamNode* = mlirlite.new_param_node(pool, cur_ty)
|
||||
@@ -666,7 +682,7 @@ novirtual class CallGenLeafKit(CallGenCBuiltin.CallGenCBuiltin):
|
||||
|
||||
# translate extra args and link them
|
||||
prev: mlirlite.Value* = fmt_ptr
|
||||
for ai in range(1, can):
|
||||
for ai in range(1, int(can)):
|
||||
arg: ast.AST* = cargs.get(ai)
|
||||
if arg is not None:
|
||||
arg_val: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, arg, None, 0, trans)
|
||||
@@ -708,7 +724,7 @@ novirtual class CallGenLeafKit(CallGenCBuiltin.CallGenCBuiltin):
|
||||
arg_tail: mlirlite.Value* = None
|
||||
arg_count: int = 1 # the format string is printf arg 1
|
||||
|
||||
for ai in range(can):
|
||||
for ai in range(int(can)):
|
||||
arg: ast.AST* = cargs.get(ai)
|
||||
if arg is None: continue
|
||||
arg_val: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, arg, None, 0, trans)
|
||||
@@ -830,7 +846,7 @@ novirtual class CallGenLeafKit(CallGenCBuiltin.CallGenCBuiltin):
|
||||
return None
|
||||
# Single pass: record the first same-bare-name entry as fallback; return this module's entry early.
|
||||
fallback_entry: GenTypes.FuncEntry* = None
|
||||
for i in range(func_count):
|
||||
for i in range(int(func_count)):
|
||||
entry_addr: uint64 = uint64(funcs) + i * entry_size
|
||||
entry: GenTypes.FuncEntry* = GenTypes.FuncEntry*(entry_addr)
|
||||
if entry is not None and entry.Name is not None:
|
||||
@@ -846,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
|
||||
@@ -983,7 +999,7 @@ novirtual class CallGenLeafKit(CallGenCBuiltin.CallGenCBuiltin):
|
||||
entry_g: GenTypes.FuncEntry* = GenTypes.FuncEntry*(uint64(self.GlobalFuncs) + gi * entry_size)
|
||||
return entry_g
|
||||
return None
|
||||
for i in range(self.GlobalFuncCount):
|
||||
for i in range(int(self.GlobalFuncCount)):
|
||||
entry_addr: uint64 = uint64(self.GlobalFuncs) + i * entry_size
|
||||
entry: GenTypes.FuncEntry* = GenTypes.FuncEntry*(entry_addr)
|
||||
if entry.Name is not None:
|
||||
@@ -1004,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
|
||||
@@ -1049,9 +1065,9 @@ 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(default_count):
|
||||
for di in range(int(default_count)):
|
||||
dnode: ast.AST* = dl.get(di)
|
||||
if dnode is not None and dnode.kind() == ast.ASTKind.Constant:
|
||||
dcn: ast.Constant* = ast.Constant*(dnode)
|
||||
@@ -1099,7 +1115,7 @@ novirtual class CallGenLeafKit(CallGenCBuiltin.CallGenCBuiltin):
|
||||
"""Find default-argument info by name and optional defining-module SHA1, or None"""
|
||||
if name is None or self.GlobalDefaults is None: return None
|
||||
entry_size: sizet = GenTypes.FuncDefaultsEntry.__sizeof__()
|
||||
for i in range(self.GlobalDefaultsCount):
|
||||
for i in range(int(self.GlobalDefaultsCount)):
|
||||
entry_addr: uint64 = uint64(self.GlobalDefaults) + i * entry_size
|
||||
entry: GenTypes.FuncDefaultsEntry* = GenTypes.FuncDefaultsEntry*(entry_addr)
|
||||
if entry.Name is None: continue
|
||||
@@ -1139,7 +1155,7 @@ novirtual class CallGenLeafKit(CallGenCBuiltin.CallGenCBuiltin):
|
||||
"""Look up a name in a func table; returns the full FuncEntry or None"""
|
||||
if name is None or funcs is None: return None
|
||||
entry_size: sizet = GenTypes.FuncEntry.__sizeof__()
|
||||
for i in range(func_count):
|
||||
for i in range(int(func_count)):
|
||||
entry_addr: uint64 = uint64(funcs) + i * entry_size
|
||||
entry: GenTypes.FuncEntry* = GenTypes.FuncEntry*(entry_addr)
|
||||
if entry.Name is not None:
|
||||
@@ -1151,7 +1167,7 @@ novirtual class CallGenLeafKit(CallGenCBuiltin.CallGenCBuiltin):
|
||||
"""Find a func table entry by bare name and defining-module SHA1, or None"""
|
||||
if name is None or funcs is None: return None
|
||||
entry_size: sizet = GenTypes.FuncEntry.__sizeof__()
|
||||
for i in range(func_count):
|
||||
for i in range(int(func_count)):
|
||||
entry_addr: uint64 = uint64(funcs) + i * entry_size
|
||||
entry: GenTypes.FuncEntry* = GenTypes.FuncEntry*(entry_addr)
|
||||
if entry is None or entry.Name is None: continue
|
||||
|
||||
@@ -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"""
|
||||
@@ -405,7 +404,7 @@ novirtual class CallGenLib(CallGenBase.CallGenBase):
|
||||
|
||||
# list iteration is unsupported; use range + get instead
|
||||
api_count: sizet = len(win32_apis)
|
||||
for ai in range(api_count):
|
||||
for ai in range(int(api_count)):
|
||||
api_name: str = win32_apis.get(ai)
|
||||
if string.strcmp(api_name, func_name) == 0: return i64_ty
|
||||
|
||||
@@ -543,7 +542,7 @@ novirtual class CallGenLib(CallGenBase.CallGenBase):
|
||||
# Find the matching keyword argument
|
||||
param_name: str = mlirlite.param_get_name(cur_param)
|
||||
if param_name is not None:
|
||||
for ki in range(kw_count):
|
||||
for ki in range(int(kw_count)):
|
||||
kw: ast.Keyword* = ast.Keyword*(keywords.get(ki))
|
||||
if kw is None or kw.arg is None or kw.value is None: continue
|
||||
if self.Gen.CallGenCall._kwarg_name_matches(kw.arg, param_name) == 1:
|
||||
|
||||
+1420
-1418
File diff suppressed because it is too large.
Load diff
@@ -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
|
||||
@@ -120,7 +119,7 @@ novirtual class CallGenProp(CallGenBase.CallGenBase):
|
||||
n_av: sizet = string.strlen(annot_val)
|
||||
last_dot_av: sizet = n_av
|
||||
depth_av: int = 0
|
||||
for ai_av in range(n_av):
|
||||
for ai_av in range(int(n_av)):
|
||||
ch_av: int = annot_val[ai_av]
|
||||
if ch_av == '[': depth_av += 1
|
||||
elif ch_av == ']': depth_av -= 1
|
||||
@@ -226,7 +225,7 @@ novirtual class CallGenProp(CallGenBase.CallGenBase):
|
||||
can: sizet = 0
|
||||
if cargs is not None: can = len(cargs)
|
||||
tail: mlirlite.Value* = head
|
||||
for ai in range(can):
|
||||
for ai in range(int(can)):
|
||||
arg_node: ast.AST* = cargs.get(ai)
|
||||
if arg_node is None: continue
|
||||
arg_val: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, arg_node, None, 0, trans)
|
||||
|
||||
@@ -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,4 +1,3 @@
|
||||
import t, c
|
||||
from stdint import *
|
||||
import ast
|
||||
import mlirlite
|
||||
@@ -35,7 +34,7 @@ novirtual class CallGenVt(CallGenBase.CallGenBase):
|
||||
# Build the vtable type { i8*, i8*, ... }; its field list uses mlirlite.ParamNode, not Function*.
|
||||
vt_first: mlirlite.ParamNode* = None
|
||||
vt_prev: mlirlite.ParamNode* = None
|
||||
for vi in range(method_count):
|
||||
for vi in range(int(method_count)):
|
||||
pn: mlirlite.ParamNode* = mlirlite.new_param_node(pool, i8_ptr_ty)
|
||||
if pn is None: continue
|
||||
if vt_first is None: vt_first = pn
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import t, c
|
||||
from stdint import *
|
||||
import ast
|
||||
import memhub
|
||||
@@ -164,7 +163,7 @@ novirtual class ClassGenBase:
|
||||
bases: list[ast.AST*]* = cd.bases
|
||||
bn: sizet = len(bases)
|
||||
if bn == 0: return 0
|
||||
for bi in range(bn):
|
||||
for bi in range(int(bn)):
|
||||
base_node: ast.AST* = bases.get(bi)
|
||||
if base_node is None: continue
|
||||
if self._is_cenum_base(base_node, trans) == 1: return 1
|
||||
@@ -202,7 +201,7 @@ novirtual class ClassGenBase:
|
||||
bases: list[ast.AST*]* = cd.bases
|
||||
bn: sizet = len(bases)
|
||||
if bn == 0: return 0
|
||||
for bi in range(bn):
|
||||
for bi in range(int(bn)):
|
||||
base_node: ast.AST* = bases.get(bi)
|
||||
if base_node is None: continue
|
||||
if self._is_cunion_base(base_node, trans) == 1: return 1
|
||||
@@ -240,7 +239,7 @@ novirtual class ClassGenBase:
|
||||
bases: list[ast.AST*]* = cd.bases
|
||||
bn: sizet = len(bases)
|
||||
if bn == 0: return 0
|
||||
for bi in range(bn):
|
||||
for bi in range(int(bn)):
|
||||
base_node: ast.AST* = bases.get(bi)
|
||||
if base_node is None: continue
|
||||
if self._is_crenum_base(base_node, trans) == 1: return 1
|
||||
@@ -250,7 +249,7 @@ novirtual class ClassGenBase:
|
||||
if decorator_list is None: return 0
|
||||
dn: sizet = len(decorator_list)
|
||||
if dn == 0: return 0
|
||||
for di in range(dn):
|
||||
for di in range(int(dn)):
|
||||
deco: ast.AST* = decorator_list.get(di)
|
||||
if deco is None: continue
|
||||
k: int = deco.kind()
|
||||
@@ -285,7 +284,7 @@ novirtual class ClassGenBase:
|
||||
if decorator_list is None: return 0
|
||||
dn: sizet = len(decorator_list)
|
||||
if dn == 0: return 0
|
||||
for di in range(dn):
|
||||
for di in range(int(dn)):
|
||||
deco: ast.AST* = decorator_list.get(di)
|
||||
if deco is None: continue
|
||||
k: int = deco.kind()
|
||||
@@ -297,7 +296,7 @@ novirtual class ClassGenBase:
|
||||
if at0.attr is None or string.strcmp(at0.attr, "Attribute") != 0: continue
|
||||
if cl.args is None: continue
|
||||
an: sizet = len(cl.args)
|
||||
for ai in range(an):
|
||||
for ai in range(int(an)):
|
||||
ag: ast.AST* = cl.args.get(ai)
|
||||
if ag is not None and self._tattr_name(ag, attr_name) != 0: return 1
|
||||
else:
|
||||
@@ -324,7 +323,7 @@ novirtual class ClassGenBase:
|
||||
if decorator_list is None: return 0
|
||||
dn: sizet = len(decorator_list)
|
||||
if dn == 0: return 0
|
||||
for di in range(dn):
|
||||
for di in range(int(dn)):
|
||||
deco: ast.AST* = decorator_list.get(di)
|
||||
if deco is None: continue
|
||||
if deco.kind() == ast.ASTKind.Call:
|
||||
@@ -334,7 +333,7 @@ novirtual class ClassGenBase:
|
||||
if at0.attr is None or string.strcmp(at0.attr, "Attribute") != 0: continue
|
||||
if cl.args is None: continue
|
||||
an: sizet = len(cl.args)
|
||||
for ai in range(an):
|
||||
for ai in range(int(an)):
|
||||
ag: ast.AST* = cl.args.get(ai)
|
||||
if ag is not None:
|
||||
av: int = self._tattr_aligned_int(ag)
|
||||
@@ -349,7 +348,7 @@ novirtual class ClassGenBase:
|
||||
if decorator_list is None: return meta
|
||||
dn: sizet = len(decorator_list)
|
||||
if dn == 0: return meta
|
||||
for di in range(dn):
|
||||
for di in range(int(dn)):
|
||||
deco: ast.AST* = decorator_list.get(di)
|
||||
if deco is None: continue
|
||||
k: int = deco.kind()
|
||||
@@ -390,7 +389,7 @@ novirtual class ClassGenBase:
|
||||
self.ClassMetaCount = 0
|
||||
if self.ClassMetaCount >= CLASS_META_MAX: return -1
|
||||
# Idempotent: skip duplicate (kind, cls, field).
|
||||
for i in range(self.ClassMetaCount):
|
||||
for i in range(int(self.ClassMetaCount)):
|
||||
if self.ClassMetaKinds[i] == kind:
|
||||
if string.strcmp(self.ClassMetaClss + i * 64, cls) == 0:
|
||||
if string.strcmp(self.ClassMetaFields + i * 64, field) == 0: return 0
|
||||
@@ -406,7 +405,7 @@ novirtual class ClassGenBase:
|
||||
"""Return the field count of the given metadata kind for a class."""
|
||||
if self.ClassMetaKinds is None or cls is None: return 0
|
||||
n_cm: int = 0
|
||||
for i in range(self.ClassMetaCount):
|
||||
for i in range(int(self.ClassMetaCount)):
|
||||
if self.ClassMetaKinds[i] == kind:
|
||||
if string.strcmp(self.ClassMetaClss + i * 64, cls) == 0: n_cm += 1
|
||||
return n_cm
|
||||
@@ -414,7 +413,7 @@ novirtual class ClassGenBase:
|
||||
"""Return the idx-th field name of the given metadata kind for a class."""
|
||||
if self.ClassMetaKinds is None or cls is None: return None
|
||||
n_cm2: int = 0
|
||||
for i in range(self.ClassMetaCount):
|
||||
for i in range(int(self.ClassMetaCount)):
|
||||
if self.ClassMetaKinds[i] == kind:
|
||||
if string.strcmp(self.ClassMetaClss + i * 64, cls) == 0:
|
||||
if n_cm2 == idx: return self.ClassMetaFields + i * 64
|
||||
@@ -434,7 +433,7 @@ novirtual class ClassGenBase:
|
||||
self.DecoratorFuncCount = 0
|
||||
if self.DecoratorFuncCount >= DECOR_FUNC_MAX: return -1
|
||||
# Idempotent: skip an already-registered function name (multi-pass scan).
|
||||
for i in range(self.DecoratorFuncCount):
|
||||
for i in range(int(self.DecoratorFuncCount)):
|
||||
if string.strcmp(self.DecoratorFuncNames + i * DECOR_NAME_LEN, func_name) == 0: return 0
|
||||
idx_dc: sizet = sizet(self.DecoratorFuncCount) * DECOR_NAME_LEN
|
||||
string.strncpy(self.DecoratorFuncNames + idx_dc, func_name, DECOR_NAME_LEN - 1)
|
||||
@@ -447,13 +446,13 @@ novirtual class ClassGenBase:
|
||||
def lookup_decor_wrapper(self, func_name: str) -> str:
|
||||
"""Look up the wrapper name by original function name; None when not registered."""
|
||||
if self.DecoratorFuncNames is None or func_name is None: return None
|
||||
for i in range(self.DecoratorFuncCount):
|
||||
for i in range(int(self.DecoratorFuncCount)):
|
||||
if string.strcmp(self.DecoratorFuncNames + i * DECOR_NAME_LEN, func_name) == 0: return self.DecoratorWrapNames + i * DECOR_NAME_LEN
|
||||
return None
|
||||
def lookup_decor_arg0(self, func_name: str) -> int64:
|
||||
"""Look up a decorator's first constant arg by original function name (0 when none)."""
|
||||
if self.DecoratorFuncNames is None or func_name is None: return 0
|
||||
for i in range(self.DecoratorFuncCount):
|
||||
for i in range(int(self.DecoratorFuncCount)):
|
||||
if string.strcmp(self.DecoratorFuncNames + i * DECOR_NAME_LEN, func_name) == 0: return self.DecoratorArgs[i]
|
||||
return 0
|
||||
# ============================================================
|
||||
@@ -478,7 +477,7 @@ novirtual class ClassGenBase:
|
||||
if class_name is None: return -1
|
||||
if self._ensure_exception_tables() != 0: return -1
|
||||
# Idempotent: return the existing code if already registered (multi-pass).
|
||||
for i in range(self.ExceptionCount):
|
||||
for i in range(int(self.ExceptionCount)):
|
||||
if string.strcmp(self.ExceptionNames + i * EXCEPTION_NAME_LEN, class_name) == 0: return self.ExceptionCodes[i]
|
||||
if self.ExceptionCount >= EXCEPTION_MAX: return -1
|
||||
idx_e: sizet = sizet(self.ExceptionCount) * EXCEPTION_NAME_LEN
|
||||
@@ -512,13 +511,13 @@ novirtual class ClassGenBase:
|
||||
bi: int = self._builtin_exception_code(name)
|
||||
if bi != 0: return bi
|
||||
if self.ExceptionNames is None: return 0
|
||||
for i in range(self.ExceptionCount):
|
||||
for i in range(int(self.ExceptionCount)):
|
||||
if string.strcmp(self.ExceptionNames + i * EXCEPTION_NAME_LEN, name) == 0: return self.ExceptionCodes[i]
|
||||
return 0
|
||||
def lookup_exception_parent(self, name: str) -> str:
|
||||
"""Look up a user exception's parent name; None when it is not a user exception."""
|
||||
if name is None or self.ExceptionNames is None: return None
|
||||
for i in range(self.ExceptionCount):
|
||||
for i in range(int(self.ExceptionCount)):
|
||||
if string.strcmp(self.ExceptionNames + i * EXCEPTION_NAME_LEN, name) == 0:
|
||||
p_e: str = self.ExceptionParents + i * EXCEPTION_NAME_LEN
|
||||
if p_e is not None and p_e[0] != '\0': return p_e
|
||||
@@ -529,7 +528,7 @@ novirtual class ClassGenBase:
|
||||
if name is None: return 0
|
||||
if self._builtin_exception_code(name) != 0: return 1
|
||||
if self.ExceptionNames is None: return 0
|
||||
for i in range(self.ExceptionCount):
|
||||
for i in range(int(self.ExceptionCount)):
|
||||
if string.strcmp(self.ExceptionNames + i * EXCEPTION_NAME_LEN, name) == 0: return 1
|
||||
return 0
|
||||
def collect_descendant_codes(self, name: str, out_codes: int*, max_codes: int) -> int:
|
||||
@@ -545,7 +544,7 @@ novirtual class ClassGenBase:
|
||||
if code_e != 0 and self._exception_codes_tmp_count < max_codes:
|
||||
# Deduplicate (guards against inheritance cycles).
|
||||
dup: int = 0
|
||||
for i in range(self._exception_codes_tmp_count):
|
||||
for i in range(int(self._exception_codes_tmp_count)):
|
||||
if out_codes[i] == code_e:
|
||||
dup = 1
|
||||
break
|
||||
@@ -554,7 +553,7 @@ novirtual class ClassGenBase:
|
||||
self._exception_codes_tmp_count += 1
|
||||
# Recurse over user exceptions whose parent is name.
|
||||
if self.ExceptionNames is None: return
|
||||
for i in range(self.ExceptionCount):
|
||||
for i in range(int(self.ExceptionCount)):
|
||||
cname_e: str = self.ExceptionNames + i * EXCEPTION_NAME_LEN
|
||||
pname_e: str = self.ExceptionParents + i * EXCEPTION_NAME_LEN
|
||||
if pname_e is not None and pname_e[0] != '\0':
|
||||
@@ -564,7 +563,7 @@ novirtual class ClassGenBase:
|
||||
if cd is None or cd.bases is None: return 0
|
||||
bases_e: list[ast.AST*]* = cd.bases
|
||||
bn_e: sizet = len(bases_e)
|
||||
for bi_e in range(bn_e):
|
||||
for bi_e in range(int(bn_e)):
|
||||
base_e: ast.AST* = bases_e.get(bi_e)
|
||||
if base_e is None: continue
|
||||
base_name_e: str = None
|
||||
@@ -581,7 +580,7 @@ novirtual class ClassGenBase:
|
||||
if cd is None or cd.bases is None: return None
|
||||
bases_e2: list[ast.AST*]* = cd.bases
|
||||
bn_e2: sizet = len(bases_e2)
|
||||
for bi_e2 in range(bn_e2):
|
||||
for bi_e2 in range(int(bn_e2)):
|
||||
base_e2: ast.AST* = bases_e2.get(bi_e2)
|
||||
if base_e2 is None: continue
|
||||
base_name_e2: str = None
|
||||
@@ -606,7 +605,7 @@ novirtual class ClassGenBase:
|
||||
if lst_rcm.elts is None: return
|
||||
elts_rcm: list[ast.AST*]* = lst_rcm.elts
|
||||
en_rcm: sizet = len(elts_rcm)
|
||||
for ei_rcm in range(en_rcm):
|
||||
for ei_rcm in range(int(en_rcm)):
|
||||
el_rcm: ast.AST* = elts_rcm.get(ei_rcm)
|
||||
if el_rcm is None or el_rcm.kind() != ast.ASTKind.Constant: continue
|
||||
ec_rcm: ast.Constant* = ast.Constant*(el_rcm)
|
||||
@@ -629,7 +628,7 @@ novirtual class ClassGenBase:
|
||||
viperlib.snprintf(key_buf, cl_len + mn_len + 2, "%s.%s", class_name, method_name)
|
||||
# Update duplicate entry.
|
||||
i: int
|
||||
for i in range(self.MethodMetaCount):
|
||||
for i in range(int(self.MethodMetaCount)):
|
||||
if string.strcmp(self.MethodMetaKeys + i * META_KEY_LEN, key_buf) == 0:
|
||||
flags_ptr: int* = (int*)(uint64(self.MethodMetaFlags) + i * 4)
|
||||
# OR-combine flags: a getter then setter/deleter registers repeatedly,
|
||||
@@ -655,7 +654,7 @@ novirtual class ClassGenBase:
|
||||
viperlib.snprintf(key_buf, cl_len + mn_len + 2, "%s.%s", class_name, method_name)
|
||||
found: int = 0
|
||||
i: int
|
||||
for i in range(self.MethodMetaCount):
|
||||
for i in range(int(self.MethodMetaCount)):
|
||||
if string.strcmp(self.MethodMetaKeys + i * META_KEY_LEN, key_buf) == 0:
|
||||
flags_ptr: int* = (int*)(uint64(self.MethodMetaFlags) + i * 4)
|
||||
found = flags_ptr[0]
|
||||
@@ -728,7 +727,7 @@ novirtual class ClassGenBase:
|
||||
bases: list[ast.AST*]* = cd.bases
|
||||
bn: sizet = len(bases)
|
||||
if bn == 0: return None
|
||||
for bi in range(bn):
|
||||
for bi in range(int(bn)):
|
||||
base_node: ast.AST* = bases.get(bi)
|
||||
if base_node is None: continue
|
||||
_fi_pc: str = None
|
||||
@@ -762,7 +761,7 @@ novirtual class ClassGenBase:
|
||||
bases: list[ast.AST*]* = cd.bases
|
||||
bn: sizet = len(bases)
|
||||
count: int = 0
|
||||
for bi in range(bn):
|
||||
for bi in range(int(bn)):
|
||||
if count >= max_parents: break
|
||||
base_node: ast.AST* = bases.get(bi)
|
||||
if base_node is None: continue
|
||||
@@ -779,7 +778,7 @@ novirtual class ClassGenBase:
|
||||
def _field_name_exists(self, names_buf: sizet*, count: int, name: str) -> int:
|
||||
"""Return 1 if the field name already exists in names_buf (first-declared parent wins; duplicates skipped)."""
|
||||
if names_buf is None or name is None: return 0
|
||||
for fi in range(count):
|
||||
for fi in range(int(count)):
|
||||
addr: sizet = names_buf[fi]
|
||||
if addr != 0:
|
||||
existing: str = rawptr(addr)
|
||||
@@ -789,7 +788,7 @@ novirtual class ClassGenBase:
|
||||
"""Return 1 if the class body declares fields (AnnAssign); 0=methods-only/no-field class."""
|
||||
if cd is None or cd.children is None: return 0
|
||||
cn: sizet = len(cd.children)
|
||||
for ci in range(cn):
|
||||
for ci in range(int(cn)):
|
||||
stmt: ast.AST* = cd.children.get(ci)
|
||||
if stmt is None: continue
|
||||
if stmt.kind() == ast.ASTKind.AnnAssign: return 1
|
||||
@@ -801,7 +800,7 @@ novirtual class ClassGenBase:
|
||||
bases: list[ast.AST*]* = cd.bases
|
||||
bn: sizet = len(bases)
|
||||
if bn == 0: return
|
||||
for bi in range(bn):
|
||||
for bi in range(int(bn)):
|
||||
base_node: ast.AST* = bases.get(bi)
|
||||
if base_node is None: continue
|
||||
bk: int = base_node.kind()
|
||||
@@ -831,7 +830,7 @@ novirtual class ClassGenBase:
|
||||
if cd is None or cd.children is None: return 0
|
||||
children: list[ast.AST*]* = cd.children
|
||||
cn: sizet = len(children)
|
||||
for ci in range(cn):
|
||||
for ci in range(int(cn)):
|
||||
stmt: ast.AST* = children.get(ci)
|
||||
if stmt is None: continue
|
||||
if stmt.kind() != ast.ASTKind.FunctionDef: continue
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import lib.core.Gen.GenTypes as GenTypes
|
||||
import t, c
|
||||
from stdint import *
|
||||
import ast
|
||||
import mlirlite
|
||||
@@ -162,9 +161,13 @@ novirtual class ClassGenDef(ClassGenBase.ClassGenBase):
|
||||
# Field const-object flag buffer (1 = annotation has t.CConst and the type is not
|
||||
# a pointer), parallel to field_volatile_buf with values 0/1.
|
||||
field_const_buf: sizet* = pool.alloc(8 * GenTypes.FIELD_MAX)
|
||||
# Field byte-order buffer (0 = native, 1 = t.BigEndian, 2 = t.LittleEndian), parallel
|
||||
# to field_volatile_buf; from the `X | t.BigEndian` annotation union.
|
||||
field_byteorder_buf: sizet* = pool.alloc(8 * GenTypes.FIELD_MAX)
|
||||
if field_names_buf is None or field_types_buf is None or field_defaults_buf is None: return 0
|
||||
if field_annot_buf is None or field_volatile_buf is None: return 0
|
||||
if field_pointee_volatile_buf is None or field_const_buf is None: return 0
|
||||
if field_byteorder_buf is None: return 0
|
||||
string.memset(field_names_buf, 0, 8 * GenTypes.FIELD_MAX)
|
||||
string.memset(field_types_buf, 0, 8 * GenTypes.FIELD_MAX)
|
||||
string.memset(field_defaults_buf, 0, 8 * GenTypes.FIELD_MAX)
|
||||
@@ -172,13 +175,14 @@ novirtual class ClassGenDef(ClassGenBase.ClassGenBase):
|
||||
string.memset(field_volatile_buf, 0, 8 * GenTypes.FIELD_MAX)
|
||||
string.memset(field_pointee_volatile_buf, 0, 8 * GenTypes.FIELD_MAX)
|
||||
string.memset(field_const_buf, 0, 8 * GenTypes.FIELD_MAX)
|
||||
string.memset(field_byteorder_buf, 0, 8 * GenTypes.FIELD_MAX)
|
||||
|
||||
field_count: int = 0
|
||||
|
||||
# 1.5 Inherit parent fields (field flattening): parent fields precede child
|
||||
# fields, layout [vtable_ptr?, parent..., child...]; dedup by name (first wins).
|
||||
if parents_count > 0 and parents_buf is not None:
|
||||
for pi in range(parents_count):
|
||||
for pi in range(int(parents_count)):
|
||||
pname_addr: sizet = parents_buf[pi]
|
||||
if pname_addr == 0: continue
|
||||
pname: str = rawptr(pname_addr)
|
||||
@@ -194,7 +198,7 @@ novirtual class ClassGenDef(ClassGenBase.ClassGenBase):
|
||||
# entries survive passes and the existing early-return never repairs it).
|
||||
return 0
|
||||
pfc: int = parent_entry.FieldCount
|
||||
for pfi in range(pfc):
|
||||
for pfi in range(int(pfc)):
|
||||
pfe: GenTypes.FieldEntry* = self.Gen.StructGen._get_field_entry(parent_entry, pfi)
|
||||
if pfe is None: continue
|
||||
pfn: str = self.Gen.StructGen.get_field_name_ptr(pfe)
|
||||
@@ -221,13 +225,15 @@ novirtual class ClassGenDef(ClassGenBase.ClassGenBase):
|
||||
field_pointee_volatile_buf[field_count] = sizet(pfe.PointeeVolatile)
|
||||
# Inherit the const-object flag (unchanged after flattening)
|
||||
field_const_buf[field_count] = sizet(pfe.IsConst)
|
||||
# Inherit the byte-order flag (unchanged after flattening)
|
||||
field_byteorder_buf[field_count] = sizet(pfe.ByteOrder)
|
||||
field_count += 1
|
||||
|
||||
children: list[ast.AST*]* = cd.children
|
||||
if children is None: return 0
|
||||
|
||||
cn: sizet = len(children)
|
||||
for ci in range(cn):
|
||||
for ci in range(int(cn)):
|
||||
stmt: ast.AST* = children.get(ci)
|
||||
if stmt is None: continue
|
||||
sk: int = stmt.kind()
|
||||
@@ -340,11 +346,17 @@ novirtual class ClassGenDef(ClassGenBase.ClassGenBase):
|
||||
# through the object fail-fasts, matching TPC's Gen.field_const_flags.
|
||||
if self.Gen.TypeGenErr.is_const_object_marker(aa.annotation, field_ty) != 0: field_const_buf[field_count] = 1
|
||||
else: field_const_buf[field_count] = 0
|
||||
# Byte-order marker (t.BigEndian / t.LittleEndian) -> 1 / 2; annotation `|`
|
||||
# means "and", so `X | t.BigEndian` is a BigEndian-encoded X field.
|
||||
if self.Gen.TypeGenErr.has_decorator_marker(aa.annotation, "BigEndian", 0) != 0: field_byteorder_buf[field_count] = 1
|
||||
elif self.Gen.TypeGenErr.has_decorator_marker(aa.annotation, "LittleEndian", 0) != 0: field_byteorder_buf[field_count] = 2
|
||||
else: field_byteorder_buf[field_count] = 0
|
||||
else:
|
||||
field_annot_buf[field_count] = 0
|
||||
field_volatile_buf[field_count] = 0
|
||||
field_pointee_volatile_buf[field_count] = 0
|
||||
field_const_buf[field_count] = 0
|
||||
field_byteorder_buf[field_count] = 0
|
||||
field_count += 1
|
||||
|
||||
if field_count == 0:
|
||||
@@ -373,7 +385,7 @@ novirtual class ClassGenDef(ClassGenBase.ClassGenBase):
|
||||
prev_node = vtable_node
|
||||
total_field_count = field_count + 1
|
||||
|
||||
for fi in range(field_count):
|
||||
for fi in range(int(field_count)):
|
||||
fty_addr: sizet = field_types_buf[fi]
|
||||
if fty_addr == 0: continue
|
||||
fty: mlirlite.LLVMType* = (mlirlite.LLVMType*)(fty_addr)
|
||||
@@ -439,7 +451,7 @@ novirtual class ClassGenDef(ClassGenBase.ClassGenBase):
|
||||
|
||||
# Scan field types and declare cross-module struct refs in the current module, e.g.
|
||||
# list[int].__pool__ referencing memhub.MemManager needs a %"sha1.MemManager" opaque.
|
||||
for fi in range(field_count):
|
||||
for fi in range(int(field_count)):
|
||||
fty_addr_op: sizet = field_types_buf[fi]
|
||||
if fty_addr_op == 0: continue
|
||||
fty_op: mlirlite.LLVMType* = (mlirlite.LLVMType*)(fty_addr_op)
|
||||
@@ -492,7 +504,7 @@ novirtual class ClassGenDef(ClassGenBase.ClassGenBase):
|
||||
i8_ptr_ty2: mlirlite.LLVMType* = mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||||
self.Gen.StructGen.add_field(pool, entry, "__vtable__", i8_ptr_ty2, None, None)
|
||||
|
||||
for fi in range(field_count):
|
||||
for fi in range(int(field_count)):
|
||||
fname_addr: sizet = field_names_buf[fi]
|
||||
if fname_addr == 0: continue
|
||||
fname: str = rawptr(fname_addr)
|
||||
@@ -518,6 +530,10 @@ novirtual class ClassGenDef(ClassGenBase.ClassGenBase):
|
||||
if fidx_add >= 0 and field_pointee_volatile_buf[fi] != 0:
|
||||
fe_pv: GenTypes.FieldEntry* = self.Gen.StructGen._get_field_entry(entry, fidx_add)
|
||||
if fe_pv is not None: fe_pv.PointeeVolatile = 1
|
||||
# byte-order field flag (field_byteorder_buf[fi] corresponds 1:1 with add_field order)
|
||||
if fidx_add >= 0 and field_byteorder_buf[fi] != 0:
|
||||
fe_bo: GenTypes.FieldEntry* = self.Gen.StructGen._get_field_entry(entry, fidx_add)
|
||||
if fe_bo is not None: fe_bo.ByteOrder = int(field_byteorder_buf[fi])
|
||||
# const object field flag (field_const_buf[fi] corresponds 1:1 with add_field order)
|
||||
if fidx_add >= 0 and field_const_buf[fi] != 0:
|
||||
fe_c: GenTypes.FieldEntry* = self.Gen.StructGen._get_field_entry(entry, fidx_add)
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import lib.core.Gen.GenTypes as GenTypes
|
||||
import t, c
|
||||
from stdint import *
|
||||
import ast
|
||||
import mlirlite
|
||||
@@ -168,7 +167,7 @@ novirtual class ClassGenEnum(ClassGenBase.ClassGenBase):
|
||||
"""Count visible names in an element table (shared by Tuple/List)."""
|
||||
if elts is None: return 0
|
||||
n: int = 0
|
||||
for i in range(len(elts)): n += self._enum_target_visible_names(elts.get(i))
|
||||
for i in range(int(len(elts))): n += self._enum_target_visible_names(elts.get(i))
|
||||
return n
|
||||
def _enum_target_visible_names(self, node: ast.AST*) -> int:
|
||||
"""Count visible names at a member-name position (Name / Tuple / List items, Starred recursion)."""
|
||||
@@ -189,14 +188,14 @@ novirtual class ClassGenEnum(ClassGenBase.ClassGenBase):
|
||||
"""Total source-visible member names in an enum class body (including forms the registrar rejects)."""
|
||||
if children is None: return 0
|
||||
n: int = 0
|
||||
for ci in range(len(children)):
|
||||
for ci in range(int(len(children))):
|
||||
stmt: ast.AST* = children.get(ci)
|
||||
if stmt is None: continue
|
||||
sk: int = stmt.kind()
|
||||
if sk == ast.ASTKind.Assign:
|
||||
ag: ast.Assign* = ast.Assign*(stmt)
|
||||
if ag.targets is not None:
|
||||
for ti in range(len(ag.targets)): n += self._enum_target_visible_names(ag.targets.get(ti))
|
||||
for ti in range(int(len(ag.targets))): n += self._enum_target_visible_names(ag.targets.get(ti))
|
||||
elif sk == ast.ASTKind.AnnAssign:
|
||||
aa: ast.AnnAssign* = ast.AnnAssign*(stmt)
|
||||
n += self._enum_target_visible_names(aa.target)
|
||||
@@ -207,7 +206,7 @@ novirtual class ClassGenEnum(ClassGenBase.ClassGenBase):
|
||||
def _enum_first_unregistered_node(self, children: list[ast.AST*]*) -> ast.AST*:
|
||||
"""First statement with visible member names the registrar rejects, for fail-fast line location; None if none."""
|
||||
if children is None: return None
|
||||
for ci in range(len(children)):
|
||||
for ci in range(int(len(children))):
|
||||
stmt: ast.AST* = children.get(ci)
|
||||
if stmt is None: continue
|
||||
sk: int = stmt.kind()
|
||||
@@ -220,7 +219,7 @@ novirtual class ClassGenEnum(ClassGenBase.ClassGenBase):
|
||||
elif sk == ast.ASTKind.Assign:
|
||||
ag: ast.Assign* = ast.Assign*(stmt)
|
||||
if ag.targets is not None:
|
||||
for ti in range(len(ag.targets)):
|
||||
for ti in range(int(len(ag.targets))):
|
||||
tgt: ast.AST* = ag.targets.get(ti)
|
||||
if tgt is not None and tgt.kind() != ast.ASTKind.Name and self._enum_target_visible_names(tgt) > 0: return stmt
|
||||
return None
|
||||
@@ -253,7 +252,7 @@ novirtual class ClassGenEnum(ClassGenBase.ClassGenBase):
|
||||
member_count: int = 0
|
||||
next_val: int64 = 0
|
||||
|
||||
for ci in range(cn):
|
||||
for ci in range(int(cn)):
|
||||
stmt: ast.AST* = children.get(ci)
|
||||
if stmt is None: continue
|
||||
sk: int = stmt.kind()
|
||||
@@ -320,7 +319,7 @@ novirtual class ClassGenEnum(ClassGenBase.ClassGenBase):
|
||||
next_val = cur_val_a + 1
|
||||
|
||||
# Store per target (non-Name targets are caught by the registration-side fallback check)
|
||||
for ti in range(tn):
|
||||
for ti in range(int(tn)):
|
||||
t0: ast.AST* = tgts.get(ti)
|
||||
if t0 is None or t0.kind() != ast.ASTKind.Name: continue
|
||||
tgt_a: ast.Name* = ast.Name*(t0)
|
||||
@@ -349,7 +348,7 @@ novirtual class ClassGenEnum(ClassGenBase.ClassGenBase):
|
||||
# Determine the base type: the widest member type in bits
|
||||
base_ty: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
||||
max_bits: int = 0
|
||||
for mi in range(member_count):
|
||||
for mi in range(int(member_count)):
|
||||
ty_addr: sizet = member_types_buf[mi]
|
||||
if ty_addr == 0: continue
|
||||
mty: mlirlite.LLVMType* = (mlirlite.LLVMType*)(ty_addr)
|
||||
@@ -365,7 +364,7 @@ novirtual class ClassGenEnum(ClassGenBase.ClassGenBase):
|
||||
return 0
|
||||
|
||||
# Add members
|
||||
for mi in range(member_count):
|
||||
for mi in range(int(member_count)):
|
||||
fname_addr: sizet = member_names_buf[mi]
|
||||
if fname_addr == 0: continue
|
||||
mname: str = rawptr(fname_addr)
|
||||
@@ -486,7 +485,7 @@ novirtual class ClassGenEnum(ClassGenBase.ClassGenBase):
|
||||
field_count: int = 0
|
||||
max_size: int = 0
|
||||
|
||||
for ci in range(cn):
|
||||
for ci in range(int(cn)):
|
||||
stmt: ast.AST* = children.get(ci)
|
||||
if stmt is None: continue
|
||||
sk: int = stmt.kind()
|
||||
@@ -572,7 +571,7 @@ novirtual class ClassGenEnum(ClassGenBase.ClassGenBase):
|
||||
entry.IsUnion = 1
|
||||
|
||||
# Add field info (all fields have Index=0 because they share offset 0)
|
||||
for fi in range(field_count):
|
||||
for fi in range(int(field_count)):
|
||||
fname_addr: sizet = field_names_buf[fi]
|
||||
if fname_addr == 0: continue
|
||||
fname: str = rawptr(fname_addr)
|
||||
@@ -665,7 +664,7 @@ novirtual class ClassGenEnum(ClassGenBase.ClassGenBase):
|
||||
variant_count: int = 0
|
||||
max_field_count: int = 0 # max field count across all variants
|
||||
|
||||
for ci in range(cn):
|
||||
for ci in range(int(cn)):
|
||||
stmt: ast.AST* = children.get(ci)
|
||||
if stmt is None: continue
|
||||
sk: int = stmt.kind()
|
||||
@@ -679,7 +678,7 @@ novirtual class ClassGenEnum(ClassGenBase.ClassGenBase):
|
||||
variant_children: list[ast.AST*]* = variant_cd.children
|
||||
vcn: sizet = len(variant_children)
|
||||
field_idx: int = 0
|
||||
for vi in range(vcn):
|
||||
for vi in range(int(vcn)):
|
||||
vstmt: ast.AST* = variant_children.get(vi)
|
||||
if vstmt is None: continue
|
||||
vk: int = vstmt.kind()
|
||||
@@ -737,9 +736,9 @@ novirtual class ClassGenEnum(ClassGenBase.ClassGenBase):
|
||||
position_max_sizes[0] = sizet(4)
|
||||
|
||||
# Positions 1+: take the max field size across all variants at each position
|
||||
for pos in range(1, total_positions):
|
||||
for pos in range(1, int(total_positions)):
|
||||
pos_max: sizet = 0
|
||||
for vi in range(variant_count):
|
||||
for vi in range(int(variant_count)):
|
||||
if vi >= 64:
|
||||
break # variant_field_sizes 64-class slot cap; guards against out-of-bounds reads
|
||||
field_pos: int = pos - 1 # variant fields start at position 0
|
||||
@@ -757,7 +756,7 @@ novirtual class ClassGenEnum(ClassGenBase.ClassGenBase):
|
||||
first_node: mlirlite.ParamNode* = None
|
||||
tail_node: mlirlite.ParamNode* = None
|
||||
|
||||
for pos in range(total_positions):
|
||||
for pos in range(int(total_positions)):
|
||||
psize: int = int(position_max_sizes[pos])
|
||||
field_ty: mlirlite.LLVMType* = i32_ty
|
||||
if psize > 4: field_ty = i64_ty
|
||||
@@ -815,7 +814,7 @@ novirtual class ClassGenEnum(ClassGenBase.ClassGenBase):
|
||||
self.Gen.StructGen.add_field(pool, entry, "__tag", i32_ty, None, None)
|
||||
|
||||
# Add payload fields (positions 1+, i32 or i64)
|
||||
for pos in range(1, total_positions):
|
||||
for pos in range(1, int(total_positions)):
|
||||
psize2: int = int(position_max_sizes[pos])
|
||||
payload_ty: mlirlite.LLVMType* = i32_ty
|
||||
if psize2 > 4: payload_ty = i64_ty
|
||||
@@ -839,7 +838,7 @@ novirtual class ClassGenEnum(ClassGenBase.ClassGenBase):
|
||||
children: list[ast.AST*]* = cd.children
|
||||
if children is None: return
|
||||
cn: sizet = len(children)
|
||||
for ci in range(cn):
|
||||
for ci in range(int(cn)):
|
||||
stmt: ast.AST* = children.get(ci)
|
||||
if stmt is None or stmt.kind() != ast.ASTKind.ClassDef: continue
|
||||
variant_cd: ast.ClassDef* = ast.ClassDef*(stmt)
|
||||
@@ -847,7 +846,7 @@ novirtual class ClassGenEnum(ClassGenBase.ClassGenBase):
|
||||
vchildren: list[ast.AST*]* = variant_cd.children
|
||||
vcn: sizet = len(vchildren)
|
||||
field_idx: int = 0
|
||||
for vi in range(vcn):
|
||||
for vi in range(int(vcn)):
|
||||
vstmt: ast.AST* = vchildren.get(vi)
|
||||
if vstmt is None: continue
|
||||
if vstmt.kind() != ast.ASTKind.AnnAssign: continue
|
||||
@@ -902,7 +901,7 @@ novirtual class ClassGenEnum(ClassGenBase.ClassGenBase):
|
||||
# VLogger.error(f"[RENUM-FAIL] RCV={rcv_chk} abnormal (perm_pool address expected after lazy init)", "RENUM")
|
||||
|
||||
# Check whether already registered (avoid duplicate registration)
|
||||
for i in range(self.RenumClassCount):
|
||||
for i in range(int(self.RenumClassCount)):
|
||||
existing: str = str(self.RenumClassNames[i])
|
||||
if existing is not None and string.strcmp(existing, class_name) == 0: return
|
||||
|
||||
@@ -930,7 +929,7 @@ novirtual class ClassGenEnum(ClassGenBase.ClassGenBase):
|
||||
cn: sizet = len(children)
|
||||
# First pass: find the upper bound of Assign variants' explicit integer values (stays -1 if none)
|
||||
max_explicit: int = -1
|
||||
for ci in range(cn):
|
||||
for ci in range(int(cn)):
|
||||
stmt_p: ast.AST* = children.get(ci)
|
||||
if stmt_p is None or stmt_p.kind() != ast.ASTKind.Assign: continue
|
||||
ag_p: ast.Assign* = ast.Assign*(stmt_p)
|
||||
@@ -948,7 +947,7 @@ novirtual class ClassGenEnum(ClassGenBase.ClassGenBase):
|
||||
slot: int = 0
|
||||
next_class_tag: int = max_explicit + 1
|
||||
variant_index: int = 0
|
||||
for ci in range(cn):
|
||||
for ci in range(int(cn)):
|
||||
if slot >= variant_count: break
|
||||
if slot >= RENUM_VARIANT_PER_CLASS:
|
||||
# Fixed 32 slots per class: exceeding it would overflow into the next class's slots; report fail-fast
|
||||
@@ -1006,9 +1005,9 @@ novirtual class ClassGenEnum(ClassGenBase.ClassGenBase):
|
||||
"""Find the REnum class name owning a variant name; None if absent"""
|
||||
if variant_name is None or self.RenumClassNames is None: return None
|
||||
|
||||
for i in range(self.RenumClassCount):
|
||||
for i in range(int(self.RenumClassCount)):
|
||||
vcount: int = int(self.RenumClassVariantCounts[i])
|
||||
for j in range(vcount):
|
||||
for j in range(int(vcount)):
|
||||
name_offset: sizet = i * RENUM_VARIANT_PER_CLASS + j
|
||||
vname: str = str(self.RenumVariantNames[name_offset])
|
||||
if vname is not None and string.strcmp(vname, variant_name) == 0:
|
||||
@@ -1042,21 +1041,21 @@ novirtual class ClassGenEnum(ClassGenBase.ClassGenBase):
|
||||
def _is_renum_class_name(self, class_name: str) -> int:
|
||||
"""Check whether a name is a registered REnum class, 1=yes / 0=no"""
|
||||
if class_name is None or self.RenumClassNames is None: return 0
|
||||
for i in range(self.RenumClassCount):
|
||||
for i in range(int(self.RenumClassCount)):
|
||||
existing: str = str(self.RenumClassNames[i])
|
||||
if existing is not None and string.strcmp(existing, class_name) == 0: return 1
|
||||
return 0
|
||||
def _lookup_renum_variant_in_class(self, class_name: str, variant_name: str) -> int:
|
||||
"""Find a variant in a given REnum class, return its tag value or -1"""
|
||||
if class_name is None or variant_name is None or self.RenumClassNames is None: return -1
|
||||
for i in range(self.RenumClassCount):
|
||||
for i in range(int(self.RenumClassCount)):
|
||||
existing: str = str(self.RenumClassNames[i])
|
||||
if existing is None or string.strcmp(existing, class_name) != 0: continue
|
||||
vcount: int = int(self.RenumClassVariantCounts[i])
|
||||
for j in range(vcount):
|
||||
for j in range(int(vcount)):
|
||||
name_offset: sizet = i * RENUM_VARIANT_PER_CLASS + j
|
||||
vname: str = str(self.RenumVariantNames[name_offset])
|
||||
if vname is not None and string.strcmp(vname, variant_name) == 0: return self.RenumVariantTags[name_offset]
|
||||
if vname is not None and string.strcmp(vname, variant_name) == 0: return int(self.RenumVariantTags[name_offset])
|
||||
return -1
|
||||
return -1
|
||||
def _register_renum_field_alias(self, pool: memhub.MemBuddy*, class_name: str, field_name: str, payload_idx: int, field_ty: mlirlite.LLVMType* = None, variant_name: str = None):
|
||||
@@ -1100,11 +1099,24 @@ novirtual class ClassGenEnum(ClassGenBase.ClassGenBase):
|
||||
|
||||
# Dedupe check (pointer-free): the class slot holds a perm_pool string copy compared with strcmp,
|
||||
# so it no longer relies on pointer equality (registration uses AST pointers, lookup uses pool copies).
|
||||
for i in range(self.RenumAliasCount):
|
||||
for i in range(int(self.RenumAliasCount)):
|
||||
cls_existing_ptr: str = rawptr(self.RenumAliasClass[i])
|
||||
if cls_existing_ptr is not None and string.strcmp(cls_existing_ptr, class_name) == 0:
|
||||
fld_existing_ptr: str = rawptr(self.RenumAliasField[i])
|
||||
if fld_existing_ptr is not None and string.strcmp(fld_existing_ptr, field_name) == 0:
|
||||
# **变体名也必须一致**:同一 REnum 的不同变体可有**同名字段**
|
||||
# (LLVMType.Int.Bits 与 LLVMType.Float.Bits)。旧判据只比 (class, field),
|
||||
# 第二个同名字段被当成重复记录直接丢弃 ⇒ _lookup_renum_member_ty(class,
|
||||
# variant, idx) 查不到该记录 ⇒ match 绑定退化为按**物理槽类型**读取
|
||||
# (max-payload 槽是 i8*/i64)⇒ 赋给 i32 时误报"隐式窄化禁止"(假阳性)。
|
||||
_vn_existing: str = rawptr(self.RenumAliasVariant[i])
|
||||
_vn_same: int = 0
|
||||
if variant_name is None:
|
||||
_vn_same = 1 if _vn_existing is None else 0
|
||||
elif _vn_existing is not None:
|
||||
_vn_same = 1 if string.strcmp(_vn_existing, variant_name) == 0 else 0
|
||||
if _vn_same == 0:
|
||||
continue
|
||||
# Existing record: if the first pass left the type slot 0 (self-referencing field, see
|
||||
# _refill_renum_field_types), backfill the real type now; a non-zero slot is left as is.
|
||||
if self.RenumAliasTy[i] == 0 and field_ty is not None:
|
||||
@@ -1148,17 +1160,17 @@ novirtual class ClassGenEnum(ClassGenBase.ClassGenBase):
|
||||
def _lookup_renum_field_alias(self, class_name: str, field_name: str) -> int:
|
||||
"""Look up an REnum variant field name's payload index, return it or -1 (class/field compared by content)."""
|
||||
if class_name is None or field_name is None or self.RenumAliasClass is None: return -1
|
||||
for i in range(self.RenumAliasCount):
|
||||
for i in range(int(self.RenumAliasCount)):
|
||||
cls_existing_ptr: str = rawptr(self.RenumAliasClass[i])
|
||||
if cls_existing_ptr is not None and string.strcmp(cls_existing_ptr, class_name) == 0:
|
||||
# field_name is compared by strcmp content (pointers differ, content is the same)
|
||||
fld_existing_ptr: str = rawptr(self.RenumAliasField[i])
|
||||
if fld_existing_ptr is not None and string.strcmp(fld_existing_ptr, field_name) == 0: return self.RenumAliasIndex[i]
|
||||
if fld_existing_ptr is not None and string.strcmp(fld_existing_ptr, field_name) == 0: return int(self.RenumAliasIndex[i])
|
||||
return -1
|
||||
def _lookup_renum_field_alias_ty(self, class_name: str, field_name: str) -> mlirlite.LLVMType*:
|
||||
"""Look up an REnum variant member's actual LLVM type, or None when the type slot is 0 (binding falls back to the slot type)."""
|
||||
if class_name is None or field_name is None or self.RenumAliasTy is None: return None
|
||||
for i in range(self.RenumAliasCount):
|
||||
for i in range(int(self.RenumAliasCount)):
|
||||
cls_existing_ptr: str = rawptr(self.RenumAliasClass[i])
|
||||
if cls_existing_ptr is not None and string.strcmp(cls_existing_ptr, class_name) == 0:
|
||||
fld_existing_ptr: str = rawptr(self.RenumAliasField[i])
|
||||
@@ -1170,7 +1182,7 @@ novirtual class ClassGenEnum(ClassGenBase.ClassGenBase):
|
||||
"""Look up a variant member's actual LLVM type by (class, variant name, payload index) for match binding; None if unresolved."""
|
||||
if class_name is None or variant_name is None or self.RenumAliasVariant is None: return None
|
||||
if payload_idx < 1: return None
|
||||
for i in range(self.RenumAliasCount):
|
||||
for i in range(int(self.RenumAliasCount)):
|
||||
cls_existing_ptr: str = rawptr(self.RenumAliasClass[i])
|
||||
if cls_existing_ptr is None or string.strcmp(cls_existing_ptr, class_name) != 0: continue
|
||||
if self.RenumAliasVariant[i] == 0: continue
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import lib.core.Gen.GenTypes as GenTypes
|
||||
import t, c
|
||||
from stdint import *
|
||||
import ast
|
||||
import mlirlite
|
||||
@@ -65,7 +64,7 @@ novirtual class ClassGenGeneric(ClassGenBase.ClassGenBase):
|
||||
# Assign the list to a local to trigger type inference (avoids field __len__ failure)
|
||||
names_local: list[str]* = self.GenericClassNames
|
||||
cn: sizet = len(names_local)
|
||||
for i in range(cn):
|
||||
for i in range(int(cn)):
|
||||
nm: str = names_local.get(i)
|
||||
if nm is not None and string.strcmp(nm, cd.name) == 0:
|
||||
# A duplicate name must not simply return: the first-registered node may lack
|
||||
@@ -92,13 +91,13 @@ novirtual class ClassGenGeneric(ClassGenBase.ClassGenBase):
|
||||
# Compute total length: each name length + (n-1) spaces + NUL
|
||||
total: sizet = 1
|
||||
ti: sizet
|
||||
for ti in range(tp_n):
|
||||
for ti in range(int(tp_n)):
|
||||
tp_name: str = tp.get(ti)
|
||||
if tp_name is not None: total += string.strlen(tp_name) + 1
|
||||
buf: str = CompilerFactory.factory.PermPool.alloc(total)
|
||||
if buf is None: return None
|
||||
buf[0] = '\0'
|
||||
for ti in range(tp_n):
|
||||
for ti in range(int(tp_n)):
|
||||
tp_name = tp.get(ti)
|
||||
if tp_name is None: continue
|
||||
if ti > 0 and buf[0] != '\0': string.strcat(buf, " ")
|
||||
@@ -136,7 +135,7 @@ novirtual class ClassGenGeneric(ClassGenBase.ClassGenBase):
|
||||
keys_local: list[str]* = self.GenericFuncKeys
|
||||
kn: sizet = len(keys_local)
|
||||
ki: sizet
|
||||
for ki in range(kn):
|
||||
for ki in range(int(kn)):
|
||||
knm: str = keys_local.get(ki)
|
||||
if knm is not None and string.strcmp(knm, key_buf) == 0: return
|
||||
tp_names_buf: str = self._copy_type_params_stdlib(fd.type_params)
|
||||
@@ -153,7 +152,7 @@ novirtual class ClassGenGeneric(ClassGenBase.ClassGenBase):
|
||||
nodes_local: list[ast.FunctionDef*]* = self.GenericFuncNodes
|
||||
kn: sizet = len(keys_local)
|
||||
i: sizet
|
||||
for i in range(kn):
|
||||
for i in range(int(kn)):
|
||||
knm: str = keys_local.get(i)
|
||||
if knm is not None and string.strcmp(knm, key) == 0: return nodes_local.get(i)
|
||||
return None
|
||||
@@ -165,7 +164,7 @@ novirtual class ClassGenGeneric(ClassGenBase.ClassGenBase):
|
||||
sha1s_local: list[str]* = self.GenericFuncSha1s
|
||||
kn: sizet = len(keys_local)
|
||||
i: sizet
|
||||
for i in range(kn):
|
||||
for i in range(int(kn)):
|
||||
knm: str = keys_local.get(i)
|
||||
if knm is not None and string.strcmp(knm, key) == 0: return sha1s_local.get(i)
|
||||
return None
|
||||
@@ -177,7 +176,7 @@ novirtual class ClassGenGeneric(ClassGenBase.ClassGenBase):
|
||||
files_local: list[str]* = self.GenericFuncFiles
|
||||
kn: sizet = len(keys_local)
|
||||
i: sizet
|
||||
for i in range(kn):
|
||||
for i in range(int(kn)):
|
||||
knm: str = keys_local.get(i)
|
||||
if knm is not None and string.strcmp(knm, key) == 0: return files_local.get(i)
|
||||
return None
|
||||
@@ -188,7 +187,7 @@ novirtual class ClassGenGeneric(ClassGenBase.ClassGenBase):
|
||||
keys_local: list[str]* = self.GenericFuncKeys
|
||||
kn: sizet = len(keys_local)
|
||||
i: sizet
|
||||
for i in range(kn):
|
||||
for i in range(int(kn)):
|
||||
knm: str = keys_local.get(i)
|
||||
if knm is not None and string.strcmp(knm, key) == 0:
|
||||
if i >= GENERIC_TEMPLATE_MAX: return None
|
||||
@@ -199,7 +198,7 @@ novirtual class ClassGenGeneric(ClassGenBase.ClassGenBase):
|
||||
if tp_list is None: return None
|
||||
seg_start: sizet = 0
|
||||
blen: sizet = string.strlen(buf_tp)
|
||||
for pos in range(blen + 1):
|
||||
for pos in range(int(blen + 1)):
|
||||
if buf_tp[pos] == ' ' or buf_tp[pos] == '\0':
|
||||
seg_len: sizet = pos - seg_start
|
||||
if seg_len > 0:
|
||||
@@ -221,7 +220,7 @@ novirtual class ClassGenGeneric(ClassGenBase.ClassGenBase):
|
||||
alist: list[ast.AST*]* = ags.args
|
||||
an: sizet = len(alist)
|
||||
ai: sizet
|
||||
for ai in range(an):
|
||||
for ai in range(int(an)):
|
||||
pa: ast.AST* = alist.get(ai)
|
||||
if pa is None or pa.kind() != ast.ASTKind.Arg: continue
|
||||
ag: ast.Arg* = ast.Arg*(pa)
|
||||
@@ -342,7 +341,7 @@ novirtual class ClassGenGeneric(ClassGenBase.ClassGenBase):
|
||||
body: list[ast.AST*]* = fd.children
|
||||
bn: sizet = len(body)
|
||||
bi: sizet
|
||||
for bi in range(bn):
|
||||
for bi in range(int(bn)):
|
||||
stmt: ast.AST* = body.get(bi)
|
||||
if stmt is not None: new_fd.append(stmt)
|
||||
return new_fd
|
||||
@@ -388,7 +387,7 @@ novirtual class ClassGenGeneric(ClassGenBase.ClassGenBase):
|
||||
alist: list[ast.AST*]* = ags.args
|
||||
an: sizet = len(alist)
|
||||
ai: sizet
|
||||
for ai in range(an):
|
||||
for ai in range(int(an)):
|
||||
pa: ast.AST* = alist.get(ai)
|
||||
if pa is None or pa.kind() != ast.ASTKind.Arg: continue
|
||||
arg: ast.Arg* = ast.Arg*(pa)
|
||||
@@ -412,7 +411,7 @@ novirtual class ClassGenGeneric(ClassGenBase.ClassGenBase):
|
||||
if mode_buf_g is not None:
|
||||
mode_cnt_g: int = 0
|
||||
has_ref_g: int = 0
|
||||
for ai_g in range(an):
|
||||
for ai_g in range(int(an)):
|
||||
pa_g: ast.AST* = alist.get(ai_g)
|
||||
if pa_g is None or pa_g.kind() != ast.ASTKind.Arg: continue
|
||||
arg_g: ast.Arg* = ast.Arg*(pa_g)
|
||||
@@ -452,7 +451,7 @@ novirtual class ClassGenGeneric(ClassGenBase.ClassGenBase):
|
||||
# is replaced with the concrete type name (nested specialization: generic fn calling generic fn).
|
||||
ta_cnt_g: sizet = len(type_args)
|
||||
ta_i_g: sizet
|
||||
for ta_i_g in range(ta_cnt_g):
|
||||
for ta_i_g in range(int(ta_cnt_g)):
|
||||
ta_raw_g: str = type_args.get(ta_i_g)
|
||||
if ta_raw_g is not None:
|
||||
ta_rsv_g: str = self.Gen.TypeGenBase.resolve_generic_type_name(ta_raw_g)
|
||||
@@ -549,7 +548,7 @@ novirtual class ClassGenGeneric(ClassGenBase.ClassGenBase):
|
||||
names_local: list[str]* = self.GenericClassNames
|
||||
cn: sizet = len(names_local)
|
||||
i: sizet
|
||||
for i in range(cn):
|
||||
for i in range(int(cn)):
|
||||
nm: str = names_local.get(i)
|
||||
if nm is not None and string.strcmp(nm, class_name) == 0:
|
||||
if i >= GENERIC_TEMPLATE_MAX: return None
|
||||
@@ -562,7 +561,7 @@ novirtual class ClassGenGeneric(ClassGenBase.ClassGenBase):
|
||||
if tp_list is None: return None
|
||||
seg_start: sizet = 0
|
||||
blen: sizet = string.strlen(buf_tp)
|
||||
for pos in range(blen + 1):
|
||||
for pos in range(int(blen + 1)):
|
||||
if buf_tp[pos] == ' ' or buf_tp[pos] == '\0':
|
||||
seg_len: sizet = pos - seg_start
|
||||
if seg_len > 0:
|
||||
@@ -579,10 +578,10 @@ 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(cn):
|
||||
for i in range(int(cn)):
|
||||
nm: str = names_local.get(i)
|
||||
if nm is not None and string.strcmp(nm, class_name) == 0:
|
||||
node: ast.ClassDef* = nodes_local.get(i)
|
||||
@@ -596,7 +595,7 @@ novirtual class ClassGenGeneric(ClassGenBase.ClassGenBase):
|
||||
sha1s_local: list[str]* = self.GenericClassSha1s
|
||||
cn: sizet = len(names_local)
|
||||
i: sizet
|
||||
for i in range(cn):
|
||||
for i in range(int(cn)):
|
||||
nm: str = names_local.get(i)
|
||||
if nm is not None and string.strcmp(nm, class_name) == 0:
|
||||
sha1_val: str = sha1s_local.get(i)
|
||||
@@ -609,7 +608,7 @@ novirtual class ClassGenGeneric(ClassGenBase.ClassGenBase):
|
||||
files_local: list[str]* = self.GenericClassFiles
|
||||
cn: sizet = len(names_local)
|
||||
i: sizet
|
||||
for i in range(cn):
|
||||
for i in range(int(cn)):
|
||||
nm: str = names_local.get(i)
|
||||
if nm is not None and string.strcmp(nm, class_name) == 0:
|
||||
f_val: str = files_local.get(i)
|
||||
@@ -624,7 +623,7 @@ novirtual class ClassGenGeneric(ClassGenBase.ClassGenBase):
|
||||
norm_args: list[str]* = list[str](pool, 8)
|
||||
if norm_args is None: return class_name
|
||||
i: sizet
|
||||
for i in range(n):
|
||||
for i in range(int(n)):
|
||||
ta_raw: str = type_args.get(i)
|
||||
if ta_raw is None: continue
|
||||
ta_norm: str = self.Gen.TypeGenResolve._norm_type_arg_name(pool, ta_raw, from_imports)
|
||||
@@ -634,7 +633,7 @@ novirtual class ClassGenGeneric(ClassGenBase.ClassGenBase):
|
||||
if n == 0: return class_name
|
||||
# Compute total length: class_name + "[" + arg0 + "," + arg1 + ... + "]" + NUL
|
||||
total_len: sizet = string.strlen(class_name) + 2 # "[" and "]"
|
||||
for i in range(n):
|
||||
for i in range(int(n)):
|
||||
if i > 0:
|
||||
total_len += 1 # ","
|
||||
ta: str = norm_args.get(i)
|
||||
@@ -646,7 +645,7 @@ novirtual class ClassGenGeneric(ClassGenBase.ClassGenBase):
|
||||
mangled[0] = '\0'
|
||||
string.strcat(mangled, class_name)
|
||||
string.strcat(mangled, "[")
|
||||
for i in range(n):
|
||||
for i in range(int(n)):
|
||||
if i > 0: string.strcat(mangled, ",")
|
||||
ta: str = norm_args.get(i)
|
||||
if ta is not None: string.strcat(mangled, ta)
|
||||
@@ -663,7 +662,7 @@ novirtual class ClassGenGeneric(ClassGenBase.ClassGenBase):
|
||||
if nm.id is not None:
|
||||
n: sizet = len(tp_names) if tp_names is not None else 0
|
||||
i: sizet
|
||||
for i in range(n):
|
||||
for i in range(int(n)):
|
||||
tpn: str = tp_names.get(i)
|
||||
if tpn is not None and string.strcmp(nm.id, tpn) == 0:
|
||||
ta: str = type_args.get(i)
|
||||
@@ -701,7 +700,7 @@ novirtual class ClassGenGeneric(ClassGenBase.ClassGenBase):
|
||||
tup_elts: list[ast.AST*]* = tup.elts
|
||||
en: sizet = len(tup_elts)
|
||||
ei: sizet
|
||||
for ei in range(en):
|
||||
for ei in range(int(en)):
|
||||
el: ast.AST* = tup_elts.get(ei)
|
||||
new_el: ast.AST* = self._clone_annotation(pool, el, tp_names, type_args)
|
||||
if new_el is not None: new_elts.append(new_el)
|
||||
@@ -725,7 +724,7 @@ novirtual class ClassGenGeneric(ClassGenBase.ClassGenBase):
|
||||
old_arg_list: list[ast.AST*]* = old_args.args
|
||||
an: sizet = len(old_arg_list)
|
||||
ai: sizet
|
||||
for ai in range(an):
|
||||
for ai in range(int(an)):
|
||||
arg_node: ast.AST* = old_arg_list.get(ai)
|
||||
if arg_node is None: continue
|
||||
if arg_node.kind() == ast.ASTKind.Arg:
|
||||
@@ -744,7 +743,7 @@ novirtual class ClassGenGeneric(ClassGenBase.ClassGenBase):
|
||||
cd_children: list[ast.AST*]* = cd.children
|
||||
cn: sizet = len(cd_children)
|
||||
ci: sizet
|
||||
for ci in range(cn):
|
||||
for ci in range(int(cn)):
|
||||
child: ast.AST* = cd_children.get(ci)
|
||||
if child is None: continue
|
||||
ck: int = child.kind()
|
||||
@@ -767,7 +766,7 @@ novirtual class ClassGenGeneric(ClassGenBase.ClassGenBase):
|
||||
fd_children: list[ast.AST*]* = fd.children
|
||||
bcn: sizet = len(fd_children)
|
||||
bci: sizet
|
||||
for bci in range(bcn):
|
||||
for bci in range(int(bcn)):
|
||||
body_stmt: ast.AST* = fd_children.get(bci)
|
||||
if body_stmt is not None: new_fd.append(body_stmt)
|
||||
new_cd.append(new_fd)
|
||||
@@ -779,7 +778,7 @@ novirtual class ClassGenGeneric(ClassGenBase.ClassGenBase):
|
||||
"""Find a cached specialization name; None when absent."""
|
||||
if self.SpecKeys is None or spec_key is None: return None
|
||||
i: int
|
||||
for i in range(self.SpecCount):
|
||||
for i in range(int(self.SpecCount)):
|
||||
key_addr: sizet = self.SpecKeys[i]
|
||||
if key_addr != 0:
|
||||
k: str = rawptr(key_addr)
|
||||
@@ -865,7 +864,7 @@ novirtual class ClassGenGeneric(ClassGenBase.ClassGenBase):
|
||||
# concrete type name to avoid ndarray[T] double-recursion crashes; in-place edits are side-effect free.
|
||||
ta_cnt_g: sizet = len(type_args)
|
||||
ta_i_g: sizet
|
||||
for ta_i_g in range(ta_cnt_g):
|
||||
for ta_i_g in range(int(ta_cnt_g)):
|
||||
ta_raw_g: str = type_args.get(ta_i_g)
|
||||
if ta_raw_g is not None:
|
||||
ta_rsv_g: str = self.Gen.TypeGenBase.resolve_generic_type_name(ta_raw_g)
|
||||
@@ -929,7 +928,7 @@ novirtual class ClassGenGeneric(ClassGenBase.ClassGenBase):
|
||||
sk_n: sizet = len(type_args)
|
||||
sk_total: sizet = string.strlen(class_name) + 2 # "<" and ">"
|
||||
sk_i: sizet
|
||||
for sk_i in range(sk_n):
|
||||
for sk_i in range(int(sk_n)):
|
||||
if sk_i > 0:
|
||||
sk_total += 1 # ","
|
||||
sk_ta: str = type_args.get(sk_i)
|
||||
@@ -942,7 +941,7 @@ novirtual class ClassGenGeneric(ClassGenBase.ClassGenBase):
|
||||
spec_key[0] = '\0'
|
||||
string.strcat(spec_key, class_name)
|
||||
string.strcat(spec_key, "<")
|
||||
for sk_i in range(sk_n):
|
||||
for sk_i in range(int(sk_n)):
|
||||
if sk_i > 0: string.strcat(spec_key, ",")
|
||||
sk_ta: str = type_args.get(sk_i)
|
||||
if sk_ta is not None: string.strcat(spec_key, sk_ta)
|
||||
@@ -1026,7 +1025,7 @@ novirtual class ClassGenGeneric(ClassGenBase.ClassGenBase):
|
||||
if string.strcmp(class_name, "GSListNode") == 0:
|
||||
result_entry: GenTypes.StructEntry* = self.Gen.StructGen.find_struct(spec_name)
|
||||
if result_entry is not None:
|
||||
for ri in range(result_entry.FieldCount):
|
||||
for ri in range(int(result_entry.FieldCount)):
|
||||
rfe: GenTypes.FieldEntry* = self.Gen.StructGen._get_field_entry(result_entry, ri)
|
||||
if rfe is not None:
|
||||
rfn: str = self.Gen.StructGen.get_field_name_ptr(rfe)
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import t, c
|
||||
from stdint import *
|
||||
from . import ClassGenBase
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import lib.core.Gen.GenTypes as GenTypes
|
||||
import lib.core.Gen.Type.TypeSpec as TSpec
|
||||
import t, c
|
||||
from stdint import *
|
||||
import ast
|
||||
import mlirlite
|
||||
@@ -19,8 +18,8 @@ import lib.core.Gen.DebugInfoGen as DebugInfoGen
|
||||
|
||||
# Method metadata flag: TLS (@t.TLS decorator; local copy avoids a cross-module CDefine lookup).
|
||||
define META_TLS = 32
|
||||
# Max number of vtable methods.
|
||||
define VTBL_METHOD_MAX = 32
|
||||
# Vtable method tables are sized per class at collection time (parents' count + own methods);
|
||||
# there is deliberately no fixed VTBL_METHOD_MAX.
|
||||
define SCOPE_FUNCTION = 1
|
||||
|
||||
# ============================================================
|
||||
@@ -28,16 +27,21 @@ define SCOPE_FUNCTION = 1
|
||||
novirtual class ClassGenVTable(ClassGenBase.ClassGenBase):
|
||||
"""vtable collection, generation, and method translation."""
|
||||
|
||||
# _vtable_strcat - append src to the end of dst.
|
||||
# _vtable_strcat - append src to the end of dst; fail-fast when it does not fit.
|
||||
def _vtable_strcat(self, dst: str, dst_size: sizet, src: str):
|
||||
"""Append src to the end of dst."""
|
||||
"""Append src to the end of dst.
|
||||
|
||||
A truncated vtable initializer stays plausible LLVM syntax but is cut in the middle of a
|
||||
bitcast, which then surfaces as a verifier error pointing at an unrelated method.
|
||||
"""
|
||||
if dst is None or src is None: return
|
||||
dlen: sizet = string.strlen(dst)
|
||||
slen: sizet = string.strlen(src)
|
||||
remain: sizet = dst_size - dlen
|
||||
if remain <= 0: return
|
||||
if dlen + slen + 1 > dst_size:
|
||||
self.Gen.TypeGenErr.fatal_error(None, f"vtable initializer text exceeds its buffer: {dlen} byte(s) used + {slen} byte(s) to append + 1 NUL > capacity {dst_size} byte(s); the class has too many virtual methods for the fixed buffer", "OverflowError")
|
||||
return
|
||||
i: sizet = 0
|
||||
while i < slen and i + 1 < remain:
|
||||
while i < slen:
|
||||
dst[dlen + i] = src[i]
|
||||
i += 1
|
||||
dst[dlen + i] = '\0'
|
||||
@@ -62,15 +66,35 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase):
|
||||
if children is None: return 0
|
||||
cn: sizet = len(children)
|
||||
|
||||
# Capacity comes from the class itself: the parents' already-collected vtable methods
|
||||
# (an upper bound — dedupe/override only shrink it) plus the number of own method
|
||||
# definitions. A fixed 32 silently dropped every later method, after which a call missed
|
||||
# the slot and fell back to a static call of the base implementation.
|
||||
parent_count: int = self.Gen.StructGen.get_parent_count(class_name)
|
||||
vt_cap: int = 0
|
||||
if parent_count > 0:
|
||||
for pi0 in range(int(parent_count)):
|
||||
pname0: str = self.Gen.StructGen.get_parent_name_at(class_name, pi0)
|
||||
if pname0 is None: continue
|
||||
pe0: GenTypes.StructEntry* = self.Gen.StructGen.find_struct_by_module(pname0, trans.ModuleSha1)
|
||||
if pe0 is None: pe0 = self.Gen.StructGen.find_struct(pname0)
|
||||
if pe0 is not None and pe0.VTableMethodCount > 0: vt_cap = vt_cap + pe0.VTableMethodCount
|
||||
for ci0 in range(int(cn)):
|
||||
stmt0: ast.AST* = children.get(ci0)
|
||||
if stmt0 is not None and stmt0.kind() == ast.ASTKind.FunctionDef: vt_cap = vt_cap + 1
|
||||
vt_cap = vt_cap + 1 # one spare slot
|
||||
if vt_cap < 1: vt_cap = 1
|
||||
vt_bytes: sizet = sizet(8 * vt_cap)
|
||||
|
||||
# Allocate with PermPool: VTableMethods must survive per-file pool resets.
|
||||
full_names_buf: sizet* = CompilerFactory.factory.PermPool.alloc(8 * VTBL_METHOD_MAX)
|
||||
method_names_buf: sizet* = CompilerFactory.factory.PermPool.alloc(8 * VTBL_METHOD_MAX)
|
||||
full_names_buf: sizet* = CompilerFactory.factory.PermPool.alloc(vt_bytes)
|
||||
method_names_buf: sizet* = CompilerFactory.factory.PermPool.alloc(vt_bytes)
|
||||
# Record each method's parent SHA1 (non-zero = inherited, mangle with parent module SHA1).
|
||||
method_parent_sha1s: sizet* = CompilerFactory.factory.PermPool.alloc(8 * VTBL_METHOD_MAX)
|
||||
method_parent_sha1s: sizet* = CompilerFactory.factory.PermPool.alloc(vt_bytes)
|
||||
if full_names_buf is None or method_names_buf is None or method_parent_sha1s is None: return 0
|
||||
string.memset(full_names_buf, 0, 8 * VTBL_METHOD_MAX)
|
||||
string.memset(method_names_buf, 0, 8 * VTBL_METHOD_MAX)
|
||||
string.memset(method_parent_sha1s, 0, 8 * VTBL_METHOD_MAX)
|
||||
string.memset(full_names_buf, 0, vt_bytes)
|
||||
string.memset(method_names_buf, 0, vt_bytes)
|
||||
string.memset(method_parent_sha1s, 0, vt_bytes)
|
||||
|
||||
method_count: int = 0
|
||||
|
||||
@@ -78,7 +102,6 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase):
|
||||
# subclass, others the parent; SHA1-aware lookup avoids cross-module same-name mismatches.
|
||||
vt_self_entry: GenTypes.StructEntry* = self.Gen.StructGen.find_struct_by_module(class_name, trans.ModuleSha1)
|
||||
# Multiple inheritance: walk parents in declaration order, dedupe by short name (first wins).
|
||||
parent_count: int = self.Gen.StructGen.get_parent_count(class_name)
|
||||
if parent_count > 0:
|
||||
for pi in range(int(parent_count)):
|
||||
pname: str = self.Gen.StructGen.get_parent_name_at(class_name, pi)
|
||||
@@ -92,7 +115,10 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase):
|
||||
if vt_parent_entry is not None: parent_vt_count = vt_parent_entry.VTableMethodCount
|
||||
if parent_vt_count > 0:
|
||||
for pvi in range(int(parent_vt_count)):
|
||||
if method_count >= VTBL_METHOD_MAX: break
|
||||
if method_count >= vt_cap:
|
||||
# Unreachable with the upper-bound sizing above; stay loud rather than
|
||||
# writing past the PermPool array.
|
||||
self.Gen.TypeGenErr.fatal_error(cd, f"internal error: vtable of '{class_name}' exceeded its computed capacity {vt_cap}", "InternalCompilerError")
|
||||
pmethod_name: str = None
|
||||
if vt_parent_entry is not None: pmethod_name = self.Gen.StructGen.get_vtable_method_name(pname, pvi)
|
||||
if pmethod_name is None: continue
|
||||
@@ -160,7 +186,10 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase):
|
||||
if stmt.kind() != ast.ASTKind.FunctionDef: continue
|
||||
fd: ast.FunctionDef* = ast.FunctionDef*(stmt)
|
||||
if fd is None or fd.name is None: continue
|
||||
if method_count >= VTBL_METHOD_MAX: break
|
||||
if method_count >= vt_cap:
|
||||
# Unreachable with the upper-bound sizing above (own methods were counted);
|
||||
# stay loud rather than writing past the PermPool array.
|
||||
self.Gen.TypeGenErr.fatal_error(cd, f"internal error: vtable of '{class_name}' exceeded its computed capacity {vt_cap}", "InternalCompilerError")
|
||||
|
||||
mname: str = fd.name
|
||||
# Skip constructors and special methods (they must not enter the vtable).
|
||||
@@ -219,7 +248,12 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase):
|
||||
mod: mlirlite.LLVMModule* = trans.Module
|
||||
|
||||
# Collect virtual methods (inherited + own) into Vt* buffers and set entry.VTableMethodCount/VTableMethods.
|
||||
if self._collect_vtable_methods(trans, cd, class_name) == 0: return 0
|
||||
# 带 virtual 标记的类(调用方已保证 HasVTable==1)必须**始终**发射自己的 @"<sha1>.类名.vtable"
|
||||
# 全局量,即便 VTableMethodCount==0:isinstance() 在运行期要引用该符号(CallGenLeafKit._make_vtable_ref_i8),
|
||||
# 缺定义会在链接期 undefined symbol。故这里不因「收集到 0 个虚方法」而提前返回,而是继续构造一个
|
||||
# 零字段的 vtable(!llvm.struct<()> 合法),其形状与 VTableMethodCount==0 / 对象布局保持一致
|
||||
# (对象仅在槽位处保存 vtable 指针,vtable 自身元素个数不影响对象布局)。
|
||||
self._collect_vtable_methods(trans, cd, class_name)
|
||||
full_names_buf: sizet* = self.VtFullNames
|
||||
method_names_buf: sizet* = self.VtMethodNames
|
||||
method_parent_sha1s: sizet* = self.VtParentSha1s
|
||||
@@ -242,18 +276,28 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase):
|
||||
vtable_ty: mlirlite.LLVMType* = mlirlite.Struct(pool, first_node, method_count, None)
|
||||
if vtable_ty is None: return 0
|
||||
|
||||
# Build the initializer string; 16384 bytes so large vtables (MemBuddy 28+) are not truncated.
|
||||
init_buf: str = pool.alloc(16384)
|
||||
# Size the initializer from the real method list: each entry is
|
||||
# "i8* bitcast (<func ty>* @<mangled> to i8*)", where the function type text alone can
|
||||
# reach 1024 bytes and the mangled name carries a SHA1 prefix. The old fixed 16384 bytes
|
||||
# silently cut long vtables in the middle of a bitcast.
|
||||
init_cap: sizet = 64
|
||||
for si in range(int(method_count)):
|
||||
name_addr0: sizet = full_names_buf[si]
|
||||
if name_addr0 == 0:
|
||||
init_cap = init_cap + 12
|
||||
else:
|
||||
init_cap = init_cap + 1024 + 96 + string.strlen(rawptr(name_addr0)) + 128
|
||||
init_buf: str = pool.alloc(init_cap)
|
||||
if init_buf is None: return 0
|
||||
init_buf[0] = '\0'
|
||||
self._vtable_strcat(init_buf, 16384, "{ ")
|
||||
self._vtable_strcat(init_buf, init_cap, "{ ")
|
||||
|
||||
for i in range(int(method_count)):
|
||||
if i > 0: self._vtable_strcat(init_buf, 16384, ", ")
|
||||
if i > 0: self._vtable_strcat(init_buf, init_cap, ", ")
|
||||
|
||||
full_name_addr: sizet = full_names_buf[i]
|
||||
if full_name_addr == 0:
|
||||
self._vtable_strcat(init_buf, 16384, "i8* null")
|
||||
self._vtable_strcat(init_buf, init_cap, "i8* null")
|
||||
continue
|
||||
|
||||
full_name: str = rawptr(full_name_addr)
|
||||
@@ -273,7 +317,7 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase):
|
||||
ge: GenTypes.FuncEntry* = self.Gen.CallGenLeafKit.find_func_entry_global(full_name)
|
||||
func_ty_buf: str = pool.alloc(1024)
|
||||
if func_ty_buf is None:
|
||||
self._vtable_strcat(init_buf, 16384, "i8* null")
|
||||
self._vtable_strcat(init_buf, init_cap, "i8* null")
|
||||
continue
|
||||
func_ty_buf[0] = '\0'
|
||||
if ge is None or ge.Spec is None or TSpec.SpecToLLVMText(ge.Spec, func_ty_buf, 1024) != 0:
|
||||
@@ -281,18 +325,18 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase):
|
||||
continue
|
||||
|
||||
# Build the bitcast string: i8* bitcast (func_ty* @mangled to i8*).
|
||||
self._vtable_strcat(init_buf, 16384, "i8* bitcast (")
|
||||
self._vtable_strcat(init_buf, 16384, func_ty_buf)
|
||||
self._vtable_strcat(init_buf, 16384, "* @")
|
||||
self._vtable_strcat(init_buf, init_cap, "i8* bitcast (")
|
||||
self._vtable_strcat(init_buf, init_cap, func_ty_buf)
|
||||
self._vtable_strcat(init_buf, init_cap, "* @")
|
||||
|
||||
if self._vtable_name_needs_quote(mangled_name) != 0:
|
||||
self._vtable_strcat(init_buf, 16384, "\"")
|
||||
self._vtable_strcat(init_buf, 16384, mangled_name)
|
||||
self._vtable_strcat(init_buf, 16384, "\"")
|
||||
else: self._vtable_strcat(init_buf, 16384, mangled_name)
|
||||
self._vtable_strcat(init_buf, 16384, " to i8*)")
|
||||
self._vtable_strcat(init_buf, init_cap, "\"")
|
||||
self._vtable_strcat(init_buf, init_cap, mangled_name)
|
||||
self._vtable_strcat(init_buf, init_cap, "\"")
|
||||
else: self._vtable_strcat(init_buf, init_cap, mangled_name)
|
||||
self._vtable_strcat(init_buf, init_cap, " to i8*)")
|
||||
|
||||
self._vtable_strcat(init_buf, 16384, " }")
|
||||
self._vtable_strcat(init_buf, init_cap, " }")
|
||||
|
||||
# 4. Build the vtable global variable name.
|
||||
vtable_name_buf: str = pool.alloc(128)
|
||||
@@ -350,14 +394,30 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase):
|
||||
if fd_meta != 0: self.Gen.ClassGenBase.register_method_meta(class_name, fd_check.name, fd_meta)
|
||||
|
||||
if has_method == 0:
|
||||
# Subclass has no own methods but may inherit parent virtual methods; use oop_entry.ParentName directly.
|
||||
parent_name: str = None
|
||||
if oop_entry is not None: parent_name = oop_entry.ParentName
|
||||
if parent_name is not None:
|
||||
parent_vt_count: int = self.Gen.StructGen.get_vtable_method_count(parent_name)
|
||||
if parent_vt_count > 0:
|
||||
has_method = 1 # Mark as having methods (inherited virtual methods).
|
||||
if has_method == 0: return 0
|
||||
# 语义规则(OOP):类体内没有任何函数定义(无 def)且**不带 virtual 标记**的 class 一律视为
|
||||
# struct,不做 OOP 合成:
|
||||
# - 不标记 IsOOP(构造点据此走「分配 + 零/默认值」路径,不再引用 __init__/__before_init__);
|
||||
# - 不合成 __before_init__;不合成默认 __init__。
|
||||
# has_method==0 恰好等价于「类体里没有 FunctionDef」(Pass 1 见到任一个 def 就置 1)。
|
||||
#
|
||||
# 「带 virtual 标记」在本工程里没有独立的「虚类」位,统一走 entry.HasVTable==1:
|
||||
# - 该标记由 ClassGenDef 依据 ClassGenBase._detect_vtable_status 置位(ClassGenDef.vp:487);
|
||||
# - _detect_vtable_status 覆盖:类级 @t.CVTable(`virtual class` 的语法糖,parser.vp:556-621 合成)、
|
||||
# 方法级 @t.CVTable(`virtual def` 同款合成)、以及继承链上父类 HasVTable==1 的推导
|
||||
# (ClassGenBase.vp:883-916)。
|
||||
# 即「显式 virtual 标记 或 由虚父类推导出的虚表」这一口径,且不含纯数据类。
|
||||
#
|
||||
# 带 virtual 标记的类不计入上面的「无 def 按 struct」规则:无论类体有无 def,都必须照常构造
|
||||
# __before_init__ 与 __init__,并仍标为 OOP。理由(为什么 virtual 类必须保留默认构造):
|
||||
# 1) 语义上它就是多态类,构造时必须初始化 vptr(否则虚调用 / isinstance 会在运行期错判或解引用空指针);
|
||||
# 2) 该类的 __before_init__ 承载「写 vtable 指针 + 字段零填 + 默认值」,与是否有自有方法无关。
|
||||
# 故这里只对「既无 def 又无 virtual 标记」的纯数据类提前 return;带标记者继续走下方常规
|
||||
# OOP 合成(标 IsOOP、发 vtable、发 __before_init__、发默认 __init__)。vtable 全局量的发射
|
||||
# 由 _generate_vtable 负责(它即便方法数为 0 也需发射,见该函数注释)。
|
||||
pd_has_vt: int = 0
|
||||
if oop_entry is not None: pd_has_vt = oop_entry.HasVTable
|
||||
if pd_has_vt != 1:
|
||||
return 0
|
||||
|
||||
# Mark the struct as OOP.
|
||||
if oop_entry is not None:
|
||||
@@ -422,6 +482,33 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase):
|
||||
# Emit __before_init__ (zero-fill + defaults + vtable pointer).
|
||||
self._generate_before_init(trans, struct_ty, class_name)
|
||||
|
||||
# Emit a default (empty) __init__ when neither this class nor any MRO ancestor defines one.
|
||||
# Why: an explicit `obj.__init__()` in source lowers to a direct call @SHA1.Class.__init__
|
||||
# (the call site is never narrowed to a no-op), so the symbol must exist or ld.lld fails
|
||||
# with "undefined symbol: ...__init__". The body is intentionally empty -- __before_init__
|
||||
# owns zero-fill / defaults / vtable setup, and a class without a user __init__ has no init
|
||||
# logic to run (matches the documented placement-new idiom `obj.__before_init__()` then
|
||||
# `obj.__init__(args)`).
|
||||
# Skipped when an ancestor has a user __init__: emitting a child default would shadow it and
|
||||
# silently drop the inherited initialization for explicit `child.__init__()` calls.
|
||||
di_needed: int = 0
|
||||
if has_init == 0:
|
||||
di_needed = 1
|
||||
di_pool: memhub.MemBuddy* = trans.Pool
|
||||
if di_pool is not None:
|
||||
di_mro: sizet* = self.Gen.StructGen.get_mro_chain(di_pool, class_name)
|
||||
di_mi: int = 1
|
||||
while di_mro is not None and di_mro[di_mi] != 0:
|
||||
di_anc: str = rawptr(di_mro[di_mi])
|
||||
if di_anc is not None and string.strcmp(di_anc, class_name) != 0:
|
||||
di_anc_entry: GenTypes.StructEntry* = self.Gen.StructGen.find_struct(di_anc)
|
||||
if di_anc_entry is not None and di_anc_entry.HasInit != 0:
|
||||
di_needed = 0
|
||||
break
|
||||
di_mi += 1
|
||||
if di_needed != 0:
|
||||
self._generate_default_init(trans, struct_ty, class_name)
|
||||
|
||||
return 0
|
||||
# _translate_method - emit define <ret_ty> @SHA1.ClassName.method_name(Ptr(StructTy) %self, ...).
|
||||
# self is registered as an SSA Ptr(struct_ty) value (no alloca) so self.field can GEP directly.
|
||||
@@ -528,7 +615,7 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase):
|
||||
if decl_reuse == 0:
|
||||
# Default-parameter info (args[0] is self for normal methods; @staticmethod has no self).
|
||||
# Assign the attribute to a typed list[...]* local before __len__() or GEP base becomes i32 0.
|
||||
md_defaults: t.CPtr = None
|
||||
md_defaults: rawptr = None
|
||||
md_default_count: int = 0
|
||||
md_param_count: int = 0
|
||||
md_args_node: ast.Arguments* = fd.args
|
||||
@@ -646,18 +733,22 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase):
|
||||
|
||||
# @t.TLS one-time init guard (same implementation as FuncGenMixin.translate_function_def, also for methods).
|
||||
if is_tls_m != 0:
|
||||
tls_m_flag_name: str = pool.alloc(128)
|
||||
# Exact capacities: the reference used to be 64 while the definition was 128, so a long
|
||||
# method name produced a reference to a different (nonexistent) global.
|
||||
tls_m_flag_cap: sizet = string.strlen(method_name) + 24
|
||||
tls_m_flag_name: str = pool.alloc(tls_m_flag_cap)
|
||||
if tls_m_flag_name is not None:
|
||||
viperlib.snprintf(tls_m_flag_name, 128, "__tls_init_done_%s", method_name)
|
||||
viperlib.snprintf(tls_m_flag_name, tls_m_flag_cap, "__tls_init_done_%s", method_name)
|
||||
tls_m_gv: mlirlite.GlobalVariable* = mlirlite.create_global_int(pool, mod, tls_m_flag_name, 1, 0)
|
||||
if tls_m_gv is not None:
|
||||
i1_ty_m: mlirlite.LLVMType* = mlirlite.Int1(pool)
|
||||
if i1_ty_m is not None:
|
||||
# GlobalVariable is not a Value subclass: build a Value view before load/store.
|
||||
tls_m_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, i1_ty_m)
|
||||
tls_m_rn: str = pool.alloc(64)
|
||||
tls_m_rn_cap: sizet = string.strlen(tls_m_flag_name) + 2
|
||||
tls_m_rn: str = pool.alloc(tls_m_rn_cap)
|
||||
if tls_m_rn is not None:
|
||||
viperlib.snprintf(tls_m_rn, 64, "@%s", tls_m_flag_name)
|
||||
viperlib.snprintf(tls_m_rn, tls_m_rn_cap, "@%s", tls_m_flag_name)
|
||||
tls_m_ref: mlirlite.Value* = mlirlite.SSAValue(pool, tls_m_ptr_ty, tls_m_rn)
|
||||
if tls_m_ref is not None:
|
||||
tls_m_ld: mlirlite.Value* = mlirlite.build_load(func_builder, i1_ty_m, tls_m_ref)
|
||||
@@ -1046,4 +1137,65 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase):
|
||||
# 3. ret void
|
||||
mlirlite.build_ret_void(func_builder)
|
||||
|
||||
return 0
|
||||
# _generate_default_init - emit define void @SHA1.ClassName.__init__(Ptr(StructTy) %self) { ret void }.
|
||||
# Companion to _generate_before_init: exists purely so that explicit `obj.__init__()` source calls
|
||||
# (which lower to a direct @SHA1.Class.__init__ call) link even when the class defines no __init__.
|
||||
# The body must stay empty: __before_init__ already owns zero-fill / defaults / vtable setup.
|
||||
def _generate_default_init(self, trans: HT.Translator*, struct_ty: mlirlite.LLVMType*, class_name: str) -> int:
|
||||
"""Emit an empty default __init__ so explicit obj.__init__() call sites link."""
|
||||
if trans is None or struct_ty is None or class_name is None: return 0
|
||||
|
||||
pool: memhub.MemBuddy* = trans.Pool
|
||||
mod: mlirlite.LLVMModule* = trans.Module
|
||||
funcs_ptr: GenTypes.FuncEntry* = trans._funcs
|
||||
func_count: int = trans._func_count
|
||||
|
||||
self_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, struct_ty)
|
||||
void_ty: mlirlite.LLVMType* = mlirlite.Void(pool)
|
||||
|
||||
# Build the function name: ClassName.__init__(SHA1 namespace applied by _mangle_name).
|
||||
full_name_buf: str = pool.alloc(128)
|
||||
if full_name_buf is None: return 0
|
||||
viperlib.snprintf(full_name_buf, 128, "%s.__init__", class_name)
|
||||
full_name: str = full_name_buf
|
||||
|
||||
mangled_name: str = self.Gen.FuncGenBase._mangle_name(trans, full_name)
|
||||
|
||||
# A call site may have forward-declared __init__ first; upgrade the declaration to a
|
||||
# definition (clear IsDeclared). If a body already exists, bail to avoid a duplicate symbol.
|
||||
di_func: mlirlite.Function* = self.Gen.CallGenBase.find_func_in_module(mod, mangled_name)
|
||||
di_decl_reuse: int = 0
|
||||
if di_func is not None:
|
||||
if di_func.IsDeclared == 0: return 0
|
||||
di_decl_reuse = 1
|
||||
else:
|
||||
di_func = mlirlite.create_function(pool, mod, mangled_name, void_ty)
|
||||
if di_func is None: return 0
|
||||
func: mlirlite.Function* = di_func
|
||||
|
||||
max_funcs: int = 1024
|
||||
rc_add: int = self.Gen.CallGenLeafKit.add_func_to_table(funcs_ptr, func_count, full_name, func, max_funcs, None, 0, 0, pool)
|
||||
if rc_add == 0: trans._func_count = func_count + 1
|
||||
|
||||
if di_decl_reuse != 0:
|
||||
# Reusing the declaration: name/type its first param as %self (printer only emits named params).
|
||||
di_first_param: mlirlite.Param* = mlirlite.function_get_param_head(func)
|
||||
if di_first_param is not None:
|
||||
di_first_param.Ty = self_ptr_ty
|
||||
di_first_param.Name = "%self"
|
||||
func.IsDeclared = 0
|
||||
else:
|
||||
mlirlite.add_param(pool, func, self_ptr_ty, "%self")
|
||||
|
||||
self.Gen.CallGenLeafKit.update_global_func_spec(full_name, func)
|
||||
|
||||
entry_blk: mlirlite.BasicBlock* = mlirlite.create_block(pool, func, "entry")
|
||||
if entry_blk is None: return 0
|
||||
func_builder: mlirlite.IRBuilder* = mlirlite.new_builder(pool, func)
|
||||
if func_builder is None: return 0
|
||||
mlirlite.position_at_end(func_builder, entry_blk)
|
||||
|
||||
mlirlite.build_ret_void(func_builder)
|
||||
|
||||
return 0
|
||||
@@ -1,11 +1,10 @@
|
||||
import t, c
|
||||
from stdint import *
|
||||
import mlirlite
|
||||
import memhub
|
||||
import string
|
||||
import stdlib
|
||||
import viperlib
|
||||
import w32.win32file as w32file
|
||||
import win32.win32file as win32file
|
||||
import lib.core.VLogger as VLogger
|
||||
|
||||
# DebugInfoGen - emits function-level DWARF metadata (mlirlite text IR), mirroring TPC LLVMCG/DebugInfo.py node-for-node.
|
||||
@@ -137,11 +136,11 @@ def _EnsureModuleNodes(pool: memhub.MemBuddy*) -> int:
|
||||
if slen > 0: string.strncpy(nbuf, src, slen)
|
||||
nbuf[slen] = '\0'
|
||||
if slen > 0 and nbuf[0] != '/' and not (slen >= 2 and nbuf[1] == ':'):
|
||||
# Relative path -> prefix CWD via a w32file low-level call (as in Repl.vp), not os._mbuddy which the compiler process may not have initialized.
|
||||
# Relative path -> prefix CWD via a win32file low-level call (as in Repl.vp), not os._mbuddy which the compiler process may not have initialized.
|
||||
CwdBuf: bytes = pool.alloc(4112)
|
||||
if CwdBuf is not None:
|
||||
CwdBuf[0] = '\0'
|
||||
nc: ULONG = w32file.GetCurrentDirectoryA(ULONG(4111), CwdBuf)
|
||||
nc: ULONG = win32file.GetCurrentDirectoryA(ULONG(4111), CwdBuf)
|
||||
if nc > 0:
|
||||
# Defensive: when the buffer is too small GetCurrentDirectoryA writes undefined content; force a NUL.
|
||||
CwdBuf[4110] = '\0'
|
||||
@@ -149,7 +148,7 @@ def _EnsureModuleNodes(pool: memhub.MemBuddy*) -> int:
|
||||
# Normalize all backslashes (the CWD segment may contain '\' too).
|
||||
flen: sizet = string.strlen(nbuf)
|
||||
ni: sizet = 0
|
||||
for ni in range(flen):
|
||||
for ni in range(int(flen)):
|
||||
if nbuf[ni] == '\\': nbuf[ni] = '/'
|
||||
# Save the absolute path for mlirlite's debug call-site recorder (.vpcall file paths must match DIFile: absolute + forward slash).
|
||||
_g_abs_file_path = nbuf
|
||||
|
||||
@@ -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
|
||||
@@ -106,6 +104,10 @@ novirtual class ExprGenAttr(ExprGenBase.ExprGenBase):
|
||||
if string.strcmp(name, "CRef") == 0: return 1
|
||||
if string.strcmp(name, "CTempRef") == 0: return 1
|
||||
if string.strcmp(name, "CAutoPtr") == 0: return 1
|
||||
# Byte-order markers (t.BigEndian / t.LittleEndian): decorator markers used as
|
||||
# `X | t.BigEndian`; recognized here so they are not taken for an unregistered class.
|
||||
if string.strcmp(name, "BigEndian") == 0: return 1
|
||||
if string.strcmp(name, "LittleEndian") == 0: return 1
|
||||
return 0
|
||||
def _attr_module_path_str(self, pool: memhub.MemBuddy*, node: ast.AST*) -> str:
|
||||
"""Convert a module-reference AST into a dotted module path; None for non-module forms."""
|
||||
@@ -441,7 +443,10 @@ novirtual class ExprGenAttr(ExprGenBase.ExprGenBase):
|
||||
if cdef_str_v is not None: return self.Gen.ExprGenConst.create_global_string(builder, pool, mod, cdef_str_v, trans)
|
||||
# cross-module float CDefine (e.g. vipermath.U_M_PI) → float constant
|
||||
dchk_ma: double = self.Gen.TypeGenCDef.lookup_cdefine_double(at.attr)
|
||||
if self.Gen.TypeGenCDef.is_cdefine_double() != 0: return mlirlite.ConstFloat(pool, mlirlite.Double(pool), dchk_ma)
|
||||
# f32 声明的常量直接出 f32 常量(否则 f32 字面量会被提升成 double)
|
||||
if self.Gen.TypeGenCDef.is_cdefine_double() != 0:
|
||||
if self.Gen.TypeGenCDef.is_cdefine_float32() != 0: return mlirlite.ConstFloat(pool, mlirlite.Float(pool), dchk_ma)
|
||||
return mlirlite.ConstFloat(pool, mlirlite.Double(pool), dchk_ma)
|
||||
return self.Gen.ExprGenCdef._const_int_for_cdefine(pool, at.attr, cdef_ma_val)
|
||||
# check whether nm_ma.id.at.attr is an imported submodule (e.g. win32.win32file);
|
||||
# for a submodule do not create a global, let the nested attribute path handle it
|
||||
@@ -557,9 +562,11 @@ novirtual class ExprGenAttr(ExprGenBase.ExprGenBase):
|
||||
viperlib.snprintf(tmp_fi, tmp_fi_len, "%s:%s", at.attr, qual_buf)
|
||||
cdef_val_nested = self.Gen.ExprGenCdef._lookup_cross_module_cdefine(pool, at.attr, tmp_fi)
|
||||
if self.Gen.TypeGenCDef.is_cdefine_found() != 0:
|
||||
# cross-module float CDefine → float constant
|
||||
# cross-module float CDefine → float constant (f32 声明直接出 f32)
|
||||
dchk_nst: double = self.Gen.TypeGenCDef.lookup_cdefine_double(at.attr)
|
||||
if self.Gen.TypeGenCDef.is_cdefine_double() != 0: return mlirlite.ConstFloat(pool, mlirlite.Double(pool), dchk_nst)
|
||||
if self.Gen.TypeGenCDef.is_cdefine_double() != 0:
|
||||
if self.Gen.TypeGenCDef.is_cdefine_float32() != 0: return mlirlite.ConstFloat(pool, mlirlite.Float(pool), dchk_nst)
|
||||
return mlirlite.ConstFloat(pool, mlirlite.Double(pool), dchk_nst)
|
||||
return self.Gen.ExprGenCdef._const_int_for_cdefine(pool, at.attr, cdef_val_nested)
|
||||
# CDefine not found: fail-fast and stop compiling (no more undefined global
|
||||
# references, which would make llc report "use of undefined value").
|
||||
@@ -644,8 +651,13 @@ novirtual class ExprGenAttr(ExprGenBase.ExprGenBase):
|
||||
decl_ptr_rn: mlirlite.LLVMType* = mlirlite.Ptr(pool, decl_ty_rn)
|
||||
if decl_ptr_rn is not None:
|
||||
cast_ptr_rn: mlirlite.Value* = mlirlite.build_bitcast(builder, alias_ptr, decl_ptr_rn)
|
||||
if cast_ptr_rn is not None: return mlirlite.build_load(builder, decl_ty_rn, cast_ptr_rn)
|
||||
return mlirlite.build_load(builder, alias_fe.Ty, alias_ptr, vol_ba_rd)
|
||||
if cast_ptr_rn is not None:
|
||||
rn_alias_val: mlirlite.Value* = mlirlite.build_load(builder, decl_ty_rn, cast_ptr_rn)
|
||||
# BigEndian REnum payload alias: un-swap the load (pairing the write side).
|
||||
return self.apply_byte_order_bswap(pool, builder, rn_alias_val, alias_fe.ByteOrder)
|
||||
rn_slot_val: mlirlite.Value* = mlirlite.build_load(builder, alias_fe.Ty, alias_ptr, vol_ba_rd)
|
||||
# BigEndian REnum slot: un-swap the load (pairing the write side).
|
||||
return self.apply_byte_order_bswap(pool, builder, rn_slot_val, alias_fe.ByteOrder)
|
||||
# property getter fallback: when obj.attr is a @property/@x.getter method, emit a call
|
||||
# (try before reporting a field-not-found error, to avoid a misleading message)
|
||||
if field_info is None and at.value is not None and trans is not None:
|
||||
@@ -705,7 +717,10 @@ novirtual class ExprGenAttr(ExprGenBase.ExprGenBase):
|
||||
if self.Gen.StructGen.is_union_by_type(struct_ty) == 1:
|
||||
field_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, field_ty)
|
||||
casted: mlirlite.Value* = mlirlite.build_bitcast(builder, obj_ptr, field_ptr_ty)
|
||||
if casted is not None: return mlirlite.build_load(builder, field_ty, casted, field_vol_rd)
|
||||
if casted is not None:
|
||||
union_val_rd: mlirlite.Value* = mlirlite.build_load(builder, field_ty, casted, field_vol_rd)
|
||||
# BigEndian field: the slot holds the byte-swapped pattern, so un-swap the load.
|
||||
return self.apply_byte_order_bswap(pool, builder, union_val_rd, field_info.ByteOrder)
|
||||
return None
|
||||
# normal struct: GEP + load
|
||||
field_idx: int = field_info.Index
|
||||
@@ -720,11 +735,17 @@ novirtual class ExprGenAttr(ExprGenBase.ExprGenBase):
|
||||
decl_ptr_rd: mlirlite.LLVMType* = mlirlite.Ptr(pool, decl_ty_rd)
|
||||
if decl_ptr_rd is not None:
|
||||
cast_ptr_rd: mlirlite.Value* = mlirlite.build_bitcast(builder, field_ptr, decl_ptr_rd)
|
||||
if cast_ptr_rd is not None: return mlirlite.build_load(builder, decl_ty_rd, cast_ptr_rd, field_vol_rd)
|
||||
if cast_ptr_rd is not None:
|
||||
rn_val_rd: mlirlite.Value* = mlirlite.build_load(builder, decl_ty_rd, cast_ptr_rd, field_vol_rd)
|
||||
# BigEndian REnum payload slot: un-swap the load, pairing with the write side.
|
||||
return self.apply_byte_order_bswap(pool, builder, rn_val_rd, field_info.ByteOrder)
|
||||
# an array-typed field cannot be loaded as an SSA value; return the field pointer for
|
||||
# later indexing: self.state[0] → GEP the state field → GEP the array element
|
||||
if self.is_array_type(field_ty) != 0: return field_ptr
|
||||
loaded_val: mlirlite.Value* = mlirlite.build_load(builder, field_ty, field_ptr, field_vol_rd)
|
||||
# BigEndian field: the memory slot holds the byte-swapped pattern, so un-swap the
|
||||
# loaded value; a no-op for non-16/32/64-bit ints (pointers/aggregates unaffected).
|
||||
loaded_val = self.apply_byte_order_bswap(pool, builder, loaded_val, field_info.ByteOrder)
|
||||
# a union-typed field (e.g. Token*) is compiled as i8*; when AnnotClassName points to a
|
||||
# concrete struct type, bitcast the loaded pointer (value→pointer bitcast would be invalid).
|
||||
fc_annot: str = self.Gen.StructGen.get_field_annot_class_name(field_info)
|
||||
@@ -930,6 +951,16 @@ novirtual class ExprGenAttr(ExprGenBase.ExprGenBase):
|
||||
fe: GenTypes.FieldEntry* = self._resolve_attr_field_entry(pool, trans, attr_node)
|
||||
return self.Gen.StructGen.field_is_const(fe)
|
||||
|
||||
# attr_field_byte_order - the field's byte order (0=native / 1=BigEndian / 2=LittleEndian) for the
|
||||
# write path, where get_attribute_ptr resolves the pointer but not the FieldEntry. Mirrors
|
||||
# attr_field_is_volatile/attr_field_is_const (same _resolve_attr_field_entry resolution: only a
|
||||
# Name base resolves; a nested/global receiver yields 0 -> no swap, never a wrong swap).
|
||||
def attr_field_byte_order(self, pool: memhub.MemBuddy*, trans: HT.Translator*, attr_node: ast.AST*) -> int:
|
||||
"""Return the obj.field field's byte order (0 = native/unknown)."""
|
||||
fe: GenTypes.FieldEntry* = self._resolve_attr_field_entry(pool, trans, attr_node)
|
||||
if fe is None: return 0
|
||||
return fe.ByteOrder
|
||||
|
||||
def attr_field_decl_src(self, pool: memhub.MemBuddy*, trans: HT.Translator*, attr_node: ast.AST*) -> str:
|
||||
"""Return the field annotation source name for const diagnostics, or None."""
|
||||
fe: GenTypes.FieldEntry* = self._resolve_attr_field_entry(pool, trans, attr_node)
|
||||
|
||||
@@ -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
|
||||
@@ -67,7 +65,7 @@ novirtual class ExprGenBase:
|
||||
pos += 1
|
||||
buf[pos] = '"'
|
||||
pos += 1
|
||||
for i in range(slen):
|
||||
for i in range(int(slen)):
|
||||
ch: char = src[i]
|
||||
if ch == '\0': break
|
||||
if ch == '\"' or ch == '\\' or ch < 0x20:
|
||||
@@ -121,6 +119,61 @@ novirtual class ExprGenBase:
|
||||
def get_ptr_level(self, ty: mlirlite.LLVMType*) -> int:
|
||||
"""Compute pointer level (0=non-ptr, 1=ptr, 2=ptr-to-ptr); delegates to TSpec.SpecTypePtrLevel."""
|
||||
return TSpec.SpecTypePtrLevel(ty)
|
||||
# _const_fits_int_type - 编译期整型常量的值能否**无损**落进目标整型位宽。
|
||||
# 有符号或无符号任一解释放得下即算「放得下」(位型不变);用于二元运算按常量适配另一侧位宽。
|
||||
def _const_fits_int_type(self, val: mlirlite.Value*, target_ty: mlirlite.LLVMType*) -> int:
|
||||
"""1 = the integer constant's value fits the target integer width losslessly; else 0."""
|
||||
if val is None or target_ty is None or val.IsConst != 1: return 0
|
||||
bits: int = self.get_llvm_type_bits(target_ty)
|
||||
if bits <= 0 or bits > 63: return 0
|
||||
v: int64 = val.IntVal
|
||||
lo: int64 = -(int64(1) << (bits - 1))
|
||||
hi: int64 = (int64(1) << (bits - 1)) - 1
|
||||
if v >= lo and v <= hi: return 1
|
||||
if v >= 0:
|
||||
if bits == 63: return 1
|
||||
if v <= (int64(1) << bits) - 1: return 1
|
||||
return 0
|
||||
# _int_type_of_bits - 合成指定位宽的 LLVM 整型(1/8/16/32/64 快路径,其余按 LLVMType.Int 合成)。
|
||||
def _int_type_of_bits(self, pool: memhub.MemBuddy*, bits: int) -> mlirlite.LLVMType*:
|
||||
"""LLVM integer type of the given bit width."""
|
||||
if bits == 1: return mlirlite.Int1(pool)
|
||||
if bits == 8: return mlirlite.Int8(pool)
|
||||
if bits == 16: return mlirlite.Int16(pool)
|
||||
if bits == 32: return mlirlite.Int32(pool)
|
||||
if bits == 64: return mlirlite.Int64(pool)
|
||||
ty: mlirlite.LLVMType* = pool.alloc(mlirlite.LLVMType.__sizeof__())
|
||||
if ty is None: return None
|
||||
string.memset(ty, 0, mlirlite.LLVMType.__sizeof__())
|
||||
*ty = *mlirlite.LLVMType.Int(bits)
|
||||
return ty
|
||||
# apply_byte_order_bswap - byte-order bswap primitive (TPC parity: BaseGen._apply_bswap_if_big,
|
||||
# extended to judge BOTH markers against the target's native order).
|
||||
# Swapping is needed only when the declared encoding differs from the target's native order,
|
||||
# so: BigEndian field on a little-endian target -> swap; LittleEndian field on a big-endian
|
||||
# target -> swap; declared order == target order -> no swap. byte_order==0 (unspecified)
|
||||
# means native and is always zero-cost: no IR is emitted at all. 16/32/64-bit integers only,
|
||||
# matching TPC (i8/floats/pointers/aggregates are never swapped).
|
||||
# Call name: bare "llvm.bswap" (no width suffix). build_call emits `call i32 @llvm.bswap(i32 %x)`;
|
||||
# __function.vp:2607 detects a bare `@llvm.*` (non-`llvm.mlir.`) callee and renders it as
|
||||
# `llvm.call_intrinsic "llvm.bswap"(...)`, and mlir-translate mangles the name from the operand
|
||||
# type to `llvm.bswap.i32` (README.md:367-378, the same path used by CallGenVa.vp:123 `llvm.va_start`
|
||||
# -> `llvm.va_start.p0`). Passing `llvm.bswap.i32` explicitly is unnecessary and would break the
|
||||
# uniform mangle path, so the un-suffixed name is used.
|
||||
def apply_byte_order_bswap(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, val: mlirlite.Value*, byte_order: int) -> mlirlite.Value*:
|
||||
"""Return llvm.bswap(val) when val's declared byte_order differs from the target order; else val."""
|
||||
if byte_order == 0: return val
|
||||
# 1=BigEndian, 2=LittleEndian; compare against the target's native order.
|
||||
want_big: int = 0
|
||||
if byte_order == 1: want_big = 1
|
||||
if want_big == self.Gen.TypeGenResolve.target_is_big_endian(): return val
|
||||
if val is None or val.Ty is None: return val
|
||||
bits: int = self.get_llvm_type_bits(val.Ty)
|
||||
if bits != 16 and bits != 32 and bits != 64: return val
|
||||
# Same-type in-and-out (iN -> iN); ret_ty=val.Ty keeps the call well-typed before mangling.
|
||||
swapped: mlirlite.Value* = mlirlite.build_call(builder, "llvm.bswap", val, 1, val.Ty, 0)
|
||||
if swapped is None: return val
|
||||
return swapped
|
||||
# _is_zero_int_literal - is the AST node the integer literal 0?
|
||||
# Used for ptr != 0 / ptr == 0, where 0 means NULL and is allowed.
|
||||
def _is_zero_int_literal(self, node: ast.AST*) -> int:
|
||||
@@ -428,15 +481,23 @@ novirtual class ExprGenBase:
|
||||
return buf
|
||||
# _ptr_cast_reason_text - human-readable text for a forbidden ptr -> ptr cast.
|
||||
# Names the level and why, plus source/target types; result is heap-copied.
|
||||
# 注意:LLVM 不透明指针(!llvm.ptr)打印出来两边一模一样,看不出问题所在;故额外
|
||||
# 打印**级数 + pointee 类型**,否则现场只能看到一个"2 -> 1"而无法定位是哪个表达式。
|
||||
def _ptr_cast_reason_text(self, code: int, pool: memhub.MemBuddy*, src_ty: mlirlite.LLVMType*, dst_ty: mlirlite.LLVMType*) -> str:
|
||||
"""Human-readable reason for a forbidden ptr -> ptr cast (includes source/target types)."""
|
||||
why: str = "unknown reason"
|
||||
if code == PC_DEPTH: why = f"pointer level differs (source level {self.get_ptr_level(src_ty)} -> target level {self.get_ptr_level(dst_ty)})"
|
||||
slev: int = self.get_ptr_level(src_ty)
|
||||
dlev: int = self.get_ptr_level(dst_ty)
|
||||
if code == PC_DEPTH: why = f"pointer level differs (source level {slev} -> target level {dlev})"
|
||||
elif code == PC_NONBASIC: why = "the pointee type is a non-basic type (struct/class/union/array etc.): implicit conversion is never allowed between a non-basic type and its pointer, or between pointers of different non-basic types"
|
||||
elif code == PC_LOSSY: why = "conversion between the pointee types is lossy (int -> float with insufficient width / float -> int)"
|
||||
s_txt: str = self._type_text(pool, src_ty)
|
||||
d_txt: str = self._type_text(pool, dst_ty)
|
||||
return GenApi.escape_fstring_copy(f"implicit pointer-to-pointer conversion is forbidden: {why}; source type {s_txt} -> target type {d_txt}")
|
||||
s_pt: str = "?"
|
||||
d_pt: str = "?"
|
||||
if src_ty is not None and slev > 0: s_pt = self._type_text(pool, src_ty.Pointee)
|
||||
if dst_ty is not None and dlev > 0: d_pt = self._type_text(pool, dst_ty.Pointee)
|
||||
return GenApi.escape_fstring_copy(f"implicit pointer-to-pointer conversion is forbidden: {why}; source type {s_txt} -> target type {d_txt}; source: level {slev}, pointee {s_pt} -> target: level {dlev}, pointee {d_pt}")
|
||||
# _ctor_call_agg_ty - aggregate type if node is a class/struct constructor call.
|
||||
# The only exception to rule 3 ("construction materialization"); else None.
|
||||
def _ctor_call_agg_ty(self, node: ast.AST*) -> mlirlite.LLVMType*:
|
||||
@@ -455,7 +516,7 @@ novirtual class ExprGenBase:
|
||||
if callee is None: return None
|
||||
ent: GenTypes.StructEntry* = self.Gen.StructGen.find_struct(callee)
|
||||
if ent is not None and ent.Ty is not None: return ent.Ty
|
||||
# REnum variant ctor `EnumType.Variant(...)`: the callee is not a struct
|
||||
# REnum variant ctor `EnumType.Variant(...)`: the callee is not a struct
|
||||
# name, but materializes the base enum, so return that aggregate type.
|
||||
if cl.func.kind() == ast.ASTKind.Attribute:
|
||||
# Take the segment left of the variant name as the enum class name
|
||||
@@ -474,6 +535,23 @@ novirtual class ExprGenBase:
|
||||
"""Are a and b the same aggregate type; 1=yes / 0=no."""
|
||||
if a is None or b is None: return 0
|
||||
return self.Gen.AssignGen._stg_ty_equal(pool, a, b)
|
||||
# _agg_value_slot_ty - 值语义槽:源表达式的变量槽**自身**持有聚合物(`x: Foo`,Spec.PtrLevel == 0)。
|
||||
# 真·指针值(`p: Foo*`,以及**裸类形参** `node: T` —— 其 ABI 即 T*,见 ClassGenVTable "objects are
|
||||
# pointers")的 Spec.PtrLevel >= 1,返回 None,仍必须显式解引用(c.deref(p) / *p)。
|
||||
# 返回该聚合类型;不适用时 None。
|
||||
def _agg_value_slot_ty(self, node: ast.AST*, val: mlirlite.Value*, trans: HT.Translator*) -> mlirlite.LLVMType*:
|
||||
"""Aggregate type when the source is a variable slot holding the aggregate by value; else None."""
|
||||
n: ast.AST* = self._value_node_of(node)
|
||||
if n is None or n.kind() != ast.ASTKind.Name: return None
|
||||
nm: str = (ast.Name*(n)).id
|
||||
if nm is None or trans is None: return None
|
||||
ve: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm)
|
||||
if ve is None or ve.Alloca is None or ve.Spec is None: return None
|
||||
# 值语义槽:按名解析到该变量的值类型;此处仅做类型级判定(不依赖槽身份,避免别名/重复登记漏判)。
|
||||
# PtrLevel>=1(含裸类形参 `node: T` 的 ABI 指针):真指针,必须显式解引用。
|
||||
if ve.Spec.PtrLevel != 0: return None
|
||||
if self.is_ptr_type(ve.Alloca.Ty) == 0: return None
|
||||
return ve.Alloca.Ty.Pointee
|
||||
# Type coercion: int sext/trunc, float fpext/fptrunc, int<->float si2fp/fp2si.
|
||||
def coerce_to_type(self, builder: mlirlite.IRBuilder*, val: mlirlite.Value*, target_ty: mlirlite.LLVMType*, node: ast.AST* = None, trans: HT.Translator* = None) -> mlirlite.Value*:
|
||||
"""Coerce val to target_ty: int sext/trunc, float fpext/fptrunc, int/float conversions."""
|
||||
@@ -490,7 +568,7 @@ novirtual class ExprGenBase:
|
||||
self.Gen.TypeGenErr.fatal_error(self._value_node_of(node), "a void value cannot take part in any operation (void is only allowed for void* and typedef aliases)", "TypeError")
|
||||
return val
|
||||
# 聚合目标(结构体/数组)不接受标量(整数/浮点)初始化或赋值:标量→聚合在
|
||||
# TPV 无转换语义;旧行为把标量原样交给 build_store,而 build_store 不做类型
|
||||
# VPC 无转换语义;旧行为把标量原样交给 build_store,而 build_store 不做类型
|
||||
# 校验,落成 `store i32 0, ptr %x`——只写聚合首元素字节并抬高对齐,其余部分
|
||||
# 保持未初始化(历史现场:`l: [10]char = 0` → 打印 "Hello"+乱码)。
|
||||
# 聚合零初始化必须写 `= None`(上方 IsNoneValue → _NoneValueAsTarget 分支)。
|
||||
@@ -526,20 +604,16 @@ novirtual class ExprGenBase:
|
||||
return val
|
||||
if val_bits != 0 and target_bits != 0:
|
||||
if val_bits == target_bits: return val
|
||||
# Target i1 (bool / t.CBool): truth normalization `x != 0 -> 1/0`
|
||||
# (C 6.3.1.2), not trunc; keeps parity with `bool(x)` and condition paths.
|
||||
if target_bits == 1:
|
||||
zero_i1: mlirlite.Value* = mlirlite.const_int(builder.Pool, val_bits, 0)
|
||||
return mlirlite.build_icmp(builder, mlirlite.ICMP_NE, val, zero_i1)
|
||||
if val_bits < target_bits:
|
||||
# Unified zext/sext: i1/i8 default zext, unsigned AST nodes zext,
|
||||
# otherwise sext. trans is required so unsigned annotations resolve.
|
||||
if self._zext_or_sext_val(builder.Pool, val, node, trans) != 0: return mlirlite.build_zext(builder, val, target_ty)
|
||||
return mlirlite.build_sext(builder, val, target_ty)
|
||||
# 隐式窄化**禁止**(规则 2026-10-10 收紧,取代"整数↔整数 所有方向都允许"):
|
||||
# 位宽高→低会丢高位,必须显式 cast 或加宽目标类型。仅两类豁免:
|
||||
# ① 目标 i1:真值归一化(x != 0),语义明确,不是位截断(已在上方返回);
|
||||
# ② 源是**编译期常量且值能落进目标位宽**(如字面量 5 → i32),无信息损失。
|
||||
# 位宽高→低会丢高位,必须显式 cast 或加宽目标类型。豁免只剩一类:
|
||||
# 源是**编译期常量且值能落进目标位宽**(如字面量 5 → i32、True/False → i1),无信息损失。
|
||||
# (注意 `True`/`False` 常量按 i32 1/0 物化,见 ExprGenConst.translate_constant,
|
||||
# 正是靠此豁免才能赋给 i1 目标。)
|
||||
if val.IsConst == 1:
|
||||
iv: int64 = val.IntVal
|
||||
lo_c: int64 = -(int64(1) << (target_bits - 1))
|
||||
@@ -547,6 +621,15 @@ novirtual class ExprGenBase:
|
||||
if iv < lo_c or iv > hi_c:
|
||||
self.Gen.TypeGenErr.fatal_error(node, f"narrowing truncation overflow: constant {iv} is outside the {target_bits}-bit representable range [{lo_c}, {hi_c}]; use an explicit cast or widen the target type", "OverflowError")
|
||||
return mlirlite.build_trunc(builder, val, target_ty)
|
||||
# 规则 2026-10-13:目标 i1(bool / t.CBool)**不再**对宽整型做隐式真值归一化
|
||||
# (旧的 `x != 0 -> 1/0`)。真值归一化会掩盖真实的位宽不匹配(如 `b: bool = some_i32`),
|
||||
# 属静默转换,必须 fail-fast;需要显式 `bool(x)` / `t.CBool(x)`。
|
||||
# 合法写法不受影响:比较(!=/==/…)、`and` / `or`、`not` 本身即产出 i1
|
||||
# (translate_compare / translate_boolop / translate_unaryop),i1 源走上面的
|
||||
# `val_bits == target_bits` 直接放行;True/False 常量走上面的常量豁免。
|
||||
if target_bits == 1:
|
||||
self.Gen.TypeGenErr.fatal_error(node, f"implicit narrowing is forbidden: a {val_bits}-bit value cannot be implicitly assigned to bool (i1) (it would be silently reduced to its truthiness); use an explicit cast (e.g. bool(x) / t.CBool(x))", "CastError")
|
||||
return val
|
||||
self.Gen.TypeGenErr.fatal_error(node, f"implicit narrowing is forbidden: a {val_bits}-bit value cannot be implicitly assigned to a {target_bits}-bit type (the high bits would be lost); use an explicit cast (e.g. int32(x) / t.CInt32T(x)) or widen the target type", "CastError")
|
||||
return val
|
||||
# Float conversion.
|
||||
@@ -564,19 +647,34 @@ novirtual class ExprGenBase:
|
||||
if val_bits != 0 and target_fbits != 0: return mlirlite.build_si2fp(builder, val, target_ty)
|
||||
# float -> int
|
||||
if val_fbits != 0 and target_bits != 0: return mlirlite.build_fp2si(builder, val, target_ty)
|
||||
# Aggregate value -> pointer: spill a temp to the entry block and pass its
|
||||
# address (TPV never passes aggregates by value).
|
||||
# Aggregate value -> pointer: spill a temp to the entry block and pass its
|
||||
# address (VPC never passes aggregates by value).
|
||||
if self.is_ptr_type(val.Ty) == 0 and self.is_ptr_type(target_ty) != 0:
|
||||
vk_agg: int = val.Ty.kind()
|
||||
if vk_agg == mlirlite.LLVMType.Struct or vk_agg == mlirlite.LLVMType.Array:
|
||||
# Rule 3: implicit value -> ptr for non-basic types (Foo -> Foo*) is
|
||||
# forbidden, except "construction materialization" (see _ctor_call_agg_ty).
|
||||
ctor_v2p: mlirlite.LLVMType* = self._ctor_call_agg_ty(node)
|
||||
if self._same_agg_ty(builder.Pool, ctor_v2p, val.Ty) == 0:
|
||||
m_v2p: str = "implicit value/pointer conversion for non-basic types is forbidden (except construction materialization): a non-basic value cannot be implicitly converted to a pointer (Foo -> Foo*); to take its address use c.addr(...) / &v explicitly, or cast explicitly"
|
||||
v2p_txt: str = GenApi.escape_fstring_copy(f"; source value type {self._type_text(builder.Pool, val.Ty)} -> target type {self._type_text(builder.Pool, target_ty)}")
|
||||
self.Gen.TypeGenErr.fatal_error(self._value_node_of(node), f"{m_v2p}{v2p_txt}", "CastError")
|
||||
return val
|
||||
# forbidden, except two exemptions:
|
||||
# (a) "construction materialization" (see _ctor_call_agg_ty);
|
||||
# (b) 同型聚合值 → 同型聚合指针:值拷贝语义(等价于 C 按值传结构体)。
|
||||
# 实参聚合值被拷进**本帧(调用者帧)**的临时槽,再把该临时槽地址按 ABI
|
||||
# 交给被调者:被调者得到一份独立拷贝、就地修改不影响调用者,且该地址在
|
||||
# 整个调用期间有效(生命周期覆盖调用),故合法用例不会悬垂。
|
||||
# 唯有被调者**保存**此地址(如容器 append 挂链)才会悬垂 —— 这类写法必须由
|
||||
# 源码显式声明为指针形参(`node: T*`)并传入既有对象的地址(此时实参本就是
|
||||
# 指针,不会走到此处隐式拷贝)。显式 T* / &v / c.addr(...) 的正常用法不受影响。
|
||||
same_v2p: int = 0
|
||||
match target_ty:
|
||||
case mlirlite.LLVMType.Ptr(pointee_v2p):
|
||||
if self._same_agg_ty(builder.Pool, pointee_v2p, val.Ty) != 0: same_v2p = 1
|
||||
case _:
|
||||
pass
|
||||
if same_v2p == 0:
|
||||
ctor_v2p: mlirlite.LLVMType* = self._ctor_call_agg_ty(node)
|
||||
if self._same_agg_ty(builder.Pool, ctor_v2p, val.Ty) == 0:
|
||||
m_v2p: str = "implicit value/pointer conversion for non-basic types is forbidden (except construction materialization): a non-basic value cannot be implicitly converted to a pointer (Foo -> Foo*); to take its address use c.addr(...) / &v explicitly, or cast explicitly"
|
||||
v2p_txt: str = GenApi.escape_fstring_copy(f"; source value type {self._type_text(builder.Pool, val.Ty)} -> target type {self._type_text(builder.Pool, target_ty)}")
|
||||
self.Gen.TypeGenErr.fatal_error(self._value_node_of(node), f"{m_v2p}{v2p_txt}", "CastError")
|
||||
return val
|
||||
tmp_agg: mlirlite.Value* = self.Gen.VarGen._alloca_at_entry(builder, val.Ty)
|
||||
if tmp_agg is not None:
|
||||
mlirlite.build_store(builder, val, tmp_agg)
|
||||
@@ -588,14 +686,23 @@ novirtual class ExprGenBase:
|
||||
bc_agg: mlirlite.Value* = mlirlite.build_bitcast(builder, tmp_agg, target_ty)
|
||||
if bc_agg is not None: return bc_agg
|
||||
return tmp_agg
|
||||
# Pointer -> non-pointer value: aggregate targets keep the implicit load only
|
||||
# for construction materialization; real Foo* values must be dereferenced.
|
||||
# Pointer -> non-pointer value(聚合目标)。两类隐式 load 被保留:
|
||||
# ① **指向同型聚合的指针 → 同型聚合值**:值拷贝语义。裸类形参(`elem: T`)的 ABI 就是
|
||||
# `T*`(对象即指针),把它赋给同型值字段/变量时按值拷贝;`x: T` 这类值语义槽指针同理。
|
||||
# 拷贝后目标独立、不会悬垂 —— 与「传参用指针」的 ABI 选择互不冲突。
|
||||
# ② 构造物化(见 _ctor_call_agg_ty)。
|
||||
# 其余(不同型聚合 / 多级指针)仍 fail-fast,须显式 *p / c.deref(p) 或显式 cast。
|
||||
if self.is_ptr_type(val.Ty) != 0 and self.is_ptr_type(target_ty) == 0:
|
||||
target_kind: int = target_ty.kind()
|
||||
if target_kind == mlirlite.LLVMType.Struct or target_kind == mlirlite.LLVMType.Array:
|
||||
# Rule 3: implicit ptr -> value for non-basic types (Foo* -> Foo) is
|
||||
# forbidden, except "construction materialization" (see _ctor_call_agg_ty).
|
||||
# ① 同型指针 → 值拷贝(覆盖裸类形参的 `T*` ABI)
|
||||
if val.Ty.Pointee is not None and self._same_agg_ty(builder.Pool, val.Ty.Pointee, target_ty) != 0:
|
||||
return mlirlite.build_load(builder, target_ty, val)
|
||||
ctor_p2v: mlirlite.LLVMType* = self._ctor_call_agg_ty(node)
|
||||
# ② 值语义槽(`x: Foo`,槽自身持有聚合物):读槽 = 值拷贝
|
||||
slot_p2v: mlirlite.LLVMType* = self._agg_value_slot_ty(node, val, trans)
|
||||
if self._same_agg_ty(builder.Pool, slot_p2v, target_ty) != 0:
|
||||
return mlirlite.build_load(builder, target_ty, val)
|
||||
if self._same_agg_ty(builder.Pool, ctor_p2v, target_ty) == 0:
|
||||
m_p2v: str = "implicit value/pointer conversion for non-basic types is forbidden (except construction materialization): a pointer type cannot be implicitly converted to a non-basic value (Foo* -> Foo); to dereference use c.deref(p) / *p explicitly, or cast explicitly"
|
||||
p2v_txt: str = GenApi.escape_fstring_copy(f"; source type {self._type_text(builder.Pool, val.Ty)} -> target value type {self._type_text(builder.Pool, target_ty)}")
|
||||
@@ -620,7 +727,7 @@ novirtual class ExprGenBase:
|
||||
if val is None or val.Ty is None: return 0
|
||||
return self.Gen.ExprGenOps.should_zext_widen(pool, val, None, node, trans)
|
||||
|
||||
# translate_ptr_arith: ptr +/- int via getelementptr (index scaled by pointee
|
||||
# translate_ptr_arith: ptr +/- int via getelementptr (index scaled by pointee
|
||||
# size); array pointees ([N x ty]*) decay to element pointers first.
|
||||
def translate_ptr_arith(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, ptr_val: mlirlite.Value*, int_val: mlirlite.Value*, op: int) -> mlirlite.Value*:
|
||||
"""Pointer arithmetic: ptr +/- int via GEP with the index scaled by element size."""
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import t, c
|
||||
from stdint import *
|
||||
import ast
|
||||
import mlirlite
|
||||
@@ -95,6 +94,24 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
return None
|
||||
else: pf_chk.close()
|
||||
return buf
|
||||
# _pyi_annot_float_kind - 从 .vpi/.pyi 行的注解段(`NAME:` 到 `=` 之间)判定浮点宽度种类:
|
||||
# 独立单词 float → 1(f32);double / 未声明 / 非浮点 → 0。词边界判定避免命中 `double`/`CFloat32`。
|
||||
def _pyi_annot_float_kind(self, buf: bytes, start: sizet, end: sizet) -> int:
|
||||
"""Return 1 when the annotation text [start,end) declares a 32-bit `float`, else 0."""
|
||||
i: sizet = start
|
||||
while i + 5 <= end:
|
||||
if (buf[i] == 'f' and buf[i + 1] == 'l' and buf[i + 2] == 'o' and buf[i + 3] == 'a' and buf[i + 4] == 't'):
|
||||
ok_before: int = 1
|
||||
ok_after: int = 1
|
||||
if i > start:
|
||||
pb: char = buf[i - 1]
|
||||
if (pb >= 'A' and pb <= 'Z') or (pb >= 'a' and pb <= 'z') or (pb >= '0' and pb <= '9') or pb == '_': ok_before = 0
|
||||
if i + 5 < end:
|
||||
pa: char = buf[i + 5]
|
||||
if (pa >= 'A' and pa <= 'Z') or (pa >= 'a' and pa <= 'z') or (pa >= '0' and pa <= '9') or pa == '_': ok_after = 0
|
||||
if ok_before != 0 and ok_after != 0: return 1
|
||||
i += 1
|
||||
return 0
|
||||
# _lookup_cross_module_cdefine - cross-module CDefine lookup: resolve the source module
|
||||
# from from_imports, parse its pyi "NAME: t.CDefine = value", then cache in the current module.
|
||||
def _lookup_cross_module_cdefine(self, pool: memhub.MemBuddy*, name: str, from_imports: str, imported_modules: str = None, depth: int = 0, node: ast.AST* = None) -> int64:
|
||||
@@ -147,7 +164,9 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
if self.Gen.TypeGenCDef.is_cdefine_str() != 0: self.Gen.TypeGenCDef.register_cdefine_string(pool, name, xval)
|
||||
elif self.Gen.TypeGenCDef.is_cdefine_double() != 0:
|
||||
xd_v: double = string.atof(xval)
|
||||
self.Gen.TypeGenCDef.register_cdefine_double(pool, name, xd_v)
|
||||
# 缓存里带宽度种类(f32 声明 → 1),否则使用方会把 f32 常量当 double 物化
|
||||
xd_f32: int = self.Gen.TypeGenCDef.is_cdefine_float32()
|
||||
self.Gen.TypeGenCDef.register_cdefine_double(pool, name, xd_v, xd_f32)
|
||||
else:
|
||||
xv_i: int64 = string.atoll(xval)
|
||||
self.Gen.TypeGenCDef.register_cdefine_constant(pool, name, xv_i)
|
||||
@@ -286,6 +305,9 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
result_val: int64 = 0
|
||||
result_found: int = 0
|
||||
res_is_double: int = 0
|
||||
# 浮点宽度种类(1 = 注解声明为 32 位 float):跨模块缓存与本地注册都要带上,
|
||||
# 否则使用方把 f32 常量当 double 物化(`define PI: float = ...` 现场)。
|
||||
res_is_f32: int = 0
|
||||
res_is_str: int = 0
|
||||
while pos < total_len:
|
||||
# find line start
|
||||
@@ -321,6 +343,9 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
eq_pos: sizet = line_start + name_len + 1
|
||||
while eq_pos < line_start + line_len and pyi_buf[eq_pos] != '=': eq_pos += 1
|
||||
if eq_pos >= line_start + line_len: continue
|
||||
# 注解段 = [名字冒号后, '='),用于判定声明的浮点宽度(如 `PI: float | t.CDefine`)
|
||||
annot_start: sizet = line_start + name_len + 1
|
||||
annot_end: sizet = eq_pos
|
||||
eq_pos += 1 # skip '='
|
||||
|
||||
# skip spaces
|
||||
@@ -578,7 +603,9 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
if is_float_val != 0:
|
||||
# float CDefine (e.g. U_M_PI = 3.14159...): cache as double for double
|
||||
# constant generation; int part serves the int path (is_cdefine_double flag).
|
||||
self.Gen.TypeGenCDef.register_cdefine_double(pool, name, ldbl)
|
||||
# 注解声明为 float 时带上 f32 种类,使用方才能直接出 f32 常量。
|
||||
res_is_f32 = self._pyi_annot_float_kind(pyi_buf, annot_start, annot_end)
|
||||
self.Gen.TypeGenCDef.register_cdefine_double(pool, name, ldbl, res_is_f32)
|
||||
result_val = lval
|
||||
result_found = 1
|
||||
res_is_double = 1
|
||||
@@ -800,7 +827,8 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
cbuf_d: str = pool.alloc(64)
|
||||
if cbuf_d is not None:
|
||||
stdio.snprintf(cbuf_d, 64, "%.17g", dval_cc)
|
||||
self.Gen.TypeGenCDef.register_xmod_cdefine(ckey2, cbuf_d, 1, 0)
|
||||
# 浮点种类随缓存一起带上(1=f64 / 2=f32),供后续使用方直接物化宽度
|
||||
self.Gen.TypeGenCDef.register_xmod_cdefine(ckey2, cbuf_d, self.Gen.TypeGenCDef.xmod_float_kind(), 0)
|
||||
self.Gen.TypeGenCDef.set_cdefine_found(1)
|
||||
else: self.Gen.TypeGenCDef.set_cdefine_found(0)
|
||||
|
||||
@@ -842,17 +870,27 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
# ============================================================
|
||||
# cdefine_use_width_value - materialize CDefine constants at the use-site width (TPC-aligned)
|
||||
def _cdefine_bits_value(self, value: int64, bits: int) -> int64:
|
||||
"""把值按位宽**截断**后再重解释为该位宽的有符号表示(与 AssignGenMixin._normalize_cdefine_bits 同口径)。
|
||||
"""把值按位宽处理:**只有在当前位宽的两种解释下都装不下时才截断**,否则原样保留。
|
||||
|
||||
「左侧只会看到截断了的数据」:超宽的值先按 bits 取低位(WORD(0x80000000) → 0),
|
||||
而不是只对"刚好越界一点"的值做重解释。
|
||||
实现用左移后算术右移,而不是 `value & 0xFFFFFFFF` 这类大掩码字面量:
|
||||
掩码字面量(如 4294967295)超出 int32 范围,其加宽方向在 TPC/VPC 上可能判成 sext,
|
||||
会得到 0xFFFFFFFFFFFFFFFF 而不是 0xFFFFFFFF,导致截断失效。
|
||||
「左侧只会看到截断了的数据」:超宽的值先按 bits 取低位(如 WORD(0x80000000) → 0)。
|
||||
但不该把"无符号可容、有符号装不下"的**正值**重解释成负数:旧实现无条件
|
||||
`(value << 56) >> 56`,8 位下的 160 被读成 -96,正值静默变负 —— 现场
|
||||
Gargantua `_start` 的 `string.memset(fb, 0, GW * GH * 4)`(GW=160, GH=90)
|
||||
被折成 -34560,作为长度写穿映射区 → #PF。位型不变(const_int 本就按位宽截断),
|
||||
只是不再翻转可容正值的语义。实现仍避开大掩码字面量(见下)。
|
||||
"""
|
||||
if bits == 8: return (value << 56) >> 56
|
||||
if bits == 16: return (value << 48) >> 48
|
||||
if bits == 32: return (value << 32) >> 32
|
||||
if bits == 8:
|
||||
if value >= -128 and value <= 127: return value
|
||||
if value > 127 and (value >> 8) == 0: return value
|
||||
return (value << 56) >> 56
|
||||
if bits == 16:
|
||||
if value >= -32768 and value <= 32767: return value
|
||||
if value > 32767 and (value >> 16) == 0: return value
|
||||
return (value << 48) >> 48
|
||||
if bits == 32:
|
||||
if value >= -2147483647 - 1 and value <= 2147483647: return value
|
||||
if value > 2147483647 and (value >> 32) == 0: return value
|
||||
return (value << 32) >> 32
|
||||
return value
|
||||
|
||||
def cdefine_use_width_value(self, pool: memhub.MemBuddy*, node: ast.AST*, name: str, value: int64, target_ty: mlirlite.LLVMType*, use_ctx: str = None) -> mlirlite.Value*:
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import t, c
|
||||
from stdint import *
|
||||
import ast
|
||||
import mlirlite
|
||||
@@ -54,7 +53,7 @@ novirtual class ExprGenCls(ExprGenBase.ExprGenBase):
|
||||
key_buf: bytes = stdlib.malloc(ml + sl + 2)
|
||||
if key_buf is None: return
|
||||
viperlib.snprintf(key_buf, ml + sl + 2, "%s.%s", module_sha1, symbol)
|
||||
for i in range(self.DocCount):
|
||||
for i in range(int(self.DocCount)):
|
||||
if string.strcmp(self.DocKeys + i * DOC_KEY_SZ, key_buf) == 0:
|
||||
stdlib.free(key_buf)
|
||||
return
|
||||
@@ -70,7 +69,7 @@ novirtual class ExprGenCls(ExprGenBase.ExprGenBase):
|
||||
key_buf: bytes = stdlib.malloc(ml + sl + 2)
|
||||
if key_buf is None: return None
|
||||
viperlib.snprintf(key_buf, ml + sl + 2, "%s.%s", module_sha1, symbol)
|
||||
for i in range(self.DocCount):
|
||||
for i in range(int(self.DocCount)):
|
||||
if string.strcmp(self.DocKeys + i * DOC_KEY_SZ, key_buf) == 0:
|
||||
stdlib.free(key_buf)
|
||||
return self.DocValues + i * DOC_VAL_SZ
|
||||
@@ -82,7 +81,7 @@ novirtual class ExprGenCls(ExprGenBase.ExprGenBase):
|
||||
ch: list[ast.AST*]* = tree.children
|
||||
if ch is None: return
|
||||
cn: sizet = len(ch)
|
||||
for ci in range(cn):
|
||||
for ci in range(int(cn)):
|
||||
child: ast.AST* = ch.get(ci)
|
||||
if child is None: continue
|
||||
kd: int = child.kind()
|
||||
@@ -102,7 +101,7 @@ novirtual class ExprGenCls(ExprGenBase.ExprGenBase):
|
||||
cd_body: list[ast.AST*]* = cd.children
|
||||
if cd_body is not None:
|
||||
cbn: sizet = len(cd_body)
|
||||
for mi in range(cbn):
|
||||
for mi in range(int(cbn)):
|
||||
m: ast.AST* = cd_body.get(mi)
|
||||
if m is None or m.kind() != ast.ASTKind.FunctionDef: continue
|
||||
mfd: ast.FunctionDef* = ast.FunctionDef*(m)
|
||||
@@ -183,13 +182,13 @@ novirtual class ExprGenCls(ExprGenBase.ExprGenBase):
|
||||
stdlib.free(items)
|
||||
return 1
|
||||
total: sizet = 0
|
||||
for j in range(n_items): total += string.strlen(items + j * 128) + 1
|
||||
for j in range(int(n_items)): total += string.strlen(items + j * 128) + 1
|
||||
sym_buf: bytes = stdlib.malloc(total + 1)
|
||||
if sym_buf is None:
|
||||
stdlib.free(items)
|
||||
return 0
|
||||
sym_buf[0] = '\0'
|
||||
for j in range(n_items):
|
||||
for j in range(int(n_items)):
|
||||
if j > 0: string.strcat(sym_buf, ".")
|
||||
string.strcat(sym_buf, items + (n_items - 1 - j) * 128)
|
||||
string.strcpy(out_mod, mod_sha1)
|
||||
@@ -199,14 +198,14 @@ novirtual class ExprGenCls(ExprGenBase.ExprGenBase):
|
||||
return 1
|
||||
# variable/class: symbol = head_id + '.' + items reversed
|
||||
total2: sizet = string.strlen(head_id)
|
||||
for j in range(n_items): total2 += string.strlen(items + j * 128) + 1
|
||||
for j in range(int(n_items)): total2 += string.strlen(items + j * 128) + 1
|
||||
sym2: bytes = stdlib.malloc(total2 + 1)
|
||||
if sym2 is None:
|
||||
stdlib.free(items)
|
||||
return 0
|
||||
sym2[0] = '\0'
|
||||
string.strcat(sym2, head_id)
|
||||
for j in range(n_items):
|
||||
for j in range(int(n_items)):
|
||||
string.strcat(sym2, ".")
|
||||
string.strcat(sym2, items + (n_items - 1 - j) * 128)
|
||||
se2: GenTypes.StructEntry* = self.Gen.StructGen.find_struct(head_id)
|
||||
@@ -333,7 +332,7 @@ novirtual class ExprGenCls(ExprGenBase.ExprGenBase):
|
||||
if seg is None: return None
|
||||
start: sizet = 0
|
||||
i: sizet
|
||||
for i in range(alen):
|
||||
for i in range(int(alen)):
|
||||
if annot[i] == '|':
|
||||
s: sizet = start
|
||||
e: sizet = i
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import t, c
|
||||
from stdint import *
|
||||
import ast
|
||||
import mlirlite
|
||||
@@ -24,7 +23,7 @@ novirtual class ExprGenConst(ExprGenBase.ExprGenBase):
|
||||
if escaped is None: return None
|
||||
|
||||
slen: sizet = string.strlen(str_val)
|
||||
count: int = slen + 1
|
||||
count: int = int(slen + 1)
|
||||
|
||||
i8_ty: mlirlite.LLVMType* = mlirlite.Int8(pool)
|
||||
arr_ty: mlirlite.LLVMType* = mlirlite.Array(pool, i8_ty, count)
|
||||
@@ -60,6 +59,25 @@ novirtual class ExprGenConst(ExprGenBase.ExprGenBase):
|
||||
|
||||
bc: mlirlite.Value* = mlirlite.build_bitcast(builder, gv_ref, i8_ptr_ty)
|
||||
return bc
|
||||
# _explicit_width_int_const - 显式宽度字面量(`123_int32` / `0xFFFFFFFF_uint32`):
|
||||
# 按声明宽度出常量;值必须落在该宽度的可表示范围(有符号 [-2^(w-1), 2^(w-1)-1];无符号 [0, 2^w-1]),
|
||||
# 越界编译期 fail-fast(不允许隐式截断)。w==64 时 int64 全域都合法。
|
||||
def _explicit_width_int_const(self, pool: memhub.MemBuddy*, node: ast.AST*, w: int, is_unsigned: int, iv: int64) -> mlirlite.Value*:
|
||||
"""Explicit-width integer literal: strict range check, then a constant of exactly w bits."""
|
||||
ok: int = 1
|
||||
if is_unsigned != 0:
|
||||
if w < 64 and iv < 0: ok = 0
|
||||
elif w <= 62 and iv > (int64(1) << w) - 1: ok = 0
|
||||
else:
|
||||
if w < 64:
|
||||
lo: int64 = -(int64(1) << (w - 1))
|
||||
hi: int64 = (int64(1) << (w - 1)) - 1
|
||||
if iv < lo or iv > hi: ok = 0
|
||||
if ok == 0:
|
||||
wname: str = "int"
|
||||
if is_unsigned != 0: wname = "uint"
|
||||
self.Gen.TypeGenErr.fatal_error(node, f"integer literal {iv} does not fit the explicit width {wname}{w} (it would truncate); use a wider suffix or an explicit cast", "OverflowError")
|
||||
return mlirlite.const_int(pool, w, iv)
|
||||
# Translate constant expressions
|
||||
def translate_constant(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, node: ast.AST*, trans: HT.Translator* = None, target_ty: mlirlite.LLVMType* = None) -> mlirlite.Value*:
|
||||
"""Translate a constant node (int/str/bool)."""
|
||||
@@ -70,11 +88,28 @@ novirtual class ExprGenConst(ExprGenBase.ExprGenBase):
|
||||
# Bit width: (1) if the target context is >32-bit int, use i64 directly - required
|
||||
# since atoll wraps 0xFFFF... to -1 as signed int64; (2) else i64 if out of i32 range, otherwise i32.
|
||||
iv: int64 = cn.int_val
|
||||
# 显式宽度后缀(_int<N>/_uint<N>/_i<N>/_u<N>):按声明宽度出常量并严格校验不截断
|
||||
if cn.int_width > 0: return self._explicit_width_int_const(pool, node, cn.int_width, cn.int_unsigned, iv)
|
||||
if target_ty is not None and self.is_ptr_type(target_ty) == 0 and self.get_llvm_type_bits(target_ty) > 32: return mlirlite.const_int64(pool, iv)
|
||||
# 位宽按值推导:i32 放不下、但 u32 放得下的字面量归为 32 位(u32)常量,而不是一律落 64 位
|
||||
# (否则 `crc ^ 0xFFFFFFFF` 的异或会变成 64 位,赋值回 32 位 uint 时报“隐式窄化禁止”)。
|
||||
# 文本用有符号等价(LLVM/MLIR 惯例,避免越界字面量);IntVal 保留无符号真值,
|
||||
# 使窄化检查按真实数值判定,不静默截断。
|
||||
if iv > 2147483647 and iv <= 4294967295:
|
||||
ty32: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
||||
nm32: str = pool.alloc(32)
|
||||
if nm32 is None: return mlirlite.const_int32(pool, iv)
|
||||
viperlib.snprintf(nm32, 32, "%lld", iv - 4294967296)
|
||||
return mlirlite.ConstInt(pool, ty32, iv, nm32)
|
||||
if iv > 2147483647 or iv < -2147483648: return mlirlite.const_int64(pool, iv)
|
||||
return mlirlite.const_int32(pool, iv)
|
||||
elif cn.const_kind == ast.CONST_FLOAT:
|
||||
# Float constants default to double (64-bit); coerce_to_type fptruncs on assignment
|
||||
# 目标类型是 32 位浮点(float(1.5) / x: float = 1.5 / f32 形参)→ 直接出 f32 常量。
|
||||
# 旧实现无视 target_ty 一律先出 double 常量、再由 coerce_to_type fptrunc,
|
||||
# 现场:float(2.0) / float(1.0) 生成 f64 常量 + fptrunc,与双精度常量混算时又多出 fpext。
|
||||
# 其余情况(无目标类型 / double / 非浮点)保持 double 常量,由 coerce_to_type 负责转换。
|
||||
if target_ty is not None and self.get_llvm_float_bits(target_ty) == 32:
|
||||
return mlirlite.ConstFloat(pool, target_ty, cn.float_val)
|
||||
double_ty: mlirlite.LLVMType* = mlirlite.Double(pool)
|
||||
return mlirlite.ConstFloat(pool, double_ty, cn.float_val)
|
||||
elif cn.const_kind == ast.CONST_CHAR:
|
||||
@@ -267,7 +302,7 @@ novirtual class ExprGenConst(ExprGenBase.ExprGenBase):
|
||||
self.Gen.CallGenCall._call_method_on_ptr(pool, builder, mod, spec_name, "append", storage_ptr, append_args, 1, trans)
|
||||
|
||||
# Translate the remaining elements
|
||||
for i in range(1, elts_count):
|
||||
for i in range(1, int(elts_count)):
|
||||
elem_node_i: ast.AST* = elts.get(i)
|
||||
elem_val_i: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, elem_node_i, None, 0, trans)
|
||||
if elem_val_i is not None:
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import t, c
|
||||
from stdint import *
|
||||
import ast
|
||||
import mlirlite
|
||||
@@ -26,8 +25,25 @@ novirtual class ExprGenFstr(ExprGenBase.ExprGenBase):
|
||||
js: ast.JoinedStr* = ast.JoinedStr*(node)
|
||||
if js is None or js.values is None: return None
|
||||
|
||||
parts: list[ast.AST*]* = js.values
|
||||
pn: sizet = len(parts)
|
||||
|
||||
# Size the format buffer from the actual parts: each literal may double ('%' -> '%%') and
|
||||
# each conversion contributes a short spec. A fixed 1024 silently dropped trailing
|
||||
# literals/converters while their arguments were still appended -> wrong runtime output.
|
||||
fmt_cap: sizet = 16
|
||||
for pi0 in range(int(pn)):
|
||||
part0: ast.AST* = parts.get(pi0)
|
||||
if part0 is None: continue
|
||||
if part0.kind() == ast.ASTKind.Constant:
|
||||
pc0: ast.Constant* = ast.Constant*(part0)
|
||||
if pc0.const_kind == ast.CONST_STR and pc0.str_val is not None:
|
||||
fmt_cap = fmt_cap + string.strlen(pc0.str_val) * 2
|
||||
elif part0.kind() == ast.ASTKind.FormattedValue:
|
||||
fmt_cap = fmt_cap + FSTR_SPEC_MAX + 16
|
||||
|
||||
# Compile-time format string buffer (pool-allocated)
|
||||
fmt_buf: str = pool.alloc(FSTR_BUF_SIZE)
|
||||
fmt_buf: str = pool.alloc(fmt_cap)
|
||||
if fmt_buf is None: return None
|
||||
fmt_buf[0] = '\0'
|
||||
|
||||
@@ -36,15 +52,13 @@ novirtual class ExprGenFstr(ExprGenBase.ExprGenBase):
|
||||
arg_tail: mlirlite.Value* = None
|
||||
arg_count: int = 0
|
||||
|
||||
parts: list[ast.AST*]* = js.values
|
||||
pn: sizet = len(parts)
|
||||
for pi in range(pn):
|
||||
for pi in range(int(pn)):
|
||||
part: ast.AST* = parts.get(pi)
|
||||
if part is None: continue
|
||||
if part.kind() == ast.ASTKind.Constant:
|
||||
pcn: ast.Constant* = ast.Constant*(part)
|
||||
if pcn.const_kind == ast.CONST_STR and pcn.str_val is not None:
|
||||
self._fstr_append_literal(fmt_buf, pcn.str_val)
|
||||
self._fstr_append_literal(fmt_buf, fmt_cap, pcn.str_val)
|
||||
continue
|
||||
if part.kind() != ast.ASTKind.FormattedValue: continue
|
||||
fv: ast.FormattedValue* = ast.FormattedValue*(part)
|
||||
@@ -66,7 +80,7 @@ novirtual class ExprGenFstr(ExprGenBase.ExprGenBase):
|
||||
# fail-fast: cannot infer the f-string format (missing value type); abort
|
||||
self.Gen.TypeGenErr.fatal_error(fv.value, "f-string format cannot be inferred (value type missing)", "TypeError")
|
||||
# Append the format specifier
|
||||
if string.strlen(fmt_buf) + string.strlen(c_fmt) < FSTR_BUF_SIZE - 1: string.strcat(fmt_buf, c_fmt)
|
||||
self._fstr_append_raw(fmt_buf, fmt_cap, c_fmt)
|
||||
# Convert the argument type (i8/i16->i32, bool->i32, f32->f64)
|
||||
cast_val: mlirlite.Value* = self._fstr_cast_value(builder, pool, val, c_fmt, fv.value, trans)
|
||||
if cast_val is None: cast_val = val
|
||||
@@ -82,7 +96,10 @@ novirtual class ExprGenFstr(ExprGenBase.ExprGenBase):
|
||||
if node is None or builder is None or trans is None: return None
|
||||
fv: ast.FormattedValue* = ast.FormattedValue*(node)
|
||||
if fv is None or fv.value is None: return None
|
||||
fmt_buf: str = pool.alloc(FSTR_BUF_SIZE)
|
||||
# A bare FormattedValue contributes exactly one conversion spec (bounded by FSTR_SPEC_MAX
|
||||
# plus the C prefix/width); size the buffer from that instead of the fixed 1024.
|
||||
fmt_cap: sizet = FSTR_SPEC_MAX + 64
|
||||
fmt_buf: str = pool.alloc(fmt_cap)
|
||||
if fmt_buf is None: return None
|
||||
fmt_buf[0] = '\0'
|
||||
arg_head: mlirlite.Value* = None
|
||||
@@ -103,7 +120,7 @@ novirtual class ExprGenFstr(ExprGenBase.ExprGenBase):
|
||||
if c_fmt is None:
|
||||
# fail-fast: cannot infer the f-string format (missing value type); abort
|
||||
self.Gen.TypeGenErr.fatal_error(fv.value, "f-string format cannot be inferred (value type missing)", "TypeError")
|
||||
if string.strlen(fmt_buf) + string.strlen(c_fmt) < FSTR_BUF_SIZE - 1: string.strcat(fmt_buf, c_fmt)
|
||||
self._fstr_append_raw(fmt_buf, fmt_cap, c_fmt)
|
||||
cast_val: mlirlite.Value* = self._fstr_cast_value(builder, pool, val, c_fmt, fv.value, trans)
|
||||
if cast_val is None: cast_val = val
|
||||
cast_val.Next = None
|
||||
@@ -149,34 +166,38 @@ novirtual class ExprGenFstr(ExprGenBase.ExprGenBase):
|
||||
if arg_tail is not None: arg_tail.Next = None
|
||||
mlirlite.build_call(builder, "snprintf", buf_ptr, 3 + arg_count, i32_ty, 1)
|
||||
return buf_ptr
|
||||
def _fstr_append_literal(self, fmt_buf: str, lit: str) -> None:
|
||||
"""Append a literal to the format string, escaping percent signs."""
|
||||
def _fstr_append_literal(self, fmt_buf: str, cap: sizet, lit: str) -> None:
|
||||
"""Append a literal to the format string, escaping percent signs; fail-fast on overflow."""
|
||||
if fmt_buf is None or lit is None: return
|
||||
cur_len: sizet = string.strlen(fmt_buf)
|
||||
rem: sizet = FSTR_BUF_SIZE - 1 - cur_len
|
||||
if rem == 0: return
|
||||
src_len: sizet = string.strlen(lit)
|
||||
# Worst case: every character expands to "%%".
|
||||
if cur_len + src_len * 2 + 1 > cap:
|
||||
self.Gen.TypeGenErr.fatal_error(None, f"f-string format text exceeds its {cap}-byte buffer ({cur_len} used + up to {src_len * 2} escaped + 1 NUL); shorten the literal", "OverflowError")
|
||||
si: sizet = 0
|
||||
while si < src_len and rem > 0:
|
||||
while si < src_len:
|
||||
ch: char = lit[si]
|
||||
if ch == '%':
|
||||
if rem < 2: break
|
||||
fmt_buf[cur_len] = '%'
|
||||
fmt_buf[cur_len + 1] = '%'
|
||||
cur_len += 2
|
||||
rem -= 2
|
||||
else:
|
||||
fmt_buf[cur_len] = ch
|
||||
cur_len += 1
|
||||
rem -= 1
|
||||
si += 1
|
||||
fmt_buf[cur_len] = '\0'
|
||||
def _fstr_append_raw(self, fmt_buf: str, s: str) -> None:
|
||||
"""Append a raw string without percent escaping."""
|
||||
def _fstr_append_raw(self, fmt_buf: str, cap: sizet, s: str) -> None:
|
||||
"""Append a raw string without percent escaping; fail-fast on overflow."""
|
||||
if fmt_buf is None or s is None: return
|
||||
cur_len: sizet = string.strlen(fmt_buf)
|
||||
src_len: sizet = string.strlen(s)
|
||||
if cur_len + src_len < FSTR_BUF_SIZE: string.strcpy(fmt_buf + cur_len, s)
|
||||
if cur_len + src_len + 1 > cap:
|
||||
self.Gen.TypeGenErr.fatal_error(None, f"f-string format text exceeds its {cap}-byte buffer ({cur_len} used + {src_len} to append + 1 NUL); shorten the format spec", "OverflowError")
|
||||
i: sizet = 0
|
||||
while i < src_len:
|
||||
fmt_buf[cur_len + i] = s[i]
|
||||
i += 1
|
||||
fmt_buf[cur_len + src_len] = '\0'
|
||||
def _fstr_parse_format_spec(self, pool: memhub.MemBuddy*, spec_node: ast.AST*, val: mlirlite.Value*, arg_node: ast.AST*) -> str:
|
||||
"""Convert a Python format_spec into a C printf format string."""
|
||||
if spec_node is None or pool is None or val is None: return None
|
||||
@@ -189,16 +210,22 @@ novirtual class ExprGenFstr(ExprGenBase.ExprGenBase):
|
||||
jss: ast.JoinedStr* = ast.JoinedStr*(spec_node)
|
||||
if jss.values is None: return None
|
||||
vn: sizet = len(jss.values)
|
||||
for vi in range(vn):
|
||||
for vi in range(int(vn)):
|
||||
vpart: ast.AST* = jss.values.get(vi)
|
||||
if vpart is None: continue
|
||||
if vpart.kind() != ast.ASTKind.Constant: return None
|
||||
vcn: ast.Constant* = ast.Constant*(vpart)
|
||||
if vcn.const_kind != ast.CONST_STR or vcn.str_val is None: return None
|
||||
if string.strlen(spec) + string.strlen(vcn.str_val) < FSTR_SPEC_MAX - 1: string.strcat(spec, vcn.str_val)
|
||||
# fail-fast: a truncated spec can still look valid (e.g. ">10.5f" cut to ">10."),
|
||||
# which silently changes the output format.
|
||||
if string.strlen(spec) + string.strlen(vcn.str_val) >= FSTR_SPEC_MAX - 1:
|
||||
self.Gen.TypeGenErr.fatal_error(arg_node, f"f-string format spec exceeds {FSTR_SPEC_MAX - 1} characters; shorten it", "OverflowError")
|
||||
string.strcat(spec, vcn.str_val)
|
||||
elif sk == ast.ASTKind.Constant:
|
||||
cn: ast.Constant* = ast.Constant*(spec_node)
|
||||
if cn.const_kind != ast.CONST_STR or cn.str_val is None: return None
|
||||
if string.strlen(cn.str_val) >= FSTR_SPEC_MAX - 1:
|
||||
self.Gen.TypeGenErr.fatal_error(arg_node, f"f-string format spec exceeds {FSTR_SPEC_MAX - 1} characters; shorten it", "OverflowError")
|
||||
string.strncpy(spec, cn.str_val, FSTR_SPEC_MAX - 1)
|
||||
else: return None
|
||||
slen: sizet = string.strlen(spec)
|
||||
|
||||
@@ -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()
|
||||
@@ -46,12 +45,12 @@ novirtual class ExprGenMixin(ExprGenBase.ExprGenBase):
|
||||
rv: mlirlite.Value* = self.Gen.ExprGenConst.translate_constant(builder, pool, mod, node, trans, target_ty)
|
||||
return rv
|
||||
elif k == ast.ASTKind.Name: return self.Gen.ExprGenName.translate_name_value(builder, pool, node, trans, target_ty, use_ctx)
|
||||
elif k == ast.ASTKind.BinOp: return self.Gen.ExprGenOps.translate_binop(pool, builder, mod, node, trans)
|
||||
elif k == ast.ASTKind.BinOp: return self.Gen.ExprGenOps.translate_binop(pool, builder, mod, node, trans, target_ty)
|
||||
elif k == ast.ASTKind.Call: return self.Gen.CallGen.translate_call(pool, builder, mod, node, funcs_ptr, func_count, trans)
|
||||
# M2.4 `new stack|heap|memgr(x) [slife|hlife] T(args)`:只换"取存储",其余复用构造序列
|
||||
elif k == ast.ASTKind.New: return self.Gen.CallGenCtor._translate_new_expr(pool, builder, mod, ast.New*(node), trans)
|
||||
elif k == ast.ASTKind.Compare: return self.Gen.ExprGenOps.translate_compare(builder, pool, mod, node, trans)
|
||||
elif k == ast.ASTKind.UnaryOp: return self.Gen.ExprGenOps.translate_unaryop(builder, pool, mod, node, trans)
|
||||
elif k == ast.ASTKind.UnaryOp: return self.Gen.ExprGenOps.translate_unaryop(builder, pool, mod, node, trans, target_ty)
|
||||
elif k == ast.ASTKind.BoolOp: return self.Gen.ExprGenOps.translate_boolop(builder, pool, mod, node, funcs_ptr, func_count, trans)
|
||||
elif k == ast.ASTKind.Subscript: return self.Gen.ExprGenName.translate_subscript(builder, pool, mod, node, trans)
|
||||
elif k == ast.ASTKind.Attribute: return self.Gen.ExprGenAttr.translate_attribute(builder, pool, mod, node, trans)
|
||||
|
||||
@@ -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,7 +54,11 @@ 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")
|
||||
if self.Gen.TypeGenCDef.is_cdefine_double() != 0: return mlirlite.ConstFloat(pool, mlirlite.Double(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).
|
||||
cdef_val_c: int64 = self.Gen.TypeGenCDef.lookup_cdefine_constant(nm_id)
|
||||
@@ -81,7 +84,10 @@ novirtual class ExprGenName(ExprGenBase.ExprGenBase):
|
||||
# Cross-module float CDefine is cached as double; return a float constant
|
||||
# rather than a truncated integer.
|
||||
dchk_nm: double = self.Gen.TypeGenCDef.lookup_cdefine_double(nm_id)
|
||||
if self.Gen.TypeGenCDef.is_cdefine_double() != 0: return mlirlite.ConstFloat(pool, mlirlite.Double(pool), dchk_nm)
|
||||
# 同本模块路径:f32 声明的常量直接出 f32 常量(跨模块缓存已带宽度种类)。
|
||||
if self.Gen.TypeGenCDef.is_cdefine_double() != 0:
|
||||
if self.Gen.TypeGenCDef.is_cdefine_float32() != 0: return mlirlite.ConstFloat(pool, mlirlite.Float(pool), dchk_nm)
|
||||
return mlirlite.ConstFloat(pool, mlirlite.Double(pool), dchk_nm)
|
||||
# Same use-site width rule (cross-module CDefine)
|
||||
uw_x: mlirlite.Value* = self.Gen.ExprGenCdef.cdefine_use_width_value(pool, node, nm_id, cdef_val, target_ty, use_ctx)
|
||||
if uw_x is not None: return uw_x
|
||||
@@ -173,10 +179,14 @@ novirtual class ExprGenName(ExprGenBase.ExprGenBase):
|
||||
return alloca
|
||||
# t.CVolatile local: load carries volatile
|
||||
vol_l: int = 0
|
||||
bo_l: int = 0
|
||||
if trans is not None:
|
||||
ve_l: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm_id)
|
||||
vol_l = self.Gen.VarGen.var_is_volatile(ve_l)
|
||||
return mlirlite.build_load(builder, load_ty, alloca, vol_l)
|
||||
bo_l = self.Gen.VarGen.var_byte_order(ve_l)
|
||||
local_val_l: mlirlite.Value* = mlirlite.build_load(builder, load_ty, alloca, vol_l)
|
||||
# BigEndian local: the slot is byte-swapped, so un-swap the load (pairs the store in AssignGenMixin).
|
||||
return self.apply_byte_order_bswap(pool, builder, local_val_l, bo_l)
|
||||
|
||||
# REPL session variables: a name missing from the local table is read back from its ctx slot,
|
||||
# checked after locals and before module aliases; non-REPL behavior is unchanged.
|
||||
@@ -463,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)."""
|
||||
@@ -561,7 +571,13 @@ novirtual class ExprGenName(ExprGenBase.ExprGenBase):
|
||||
case mlirlite.LLVMType.Array(elem_ty, count):
|
||||
# Array traversal: getelementptr [N x elem_ty], ... , i32 0, i32 %idx
|
||||
elem_ptr: mlirlite.Value* = mlirlite.build_gep_array(builder, pointee, elem_ty, alloca, idx_val)
|
||||
if elem_ptr is not None: return mlirlite.build_load(builder, elem_ty, elem_ptr)
|
||||
if elem_ptr is not None:
|
||||
arr_val_rd: mlirlite.Value* = mlirlite.build_load(builder, elem_ty, elem_ptr)
|
||||
# BigEndian array variable: the element slot is byte-swapped; un-swap
|
||||
# on load (pairs the element store in AssignGenMixin). Non-Name bases are
|
||||
# not covered (matches TPC, which also keys off the base Name).
|
||||
bo_arr_rd: int = self.Gen.VarGen.var_byte_order(self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm.id))
|
||||
return self.apply_byte_order_bswap(pool, builder, arr_val_rd, bo_arr_rd)
|
||||
return None
|
||||
case mlirlite.LLVMType.Struct(_fs_v, _fc_v, lsn_v):
|
||||
# list[T] value variable (an unannotated assignment rhs returns a list
|
||||
@@ -575,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
|
||||
@@ -859,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.
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import lib.core.Gen.GenTypes as GenTypes
|
||||
import t, c
|
||||
from stdint import *
|
||||
import ast
|
||||
import mlirlite
|
||||
@@ -84,13 +83,22 @@ novirtual class ExprGenOps(ExprGenBase.ExprGenBase):
|
||||
mlirlite.position_at_end(builder, else_blk_for_phi)
|
||||
orelse_val = self.coerce_to_type(builder, orelse_val, body_val.Ty, node)
|
||||
elif body_bits > orelse_bits and orelse_bits > 0:
|
||||
# Coerce orelse_val in the else block
|
||||
mlirlite.position_at_end(builder, else_blk_for_phi)
|
||||
orelse_val = self.coerce_to_type(builder, orelse_val, body_val.Ty, node)
|
||||
# 宽侧是「能无损落进窄侧」的编译期常量时,把**常量**降到窄侧(与二元运算同一口径),
|
||||
# 避免 `tab[..] if c else '='`(i8 元素 vs i32 字符字面量)被抬成 i32、再赋回 i8 时误报窄化。
|
||||
if self._const_fits_int_type(body_val, orelse_val.Ty) != 0:
|
||||
mlirlite.position_at_end(builder, then_blk_for_phi)
|
||||
body_val = self.coerce_to_type(builder, body_val, orelse_val.Ty, node)
|
||||
else:
|
||||
mlirlite.position_at_end(builder, else_blk_for_phi)
|
||||
orelse_val = self.coerce_to_type(builder, orelse_val, body_val.Ty, node)
|
||||
elif orelse_bits > body_bits and body_bits > 0:
|
||||
# Back in the then block, coerce body_val
|
||||
mlirlite.position_at_end(builder, then_blk_for_phi)
|
||||
body_val = self.coerce_to_type(builder, body_val, orelse_val.Ty, node)
|
||||
# 同口径:宽侧(orelse)是能落进窄侧的常量时,把它降下来(在 else 块内完成强转)
|
||||
if self._const_fits_int_type(orelse_val, body_val.Ty) != 0:
|
||||
mlirlite.position_at_end(builder, else_blk_for_phi)
|
||||
orelse_val = self.coerce_to_type(builder, orelse_val, body_val.Ty, node)
|
||||
else:
|
||||
mlirlite.position_at_end(builder, then_blk_for_phi)
|
||||
body_val = self.coerce_to_type(builder, body_val, orelse_val.Ty, node)
|
||||
elif body_bits == orelse_bits and body_bits > 0:
|
||||
# Same bits but different types (e.g. i64 vs Ptr): null cannot be used in an i64 PHI.
|
||||
# This branch should not trigger (case 0 covers it); kept as a defensive guard.
|
||||
@@ -126,6 +134,15 @@ novirtual class ExprGenOps(ExprGenBase.ExprGenBase):
|
||||
if narrow_val is None or narrow_val.Ty is None: return 0
|
||||
narrow_bits: int = self.get_llvm_type_bits(narrow_val.Ty)
|
||||
if narrow_bits == 1: return 1
|
||||
# 编译期 CDefine 常量(裸名 `N` 或模块属性 `mod.N`)单独判定,必须**早于**下方「i8 默认 zext」:
|
||||
# · 负值 → sext(有符号语义,如 `define Z_DEFAULT_COMPRESSION = (-1)` 必须变 i32 -1,而非 255);
|
||||
# · 非负 → zext(无符号位模式,如 `define WINDOW_SIZE = (1 << 15)` 必须变 32768,而非 -32768)。
|
||||
# 否则这两类常量拓宽时必有一类被弄错(i8 位型走「默认 zext」;i16 位型若判不出无符号则走 sext)。
|
||||
if narrow_node is not None:
|
||||
cd_v_zw: int64 = self._cdefine_int_of(narrow_node)
|
||||
if self.Gen.TypeGenCDef.is_cdefine_found() != 0:
|
||||
if cd_v_zw < 0: return 0
|
||||
return 1
|
||||
if narrow_bits == 8:
|
||||
# i8 defaults to unsigned-byte handling (systems convention), but an explicitly signed source annotation
|
||||
# must sext to preserve sign: `int32(v)` with v: int8 = -50 must stay -50, not become 206.
|
||||
@@ -141,14 +158,42 @@ novirtual class ExprGenOps(ExprGenBase.ExprGenBase):
|
||||
# Name/Subscript, unsignedness comes from trans.SymTab; without trans it returns 0 and sext is wrongly used.
|
||||
if narrow_node is not None:
|
||||
if self._node_type_unsigned(pool, narrow_node, trans) != 0: return 1
|
||||
# A CDefine constant with the high bit set (0x80000000..0xFFFFFFFF) -> zext. CDefine is a compile-time value,
|
||||
# so _node_type_unsigned finds no annotation; the negative-signed range means an unsigned sentinel (e.g. 0xFFFFFFFF).
|
||||
if narrow_node is not None and narrow_node.kind() == ast.ASTKind.Name:
|
||||
cdef_nm_zw: ast.Name* = ast.Name*(narrow_node)
|
||||
if cdef_nm_zw.id is not None:
|
||||
cdv_zw: int64 = self.Gen.TypeGenCDef.lookup_cdefine_constant(cdef_nm_zw.id)
|
||||
if self.Gen.TypeGenCDef.is_cdefine_found() != 0:
|
||||
if cdv_zw > 0x7FFFFFFF: return 1
|
||||
# A non-negative CDefine whose minimal width's high bit is set (0x80000000 in 32 bits, or
|
||||
# 50000 in 16 bits) is materialized as an unsigned bit pattern -> zext. Covered by
|
||||
# _node_type_unsigned below (which delegates to _cdefine_unsigned_bitpattern); keep return 0.
|
||||
return 0
|
||||
# _cdefine_int_of - 取节点引用的 CDefine 常量值。支持两种引用形态:
|
||||
# · 裸名 `N`
|
||||
# · 模块属性 `mod.N`(跨模块经 .vpi 注册;常量名就是 at.attr)
|
||||
# 未命中时 `Gen.TypeGenCDef.is_cdefine_found()` 为 0 —— 本函数每次都刷新该标志(空名也走一次 lookup)。
|
||||
def _cdefine_int_of(self, node: ast.AST*) -> int64:
|
||||
"""CDefine integer value referenced by node (bare name or `mod.N`); check is_cdefine_found()."""
|
||||
nm_cd: str = ""
|
||||
if node is not None:
|
||||
if node.kind() == ast.ASTKind.Name:
|
||||
n_cd: ast.Name* = ast.Name*(node)
|
||||
if n_cd.id is not None: nm_cd = n_cd.id
|
||||
elif node.kind() == ast.ASTKind.Attribute:
|
||||
a_cd: ast.Attribute* = ast.Attribute*(node)
|
||||
if a_cd.attr is not None: nm_cd = a_cd.attr
|
||||
return self.Gen.TypeGenCDef.lookup_cdefine_constant(nm_cd)
|
||||
# _cdefine_unsigned_bitpattern - 1 if node 是**非负 CDefine 常量**,且按它的最小可容纳位宽 W_min
|
||||
# 取无符号位模式时 high bit 已置位(如 50000 → W_min=16 → 0xC350)。规范:非负数按无符号位模式
|
||||
# 物化、负值按有符号解释(Debugger/define-constants.md §7.2)。这类常量在**宽化**与**比较**
|
||||
# (for-range 边界)时必须按无符号处理,否则 32768..65535 的常量会被读成负数(range 变 0 次)。
|
||||
def _cdefine_unsigned_bitpattern(self, node: ast.AST*) -> int:
|
||||
"""1 = node is a non-negative CDefine whose minimal width's high bit is set; else 0."""
|
||||
v_cd: int64 = self._cdefine_int_of(node)
|
||||
if self.Gen.TypeGenCDef.is_cdefine_found() == 0: return 0
|
||||
if v_cd < 0: return 0
|
||||
w_cd: int = self.Gen.AssignGen.define_min_width(v_cd)
|
||||
# 该位宽的有符号上限 2^(w-1)-1;超过即 high bit 置位(只写安全字面量,避免 1<<63 / 4294967295 之类)
|
||||
if w_cd == 8:
|
||||
if v_cd > 127: return 1
|
||||
elif w_cd == 16:
|
||||
if v_cd > 32767: return 1
|
||||
elif w_cd == 32:
|
||||
if v_cd > 2147483647: return 1
|
||||
return 0
|
||||
def _node_type_unsigned(self, pool: memhub.MemBuddy*, node: ast.AST*, trans: HT.Translator* = None) -> int:
|
||||
"""Return 1 if the AST node resolves to an unsigned integer type, else 0."""
|
||||
@@ -162,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)
|
||||
@@ -177,6 +233,9 @@ novirtual class ExprGenOps(ExprGenBase.ExprGenBase):
|
||||
vcn_u: str = self.Gen.VarGen.var_annot_class_name(ve)
|
||||
if vcn_u is not None:
|
||||
if self._type_name_unsigned(vcn_u) != 0: return 1
|
||||
# 3) 非负 CDefine 常量:按最小可容纳位宽取无符号位模式(Debugger/define-constants.md §7.2)。
|
||||
# 例:define N = 50000 → W_min=16、值在 32768..65535 ⇒ 宽化/比较必须按无符号。
|
||||
if self._cdefine_unsigned_bitpattern(node) != 0: return 1
|
||||
return 0
|
||||
if k == ast.ASTKind.BinOp:
|
||||
bop: ast.BinOp* = ast.BinOp*(node)
|
||||
@@ -202,15 +261,13 @@ novirtual class ExprGenOps(ExprGenBase.ExprGenBase):
|
||||
if self._annot_str_unsigned(scn_u) != 0: return 1
|
||||
return 0
|
||||
if k == ast.ASTKind.Attribute:
|
||||
# Struct field (e.g. viperio.Buf's self.capacity/length: sizet): get the class name from the receiver's AnnotClassName, then look up the field annotation to decide unsignedness. Read-only; returns 0 on failure, so find_struct + manual field walk is used instead of lookup_field_by_class.
|
||||
# Struct field (e.g. viperio.Buf's self.capacity/length: sizet): decide unsignedness from the
|
||||
# *field's* annotation, resolved through the receiver's struct class name. The receiver is not
|
||||
# limited to a plain variable name: it may be a nested field (a.b.f), an index/deref
|
||||
# (arr[i].f / ptr[i].f) or a module-qualified global (pkg.mod.g[i].f). Read-only; 0 = unresolved.
|
||||
at_u: ast.Attribute* = ast.Attribute*(node)
|
||||
if at_u.value is not None and at_u.attr is not None and trans is not None:
|
||||
cls_nm_u: str = None
|
||||
if at_u.value.kind() == ast.ASTKind.Name:
|
||||
vn_u: ast.Name* = ast.Name*(at_u.value)
|
||||
if vn_u.id is not None:
|
||||
ve_u: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, vn_u.id)
|
||||
if ve_u is not None: cls_nm_u = self.Gen.VarGen.var_annot_class_name(ve_u)
|
||||
cls_nm_u: str = self._attr_receiver_class_name(pool, at_u.value, trans)
|
||||
if cls_nm_u is not None:
|
||||
# Take the bare class name (before the dot): look up by bare name only, avoiding module-qualified resolution
|
||||
bare_u: str = cls_nm_u
|
||||
@@ -230,6 +287,72 @@ novirtual class ExprGenOps(ExprGenBase.ExprGenBase):
|
||||
break
|
||||
return 0
|
||||
return 0
|
||||
# _attr_receiver_class_name - struct class name of an attribute receiver expression. Resolves by the
|
||||
# receiver's *type*, not by shape: variable/field annotation class name (Name / nested Attribute), the
|
||||
# same resolution one array/pointer level down for an index/deref base (arr[i] / ptr[i]), or the global's
|
||||
# element type for a module-qualified global (Mod.g / pkg.mod.g). None when unresolved.
|
||||
def _attr_receiver_class_name(self, pool: memhub.MemBuddy*, node: ast.AST*, trans: HT.Translator*) -> str:
|
||||
"""Struct class name of an attribute receiver (Name / Attribute / Subscript); None if unresolved."""
|
||||
if node is None or trans is None: return None
|
||||
k: int = node.kind()
|
||||
if k == ast.ASTKind.Subscript:
|
||||
# arr[i].f / ptr[i].f: the element sits one array/pointer level below the base, and the base's
|
||||
# class-name resolution already denotes that element struct (the levels are transparent here),
|
||||
# so resolve the subscript base directly.
|
||||
sub_rc: ast.Subscript* = ast.Subscript*(node)
|
||||
if sub_rc.value is None: return None
|
||||
return self._attr_receiver_class_name(pool, sub_rc.value, trans)
|
||||
if k == ast.ASTKind.Attribute:
|
||||
# Module-qualified global (Mod.g / pkg.mod.g): the class name comes from the global's element type.
|
||||
gc_rc: str = self._module_global_element_class(pool, trans, ast.Attribute*(node))
|
||||
if gc_rc is not None: return gc_rc
|
||||
# Object field chain (a.b.f): the field's annotation class name (recursive).
|
||||
return self.Gen.ExprGenAttr._resolve_attr_field_class(pool, trans, node, 1)
|
||||
if k == ast.ASTKind.Name:
|
||||
nm_rc: ast.Name* = ast.Name*(node)
|
||||
if nm_rc.id is None: return None
|
||||
ve_rc: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm_rc.id)
|
||||
if ve_rc is None: return None
|
||||
return self.Gen.VarGen.var_annot_class_name(ve_rc)
|
||||
return None
|
||||
# _module_global_element_class - element class name of a module-qualified global `Mod.g`. The module path
|
||||
# may be multi-component (e.g. `intr.idt.idt`: package intr -> submodule idt -> global idt). Mirrors
|
||||
# ExprGenAttr._resolve_module_global_class_name but accepts a dotted module path and also unwraps an array
|
||||
# element, so a module-global array such as `idt: [256]idt_entry` resolves to 'idt_entry'. None otherwise.
|
||||
def _module_global_element_class(self, pool: memhub.MemBuddy*, trans: HT.Translator*, at: ast.Attribute*) -> str:
|
||||
"""Element class name of the module-qualified global `at.value.at.attr`; None when not a global."""
|
||||
if at is None or at.value is None or at.attr is None or trans is None: return None
|
||||
if trans._imported_modules is None or trans.Module is None: return None
|
||||
mod_path: str = self.Gen.ExprGenAttr._attr_module_path_str(pool, at.value)
|
||||
if mod_path is None: return None
|
||||
# restore import aliases (import X.Y as Z) so the import/SHA1 checks resolve
|
||||
if trans._from_imports is not None:
|
||||
orig_mg: str = self.Gen.ImportsGen.lookup_from_import(trans._from_imports, mod_path, 0)
|
||||
if orig_mg is not None:
|
||||
oml_mg: sizet = 0
|
||||
while orig_mg[oml_mg] != ' ' and orig_mg[oml_mg] != '\0' and orig_mg[oml_mg] != ':': oml_mg += 1
|
||||
if oml_mg > 0:
|
||||
om_buf_mg: str = pool.alloc(oml_mg + 1)
|
||||
if om_buf_mg is not None:
|
||||
string.strncpy(om_buf_mg, orig_mg, oml_mg)
|
||||
om_buf_mg[oml_mg] = '\0'
|
||||
mod_path = om_buf_mg
|
||||
# package submodules (`intr.idt`) count as known when the package is imported
|
||||
if self.Gen.ImportsGen.is_module_imported_or_known(trans._imported_modules, mod_path) == 0: return None
|
||||
gv: mlirlite.GlobalVariable* = self.Gen.ExprGenAttr.find_global_in_module(trans.Module, self.Gen.FuncGenBase._mangle_name_with_sha1(self.Gen.ExprGenAttr._lookup_module_sha1(mod_path), at.attr))
|
||||
if gv is None: gv = self.Gen.ExprGenAttr.find_global_in_module(trans.Module, at.attr)
|
||||
if gv is None or gv.Ty is None: return None
|
||||
# a global is usually `Element*` / `Element**` or an array `[N]Element`: descend to the leaf type
|
||||
# and take its registered struct class name.
|
||||
elem_ty: mlirlite.LLVMType* = gv.Ty
|
||||
lvl_mg: int = 0
|
||||
while elem_ty is not None and lvl_mg < 4:
|
||||
match elem_ty:
|
||||
case mlirlite.LLVMType.Ptr(inner_mg): elem_ty = inner_mg
|
||||
case mlirlite.LLVMType.Array(elem_mg, _): elem_ty = elem_mg
|
||||
case _: return self.Gen.StructGen.get_class_name_by_type(pool, elem_ty)
|
||||
lvl_mg += 1
|
||||
return None
|
||||
def _node_type_signed(self, pool: memhub.MemBuddy*, node: ast.AST*, trans: HT.Translator* = None) -> int:
|
||||
"""Return 1 if the source node is explicitly a signed integer, else 0."""
|
||||
if node is None: return 0
|
||||
@@ -541,7 +664,7 @@ novirtual class ExprGenOps(ExprGenBase.ExprGenBase):
|
||||
return mlirlite.build_icmp(builder, predicate, lhs, rhs)
|
||||
# translate_unaryop - UnaryOp(op, operand)
|
||||
# ============================================================
|
||||
def translate_unaryop(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, node: ast.AST*, trans: HT.Translator* = None) -> mlirlite.Value*:
|
||||
def translate_unaryop(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, node: ast.AST*, trans: HT.Translator* = None, target_ty: mlirlite.LLVMType* = None) -> mlirlite.Value*:
|
||||
"""Translate unary operations (-x, +x, not x, ~x)."""
|
||||
uo: ast.UnaryOp* = ast.UnaryOp*(node)
|
||||
if uo is None: return None
|
||||
@@ -554,29 +677,44 @@ novirtual class ExprGenOps(ExprGenBase.ExprGenBase):
|
||||
ovl_u: mlirlite.Value* = self.try_unary_operator_overload(pool, builder, mod, operand, uo.op, trans)
|
||||
if ovl_u is not None: return ovl_u
|
||||
|
||||
# C 语义整数提升(与算术/比较同一口径):一元 +/-/~ 先把窄于 32 位的整数操作数提升到 i32,
|
||||
# 这样 `0 - x` / `x ^ -1` 的常量与操作数同宽(否则是 i32 常量对 i8/i16 操作数)。
|
||||
# `not x` 不需要:结果为 i1,操作数宽度不影响语义。
|
||||
# 一元 +/-/~ 的整数提升按**符号性**分档(见 wiki/Implicit.md):
|
||||
# · **u 类(无符号)不提升**:保持原位宽(`~uint16` 仍是 uint16、`-uint8` 在 u8 内取反),
|
||||
# 位图/掩码写法因此能「同宽运算」;
|
||||
# · **i 类(有符号)提升到 i32**:防负数陷阱(如 int8 -128 取负会溢出,提升后 = 128)。
|
||||
# 未标注符号时按 i 类处理(保守,守住负数陷阱)。`not x` 不需要:结果为 i1,与位宽无关。
|
||||
if uo.op == ast.OpKind.USub or uo.op == ast.OpKind.UAdd or uo.op == ast.OpKind.Invert:
|
||||
u_bits: int = self.get_llvm_type_bits(operand.Ty)
|
||||
if u_bits != 0 and u_bits < 32:
|
||||
u_i32_ty: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
||||
u_probe: mlirlite.Value* = mlirlite.const_int32(pool, 0)
|
||||
if self.should_zext_widen(pool, operand, u_probe, uo.operand, trans) != 0: operand = mlirlite.build_zext(builder, operand, u_i32_ty)
|
||||
else: operand = mlirlite.build_sext(builder, operand, u_i32_ty)
|
||||
u_is_uns: int = self._node_type_unsigned(pool, uo.operand, trans)
|
||||
if u_is_uns == 0:
|
||||
u_i32_ty: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
||||
u_probe: mlirlite.Value* = mlirlite.const_int32(pool, 0)
|
||||
if self.should_zext_widen(pool, operand, u_probe, uo.operand, trans) != 0: operand = mlirlite.build_zext(builder, operand, u_i32_ty)
|
||||
else: operand = mlirlite.build_sext(builder, operand, u_i32_ty)
|
||||
|
||||
if uo.op == ast.OpKind.USub:
|
||||
# -x
|
||||
# Float type uses fneg (emit fneg directly to avoid 0.0 - x type mismatch)
|
||||
operand_fbits: int = self.get_llvm_float_bits(operand.Ty)
|
||||
if operand_fbits > 0: return mlirlite.build_fneg(builder, operand)
|
||||
# Integer: -x = 0 - x, choosing the zero constant by bit width (avoid i32/i64 mismatch)
|
||||
if operand_fbits > 0:
|
||||
# 编译期常量折叠:-c 直接产出常量并保持 IsConst
|
||||
if operand.IsConst == 1:
|
||||
# 目标类型是 32 位浮点(`float(-3.5)` / `x: float = -3.5`)→ 直接出 f32 常量,
|
||||
# 不再"f64 常量 + fptrunc"(与 ExprGenConst.translate_constant 的浮点字面量同口径)。
|
||||
# 位宽安全:ConstFloat 的十六进制位数只由 MlirTypeText(ty) 决定(与 MLIR 渲染器
|
||||
# 取类型文本同一个函数),故不会再出现"f32 类型配 16 位文本"那类位宽错配。
|
||||
if target_ty is not None and self.get_llvm_float_bits(target_ty) == 32:
|
||||
return mlirlite.ConstFloat(pool, target_ty, -operand.FloatVal)
|
||||
return mlirlite.ConstFloat(pool, operand.Ty, -operand.FloatVal)
|
||||
return mlirlite.build_fneg(builder, operand)
|
||||
# 编译期常量折叠:-c 直接产出常量(IsConst=1)。否则 `v: int8 = -50` 会被当成运行时 i32 值,
|
||||
# 触发“隐式窄化禁止”,尽管其数值完全落在 i8 内(隐式窄化的“编译期常量”豁免失效)。
|
||||
operand_bits: int = self.get_llvm_type_bits(operand.Ty)
|
||||
if operand_bits != 0 and operand.IsConst == 1:
|
||||
return mlirlite.const_int(pool, operand_bits, -operand.IntVal)
|
||||
# Integer: -x = 0 - x;0 常量必须与**当前**操作数同宽(u 类不提升后可能是 i8/i16/i32/i64)
|
||||
zero: mlirlite.Value* = mlirlite.const_int32(pool, 0)
|
||||
if operand_bits == 64: zero = mlirlite.const_int64(pool, 0)
|
||||
elif operand_bits > 0 and operand_bits != 32:
|
||||
# Other widths (i8/i16 etc.): use i32 zero; operand will be coerced
|
||||
zero = mlirlite.const_int32(pool, 0)
|
||||
if operand_bits != 0 and operand_bits != 32: zero = mlirlite.const_int(pool, operand_bits, 0)
|
||||
return mlirlite.build_sub(builder, zero, operand)
|
||||
elif uo.op == ast.OpKind.UAdd:
|
||||
# +x = x
|
||||
@@ -590,8 +728,12 @@ novirtual class ExprGenOps(ExprGenBase.ExprGenBase):
|
||||
elif uo.op == ast.OpKind.Invert:
|
||||
# ~x = x ^ -1
|
||||
operand_bits_inv: int = self.get_llvm_type_bits(operand.Ty)
|
||||
# 编译期常量折叠:~c 直接产出常量并保持 IsConst(与 -c 相同的窄化豁免口径)
|
||||
if operand_bits_inv != 0 and operand.IsConst == 1:
|
||||
return mlirlite.const_int(pool, operand_bits_inv, operand.IntVal ^ -1)
|
||||
# -1 掩码必须与**当前**操作数同宽(u 类不提升后可能是 i8/i16/i32/i64)
|
||||
neg1: mlirlite.Value* = mlirlite.const_int32(pool, -1)
|
||||
if operand_bits_inv == 64: neg1 = mlirlite.const_int64(pool, -1)
|
||||
if operand_bits_inv != 0 and operand_bits_inv != 32: neg1 = mlirlite.const_int(pool, operand_bits_inv, -1)
|
||||
return mlirlite.build_xor(builder, operand, neg1)
|
||||
elif uo.op == ast.OpKind.Deref:
|
||||
# *p -> load by the pointee type (aligned with translate_c_deref)
|
||||
@@ -639,6 +781,13 @@ novirtual class ExprGenOps(ExprGenBase.ExprGenBase):
|
||||
case mlirlite.LLVMType.Struct(_, _, _):
|
||||
obj_ty_ao: mlirlite.LLVMType* = mlirlite.Ptr(pool, ao2)
|
||||
return mlirlite.build_load(builder, obj_ty_ao, v_ao)
|
||||
case mlirlite.LLVMType.Func(_, _, _):
|
||||
# 函数指针变量(嵌套函数名在符号表里是 Ptr(Func)):变量本身就是
|
||||
# 那个函数地址,`&f` 应给出它的值 Ptr(Func)(一级),而不是变量
|
||||
# 地址 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)
|
||||
case _:
|
||||
pass
|
||||
case _:
|
||||
@@ -696,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
|
||||
@@ -1098,18 +1247,51 @@ novirtual class ExprGenOps(ExprGenBase.ExprGenBase):
|
||||
if node is not None: col_s = node.col_offset + 1
|
||||
self.Gen.TypeGenErr.fatal_error(node, f"[VPC] illegal double-pointer arithmetic type: op = '{op_lbl}'{aug_note}, " f"left operand type = {lty_lbl}, right operand type = {rty_lbl}. " f"in this language str is i8*, with no `+` concatenation semantics (`str + str` and pointer +/- pointer " f"both degrade to raw pointer add/sub => illegal IR: opt reports " f"invalid operand type for instruction). " f"silent degradation is forbidden, compilation aborted. " f"fix: build strings with f-string interpolation, string.strcat, or viperlib.snprintf.", "TypeError", col_s, col_s)
|
||||
return 1
|
||||
# binop_needs_int_promotion - whether this op takes C-style integer promotion (to >= 32 bits)
|
||||
def binop_needs_int_promotion(self, op: int) -> int:
|
||||
"""C 语义整数提升的适用范围:**算术与移位**为 1(窄于 32 位先提升再算,避免 8/16 位回绕),
|
||||
**位运算(& | ^)为 0**——按既有约定"两侧同宽就不提升"(WORD | WORD 保持 u16)。"""
|
||||
if op == ast.OpKind.Add or op == ast.OpKind.Sub or op == ast.OpKind.Mult: return 1
|
||||
if op == ast.OpKind.Div or op == ast.OpKind.FloorDiv or op == ast.OpKind.Mod: return 1
|
||||
if op == ast.OpKind.LShift or op == ast.OpKind.RShift: return 1
|
||||
return 0
|
||||
# _binop_work_bits - 二元运算的「工作位宽」(**不做 C 式整数提升**)
|
||||
# 本语言取消了 C 的「小整型先提升到 int(i32)」语义:i8/i16 不再预先抬到 32 位再算。
|
||||
# · 有上下文目标位宽(赋值/形参/返回目标)→ 以它为基础;
|
||||
# · 无目标 → 取两侧操作数位宽的较大者,等宽化逻辑随后把**较小**的一侧拓宽到此位宽(结果位宽 = 较大者);
|
||||
# · 移位 → **计数(rhs)不代表结果位宽**,不参与比较;结果位宽 = 左操作数位宽(无目标时)。
|
||||
# 任一侧位宽为 0(未知/未定)会天然退化为另一侧(0 不抬高 max);只有连目标与两侧位宽**全都缺失**
|
||||
# (类型信息完全缺失)时才回退到 32 —— 这只是可编译性兜底,不代表"提升"。
|
||||
# 注意:本 helper 的 max 不含「常量豁免」——「能无损落进另一侧位宽的常量不抬高工作位宽」这条规则
|
||||
# 由等宽化处的 _const_operand_demotes 实施(见下方),因为本语言非移位运算的实际结果位宽就是
|
||||
# 「两侧中较宽的一侧」,等宽化处才是决定点;移位本就不让计数参与,故与此无关。
|
||||
def _binop_work_bits(self, op: int, tgt_bits: int, lhs_bits: int, rhs_bits: int) -> int:
|
||||
"""Working bit width for translate_binop (no C-style integer promotion)."""
|
||||
w: int = tgt_bits
|
||||
if lhs_bits > w: w = lhs_bits
|
||||
if op != ast.OpKind.LShift and op != ast.OpKind.RShift:
|
||||
if rhs_bits > w: w = rhs_bits
|
||||
if w == 0: w = 32
|
||||
return w
|
||||
|
||||
# _const_operand_demotes - 位宽上更宽的一侧是编译期常量时,它能否**降为另一侧位宽**,
|
||||
# 从而不抬高运算的工作位宽(常量适配的唯一收口处,位运算与算术共用)。
|
||||
# 用户规则:常量应隐式适配另一侧位宽——`baseport(u16) + 7` 的 `7` 默认是 i32,但应适配 u16,
|
||||
# 使整个加法保持在 u16(结果位宽 16),而不是把 u16 变量抬到 i32 后在赋值/传参处报隐式窄化。
|
||||
# 判定:
|
||||
# 1) 常量值必须能**无损**落进另一侧位宽(复用 _const_fits_int_type);否则常量照常计入 max
|
||||
# (如 `u8 变量 + 0x1234`:0x1234 落不进 u8,仍把变量拓宽到 i32,该报窄化就照旧报)。
|
||||
# 2) 位运算 & | ^:无进位、结果不会超出参与值位宽,常量总能无损降——既有行为,保持不变。
|
||||
# 3) 算术 + - * / %:另一侧也是常量时**不降**——`(MEMBUDDY_MAX_ORDERS + 1) * 8` 这类窄常量
|
||||
# 乘积若逐个降宽会在 i8 回绕(老 bug:`i8(32)+1 → 33`、`×8` 溢出成 8,见 includes/memhub.vp),
|
||||
# 必须保持「不降宽」的既有行为。
|
||||
# 4) 算术:常量位宽不高于目标位宽时也不降——此时 max(目标位宽, 另一侧位宽) 已不小于常量位宽,
|
||||
# 常量并没有抬高工作位宽(见 _binop_work_bits),无需也不应改变结果。
|
||||
def _const_operand_demotes(self, is_bitwise: int, const_val: mlirlite.Value*, const_bits: int, other_val: mlirlite.Value*, other_ty: mlirlite.LLVMType*, tgt_bits: int) -> int:
|
||||
"""1 = the wider-side constant may be demoted to the other side's type (no work-width raise)."""
|
||||
if const_val is None or other_val is None: return 0
|
||||
if const_val.IsConst != 1: return 0
|
||||
if self._const_fits_int_type(const_val, other_ty) == 0: return 0
|
||||
if is_bitwise != 0: return 1
|
||||
if other_val.IsConst == 1: return 0
|
||||
if const_bits <= tgt_bits: return 0
|
||||
return 1
|
||||
|
||||
# translate_binop - binary operations with automatic type promotion
|
||||
# ============================================================
|
||||
def translate_binop(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, node: ast.AST*, trans: HT.Translator* = None) -> mlirlite.Value*:
|
||||
def translate_binop(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, node: ast.AST*, trans: HT.Translator* = None, target_ty: mlirlite.LLVMType* = None) -> mlirlite.Value*:
|
||||
"""Translate a binary operation with automatic type promotion."""
|
||||
binop: ast.BinOp* = ast.BinOp*(node)
|
||||
if binop is None: return None
|
||||
@@ -1118,8 +1300,9 @@ novirtual class ExprGenOps(ExprGenBase.ExprGenBase):
|
||||
rhs_node: ast.AST* = binop.right
|
||||
op: int = binop.op
|
||||
|
||||
# Translate rhs first (only once, to avoid repeating side effects)
|
||||
rhs: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, rhs_node, None, 0, trans)
|
||||
# Translate rhs first (only once, to avoid repeating side effects).
|
||||
# target_ty 下传:内层运算(含移位)据此采用「目标感知工作位宽」。
|
||||
rhs: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, rhs_node, None, 0, trans, target_ty)
|
||||
if rhs is None: return None
|
||||
|
||||
# === Operator overload path 1: lhs is a Name bound to a struct variable ===
|
||||
@@ -1139,7 +1322,7 @@ novirtual class ExprGenOps(ExprGenBase.ExprGenBase):
|
||||
if ovl_result is not None: return ovl_result
|
||||
|
||||
# Translate lhs normally
|
||||
lhs: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, lhs_node, None, 0, trans)
|
||||
lhs: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, lhs_node, None, 0, trans, target_ty)
|
||||
if lhs is None: return None
|
||||
|
||||
# === Operator overload path 2: lhs is Ptr(Struct) (e.g. after loading a Counter* variable) ===
|
||||
@@ -1166,6 +1349,59 @@ novirtual class ExprGenOps(ExprGenBase.ExprGenBase):
|
||||
lhs_fbits: int = self.get_llvm_float_bits(lhs.Ty)
|
||||
rhs_fbits: int = self.get_llvm_float_bits(rhs.Ty)
|
||||
|
||||
# 纯常量整型算式(两侧都是编译期整型常量):按**精确值**折叠并按值定宽,
|
||||
# 禁止按操作数的最小宽度回绕(现场:`1024 * 1024 * 4` 按 i16 算成 0 ⇒
|
||||
# WINBUF_MAX_BYTES=0 ⇒ 窗口缓冲只剩几字节 ⇒ 内核 memcpy 240KB 越界读/三重故障)。
|
||||
if lhs_bits > 0 and rhs_bits > 0 and lhs.IsConst == 1 and rhs.IsConst == 1:
|
||||
cf_ok: int = 1
|
||||
cf_v: int64 = 0
|
||||
if op == ast.OpKind.Add: cf_v = lhs.IntVal + rhs.IntVal
|
||||
elif op == ast.OpKind.Sub: cf_v = lhs.IntVal - rhs.IntVal
|
||||
elif op == ast.OpKind.Mult: cf_v = lhs.IntVal * rhs.IntVal
|
||||
elif op == ast.OpKind.BitOr: cf_v = lhs.IntVal | rhs.IntVal
|
||||
elif op == ast.OpKind.BitAnd: cf_v = lhs.IntVal & rhs.IntVal
|
||||
elif op == ast.OpKind.BitXor: cf_v = lhs.IntVal ^ rhs.IntVal
|
||||
elif op == ast.OpKind.Div or op == ast.OpKind.FloorDiv or op == ast.OpKind.Mod:
|
||||
# 除法/整除/取模也做静态折叠(与 + - * 完全同口径:算精确值,再由下面按值自动提升位宽)。
|
||||
# 语义**与运行期一致**:Div/FloorDiv → sdiv(向零截断),Mod → srem(余数符号随被除数)
|
||||
# —— 宿主侧的 `//` / `%` 正是这两个语义(本语言把 FloorDiv 也映射成 sdiv,
|
||||
# 见本文件运行期分支;AssignGenMixin 的常量求值器同款)。无符号位型常量按规范
|
||||
# 恒为非负,负值常量必属有符号操作数集 ⇒ 与运行期 udiv/urem 的取值一致。
|
||||
# 除零:绝不交给宿主(Python 会直接把编译器打挂)——先关掉折叠再 fail-fast
|
||||
# (诊断文本与 AssignGenMixin 的常量求值器一致)。
|
||||
if rhs.IntVal == 0:
|
||||
cf_ok = 0
|
||||
self.Gen.TypeGenErr.fatal_error(node, "constant expression division by zero: the right operand of '/', '//' or '%' evaluates to 0 at compile time.", "ValueError")
|
||||
elif op == ast.OpKind.Mod: cf_v = lhs.IntVal % rhs.IntVal
|
||||
else: cf_v = lhs.IntVal // rhs.IntVal
|
||||
elif op == ast.OpKind.LShift or op == ast.OpKind.RShift:
|
||||
if rhs.IntVal >= 0 and rhs.IntVal < 64:
|
||||
if op == ast.OpKind.LShift: cf_v = lhs.IntVal << rhs.IntVal
|
||||
else: cf_v = lhs.IntVal >> rhs.IntVal
|
||||
else: cf_ok = 0
|
||||
else: cf_ok = 0
|
||||
if cf_ok != 0:
|
||||
# 定宽:下限取操作数较宽者,只有精确值装不下才加宽(避免把 `A + 1` 这类
|
||||
# 常量算式整体抬成 i64,引出新的“隐式窄化”报错)。
|
||||
cf_w: int = lhs_bits if lhs_bits > rhs_bits else rhs_bits
|
||||
if cf_v != 0:
|
||||
t_cf: int64 = cf_v
|
||||
if cf_v < 0: t_cf = cf_v ^ int64(-1)
|
||||
vw_cf: int = 1 # 符号位
|
||||
# 固定 64 次上界(i64 最多 64 位):不用 `while t != 0` —— 某些代码生成下
|
||||
# i64 的移位/比较不收敛,会死循环(现场:主编译器卡死在 ExprGenOps 死循环里)。
|
||||
bi_cf: int = 0
|
||||
while bi_cf < 64 and t_cf > 0:
|
||||
vw_cf += 1
|
||||
t_cf = t_cf >> 1
|
||||
bi_cf += 1
|
||||
if vw_cf > cf_w: cf_w = vw_cf
|
||||
if cf_w <= 8: cf_w = 8
|
||||
elif cf_w <= 16: cf_w = 16
|
||||
elif cf_w <= 32: cf_w = 32
|
||||
else: cf_w = 64
|
||||
return mlirlite.const_int(pool, cf_w, cf_v)
|
||||
|
||||
# Float operation: use float instructions when either operand is float (must precede pointer arithmetic, since float has 0 int bits and would be mistaken for a pointer).
|
||||
if lhs_fbits != 0 or rhs_fbits != 0:
|
||||
# Determine the target float type (use the larger bit width)
|
||||
@@ -1202,29 +1438,50 @@ novirtual class ExprGenOps(ExprGenBase.ExprGenBase):
|
||||
if binop_unsigned == 0 and rhs_node is not None:
|
||||
if self._node_type_unsigned(pool, rhs_node, trans) != 0: binop_unsigned = 1
|
||||
|
||||
# C 语义整数提升(仅算术/移位;位运算 & | ^ 保持"同宽不提升"):
|
||||
# 两侧都窄于 32 位时先提升到 32 位再算,避免 8/16 位回绕(如两个 16 位 define 的
|
||||
# `MAX_INCLUDES * MAX_REL_PATH_LEN` 会算成 0)。提升后把 lhs_bits/rhs_bits 记为 32,
|
||||
# 让下面的等宽化逻辑不再重复处理(否则会对同宽 i32 再做一次无意义转换)。
|
||||
if self.binop_needs_int_promotion(op) != 0:
|
||||
if lhs_bits != 0 and rhs_bits != 0 and lhs_bits < 32 and rhs_bits < 32:
|
||||
i32_ty_pr: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
||||
# 加宽方向与下面的等宽化同一判据(i1/i8 默认 zext、带符号注解 sext、无符号 typedef zext);
|
||||
# probe 只是给 should_zext_widen 提供"宽侧类型",其常量值不参与判据。
|
||||
i32_probe: mlirlite.Value* = mlirlite.const_int32(pool, 0)
|
||||
if self.should_zext_widen(pool, lhs, i32_probe, lhs_node, trans) != 0: lhs = mlirlite.build_zext(builder, lhs, i32_ty_pr)
|
||||
else: lhs = self.coerce_to_type(builder, lhs, i32_ty_pr, node, trans)
|
||||
if self.should_zext_widen(pool, rhs, i32_probe, rhs_node, trans) != 0: rhs = mlirlite.build_zext(builder, rhs, i32_ty_pr)
|
||||
else: rhs = self.coerce_to_type(builder, rhs, i32_ty_pr, node, trans)
|
||||
lhs_bits = 32
|
||||
rhs_bits = 32
|
||||
# 本语言**不做 C 式整数提升**:小整型(i8/i16 等)不再像 C 那样先抬到 int(i32) 再运算。
|
||||
# · 两侧位宽相同 → 直接按该位宽运算(`uint16 + uint16` 结果仍是 i16);
|
||||
# · 两侧位宽不同 → 由下方「等宽化」把**较小**的一侧拓宽到较大的一侧(结果位宽 = 较大者)。
|
||||
# 既有能力保留:**上下文目标位宽感知**(有明确赋值目标时工作位宽取其位宽)与
|
||||
# **位运算常量无损适配**(& | ^ 时能无损落进另一侧位宽的常量去适配另一侧,见下)。
|
||||
|
||||
if lhs_bits > rhs_bits:
|
||||
if self.should_zext_widen(pool, rhs, lhs, rhs_node, trans) != 0: rhs = mlirlite.build_zext(builder, rhs, lhs.Ty)
|
||||
else: rhs = self.coerce_to_type(builder, rhs, lhs.Ty, node, trans)
|
||||
elif rhs_bits > lhs_bits:
|
||||
if self.should_zext_widen(pool, lhs, rhs, lhs_node, trans) != 0: lhs = mlirlite.build_zext(builder, lhs, rhs.Ty)
|
||||
else: lhs = self.coerce_to_type(builder, lhs, rhs.Ty, node, trans)
|
||||
# 运算的「工作位宽」:优先取上下文(赋值/形参/返回目标)位宽;无上下文时取两侧操作数的较大位宽
|
||||
# (移位只取左操作数位宽,计数不参与)。于是 `s = A ^ B`(s: ushort)直接算到 16 位,不会先被
|
||||
# 抬成 32 位、再在赋值处撞「隐式窄化」;而 `m: uint = (uchar(x) << 16) | …` 目标为 uint,按 32 位算。
|
||||
tgt_bits: int = 0
|
||||
if target_ty is not None and self.is_ptr_type(target_ty) == 0: tgt_bits = self.get_llvm_type_bits(target_ty)
|
||||
work_bits: int = self._binop_work_bits(op, tgt_bits, lhs_bits, rhs_bits)
|
||||
|
||||
# 移位:结果位宽取**左操作数**位宽(计数宽度不代表结果类型,不参与)。有目标位宽时把左操作数
|
||||
# 拓宽到目标位宽,使 `m: uint = uchar(x) << 16` 这类写法移出的位不丢(与目标感知工作位宽一致);
|
||||
# 无目标时 work_bits = 左操作数位宽,故不再有「先抬到 i32」的旧行为。
|
||||
if op == ast.OpKind.LShift or op == ast.OpKind.RShift:
|
||||
if lhs_bits != 0 and lhs_bits < work_bits:
|
||||
work_ty_sh: mlirlite.LLVMType* = self._int_type_of_bits(pool, work_bits)
|
||||
sh_probe: mlirlite.Value* = mlirlite.const_int32(pool, 0)
|
||||
if self.should_zext_widen(pool, lhs, sh_probe, lhs_node, trans) != 0: lhs = mlirlite.build_zext(builder, lhs, work_ty_sh)
|
||||
else: lhs = self.coerce_to_type(builder, lhs, work_ty_sh, node, trans)
|
||||
lhs_bits = work_bits
|
||||
# 计数一律适配到左操作数类型:常量按「无损」适配;运行时更宽的值按 C 语义截到该宽度
|
||||
# (计数不是"值",不受隐式窄化规则约束)。
|
||||
if lhs_bits != 0 and rhs_bits != 0 and rhs.Ty != lhs.Ty:
|
||||
if rhs_bits < lhs_bits or self._const_fits_int_type(rhs, lhs.Ty) != 0: rhs = self.coerce_to_type(builder, rhs, lhs.Ty, node, trans)
|
||||
else: rhs = mlirlite.build_trunc(builder, rhs, lhs.Ty)
|
||||
rhs_bits = lhs_bits
|
||||
else:
|
||||
# 「常量适配」:一侧是能无损落进另一侧位宽的编译期常量时,把**常量**降到另一侧位宽,使其
|
||||
# 不抬高工作位宽(用户规则:常量应隐式适配另一侧,`baseport(u16) + 7` 结果仍是 u16)。
|
||||
# 也覆盖既有的位运算(& | ^)常量无损适配,二者共用 _const_operand_demotes 一处判定;
|
||||
# 算术的两处例外(另一侧是常量 / 常量未超过目标位宽)见该 helper 的注释。
|
||||
is_bitwise_op: int = 0
|
||||
if op == ast.OpKind.BitAnd or op == ast.OpKind.BitOr or op == ast.OpKind.BitXor: is_bitwise_op = 1
|
||||
if lhs_bits > rhs_bits:
|
||||
if self._const_operand_demotes(is_bitwise_op, lhs, lhs_bits, rhs, rhs.Ty, tgt_bits) != 0: lhs = self.coerce_to_type(builder, lhs, rhs.Ty, node, trans)
|
||||
elif self.should_zext_widen(pool, rhs, lhs, rhs_node, trans) != 0: rhs = mlirlite.build_zext(builder, rhs, lhs.Ty)
|
||||
else: rhs = self.coerce_to_type(builder, rhs, lhs.Ty, node, trans)
|
||||
elif rhs_bits > lhs_bits:
|
||||
if self._const_operand_demotes(is_bitwise_op, rhs, rhs_bits, lhs, lhs.Ty, tgt_bits) != 0: rhs = self.coerce_to_type(builder, rhs, lhs.Ty, node, trans)
|
||||
elif self.should_zext_widen(pool, lhs, rhs, lhs_node, trans) != 0: lhs = mlirlite.build_zext(builder, lhs, rhs.Ty)
|
||||
else: lhs = self.coerce_to_type(builder, lhs, rhs.Ty, node, trans)
|
||||
|
||||
# Bitwise type safety: when both sides are pointers (lhs_bits==0 and rhs_bits==0), bitwise ops are illegal (LLVM
|
||||
# requires integer operands). Common trigger: a failed CDefine lookup returns pointer null, then `|` with a pointer.
|
||||
@@ -1233,8 +1490,9 @@ novirtual class ExprGenOps(ExprGenBase.ExprGenBase):
|
||||
# fail-fast: illegal bitwise operand types; abort compilation (expose the upstream root cause)
|
||||
self.Gen.TypeGenErr.fatal_error(node, "illegal bitwise operand types: both lhs/rhs are non-integer types (pointer or struct), cannot perform bitwise operations", "TypeError")
|
||||
|
||||
# 移位:计数静态可确定时做范围检查(与 AugAssign 的 <<= / >>= 共用);
|
||||
# 计数宽度本身豁免,按提升/等宽化后的左操作数有效位宽比较(C 语义下 u8 << 8 合法)。
|
||||
# 移位:计数静态可确定时做范围检查(与 AugAssign 的 <<= / >>= 共用);计数宽度本身豁免,
|
||||
# 按等宽化后的左操作数有效位宽比较。注意:不再做整数提升后,`uchar(x) << 8` 的左操作数是 i8,
|
||||
# 计数 8 >= 8 会被如实拦下(这是新语义下的真实越界,而非旧 C 语义"先提升到 i32"后的合法移位)。
|
||||
if op == ast.OpKind.LShift or op == ast.OpKind.RShift:
|
||||
self.Gen.AssignGen.require_shift_count_in_range(pool, trans, node, rhs_node, self.get_llvm_type_bits(lhs.Ty))
|
||||
|
||||
|
||||
@@ -1,292 +1,293 @@
|
||||
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.core.Gen.GenApi as GenApi
|
||||
import lib.core.Gen.Call.CallGenBase as CallGenBase
|
||||
|
||||
# FuncGenBase - split from FuncGenMixin (stage D): attrs / naming / mangle / create_function helpers.
|
||||
novirtual class FuncGenBase(CallGenBase.CallGenBase):
|
||||
"""Shared helpers for function attributes, naming, mangling and creation."""
|
||||
|
||||
# extract_func_attrs - extract c.Attribute attrs from decorator_list.
|
||||
# Supports @c.Attribute(t.attr.xxx()/t.attr.xxx/t.attr.llvm.xxx); returns LLVM IR attr string or None.
|
||||
def extract_func_attrs(self, pool: memhub.MemBuddy*, decorator_list: list[ast.AST*]*, imported_modules: str) -> str:
|
||||
"""Extract c.Attribute attrs from decorators; return the LLVM IR attr string."""
|
||||
if decorator_list is None: return None
|
||||
dn: sizet = len(decorator_list)
|
||||
if dn == 0: return None
|
||||
|
||||
attrs_buf: str = pool.alloc(256)
|
||||
if attrs_buf is None: return None
|
||||
attrs_buf[0] = '\0'
|
||||
found_any: int = 0
|
||||
|
||||
for di in range(dn):
|
||||
deco: ast.AST* = decorator_list.get(di)
|
||||
if deco is None: continue
|
||||
dk: int = deco.kind()
|
||||
# Direct form `@t.attr.<name>` (same as ClassGenBase._has_decorator): `inline def f`
|
||||
# synthesizes it and does NOT require `import c`; unsupported names are ignored.
|
||||
if dk == ast.ASTKind.Attribute:
|
||||
dan: str = self._get_attr_name_from_node(deco)
|
||||
if dan is not None and string.strcmp(dan, "section") != 0:
|
||||
if self._append_llvm_attr(attrs_buf, dan) != 0: found_any = 1
|
||||
continue
|
||||
if dk != ast.ASTKind.Call: continue
|
||||
call_node: ast.Call* = ast.Call*(deco)
|
||||
if call_node.func is None: continue
|
||||
cfk: int = call_node.func.kind()
|
||||
if cfk != ast.ASTKind.Attribute: continue
|
||||
|
||||
# Detect c.Attribute
|
||||
func_attr: ast.Attribute* = ast.Attribute*(call_node.func)
|
||||
if func_attr.attr is None: continue
|
||||
if string.strcmp(func_attr.attr, "Attribute") != 0: continue
|
||||
if func_attr.value is None: continue
|
||||
fvk: int = func_attr.value.kind()
|
||||
if fvk != ast.ASTKind.Name: continue
|
||||
c_name: ast.Name* = ast.Name*(func_attr.value)
|
||||
if c_name.id is None: continue
|
||||
if string.strcmp(c_name.id, "c") != 0: continue
|
||||
imp_c: int = self.Gen.ImportsGen.is_module_imported(imported_modules, "c")
|
||||
if imp_c == 0: continue
|
||||
|
||||
# Iterate args and extract attrs
|
||||
if call_node.args is None: continue
|
||||
args_list: list[ast.AST*]* = call_node.args
|
||||
an: sizet = len(args_list)
|
||||
for ai in range(an):
|
||||
arg_node: ast.AST* = args_list.get(ai)
|
||||
if arg_node is None: continue
|
||||
# Extract attr name (t.attr.xxx / t.attr.xxx() / t.attr.llvm.xxx)
|
||||
attr_name: str = self._get_attr_name_from_node(arg_node)
|
||||
if attr_name is None: continue
|
||||
# section string attr cannot be inlined on the signature line (llc: expected
|
||||
# string constant); it must go through attr group #N, handled by FuncGenDef.
|
||||
if string.strcmp(attr_name, "section") == 0: continue
|
||||
# Map to LLVM attr name and append
|
||||
ar: int = self._append_llvm_attr(attrs_buf, attr_name)
|
||||
if ar != 0: found_any = 1
|
||||
|
||||
if found_any == 0: return None
|
||||
return attrs_buf
|
||||
def extract_func_section(self, pool: memhub.MemBuddy*, decorator_list: list[ast.AST*]*, imported_modules: str) -> str:
|
||||
"""Extract @c.Attribute(t.attr.section(...)) as the attr-group text."""
|
||||
if pool is None or decorator_list is None: return None
|
||||
dn: sizet = len(decorator_list)
|
||||
if dn == 0: return None
|
||||
for di in range(dn):
|
||||
deco: ast.AST* = decorator_list.get(di)
|
||||
if deco is None or deco.kind() != ast.ASTKind.Call: continue
|
||||
call_node: ast.Call* = ast.Call*(deco)
|
||||
if call_node.func is None or call_node.func.kind() != ast.ASTKind.Attribute: continue
|
||||
func_attr: ast.Attribute* = ast.Attribute*(call_node.func)
|
||||
if func_attr.attr is None or string.strcmp(func_attr.attr, "Attribute") != 0: continue
|
||||
if call_node.args is None: continue
|
||||
args_list: list[ast.AST*]* = call_node.args
|
||||
an: sizet = len(args_list)
|
||||
for ai in range(an):
|
||||
arg_node: ast.AST* = args_list.get(ai)
|
||||
if arg_node is None: continue
|
||||
attr_name: str = self._get_attr_name_from_node(arg_node)
|
||||
if attr_name is None or string.strcmp(attr_name, "section") != 0: continue
|
||||
sec_val: str = self._get_attr_call_arg_str(arg_node)
|
||||
if sec_val is None: continue
|
||||
sec_buf: str = pool.alloc(128)
|
||||
if sec_buf is None: return None
|
||||
viperlib.snprintf(sec_buf, 128, "\"section\"=\"%s\"", sec_val)
|
||||
return sec_buf
|
||||
return None
|
||||
def _get_attr_name_from_node(self, node: ast.AST*) -> str:
|
||||
"""Extract the attribute name from a Call or Attribute AST node."""
|
||||
if node is None: return None
|
||||
k: int = node.kind()
|
||||
|
||||
# Call node: t.attr.always_inline()
|
||||
if k == ast.ASTKind.Call:
|
||||
call: ast.Call* = ast.Call*(node)
|
||||
if call.func is None or call.func.kind() != ast.ASTKind.Attribute: return None
|
||||
at: ast.Attribute* = ast.Attribute*(call.func)
|
||||
return at.attr
|
||||
|
||||
# Attribute node: t.attr.packed or t.attr.llvm.nounwind
|
||||
if k == ast.ASTKind.Attribute:
|
||||
at2: ast.Attribute* = ast.Attribute*(node)
|
||||
return at2.attr
|
||||
|
||||
return None
|
||||
def _get_attr_call_arg_str(self, node: ast.AST*) -> str:
|
||||
"""Return the first positional string constant of a t.attr.xxx(...) call."""
|
||||
if node is None or node.kind() != ast.ASTKind.Call: return None
|
||||
cl: ast.Call* = ast.Call*(node)
|
||||
if cl.args is None or len(cl.args) == 0: return None
|
||||
a0: ast.AST* = cl.args.get(0)
|
||||
if a0 is None or a0.kind() != ast.ASTKind.Constant: return None
|
||||
cn: ast.Constant* = ast.Constant*(a0)
|
||||
if cn.const_kind != ast.CONST_STR: return None
|
||||
return cn.str_val
|
||||
def _append_llvm_attr(self, attrs_buf: str, attr_name: str) -> int:
|
||||
"""Map an attribute name to its LLVM name and append it; 1=appended, 0=unsupported."""
|
||||
if attrs_buf is None or attr_name is None: return 0
|
||||
|
||||
# First determine the mapped LLVM attr name
|
||||
llvm_name: str = None
|
||||
if string.strcmp(attr_name, "always_inline") == 0: llvm_name = "alwaysinline"
|
||||
elif string.strcmp(attr_name, "noinline") == 0: llvm_name = "noinline"
|
||||
elif string.strcmp(attr_name, "noreturn") == 0: llvm_name = "noreturn"
|
||||
elif string.strcmp(attr_name, "pure") == 0: llvm_name = "readonly"
|
||||
elif string.strcmp(attr_name, "const") == 0: llvm_name = "readnone"
|
||||
elif string.strcmp(attr_name, "nounwind") == 0: llvm_name = "nounwind"
|
||||
elif string.strcmp(attr_name, "noredzone") == 0: llvm_name = "noredzone"
|
||||
elif string.strcmp(attr_name, "stackrealign") == 0: llvm_name = '"stackrealign"'
|
||||
elif string.strcmp(attr_name, "willreturn") == 0: llvm_name = "willreturn"
|
||||
elif string.strcmp(attr_name, "mustprogress") == 0: llvm_name = "mustprogress"
|
||||
else:
|
||||
# packed/aligned/section/visibility/weak are unsupported as inline function attrs
|
||||
return 0
|
||||
|
||||
# Supported attr: append a space separator if the buffer is non-empty
|
||||
if attrs_buf[0] != '\0': self._append_str(attrs_buf, " ", 256)
|
||||
self._append_str(attrs_buf, llvm_name, 256)
|
||||
return 1
|
||||
def _append_str(self, buf: str, s: str, max_len: sizet = 256) -> None:
|
||||
"""Append a string to buf with a bounded write to avoid overflow."""
|
||||
if buf is None or s is None: return
|
||||
# Defensive: TPV default-arg registration is buggy (missing max_len falls to 0);
|
||||
# fall back to 256 so the bounded-write check does not drop all attr text.
|
||||
if max_len == 0: max_len = 256
|
||||
cur_len: sizet = string.strlen(buf)
|
||||
s_len: sizet = string.strlen(s)
|
||||
if cur_len + s_len + 1 > max_len: return # No remaining capacity: drop this append to avoid a bounds write (uncertain)
|
||||
string.strcpy(buf + cur_len, s)
|
||||
# _mangle_name - prepend a SHA1 prefix to a name.
|
||||
# Returns "sha1.name"; if ModuleSha1 is None or the name already has a SHA1 prefix, returns as-is.
|
||||
def _mangle_name(self, trans: HT.Translator*, name: str) -> str:
|
||||
"""Prepend the SHA1 prefix to a name (no main/export check)."""
|
||||
if trans is None or name is None: return name
|
||||
if trans.ModuleSha1 is None: return name
|
||||
name_len: sizet = string.strlen(name)
|
||||
# Skip if it already has a SHA1 prefix ("{16hex}."). [C10] Use the single source of
|
||||
# truth GenApi.sha1_prefix_len (aligned with the ">= 17" check).
|
||||
if GenApi.sha1_prefix_len(name) != 0: return name
|
||||
sha1: str = trans.ModuleSha1
|
||||
sha1_len: sizet = string.strlen(sha1)
|
||||
mangled: str = stdlib.malloc(sha1_len + name_len + 2)
|
||||
if mangled is None: return name
|
||||
string.strcpy(mangled, sha1)
|
||||
mangled[sha1_len] = '.'
|
||||
string.strcpy(mangled + sha1_len + 1, name)
|
||||
return mangled
|
||||
# _mangle_name_with_sha1 - prepend the given SHA1 to a name.
|
||||
# Used for cross-module vtable inheritance: inherited methods mangle with the parent module's SHA1.
|
||||
def _mangle_name_with_sha1(self, sha1: str, name: str) -> str:
|
||||
"""Prepend the given SHA1 to a name (no main/export check)."""
|
||||
if sha1 is None or name is None: return name
|
||||
name_len: sizet = string.strlen(name)
|
||||
# Skip if it already has a SHA1 prefix ("{16hex}."). [C10] Use the single source of
|
||||
# truth GenApi.sha1_prefix_len (same check as _mangle_name).
|
||||
if GenApi.sha1_prefix_len(name) != 0: return name
|
||||
sha1_len: sizet = string.strlen(sha1)
|
||||
mangled: str = stdlib.malloc(sha1_len + name_len + 2)
|
||||
if mangled is None: return name
|
||||
string.strcpy(mangled, sha1)
|
||||
mangled[sha1_len] = '.'
|
||||
string.strcpy(mangled + sha1_len + 1, name)
|
||||
return mangled
|
||||
# _mangle_func_name - prepend a SHA1 prefix to a function name (with main/export checks).
|
||||
# No prefix for main, when has_export != 0 (t.CExport), when already prefixed, or when ModuleSha1 is None.
|
||||
def _mangle_func_name(self, trans: HT.Translator*, name: str, has_export: int) -> str:
|
||||
"""Prepend the SHA1 prefix to a function name (checks main/export)."""
|
||||
if trans is None or name is None: return name
|
||||
if has_export != 0: return name
|
||||
if string.strcmp(name, "main") == 0: return name
|
||||
return self._mangle_name(trans, name)
|
||||
# _body_has_raise - whether the body contains raise (non-StopIteration).
|
||||
# Matches TPC HandlesFunctions.py _body_contains_raise; v1 only scans top-level raise, not nested ones.
|
||||
def _body_has_raise(self, fd: ast.FunctionDef*) -> int:
|
||||
"""Whether the body has a top-level non-StopIteration raise; 1=yes, 0=no."""
|
||||
if fd is None or fd.children is None: return 0
|
||||
body_f: list[ast.AST*]* = fd.children
|
||||
bn_f: sizet = len(body_f)
|
||||
for bi_f in range(bn_f):
|
||||
stmt_f: ast.AST* = body_f.get(bi_f)
|
||||
if stmt_f is None: continue
|
||||
if stmt_f.kind() != ast.ASTKind.Raise: continue
|
||||
rn_f: ast.Raise* = ast.Raise*(stmt_f)
|
||||
if rn_f is None or rn_f.exc is None: continue
|
||||
ek_f: int = rn_f.exc.kind()
|
||||
is_stop_f: int = 0
|
||||
if ek_f == ast.ASTKind.Name:
|
||||
enm_f: ast.Name* = ast.Name*(rn_f.exc)
|
||||
if enm_f.id is not None and string.strcmp(enm_f.id, "StopIteration") == 0: is_stop_f = 1
|
||||
elif ek_f == ast.ASTKind.Call:
|
||||
ecl_f: ast.Call* = ast.Call*(rn_f.exc)
|
||||
if ecl_f.func is not None and ecl_f.func.kind() == ast.ASTKind.Name:
|
||||
ecn_f: ast.Name* = ast.Name*(ecl_f.func)
|
||||
if ecn_f.id is not None and string.strcmp(ecn_f.id, "StopIteration") == 0: is_stop_f = 1
|
||||
if is_stop_f == 0: return 1
|
||||
return 0
|
||||
# RejectCReturnDecorator - @c.CReturn was removed; fail at compile time if seen.
|
||||
# Multi-return goes only through `-> tuple[...]`; any CReturn form is fail-fast, shared with FuncGenDef.
|
||||
def RejectCReturnDecorator(self, node: ast.AST*, decorators: list[ast.AST*]*) -> int:
|
||||
"""Fail if @c.CReturn appears in the decorators; 0=none found."""
|
||||
if decorators is None: return 0
|
||||
rcr_dn: sizet = len(decorators)
|
||||
for rcr_i in range(rcr_dn):
|
||||
rcr_deco: ast.AST* = decorators.get(rcr_i)
|
||||
if rcr_deco is None: continue
|
||||
# Handle both @c.CReturn(...) (Call(func=Attribute)) and bare @c.CReturn (Attribute)
|
||||
rcr_attr: ast.Attribute* = None
|
||||
if rcr_deco.kind() == ast.ASTKind.Call:
|
||||
rcr_call: ast.Call* = ast.Call*(rcr_deco)
|
||||
if rcr_call.func is not None and rcr_call.func.kind() == ast.ASTKind.Attribute: rcr_attr = ast.Attribute*(rcr_call.func)
|
||||
elif rcr_deco.kind() == ast.ASTKind.Attribute: rcr_attr = ast.Attribute*(rcr_deco)
|
||||
if rcr_attr is None or rcr_attr.attr is None: continue
|
||||
if string.strcmp(rcr_attr.attr, "CReturn") == 0:
|
||||
self.Gen.TypeGenErr.fatal_error(node, "`@c.CReturn` removed: use a return annotation `-> tuple[ty1, ty2, ...]` instead (multiple return values are packed into a struct return)", "TypeError")
|
||||
return 1
|
||||
return 0
|
||||
# Create LLVM function (simple version, declaration only)
|
||||
def create_function(self, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, name: str, args_node: ast.AST*, ret_ty: mlirlite.LLVMType*) -> mlirlite.Function*:
|
||||
"""Create an LLVM function and add its parameters."""
|
||||
if name is None or mod is None: return None
|
||||
|
||||
func: mlirlite.Function* = mlirlite.create_function(pool, mod, name, ret_ty)
|
||||
if func is None: return None
|
||||
|
||||
if args_node is not None:
|
||||
ags: ast.Arguments* = ast.Arguments*(args_node)
|
||||
if ags.args is not None:
|
||||
alist: list[ast.AST*]* = ags.args
|
||||
an: sizet = len(alist)
|
||||
i32_ty: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
||||
for ai in range(an):
|
||||
arg: ast.Arg* = ast.Arg*(alist.get(ai))
|
||||
if arg is not None and arg.arg is not None:
|
||||
# t.CVoid means no args: skip
|
||||
if arg.annotation is not None:
|
||||
if arg.annotation.kind() == ast.ASTKind.Attribute:
|
||||
at_cf: ast.Attribute* = ast.Attribute*(arg.annotation)
|
||||
if at_cf.attr is not None and string.strcmp(at_cf.attr, "CVoid") == 0: continue
|
||||
param_ty: mlirlite.LLVMType* = i32_ty
|
||||
if arg.annotation is not None:
|
||||
resolved: mlirlite.LLVMType* = self.Gen.TypeGenResolve.resolve_annotation_type(pool, arg.annotation, None, None, None, 0)
|
||||
if resolved is None:
|
||||
# [S4 fail-fast] Annotation present but unresolvable: no silent i32 fallback.
|
||||
# This fn lacks trans/imports context, so failure surfaces upstream.
|
||||
self.Gen.TypeGenErr.fatal_error(arg, f"function '{name}' param '{arg.arg}' type annotation failed to resolve", "NameError")
|
||||
return None
|
||||
param_ty = resolved
|
||||
pname: str = pool.alloc(32)
|
||||
if pname is not None: viperlib.snprintf(pname, 32, "%%%s", arg.arg)
|
||||
mlirlite.add_param(pool, func, param_ty, pname)
|
||||
|
||||
from stdint import *
|
||||
import ast
|
||||
import mlirlite
|
||||
import memhub
|
||||
import string
|
||||
import viperlib
|
||||
import stdlib
|
||||
import lib.core.VLogger as VLogger
|
||||
import lib.core.Gen.Translator as HT
|
||||
import lib.core.Gen.GenApi as GenApi
|
||||
import lib.core.Gen.Call.CallGenBase as CallGenBase
|
||||
|
||||
# FuncGenBase - split from FuncGenMixin (stage D): attrs / naming / mangle / create_function helpers.
|
||||
novirtual class FuncGenBase(CallGenBase.CallGenBase):
|
||||
"""Shared helpers for function attributes, naming, mangling and creation."""
|
||||
|
||||
# extract_func_attrs - extract c.Attribute attrs from decorator_list.
|
||||
# Supports @c.Attribute(t.attr.xxx()/t.attr.xxx/t.attr.llvm.xxx); returns LLVM IR attr string or None.
|
||||
def extract_func_attrs(self, pool: memhub.MemBuddy*, decorator_list: list[ast.AST*]*, imported_modules: str) -> str:
|
||||
"""Extract c.Attribute attrs from decorators; return the LLVM IR attr string."""
|
||||
if decorator_list is None: return None
|
||||
dn: sizet = len(decorator_list)
|
||||
if dn == 0: return None
|
||||
|
||||
attrs_buf: str = pool.alloc(256)
|
||||
if attrs_buf is None: return None
|
||||
attrs_buf[0] = '\0'
|
||||
found_any: int = 0
|
||||
|
||||
for di in range(int(dn)):
|
||||
deco: ast.AST* = decorator_list.get(di)
|
||||
if deco is None: continue
|
||||
dk: int = deco.kind()
|
||||
# Direct form `@t.attr.<name>` (same as ClassGenBase._has_decorator): `inline def f`
|
||||
# synthesizes it and does NOT require `import c`; unsupported names are ignored.
|
||||
if dk == ast.ASTKind.Attribute:
|
||||
dan: str = self._get_attr_name_from_node(deco)
|
||||
if dan is not None and string.strcmp(dan, "section") != 0:
|
||||
if self._append_llvm_attr(attrs_buf, dan) != 0: found_any = 1
|
||||
continue
|
||||
if dk != ast.ASTKind.Call: continue
|
||||
call_node: ast.Call* = ast.Call*(deco)
|
||||
if call_node.func is None: continue
|
||||
cfk: int = call_node.func.kind()
|
||||
if cfk != ast.ASTKind.Attribute: continue
|
||||
|
||||
# Detect c.Attribute
|
||||
func_attr: ast.Attribute* = ast.Attribute*(call_node.func)
|
||||
if func_attr.attr is None: continue
|
||||
if string.strcmp(func_attr.attr, "Attribute") != 0: continue
|
||||
if func_attr.value is None: continue
|
||||
fvk: int = func_attr.value.kind()
|
||||
if fvk != ast.ASTKind.Name: continue
|
||||
c_name: ast.Name* = ast.Name*(func_attr.value)
|
||||
if c_name.id is None: continue
|
||||
if string.strcmp(c_name.id, "c") != 0: continue
|
||||
# 不要求 `import c`:`@__attribute__(...)` 会被 ast/parser.vp(_DesugarMagicAttribute)
|
||||
# 脱糖成 `@c.Attribute(...)`,这里的 `c.` 前缀是编译器自己产生的(用户写的是
|
||||
# `@__attribute__`,并非 `@c.Attribute`)。若在此要求 `import c`,用户明明没引入 c
|
||||
# 就会把整条装饰器静默丢弃(`stackrealign`/`always_inline` 等函数属性全失效)。
|
||||
# extract_func_section 本就没有这个守卫,二者保持一致。
|
||||
|
||||
# Iterate args and extract attrs
|
||||
if call_node.args is None: continue
|
||||
args_list: list[ast.AST*]* = call_node.args
|
||||
an: sizet = len(args_list)
|
||||
for ai in range(int(an)):
|
||||
arg_node: ast.AST* = args_list.get(ai)
|
||||
if arg_node is None: continue
|
||||
# Extract attr name (t.attr.xxx / t.attr.xxx() / t.attr.llvm.xxx)
|
||||
attr_name: str = self._get_attr_name_from_node(arg_node)
|
||||
if attr_name is None: continue
|
||||
# section string attr cannot be inlined on the signature line (llc: expected
|
||||
# string constant); it must go through attr group #N, handled by FuncGenDef.
|
||||
if string.strcmp(attr_name, "section") == 0: continue
|
||||
# Map to LLVM attr name and append
|
||||
ar: int = self._append_llvm_attr(attrs_buf, attr_name)
|
||||
if ar != 0: found_any = 1
|
||||
|
||||
if found_any == 0: return None
|
||||
return attrs_buf
|
||||
def extract_func_section(self, pool: memhub.MemBuddy*, decorator_list: list[ast.AST*]*, imported_modules: str) -> str:
|
||||
"""Extract @c.Attribute(t.attr.section(...)) as the attr-group text."""
|
||||
if pool is None or decorator_list is None: return None
|
||||
dn: sizet = len(decorator_list)
|
||||
if dn == 0: return None
|
||||
for di in range(int(dn)):
|
||||
deco: ast.AST* = decorator_list.get(di)
|
||||
if deco is None or deco.kind() != ast.ASTKind.Call: continue
|
||||
call_node: ast.Call* = ast.Call*(deco)
|
||||
if call_node.func is None or call_node.func.kind() != ast.ASTKind.Attribute: continue
|
||||
func_attr: ast.Attribute* = ast.Attribute*(call_node.func)
|
||||
if func_attr.attr is None or string.strcmp(func_attr.attr, "Attribute") != 0: continue
|
||||
if call_node.args is None: continue
|
||||
args_list: list[ast.AST*]* = call_node.args
|
||||
an: sizet = len(args_list)
|
||||
for ai in range(int(an)):
|
||||
arg_node: ast.AST* = args_list.get(ai)
|
||||
if arg_node is None: continue
|
||||
attr_name: str = self._get_attr_name_from_node(arg_node)
|
||||
if attr_name is None or string.strcmp(attr_name, "section") != 0: continue
|
||||
sec_val: str = self._get_attr_call_arg_str(arg_node)
|
||||
if sec_val is None: continue
|
||||
sec_buf: str = pool.alloc(128)
|
||||
if sec_buf is None: return None
|
||||
viperlib.snprintf(sec_buf, 128, "\"section\"=\"%s\"", sec_val)
|
||||
return sec_buf
|
||||
return None
|
||||
def _get_attr_name_from_node(self, node: ast.AST*) -> str:
|
||||
"""Extract the attribute name from a Call or Attribute AST node."""
|
||||
if node is None: return None
|
||||
k: int = node.kind()
|
||||
|
||||
# Call node: t.attr.always_inline()
|
||||
if k == ast.ASTKind.Call:
|
||||
call: ast.Call* = ast.Call*(node)
|
||||
if call.func is None or call.func.kind() != ast.ASTKind.Attribute: return None
|
||||
at: ast.Attribute* = ast.Attribute*(call.func)
|
||||
return at.attr
|
||||
|
||||
# Attribute node: t.attr.packed or t.attr.llvm.nounwind
|
||||
if k == ast.ASTKind.Attribute:
|
||||
at2: ast.Attribute* = ast.Attribute*(node)
|
||||
return at2.attr
|
||||
|
||||
return None
|
||||
def _get_attr_call_arg_str(self, node: ast.AST*) -> str:
|
||||
"""Return the first positional string constant of a t.attr.xxx(...) call."""
|
||||
if node is None or node.kind() != ast.ASTKind.Call: return None
|
||||
cl: ast.Call* = ast.Call*(node)
|
||||
if cl.args is None or len(cl.args) == 0: return None
|
||||
a0: ast.AST* = cl.args.get(0)
|
||||
if a0 is None or a0.kind() != ast.ASTKind.Constant: return None
|
||||
cn: ast.Constant* = ast.Constant*(a0)
|
||||
if cn.const_kind != ast.CONST_STR: return None
|
||||
return cn.str_val
|
||||
def _append_llvm_attr(self, attrs_buf: str, attr_name: str) -> int:
|
||||
"""Map an attribute name to its LLVM name and append it; 1=appended, 0=unsupported."""
|
||||
if attrs_buf is None or attr_name is None: return 0
|
||||
|
||||
# First determine the mapped LLVM attr name
|
||||
llvm_name: str = None
|
||||
if string.strcmp(attr_name, "always_inline") == 0: llvm_name = "alwaysinline"
|
||||
elif string.strcmp(attr_name, "noinline") == 0: llvm_name = "noinline"
|
||||
elif string.strcmp(attr_name, "noreturn") == 0: llvm_name = "noreturn"
|
||||
elif string.strcmp(attr_name, "pure") == 0: llvm_name = "readonly"
|
||||
elif string.strcmp(attr_name, "const") == 0: llvm_name = "readnone"
|
||||
elif string.strcmp(attr_name, "nounwind") == 0: llvm_name = "nounwind"
|
||||
elif string.strcmp(attr_name, "noredzone") == 0: llvm_name = "noredzone"
|
||||
elif string.strcmp(attr_name, "stackrealign") == 0: llvm_name = '"stackrealign"'
|
||||
elif string.strcmp(attr_name, "willreturn") == 0: llvm_name = "willreturn"
|
||||
elif string.strcmp(attr_name, "mustprogress") == 0: llvm_name = "mustprogress"
|
||||
else:
|
||||
# packed/aligned/section/visibility/weak are unsupported as inline function attrs
|
||||
return 0
|
||||
|
||||
# Supported attr: append a space separator if the buffer is non-empty
|
||||
if attrs_buf[0] != '\0': self._append_str(attrs_buf, " ", 256)
|
||||
self._append_str(attrs_buf, llvm_name, 256)
|
||||
return 1
|
||||
def _append_str(self, buf: str, s: str, max_len: sizet = 256) -> None:
|
||||
"""Append a string to buf with a bounded write to avoid overflow."""
|
||||
if buf is None or s is None: return
|
||||
# Defensive: VPC default-arg registration is buggy (missing max_len falls to 0);
|
||||
# fall back to 256 so the bounded-write check does not drop all attr text.
|
||||
if max_len == 0: max_len = 256
|
||||
cur_len: sizet = string.strlen(buf)
|
||||
s_len: sizet = string.strlen(s)
|
||||
if cur_len + s_len + 1 > max_len: return # No remaining capacity: drop this append to avoid a bounds write (uncertain)
|
||||
string.strcpy(buf + cur_len, s)
|
||||
# _mangle_name - prepend a SHA1 prefix to a name.
|
||||
# Returns "sha1.name"; if ModuleSha1 is None or the name already has a SHA1 prefix, returns as-is.
|
||||
def _mangle_name(self, trans: HT.Translator*, name: str) -> str:
|
||||
"""Prepend the SHA1 prefix to a name (no main/export check)."""
|
||||
if trans is None or name is None: return name
|
||||
if trans.ModuleSha1 is None: return name
|
||||
name_len: sizet = string.strlen(name)
|
||||
# Skip if it already has a SHA1 prefix ("{16hex}."). [C10] Use the single source of
|
||||
# truth GenApi.sha1_prefix_len (aligned with the ">= 17" check).
|
||||
if GenApi.sha1_prefix_len(name) != 0: return name
|
||||
sha1: str = trans.ModuleSha1
|
||||
sha1_len: sizet = string.strlen(sha1)
|
||||
mangled: str = stdlib.malloc(sha1_len + name_len + 2)
|
||||
if mangled is None: return name
|
||||
string.strcpy(mangled, sha1)
|
||||
mangled[sha1_len] = '.'
|
||||
string.strcpy(mangled + sha1_len + 1, name)
|
||||
return mangled
|
||||
# _mangle_name_with_sha1 - prepend the given SHA1 to a name.
|
||||
# Used for cross-module vtable inheritance: inherited methods mangle with the parent module's SHA1.
|
||||
def _mangle_name_with_sha1(self, sha1: str, name: str) -> str:
|
||||
"""Prepend the given SHA1 to a name (no main/export check)."""
|
||||
if sha1 is None or name is None: return name
|
||||
name_len: sizet = string.strlen(name)
|
||||
# Skip if it already has a SHA1 prefix ("{16hex}."). [C10] Use the single source of
|
||||
# truth GenApi.sha1_prefix_len (same check as _mangle_name).
|
||||
if GenApi.sha1_prefix_len(name) != 0: return name
|
||||
sha1_len: sizet = string.strlen(sha1)
|
||||
mangled: str = stdlib.malloc(sha1_len + name_len + 2)
|
||||
if mangled is None: return name
|
||||
string.strcpy(mangled, sha1)
|
||||
mangled[sha1_len] = '.'
|
||||
string.strcpy(mangled + sha1_len + 1, name)
|
||||
return mangled
|
||||
# _mangle_func_name - prepend a SHA1 prefix to a function name (with main/export checks).
|
||||
# No prefix for main, when has_export != 0 (t.CExport), when already prefixed, or when ModuleSha1 is None.
|
||||
def _mangle_func_name(self, trans: HT.Translator*, name: str, has_export: int) -> str:
|
||||
"""Prepend the SHA1 prefix to a function name (checks main/export)."""
|
||||
if trans is None or name is None: return name
|
||||
if has_export != 0: return name
|
||||
if string.strcmp(name, "main") == 0: return name
|
||||
return self._mangle_name(trans, name)
|
||||
# _body_has_raise - whether the body contains raise (non-StopIteration).
|
||||
# Matches TPC HandlesFunctions.py _body_contains_raise; v1 only scans top-level raise, not nested ones.
|
||||
def _body_has_raise(self, fd: ast.FunctionDef*) -> int:
|
||||
"""Whether the body has a top-level non-StopIteration raise; 1=yes, 0=no."""
|
||||
if fd is None or fd.children is None: return 0
|
||||
body_f: list[ast.AST*]* = fd.children
|
||||
bn_f: sizet = len(body_f)
|
||||
for bi_f in range(int(bn_f)):
|
||||
stmt_f: ast.AST* = body_f.get(bi_f)
|
||||
if stmt_f is None: continue
|
||||
if stmt_f.kind() != ast.ASTKind.Raise: continue
|
||||
rn_f: ast.Raise* = ast.Raise*(stmt_f)
|
||||
if rn_f is None or rn_f.exc is None: continue
|
||||
ek_f: int = rn_f.exc.kind()
|
||||
is_stop_f: int = 0
|
||||
if ek_f == ast.ASTKind.Name:
|
||||
enm_f: ast.Name* = ast.Name*(rn_f.exc)
|
||||
if enm_f.id is not None and string.strcmp(enm_f.id, "StopIteration") == 0: is_stop_f = 1
|
||||
elif ek_f == ast.ASTKind.Call:
|
||||
ecl_f: ast.Call* = ast.Call*(rn_f.exc)
|
||||
if ecl_f.func is not None and ecl_f.func.kind() == ast.ASTKind.Name:
|
||||
ecn_f: ast.Name* = ast.Name*(ecl_f.func)
|
||||
if ecn_f.id is not None and string.strcmp(ecn_f.id, "StopIteration") == 0: is_stop_f = 1
|
||||
if is_stop_f == 0: return 1
|
||||
return 0
|
||||
# RejectCReturnDecorator - @c.CReturn was removed; fail at compile time if seen.
|
||||
# Multi-return goes only through `-> tuple[...]`; any CReturn form is fail-fast, shared with FuncGenDef.
|
||||
def RejectCReturnDecorator(self, node: ast.AST*, decorators: list[ast.AST*]*) -> int:
|
||||
"""Fail if @c.CReturn appears in the decorators; 0=none found."""
|
||||
if decorators is None: return 0
|
||||
rcr_dn: sizet = len(decorators)
|
||||
for rcr_i in range(int(rcr_dn)):
|
||||
rcr_deco: ast.AST* = decorators.get(rcr_i)
|
||||
if rcr_deco is None: continue
|
||||
# Handle both @c.CReturn(...) (Call(func=Attribute)) and bare @c.CReturn (Attribute)
|
||||
rcr_attr: ast.Attribute* = None
|
||||
if rcr_deco.kind() == ast.ASTKind.Call:
|
||||
rcr_call: ast.Call* = ast.Call*(rcr_deco)
|
||||
if rcr_call.func is not None and rcr_call.func.kind() == ast.ASTKind.Attribute: rcr_attr = ast.Attribute*(rcr_call.func)
|
||||
elif rcr_deco.kind() == ast.ASTKind.Attribute: rcr_attr = ast.Attribute*(rcr_deco)
|
||||
if rcr_attr is None or rcr_attr.attr is None: continue
|
||||
if string.strcmp(rcr_attr.attr, "CReturn") == 0:
|
||||
self.Gen.TypeGenErr.fatal_error(node, "`@c.CReturn` removed: use a return annotation `-> tuple[ty1, ty2, ...]` instead (multiple return values are packed into a struct return)", "TypeError")
|
||||
return 1
|
||||
return 0
|
||||
# Create LLVM function (simple version, declaration only)
|
||||
def create_function(self, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, name: str, args_node: ast.AST*, ret_ty: mlirlite.LLVMType*) -> mlirlite.Function*:
|
||||
"""Create an LLVM function and add its parameters."""
|
||||
if name is None or mod is None: return None
|
||||
|
||||
func: mlirlite.Function* = mlirlite.create_function(pool, mod, name, ret_ty)
|
||||
if func is None: return None
|
||||
|
||||
if args_node is not None:
|
||||
ags: ast.Arguments* = ast.Arguments*(args_node)
|
||||
if ags.args is not None:
|
||||
alist: list[ast.AST*]* = ags.args
|
||||
an: sizet = len(alist)
|
||||
i32_ty: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
||||
for ai in range(int(an)):
|
||||
arg: ast.Arg* = ast.Arg*(alist.get(ai))
|
||||
if arg is not None and arg.arg is not None:
|
||||
# t.CVoid means no args: skip
|
||||
if arg.annotation is not None:
|
||||
if arg.annotation.kind() == ast.ASTKind.Attribute:
|
||||
at_cf: ast.Attribute* = ast.Attribute*(arg.annotation)
|
||||
if at_cf.attr is not None and string.strcmp(at_cf.attr, "CVoid") == 0: continue
|
||||
param_ty: mlirlite.LLVMType* = i32_ty
|
||||
if arg.annotation is not None:
|
||||
resolved: mlirlite.LLVMType* = self.Gen.TypeGenResolve.resolve_annotation_type(pool, arg.annotation, None, None, None, 0)
|
||||
if resolved is None:
|
||||
# [S4 fail-fast] Annotation present but unresolvable: no silent i32 fallback.
|
||||
# This fn lacks trans/imports context, so failure surfaces upstream.
|
||||
self.Gen.TypeGenErr.fatal_error(arg, f"function '{name}' param '{arg.arg}' type annotation failed to resolve", "NameError")
|
||||
return None
|
||||
param_ty = resolved
|
||||
pname: str = pool.alloc(32)
|
||||
if pname is not None: viperlib.snprintf(pname, 32, "%%%s", arg.arg)
|
||||
mlirlite.add_param(pool, func, param_ty, pname)
|
||||
|
||||
return func
|
||||
@@ -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:
|
||||
@@ -462,6 +461,33 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
|
||||
if func is not None and mlirlite.function_is_declared(func) != 0:
|
||||
# Reuse the forward declare: clear IsDeclared and turn it into a define
|
||||
func.IsDeclared = 0
|
||||
# 参数名对齐(根因修复):复用的前置声明未必是 forward_declare_functions 按源码建的,也可能是
|
||||
# **模块前言**里 Translator 建的 C 库声明(如 free 用合成名 "p")。而函数体是用**源码形参名**
|
||||
# %<arg.arg> 生成参数 SSA 引用的(见下方 alloca 段的 SSAValue)。若签名沿用声明里的旧名,就会
|
||||
# 打印出 `llvm.func @f(%p) { ... %ptr ... }`:入口实参是 %p,体内 %ptr 却在 f 内未定义,
|
||||
# mlir-opt 会把 %ptr 解析到后续同名函数的入口实参并报 "region entry argument '%ptr' is already
|
||||
# in use"(现场:VKernel 的 free 头 %p / 体 %ptr,被解析到 _slab_find_page(%ptr))。
|
||||
# 故这里按源码形参名重写复用到的形参名,使签名与函数体一致。forward_declare_functions 建的前置
|
||||
# 声明本就用源码名,此处对其幂等(同名重写)。
|
||||
reused_p: mlirlite.Param* = mlirlite.function_get_param_head(func)
|
||||
if args_node is not None:
|
||||
reuse_ags: ast.Arguments* = ast.Arguments*(args_node)
|
||||
if reuse_ags.args is not None:
|
||||
reuse_list: list[ast.AST*]* = reuse_ags.args
|
||||
reuse_n: sizet = len(reuse_list)
|
||||
for reuse_i in range(int(reuse_n)):
|
||||
rearg: ast.Arg* = ast.Arg*(reuse_list.get(reuse_i))
|
||||
if rearg is None or rearg.arg is None: continue
|
||||
# CVoid means empty param: not part of the signature (mirrors the add_param loops above)
|
||||
if rearg.annotation is not None and self.Gen.TypeGenResolve._is_t_attr(rearg.annotation, "CVoid", imported_modules) != 0: continue
|
||||
if reused_p is None:
|
||||
# 复用声明形参少于定义:签名与函数体必然错配,禁止静默,fail-fast
|
||||
self.Gen.TypeGenErr.fatal_error(rearg, f"reused declaration for '{fd.name}' has fewer parameters than the definition", "TypeError")
|
||||
return 0
|
||||
reuse_pname: str = pool.alloc(32)
|
||||
if reuse_pname is not None: viperlib.snprintf(reuse_pname, 32, "%%%s", rearg.arg)
|
||||
reused_p.Name = reuse_pname
|
||||
reused_p = mlirlite.param_get_next(reused_p)
|
||||
# Set decorator attributes (not set when the forward declare was created)
|
||||
func_attrs_reuse: str = self.Gen.FuncGenBase.extract_func_attrs(pool, fd.decorator_list, imported_modules)
|
||||
if func_attrs_reuse is not None: mlirlite.function_set_attrs(func, func_attrs_reuse)
|
||||
@@ -483,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:
|
||||
@@ -572,9 +598,12 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
|
||||
# @t.TLS one-time-init guard (aligned with TPC HandlesFunctions.py): the first call runs the body and sets
|
||||
# __tls_init_done_{name}=1; later calls return the default. Without it every call re-runs TlsAlloc and loses state.
|
||||
if has_tls != 0:
|
||||
tls_flag_name: str = pool.alloc(128)
|
||||
# Exact capacity: the reference below used to be 64 while the definition was 128, so a
|
||||
# long function name produced a reference to a different (nonexistent) global.
|
||||
tls_flag_cap: sizet = string.strlen(fd.name) + 24
|
||||
tls_flag_name: str = pool.alloc(tls_flag_cap)
|
||||
if tls_flag_name is not None:
|
||||
viperlib.snprintf(tls_flag_name, 128, "__tls_init_done_%s", fd.name)
|
||||
viperlib.snprintf(tls_flag_name, tls_flag_cap, "__tls_init_done_%s", fd.name)
|
||||
tls_flag_gv: mlirlite.GlobalVariable* = mlirlite.create_global_int(pool, mod, tls_flag_name, 1, 0)
|
||||
if tls_flag_gv is not None:
|
||||
i1_ty_t: mlirlite.LLVMType* = mlirlite.Int1(pool)
|
||||
@@ -582,9 +611,10 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
|
||||
# GlobalVariable is not a Value subclass (different field layout), so build_load/store reading
|
||||
# ptr.Ty/ptr.Name as a Value misaligns. As in create_global_string, build a Value view (Ty=Ptr(i1), Name="@name").
|
||||
tls_gv_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, i1_ty_t)
|
||||
tls_ref_name: str = pool.alloc(64)
|
||||
tls_ref_cap: sizet = string.strlen(tls_flag_name) + 2
|
||||
tls_ref_name: str = pool.alloc(tls_ref_cap)
|
||||
if tls_ref_name is not None:
|
||||
viperlib.snprintf(tls_ref_name, 64, "@%s", tls_flag_name)
|
||||
viperlib.snprintf(tls_ref_name, tls_ref_cap, "@%s", tls_flag_name)
|
||||
tls_flag_ref: mlirlite.Value* = mlirlite.SSAValue(pool, tls_gv_ptr_ty, tls_ref_name)
|
||||
if tls_flag_ref is not None:
|
||||
tls_flag_ld: mlirlite.Value* = mlirlite.build_load(func_builder, i1_ty_t, tls_flag_ref)
|
||||
@@ -967,13 +997,17 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
|
||||
|
||||
# Recursion guard flag (outermost): @__decor_rec_{wrapper} = global i8 0
|
||||
rec_flag_ref: mlirlite.Value* = None
|
||||
rec_flag_name: str = pool.alloc(128)
|
||||
# Exact capacities: definition and reference used different fixed sizes (128 vs 96), so a
|
||||
# long wrapper name produced two different globals (guard silently bypassed / undefined).
|
||||
rec_flag_cap: sizet = string.strlen(wrapper_name) + 24
|
||||
rec_flag_name: str = pool.alloc(rec_flag_cap)
|
||||
if rec_flag_name is not None:
|
||||
viperlib.snprintf(rec_flag_name, 128, "__decor_rec_%s", wrapper_name)
|
||||
viperlib.snprintf(rec_flag_name, rec_flag_cap, "__decor_rec_%s", wrapper_name)
|
||||
mlirlite.create_global_int(pool, mod, rec_flag_name, 8, 0)
|
||||
rec_ref_nm: str = pool.alloc(96)
|
||||
rec_ref_cap: sizet = string.strlen(rec_flag_name) + 2
|
||||
rec_ref_nm: str = pool.alloc(rec_ref_cap)
|
||||
if rec_ref_nm is not None:
|
||||
viperlib.snprintf(rec_ref_nm, 96, "@%s", rec_flag_name)
|
||||
viperlib.snprintf(rec_ref_nm, rec_ref_cap, "@%s", rec_flag_name)
|
||||
rec_flag_ref = mlirlite.SSAValue(pool, mlirlite.Ptr(pool, i8_ty), rec_ref_nm)
|
||||
|
||||
wb: mlirlite.IRBuilder* = mlirlite.new_builder(pool, wrapper)
|
||||
|
||||
@@ -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
|
||||
@@ -381,12 +380,15 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
fn_func_ty: mlirlite.LLVMType* = mlirlite.Func(pool, i32_ty, fn_param_node, 1)
|
||||
fn_func_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, fn_func_ty)
|
||||
|
||||
# Create the function reference Value (type i32(i8*)*, using the SHA1 mangled name)
|
||||
fn_ptr_name: str = pool.alloc(64)
|
||||
# Create the function reference Value (type i32(i8*)*, using the SHA1 mangled name).
|
||||
# Exact capacity: the mangled name (SHA1 prefix + closure name) can exceed the old 64-byte
|
||||
# buffer, and a truncated name makes closure slot 0 point at a nonexistent symbol.
|
||||
fn_ptr_cap: sizet = string.strlen(mangled_promoted) + 4
|
||||
fn_ptr_name: str = pool.alloc(fn_ptr_cap)
|
||||
if fn_ptr_name is not None:
|
||||
# SHA1-prefixed names contain '.' so they need quoting (e.g. @"sha1.__closure_func")
|
||||
if string.strchr(mangled_promoted, '.') is not None: viperlib.snprintf(fn_ptr_name, 64, "@\"%s\"", mangled_promoted)
|
||||
else: viperlib.snprintf(fn_ptr_name, 64, "@%s", mangled_promoted)
|
||||
if string.strchr(mangled_promoted, '.') is not None: viperlib.snprintf(fn_ptr_name, fn_ptr_cap, "@\"%s\"", mangled_promoted)
|
||||
else: viperlib.snprintf(fn_ptr_name, fn_ptr_cap, "@%s", mangled_promoted)
|
||||
fn_ptr_val: mlirlite.Value* = mlirlite.SSAValue(pool, fn_func_ptr_ty, fn_ptr_name)
|
||||
|
||||
# bitcast the function pointer to i8* (the closure stores i8*)
|
||||
@@ -775,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)
|
||||
@@ -895,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
|
||||
@@ -932,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)
|
||||
@@ -1145,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()
|
||||
|
||||
+71
-50
@@ -1,4 +1,3 @@
|
||||
import t, c
|
||||
from stdint import *
|
||||
import ast
|
||||
import mlirlite
|
||||
@@ -136,6 +135,20 @@ def has_decorator_marker(node: ast.AST*, marker_name: str, depth: int = 0) -> in
|
||||
return 0
|
||||
|
||||
|
||||
# ---------------- has_pointee_volatile_marker (delegates to TypeGenErr.has_pointee_volatile_marker) ----------------
|
||||
def has_pointee_volatile_marker(node: ast.AST*) -> int:
|
||||
"""Delegate to gen.has_pointee_volatile_marker (rawptr[t.CVolatile | T] field/var pointee)"""
|
||||
if _g_gen is not None: return _g_gen.TypeGenErr.has_pointee_volatile_marker(node)
|
||||
return 0
|
||||
|
||||
|
||||
# ---------------- is_const_object_marker (delegates to TypeGenErr.is_const_object_marker) ----------------
|
||||
def is_const_object_marker(node: ast.AST*, var_ty: mlirlite.LLVMType*) -> int:
|
||||
"""Delegate to gen.is_const_object_marker (t.CConst non-pointer object field/var)"""
|
||||
if _g_gen is not None: return _g_gen.TypeGenErr.is_const_object_marker(node, var_ty)
|
||||
return 0
|
||||
|
||||
|
||||
# ---------------- _get_type_name_from_annotation (delegates to TypeGenResolve._get_type_name_from_annotation) ----------------
|
||||
def _get_type_name_from_annotation(pool: memhub.MemBuddy*, node: ast.AST*) -> str:
|
||||
"""Delegate to gen._get_type_name_from_annotation"""
|
||||
@@ -178,9 +191,17 @@ def cdefine_decl_bits(pool: memhub.MemBuddy*, annot: ast.AST*, value_node: ast.A
|
||||
|
||||
|
||||
# ---------------- register_cdefine_double (delegates to TypeGenCDef.register_cdefine_double) ----------------
|
||||
def register_cdefine_double(pool: memhub.MemBuddy*, name: str, value: double):
|
||||
"""Delegate to gen.register_cdefine_double"""
|
||||
if _g_gen is not None: _g_gen.TypeGenCDef.register_cdefine_double(pool, name, value)
|
||||
def register_cdefine_double(pool: memhub.MemBuddy*, name: str, value: double, is_f32: int = 0):
|
||||
"""Delegate to gen.register_cdefine_double (is_f32: 1=float 声明,0=double)"""
|
||||
if _g_gen is not None: _g_gen.TypeGenCDef.register_cdefine_double(pool, name, value, is_f32)
|
||||
|
||||
|
||||
# ---------------- cdefine_decl_float_bits (delegates to AssignGenMixin.cdefine_decl_float_bits) ----------------
|
||||
def cdefine_decl_float_bits(pool: memhub.MemBuddy*, annot: ast.AST*) -> int:
|
||||
"""define 声明的**浮点**宽度裁决:注解显式写了 float → 1(f32),double/未写 → 0。
|
||||
跨模块(.vpi 重解析 define 的 AST)注册必须用它,否则使用方把 f32 常量当 double 物化。"""
|
||||
if _g_gen is not None: return _g_gen.AssignGen.cdefine_decl_float_bits(pool, annot)
|
||||
return 0
|
||||
|
||||
|
||||
# ---------------- is_cdefine_annotation (delegates to AssignGenMixin.is_cdefine_annotation) ----------------
|
||||
@@ -665,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,4 +1,3 @@
|
||||
import t, c
|
||||
from stdint import *
|
||||
import ast
|
||||
import mlirlite
|
||||
@@ -79,6 +78,10 @@ class VarEntry:
|
||||
# 生命期标签(LifeTag,见文件头 define LIFE_*):该变量**当前值**的生命期。
|
||||
# 追加在末尾以保持既有字段偏移不变。默认 LIFE_UNKNOWN(未登记 = 保守放行)。
|
||||
Life: int
|
||||
# Byte order of this variable, from the `T | t.BigEndian` / `T | t.LittleEndian` annotation union:
|
||||
# 0=native (no marker), 1=BigEndian, 2=LittleEndian. Appended at the end so existing field offsets
|
||||
# are unchanged; local loads/stores bswap when 1 (16/32/64-bit ints only). Mirrors FieldEntry.ByteOrder.
|
||||
ByteOrder: int
|
||||
# 注:P2 的"已失效"状态**不放在这里** —— 给这个热结构加字段会改变布局、牵连整条变量注册
|
||||
# 路径(现场:任何工程编译时都在"函数形参注册刚结束"处 access-violation)。改由
|
||||
# VarGenMixin 的独立失效槽表 _g_freed_slots 保存(键 = 变量槽 alloca,槽本身唯一)。
|
||||
@@ -121,6 +124,10 @@ class FieldEntry:
|
||||
# 1=this field is a const object (annotation has t.CConst and the type is not a pointer):
|
||||
# assigning it via an object fails fast, same as VarEntry.IsConst.
|
||||
IsConst: int
|
||||
# Byte order of this field, from the `X | t.BigEndian` / `X | t.LittleEndian` annotation
|
||||
# union: 0=native (no marker), 1=BigEndian, 2=LittleEndian. Appended at the end so offsets
|
||||
# are unchanged; consumers bswap on field load/store when 1 (16/32/64-bit ints only).
|
||||
ByteOrder: int
|
||||
|
||||
|
||||
# ============================================================
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import string
|
||||
import lib.core.CompilerFactory as CompilerFactory
|
||||
import t, c
|
||||
from stdint import *
|
||||
import stdio
|
||||
import stdlib
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import lib.core.Gen.Generator as Generator
|
||||
import t, c
|
||||
from stdint import *
|
||||
import ast
|
||||
import memhub
|
||||
@@ -9,7 +8,7 @@ import stdlib
|
||||
import viperlib
|
||||
import stdio
|
||||
import sys
|
||||
import w32.fileio as fileio
|
||||
import win32.fileio as fileio
|
||||
import lib.Stub.StubMerger as StubMerger
|
||||
import lib.core.CompilerFactory as CompilerFactory
|
||||
import lib.core.Gen.Translator as HT
|
||||
@@ -165,7 +164,7 @@ novirtual class ImportsGenMixin:
|
||||
word_len: sizet = ci - word_start
|
||||
if word_len == name_len:
|
||||
Matched: int = 1
|
||||
for ei in range(name_len):
|
||||
for ei in range(int(name_len)):
|
||||
if cur[word_start + ei] != name[ei]:
|
||||
Matched = 0
|
||||
break
|
||||
@@ -179,7 +178,7 @@ novirtual class ImportsGenMixin:
|
||||
if name is None: return 0
|
||||
if StubMergerStore.LookupSha1ByModName(name) is None: return 0
|
||||
nl: sizet = string.strlen(name)
|
||||
for dot_pos in range(nl):
|
||||
for dot_pos in range(int(nl)):
|
||||
if name[dot_pos] == '.':
|
||||
pfx: str = stdlib.malloc(dot_pos + 1)
|
||||
if pfx is not None:
|
||||
@@ -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).
|
||||
@@ -238,7 +237,7 @@ novirtual class ImportsGenMixin:
|
||||
# Compare the name
|
||||
if entry_name_len == name_len:
|
||||
Matched: int = 1
|
||||
for ei in range(name_len):
|
||||
for ei in range(int(name_len)):
|
||||
if cur[ci + ei] != local_name[ei]:
|
||||
Matched = 0
|
||||
break
|
||||
@@ -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.
|
||||
@@ -305,7 +304,7 @@ novirtual class ImportsGenMixin:
|
||||
# Compare the name
|
||||
elif entry_name_len == name_len:
|
||||
Matched: int = 1
|
||||
for ei in range(name_len):
|
||||
for ei in range(int(name_len)):
|
||||
if cur[ci + ei] != name[ei]:
|
||||
Matched = 0
|
||||
break
|
||||
@@ -327,6 +326,15 @@ novirtual class ImportsGenMixin:
|
||||
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:
|
||||
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:
|
||||
self.ReexportSha1s = CompilerFactory.factory.PermPool.alloc(MAX_REEXPORT * 17)
|
||||
@@ -338,7 +346,7 @@ novirtual class ImportsGenMixin:
|
||||
string.memset(self.ReexportSrcs, 0, MAX_REEXPORT * 64)
|
||||
if self.ReexportCount >= MAX_REEXPORT: return -1
|
||||
# Idempotency check: look for an existing identical mapping
|
||||
for i in range(self.ReexportCount):
|
||||
for i in range(int(self.ReexportCount)):
|
||||
sidx: sizet = sizet(i) * 17
|
||||
fidx: sizet = sizet(i) * 64
|
||||
if string.strcmp(self.ReexportSha1s + sidx, mod_sha1) == 0:
|
||||
@@ -365,7 +373,7 @@ novirtual class ImportsGenMixin:
|
||||
"""Return the source module name for a re-export mapping, or None."""
|
||||
if mod_sha1 is None or func_name is None: return None
|
||||
if self.ReexportSha1s is None or self.ReexportCount <= 0: return None
|
||||
for i in range(self.ReexportCount):
|
||||
for i in range(int(self.ReexportCount)):
|
||||
sidx: sizet = sizet(i) * 17
|
||||
fidx: sizet = sizet(i) * 64
|
||||
if string.strcmp(self.ReexportSha1s + sidx, mod_sha1) == 0:
|
||||
@@ -389,7 +397,7 @@ novirtual class ImportsGenMixin:
|
||||
pkg_len: sizet = string.strlen(pkg)
|
||||
last_dot: sizet = 0
|
||||
found: int = 0
|
||||
for i in range(pkg_len):
|
||||
for i in range(int(pkg_len)):
|
||||
if pkg[i] == '.':
|
||||
last_dot = i
|
||||
found = 1
|
||||
@@ -435,7 +443,7 @@ novirtual class ImportsGenMixin:
|
||||
# Find the last / or \
|
||||
last_sep: sizet = 0
|
||||
found: int = 0
|
||||
for i in range(rlen):
|
||||
for i in range(int(rlen)):
|
||||
ch: char = rel_path[i]
|
||||
if ch == '/' or ch == '\\':
|
||||
last_sep = i
|
||||
@@ -447,7 +455,7 @@ novirtual class ImportsGenMixin:
|
||||
dir_len: sizet = last_sep
|
||||
buf: str = pool.alloc(dir_len + 1)
|
||||
if buf is None: return None
|
||||
for j in range(dir_len):
|
||||
for j in range(int(dir_len)):
|
||||
ch2: char = rel_path[j]
|
||||
if ch2 == '/' or ch2 == '\\': buf[j] = '.'
|
||||
else: buf[j] = ch2
|
||||
@@ -565,7 +573,7 @@ novirtual class ImportsGenMixin:
|
||||
pool_val: memhub.MemBuddy* = trans.Pool
|
||||
ctx: StubMergerStore.ImportCtx* = StubMergerStore.AllocImportCtx(pool_val, trans._imported_modules, trans._from_imports)
|
||||
if ctx is None: return 0
|
||||
for ni in range(nn):
|
||||
for ni in range(int(nn)):
|
||||
alias: ast.Alias* = ast.Alias*(names.get(ni))
|
||||
if alias is not None and alias.name is not None:
|
||||
# Validate the module SHA1 at import time (fast-fail, full translation only):
|
||||
@@ -595,7 +603,7 @@ novirtual class ImportsGenMixin:
|
||||
names_m: list[ast.AST*]* = impf.names
|
||||
if names_m is not None:
|
||||
nn_m: sizet = len(names_m)
|
||||
for ni_m in range(nn_m):
|
||||
for ni_m in range(int(nn_m)):
|
||||
alias_m: ast.Alias* = ast.Alias*(names_m.get(ni_m))
|
||||
if alias_m is not None and alias_m.name is not None:
|
||||
rl_m: sizet = string.strlen(resolved)
|
||||
@@ -637,7 +645,7 @@ novirtual class ImportsGenMixin:
|
||||
names: list[ast.AST*]* = impf.names
|
||||
if names is not None:
|
||||
nn: sizet = len(names)
|
||||
for ni in range(nn):
|
||||
for ni in range(int(nn)):
|
||||
alias: ast.Alias* = ast.Alias*(names.get(ni))
|
||||
if alias is not None and alias.name is not None:
|
||||
local_name: str = alias.name
|
||||
|
||||
+1501
-1497
File diff suppressed because it is too large.
Load diff
@@ -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
|
||||
@@ -102,11 +101,11 @@ novirtual class StmtGenCifMixin:
|
||||
"""Resolve a macro name via local CDefine, cross-module CDefine, then platform macro."""
|
||||
self.CifEvalOk = 0
|
||||
if name is None or trans is None: return 0
|
||||
v: int = self.Gen.TypeGenCDef.lookup_cdefine_constant(name)
|
||||
v: int = int(self.Gen.TypeGenCDef.lookup_cdefine_constant(name))
|
||||
if self.Gen.TypeGenCDef.is_cdefine_found() != 0:
|
||||
self.CifEvalOk = 1
|
||||
return v
|
||||
v2: int = self.Gen.ExprGenCdef._lookup_cross_module_cdefine(pool, name, trans._from_imports, trans._imported_modules)
|
||||
v2: int = int(self.Gen.ExprGenCdef._lookup_cross_module_cdefine(pool, name, trans._from_imports, trans._imported_modules))
|
||||
if self.Gen.TypeGenCDef.is_cdefine_found() != 0:
|
||||
self.CifEvalOk = 1
|
||||
return v2
|
||||
@@ -121,7 +120,7 @@ novirtual class StmtGenCifMixin:
|
||||
if at.value.kind() != ast.ASTKind.Name: return 0
|
||||
vn: ast.Name* = ast.Name*(at.value)
|
||||
if vn.id is None: return 0
|
||||
v: int = self.Gen.TypeGenCDef.lookup_cdefine_constant(at.attr)
|
||||
v: int = int(self.Gen.TypeGenCDef.lookup_cdefine_constant(at.attr))
|
||||
if self.Gen.TypeGenCDef.is_cdefine_found() != 0:
|
||||
self.CifEvalOk = 1
|
||||
return v
|
||||
@@ -134,14 +133,14 @@ novirtual class StmtGenCifMixin:
|
||||
while resolved[rm_len] != ' ' and resolved[rm_len] != '\0' and resolved[rm_len] != ':': rm_len += 1
|
||||
rm_buf: str = pool.alloc(rm_len + 1)
|
||||
if rm_buf is not None:
|
||||
for rm_i in range(rm_len): rm_buf[rm_i] = resolved[rm_i]
|
||||
for rm_i in range(int(rm_len)): rm_buf[rm_i] = resolved[rm_i]
|
||||
rm_buf[rm_len] = '\0'
|
||||
real_mod = rm_buf
|
||||
fi_len: sizet = string.strlen(at.attr) + string.strlen(real_mod) + 2
|
||||
fi: str = pool.alloc(fi_len)
|
||||
if fi is None: return 0
|
||||
viperlib.snprintf(fi, fi_len, "%s:%s", at.attr, real_mod)
|
||||
v2: int = self.Gen.ExprGenCdef._lookup_cross_module_cdefine(pool, at.attr, fi)
|
||||
v2: int = int(self.Gen.ExprGenCdef._lookup_cross_module_cdefine(pool, at.attr, fi))
|
||||
if self.Gen.TypeGenCDef.is_cdefine_found() != 0:
|
||||
self.CifEvalOk = 1
|
||||
return v2
|
||||
@@ -158,7 +157,8 @@ novirtual class StmtGenCifMixin:
|
||||
# integer and boolean literals are compile-time constants (True/False carry 1/0)
|
||||
if cn.const_kind == ast.CONST_INT or cn.const_kind == ast.CONST_BOOL:
|
||||
self.CifEvalOk = 1
|
||||
return cn.int_val
|
||||
# IntVal(i64) → int(i32):显式收窄(本函数返回 int)
|
||||
return int(cn.int_val)
|
||||
self.CifEvalOk = 0
|
||||
return 0
|
||||
if k == ast.ASTKind.Call: return self._cif_eval_call(pool, trans, node)
|
||||
@@ -192,13 +192,13 @@ novirtual class StmtGenCifMixin:
|
||||
return 0
|
||||
bn: sizet = len(vals)
|
||||
if bo.op == ast.OpKind.And:
|
||||
for bi in range(bn):
|
||||
for bi in range(int(bn)):
|
||||
rv: int = self._cif_eval_node(pool, trans, vals.get(bi))
|
||||
if self.CifEvalOk == 0: return 0
|
||||
if rv == 0: return 0
|
||||
return 1
|
||||
if bo.op == ast.OpKind.Or:
|
||||
for bi in range(bn):
|
||||
for bi in range(int(bn)):
|
||||
rv: int = self._cif_eval_node(pool, trans, vals.get(bi))
|
||||
if self.CifEvalOk == 0: return 0
|
||||
if rv != 0: return 1
|
||||
@@ -216,7 +216,7 @@ novirtual class StmtGenCifMixin:
|
||||
return 0
|
||||
cc: sizet = len(comps)
|
||||
ci: sizet
|
||||
for ci in range(cc):
|
||||
for ci in range(int(cc)):
|
||||
rv: int = self._cif_eval_node(pool, trans, comps.get(ci))
|
||||
if self.CifEvalOk == 0: return 0
|
||||
op_id: int = ops_list.get(ci)
|
||||
@@ -293,7 +293,7 @@ novirtual class StmtGenCifMixin:
|
||||
# way a bare macro name resolves; a resolvable value means the macro exists.
|
||||
if arg0 is not None and arg0.kind() == ast.ASTKind.Attribute:
|
||||
self._cif_eval_node(pool, trans, arg0)
|
||||
dotted_defined: int = self.CifEvalOk
|
||||
dotted_defined: int = int(self.CifEvalOk)
|
||||
self.CifEvalOk = 1
|
||||
return 1 if dotted_defined != 0 else 0
|
||||
name_arg: str = None
|
||||
@@ -307,7 +307,7 @@ novirtual class StmtGenCifMixin:
|
||||
self.Gen.TypeGenErr.fatal_error(cl, "defined() requires a bare macro name, e.g. defined(NAME)", "SyntaxError")
|
||||
return 0
|
||||
self._cif_name_value(pool, trans, name_arg)
|
||||
defined: int = self.CifEvalOk
|
||||
defined: int = int(self.CifEvalOk)
|
||||
# the defined() test itself is evaluable even when the macro is absent (defined = 0)
|
||||
self.CifEvalOk = 1
|
||||
return 1 if defined != 0 else 0
|
||||
@@ -381,7 +381,7 @@ novirtual class StmtGenCifMixin:
|
||||
if self.CifEvalOk == 0: return 0
|
||||
return 1 if v != 0 else 0
|
||||
# @CIfdef / @CIfndef: test whether the macro name is defined
|
||||
defined: int = 0
|
||||
defined: sizet = 0
|
||||
if arg0 is not None and arg0.kind() == ast.ASTKind.Name:
|
||||
nm0: ast.Name* = ast.Name*(arg0)
|
||||
if nm0.id is None: return 0
|
||||
@@ -433,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)
|
||||
@@ -453,7 +453,7 @@ novirtual class StmtGenCifMixin:
|
||||
rt_head: mlirlite.ParamNode* = None
|
||||
rt_tail: mlirlite.ParamNode* = None
|
||||
rvi: sizet
|
||||
for rvi in range(rt_n):
|
||||
for rvi in range(int(rt_n)):
|
||||
ev_i: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, rtp_elts.get(rvi), trans._funcs, trans._func_count, trans)
|
||||
if ev_i is None:
|
||||
rt_val_ok = 0
|
||||
@@ -462,7 +462,7 @@ novirtual class StmtGenCifMixin:
|
||||
if rt_val_ok != 0:
|
||||
# build the anonymous struct type (field types = element value types)
|
||||
rvi2: sizet
|
||||
for rvi2 in range(rt_n):
|
||||
for rvi2 in range(int(rt_n)):
|
||||
rv_i: mlirlite.Value* = mlirlite.Value*((rt_vals[rvi2]))
|
||||
if rv_i is None or rv_i.Ty is None:
|
||||
rt_val_ok = 0
|
||||
@@ -480,7 +480,7 @@ novirtual class StmtGenCifMixin:
|
||||
if rt_struct_ty is not None: rt_tmp = mlirlite.build_alloca(builder, rt_struct_ty)
|
||||
if rt_tmp is not None:
|
||||
rvi3: sizet
|
||||
for rvi3 in range(rt_n):
|
||||
for rvi3 in range(int(rt_n)):
|
||||
rv3: mlirlite.Value* = mlirlite.Value*(rt_vals[rvi3])
|
||||
rfp: mlirlite.Value* = mlirlite.build_gep_struct(builder, rt_struct_ty, rv3.Ty, rt_tmp, rvi3)
|
||||
if rfp is None:
|
||||
@@ -510,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
|
||||
@@ -581,7 +590,7 @@ novirtual class StmtGenCifMixin:
|
||||
ret_bits: int = self.Gen.ExprGenBase.get_llvm_type_bits(ret_ty_rt)
|
||||
val_is_ptr: int = self.Gen.ExprGenBase.is_ptr_type(val.Ty)
|
||||
ret_is_ptr: int = self.Gen.ExprGenBase.is_ptr_type(ret_ty_rt)
|
||||
# 按值返回聚合(数组/结构体):聚合变量在 TPV 中以 alloca 指针表示,返回类型是
|
||||
# 按值返回聚合(数组/结构体):聚合变量在 VPC 中以 alloca 指针表示,返回类型是
|
||||
# 聚合值时须 load 出整个聚合(与上方多返回值 struct 的 `build_load(... rt_struct_ty ...)`
|
||||
# → ret 同形)。仅当被指类型与返回类型是同一聚合时成立;其余 Foo*→Foo 仍由
|
||||
# coerce_to_type fail-fast,不放开隐式解引用。
|
||||
@@ -663,7 +672,7 @@ novirtual class StmtGenCifMixin:
|
||||
if body_cif is not None:
|
||||
bc_cif: sizet = len(body_cif)
|
||||
bi_cif: sizet
|
||||
for bi_cif in range(bc_cif):
|
||||
for bi_cif in range(int(bc_cif)):
|
||||
stmt_cif: ast.AST* = body_cif.get(bi_cif)
|
||||
if stmt_cif is not None: self.Gen.StmtGen.translate_stmt(trans, stmt_cif)
|
||||
else:
|
||||
@@ -677,7 +686,7 @@ novirtual class StmtGenCifMixin:
|
||||
elif orelse_cif is not None:
|
||||
oc_cif: sizet = len(orelse_cif)
|
||||
oi_cif: sizet
|
||||
for oi_cif in range(oc_cif):
|
||||
for oi_cif in range(int(oc_cif)):
|
||||
stmt_cif2: ast.AST* = orelse_cif.get(oi_cif)
|
||||
if stmt_cif2 is not None: self.Gen.StmtGen.translate_stmt(trans, stmt_cif2)
|
||||
return 0
|
||||
@@ -729,7 +738,7 @@ novirtual class StmtGenCifMixin:
|
||||
body: list[ast.AST*]* = if_node.children
|
||||
if body is not None:
|
||||
body_count: sizet = len(body)
|
||||
for bi in range(body_count):
|
||||
for bi in range(int(body_count)):
|
||||
stmt: ast.AST* = body.get(bi)
|
||||
if stmt is not None: self.Gen.StmtGen.translate_stmt(trans, stmt)
|
||||
|
||||
@@ -758,7 +767,7 @@ novirtual class StmtGenCifMixin:
|
||||
trans._ctrl_merge_reachable = 1
|
||||
mlirlite.position_at_end(builder, else_bb)
|
||||
else_count: sizet = len(orelse_list)
|
||||
for ei in range(else_count):
|
||||
for ei in range(int(else_count)):
|
||||
stmt: ast.AST* = orelse_list.get(ei)
|
||||
if stmt is not None: self.Gen.StmtGen.translate_stmt(trans, stmt)
|
||||
if mlirlite.builder_cur_block_is_terminated(builder) == 0:
|
||||
@@ -769,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
|
||||
@@ -820,7 +829,7 @@ novirtual class StmtGenCifMixin:
|
||||
mlirlite.position_at_end(builder, else_bb)
|
||||
if orelse_list is not None:
|
||||
n: sizet = len(orelse_list)
|
||||
for i in range(n):
|
||||
for i in range(int(n)):
|
||||
stmt: ast.AST* = orelse_list.get(i)
|
||||
if stmt is not None: self.Gen.StmtGen.translate_stmt(trans, stmt)
|
||||
if mlirlite.builder_cur_block_is_terminated(builder) == 0:
|
||||
@@ -886,7 +895,7 @@ novirtual class StmtGenCifMixin:
|
||||
body: list[ast.AST*]* = while_node.children
|
||||
if body is not None:
|
||||
body_count: sizet = len(body)
|
||||
for bi in range(body_count):
|
||||
for bi in range(int(body_count)):
|
||||
stmt: ast.AST* = body.get(bi)
|
||||
if stmt is not None: self.Gen.StmtGen.translate_stmt(trans, stmt)
|
||||
|
||||
@@ -924,7 +933,7 @@ novirtual class StmtGenCifMixin:
|
||||
ch: list[ast.AST*]* = node.children
|
||||
if ch is not None:
|
||||
n: sizet = len(ch)
|
||||
for bi in range(n):
|
||||
for bi in range(int(n)):
|
||||
c: ast.AST* = ch.get(bi)
|
||||
if c is None: continue
|
||||
k: int = c.kind()
|
||||
@@ -937,7 +946,7 @@ novirtual class StmtGenCifMixin:
|
||||
"""Return 1 if the statement list contains a break belonging to this loop level."""
|
||||
if body is None: return 0
|
||||
n: sizet = len(body)
|
||||
for bi in range(n):
|
||||
for bi in range(int(n)):
|
||||
s: ast.AST* = body.get(bi)
|
||||
if s is None: continue
|
||||
k: int = s.kind()
|
||||
@@ -1115,8 +1124,28 @@ novirtual class StmtGenCifMixin:
|
||||
skc_r: ast.Constant* = ast.Constant*(step_arg_node)
|
||||
if skc_r.int_val < 0: step_neg_const = 1
|
||||
|
||||
# 3.5 loop-var storage width follows range arg type (u64→i64 / u16→i16 / u8→i8 / else i32);
|
||||
# a fixed i32 alloca truncates uint64 bounds. Applies only to undeclared loop vars.
|
||||
# 3.4 range 边界统一提升到**至少 i32**(仿 C 整数提升):i8/i16 边界不再以窄位宽参与比较
|
||||
# 与自增。拓宽方向由 coerce_to_type → should_zext_widen 决定:非负 define 常量是**无符号
|
||||
# 位模式**(Debugger/define-constants.md §7.2),必须 zext(define N = 50000 → 16 位 0xC350
|
||||
# → 50000,而非 sext 成 -15536)。u64 边界保持 i64(不动,避免截断)。
|
||||
if stop_val is not None and stop_val.Ty is not None:
|
||||
sb_p4: int = self.Gen.ExprGenBase.get_llvm_type_bits(stop_val.Ty)
|
||||
if sb_p4 != 0 and sb_p4 < 32:
|
||||
nd_s4: ast.AST* = args.get(0)
|
||||
if arg_count >= 2: nd_s4 = args.get(1)
|
||||
stop_val = self.Gen.ExprGenBase.coerce_to_type(builder, stop_val, i32_ty, nd_s4, trans)
|
||||
if start_val is not None and start_val.Ty is not None:
|
||||
sa_p4: int = self.Gen.ExprGenBase.get_llvm_type_bits(start_val.Ty)
|
||||
if sa_p4 != 0 and sa_p4 < 32:
|
||||
start_val = self.Gen.ExprGenBase.coerce_to_type(builder, start_val, i32_ty, args.get(0), trans)
|
||||
if step_val is not None and step_val.Ty is not None:
|
||||
sp_p4: int = self.Gen.ExprGenBase.get_llvm_type_bits(step_val.Ty)
|
||||
if sp_p4 != 0 and sp_p4 < 32:
|
||||
step_val = self.Gen.ExprGenBase.coerce_to_type(builder, step_val, i32_ty, args.get(2), trans)
|
||||
|
||||
# 3.5 loop-var storage width: 边界已提升到 ≥i32,所以只有 i64 边界需要 i64 循环变量,
|
||||
# 其余一律 i32(不再有 u16→i16 / u8→i8 档:窄位宽循环变量会把边界截断)。
|
||||
# Applies only to undeclared loop vars.
|
||||
loop_store_ty: mlirlite.LLVMType* = i32_ty
|
||||
range_arg_bits: int = 0
|
||||
if arg_count == 1:
|
||||
@@ -1128,8 +1157,6 @@ novirtual class StmtGenCifMixin:
|
||||
if stop_val is not None and stop_val.Ty is not None: stop_bits_rt = self.Gen.ExprGenBase.get_llvm_type_bits(stop_val.Ty)
|
||||
range_arg_bits = start_bits_rt if start_bits_rt > stop_bits_rt else stop_bits_rt
|
||||
if range_arg_bits == 64: loop_store_ty = mlirlite.Int64(pool)
|
||||
elif range_arg_bits == 16: loop_store_ty = mlirlite.Int16(pool)
|
||||
elif range_arg_bits == 8: loop_store_ty = mlirlite.Int8(pool)
|
||||
|
||||
# 3.6 loop unsignedness: an unsigned loop-var annotation or any unsigned range bound uses
|
||||
# icmp ult/ugt instead of slt/sgt (via ExprGenOps._node_type_unsigned).
|
||||
@@ -1138,7 +1165,7 @@ novirtual class StmtGenCifMixin:
|
||||
if self.Gen.ExprGenOps._node_type_unsigned(pool, target, trans) != 0: loop_unsigned = 1
|
||||
if loop_unsigned == 0:
|
||||
args_u: sizet
|
||||
for args_u in range(arg_count):
|
||||
for args_u in range(int(arg_count)):
|
||||
if self.Gen.ExprGenOps._node_type_unsigned(pool, args.get(args_u), trans) != 0:
|
||||
loop_unsigned = 1
|
||||
break
|
||||
@@ -1250,7 +1277,7 @@ novirtual class StmtGenCifMixin:
|
||||
body: list[ast.AST*]* = for_node.children
|
||||
if body is not None:
|
||||
body_count: sizet = len(body)
|
||||
for bi in range(body_count):
|
||||
for bi in range(int(body_count)):
|
||||
stmt: ast.AST* = body.get(bi)
|
||||
if stmt is not None: self.Gen.StmtGen.translate_stmt(trans, stmt)
|
||||
|
||||
@@ -1325,6 +1352,22 @@ novirtual class StmtGenCifMixin:
|
||||
return GenTypes.SEQ_KIND_STR
|
||||
return 0
|
||||
|
||||
# _range_work_bits - range 工作位宽:取各已求值参数位宽的最大值,下限 i32(32)。
|
||||
# 与 `for i in range(...)` 的 loop_store_ty 同口径,避免内部 i64 索引写回 i32 循环变量时误报窄化。
|
||||
def _range_work_bits(self, a: mlirlite.Value*, b: mlirlite.Value*, c: mlirlite.Value*) -> int:
|
||||
"""Working integer width for a range() source: max of the provided argument widths, at least 32."""
|
||||
bits: int = 32
|
||||
if a is not None and a.Ty is not None:
|
||||
ab: int = self.Gen.ExprGenBase.get_llvm_type_bits(a.Ty)
|
||||
if ab > bits: bits = ab
|
||||
if b is not None and b.Ty is not None:
|
||||
bb: int = self.Gen.ExprGenBase.get_llvm_type_bits(b.Ty)
|
||||
if bb > bits: bits = bb
|
||||
if c is not None and c.Ty is not None:
|
||||
cb: int = self.Gen.ExprGenBase.get_llvm_type_bits(c.Ty)
|
||||
if cb > bits: bits = cb
|
||||
return bits
|
||||
|
||||
def _build_seq_loop(self, trans: HT.Translator*, for_node: ast.For*, t1: str, t2: str, src1: ast.AST*, src2: ast.AST*, n_srcs: int) -> int:
|
||||
"""Build a sequence iteration loop for enumerate (n_srcs=1) or zip (n_srcs=2)."""
|
||||
pool: memhub.MemBuddy* = trans.Pool
|
||||
@@ -1347,34 +1390,45 @@ novirtual class StmtGenCifMixin:
|
||||
l1_data: mlirlite.Value* = None
|
||||
l1_esize: mlirlite.Value* = None
|
||||
elem1_ty: mlirlite.LLVMType* = i8_ptr_ty
|
||||
# range 的工作位宽/类型(随参数位宽,仿 `for i in range(...)`);非 range 源不使用。
|
||||
r1_wty: mlirlite.LLVMType* = None
|
||||
r1_bits: int = 0
|
||||
if kind1 == GenTypes.SEQ_KIND_RANGE:
|
||||
elem1_ty = i32_ty
|
||||
r1_start = mlirlite.const_int64(pool, 0)
|
||||
r1_step = mlirlite.const_int64(pool, 1)
|
||||
r1_stop = mlirlite.const_int64(pool, 0)
|
||||
# assign to a temp before taking args (chained cast attrs misinfer field offsets)
|
||||
# 先求值参数,再由参数位宽决定工作类型:i32 字面量就按 i32 算,
|
||||
# 避免内部 i64 索引写回 i32 循环变量时触发隐式窄化误报。
|
||||
call_src1: ast.Call* = ast.Call*(src1)
|
||||
cargs1: list[ast.AST*]* = call_src1.args
|
||||
a1: mlirlite.Value* = None
|
||||
b1: mlirlite.Value* = None
|
||||
c1: mlirlite.Value* = None
|
||||
n1: sizet = 0
|
||||
if cargs1 is not None:
|
||||
n1: sizet = len(cargs1)
|
||||
n1 = len(cargs1)
|
||||
if n1 >= 1:
|
||||
sv: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, trans.Module, cargs1.get(0), trans._funcs, trans._func_count, trans)
|
||||
if sv is None:
|
||||
a1 = self.Gen.ExprGen.translate_value(builder, pool, trans.Module, cargs1.get(0), trans._funcs, trans._func_count, trans)
|
||||
if a1 is None:
|
||||
# fail-fast: range argument failed to translate
|
||||
self.Gen.TypeGenErr.fatal_error(cargs1.get(0), "failed to translate the range argument", "TypeError")
|
||||
r1_stop = self.Gen.ExprGenBase.coerce_to_type(builder, sv, i64_ty, cargs1.get(0))
|
||||
if n1 >= 2:
|
||||
sv2: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, trans.Module, cargs1.get(1), trans._funcs, trans._func_count, trans)
|
||||
if sv2 is None:
|
||||
b1 = self.Gen.ExprGen.translate_value(builder, pool, trans.Module, cargs1.get(1), trans._funcs, trans._func_count, trans)
|
||||
if b1 is None:
|
||||
# fail-fast: range argument failed to translate
|
||||
self.Gen.TypeGenErr.fatal_error(cargs1.get(1), "failed to translate the range argument", "TypeError")
|
||||
r1_start = self.Gen.ExprGenBase.coerce_to_type(builder, sv2, i64_ty, cargs1.get(1))
|
||||
if n1 >= 3:
|
||||
sv3: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, trans.Module, cargs1.get(2), trans._funcs, trans._func_count, trans)
|
||||
if sv3 is None:
|
||||
c1 = self.Gen.ExprGen.translate_value(builder, pool, trans.Module, cargs1.get(2), trans._funcs, trans._func_count, trans)
|
||||
if c1 is None:
|
||||
# fail-fast: range argument failed to translate
|
||||
self.Gen.TypeGenErr.fatal_error(cargs1.get(2), "failed to translate the range argument", "TypeError")
|
||||
r1_step = self.Gen.ExprGenBase.coerce_to_type(builder, sv3, i64_ty, cargs1.get(2))
|
||||
r1_bits = self._range_work_bits(a1, b1, c1)
|
||||
r1_wty = self.Gen.ExprGenBase._int_type_of_bits(pool, r1_bits)
|
||||
if r1_wty is None: r1_wty = i32_ty
|
||||
elem1_ty = r1_wty
|
||||
r1_start = mlirlite.const_int(pool, r1_bits, 0)
|
||||
r1_step = mlirlite.const_int(pool, r1_bits, 1)
|
||||
r1_stop = mlirlite.const_int(pool, r1_bits, 0)
|
||||
if a1 is not None: r1_stop = self.Gen.ExprGenBase.coerce_to_type(builder, a1, r1_wty, cargs1.get(0))
|
||||
if b1 is not None: r1_start = self.Gen.ExprGenBase.coerce_to_type(builder, b1, r1_wty, cargs1.get(1))
|
||||
if c1 is not None: r1_step = self.Gen.ExprGenBase.coerce_to_type(builder, c1, r1_wty, cargs1.get(2))
|
||||
elif kind1 == GenTypes.SEQ_KIND_STR:
|
||||
elem1_ty = i8_ty
|
||||
s1_ptr = self.Gen.ExprGen.translate_value(builder, pool, trans.Module, src1, trans._funcs, trans._func_count, trans)
|
||||
@@ -1397,37 +1451,47 @@ novirtual class StmtGenCifMixin:
|
||||
r2_start: mlirlite.Value* = None
|
||||
r2_stop: mlirlite.Value* = None
|
||||
r2_step: mlirlite.Value* = None
|
||||
# range 的工作位宽/类型(source 2 对称处理)。
|
||||
r2_wty: mlirlite.LLVMType* = None
|
||||
r2_bits: int = 0
|
||||
if n_srcs >= 2:
|
||||
kind2 = self._seq_kind(trans, src2)
|
||||
if kind2 == 0: self.Gen.TypeGenErr.fatal_error(for_node, "sequence iteration: source 2 type is not supported")
|
||||
if kind2 == GenTypes.SEQ_KIND_RANGE:
|
||||
elem2_ty = i32_ty
|
||||
r2_start = mlirlite.const_int64(pool, 0)
|
||||
r2_step = mlirlite.const_int64(pool, 1)
|
||||
r2_stop = mlirlite.const_int64(pool, 0)
|
||||
# assign to a temp before taking args (chained cast attrs misinfer field offsets)
|
||||
# 与 source 1 同口径:工作类型随参数位宽,避免 i64 索引写回窄位宽循环变量误报。
|
||||
call_src2: ast.Call* = ast.Call*(src2)
|
||||
cargs2: list[ast.AST*]* = call_src2.args
|
||||
a2: mlirlite.Value* = None
|
||||
b2: mlirlite.Value* = None
|
||||
c2: mlirlite.Value* = None
|
||||
n2: sizet = 0
|
||||
if cargs2 is not None:
|
||||
n2: sizet = len(cargs2)
|
||||
n2 = len(cargs2)
|
||||
if n2 >= 1:
|
||||
svz: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, trans.Module, cargs2.get(0), trans._funcs, trans._func_count, trans)
|
||||
if svz is None:
|
||||
a2 = self.Gen.ExprGen.translate_value(builder, pool, trans.Module, cargs2.get(0), trans._funcs, trans._func_count, trans)
|
||||
if a2 is None:
|
||||
# fail-fast: range argument failed to translate
|
||||
self.Gen.TypeGenErr.fatal_error(cargs2.get(0), "failed to translate the range argument", "TypeError")
|
||||
r2_stop = self.Gen.ExprGenBase.coerce_to_type(builder, svz, i64_ty, cargs2.get(0))
|
||||
if n2 >= 2:
|
||||
svz2: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, trans.Module, cargs2.get(1), trans._funcs, trans._func_count, trans)
|
||||
if svz2 is None:
|
||||
b2 = self.Gen.ExprGen.translate_value(builder, pool, trans.Module, cargs2.get(1), trans._funcs, trans._func_count, trans)
|
||||
if b2 is None:
|
||||
# fail-fast: range argument failed to translate
|
||||
self.Gen.TypeGenErr.fatal_error(cargs2.get(1), "failed to translate the range argument", "TypeError")
|
||||
r2_start = self.Gen.ExprGenBase.coerce_to_type(builder, svz2, i64_ty, cargs2.get(1))
|
||||
if n2 >= 3:
|
||||
svz3: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, trans.Module, cargs2.get(2), trans._funcs, trans._func_count, trans)
|
||||
if svz3 is None:
|
||||
c2 = self.Gen.ExprGen.translate_value(builder, pool, trans.Module, cargs2.get(2), trans._funcs, trans._func_count, trans)
|
||||
if c2 is None:
|
||||
# fail-fast: range argument failed to translate
|
||||
self.Gen.TypeGenErr.fatal_error(cargs2.get(2), "failed to translate the range argument", "TypeError")
|
||||
r2_step = self.Gen.ExprGenBase.coerce_to_type(builder, svz3, i64_ty, cargs2.get(2))
|
||||
r2_bits = self._range_work_bits(a2, b2, c2)
|
||||
r2_wty = self.Gen.ExprGenBase._int_type_of_bits(pool, r2_bits)
|
||||
if r2_wty is None: r2_wty = i32_ty
|
||||
elem2_ty = r2_wty
|
||||
r2_start = mlirlite.const_int(pool, r2_bits, 0)
|
||||
r2_step = mlirlite.const_int(pool, r2_bits, 1)
|
||||
r2_stop = mlirlite.const_int(pool, r2_bits, 0)
|
||||
if a2 is not None: r2_stop = self.Gen.ExprGenBase.coerce_to_type(builder, a2, r2_wty, cargs2.get(0))
|
||||
if b2 is not None: r2_start = self.Gen.ExprGenBase.coerce_to_type(builder, b2, r2_wty, cargs2.get(1))
|
||||
if c2 is not None: r2_step = self.Gen.ExprGenBase.coerce_to_type(builder, c2, r2_wty, cargs2.get(2))
|
||||
elif kind2 == GenTypes.SEQ_KIND_STR:
|
||||
elem2_ty = i8_ty
|
||||
s2_ptr = self.Gen.ExprGen.translate_value(builder, pool, trans.Module, src2, trans._funcs, trans._func_count, trans)
|
||||
@@ -1487,7 +1551,11 @@ novirtual class StmtGenCifMixin:
|
||||
mlirlite.position_at_end(builder, cond_bb)
|
||||
cur_idx: mlirlite.Value* = mlirlite.build_load(builder, i64_ty, idx_alloca)
|
||||
done1: mlirlite.Value* = None
|
||||
if kind1 == GenTypes.SEQ_KIND_RANGE: done1 = mlirlite.build_icmp(builder, mlirlite.ICMP_SLT, cur_idx, r1_stop)
|
||||
if kind1 == GenTypes.SEQ_KIND_RANGE:
|
||||
# range 的索引按工作位宽截断后再比较(索引是编译器内部量,不涉及用户窄化)
|
||||
idx1_cmp: mlirlite.Value* = cur_idx
|
||||
if r1_bits != 64: idx1_cmp = mlirlite.build_trunc(builder, cur_idx, r1_wty)
|
||||
done1 = mlirlite.build_icmp(builder, mlirlite.ICMP_SLT, idx1_cmp, r1_stop)
|
||||
elif kind1 == GenTypes.SEQ_KIND_STR:
|
||||
ch_ptr1: mlirlite.Value* = mlirlite.build_gep(builder, i8_ty, s1_ptr, cur_idx)
|
||||
ch1: mlirlite.Value* = mlirlite.build_load(builder, i8_ty, ch_ptr1)
|
||||
@@ -1497,7 +1565,10 @@ novirtual class StmtGenCifMixin:
|
||||
cont: mlirlite.Value* = done1
|
||||
if n_srcs >= 2:
|
||||
done2: mlirlite.Value* = None
|
||||
if kind2 == GenTypes.SEQ_KIND_RANGE: done2 = mlirlite.build_icmp(builder, mlirlite.ICMP_SLT, cur_idx, r2_stop)
|
||||
if kind2 == GenTypes.SEQ_KIND_RANGE:
|
||||
idx2_cmp: mlirlite.Value* = cur_idx
|
||||
if r2_bits != 64: idx2_cmp = mlirlite.build_trunc(builder, cur_idx, r2_wty)
|
||||
done2 = mlirlite.build_icmp(builder, mlirlite.ICMP_SLT, idx2_cmp, r2_stop)
|
||||
elif kind2 == GenTypes.SEQ_KIND_STR:
|
||||
ch_ptr2: mlirlite.Value* = mlirlite.build_gep(builder, i8_ty, s2_ptr, cur_idx)
|
||||
ch2: mlirlite.Value* = mlirlite.build_load(builder, i8_ty, ch_ptr2)
|
||||
@@ -1512,7 +1583,9 @@ novirtual class StmtGenCifMixin:
|
||||
# source-1 element
|
||||
v1: mlirlite.Value* = None
|
||||
if kind1 == GenTypes.SEQ_KIND_RANGE:
|
||||
step1: mlirlite.Value* = mlirlite.build_mul(builder, cur_idx, r1_step)
|
||||
idx1_b: mlirlite.Value* = cur_idx
|
||||
if r1_bits != 64: idx1_b = mlirlite.build_trunc(builder, cur_idx, r1_wty)
|
||||
step1: mlirlite.Value* = mlirlite.build_mul(builder, idx1_b, r1_step)
|
||||
v1 = mlirlite.build_add(builder, r1_start, step1)
|
||||
elif kind1 == GenTypes.SEQ_KIND_STR:
|
||||
ch_ptr1b: mlirlite.Value* = mlirlite.build_gep(builder, i8_ty, s1_ptr, cur_idx)
|
||||
@@ -1539,7 +1612,9 @@ novirtual class StmtGenCifMixin:
|
||||
# source-2 element → t2
|
||||
v2: mlirlite.Value* = None
|
||||
if kind2 == GenTypes.SEQ_KIND_RANGE:
|
||||
step2: mlirlite.Value* = mlirlite.build_mul(builder, cur_idx, r2_step)
|
||||
idx2_b: mlirlite.Value* = cur_idx
|
||||
if r2_bits != 64: idx2_b = mlirlite.build_trunc(builder, cur_idx, r2_wty)
|
||||
step2: mlirlite.Value* = mlirlite.build_mul(builder, idx2_b, r2_step)
|
||||
v2 = mlirlite.build_add(builder, r2_start, step2)
|
||||
elif kind2 == GenTypes.SEQ_KIND_STR:
|
||||
ch_ptr2b: mlirlite.Value* = mlirlite.build_gep(builder, i8_ty, s2_ptr, cur_idx)
|
||||
@@ -1562,7 +1637,7 @@ novirtual class StmtGenCifMixin:
|
||||
body: list[ast.AST*]* = for_node.children
|
||||
if body is not None:
|
||||
body_count: sizet = len(body)
|
||||
for bi in range(body_count):
|
||||
for bi in range(int(body_count)):
|
||||
stmt: ast.AST* = body.get(bi)
|
||||
if stmt is not None: self.Gen.StmtGen.translate_stmt(trans, stmt)
|
||||
|
||||
@@ -1687,7 +1762,7 @@ novirtual class StmtGenCifMixin:
|
||||
body: list[ast.AST*]* = for_node.children
|
||||
if body is not None:
|
||||
body_count: sizet = len(body)
|
||||
for bi in range(body_count):
|
||||
for bi in range(int(body_count)):
|
||||
stmt: ast.AST* = body.get(bi)
|
||||
if stmt is not None: self.Gen.StmtGen.translate_stmt(trans, stmt)
|
||||
|
||||
@@ -1816,7 +1891,7 @@ novirtual class StmtGenCifMixin:
|
||||
body: list[ast.AST*]* = for_node.children
|
||||
if body is not None:
|
||||
body_count: sizet = len(body)
|
||||
for bi in range(body_count):
|
||||
for bi in range(int(body_count)):
|
||||
stmt: ast.AST* = body.get(bi)
|
||||
if stmt is not None: self.Gen.StmtGen.translate_stmt(trans, stmt)
|
||||
|
||||
@@ -1971,7 +2046,7 @@ novirtual class StmtGenCifMixin:
|
||||
body: list[ast.AST*]* = for_node.children
|
||||
if body is not None:
|
||||
body_count: sizet = len(body)
|
||||
for bi in range(body_count):
|
||||
for bi in range(int(body_count)):
|
||||
stmt: ast.AST* = body.get(bi)
|
||||
if stmt is not None: self.Gen.StmtGen.translate_stmt(trans, stmt)
|
||||
|
||||
@@ -2068,7 +2143,7 @@ novirtual class StmtGenCifMixin:
|
||||
body: list[ast.AST*]* = for_node.children
|
||||
if body is not None:
|
||||
body_count: sizet = len(body)
|
||||
for bi in range(body_count):
|
||||
for bi in range(int(body_count)):
|
||||
stmt: ast.AST* = body.get(bi)
|
||||
if stmt is not None: self.Gen.StmtGen.translate_stmt(trans, stmt)
|
||||
|
||||
@@ -2202,7 +2277,7 @@ novirtual class StmtGenCifMixin:
|
||||
body: list[ast.AST*]* = for_node.children
|
||||
if body is not None:
|
||||
body_count: sizet = len(body)
|
||||
for bi in range(body_count):
|
||||
for bi in range(int(body_count)):
|
||||
stmt: ast.AST* = body.get(bi)
|
||||
if stmt is not None: self.Gen.StmtGen.translate_stmt(trans, stmt)
|
||||
trans._break_bb = old_break
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import lib.core.Gen.GenTypes as GenTypes
|
||||
import t, c
|
||||
from stdint import *
|
||||
import ast
|
||||
import mlirlite
|
||||
@@ -37,7 +36,7 @@ novirtual class StmtGenDeclMixin(StmtGenCifMixin.StmtGenCifMixin):
|
||||
names: list[ast.AST*]* = gn.names
|
||||
if names is None: return 0
|
||||
n: sizet = len(names)
|
||||
for i in range(n):
|
||||
for i in range(int(n)):
|
||||
nm_node: ast.AST* = names.get(i)
|
||||
if nm_node is not None and nm_node.kind() == ast.ASTKind.Name:
|
||||
nm: ast.Name* = ast.Name*(nm_node)
|
||||
@@ -51,7 +50,7 @@ novirtual class StmtGenDeclMixin(StmtGenCifMixin.StmtGenCifMixin):
|
||||
names: list[ast.AST*]* = nl.names
|
||||
if names is None: return 0
|
||||
n: sizet = len(names)
|
||||
for i in range(n):
|
||||
for i in range(int(n)):
|
||||
nm_node: ast.AST* = names.get(i)
|
||||
if nm_node is not None and nm_node.kind() == ast.ASTKind.Name:
|
||||
nm: ast.Name* = ast.Name*(nm_node)
|
||||
@@ -121,67 +120,67 @@ novirtual class StmtGenDeclMixin(StmtGenCifMixin.StmtGenCifMixin):
|
||||
ifn: ast.If* = ast.If*(node)
|
||||
if ifn.children is not None:
|
||||
bn_pre_i: sizet = len(ifn.children)
|
||||
for bi_pre_i in range(bn_pre_i): self.pre_scan_allocas(trans, ifn.children.get(bi_pre_i))
|
||||
for bi_pre_i in range(int(bn_pre_i)): self.pre_scan_allocas(trans, ifn.children.get(bi_pre_i))
|
||||
if ifn.orelse is not None:
|
||||
bn_pre_e: sizet = len(ifn.orelse)
|
||||
for bi_pre_e in range(bn_pre_e): self.pre_scan_allocas(trans, ifn.orelse.get(bi_pre_e))
|
||||
for bi_pre_e in range(int(bn_pre_e)): self.pre_scan_allocas(trans, ifn.orelse.get(bi_pre_e))
|
||||
return 0
|
||||
if k == ast.ASTKind.For:
|
||||
for_n: ast.For* = ast.For*(node)
|
||||
if for_n.children is not None:
|
||||
bn_pre_f: sizet = len(for_n.children)
|
||||
for bi_pre_f in range(bn_pre_f): self.pre_scan_allocas(trans, for_n.children.get(bi_pre_f))
|
||||
for bi_pre_f in range(int(bn_pre_f)): self.pre_scan_allocas(trans, for_n.children.get(bi_pre_f))
|
||||
if for_n.orelse is not None:
|
||||
bn_pre_fe: sizet = len(for_n.orelse)
|
||||
for bi_pre_fe in range(bn_pre_fe): self.pre_scan_allocas(trans, for_n.orelse.get(bi_pre_fe))
|
||||
for bi_pre_fe in range(int(bn_pre_fe)): self.pre_scan_allocas(trans, for_n.orelse.get(bi_pre_fe))
|
||||
return 0
|
||||
if k == ast.ASTKind.While:
|
||||
wh_n: ast.While* = ast.While*(node)
|
||||
if wh_n.children is not None:
|
||||
bn_pre_w: sizet = len(wh_n.children)
|
||||
for bi_pre_w in range(bn_pre_w): self.pre_scan_allocas(trans, wh_n.children.get(bi_pre_w))
|
||||
for bi_pre_w in range(int(bn_pre_w)): self.pre_scan_allocas(trans, wh_n.children.get(bi_pre_w))
|
||||
if wh_n.orelse is not None:
|
||||
bn_pre_we: sizet = len(wh_n.orelse)
|
||||
for bi_pre_we in range(bn_pre_we): self.pre_scan_allocas(trans, wh_n.orelse.get(bi_pre_we))
|
||||
for bi_pre_we in range(int(bn_pre_we)): self.pre_scan_allocas(trans, wh_n.orelse.get(bi_pre_we))
|
||||
return 0
|
||||
if k == ast.ASTKind.Try:
|
||||
tr_n: ast.Try* = ast.Try*(node)
|
||||
if tr_n.children is not None:
|
||||
bn_pre_t: sizet = len(tr_n.children)
|
||||
for bi_pre_t in range(bn_pre_t): self.pre_scan_allocas(trans, tr_n.children.get(bi_pre_t))
|
||||
for bi_pre_t in range(int(bn_pre_t)): self.pre_scan_allocas(trans, tr_n.children.get(bi_pre_t))
|
||||
if tr_n.handlers is not None:
|
||||
bn_pre_th: sizet = len(tr_n.handlers)
|
||||
for bi_pre_th in range(bn_pre_th):
|
||||
for bi_pre_th in range(int(bn_pre_th)):
|
||||
eh_pre: ast.AST* = tr_n.handlers.get(bi_pre_th)
|
||||
if eh_pre is not None and eh_pre.kind() == ast.ASTKind.ExceptHandler:
|
||||
ehc_pre: ast.ExceptHandler* = ast.ExceptHandler*(eh_pre)
|
||||
if ehc_pre.children is not None:
|
||||
bn_pre_ehb: sizet = len(ehc_pre.children)
|
||||
for bi_pre_ehb in range(bn_pre_ehb): self.pre_scan_allocas(trans, ehc_pre.children.get(bi_pre_ehb))
|
||||
for bi_pre_ehb in range(int(bn_pre_ehb)): self.pre_scan_allocas(trans, ehc_pre.children.get(bi_pre_ehb))
|
||||
if tr_n.orelse is not None:
|
||||
bn_pre_to: sizet = len(tr_n.orelse)
|
||||
for bi_pre_to in range(bn_pre_to): self.pre_scan_allocas(trans, tr_n.orelse.get(bi_pre_to))
|
||||
for bi_pre_to in range(int(bn_pre_to)): self.pre_scan_allocas(trans, tr_n.orelse.get(bi_pre_to))
|
||||
if tr_n.finalbody is not None:
|
||||
bn_pre_tf: sizet = len(tr_n.finalbody)
|
||||
for bi_pre_tf in range(bn_pre_tf): self.pre_scan_allocas(trans, tr_n.finalbody.get(bi_pre_tf))
|
||||
for bi_pre_tf in range(int(bn_pre_tf)): self.pre_scan_allocas(trans, tr_n.finalbody.get(bi_pre_tf))
|
||||
return 0
|
||||
if k == ast.ASTKind.With:
|
||||
wth: ast.With* = ast.With*(node)
|
||||
if wth.children is not None:
|
||||
bn_pre_wb: sizet = len(wth.children)
|
||||
for bi_pre_wb in range(bn_pre_wb): self.pre_scan_allocas(trans, wth.children.get(bi_pre_wb))
|
||||
for bi_pre_wb in range(int(bn_pre_wb)): self.pre_scan_allocas(trans, wth.children.get(bi_pre_wb))
|
||||
return 0
|
||||
if k == ast.ASTKind.Match:
|
||||
mt_n: ast.Match* = ast.Match*(node)
|
||||
if mt_n.cases is not None:
|
||||
bn_pre_mc: sizet = len(mt_n.cases)
|
||||
for bi_pre_mc in range(bn_pre_mc):
|
||||
for bi_pre_mc in range(int(bn_pre_mc)):
|
||||
mc_pre: ast.AST* = mt_n.cases.get(bi_pre_mc)
|
||||
if mc_pre is not None and mc_pre.kind() == ast.ASTKind.MatchCase:
|
||||
mcc_pre: ast.MatchCase* = ast.MatchCase*(mc_pre)
|
||||
if mcc_pre.children is not None:
|
||||
bn_pre_mcb: sizet = len(mcc_pre.children)
|
||||
for bi_pre_mcb in range(bn_pre_mcb): self.pre_scan_allocas(trans, mcc_pre.children.get(bi_pre_mcb))
|
||||
for bi_pre_mcb in range(int(bn_pre_mcb)): self.pre_scan_allocas(trans, mcc_pre.children.get(bi_pre_mcb))
|
||||
return 0
|
||||
return 0
|
||||
|
||||
@@ -415,8 +414,8 @@ novirtual class StmtGenDeclMixin(StmtGenCifMixin.StmtGenCifMixin):
|
||||
if enum_entry is None or member_name is None or member_ty is None: return -1
|
||||
|
||||
# Dedup: skip an existing member with the same name (cross-module enum merging, e.g.
|
||||
# w32.fileio and fat32_types both define FRESULT).
|
||||
for ei_d in range(enum_entry.MemberCount):
|
||||
# win32.fileio and fat32_types both define FRESULT).
|
||||
for ei_d in range(int(enum_entry.MemberCount)):
|
||||
ex_d: GenTypes.EnumMember* = self._get_enum_member(enum_entry, ei_d)
|
||||
if ex_d is not None and ex_d.Name is not None:
|
||||
if string.strcmp(ex_d.Name, member_name) == 0: return ei_d
|
||||
@@ -445,7 +444,7 @@ novirtual class StmtGenDeclMixin(StmtGenCifMixin.StmtGenCifMixin):
|
||||
def find_enum(self, name: str) -> GenTypes.EnumEntry*:
|
||||
"""Look up an enum by class name; returns EnumEntry or None"""
|
||||
if name is None or self.EnumTable is None: return None
|
||||
for i in range(self.EnumCount):
|
||||
for i in range(int(self.EnumCount)):
|
||||
entry: GenTypes.EnumEntry* = self._get_enum_entry(i)
|
||||
if entry is not None and entry.Name is not None:
|
||||
if string.strcmp(entry.Name, name) == 0: return entry
|
||||
@@ -456,7 +455,7 @@ novirtual class StmtGenDeclMixin(StmtGenCifMixin.StmtGenCifMixin):
|
||||
if enum_name is None or member_name is None: return None
|
||||
entry: GenTypes.EnumEntry* = self.find_enum(enum_name)
|
||||
if entry is None: return None
|
||||
for mi in range(entry.MemberCount):
|
||||
for mi in range(int(entry.MemberCount)):
|
||||
me: GenTypes.EnumMember* = self._get_enum_member(entry, mi)
|
||||
if me is not None and me.Name is not None:
|
||||
if string.strcmp(me.Name, member_name) == 0: return me
|
||||
|
||||
@@ -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
|
||||
@@ -32,6 +31,12 @@ novirtual class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
|
||||
# define). Every instruction then carries ", !dbg !N".
|
||||
if node is not None and trans._cur_builder is not None:
|
||||
DebugInfoGen.SetStatement(trans.Pool, trans._cur_builder, node.lineno, node.col_offset + 1)
|
||||
# 注意:**不要**用当前语句位置覆盖栈帧(那会把“函数定义帧”与错误帧叠成同一位置、
|
||||
# 被去重合并,反而丢掉 `_start` 那帧)。栈帧保留“被调函数的定义位置”,
|
||||
# “当前语句/具体表达式”由错误帧给出(见 TypeGenErr 的 CurArg 兜底)。
|
||||
# 仅在语句边界清掉上一条语句遗留的实参位置,避免兜底定位串味。
|
||||
if node is not None:
|
||||
self.Gen.TypeGenBase.set_cur_arg(None, 0, 0)
|
||||
if trans.TranslateDepth >= HT.TRANSLATE_MAX_DEPTH: self.Gen.TypeGenErr.fatal_error(node, "statement translation nested too deeply (likely an AST self-reference cycle or a translation bug)", "RecursionError")
|
||||
trans.TranslateDepth += 1
|
||||
mret: int = self._translate_stmt_impl(trans, node)
|
||||
@@ -113,7 +118,7 @@ novirtual class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
|
||||
builder: mlirlite.IRBuilder* = trans._cur_builder
|
||||
if pool is None or mod is None or builder is None: return 0
|
||||
tn: sizet = len(dnode.targets)
|
||||
for ti in range(tn):
|
||||
for ti in range(int(tn)):
|
||||
tgt: ast.AST* = dnode.targets.get(ti)
|
||||
if tgt is None: continue
|
||||
# del obj -> __delete__ (matches TPC HandlesDelete.py; no-op if undefined)
|
||||
@@ -171,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:
|
||||
@@ -265,7 +270,7 @@ novirtual class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
|
||||
except_bbs: list[mlirlite.BasicBlock*]* = list[mlirlite.BasicBlock*](pool, 8)
|
||||
match_bbs: list[mlirlite.BasicBlock*]* = list[mlirlite.BasicBlock*](pool, 8)
|
||||
if except_bbs is None or match_bbs is None: return 0
|
||||
for i_t in range(n_handlers_t):
|
||||
for i_t in range(int(n_handlers_t)):
|
||||
viperlib.snprintf(bb_buf_t, 64, "try.except.%d.%d", lc_t, i_t)
|
||||
eb_t: mlirlite.BasicBlock* = mlirlite.create_block(pool, func, bb_buf_t)
|
||||
viperlib.snprintf(bb_buf_t, 64, "try.match.%d.%d", lc_t, i_t)
|
||||
@@ -286,7 +291,7 @@ novirtual class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
|
||||
mlirlite.position_at_end(builder, try_bb)
|
||||
if tr.children is not None:
|
||||
tb_n: sizet = len(tr.children)
|
||||
for tbi in range(tb_n):
|
||||
for tbi in range(int(tb_n)):
|
||||
stmt_t: ast.AST* = tr.children.get(tbi)
|
||||
if stmt_t is not None: self.translate_stmt(trans, stmt_t)
|
||||
# try body completed: clear the code, go to else (if any) or After
|
||||
@@ -303,7 +308,7 @@ novirtual class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
|
||||
else:
|
||||
eh_code_val: mlirlite.Value* = mlirlite.build_load(builder, i32_ty_t, exception_code)
|
||||
next_check_t: mlirlite.BasicBlock* = None
|
||||
for i_t in range(n_handlers_t):
|
||||
for i_t in range(int(n_handlers_t)):
|
||||
hdr_t: ast.ExceptHandler* = ast.ExceptHandler*(tr.handlers.get(i_t))
|
||||
match_bb_t: mlirlite.BasicBlock* = match_bbs.get(i_t)
|
||||
if i_t > 0 and next_check_t is not None:
|
||||
@@ -332,7 +337,7 @@ novirtual class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
|
||||
codes_buf_t[0] = c0_t
|
||||
n_codes_t = 1
|
||||
match_cond_t: mlirlite.Value* = None
|
||||
for ci_t in range(n_codes_t):
|
||||
for ci_t in range(int(n_codes_t)):
|
||||
code_val_t: mlirlite.Value* = mlirlite.const_int32(pool, codes_buf_t[ci_t])
|
||||
is_match_t: mlirlite.Value* = mlirlite.build_icmp(builder, mlirlite.ICMP_EQ, eh_code_val, code_val_t)
|
||||
if match_cond_t is None: match_cond_t = is_match_t
|
||||
@@ -371,13 +376,13 @@ novirtual class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
|
||||
mlirlite.build_br(builder, after_bb)
|
||||
|
||||
# ---- 7. except bodies ----
|
||||
for i_t in range(n_handlers_t):
|
||||
for i_t in range(int(n_handlers_t)):
|
||||
hdr2_t: ast.ExceptHandler* = ast.ExceptHandler*(tr.handlers.get(i_t))
|
||||
except_bb2_t: mlirlite.BasicBlock* = except_bbs.get(i_t)
|
||||
mlirlite.position_at_end(builder, except_bb2_t)
|
||||
if hdr2_t is not None and hdr2_t.children is not None:
|
||||
hn2_t: sizet = len(hdr2_t.children)
|
||||
for hi2 in range(hn2_t):
|
||||
for hi2 in range(int(hn2_t)):
|
||||
stmt2_t: ast.AST* = hdr2_t.children.get(hi2)
|
||||
if stmt2_t is not None: self.translate_stmt(trans, stmt2_t)
|
||||
if mlirlite.builder_cur_block_is_terminated(builder) == 0: mlirlite.build_br(builder, after_bb)
|
||||
@@ -387,7 +392,7 @@ novirtual class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
|
||||
mlirlite.position_at_end(builder, else_bb)
|
||||
if tr.orelse is not None:
|
||||
en_t: sizet = len(tr.orelse)
|
||||
for ei in range(en_t):
|
||||
for ei in range(int(en_t)):
|
||||
stmt3_t: ast.AST* = tr.orelse.get(ei)
|
||||
if stmt3_t is not None: self.translate_stmt(trans, stmt3_t)
|
||||
if mlirlite.builder_cur_block_is_terminated(builder) == 0: mlirlite.build_br(builder, after_bb)
|
||||
@@ -527,7 +532,7 @@ novirtual class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
|
||||
string.memset(ctx_class_buf, 0, 8 * n)
|
||||
|
||||
# 1. all items: build context, call __enter__, bind asname
|
||||
for i in range(n):
|
||||
for i in range(int(n)):
|
||||
item_node: ast.AST* = items.get(i)
|
||||
if item_node is None: continue
|
||||
if item_node.kind() != ast.ASTKind.WithItem: continue
|
||||
@@ -591,12 +596,12 @@ novirtual class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
|
||||
body: list[ast.AST*]* = wt.children
|
||||
if body is not None:
|
||||
bn: sizet = len(body)
|
||||
for bi in range(bn):
|
||||
for bi in range(int(bn)):
|
||||
st: ast.AST* = body.get(bi)
|
||||
if st is not None: self.translate_stmt(trans, st)
|
||||
|
||||
# 2.5 FakeDuck: __provides__ metadata -> emit _pop_provider calls in reverse.
|
||||
for pi_pop in range(n):
|
||||
for pi_pop in range(int(n)):
|
||||
if ctx_class_buf[pi_pop] != 0:
|
||||
cls_pop: str = rawptr(ctx_class_buf[pi_pop])
|
||||
fd_cls_bare_p: str = cls_pop
|
||||
@@ -606,8 +611,8 @@ novirtual class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
|
||||
if fd_cls_bare_p is not None: self.Gen.CallGenFd._emit_provider_pop(trans, pool, builder, mod, fd_cls_bare_p)
|
||||
|
||||
# 3. call __exit__() in reverse order (Python with semantics: LIFO)
|
||||
for i in range(n):
|
||||
ci: int = n - 1 - i
|
||||
for i in range(int(n)):
|
||||
ci: int = int(n - 1 - i)
|
||||
if ctx_class_buf[ci] != 0:
|
||||
cls_ci: str = rawptr(ctx_class_buf[ci])
|
||||
self.Gen.VarGen.set_var_annot_class_name(trans.SymTab, "__with_ctx", cls_ci)
|
||||
|
||||
+2117
-2105
File diff suppressed because it is too large.
Load diff
+1007
-1008
File diff suppressed because it is too large.
Load diff
@@ -1,5 +1,4 @@
|
||||
import lib.core.Gen.Generator as Generator
|
||||
import t, c
|
||||
from stdint import *
|
||||
import ast
|
||||
import mlirlite
|
||||
@@ -548,7 +547,22 @@ novirtual class TranslatorGenMixin:
|
||||
# resolved struct value in Ptr; pure t.CStruct keeps a value instance.
|
||||
if resolved.kind() == mlirlite.LLVMType.Struct:
|
||||
gv_entry: GenTypes.StructEntry* = self.Gen.StructGen.find_struct_by_type(resolved)
|
||||
if gv_entry is not None and (gv_entry.HasNew != 0 or gv_entry.HasVTable != 0 or gv_entry.IsOOP != 0):
|
||||
# [TEMP-DIAG] 模块级 struct 全局的类型决策(定位完删除):
|
||||
# hasnew/hasvtable/isoop 任一为真 ⇒ 全局被包成 Ptr(指针槽),
|
||||
# 与"局部变量=值本体"不一致 ⇒ `CAM = V3(...)` 被当成把栈地址交给全局。
|
||||
gv_hn: int = -1; gv_hv: int = -1; gv_oo: int = -1
|
||||
if gv_entry is not None:
|
||||
gv_hn = gv_entry.HasNew; gv_hv = gv_entry.HasVTable; gv_oo = gv_entry.IsOOP
|
||||
stdio.printf("[GVAR-DIAG] name=%s hasnew=%d hasvtable=%d isoop=%d\n",
|
||||
var_name, gv_hn, gv_hv, gv_oo)
|
||||
if gv_entry is not None and gv_entry.HasNew != 0:
|
||||
# 只有定义了 `__new__`(自定义分配器,对象存储可能不在本帧)的类才**必须**
|
||||
# 以指针表示。仅"有方法 / 带 vtable"(IsOOP / HasVTable)的类,值本体内联
|
||||
# 存储同样可工作(方法调用把 &obj 作 self 传入,vptr 亦可内联在值里),
|
||||
# 因此模块级全局也按**值本位**存储,与局部变量(`alloca %T`)语义一致:
|
||||
# `CAM: V3` ⇒ `@CAM = global %V3`,`CAM = V3(...)` 即整块就地写入
|
||||
# (不再产生"把本帧对象地址存进全局"的悬垂,也不再让
|
||||
# check_return_frame_decl 误报 LifetimeError)。
|
||||
# Object-class global (object-as-pointer): remember the Struct value type
|
||||
# so a BSS storage global can hold the instance; otherwise @g is NULL.
|
||||
resolved_stg_ty = resolved
|
||||
@@ -595,8 +609,18 @@ novirtual class TranslatorGenMixin:
|
||||
init_sym_node = uop_g.operand
|
||||
init_kind = 5
|
||||
else:
|
||||
init_val = self.Gen.AssignGen.extract_cdefine_int_value(aa.value)
|
||||
init_kind = 1
|
||||
# 负浮点字面量 `-100.0` = UnaryOp(USub, Constant(float)):按**浮点初始化**
|
||||
# 处理(init_kind=4,值稍后从源码重读,故负号会被保留);
|
||||
# 否则会被送进整型常量提取器 ⇒ TypeError: CDefine value extraction failed。
|
||||
is_neg_f: int = 0
|
||||
if uop_g.op == ast.OpKind.USub and uop_g.operand is not None and uop_g.operand.kind() == ast.ASTKind.Constant:
|
||||
opf_d: ast.Constant* = ast.Constant*(uop_g.operand)
|
||||
if opf_d.const_kind == ast.CONST_FLOAT: is_neg_f = 1
|
||||
if is_neg_f != 0:
|
||||
init_kind = 4
|
||||
else:
|
||||
init_val = self.Gen.AssignGen.extract_cdefine_int_value(aa.value)
|
||||
init_kind = 1
|
||||
elif val_kind == ast.ASTKind.List:
|
||||
init_list_node = ast.List*(aa.value)
|
||||
init_kind = 2
|
||||
@@ -633,8 +657,18 @@ novirtual class TranslatorGenMixin:
|
||||
init_sym_node = uop_g2.operand
|
||||
init_kind = 5
|
||||
else:
|
||||
init_val = self.Gen.AssignGen.extract_cdefine_int_value(asgn.value)
|
||||
init_kind = 1
|
||||
# 同 AnnAssign 分支:负浮点字面量按**浮点初始化**处理;无注解时还要把类型
|
||||
# 推断为 double(否则默认 i32),否则 value 会被送进整型常量提取器 ⇒ fatal。
|
||||
is_neg_f2: int = 0
|
||||
if uop_g2.op == ast.OpKind.USub and uop_g2.operand is not None and uop_g2.operand.kind() == ast.ASTKind.Constant:
|
||||
opf_d2: ast.Constant* = ast.Constant*(uop_g2.operand)
|
||||
if opf_d2.const_kind == ast.CONST_FLOAT: is_neg_f2 = 1
|
||||
if is_neg_f2 != 0:
|
||||
init_kind = 4
|
||||
var_ty = mlirlite.Double(pool)
|
||||
else:
|
||||
init_val = self.Gen.AssignGen.extract_cdefine_int_value(asgn.value)
|
||||
init_kind = 1
|
||||
elif val_kind2 == ast.ASTKind.List:
|
||||
init_list_node = ast.List*(asgn.value)
|
||||
init_kind = 2
|
||||
@@ -727,12 +761,22 @@ novirtual class TranslatorGenMixin:
|
||||
viperlib.snprintf(init_buf, 48, "%lld", init_val)
|
||||
gv.Initializer = init_buf
|
||||
elif init_kind == 2:
|
||||
# List literal -> array initializer [N x elem_ty] [elem_ty val0, ...]
|
||||
arr_init: str = self._build_array_initializer_text(pool, var_ty, init_list_node)
|
||||
if arr_init is not None: gv.Initializer = arr_init
|
||||
# 空字面量 `= []`:整块清零 ⇒ 直接 zeroinitializer。
|
||||
# 若走下面的"逐元素展开",`[N]uint8` 这类大数组会生成**百万级**元素文本
|
||||
# (`[i8 0, i8 0, …]`)——既撞上 MLIR 侧上限(too many global array elements),
|
||||
# 又让每个元素 ~5KB 的渲染开销把编译池吃光
|
||||
# (现场:n=1048576、src_head=[i8 0, i8 0, …]、[MEM] 池耗尽)。
|
||||
ln_c: ast.List* = ast.List*(init_list_node)
|
||||
elts_c: list[ast.AST*]* = None
|
||||
if ln_c is not None: elts_c = ln_c.elts
|
||||
if init_list_node is None or elts_c is None or len(elts_c) == 0:
|
||||
gv.Initializer = "zeroinitializer"
|
||||
else:
|
||||
# Initializer build failed: error out (fail-fast) instead of falling back to zeroinitializer
|
||||
self.Gen.TypeGenErr.fatal_error(node, f"failed to build the initializer of module-level variable '{var_name}'", "TypeError")
|
||||
arr_init: str = self._build_array_initializer_text(pool, var_ty, init_list_node)
|
||||
if arr_init is not None: gv.Initializer = arr_init
|
||||
else:
|
||||
# Initializer build failed: error out (fail-fast) instead of falling back
|
||||
self.Gen.TypeGenErr.fatal_error(node, f"failed to build the initializer of module-level variable '{var_name}'", "TypeError")
|
||||
elif init_kind == 3:
|
||||
# String literal initializer: for array types (t.CArray[char, N]) use a byte-array
|
||||
# initializer (content + zero padding); a string-pointer GEP initializer would mismatch.
|
||||
|
||||
@@ -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
|
||||
@@ -28,6 +24,11 @@ novirtual class TypeGenBase:
|
||||
Gen: Generator.Generator*
|
||||
TraceFrames: GenTypes.TraceFrame* # malloc array (TypeGenMixin.TRACE_MAX slots)
|
||||
TraceCount: int
|
||||
# --- 当前实参位置:翻译调用实参时记录。报错节点是**合成**的(lineno==0,如构造器内联出的
|
||||
# 参数赋值)时,用它兜底定位到具体表达式(如 `&_on_submit`),而不是回退到函数定义行。---
|
||||
CurArgFile: str
|
||||
CurArgLine: int
|
||||
CurArgCol: int
|
||||
|
||||
# --- Global generic specialization context (was _g_generic_tp_names/_g_generic_type_args) ---
|
||||
GenericTpNames: list[str]*
|
||||
@@ -40,10 +41,13 @@ novirtual class TypeGenBase:
|
||||
# --- Global CDefine compile-time constant table (was _g_cdefine_*) ---
|
||||
CDefineNames: list[str]*
|
||||
CDefineValues: list[str]*
|
||||
# 浮点常量的**宽度种类**:1=f64(double,默认),2=f32(float);0=非浮点(整型/字符串)。
|
||||
# 判定"是不是浮点常量"一律用 `!= 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
|
||||
@@ -58,6 +62,7 @@ novirtual class TypeGenBase:
|
||||
# Not cleared by reset_to(mark2)/clear_cdefine_constants, so CONST_INT lookup happens once.
|
||||
XmodCdefNames: list[str]*
|
||||
XmodCdefValues: list[str]*
|
||||
# 同 CDefineDoubleFlags 的编码:1=f64,2=f32,0=非浮点。
|
||||
XmodCdefDoubleFlags: list[int]*
|
||||
XmodCdefStrFlags: list[int]*
|
||||
|
||||
@@ -107,6 +112,9 @@ novirtual class TypeGenBase:
|
||||
if self is None: return 1
|
||||
self.TraceFrames = None
|
||||
self.TraceCount = 0
|
||||
self.CurArgFile = None
|
||||
self.CurArgLine = 0
|
||||
self.CurArgCol = 0
|
||||
self.GenericTpNames = None
|
||||
self.GenericTypeArgs = None
|
||||
self.GenericCtxStack = None
|
||||
@@ -114,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
|
||||
@@ -199,6 +207,12 @@ novirtual class TypeGenBase:
|
||||
frame.lineno = 0
|
||||
frame.col_start = 0
|
||||
frame.col_end = 0
|
||||
# set_cur_arg - 记录正在翻译的实参位置(file/line/col),供“合成节点无行号”时兜底定位。
|
||||
def set_cur_arg(self, file_name: str, lineno: int, col_start: int) -> None:
|
||||
"""Remember the argument position currently being translated."""
|
||||
self.CurArgFile = file_name
|
||||
self.CurArgLine = lineno
|
||||
self.CurArgCol = col_start
|
||||
def clear_trace(self) -> None:
|
||||
"""Clear the trace stack (called before translating each file)."""
|
||||
for i in range(int(self.TraceCount)):
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -23,6 +17,14 @@ 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 CDEFINE_FLOAT_KIND_F32: int = 2
|
||||
|
||||
|
||||
# TypeGenCDef - split from TypeGenMixin (stage D): CDefine compile-time constant table (register/lookup/is/set/clear)
|
||||
novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
|
||||
@@ -148,8 +150,12 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
|
||||
if self.CDefineDoubleFlags is not None: self.CDefineDoubleFlags.append(0)
|
||||
if self.CDefineStrFlags is not None: self.CDefineStrFlags.append(0)
|
||||
if self.CDefineBits is not None: self.CDefineBits.append(bits)
|
||||
def register_cdefine_double(self, pool: memhub.MemBuddy*, name: str, value: double) -> None:
|
||||
"""Register a float CDefine constant; store the value as %f text and flag it."""
|
||||
def register_cdefine_double(self, pool: memhub.MemBuddy*, name: str, value: double, is_f32: int = 0) -> None:
|
||||
"""Register a float CDefine constant; store the value as %f text and flag it.
|
||||
|
||||
is_f32: 1 表示该常量声明为 32 位 float(`define PI: float = ...` / `float(...)`),
|
||||
物化时直接出 f32 常量;0(默认)表示 double。
|
||||
"""
|
||||
if name is None: return
|
||||
if self.CDefineNames is None:
|
||||
tp: memhub.MemBuddy* = self._cdefine_table_pool(pool)
|
||||
@@ -185,7 +191,10 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
|
||||
stdio.snprintf(val_buf, 32, "%.17g", value)
|
||||
self.CDefineValues.append(val_buf)
|
||||
else: self.CDefineValues.append("0")
|
||||
if self.CDefineDoubleFlags is not None: self.CDefineDoubleFlags.append(1)
|
||||
# 浮点宽度种类:f32=2,f64=1(判定处一律 `!= 0`,故与旧数据兼容)。
|
||||
if self.CDefineDoubleFlags is not None:
|
||||
if is_f32 != 0: self.CDefineDoubleFlags.append(CDEFINE_FLOAT_KIND_F32)
|
||||
else: self.CDefineDoubleFlags.append(CDEFINE_FLOAT_KIND_F64)
|
||||
if self.CDefineStrFlags is not None: self.CDefineStrFlags.append(0)
|
||||
if self.CDefineBits is not None: self.CDefineBits.append(0)
|
||||
# fail-fast: a failed CDefine value buffer must abort compilation
|
||||
@@ -241,7 +250,11 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
|
||||
self.CDefineFound = 1
|
||||
# Cross-module CDefine lookup cache (PermPool-allocated, reused across files)
|
||||
def register_xmod_cdefine(self, name: str, value_str: str, is_double: int, is_str: int) -> None:
|
||||
"""Cache a cross-module CDefine lookup result in the perm_pool global cache."""
|
||||
"""Cache a cross-module CDefine lookup result in the perm_pool global cache.
|
||||
|
||||
is_double 沿用 CDefineDoubleFlags 的编码(1=f64,2=f32,0=非浮点),
|
||||
这样跨模块读取 float 声明的常量时也能保持 f32。
|
||||
"""
|
||||
if name is None or value_str is None: return
|
||||
# Dedupe: skip if cached (first lookup wins; CDefines are globally unique)
|
||||
if self.XmodCdefNames is not None:
|
||||
@@ -285,7 +298,8 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
|
||||
if val_str is not None:
|
||||
self.CDefineFound = 1
|
||||
if self.XmodCdefStrFlags is not None and self.XmodCdefStrFlags.get(i) == 1: self.CDefineIsStr = 1
|
||||
elif self.XmodCdefDoubleFlags is not None and self.XmodCdefDoubleFlags.get(i) == 1: self.CDefineIsDouble = 1
|
||||
elif self.XmodCdefDoubleFlags is not None and self.XmodCdefDoubleFlags.get(i) != 0:
|
||||
self.CDefineIsDouble = int(self.XmodCdefDoubleFlags.get(i))
|
||||
return val_str
|
||||
return None
|
||||
def lookup_cdefine_constant(self, name: str) -> int64:
|
||||
@@ -316,8 +330,9 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
|
||||
# callers should use lookup_cdefine_string instead.
|
||||
self.CDefineIsStr = 1
|
||||
return 0
|
||||
if self.CDefineDoubleFlags is not None and self.CDefineDoubleFlags.get(i) == 1:
|
||||
self.CDefineIsDouble = 1
|
||||
if self.CDefineDoubleFlags is not None and self.CDefineDoubleFlags.get(i) != 0:
|
||||
# 浮点常量(f64=1 / f32=2):把种类原样带给 is_cdefine_double()/is_cdefine_float32()
|
||||
self.CDefineIsDouble = int(self.CDefineDoubleFlags.get(i))
|
||||
return string.atoll(val_str)
|
||||
return string.atoll(val_str)
|
||||
return 0
|
||||
@@ -368,8 +383,9 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
|
||||
if self.CDefineStrFlags is not None and self.CDefineStrFlags.get(i) == 1:
|
||||
self.CDefineIsStr = 1
|
||||
return 0.0
|
||||
if self.CDefineDoubleFlags is not None and self.CDefineDoubleFlags.get(i) == 1:
|
||||
self.CDefineIsDouble = 1
|
||||
if self.CDefineDoubleFlags is not None and self.CDefineDoubleFlags.get(i) != 0:
|
||||
# 种类原样带回(1=f64 / 2=f32),物化处据此决定出 f64 还是 f32 常量。
|
||||
self.CDefineIsDouble = int(self.CDefineDoubleFlags.get(i))
|
||||
ret_d: double = string.atof(val_str)
|
||||
return ret_d
|
||||
ret_i2: double = double(string.atoll(val_str))
|
||||
@@ -409,6 +425,18 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase):
|
||||
def is_cdefine_double(self) -> int:
|
||||
"""Whether the last CDefine hit was a float constant."""
|
||||
return self.CDefineIsDouble
|
||||
def is_cdefine_float32(self) -> int:
|
||||
"""Whether the last CDefine hit was an f32 (`float`) constant; 0 = f64/非浮点。
|
||||
|
||||
物化浮点常量时必须先看本函数:`define PI: float = ...` 要直接出 f32 常量,
|
||||
否则与 f32 字面量混算会被提升成 double(现场:33 处 cvtss2sd)。
|
||||
"""
|
||||
if self.CDefineIsDouble == CDEFINE_FLOAT_KIND_F32: return 1
|
||||
return 0
|
||||
def xmod_float_kind(self) -> int:
|
||||
"""跨模块缓存写入用的浮点种类(1=f64 / 2=f32):把编码留在本模块,写入方不必跨模块引用 define。"""
|
||||
if self.CDefineIsDouble == CDEFINE_FLOAT_KIND_F32: return CDEFINE_FLOAT_KIND_F32
|
||||
return CDEFINE_FLOAT_KIND_F64
|
||||
def is_cdefine_found(self) -> int:
|
||||
"""Whether the last CDefine lookup found a value."""
|
||||
return self.CDefineFound
|
||||
@@ -420,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).
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -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 w32.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
|
||||
@@ -47,10 +39,23 @@ novirtual class TypeGenErr(TypeGenBase.TypeGenBase):
|
||||
# 当前帧(错误点)位置:优先 node,其次调用栈最内层有行号的帧,最后 current_file。
|
||||
lineno: int = 0
|
||||
if node is not None: lineno = node.lineno
|
||||
raw_line: int = lineno # [TEMP-DIAG] 兜底前的原始节点行号(定位完删除)
|
||||
file_name: str = self.Gen.TypeGenResolve.get_current_file()
|
||||
if file_name is None: file_name = "<unknown>"
|
||||
# Line-number fallback: cloned/synthesized nodes may have lineno 0 (e.g. ast.Arg built by
|
||||
# generic specialization hardcodes 0). Fall back to the trace stack's last valid frame.
|
||||
# 报错位置兜底(“节点位置不可信”时优先用正在翻译的实参位置,精确定位到
|
||||
# `&_on_submit` 这类表达式;见 TypeGenBase.set_cur_arg)。判据取宽:节点无行号、
|
||||
# 或**没有更深的可用位置**(节点行号不深于栈最内层帧的行号 —— 函数体里的真实
|
||||
# 表达式行号必然大于函数定义行,而 FunctionDef 自身恰好等于定义行/更浅),
|
||||
# 或栈为空(顶层函数体翻译时可能没有帧)时,都采用实参位置。
|
||||
deeper_known: int = 0
|
||||
if lineno > 0 and self.Gen.TypeGenBase.TraceCount > 0:
|
||||
tfc: GenTypes.TraceFrame* = self.Gen.TypeGenBase._get_trace_frame(self.Gen.TypeGenBase.TraceCount - 1)
|
||||
if tfc is not None and tfc.lineno > 0 and lineno > tfc.lineno: deeper_known = 1
|
||||
if (lineno <= 0 or deeper_known == 0) and self.Gen.TypeGenBase.CurArgLine > 0:
|
||||
if self.Gen.TypeGenBase.CurArgFile is not None:
|
||||
file_name = self.Gen.TypeGenBase.CurArgFile
|
||||
lineno = self.Gen.TypeGenBase.CurArgLine
|
||||
col_start = self.Gen.TypeGenBase.CurArgCol
|
||||
if lineno <= 0 and self.Gen.TypeGenBase.TraceCount > 0:
|
||||
ti_b: int = self.Gen.TypeGenBase.TraceCount - 1
|
||||
while ti_b >= 0:
|
||||
@@ -72,9 +77,17 @@ 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)
|
||||
Diag.begin(ent, err_type, msg)
|
||||
# 调用链帧(按"自外向内"顺序)**由本类遍历**(只有它拿得到 TraceFrame),填入 entry。
|
||||
for i in range(self.Gen.TypeGenBase.TraceCount):
|
||||
for i in range(int(self.Gen.TypeGenBase.TraceCount)):
|
||||
frame: GenTypes.TraceFrame* = self.Gen.TypeGenBase._get_trace_frame(i)
|
||||
if frame is not None and frame.file_name is not None:
|
||||
Diag.add_frame(ent, frame.file_name, frame.lineno)
|
||||
@@ -102,10 +115,19 @@ novirtual class TypeGenErr(TypeGenBase.TypeGenBase):
|
||||
# The subscript test uses TypeGenResolve as the single source of truth (the t.Callable
|
||||
# Attribute form is equivalent to the bare-name callable / Callable forms)
|
||||
if self.Gen.TypeGenResolve._IsCallableSubscriptValue(sub_cr.value) == 0: return None
|
||||
# slice must be Tuple(elts=[args_list, ret_node])
|
||||
# slice must be Tuple(elts=[args_list, ret_node]); the one-element form
|
||||
# `callable[[A, B]]` omits the return type and means the default int (i32) — the same
|
||||
# degradation a missing annotation gets elsewhere. It must still register, otherwise the
|
||||
# indirect call fails fast with "Return type ... is not registered".
|
||||
if sub_cr.slice is None or sub_cr.slice.kind() != ast.ASTKind.Tuple: return None
|
||||
tp_cr: ast.Tuple* = ast.Tuple*(sub_cr.slice)
|
||||
if tp_cr.elts is None or len(tp_cr.elts) != 2: return None
|
||||
if tp_cr.elts is None: return None
|
||||
# The one-element form `callable[[A, B]]` omits the return type, which means **void**
|
||||
# (no return value). It must still register, so the indirect call `f()` is statically
|
||||
# typed as void and using it as a value (e.g. `v: int = f()`) is rejected instead of
|
||||
# silently assuming int.
|
||||
if len(tp_cr.elts) == 1: return mlirlite.Void(pool)
|
||||
if len(tp_cr.elts) != 2: return None
|
||||
ret_node_cr: ast.AST* = tp_cr.elts.get(1)
|
||||
if ret_node_cr is None: return None
|
||||
return self.Gen.TypeGenResolve.resolve_annotation_type(pool, ret_node_cr, imported_modules, from_imports, trans, 0)
|
||||
@@ -214,7 +236,7 @@ novirtual class TypeGenErr(TypeGenBase.TypeGenBase):
|
||||
|
||||
ret_ty: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
||||
bn: sizet = len(body)
|
||||
for bi in range(bn):
|
||||
for bi in range(int(bn)):
|
||||
stmt: ast.AST* = body.get(bi)
|
||||
if stmt is not None and stmt.kind() == ast.ASTKind.Return:
|
||||
rt: ast.Return* = ast.Return*(stmt)
|
||||
@@ -229,7 +251,7 @@ novirtual class TypeGenErr(TypeGenBase.TypeGenBase):
|
||||
"""Whether the body has a return of a non-None value, recursively checking control-flow statements but skipping nested defs."""
|
||||
if body is None: return 0
|
||||
bn: sizet = len(body)
|
||||
for bi in range(bn):
|
||||
for bi in range(int(bn)):
|
||||
stmt: ast.AST* = body.get(bi)
|
||||
if stmt is None: continue
|
||||
k: int = stmt.kind()
|
||||
@@ -260,7 +282,7 @@ novirtual class TypeGenErr(TypeGenBase.TypeGenBase):
|
||||
# name + ':' + bits + ',', plus NUL. Use an upper bound, not an exact size.
|
||||
bound: sizet = 1 # trailing NUL
|
||||
pre_i: sizet
|
||||
for pre_i in range(an):
|
||||
for pre_i in range(int(an)):
|
||||
pre_arg: ast.Arg* = ast.Arg*(alist.get(pre_i))
|
||||
if pre_arg is None or pre_arg.arg is None: continue
|
||||
bound += string.strlen(pre_arg.arg) + 1 + 12 + 1
|
||||
@@ -271,7 +293,7 @@ novirtual class TypeGenErr(TypeGenBase.TypeGenBase):
|
||||
# 2) Single pass: resolve type -> format entry in place -> append immediately (no truncation)
|
||||
cnt: sizet = 0
|
||||
i32_ty: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
||||
for ai in range(an):
|
||||
for ai in range(int(an)):
|
||||
arg: ast.Arg* = ast.Arg*(alist.get(ai))
|
||||
if arg is None or arg.arg is None: continue
|
||||
param_ty: mlirlite.LLVMType* = i32_ty
|
||||
|
||||
@@ -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 w32.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
|
||||
|
||||
|
||||
|
||||
+2686
-2648
File diff suppressed because it is too large.
Load diff
@@ -1,4 +1,3 @@
|
||||
import t, c
|
||||
from stdint import *
|
||||
import mlirlite
|
||||
import memhub
|
||||
@@ -42,7 +41,7 @@ def _spec_name_eq(text: str, start: sizet, end: sizet, lit: str) -> int:
|
||||
if end <= start: return 0
|
||||
ln: sizet = string.strlen(lit)
|
||||
if end - start != ln: return 0
|
||||
for k in range(ln):
|
||||
for k in range(int(ln)):
|
||||
if text[start + k] != lit[k]: return 0
|
||||
return 1
|
||||
|
||||
@@ -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*
|
||||
@@ -185,7 +182,7 @@ def _spec_index_find(arena: TypeSpecArena*, key: str, h: uint64) -> int:
|
||||
if arena.Index is None or arena.IndexCap <= 0: return -1
|
||||
mask: sizet = sizet(arena.IndexCap - 1)
|
||||
idx: sizet = sizet(h) & mask
|
||||
for i in range(arena.IndexCap):
|
||||
for i in range(int(arena.IndexCap)):
|
||||
slot: sizet* = (sizet*)(uint64(arena.Index) + uint64(idx * 8))
|
||||
sv: sizet = slot[0]
|
||||
if sv == 0: return -1
|
||||
@@ -203,7 +200,7 @@ def _spec_index_insert(arena: TypeSpecArena*, key: str, h: uint64, ei: int) -> i
|
||||
if arena.Index is None or arena.IndexCap <= 0: return 1
|
||||
mask: sizet = sizet(arena.IndexCap - 1)
|
||||
idx: sizet = sizet(h) & mask
|
||||
for i in range(arena.IndexCap):
|
||||
for i in range(int(arena.IndexCap)):
|
||||
slot: sizet* = (sizet*)(uint64(arena.Index) + uint64(idx * 8))
|
||||
if slot[0] == 0:
|
||||
slot[0] = sizet(ei + 1)
|
||||
@@ -422,7 +419,7 @@ def _spec_to_text(sp: TypeSpec*, buf: str, buf_size: sizet, depth: int) -> int:
|
||||
pos: sizet = 0
|
||||
pos = _spec_text_append(buf, buf_size, pos, "fn(")
|
||||
if pos >= buf_size: return 1
|
||||
for i in range(sp.ParamCount):
|
||||
for i in range(int(sp.ParamCount)):
|
||||
if i > 0:
|
||||
pos = _spec_text_append(buf, buf_size, pos, ",")
|
||||
if pos >= buf_size: return 1
|
||||
@@ -455,7 +452,7 @@ def _spec_text_append(buf: str, buf_size: sizet, pos: sizet, text: str) -> sizet
|
||||
"""Append text at pos in buf; buf_size sentinel when full, else the new position"""
|
||||
if buf is None or text is None: return buf_size
|
||||
tlen: sizet = string.strlen(text)
|
||||
for i in range(tlen):
|
||||
for i in range(int(tlen)):
|
||||
if pos + 1 >= buf_size:
|
||||
buf[pos] = '\0'
|
||||
return buf_size
|
||||
@@ -618,7 +615,7 @@ def InternSpec(arena: TypeSpecArena*, sp: TypeSpec*) -> TypeSpec*:
|
||||
return (TypeSpec*)(uint64(slot_e[0]))
|
||||
if arena.Index is None:
|
||||
# index unavailable: fall back to the linear scan
|
||||
for i in range(arena.Count):
|
||||
for i in range(int(arena.Count)):
|
||||
exist_key: str = _spec_key_at(arena.Keys, i)
|
||||
if exist_key is not None and string.strcmp(exist_key, key_buf) == 0:
|
||||
slot_e2: sizet* = (sizet*)(uint64(arena.Specs) + uint64(i * 8))
|
||||
@@ -871,7 +868,7 @@ def _spec_llvm_text(sp: TypeSpec*, buf: str, buf_size: sizet, depth: int) -> int
|
||||
if sp.Kind == TS_FUNC:
|
||||
if _spec_llvm_text(sp.Ret, buf, buf_size, depth + 1) != 0: return 1
|
||||
if _spec_llvm_append(buf, buf_size, " (") != 0: return 1
|
||||
for i in range(sp.ParamCount):
|
||||
for i in range(int(sp.ParamCount)):
|
||||
if i > 0:
|
||||
if _spec_llvm_append(buf, buf_size, ", ") != 0: return 1
|
||||
ps: TypeSpec* = _spec_param_at(sp, i)
|
||||
|
||||
@@ -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
|
||||
@@ -216,6 +215,23 @@ novirtual class VarGenMixin:
|
||||
if entry is None: return 0
|
||||
return entry.IsConst
|
||||
|
||||
# set_var_byte_order - register a local variable's byte order (VarEntry.ByteOrder) from the
|
||||
# `T | t.BigEndian` / `T | t.LittleEndian` annotation union: 1=BigEndian / 2=LittleEndian.
|
||||
# The value is only 1/2 when a marker is present, so the local load/store paths bswap only then.
|
||||
def set_var_byte_order(self, symtab: GenTypes.SymbolTable*, name: str, byte_order: int) -> int:
|
||||
"""Set a local variable's byte order (VarEntry.ByteOrder), return 0=success / 1=failure"""
|
||||
if symtab is None or name is None: return 1
|
||||
entry: GenTypes.VarEntry* = self.lookup_var_entry(symtab, name)
|
||||
if entry is None: return 1
|
||||
entry.ByteOrder = byte_order
|
||||
return 0
|
||||
|
||||
# var_byte_order - whether a variable is big/little-endian (unified read entry for load/store).
|
||||
def var_byte_order(self, entry: GenTypes.VarEntry*) -> int:
|
||||
"""The variable entry's byte order (0=native / 1=BigEndian / 2=LittleEndian; 0 when None)"""
|
||||
if entry is None: return 0
|
||||
return entry.ByteOrder
|
||||
|
||||
# ============================================================
|
||||
# 生命期标签读写(LifeTag,见 GenTypes define LIFE_*;架构见 ownership-and-new.md §11)
|
||||
# ============================================================
|
||||
@@ -320,6 +336,8 @@ novirtual class VarGenMixin:
|
||||
entry.IsConst = 0
|
||||
# 生命期标签显式初始化:未登记 = LIFE_UNKNOWN(保守放行)。
|
||||
entry.Life = GenTypes.LIFE_UNKNOWN
|
||||
# 字节序显式初始化:未标注 = 0 (native),只有 set_var_byte_order 才置 1/2(避免槽复用残留)。
|
||||
entry.ByteOrder = 0
|
||||
# [S3 dual write] Value-type Spec (alloca.Ty is Ptr(value type), so take its pointee)
|
||||
entry.Spec = self._spec_of_var_alloca(alloca)
|
||||
return 0
|
||||
@@ -915,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
|
||||
@@ -946,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
|
||||
@@ -1328,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
|
||||
@@ -1517,7 +1535,15 @@ novirtual class VarGenMixin:
|
||||
return 1
|
||||
cur_val: mlirlite.Value* = self.repl_try_read(trans, name, node)
|
||||
if cur_val is None: return 1
|
||||
rhs_val: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, trans.Module, rhs_node, None, 0, trans)
|
||||
# 与 `x = x op y` 同口径:RHS 在左值槽的 LLVM 类型(target_ty)上下文中翻译,使嵌套 binop /
|
||||
# CDefine 的目标感知与常量适配生效,避免 REPL 会话变量的增强赋值与二元形态口径不一致
|
||||
# (对齐 AssignGenMixin 的字段/下标/解引用/变量四个站点)。cur_val 已由 repl_try_read 成功
|
||||
# 加载,故槽类型必可解析;缺失即内部错误 fail-fast(绝不退回 None 或 i32 兜底)。
|
||||
slot_ty: mlirlite.LLVMType* = self.repl_slot_ty(pool, entry)
|
||||
if slot_ty is None:
|
||||
self.Gen.TypeGenErr.fatal_error(node, f"REPL: the slot type of session variable '{name}' is not available", "TypeError")
|
||||
return 1
|
||||
rhs_val: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, trans.Module, rhs_node, None, 0, trans, slot_ty)
|
||||
if rhs_val is None:
|
||||
self.Gen.TypeGenErr.fatal_error(node, "REPL: failed to translate the right-hand side of the augmented assignment", "TypeError")
|
||||
return 1
|
||||
|
||||
+404
-393
@@ -1,393 +1,404 @@
|
||||
import t, c
|
||||
from stdint import *
|
||||
import memhub
|
||||
import string
|
||||
import stdio
|
||||
import stdlib
|
||||
import w32.win32file as win32file
|
||||
import w32.win32base as win32base
|
||||
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/
|
||||
# FindNextFileA); collects .vp paths and SHA1s for Phase1 stub gen / Phase2 merge.
|
||||
|
||||
# global mbuddy pointer
|
||||
_mbuddy: memhub.MemBuddy*
|
||||
|
||||
# max file path length
|
||||
define MAX_PATH_LEN = 512
|
||||
# max files per scan
|
||||
define MAX_FILES = 1024
|
||||
|
||||
|
||||
# FileEntry - file entry (path + SHA1)
|
||||
novirtual class FileEntry:
|
||||
"""A scanned file entry."""
|
||||
Path: str # absolute file path
|
||||
Sha1: str # first 16 SHA1 chars (16 bytes + null)
|
||||
RelPath: str # path relative to the includes root
|
||||
ModuleName: str # module name (e.g. "ast.parser")
|
||||
IncDir: str # owning includes root dir (multi-root support)
|
||||
|
||||
|
||||
# ScanResult - scan result
|
||||
novirtual class ScanResult:
|
||||
"""A collection of scan results."""
|
||||
Entries: FileEntry* # FileEntry array
|
||||
Count: int
|
||||
Capacity: int
|
||||
|
||||
|
||||
# create_scan_result - create the scan result container
|
||||
def create_scan_result(pool: memhub.MemBuddy*) -> ScanResult*:
|
||||
"""Create the scan result container with MAX_FILES preallocated slots."""
|
||||
if pool is None: return None
|
||||
size: sizet = MAX_FILES * FileEntry.__sizeof__()
|
||||
# use stdlib.malloc to avoid exhausting the mbuddy pool (consistent with StubMerger)
|
||||
entries: FileEntry* = stdlib.malloc(size)
|
||||
if entries is None: return None
|
||||
string.memset(entries, 0, size)
|
||||
|
||||
result: ScanResult* = stdlib.malloc(ScanResult.__sizeof__())
|
||||
if result is None: return None
|
||||
string.memset(result, 0, ScanResult.__sizeof__())
|
||||
result.Entries = entries
|
||||
result.Count = 0
|
||||
result.Capacity = MAX_FILES
|
||||
return result
|
||||
|
||||
|
||||
# add_file_entry - append a file entry to the scan result
|
||||
def add_file_entry(result: ScanResult*, pool: memhub.MemBuddy*, abs_path: str, rel_path: str, sha1: str, inc_dir: str) -> int:
|
||||
"""Append a file entry to the scan result; inc_dir is the owning includes root."""
|
||||
if result is None or pool is None: return 1
|
||||
if result.Count >= result.Capacity: return 1
|
||||
|
||||
# compute module name: '/' -> '.', strip .vp/.py (Utils handles __init__.vp)
|
||||
mod_buf: str = Utils.PathToModuleName(rel_path, 0)
|
||||
if mod_buf is None: return 1
|
||||
|
||||
# get the entry address
|
||||
entry_size: sizet = FileEntry.__sizeof__()
|
||||
entry_addr: uint64 = uint64(result.Entries) + result.Count * entry_size
|
||||
entry: FileEntry* = (FileEntry*)(entry_addr)
|
||||
if entry is None: return 1
|
||||
|
||||
# copy the path string
|
||||
abs_len: sizet = string.strlen(abs_path)
|
||||
abs_buf: str = stdlib.malloc(abs_len + 1)
|
||||
if abs_buf is None: return 1
|
||||
string.strcpy(abs_buf, abs_path)
|
||||
entry.Path = abs_buf
|
||||
|
||||
# copy the SHA1
|
||||
sha1_buf: str = stdlib.malloc(17)
|
||||
if sha1_buf is None: return 1
|
||||
string.strcpy(sha1_buf, sha1)
|
||||
entry.Sha1 = sha1_buf
|
||||
|
||||
# copy the relative path
|
||||
rel_buf: str = stdlib.malloc(string.strlen(rel_path) + 1)
|
||||
if rel_buf is None: return 1
|
||||
string.strcpy(rel_buf, rel_path)
|
||||
entry.RelPath = rel_buf
|
||||
|
||||
# copy the module name
|
||||
entry.ModuleName = mod_buf
|
||||
|
||||
# copy the owning includes root dir (used by Phase2 path joining)
|
||||
if inc_dir is not None:
|
||||
inc_len: sizet = string.strlen(inc_dir)
|
||||
inc_buf: str = stdlib.malloc(inc_len + 1)
|
||||
if inc_buf is None: return 1
|
||||
string.strcpy(inc_buf, inc_dir)
|
||||
entry.IncDir = inc_buf
|
||||
|
||||
result.Count += 1
|
||||
return 0
|
||||
|
||||
|
||||
# compute_file_sha1 - read file content and compute SHA1 (CRLF->LF, matching Projectrans.py)
|
||||
def compute_file_sha1(pool: memhub.MemBuddy*, file_path: str) -> str:
|
||||
"""Read a file, normalize CRLF to LF, and return the first 16 SHA1 hex chars."""
|
||||
if pool is None or file_path is None: return None
|
||||
|
||||
# read file content; BUF_SIZE must match SRC_BUF_SIZE (1MB) or trailing edits won't change SHA1
|
||||
BUF_SIZE: sizet = 1048576
|
||||
buf: bytes = Utils.read_file_to_buf(file_path, BUF_SIZE)
|
||||
if buf is None: return None
|
||||
|
||||
# strip \r in place (CRLF -> LF), matching Projectrans.py text-mode reads
|
||||
buf_len: sizet = string.strlen(buf)
|
||||
write_pos: sizet = 0
|
||||
for ch in buf:
|
||||
if ch != '\r':
|
||||
buf[write_pos] = ch
|
||||
write_pos += 1
|
||||
|
||||
# add a null terminator
|
||||
buf[write_pos] = 0
|
||||
|
||||
# compute SHA1
|
||||
ctx: hashlib.sha1* = hashlib.sha1()
|
||||
if ctx is None:
|
||||
stdlib.free(buf)
|
||||
return None
|
||||
ctx.update(buf)
|
||||
|
||||
digest: bytes = stdlib.malloc(hashlib.SHA1_DIGEST_LEN)
|
||||
if digest is None:
|
||||
stdlib.free(buf)
|
||||
return None
|
||||
ctx.final(digest)
|
||||
|
||||
# convert to hex (first 8 bytes = 16 hex chars; stdlib allocation unchanged)
|
||||
hex_buf: str = Utils.bytes_to_hex(pool, digest, 8, 0, 1)
|
||||
stdlib.free(buf)
|
||||
stdlib.free(digest)
|
||||
return hex_buf
|
||||
|
||||
|
||||
# scan_directory_recursive - recursively scan a directory (FindFirstFileA/FindNextFileA)
|
||||
def scan_directory_recursive(pool: memhub.MemBuddy*, root_dir: str, rel_prefix: str, result: ScanResult*, inc_dir: str) -> int:
|
||||
"""Recursively collect .vp/.py files under a directory; inc_dir is the includes root."""
|
||||
if pool is None or root_dir is None or result is None: return 1
|
||||
# subdir recursion failure count (not ignored; fail-fast at the summary)
|
||||
subdir_failed: int = 0
|
||||
|
||||
# build search pattern: root_dir/*
|
||||
root_len: sizet = string.strlen(root_dir)
|
||||
pattern: bytes = stdlib.malloc(root_len + 4)
|
||||
if pattern is None: return 1
|
||||
viperlib.snprintf(pattern, root_len + 4, "%s/*", root_dir)
|
||||
|
||||
# find_data buffer +16 slack: TPV's WIN32_FIND_DATAA.__sizeof__() undercounts Windows writes
|
||||
fd_size_s: sizet = win32file.WIN32_FIND_DATAA.__sizeof__()
|
||||
find_data: win32file.WIN32_FIND_DATAA* = stdlib.malloc(fd_size_s + 16)
|
||||
if find_data is None: return 1
|
||||
string.memset(find_data, 0, fd_size_s + 16)
|
||||
|
||||
handle: win32base.HANDLE = win32file.FindFirstFileA(pattern, find_data)
|
||||
if handle == win32base.INVALID_HANDLE_VALUE: return 1
|
||||
|
||||
# iterate all files and subdirectories
|
||||
while True:
|
||||
# skip . and ..
|
||||
fname: str = find_data.cFileName
|
||||
if fname is not None:
|
||||
fname0: char = fname[0]
|
||||
if fname0 == '.':
|
||||
fname1: char = fname[1]
|
||||
if fname1 == '\0':
|
||||
# "."
|
||||
if win32file.FindNextFileA(handle, find_data) == 0: break
|
||||
continue
|
||||
elif fname1 == '.':
|
||||
fname2: char = fname[2]
|
||||
if fname2 == '\0':
|
||||
# ".."
|
||||
if win32file.FindNextFileA(handle, find_data) == 0: break
|
||||
continue
|
||||
|
||||
# check whether it is a directory
|
||||
is_dir: int = find_data.dwFileAttributes & win32file.FILE_ATTRIBUTE_DIRECTORY
|
||||
|
||||
# build the full path
|
||||
fname_len: sizet = string.strlen(fname)
|
||||
full_path: bytes = Utils.JoinPath(root_dir, fname)
|
||||
if full_path is None: break
|
||||
|
||||
# build the relative path
|
||||
prefix_len: sizet = 0
|
||||
if rel_prefix is not None: prefix_len = string.strlen(rel_prefix)
|
||||
rel_path: bytes = None
|
||||
if rel_prefix is not None and prefix_len > 0: rel_path = Utils.JoinPath(rel_prefix, fname)
|
||||
else:
|
||||
rel_path = stdlib.malloc(fname_len + 1)
|
||||
if rel_path is None: break
|
||||
string.strcpy(rel_path, fname)
|
||||
if rel_path is None: break
|
||||
|
||||
if is_dir != 0:
|
||||
# recurse into the subdirectory
|
||||
if scan_directory_recursive(pool, full_path, rel_path, result, inc_dir) != 0: subdir_failed += 1
|
||||
else:
|
||||
# check for a .vp source file (Viper uses .vp only; no longer .py)
|
||||
is_py: int = 0
|
||||
if Utils.is_vp_at(fname, 0, fname_len) != 0: is_py = 1
|
||||
|
||||
if is_py != 0:
|
||||
# skip files under __pycache__
|
||||
is_pycache: int = 0
|
||||
if rel_prefix is not None:
|
||||
if string.strcmp(rel_prefix, "__pycache__") == 0: is_pycache = 1
|
||||
|
||||
if is_pycache == 0:
|
||||
# compute SHA1
|
||||
sha1: str = compute_file_sha1(pool, full_path)
|
||||
if sha1 is not None:
|
||||
add_file_entry(result, pool, full_path, rel_path, sha1, inc_dir)
|
||||
VLogger.debug(f"scan: {rel_path} -> {sha1}", "scan")
|
||||
else: VLogger.warning(f"SHA1 computation failed: {full_path}", "scan")
|
||||
|
||||
# continue to the next file
|
||||
if win32file.FindNextFileA(handle, find_data) == 0: break
|
||||
|
||||
win32file.FindClose(handle)
|
||||
if subdir_failed > 0: VLogger.error(f"subdirectory scan failed for {subdir_failed} entries", "scan")
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
# scan_includes - scan entry point: return all .vp file paths and SHA1s
|
||||
def scan_includes(pool: memhub.MemBuddy*, includes_dirs: str) -> ScanResult*:
|
||||
"""Scan space-separated includes dirs and return all scanned .vp/.py files."""
|
||||
if pool is None or includes_dirs is None: return None
|
||||
|
||||
result: ScanResult* = create_scan_result(pool)
|
||||
if result is None: return None
|
||||
|
||||
# parse space-separated includes dirs, recursively scanning and merging each
|
||||
dirs_total: sizet = string.strlen(includes_dirs)
|
||||
d_i: sizet = 0
|
||||
while d_i < dirs_total:
|
||||
while d_i < dirs_total and includes_dirs[d_i] == ' ': d_i += 1
|
||||
if d_i >= dirs_total: break
|
||||
d_start: sizet = d_i
|
||||
while d_i < dirs_total and includes_dirs[d_i] != ' ': d_i += 1
|
||||
d_len: sizet = d_i - d_start
|
||||
if d_len == 0: continue
|
||||
dir_buf: bytes = stdlib.malloc(d_len + 1)
|
||||
if dir_buf is None: VLogger.error("failed to allocate the includes directory; aborting scan", "scan")
|
||||
string.strncpy(dir_buf, includes_dirs + d_start, d_len)
|
||||
dir_buf[d_len] = '\0'
|
||||
scan_directory_recursive(pool, dir_buf, None, result, dir_buf)
|
||||
stdlib.free(dir_buf)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
# find_entry_by_module - find a file entry by module name
|
||||
def find_entry_by_module(result: ScanResult*, module_name: str) -> FileEntry*:
|
||||
"""Find a file entry by module name."""
|
||||
if result is None or module_name is None: return None
|
||||
entry_size: sizet = FileEntry.__sizeof__()
|
||||
for i in range(result.Count):
|
||||
entry_addr: uint64 = uint64(result.Entries) + i * entry_size
|
||||
entry: FileEntry* = (FileEntry*)(entry_addr)
|
||||
if entry is not None:
|
||||
if entry.ModuleName is not None:
|
||||
if string.strcmp(entry.ModuleName, module_name) == 0: return entry
|
||||
return None
|
||||
|
||||
|
||||
# _PathExistsA - path existence check (GetFileAttributesA)
|
||||
def _PathExistsA(path: str) -> int:
|
||||
"""Return 1 if the path exists, else 0, using GetFileAttributesA."""
|
||||
if path is None: return 0
|
||||
attr: ULONG = win32file.GetFileAttributesA(path)
|
||||
if attr == 0xFFFFFFFF: # INVALID_FILE_ATTRIBUTES
|
||||
return 0
|
||||
return 1
|
||||
|
||||
|
||||
# _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:
|
||||
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)
|
||||
# candidate absolute path: {inc}/{rel}{suffix} (hand-built to avoid %. *s)
|
||||
cand_len: sizet = inc_len + 1 + rel_len + suf_len
|
||||
cand: bytes = stdlib.malloc(cand_len + 1)
|
||||
if cand is None: return 0
|
||||
string.strcpy(cand, inc_dir)
|
||||
cand[inc_len] = '/'
|
||||
string.strncpy(cand + inc_len + 1, rel_seg, rel_len)
|
||||
string.strcpy(cand + inc_len + 1 + rel_len, suffix)
|
||||
if _PathExistsA(cand) == 0:
|
||||
stdlib.free(cand)
|
||||
return 0
|
||||
# path relative to the includes root: {rel}{suffix}
|
||||
rel_path: bytes = stdlib.malloc(rel_len + suf_len + 1)
|
||||
if rel_path is None:
|
||||
stdlib.free(cand)
|
||||
return 0
|
||||
string.strncpy(rel_path, rel_seg, rel_len)
|
||||
rel_path[rel_len] = '\0'
|
||||
string.strcat(rel_path, suffix)
|
||||
# Deep-copy the owning includes root dir; allocate everything before writing
|
||||
# slots to avoid a half-written state when inc allocation fails.
|
||||
inc_buf: bytes = None
|
||||
if out_inc_dir is not None:
|
||||
inc_buf = stdlib.malloc(inc_len + 1)
|
||||
if inc_buf is None:
|
||||
stdlib.free(cand)
|
||||
stdlib.free(rel_path)
|
||||
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.
|
||||
if out_abs_path is not None:
|
||||
abs_slot: sizet* = (sizet*)(out_abs_path)
|
||||
abs_slot[0] = sizet(cand)
|
||||
else: stdlib.free(cand)
|
||||
if out_rel_path is not None:
|
||||
rel_slot: sizet* = (sizet*)(out_rel_path)
|
||||
rel_slot[0] = sizet(rel_path)
|
||||
else: stdlib.free(rel_path)
|
||||
if out_inc_dir is not None and inc_buf is not None:
|
||||
inc_slot: sizet* = (sizet*)(out_inc_dir)
|
||||
inc_slot[0] = sizet(inc_buf)
|
||||
return 1
|
||||
|
||||
|
||||
# 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:
|
||||
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
|
||||
# mod_name -> relative path segment ('.' -> '/'): ast.parser -> ast/parser
|
||||
rel_seg: bytes = stdlib.malloc(mn_len + 1)
|
||||
if rel_seg is None: return 1
|
||||
rel_len: sizet = 0
|
||||
for mi in range(mn_len):
|
||||
ch_m: char = mod_name[mi]
|
||||
if ch_m == '.': rel_seg[rel_len] = '/'
|
||||
else: rel_seg[rel_len] = ch_m
|
||||
rel_len += 1
|
||||
rel_seg[rel_len] = '\0'
|
||||
|
||||
found: int = 0
|
||||
d_total: sizet = string.strlen(includes_dirs)
|
||||
d_i: sizet = 0
|
||||
while d_i < d_total:
|
||||
while d_i < d_total and includes_dirs[d_i] == ' ': d_i += 1
|
||||
if d_i >= d_total: break
|
||||
d_start: sizet = d_i
|
||||
while d_i < d_total and includes_dirs[d_i] != ' ': d_i += 1
|
||||
d_len: sizet = d_i - d_start
|
||||
if d_len == 0: continue
|
||||
inc_dir_buf: bytes = stdlib.malloc(d_len + 1)
|
||||
if inc_dir_buf is None: continue
|
||||
string.strncpy(inc_dir_buf, includes_dirs + d_start, d_len)
|
||||
inc_dir_buf[d_len] = '\0'
|
||||
# candidate 1: {inc}/{rel}.vp (file module)
|
||||
if _TryResolveModuleFile(inc_dir_buf, rel_seg, rel_len, ".vp", out_abs_path, out_rel_path, out_inc_dir) != 0:
|
||||
found = 1
|
||||
stdlib.free(inc_dir_buf)
|
||||
break
|
||||
# candidate 2: {inc}/{rel}/__init__.vp (package module)
|
||||
if _TryResolveModuleFile(inc_dir_buf, rel_seg, rel_len, "/__init__.vp", out_abs_path, out_rel_path, out_inc_dir) != 0:
|
||||
found = 1
|
||||
stdlib.free(inc_dir_buf)
|
||||
if found != 0: break
|
||||
stdlib.free(rel_seg)
|
||||
if found != 0: return 0
|
||||
return 1
|
||||
from stdint import *
|
||||
import memhub
|
||||
import string
|
||||
import stdlib
|
||||
import win32.win32file as win32file
|
||||
import win32.win32base as win32base
|
||||
import win32.fileio as fileio
|
||||
import hashlib
|
||||
import viperlib
|
||||
import lib.Projectrans.Utils as Utils
|
||||
import lib.core.VLogger as VLogger
|
||||
|
||||
|
||||
# IncludesScanner - recursive scan of the includes directory (Win32 FindFirstFileA/
|
||||
# FindNextFileA); collects .vp paths and SHA1s for Phase1 stub gen / Phase2 merge.
|
||||
|
||||
# global mbuddy pointer
|
||||
_mbuddy: memhub.MemBuddy*
|
||||
|
||||
# max file path length
|
||||
define MAX_PATH_LEN = 512
|
||||
# max files per scan
|
||||
define MAX_FILES = 1024
|
||||
|
||||
|
||||
# FileEntry - file entry (path + SHA1)
|
||||
novirtual class FileEntry:
|
||||
"""A scanned file entry."""
|
||||
Path: str # absolute file path
|
||||
Sha1: str # first 16 SHA1 chars (16 bytes + null)
|
||||
RelPath: str # path relative to the includes root
|
||||
ModuleName: str # module name (e.g. "ast.parser")
|
||||
IncDir: str # owning includes root dir (multi-root support)
|
||||
|
||||
|
||||
# ScanResult - scan result
|
||||
novirtual class ScanResult:
|
||||
"""A collection of scan results."""
|
||||
Entries: FileEntry* # FileEntry array
|
||||
Count: int
|
||||
Capacity: int
|
||||
|
||||
|
||||
# create_scan_result - create the scan result container
|
||||
def create_scan_result(pool: memhub.MemBuddy*) -> ScanResult*:
|
||||
"""Create the scan result container with MAX_FILES preallocated slots."""
|
||||
if pool is None: return None
|
||||
size: sizet = MAX_FILES * FileEntry.__sizeof__()
|
||||
# use stdlib.malloc to avoid exhausting the mbuddy pool (consistent with StubMerger)
|
||||
entries: FileEntry* = stdlib.malloc(size)
|
||||
if entries is None: return None
|
||||
string.memset(entries, 0, size)
|
||||
|
||||
result: ScanResult* = stdlib.malloc(ScanResult.__sizeof__())
|
||||
if result is None: return None
|
||||
string.memset(result, 0, ScanResult.__sizeof__())
|
||||
result.Entries = entries
|
||||
result.Count = 0
|
||||
result.Capacity = MAX_FILES
|
||||
return result
|
||||
|
||||
|
||||
# add_file_entry - append a file entry to the scan result
|
||||
def add_file_entry(result: ScanResult*, pool: memhub.MemBuddy*, abs_path: str, rel_path: str, sha1: str, inc_dir: str) -> int:
|
||||
"""Append a file entry to the scan result; inc_dir is the owning includes root."""
|
||||
if result is None or pool is None: return 1
|
||||
if result.Count >= result.Capacity: return 1
|
||||
|
||||
# compute module name: '/' -> '.', strip .vp/.py (Utils handles __init__.vp)
|
||||
mod_buf: str = Utils.PathToModuleName(rel_path, 0)
|
||||
if mod_buf is None: return 1
|
||||
|
||||
# get the entry address
|
||||
entry_size: sizet = FileEntry.__sizeof__()
|
||||
entry_addr: uint64 = uint64(result.Entries) + result.Count * entry_size
|
||||
entry: FileEntry* = (FileEntry*)(entry_addr)
|
||||
if entry is None: return 1
|
||||
|
||||
# copy the path string
|
||||
abs_len: sizet = string.strlen(abs_path)
|
||||
abs_buf: str = stdlib.malloc(abs_len + 1)
|
||||
if abs_buf is None: return 1
|
||||
string.strcpy(abs_buf, abs_path)
|
||||
entry.Path = abs_buf
|
||||
|
||||
# copy the SHA1
|
||||
sha1_buf: str = stdlib.malloc(17)
|
||||
if sha1_buf is None: return 1
|
||||
string.strcpy(sha1_buf, sha1)
|
||||
entry.Sha1 = sha1_buf
|
||||
|
||||
# copy the relative path
|
||||
rel_buf: str = stdlib.malloc(string.strlen(rel_path) + 1)
|
||||
if rel_buf is None: return 1
|
||||
string.strcpy(rel_buf, rel_path)
|
||||
entry.RelPath = rel_buf
|
||||
|
||||
# copy the module name
|
||||
entry.ModuleName = mod_buf
|
||||
|
||||
# copy the owning includes root dir (used by Phase2 path joining)
|
||||
if inc_dir is not None:
|
||||
inc_len: sizet = string.strlen(inc_dir)
|
||||
inc_buf: str = stdlib.malloc(inc_len + 1)
|
||||
if inc_buf is None: return 1
|
||||
string.strcpy(inc_buf, inc_dir)
|
||||
entry.IncDir = inc_buf
|
||||
|
||||
result.Count += 1
|
||||
return 0
|
||||
|
||||
|
||||
# compute_file_sha1 - read file content and compute SHA1 (CRLF->LF, matching Projectrans.py)
|
||||
def compute_file_sha1(pool: memhub.MemBuddy*, file_path: str) -> str:
|
||||
"""Read a file, normalize CRLF to LF, and return the first 16 SHA1 hex chars."""
|
||||
if pool is None or file_path is None: return None
|
||||
|
||||
# read file content; BUF_SIZE must match SRC_BUF_SIZE (1MB) or trailing edits won't change SHA1
|
||||
BUF_SIZE: sizet = 1048576
|
||||
# Refuse larger files: hashing only the first 1 MiB gives two files that differ later the same
|
||||
# SHA1, and the SHA1 keys the stub/cache files (colliding modules would reuse each other's IR).
|
||||
sf_sz: fileio.File* = fileio.File(file_path, fileio.MODE.R)
|
||||
if sf_sz.closed: return None
|
||||
too_big: int = 0
|
||||
if sf_sz.size() >= BUF_SIZE: too_big = 1
|
||||
sf_sz.close()
|
||||
if too_big != 0:
|
||||
VLogger.error(f"source file exceeds the {BUF_SIZE}-byte limit and cannot be hashed completely: {file_path}", "IncludesScanner")
|
||||
return None
|
||||
buf: bytes = Utils.read_file_to_buf(file_path, BUF_SIZE)
|
||||
if buf is None: return None
|
||||
|
||||
# strip \r in place (CRLF -> LF), matching Projectrans.py text-mode reads
|
||||
buf_len: sizet = string.strlen(buf)
|
||||
write_pos: sizet = 0
|
||||
for ch in buf:
|
||||
if ch != '\r':
|
||||
buf[write_pos] = ch
|
||||
write_pos += 1
|
||||
|
||||
# add a null terminator
|
||||
buf[write_pos] = 0
|
||||
|
||||
# compute SHA1
|
||||
ctx: hashlib.sha1* = hashlib.sha1()
|
||||
if ctx is None:
|
||||
stdlib.free(buf)
|
||||
return None
|
||||
ctx.update(buf)
|
||||
|
||||
digest: bytes = stdlib.malloc(hashlib.SHA1_DIGEST_LEN)
|
||||
if digest is None:
|
||||
stdlib.free(buf)
|
||||
return None
|
||||
ctx.final(digest)
|
||||
|
||||
# convert to hex (first 8 bytes = 16 hex chars; stdlib allocation unchanged)
|
||||
hex_buf: str = Utils.bytes_to_hex(pool, digest, 8, 0, 1)
|
||||
stdlib.free(buf)
|
||||
stdlib.free(digest)
|
||||
return hex_buf
|
||||
|
||||
|
||||
# scan_directory_recursive - recursively scan a directory (FindFirstFileA/FindNextFileA)
|
||||
def scan_directory_recursive(pool: memhub.MemBuddy*, root_dir: str, rel_prefix: str, result: ScanResult*, inc_dir: str) -> int:
|
||||
"""Recursively collect .vp/.py files under a directory; inc_dir is the includes root."""
|
||||
if pool is None or root_dir is None or result is None: return 1
|
||||
# subdir recursion failure count (not ignored; fail-fast at the summary)
|
||||
subdir_failed: int = 0
|
||||
|
||||
# build search pattern: root_dir/*
|
||||
root_len: sizet = string.strlen(root_dir)
|
||||
pattern: bytes = stdlib.malloc(root_len + 4)
|
||||
if pattern is None: return 1
|
||||
viperlib.snprintf(pattern, root_len + 4, "%s/*", root_dir)
|
||||
|
||||
# find_data buffer +16 slack: VPC's WIN32_FIND_DATAA.__sizeof__() undercounts Windows writes
|
||||
fd_size_s: sizet = win32file.WIN32_FIND_DATAA.__sizeof__()
|
||||
find_data: win32file.WIN32_FIND_DATAA* = stdlib.malloc(fd_size_s + 16)
|
||||
if find_data is None: return 1
|
||||
string.memset(find_data, 0, fd_size_s + 16)
|
||||
|
||||
handle: win32base.HANDLE = win32file.FindFirstFileA(pattern, find_data)
|
||||
if handle == win32base.INVALID_HANDLE_VALUE: return 1
|
||||
|
||||
# iterate all files and subdirectories
|
||||
while True:
|
||||
# skip . and ..
|
||||
fname: str = find_data.cFileName
|
||||
if fname is not None:
|
||||
fname0: char = fname[0]
|
||||
if fname0 == '.':
|
||||
fname1: char = fname[1]
|
||||
if fname1 == '\0':
|
||||
# "."
|
||||
if win32file.FindNextFileA(handle, find_data) == 0: break
|
||||
continue
|
||||
elif fname1 == '.':
|
||||
fname2: char = fname[2]
|
||||
if fname2 == '\0':
|
||||
# ".."
|
||||
if win32file.FindNextFileA(handle, find_data) == 0: break
|
||||
continue
|
||||
|
||||
# check whether it is a directory
|
||||
is_dir: int = find_data.dwFileAttributes & win32file.FILE_ATTRIBUTE_DIRECTORY
|
||||
|
||||
# build the full path
|
||||
fname_len: sizet = string.strlen(fname)
|
||||
full_path: bytes = Utils.JoinPath(root_dir, fname)
|
||||
if full_path is None: break
|
||||
|
||||
# build the relative path
|
||||
prefix_len: sizet = 0
|
||||
if rel_prefix is not None: prefix_len = string.strlen(rel_prefix)
|
||||
rel_path: bytes = None
|
||||
if rel_prefix is not None and prefix_len > 0: rel_path = Utils.JoinPath(rel_prefix, fname)
|
||||
else:
|
||||
rel_path = stdlib.malloc(fname_len + 1)
|
||||
if rel_path is None: break
|
||||
string.strcpy(rel_path, fname)
|
||||
if rel_path is None: break
|
||||
|
||||
if is_dir != 0:
|
||||
# recurse into the subdirectory
|
||||
if scan_directory_recursive(pool, full_path, rel_path, result, inc_dir) != 0: subdir_failed += 1
|
||||
else:
|
||||
# check for a .vp source file (Viper uses .vp only; no longer .py)
|
||||
is_py: int = 0
|
||||
if Utils.is_vp_at(fname, 0, fname_len) != 0: is_py = 1
|
||||
|
||||
if is_py != 0:
|
||||
# skip files under __pycache__
|
||||
is_pycache: int = 0
|
||||
if rel_prefix is not None:
|
||||
if string.strcmp(rel_prefix, "__pycache__") == 0: is_pycache = 1
|
||||
|
||||
if is_pycache == 0:
|
||||
# compute SHA1
|
||||
sha1: str = compute_file_sha1(pool, full_path)
|
||||
if sha1 is not None:
|
||||
# fail-fast: a full table used to drop the file silently, so it never got a
|
||||
# SHA1 entry / dependency edge and was therefore never compiled.
|
||||
if add_file_entry(result, pool, full_path, rel_path, sha1, inc_dir) != 0:
|
||||
VLogger.error(f"includes scan could not register '{full_path}' (the {MAX_FILES}-file capacity or an allocation failed); raise MAX_FILES", "scan")
|
||||
VLogger.debug(f"scan: {rel_path} -> {sha1}", "scan")
|
||||
else: VLogger.warning(f"SHA1 computation failed: {full_path}", "scan")
|
||||
|
||||
# continue to the next file
|
||||
if win32file.FindNextFileA(handle, find_data) == 0: break
|
||||
|
||||
win32file.FindClose(handle)
|
||||
if subdir_failed > 0: VLogger.error(f"subdirectory scan failed for {subdir_failed} entries", "scan")
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
# scan_includes - scan entry point: return all .vp file paths and SHA1s
|
||||
def scan_includes(pool: memhub.MemBuddy*, includes_dirs: str) -> ScanResult*:
|
||||
"""Scan space-separated includes dirs and return all scanned .vp/.py files."""
|
||||
if pool is None or includes_dirs is None: return None
|
||||
|
||||
result: ScanResult* = create_scan_result(pool)
|
||||
if result is None: return None
|
||||
|
||||
# parse space-separated includes dirs, recursively scanning and merging each
|
||||
dirs_total: sizet = string.strlen(includes_dirs)
|
||||
d_i: sizet = 0
|
||||
while d_i < dirs_total:
|
||||
while d_i < dirs_total and includes_dirs[d_i] == ' ': d_i += 1
|
||||
if d_i >= dirs_total: break
|
||||
d_start: sizet = d_i
|
||||
while d_i < dirs_total and includes_dirs[d_i] != ' ': d_i += 1
|
||||
d_len: sizet = d_i - d_start
|
||||
if d_len == 0: continue
|
||||
dir_buf: bytes = stdlib.malloc(d_len + 1)
|
||||
if dir_buf is None: VLogger.error("failed to allocate the includes directory; aborting scan", "scan")
|
||||
string.strncpy(dir_buf, includes_dirs + d_start, d_len)
|
||||
dir_buf[d_len] = '\0'
|
||||
scan_directory_recursive(pool, dir_buf, None, result, dir_buf)
|
||||
stdlib.free(dir_buf)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
# find_entry_by_module - find a file entry by module name
|
||||
def find_entry_by_module(result: ScanResult*, module_name: str) -> FileEntry*:
|
||||
"""Find a file entry by module name."""
|
||||
if result is None or module_name is None: return None
|
||||
entry_size: sizet = FileEntry.__sizeof__()
|
||||
for i in range(int(result.Count)):
|
||||
entry_addr: uint64 = uint64(result.Entries) + i * entry_size
|
||||
entry: FileEntry* = (FileEntry*)(entry_addr)
|
||||
if entry is not None:
|
||||
if entry.ModuleName is not None:
|
||||
if string.strcmp(entry.ModuleName, module_name) == 0: return entry
|
||||
return None
|
||||
|
||||
|
||||
# _PathExistsA - path existence check (GetFileAttributesA)
|
||||
def _PathExistsA(path: str) -> int:
|
||||
"""Return 1 if the path exists, else 0, using GetFileAttributesA."""
|
||||
if path is None: return 0
|
||||
attr: ULONG = win32file.GetFileAttributesA(path)
|
||||
if attr == 0xFFFFFFFF: # INVALID_FILE_ATTRIBUTES
|
||||
return 0
|
||||
return 1
|
||||
|
||||
|
||||
# _TryResolveModuleFile - try one candidate path and write outputs (1=hit, 0=miss);
|
||||
# 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)
|
||||
# candidate absolute path: {inc}/{rel}{suffix} (hand-built to avoid %. *s)
|
||||
cand_len: sizet = inc_len + 1 + rel_len + suf_len
|
||||
cand: bytes = stdlib.malloc(cand_len + 1)
|
||||
if cand is None: return 0
|
||||
string.strcpy(cand, inc_dir)
|
||||
cand[inc_len] = '/'
|
||||
string.strncpy(cand + inc_len + 1, rel_seg, rel_len)
|
||||
string.strcpy(cand + inc_len + 1 + rel_len, suffix)
|
||||
if _PathExistsA(cand) == 0:
|
||||
stdlib.free(cand)
|
||||
return 0
|
||||
# path relative to the includes root: {rel}{suffix}
|
||||
rel_path: bytes = stdlib.malloc(rel_len + suf_len + 1)
|
||||
if rel_path is None:
|
||||
stdlib.free(cand)
|
||||
return 0
|
||||
string.strncpy(rel_path, rel_seg, rel_len)
|
||||
rel_path[rel_len] = '\0'
|
||||
string.strcat(rel_path, suffix)
|
||||
# Deep-copy the owning includes root dir; allocate everything before writing
|
||||
# slots to avoid a half-written state when inc allocation fails.
|
||||
inc_buf: bytes = None
|
||||
if out_inc_dir is not None:
|
||||
inc_buf = stdlib.malloc(inc_len + 1)
|
||||
if inc_buf is None:
|
||||
stdlib.free(cand)
|
||||
stdlib.free(rel_path)
|
||||
return 0
|
||||
string.strcpy(inc_buf, inc_dir)
|
||||
# Write output slots (stdlib deep copies; caller frees). Slots must be cast to
|
||||
# (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)
|
||||
else: stdlib.free(cand)
|
||||
if out_rel_path is not None:
|
||||
rel_slot: sizet* = (sizet*)(out_rel_path)
|
||||
rel_slot[0] = sizet(rel_path)
|
||||
else: stdlib.free(rel_path)
|
||||
if out_inc_dir is not None and inc_buf is not None:
|
||||
inc_slot: sizet* = (sizet*)(out_inc_dir)
|
||||
inc_slot[0] = sizet(inc_buf)
|
||||
return 1
|
||||
|
||||
|
||||
# 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: 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
|
||||
# mod_name -> relative path segment ('.' -> '/'): ast.parser -> ast/parser
|
||||
rel_seg: bytes = stdlib.malloc(mn_len + 1)
|
||||
if rel_seg is None: return 1
|
||||
rel_len: sizet = 0
|
||||
for mi in range(int(mn_len)):
|
||||
ch_m: char = mod_name[mi]
|
||||
if ch_m == '.': rel_seg[rel_len] = '/'
|
||||
else: rel_seg[rel_len] = ch_m
|
||||
rel_len += 1
|
||||
rel_seg[rel_len] = '\0'
|
||||
|
||||
found: int = 0
|
||||
d_total: sizet = string.strlen(includes_dirs)
|
||||
d_i: sizet = 0
|
||||
while d_i < d_total:
|
||||
while d_i < d_total and includes_dirs[d_i] == ' ': d_i += 1
|
||||
if d_i >= d_total: break
|
||||
d_start: sizet = d_i
|
||||
while d_i < d_total and includes_dirs[d_i] != ' ': d_i += 1
|
||||
d_len: sizet = d_i - d_start
|
||||
if d_len == 0: continue
|
||||
inc_dir_buf: bytes = stdlib.malloc(d_len + 1)
|
||||
if inc_dir_buf is None: continue
|
||||
string.strncpy(inc_dir_buf, includes_dirs + d_start, d_len)
|
||||
inc_dir_buf[d_len] = '\0'
|
||||
# candidate 1: {inc}/{rel}.vp (file module)
|
||||
if _TryResolveModuleFile(inc_dir_buf, rel_seg, rel_len, ".vp", out_abs_path, out_rel_path, out_inc_dir) != 0:
|
||||
found = 1
|
||||
stdlib.free(inc_dir_buf)
|
||||
break
|
||||
# candidate 2: {inc}/{rel}/__init__.vp (package module)
|
||||
if _TryResolveModuleFile(inc_dir_buf, rel_seg, rel_len, "/__init__.vp", out_abs_path, out_rel_path, out_inc_dir) != 0:
|
||||
found = 1
|
||||
stdlib.free(inc_dir_buf)
|
||||
if found != 0: break
|
||||
stdlib.free(rel_seg)
|
||||
if found != 0: return 0
|
||||
return 1
|
||||
@@ -571,7 +571,8 @@ def ReplBaselineCapture() -> int:
|
||||
if val is None: continue
|
||||
kind: int = 0
|
||||
if cdef_str is not None and int(cdef_str.get(i)) == 1: kind = 2
|
||||
elif cdef_dbl is not None and int(cdef_dbl.get(i)) == 1: kind = 1
|
||||
# 浮点条目:1=f64 / 2=f32(见 TypeGenCDef 的 CDEFINE_FLOAT_KIND_*),非 0 即浮点
|
||||
elif cdef_dbl is not None and int(cdef_dbl.get(i)) != 0: kind = 1
|
||||
nc: str = _repl_str_dup(_perm_pool, nm)
|
||||
vc: str = _repl_str_dup(_perm_pool, val)
|
||||
if nc is None or vc is None: continue
|
||||
|
||||
+246
-244
@@ -1,244 +1,246 @@
|
||||
import t, c
|
||||
from stdint import *
|
||||
import stdio
|
||||
import stdlib
|
||||
import memhub
|
||||
import sys
|
||||
import w32.win32console as w32cmd
|
||||
import w32.win32file as w32file
|
||||
import w32.win32base as win32base
|
||||
|
||||
|
||||
# VLogger - native logger with colored Windows console output.
|
||||
# Format: ├ LEVEL: message or ├ LEVEL[category]: message (aligned with TPC lib/core/VLogger.py).
|
||||
|
||||
# Error recovery (REPL/JIT): after error, longjmp back to the REPL setjmp point.
|
||||
# mingw-w64 x86_64: longjmp is void longjmp(jmp_buf, int); setjmp expands to _setjmp(buf, NULL).
|
||||
extern def longjmp(buf: rawptr, val: int)
|
||||
|
||||
|
||||
# REPL recovery-point pointer: registered at session start, cleared at session end.
|
||||
# Covers the whole fragment pipeline; batch mode never sets it, so it stays None and exits via sys.exit(1).
|
||||
_repl_jmpbuf: rawptr = None
|
||||
|
||||
|
||||
# repl_abort - the single "terminate after diagnosis" exit in TPV.
|
||||
# Caller already printed the diagnostic; returns to REPL via longjmp if a recovery point is set, else sys.exit(1).
|
||||
def repl_abort() -> int:
|
||||
"""Unified exit after diagnosis: longjmp to the REPL if a recovery point is set, else exit."""
|
||||
if _repl_jmpbuf is not None:
|
||||
stdio.fflush(None)
|
||||
longjmp(_repl_jmpbuf, 1)
|
||||
sys.exit(1)
|
||||
return 0
|
||||
|
||||
|
||||
def set_repl_jmpbuf(buf: t.CPtr) -> int:
|
||||
"""Register the REPL jmp_buf; idempotent at each fragment entry."""
|
||||
global _repl_jmpbuf
|
||||
_repl_jmpbuf = buf
|
||||
return 0
|
||||
|
||||
|
||||
def clear_repl_jmpbuf() -> int:
|
||||
"""Clear the registered jmp_buf at REPL session end."""
|
||||
global _repl_jmpbuf
|
||||
_repl_jmpbuf = None
|
||||
return 0
|
||||
|
||||
# Log levels
|
||||
enum LogLevel:
|
||||
DEBUG, INFO, WARNING, ERROR, SUCCESS, CRITICAL
|
||||
|
||||
|
||||
# Win32 console foreground color attributes
|
||||
define FOREGROUND_BLUE = 0x0001
|
||||
define FOREGROUND_GREEN = 0x0002
|
||||
define FOREGROUND_RED = 0x0004
|
||||
define FOREGROUND_INTENSITY = 0x0008
|
||||
define FOREGROUND_WHITE = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE
|
||||
define FOREGROUND_CYAN = FOREGROUND_BLUE | FOREGROUND_GREEN
|
||||
define FOREGROUND_YELLOW = FOREGROUND_RED | FOREGROUND_GREEN
|
||||
define FOREGROUND_MAGENTA = FOREGROUND_RED | FOREGROUND_BLUE
|
||||
|
||||
# Win32 console background color attributes
|
||||
define BACKGROUND_RED = 0x0040
|
||||
define BACKGROUND_GREEN = 0x0020
|
||||
define BACKGROUND_BLUE = 0x0010
|
||||
define BACKGROUND_INTENSITY = 0x0080
|
||||
define BACKGROUND_WHITE = BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE
|
||||
|
||||
|
||||
novirtual class Logger:
|
||||
_level: int
|
||||
_console_handle: win32base.HANDLE
|
||||
_use_color: int
|
||||
jit_flag: bool
|
||||
__mbuddy__: memhub.MemBuddy*
|
||||
|
||||
def __init__(self, level: int = 1):
|
||||
self._level = level
|
||||
self._console_handle = w32file.GetStdHandle(w32file.STD_OUTPUT_HANDLE)
|
||||
self._use_color = 1
|
||||
self.jit_flag = False
|
||||
self.__mbuddy__ = _mbuddy
|
||||
|
||||
def _set_color(self, attr: WORD) -> int:
|
||||
uc: int = self._use_color
|
||||
if uc:
|
||||
ch: win32base.HANDLE = self._console_handle
|
||||
ret: int = w32cmd.SetConsoleTextAttribute(ch, attr)
|
||||
return ret
|
||||
return 0
|
||||
|
||||
def _reset_color(self) -> int:
|
||||
if self._use_color: return w32cmd.SetConsoleTextAttribute(self._console_handle, FOREGROUND_WHITE)
|
||||
return 0
|
||||
|
||||
# _log - core log output with segmented coloring (prefix symbol / level tag / message).
|
||||
# Format: ├ LEVEL: msg or ├ LEVEL[category]: msg.
|
||||
def _log(self, level: int, prefix_sym: str, level_str: str, msg: str, sym_color: WORD, level_color: WORD, category: str = "") -> int:
|
||||
# Color the prefix symbol
|
||||
self._set_color(sym_color)
|
||||
stdio.printf("%s ", prefix_sym)
|
||||
# Color the level tag
|
||||
self._set_color(level_color)
|
||||
stdio.printf("%s", level_str)
|
||||
self._reset_color()
|
||||
# Optional category
|
||||
if category is not None and category[0] != 0: stdio.printf(" [%s]", category)
|
||||
stdio.printf(": %s\n", msg)
|
||||
return 0
|
||||
|
||||
def debug(self, msg: str, category: str = "") -> int:
|
||||
return self._log(LogLevel.DEBUG, "├", "DEBUG", msg, FOREGROUND_INTENSITY, FOREGROUND_INTENSITY, category)
|
||||
|
||||
def info(self, msg: str, category: str = "") -> int:
|
||||
ret: int = self._log(LogLevel.INFO, "├", "INFO", msg, FOREGROUND_GREEN, FOREGROUND_GREEN | FOREGROUND_INTENSITY, category)
|
||||
return ret
|
||||
|
||||
def warning(self, msg: str, category: str = "") -> int:
|
||||
return self._log(LogLevel.WARNING, "├", "WARN", msg, FOREGROUND_YELLOW, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY, category)
|
||||
|
||||
def error(self, msg: str, category: str = "") -> int:
|
||||
ret: int = self._log(LogLevel.ERROR, "├", "ERROR", msg, FOREGROUND_RED, FOREGROUND_RED | FOREGROUND_INTENSITY, category)
|
||||
# JIT/REPL: diagnostic already printed; jump back to the REPL loop via the unified exit.
|
||||
if self.jit_flag:
|
||||
repl_abort()
|
||||
return ret
|
||||
# Batch mode: terminate immediately on error.
|
||||
sys.exit(1)
|
||||
return ret
|
||||
|
||||
def success(self, msg: str, category: str = "") -> int:
|
||||
return self._log(LogLevel.SUCCESS, "├", "SUCCESS", msg, FOREGROUND_GREEN, FOREGROUND_GREEN | FOREGROUND_INTENSITY, category)
|
||||
|
||||
def critical(self, msg: str, category: str = "") -> int:
|
||||
# CRITICAL uses the ╠ prefix (red-background white label, aligned with TPC style).
|
||||
return self._log(LogLevel.CRITICAL, "╠", "CRITICAL", msg, FOREGROUND_RED, BACKGROUND_RED | FOREGROUND_WHITE | FOREGROUND_INTENSITY, category)
|
||||
|
||||
def set_level(self, level: int) -> int:
|
||||
self._level = level
|
||||
return 0
|
||||
|
||||
def set_jit_flag(self, v: bool) -> int:
|
||||
"""Set the JIT/REPL flag: True makes error longjmp back to the REPL loop."""
|
||||
self.jit_flag = v
|
||||
return 0
|
||||
|
||||
# Advanced log methods.
|
||||
|
||||
def banner(self, msg: str) -> int:
|
||||
"""Print a blue-highlighted section banner."""
|
||||
self._set_color(FOREGROUND_BLUE | FOREGROUND_INTENSITY)
|
||||
stdio.printf("\n=== %s ===\n\n", msg)
|
||||
self._reset_color()
|
||||
return 0
|
||||
|
||||
def compile_error(self, msg: str, file: str = "", line: int = 0) -> int:
|
||||
"""Format a compile error with a red-background header, location and message."""
|
||||
self._set_color(BACKGROUND_RED | FOREGROUND_WHITE | FOREGROUND_INTENSITY)
|
||||
stdio.printf(" COMPILE ERROR ")
|
||||
self._set_color(FOREGROUND_RED | FOREGROUND_INTENSITY)
|
||||
stdio.printf("\n")
|
||||
if file is not None:
|
||||
if file[0] != 0 and line > 0: stdio.printf(" location: %s:%d\n", file, line)
|
||||
elif file[0] != 0: stdio.printf(" file: %s\n", file)
|
||||
stdio.printf(" error: %s\n", msg)
|
||||
self._reset_color()
|
||||
return 0
|
||||
|
||||
def compile_warning(self, msg: str, file: str = "", line: int = 0) -> int:
|
||||
"""Format a compile warning with a yellow background, location and message."""
|
||||
self._set_color(BACKGROUND_RED | BACKGROUND_GREEN | FOREGROUND_INTENSITY)
|
||||
stdio.printf(" COMPILE WARNING ")
|
||||
self._set_color(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY)
|
||||
stdio.printf("\n")
|
||||
if file is not None:
|
||||
if file[0] != 0 and line > 0: stdio.printf(" location: %s:%d\n", file, line)
|
||||
elif file[0] != 0: stdio.printf(" file: %s\n", file)
|
||||
stdio.printf(" warning: %s\n", msg)
|
||||
self._reset_color()
|
||||
return 0
|
||||
|
||||
|
||||
# Global mbuddy pointer (injected by lib.InitLib).
|
||||
_mbuddy: memhub.MemBuddy*
|
||||
|
||||
# Global logger pointer
|
||||
logger: Logger*
|
||||
log: Logger*
|
||||
|
||||
def init_logger():
|
||||
"""Initialize the global logger instance."""
|
||||
global logger, log
|
||||
raw: t.CPtr = _mbuddy.alloc(Logger.__sizeof__())
|
||||
# if raw is None: return None (memory management still unclear)
|
||||
logger = raw
|
||||
logger.__before_init__()
|
||||
logger.__init__(LogLevel.INFO)
|
||||
log = logger
|
||||
# if fmtbuffer is None: return None
|
||||
|
||||
|
||||
# --nowarn-register switch: suppresses Phase A dependency WARN output (output only).
|
||||
_nowarn_register: int = 0
|
||||
|
||||
# Module-level convenience functions (aligned with TPC lib.core.VLogger; no get_logger() needed).
|
||||
|
||||
def info(msg: str, category: str = "") -> int:
|
||||
"""Log at INFO level."""
|
||||
if log is not None: return log.info(msg, category)
|
||||
return 0
|
||||
|
||||
|
||||
def warning(msg: str, category: str = "") -> int:
|
||||
"""Log at WARN level."""
|
||||
if log is not None: return log.warning(msg, category)
|
||||
return 0
|
||||
|
||||
|
||||
def error(msg: str, category: str = "") -> int:
|
||||
"""Log at ERROR level: exit in batch mode, longjmp to REPL under JIT/REPL."""
|
||||
if log is not None: return log.error(msg, category)
|
||||
# When the logger is unavailable, still use the unified exit (REPL can recover if registered)
|
||||
repl_abort()
|
||||
return 0
|
||||
|
||||
|
||||
def success(msg: str, category: str = "") -> int:
|
||||
"""Log at SUCCESS level."""
|
||||
if log is not None: return log.success(msg, category)
|
||||
return 0
|
||||
|
||||
|
||||
def critical(msg: str, category: str = "") -> int:
|
||||
"""Log at CRITICAL level (fatal, red background)."""
|
||||
if log is not None: return log.critical(msg, category)
|
||||
return 0
|
||||
|
||||
|
||||
def debug(msg: str, category: str = "") -> int:
|
||||
"""Log at DEBUG level."""
|
||||
if log is not None: return log.debug(msg, category)
|
||||
return 0
|
||||
from stdint import *
|
||||
import stdio
|
||||
import memhub
|
||||
import sys
|
||||
import win32.win32console as win32cmd
|
||||
import win32.win32file as win32file
|
||||
import win32.win32base as win32base
|
||||
|
||||
|
||||
# VLogger - native logger with colored Windows console output.
|
||||
# Format: ├ LEVEL: message or ├ LEVEL[category]: message (aligned with TPC lib/core/VLogger.py).
|
||||
|
||||
# Error recovery (REPL/JIT): after error, longjmp back to the REPL setjmp point.
|
||||
# mingw-w64 x86_64: longjmp is void longjmp(jmp_buf, int); setjmp expands to _setjmp(buf, NULL).
|
||||
extern def longjmp(buf: rawptr, val: int)
|
||||
|
||||
|
||||
# REPL recovery-point pointer: registered at session start, cleared at session end.
|
||||
# Covers the whole fragment pipeline; batch mode never sets it, so it stays None and exits via sys.exit(1).
|
||||
_repl_jmpbuf: rawptr = None
|
||||
|
||||
|
||||
# repl_abort - the single "terminate after diagnosis" exit in VPC.
|
||||
# Caller already printed the diagnostic; returns to REPL via longjmp if a recovery point is set, else sys.exit(1).
|
||||
def repl_abort() -> int:
|
||||
"""Unified exit after diagnosis: longjmp to the REPL if a recovery point is set, else exit."""
|
||||
if _repl_jmpbuf is not None:
|
||||
stdio.fflush(None)
|
||||
longjmp(_repl_jmpbuf, 1)
|
||||
sys.exit(1)
|
||||
return 0
|
||||
|
||||
|
||||
def set_repl_jmpbuf(buf: rawptr) -> int:
|
||||
"""Register the REPL jmp_buf; idempotent at each fragment entry."""
|
||||
global _repl_jmpbuf
|
||||
_repl_jmpbuf = buf
|
||||
return 0
|
||||
|
||||
|
||||
def clear_repl_jmpbuf() -> int:
|
||||
"""Clear the registered jmp_buf at REPL session end."""
|
||||
global _repl_jmpbuf
|
||||
_repl_jmpbuf = None
|
||||
return 0
|
||||
|
||||
# Log levels
|
||||
enum LogLevel:
|
||||
DEBUG, INFO, WARNING, ERROR, SUCCESS, CRITICAL
|
||||
|
||||
|
||||
# Win32 console foreground color attributes
|
||||
# 值用 WORD(...) 显式定宽为 uint16(与 _log 的 sym_color/level_color 形参一致)。
|
||||
# 注意:仅写 `define X: WORD = 0x0002` 不够 —— define 的宽度由**右侧表达式的类型**
|
||||
# 决定(win32file.vp 的 `define STD_OUTPUT_HANDLE: DWORD = DWORD(-10)` 同款),
|
||||
# 否则常量仍是 i32 → 传给 WORD 形参即 32→16 隐式窄化。
|
||||
define FOREGROUND_BLUE: WORD = WORD(0x0001)
|
||||
define FOREGROUND_GREEN: WORD = WORD(0x0002)
|
||||
define FOREGROUND_RED: WORD = WORD(0x0004)
|
||||
define FOREGROUND_INTENSITY: WORD = WORD(0x0008)
|
||||
define FOREGROUND_WHITE: WORD = WORD(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE)
|
||||
define FOREGROUND_CYAN: WORD = WORD(FOREGROUND_BLUE | FOREGROUND_GREEN)
|
||||
define FOREGROUND_YELLOW: WORD = WORD(FOREGROUND_RED | FOREGROUND_GREEN)
|
||||
define FOREGROUND_MAGENTA: WORD = WORD(FOREGROUND_RED | FOREGROUND_BLUE)
|
||||
|
||||
# Win32 console background color attributes
|
||||
define BACKGROUND_RED: WORD = WORD(0x0040)
|
||||
define BACKGROUND_GREEN: WORD = WORD(0x0020)
|
||||
define BACKGROUND_BLUE: WORD = WORD(0x0010)
|
||||
define BACKGROUND_INTENSITY: WORD = WORD(0x0080)
|
||||
define BACKGROUND_WHITE: WORD = WORD(BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE)
|
||||
|
||||
|
||||
novirtual class Logger:
|
||||
_level: int
|
||||
_console_handle: win32base.HANDLE
|
||||
_use_color: int
|
||||
jit_flag: bool
|
||||
__mbuddy__: memhub.MemBuddy*
|
||||
|
||||
def __init__(self, level: int = 1):
|
||||
self._level = level
|
||||
self._console_handle = win32file.GetStdHandle(win32file.STD_OUTPUT_HANDLE)
|
||||
self._use_color = 1
|
||||
self.jit_flag = False
|
||||
self.__mbuddy__ = _mbuddy
|
||||
|
||||
def _set_color(self, attr: WORD) -> int:
|
||||
uc: int = self._use_color
|
||||
if uc:
|
||||
ch: win32base.HANDLE = self._console_handle
|
||||
ret: int = win32cmd.SetConsoleTextAttribute(ch, attr)
|
||||
return ret
|
||||
return 0
|
||||
|
||||
def _reset_color(self) -> int:
|
||||
if self._use_color: return win32cmd.SetConsoleTextAttribute(self._console_handle, FOREGROUND_WHITE)
|
||||
return 0
|
||||
|
||||
# _log - core log output with segmented coloring (prefix symbol / level tag / message).
|
||||
# Format: ├ LEVEL: msg or ├ LEVEL[category]: msg.
|
||||
def _log(self, level: int, prefix_sym: str, level_str: str, msg: str, sym_color: WORD, level_color: WORD, category: str = "") -> int:
|
||||
# Color the prefix symbol
|
||||
self._set_color(sym_color)
|
||||
stdio.printf("%s ", prefix_sym)
|
||||
# Color the level tag
|
||||
self._set_color(level_color)
|
||||
stdio.printf("%s", level_str)
|
||||
self._reset_color()
|
||||
# Optional category
|
||||
if category is not None and category[0] != 0: stdio.printf(" [%s]", category)
|
||||
stdio.printf(": %s\n", msg)
|
||||
return 0
|
||||
|
||||
def debug(self, msg: str, category: str = "") -> int:
|
||||
return self._log(LogLevel.DEBUG, "├", "DEBUG", msg, FOREGROUND_INTENSITY, FOREGROUND_INTENSITY, category)
|
||||
|
||||
def info(self, msg: str, category: str = "") -> int:
|
||||
ret: int = self._log(LogLevel.INFO, "├", "INFO", msg, FOREGROUND_GREEN, WORD(FOREGROUND_GREEN | FOREGROUND_INTENSITY), category)
|
||||
return ret
|
||||
|
||||
def warning(self, msg: str, category: str = "") -> int:
|
||||
return self._log(LogLevel.WARNING, "├", "WARN", msg, FOREGROUND_YELLOW, WORD(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY), category)
|
||||
|
||||
def error(self, msg: str, category: str = "") -> int:
|
||||
ret: int = self._log(LogLevel.ERROR, "├", "ERROR", msg, FOREGROUND_RED, WORD(FOREGROUND_RED | FOREGROUND_INTENSITY), category)
|
||||
# JIT/REPL: diagnostic already printed; jump back to the REPL loop via the unified exit.
|
||||
if self.jit_flag:
|
||||
repl_abort()
|
||||
return ret
|
||||
# Batch mode: terminate immediately on error.
|
||||
sys.exit(1)
|
||||
return ret
|
||||
|
||||
def success(self, msg: str, category: str = "") -> int:
|
||||
return self._log(LogLevel.SUCCESS, "├", "SUCCESS", msg, FOREGROUND_GREEN, WORD(FOREGROUND_GREEN | FOREGROUND_INTENSITY), category)
|
||||
|
||||
def critical(self, msg: str, category: str = "") -> int:
|
||||
# CRITICAL uses the ╠ prefix (red-background white label, aligned with TPC style).
|
||||
return self._log(LogLevel.CRITICAL, "╠", "CRITICAL", msg, FOREGROUND_RED, WORD(BACKGROUND_RED | FOREGROUND_WHITE | FOREGROUND_INTENSITY), category)
|
||||
|
||||
def set_level(self, level: int) -> int:
|
||||
self._level = level
|
||||
return 0
|
||||
|
||||
def set_jit_flag(self, v: bool) -> int:
|
||||
"""Set the JIT/REPL flag: True makes error longjmp back to the REPL loop."""
|
||||
self.jit_flag = v
|
||||
return 0
|
||||
|
||||
# Advanced log methods.
|
||||
|
||||
def banner(self, msg: str) -> int:
|
||||
"""Print a blue-highlighted section banner."""
|
||||
self._set_color(FOREGROUND_BLUE | FOREGROUND_INTENSITY)
|
||||
stdio.printf("\n=== %s ===\n\n", msg)
|
||||
self._reset_color()
|
||||
return 0
|
||||
|
||||
def compile_error(self, msg: str, file: str = "", line: int = 0) -> int:
|
||||
"""Format a compile error with a red-background header, location and message."""
|
||||
self._set_color(BACKGROUND_RED | FOREGROUND_WHITE | FOREGROUND_INTENSITY)
|
||||
stdio.printf(" COMPILE ERROR ")
|
||||
self._set_color(FOREGROUND_RED | FOREGROUND_INTENSITY)
|
||||
stdio.printf("\n")
|
||||
if file is not None:
|
||||
if file[0] != 0 and line > 0: stdio.printf(" location: %s:%d\n", file, line)
|
||||
elif file[0] != 0: stdio.printf(" file: %s\n", file)
|
||||
stdio.printf(" error: %s\n", msg)
|
||||
self._reset_color()
|
||||
return 0
|
||||
|
||||
def compile_warning(self, msg: str, file: str = "", line: int = 0) -> int:
|
||||
"""Format a compile warning with a yellow background, location and message."""
|
||||
self._set_color(BACKGROUND_RED | BACKGROUND_GREEN | FOREGROUND_INTENSITY)
|
||||
stdio.printf(" COMPILE WARNING ")
|
||||
self._set_color(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY)
|
||||
stdio.printf("\n")
|
||||
if file is not None:
|
||||
if file[0] != 0 and line > 0: stdio.printf(" location: %s:%d\n", file, line)
|
||||
elif file[0] != 0: stdio.printf(" file: %s\n", file)
|
||||
stdio.printf(" warning: %s\n", msg)
|
||||
self._reset_color()
|
||||
return 0
|
||||
|
||||
|
||||
# Global mbuddy pointer (injected by lib.InitLib).
|
||||
_mbuddy: memhub.MemBuddy*
|
||||
|
||||
# Global logger pointer
|
||||
logger: Logger*
|
||||
log: Logger*
|
||||
|
||||
def init_logger():
|
||||
"""Initialize the global logger instance."""
|
||||
global logger, log
|
||||
raw: rawptr = _mbuddy.alloc(Logger.__sizeof__())
|
||||
# if raw is None: return None (memory management still unclear)
|
||||
logger = raw
|
||||
logger.__before_init__()
|
||||
logger.__init__(LogLevel.INFO)
|
||||
log = logger
|
||||
# if fmtbuffer is None: return None
|
||||
|
||||
|
||||
# --nowarn-register switch: suppresses Phase A dependency WARN output (output only).
|
||||
_nowarn_register: int = 0
|
||||
|
||||
# Module-level convenience functions (aligned with TPC lib.core.VLogger; no get_logger() needed).
|
||||
|
||||
def info(msg: str, category: str = "") -> int:
|
||||
"""Log at INFO level."""
|
||||
if log is not None: return log.info(msg, category)
|
||||
return 0
|
||||
|
||||
|
||||
def warning(msg: str, category: str = "") -> int:
|
||||
"""Log at WARN level."""
|
||||
if log is not None: return log.warning(msg, category)
|
||||
return 0
|
||||
|
||||
|
||||
def error(msg: str, category: str = "") -> int:
|
||||
"""Log at ERROR level: exit in batch mode, longjmp to REPL under JIT/REPL."""
|
||||
if log is not None: return log.error(msg, category)
|
||||
# When the logger is unavailable, still use the unified exit (REPL can recover if registered)
|
||||
repl_abort()
|
||||
return 0
|
||||
|
||||
|
||||
def success(msg: str, category: str = "") -> int:
|
||||
"""Log at SUCCESS level."""
|
||||
if log is not None: return log.success(msg, category)
|
||||
return 0
|
||||
|
||||
|
||||
def critical(msg: str, category: str = "") -> int:
|
||||
"""Log at CRITICAL level (fatal, red background)."""
|
||||
if log is not None: return log.critical(msg, category)
|
||||
return 0
|
||||
|
||||
|
||||
def debug(msg: str, category: str = "") -> int:
|
||||
"""Log at DEBUG level."""
|
||||
if log is not None: return log.debug(msg, category)
|
||||
return 0
|
||||
@@ -1,14 +1,11 @@
|
||||
"""Compiler-side implementation of the `viperbuild` built-in macro library (--src Pass 0)."""
|
||||
import t, c
|
||||
from stdint import *
|
||||
from w32.win32base import *
|
||||
from win32.win32base import *
|
||||
import memhub
|
||||
import string
|
||||
import stdio
|
||||
import stdlib
|
||||
import viperlib
|
||||
import ast
|
||||
import w32.win32file as win32file
|
||||
import win32.win32file as win32file
|
||||
import lib.Projectrans.Utils as Utils
|
||||
import lib.Projectrans.Config as Config
|
||||
import lib.core.VLogger as VLogger
|
||||
@@ -131,7 +128,7 @@ def _BaseNameOf(path: str) -> str:
|
||||
if path is None: return None
|
||||
n: sizet = string.strlen(path)
|
||||
start: sizet = 0
|
||||
for i in range(n):
|
||||
for i in range(int(n)):
|
||||
ch: char = path[i]
|
||||
if ch == '/' or ch == '\\': start = i + 1
|
||||
return _DupStd(path + start)
|
||||
@@ -166,7 +163,7 @@ def _FindInList(names: list[str]*, name: str) -> int:
|
||||
"""Linear-search names for name; return its index, or -1 if not found."""
|
||||
if names is None or name is None: return -1
|
||||
n: sizet = names.__len__()
|
||||
for i in range(n):
|
||||
for i in range(int(n)):
|
||||
s: str = names.get(i)
|
||||
if s is not None:
|
||||
if string.strcmp(s, name) == 0: return int(i)
|
||||
@@ -199,7 +196,7 @@ def _SplitInto(out: list[str]*, joined: str) -> int:
|
||||
n: sizet = string.strlen(joined)
|
||||
if n == 0: return 1
|
||||
start: sizet = 0
|
||||
for i in range(n + 1):
|
||||
for i in range(int(n + 1)):
|
||||
if i == n or joined[i] == char(VB_SEP_CODE):
|
||||
seg: str = stdlib.malloc(i - start + 1)
|
||||
if seg is None: return 0
|
||||
@@ -227,7 +224,7 @@ def _JoinSep(lst: list[str]*) -> str:
|
||||
buf: str = _scratch.alloc(total + 1)
|
||||
if buf is None: return None
|
||||
pos: sizet = 0
|
||||
for i in range(n):
|
||||
for i in range(int(n)):
|
||||
s = lst.get(i)
|
||||
if s is not None:
|
||||
sl: sizet = string.strlen(s)
|
||||
@@ -255,7 +252,7 @@ def _JoinSpace(lst: list[str]*) -> str:
|
||||
buf: str = stdlib.malloc(total + 1)
|
||||
if buf is None: return None
|
||||
pos: sizet = 0
|
||||
for i in range(n):
|
||||
for i in range(int(n)):
|
||||
s = lst.get(i)
|
||||
if s is not None:
|
||||
sl: sizet = string.strlen(s)
|
||||
@@ -274,7 +271,7 @@ def _FirstSpaceTok(joined: str) -> str:
|
||||
n: sizet = string.strlen(joined)
|
||||
if n == 0: return None
|
||||
sp: sizet = n
|
||||
for i in range(n):
|
||||
for i in range(int(n)):
|
||||
if joined[i] == ' ':
|
||||
sp = i
|
||||
break
|
||||
@@ -337,7 +334,7 @@ def _EvalList(node: ast.AST*, out: list[str]*) -> int:
|
||||
lst: ast.List* = ast.List*(node)
|
||||
if lst is None or lst.elts is None: return 0
|
||||
n: sizet = lst.elts.__len__()
|
||||
for i in range(n):
|
||||
for i in range(int(n)):
|
||||
e: ast.AST* = lst.elts.get(i)
|
||||
if e is None: return 0
|
||||
if e.kind() == ast.ASTKind.Starred:
|
||||
@@ -409,7 +406,7 @@ def _HandleImport(stmt: ast.Import*) -> int:
|
||||
global _alias, _saw_viperbuild
|
||||
if stmt is None or stmt.names is None: return 0
|
||||
n: sizet = stmt.names.__len__()
|
||||
for i in range(n):
|
||||
for i in range(int(n)):
|
||||
al: ast.Alias* = ast.Alias*(stmt.names.get(i))
|
||||
if al is not None and al.name is not None:
|
||||
if string.strcmp(al.name, "viperbuild") == 0:
|
||||
@@ -434,7 +431,7 @@ def _HandleImportFrom(stmt: ast.ImportFrom*) -> int:
|
||||
_saw_viperbuild = 1
|
||||
if stmt.names is None: return 0
|
||||
n: sizet = stmt.names.__len__()
|
||||
for i in range(n):
|
||||
for i in range(int(n)):
|
||||
al: ast.Alias* = ast.Alias*(stmt.names.get(i))
|
||||
if al is not None and al.name is not None:
|
||||
if string.strcmp(al.name, "Project") == 0:
|
||||
@@ -487,7 +484,7 @@ def _ApplyCtorArgs(cl: ast.Call*) -> int:
|
||||
_c_version = _DupStd(_g_out_str)
|
||||
if cl.keywords is None: return 0
|
||||
n: sizet = cl.keywords.__len__()
|
||||
for i in range(n):
|
||||
for i in range(int(n)):
|
||||
kw: ast.Keyword* = ast.Keyword*(cl.keywords.get(i))
|
||||
if kw is not None and kw.arg is not None and kw.value is not None:
|
||||
if string.strcmp(kw.arg, "name") == 0:
|
||||
@@ -640,7 +637,7 @@ def _HandleConfigCall(cl: ast.Call*) -> int:
|
||||
"""Handle a proj.config(kw=...) call."""
|
||||
if cl is None or cl.keywords is None: return 0
|
||||
n: sizet = cl.keywords.__len__()
|
||||
for i in range(n):
|
||||
for i in range(int(n)):
|
||||
kw: ast.Keyword* = ast.Keyword*(cl.keywords.get(i))
|
||||
if kw is not None and kw.arg is not None and kw.value is not None:
|
||||
if _SetField(kw.arg, kw.value) != 0: return 1
|
||||
@@ -657,7 +654,7 @@ def _HandleBuildCall(cl: ast.Call*) -> int:
|
||||
_c_has_build = 1
|
||||
if cl.keywords is None: return 0
|
||||
n: sizet = cl.keywords.__len__()
|
||||
for i in range(n):
|
||||
for i in range(int(n)):
|
||||
kw: ast.Keyword* = ast.Keyword*(cl.keywords.get(i))
|
||||
if kw is not None and kw.arg is not None and kw.value is not None:
|
||||
if string.strcmp(kw.arg, "triple") == 0:
|
||||
@@ -687,11 +684,11 @@ def _CallArgsHaveProjRef(cl: ast.Call*) -> int:
|
||||
if cl is None: return 0
|
||||
if cl.args is not None:
|
||||
n: sizet = cl.args.__len__()
|
||||
for i in range(n):
|
||||
for i in range(int(n)):
|
||||
if _IsProjRef(cl.args.get(i)) != 0: return 1
|
||||
if cl.keywords is not None:
|
||||
n = cl.keywords.__len__()
|
||||
for i in range(n):
|
||||
for i in range(int(n)):
|
||||
kw: ast.Keyword* = ast.Keyword*(cl.keywords.get(i))
|
||||
if kw is not None and kw.value is not None:
|
||||
if _IsProjRef(kw.value) != 0: return 1
|
||||
@@ -775,7 +772,7 @@ def _ScanFile(pool: memhub.MemBuddy*, path: str) -> int:
|
||||
rc: int = 0
|
||||
if body is not None:
|
||||
cn: sizet = len(body)
|
||||
for ci in range(cn):
|
||||
for ci in range(int(cn)):
|
||||
child: ast.AST* = body.get(ci)
|
||||
if child is not None:
|
||||
kd: int = child.kind()
|
||||
@@ -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)
|
||||
@@ -853,7 +850,7 @@ def _RunBfs(pool: memhub.MemBuddy*, entry_path: str) -> int:
|
||||
if rc == 1: return 1
|
||||
# Resolve module names collected from this file
|
||||
pn: sizet = _pending.__len__()
|
||||
for pi in range(pn):
|
||||
for pi in range(int(pn)):
|
||||
mod: str = _pending.get(pi)
|
||||
if mod is not None:
|
||||
dep: str = _ResolveModuleToFile(mod)
|
||||
@@ -904,9 +901,9 @@ def _ProbeIncludes() -> str:
|
||||
exe_dir: str = _DirOf(exe_buf)
|
||||
stdlib.free(exe_buf)
|
||||
if exe_dir is None: return None
|
||||
for k in range(3 + 1):
|
||||
for k in range(int(3 + 1)):
|
||||
base: str = _DupStd(exe_dir)
|
||||
for j in range(k):
|
||||
for j in range(int(k)):
|
||||
base = _Join2(base, "..")
|
||||
cand: str = _Join2(base, "includes")
|
||||
if _IsDir(cand) != 0: return cand
|
||||
@@ -919,7 +916,7 @@ def _DeriveOutputFromEntry(entry_path: str) -> str:
|
||||
if base is None: return "app.exe"
|
||||
n: sizet = string.strlen(base)
|
||||
cut: sizet = n
|
||||
for i in range(n):
|
||||
for i in range(int(n)):
|
||||
if base[i] == '.':
|
||||
cut = i
|
||||
break
|
||||
@@ -949,7 +946,7 @@ def _FinalizeCfg(entry_path: str) -> int:
|
||||
|
||||
# ---- Defaults ----
|
||||
if _c_source is None: _c_source = _DupStd(_entry_dir)
|
||||
if _c_buildroot is None: _c_buildroot = _Join2(_c_source, ".tpv_build")
|
||||
if _c_buildroot is None: _c_buildroot = _Join2(_c_source, "__vpcbuild__")
|
||||
if _c_includes is None:
|
||||
probed: str = _ProbeIncludes()
|
||||
if probed is None:
|
||||
@@ -958,7 +955,7 @@ def _FinalizeCfg(entry_path: str) -> int:
|
||||
_c_includes = probed
|
||||
if _c_compiler is None: _c_compiler = _DupStd("llc -filetype=obj -relocation-model=pic")
|
||||
if _c_linker is None:
|
||||
# Built-in default = the verified link args from this repo's TransPyV/project.vpj.
|
||||
# Built-in default = the verified link args from this repo's ViperC/project.vpj.
|
||||
# Do NOT add -lgcc: msys64/mingw64 + lld has no libgcc and fails with "unable to find library -lgcc".
|
||||
_c_linker = _DupStd("clang++ -fuse-ld=lld -lmsvcrt -lucrt -lpthread -lmingwex -lkernel32 -Wl,--stack,0x1000000 -Wl,--no-insert-timestamp -Wl,--export-all-symbols -g")
|
||||
if _c_output is None: _c_output = _DeriveOutputFromEntry(entry_path)
|
||||
@@ -1009,7 +1006,7 @@ def _SplitToolCmd(joined: str, which: int) -> None:
|
||||
if joined is None: return
|
||||
n: sizet = string.strlen(joined)
|
||||
sp: sizet = n
|
||||
for i in range(n):
|
||||
for i in range(int(n)):
|
||||
if joined[i] == ' ':
|
||||
sp = i
|
||||
break
|
||||
@@ -1086,7 +1083,10 @@ def DiscoverAndApply(mb: memhub.MemBuddy*, entry_path: str) -> int:
|
||||
if arena is None:
|
||||
VLogger.error("viperbuild: scratch memory allocation failed", "viperbuild")
|
||||
return 1
|
||||
_scratch = memhub.MemBuddy(arena, VB_SCRATCH, 0, 0)
|
||||
# Heap-allocate the manager: `_scratch` is a module-level global, so a frame-local
|
||||
# `MemBuddy` temp would hand out a dangling address once this frame returns (LifetimeError).
|
||||
# `hlife` is explicit so NLL frame-demotion cannot apply.
|
||||
_scratch = new heap hlife memhub.MemBuddy(arena, VB_SCRATCH, 0, 0)
|
||||
if _scratch is None:
|
||||
VLogger.error("viperbuild: scratch pool initialization failed", "viperbuild")
|
||||
return 1
|
||||
|
||||
+21
-24
@@ -19,9 +19,8 @@ import string
|
||||
import stdlib
|
||||
import memhub
|
||||
import argparse
|
||||
import w32.win32console as w32cmd
|
||||
import w32.win32base as w32base
|
||||
import w32.fileio as fileio
|
||||
import win32.win32console as win32cmd
|
||||
import win32.win32base as win32base
|
||||
import sys
|
||||
import ast
|
||||
import os
|
||||
@@ -31,14 +30,12 @@ import os.path
|
||||
import shutil
|
||||
import mlirlite
|
||||
import subprocess
|
||||
import viperlib
|
||||
import t, c
|
||||
import lib.Stub.StubMerger.StubMergerBuild as StubMergerBuild
|
||||
import lib.Stub.StubMerger.StubMergerCollect as StubMergerCollect
|
||||
import lib.Phase.Phase1.Phase1Main as Phase1Main
|
||||
|
||||
|
||||
# Main Pool 1 GiB
|
||||
# Main Pool 1 GiB(不要简单调大:2 GiB 会让 MemBuddy 内部索引/位宽溢出 ——
|
||||
# ASan: access-violation at memhub.vp:858 _fl_push,就在 __init__ 阶段。
|
||||
# 池耗尽的正解是修泄漏:mlirlite 全局数组 dense 渲染的"每元素 ~5KB 不复用",见 includes e9e1933。)
|
||||
define POOL_SIZE = 1073741824
|
||||
# Permanent memory pool size: 2^29 + 264 = 512MB usable, then change to 1 GiB
|
||||
define PERM_POOL_SIZE = 1073741824
|
||||
@@ -47,11 +44,11 @@ define CODE_PAGE = 65001
|
||||
# Source code buffer size (1MB)
|
||||
define SRC_BUF_SIZE = 1048576
|
||||
# Version (dev/beta/rc)
|
||||
V_TPV_VERSION: __attribute__(section(".PRVERSN")) []char = "0.0.5-dev.479"
|
||||
V_VPC_VERSION: __attribute__(section(".PRVERSN")) []char = "0.0.5-dev.479"
|
||||
define V_VIPER_VERSION = "1.0.5-dev.970 2026-09-28 Non-LTS"
|
||||
define V_RUNTIME_VERSION = "0.2.3+19-Non-LTS"
|
||||
define V_TPC_COMMIT = "e376b949c9"
|
||||
define V_TPV_COMMIT = "ab0f37e1a8"
|
||||
define V_VPC_COMMIT = "ab0f37e1a8"
|
||||
define V_RUNTIME_COMMIT = "f087aa88e2"
|
||||
define V_TARGET = "x86_64-w64-windows-gnu"
|
||||
define V_LLVM = "22.1.8"
|
||||
@@ -62,11 +59,11 @@ define REPL_PROJ_DIAG_CAP = 2048
|
||||
static a: int
|
||||
# main: Command-line entry
|
||||
def main() -> int:
|
||||
w32cmd.SetConsoleOutputCP(CODE_PAGE)
|
||||
w32cmd.SetConsoleCP(CODE_PAGE)
|
||||
win32cmd.SetConsoleOutputCP(CODE_PAGE)
|
||||
win32cmd.SetConsoleCP(CODE_PAGE)
|
||||
|
||||
detect_flag: int = 0
|
||||
cmd_line_d: str = w32base.GetCommandLineA()
|
||||
cmd_line_d: str = win32base.GetCommandLineA()
|
||||
if cmd_line_d is not None and string.strstr(cmd_line_d, "--detection") is not None: detect_flag = 1
|
||||
|
||||
# --detection-deep: heavy diagnostics (per-alloc probe + periodic full-table scan; extremely slow, off by default); read via raw command-line strstr before MemBuddy
|
||||
@@ -137,13 +134,13 @@ def main() -> int:
|
||||
# initialize VLogger and print the startup log
|
||||
VLogger.init_logger()
|
||||
log: VLogger.Logger = VLogger.logger
|
||||
if log is None: log.error("TransPyV Failed to parse parameters")
|
||||
if log is None: log.error("ViperC Failed to parse parameters")
|
||||
|
||||
# initialize command-line args (Windows: GetCommandLineA, POSIX: /proc/self/cmdline)
|
||||
sys._init_argv()
|
||||
|
||||
# create the argument parser
|
||||
parser: argparse.ArgumentParser = argparse.ArgumentParser("TransPyV", "TransPyV command-line parameter parser", pool=mb)
|
||||
parser: argparse.ArgumentParser = argparse.ArgumentParser("ViperC", "ViperC command-line parameter parser", pool=mb)
|
||||
|
||||
# register arguments (matching Projectrans.py main())
|
||||
parser.add_argument("--project", None, argparse.STRING, 0, None, False, argparse.STORE, "Project VPJ path, by default it looks in the current directory")
|
||||
@@ -166,7 +163,7 @@ def main() -> int:
|
||||
parser.add_argument("--detection-deep", None, argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "Deep detection: probe every allocation and enable the periodic full-table scan (implies --detection; extremely slow, diagnostics only)")
|
||||
parser.add_argument("--show-args", None, argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "Show all parsed arguments")
|
||||
parser.add_argument("--repl", None, argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "Start the interactive REPL (M2 session variables + M3 cross-fragment functions: fragment -> DLL -> call)")
|
||||
parser.add_argument("--build-dir", None, argparse.STRING, 0, None, False, argparse.STORE, "Override project.vpj build_dir (default ./.tpv_build; relative paths resolve against the project dir)")
|
||||
parser.add_argument("--build-dir", None, argparse.STRING, 0, None, False, argparse.STORE, "Override project.vpj build_dir (default ./__vpcbuild__; relative paths resolve against the project dir)")
|
||||
parser.add_argument("--no-llvmpeephole", None, argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "Disable all local mlirlite peephole passes (T2 store/load forward & ret-zero, T3 no-op bitcast) in FunctionPrint serialization")
|
||||
parser.add_argument("--version", "-v", argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "Show version and exit")
|
||||
parser.add_argument("--help", "-h", argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "Show help and exit")
|
||||
@@ -186,7 +183,7 @@ def main() -> int:
|
||||
Copyright (C) 2026 Viper Project
|
||||
License MIT <https://opensource.org/licenses/MIT>
|
||||
GVSDS(TM) Viper SE Runtime Environment and Library (VRTE) %s
|
||||
Viper Frontend Compiler TransPyV %s
|
||||
Viper Frontend Compiler ViperC %s
|
||||
Author: terminexus@gvsds.com
|
||||
|
||||
Tpv commit: %s
|
||||
@@ -194,7 +191,7 @@ Tpc commit: %s
|
||||
VRTE commit: %s
|
||||
Target: %s
|
||||
|
||||
""", V_VIPER_VERSION, V_RUNTIME_VERSION, V_TPV_VERSION, V_TPV_COMMIT, V_TPC_COMMIT, V_RUNTIME_COMMIT, V_TARGET, V_LLVM)
|
||||
""", V_VIPER_VERSION, V_RUNTIME_VERSION, V_VPC_VERSION, V_VPC_COMMIT, V_TPC_COMMIT, V_RUNTIME_COMMIT, V_TARGET, V_LLVM)
|
||||
return 0
|
||||
|
||||
# --repl guard: mutually exclusive with --phase2command / --asan, checked first
|
||||
@@ -212,7 +209,7 @@ Target: %s
|
||||
stdio.printf("FAIL: --repl and --asan are mutually exclusive\n")
|
||||
stdio.printf(" Reason: --asan first instruments the fragment .ll with ASan, but the fragment artifact is a runtime\n")
|
||||
stdio.printf(" DLL loaded by LoadLibraryA, and the host process does not link the ASan runtime; instrumentation semantics are undefined\n")
|
||||
stdio.printf(" Fix: drop --asan (to debug memory issues, build an ASan TransPyV in project mode)\n")
|
||||
stdio.printf(" Fix: drop --asan (to debug memory issues, build an ASan ViperC in project mode)\n")
|
||||
argparse.release(args)
|
||||
return 2
|
||||
# --traceback is also rejected under --repl: the fragment is a runtime-loaded
|
||||
@@ -221,7 +218,7 @@ Target: %s
|
||||
stdio.printf("FAIL: --repl and --traceback are mutually exclusive\n")
|
||||
stdio.printf(" Reason: --traceback relies on startup-ctor injection + a per-module DWARF side file,\n")
|
||||
stdio.printf(" but the fragment is a runtime LoadLibraryA-loaded DLL; the two semantics are undefined\n")
|
||||
stdio.printf(" Fix: drop --traceback (for crash stacks, build a --traceback TransPyV in project mode)\n")
|
||||
stdio.printf(" Fix: drop --traceback (for crash stacks, build a --traceback ViperC in project mode)\n")
|
||||
argparse.release(args)
|
||||
return 2
|
||||
|
||||
@@ -229,7 +226,7 @@ Target: %s
|
||||
# after project-config load and before Phase1/Phase2, then returns.
|
||||
|
||||
# print parse result
|
||||
# log.banner("TransPyV argument parse result")
|
||||
# log.banner("ViperC argument parse result")
|
||||
|
||||
# string arguments
|
||||
project: str = args.get_str("project")
|
||||
@@ -328,7 +325,7 @@ Target: %s
|
||||
else:
|
||||
stdio.printf("FAIL: --repl could not find the project file repl.vpj\n")
|
||||
stdio.printf("%s", repl_diag_buf)
|
||||
stdio.printf(" You can also pass --project to specify the project file explicitly (e.g. TransPyV/project.vpj in this repo)\n")
|
||||
stdio.printf(" You can also pass --project to specify the project file explicitly (e.g. ViperC/project.vpj in this repo)\n")
|
||||
argparse.release(args)
|
||||
return 1
|
||||
# load project config (explicit --project / repl.vpj found by --repl)
|
||||
@@ -350,7 +347,7 @@ Target: %s
|
||||
# extract project.vpj's dir so relative paths resolve against it
|
||||
proj_len: sizet = string.strlen(proj_path)
|
||||
slash_pos: sizet = proj_len
|
||||
for i in range(proj_len):
|
||||
for i in range(int(proj_len)):
|
||||
idx: sizet = proj_len - 1 - i
|
||||
ch: char = proj_path[idx]
|
||||
if ch == '/' or ch == '\\':
|
||||
@@ -474,7 +471,7 @@ Target: %s
|
||||
|
||||
mf_ret: int = Phase2.RunMultiFileProject(mb, Config.SourceDir, mf_temp, mf_output, mf_cc, mf_cc_flags, mf_linker, mf_linker_flags, mf_linker_out, mf_includes_bin, Config.IncludesDirs, do_phase1, do_phase2, log, args, mf_static_libs, mf_dynamic_libs)
|
||||
argparse.release(args)
|
||||
log.success("TransPyV complete")
|
||||
log.success("ViperC complete")
|
||||
return mf_ret
|
||||
|
||||
# Reaching here means no buildable project root (SourceDir missing): fail fast, no silent "done"
|
||||
|
||||
Reference in new issue
Block a user