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
|
||||
@@ -1030,7 +1031,7 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: VLogg
|
||||
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
|
||||
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
import lib.core.Gen.GenApi as GenApi
|
||||
import t, c
|
||||
from stdint import *
|
||||
import stdio
|
||||
import string
|
||||
import stdlib
|
||||
import memhub
|
||||
import viperlib
|
||||
import w32.fileio as fileio
|
||||
import w32.win32file
|
||||
import w32.win32base
|
||||
import win32.fileio as fileio
|
||||
import win32.win32file
|
||||
import win32.win32base
|
||||
import sys
|
||||
import ast
|
||||
import mlirlite
|
||||
@@ -38,8 +37,7 @@ define SRC_BUF_SIZE = 1048576
|
||||
# Max source files (recursive scanning grows the count; too small truncates the list -> undefined symbol)
|
||||
define MAX_SRC_FILES = 128
|
||||
|
||||
@t.NoVTable
|
||||
class SrcFileEntry:
|
||||
novirtual class SrcFileEntry:
|
||||
"""Source file entry"""
|
||||
Path: str # full path
|
||||
Sha1: str # first 16 chars of SHA1
|
||||
@@ -56,15 +54,15 @@ def _ScanDirForPyFiles(mb: memhub.MemBuddy*, dir_path: str, entries: SrcFileEntr
|
||||
if pattern is None: return file_count
|
||||
viperlib.snprintf(pattern, dir_len + 16, "%s/*", dir_path)
|
||||
|
||||
find_data_size: sizet = w32.win32file.WIN32_FIND_DATAA.__sizeof__()
|
||||
find_data: w32.win32file.WIN32_FIND_DATAA* = stdlib.malloc(find_data_size + 16)
|
||||
find_data_size: sizet = win32.win32file.WIN32_FIND_DATAA.__sizeof__()
|
||||
find_data: win32.win32file.WIN32_FIND_DATAA* = stdlib.malloc(find_data_size + 16)
|
||||
if find_data is None:
|
||||
stdlib.free(pattern)
|
||||
return file_count
|
||||
string.memset(find_data, 0, find_data_size + 16)
|
||||
|
||||
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 file_count
|
||||
@@ -72,30 +70,30 @@ def _ScanDirForPyFiles(mb: memhub.MemBuddy*, dir_path: str, entries: SrcFileEntr
|
||||
while True:
|
||||
fname: str = find_data.cFileName
|
||||
if fname is None:
|
||||
if w32.win32file.FindNextFileA(handle, find_data) == 0: break
|
||||
if win32.win32file.FindNextFileA(handle, find_data) == 0: break
|
||||
continue
|
||||
fname_len: sizet = string.strlen(fname)
|
||||
if fname_len <= 0:
|
||||
if w32.win32file.FindNextFileA(handle, find_data) == 0: break
|
||||
if win32.win32file.FindNextFileA(handle, find_data) == 0: break
|
||||
continue
|
||||
# Skip . and ..
|
||||
is_dot: int = 0
|
||||
if fname_len == 1 and fname[0] == '.': is_dot = 1
|
||||
elif fname_len == 2 and fname[0] == '.' and fname[1] == '.': is_dot = 1
|
||||
if is_dot != 0:
|
||||
if w32.win32file.FindNextFileA(handle, find_data) == 0: break
|
||||
if win32.win32file.FindNextFileA(handle, find_data) == 0: break
|
||||
continue
|
||||
# Check whether it is a directory
|
||||
attrs: ULONG = find_data.dwFileAttributes
|
||||
is_dir: int = 0
|
||||
if (attrs & w32.win32file.FILE_ATTRIBUTE_DIRECTORY) != 0: is_dir = 1
|
||||
if (attrs & win32.win32file.FILE_ATTRIBUTE_DIRECTORY) != 0: is_dir = 1
|
||||
if is_dir != 0:
|
||||
# Recurse into the subdirectory
|
||||
sub_dir: bytes = Utils.JoinPath(dir_path, fname)
|
||||
if sub_dir is not None:
|
||||
file_count = _ScanDirForPyFiles(mb, sub_dir, entries, entry_size, file_count, max_files)
|
||||
stdlib.free(sub_dir)
|
||||
if w32.win32file.FindNextFileA(handle, find_data) == 0: break
|
||||
if win32.win32file.FindNextFileA(handle, find_data) == 0: break
|
||||
continue
|
||||
# Check for a .vp source file (Viper sources are only .vp now; .py is no longer recognized)
|
||||
if fname_len > 3 and Utils.is_vp_at(fname, 0, fname_len) != 0 and file_count < max_files:
|
||||
@@ -103,7 +101,7 @@ def _ScanDirForPyFiles(mb: memhub.MemBuddy*, dir_path: str, entries: SrcFileEntr
|
||||
if full_path is not None:
|
||||
sbuf: bytes = Utils.read_file_to_buf(full_path, SRC_BUF_SIZE)
|
||||
if sbuf is None:
|
||||
if w32.win32file.FindNextFileA(handle, find_data) == 0: break
|
||||
if win32.win32file.FindNextFileA(handle, find_data) == 0: break
|
||||
continue
|
||||
# Use IncludesScanner.compute_file_sha1 (CRLF->LF normalized) as StubMerger does for the same
|
||||
# App sources; raw-byte hashing makes stub/define symbol prefixes differ -> undefined reference.
|
||||
@@ -120,16 +118,16 @@ def _ScanDirForPyFiles(mb: memhub.MemBuddy*, dir_path: str, entries: SrcFileEntr
|
||||
stdlib.free(sbuf)
|
||||
# full_path is not freed: ent.Path references it
|
||||
|
||||
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 file_count
|
||||
|
||||
|
||||
# _ScanDirForAsmFiles - recursively scan a dir and collect .s assembly files.
|
||||
# Like _ScanDirForPyFiles; TPV does not translate .s but assembles it to .obj via llvm-mc for linking.
|
||||
# Like _ScanDirForPyFiles; VPC does not translate .s but assembles it to .obj via llvm-mc for linking.
|
||||
def _ScanDirForAsmFiles(mb: memhub.MemBuddy*, dir_path: str, entries: SrcFileEntry*, entry_size: sizet, file_count: int, max_files: int) -> int:
|
||||
"""Recursively scan a directory, collect .s assembly files into entries, return the new file_count"""
|
||||
if dir_path is None or entries is None: return file_count
|
||||
@@ -140,15 +138,15 @@ def _ScanDirForAsmFiles(mb: memhub.MemBuddy*, dir_path: str, entries: SrcFileEnt
|
||||
if pattern is None: return file_count
|
||||
viperlib.snprintf(pattern, dir_len + 16, "%s/*", dir_path)
|
||||
|
||||
find_data_size: sizet = w32.win32file.WIN32_FIND_DATAA.__sizeof__()
|
||||
find_data: w32.win32file.WIN32_FIND_DATAA* = stdlib.malloc(find_data_size + 16)
|
||||
find_data_size: sizet = win32.win32file.WIN32_FIND_DATAA.__sizeof__()
|
||||
find_data: win32.win32file.WIN32_FIND_DATAA* = stdlib.malloc(find_data_size + 16)
|
||||
if find_data is None:
|
||||
stdlib.free(pattern)
|
||||
return file_count
|
||||
string.memset(find_data, 0, find_data_size + 16)
|
||||
|
||||
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 file_count
|
||||
@@ -156,29 +154,29 @@ def _ScanDirForAsmFiles(mb: memhub.MemBuddy*, dir_path: str, entries: SrcFileEnt
|
||||
while True:
|
||||
fname: str = find_data.cFileName
|
||||
if fname is None:
|
||||
if w32.win32file.FindNextFileA(handle, find_data) == 0: break
|
||||
if win32.win32file.FindNextFileA(handle, find_data) == 0: break
|
||||
continue
|
||||
fname_len: sizet = string.strlen(fname)
|
||||
if fname_len <= 0:
|
||||
if w32.win32file.FindNextFileA(handle, find_data) == 0: break
|
||||
if win32.win32file.FindNextFileA(handle, find_data) == 0: break
|
||||
continue
|
||||
# Skip . and ..
|
||||
is_dot: int = 0
|
||||
if fname_len == 1 and fname[0] == '.': is_dot = 1
|
||||
elif fname_len == 2 and fname[0] == '.' and fname[1] == '.': is_dot = 1
|
||||
if is_dot != 0:
|
||||
if w32.win32file.FindNextFileA(handle, find_data) == 0: break
|
||||
if win32.win32file.FindNextFileA(handle, find_data) == 0: break
|
||||
continue
|
||||
# Check whether it is a directory
|
||||
attrs: ULONG = find_data.dwFileAttributes
|
||||
is_dir: int = 0
|
||||
if (attrs & w32.win32file.FILE_ATTRIBUTE_DIRECTORY) != 0: is_dir = 1
|
||||
if (attrs & win32.win32file.FILE_ATTRIBUTE_DIRECTORY) != 0: is_dir = 1
|
||||
if is_dir != 0:
|
||||
sub_dir: bytes = Utils.JoinPath(dir_path, fname)
|
||||
if sub_dir is not None:
|
||||
file_count = _ScanDirForAsmFiles(mb, sub_dir, entries, entry_size, file_count, max_files)
|
||||
stdlib.free(sub_dir)
|
||||
if w32.win32file.FindNextFileA(handle, find_data) == 0: break
|
||||
if win32.win32file.FindNextFileA(handle, find_data) == 0: break
|
||||
continue
|
||||
# Check for the .s suffix (assembly source)
|
||||
is_asm: int = 0
|
||||
@@ -199,9 +197,9 @@ def _ScanDirForAsmFiles(mb: memhub.MemBuddy*, dir_path: str, entries: SrcFileEnt
|
||||
VLogger.info(f"{fname} (asm sha1={sha1_val})", "project")
|
||||
# full_path is not freed: ent.Path references it
|
||||
|
||||
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 file_count
|
||||
@@ -213,7 +211,7 @@ def _PreRegisterAppGenericTemplates(mb: memhub.MemBuddy*, entries: SrcFileEntry*
|
||||
"""Pre-register App source generic class templates before mark2, return the processed count"""
|
||||
if mb is None or entries is None or file_count <= 0: return 0
|
||||
handled: int = 0
|
||||
for i in range(file_count):
|
||||
for i in range(int(file_count)):
|
||||
ea_pa: uint64 = uint64(entries) + i * entry_size
|
||||
ent_pa: SrcFileEntry* = (SrcFileEntry*)(ea_pa)
|
||||
if ent_pa is None or ent_pa.Path is None or ent_pa.Sha1 is None: continue
|
||||
@@ -252,7 +250,7 @@ def _PreRegisterAppGenericTemplates(mb: memhub.MemBuddy*, entries: SrcFileEntry*
|
||||
ch_pa: list[ast.AST*]* = tree_pa.children
|
||||
if ch_pa is not None:
|
||||
cn_pa: sizet = len(ch_pa)
|
||||
for ci_pa in range(cn_pa):
|
||||
for ci_pa in range(int(cn_pa)):
|
||||
child_pa: ast.AST* = ch_pa.get(ci_pa)
|
||||
if child_pa is None: continue
|
||||
ck_pa: int = child_pa.kind()
|
||||
@@ -324,7 +322,7 @@ def _PhaseADiagRecord(sha1: str, deps: str, miss: str) -> None:
|
||||
global _pa_diag_count
|
||||
if _pa_diag_sha1 is None or _pa_diag_deps is None or sha1 is None: return
|
||||
if string.strlen(sha1) < 16: return
|
||||
for i in range(_pa_diag_count):
|
||||
for i in range(int(_pa_diag_count)):
|
||||
es: bytes = _pa_diag_sha1 + sizet(i) * 17
|
||||
if string.strcmp(es, sha1) == 0:
|
||||
if miss is not None and _pa_diag_miss is not None:
|
||||
@@ -349,7 +347,7 @@ def _PhaseADiagRecord(sha1: str, deps: str, miss: str) -> None:
|
||||
def _PhaseADiagDepsLookup(sha1: str) -> str:
|
||||
"""Look up the persisted deps string by sha1; caller frees the copy, None if absent"""
|
||||
if _pa_diag_sha1 is None or _pa_diag_deps is None or sha1 is None: return None
|
||||
for i in range(_pa_diag_count):
|
||||
for i in range(int(_pa_diag_count)):
|
||||
es: bytes = _pa_diag_sha1 + sizet(i) * 17
|
||||
if string.strcmp(es, sha1) == 0:
|
||||
src: bytes = _pa_diag_deps + sizet(i) * PA_DEPS_MAX_STR
|
||||
@@ -364,7 +362,7 @@ def _PhaseADiagDepsLookup(sha1: str) -> str:
|
||||
def _PhaseADiagMissLookup(sha1: str) -> str:
|
||||
"""Look up the persisted missing detail by sha1; caller frees the copy, None if absent"""
|
||||
if _pa_diag_sha1 is None or _pa_diag_miss is None or sha1 is None: return None
|
||||
for i in range(_pa_diag_count):
|
||||
for i in range(int(_pa_diag_count)):
|
||||
es: bytes = _pa_diag_sha1 + sizet(i) * 17
|
||||
if string.strcmp(es, sha1) == 0:
|
||||
src: bytes = _pa_diag_miss + sizet(i) * PA_MISS_MAX_STR
|
||||
@@ -447,7 +445,7 @@ def _PhaseADumpNode(temp_dir: str, td_len: sizet, sha1: str, depth: int, prefix:
|
||||
dep_sha1: str = _PhaseAResolveDepSha1(nm)
|
||||
txt_ok: int = 0
|
||||
if dep_sha1 is not None: txt_ok = _PhaseATextLLExists(temp_dir, td_len, dep_sha1)
|
||||
# Branch characters are inlined (don't return string literals: TPV would return a dangling
|
||||
# Branch characters are inlined (don't return string literals: VPC would return a dangling
|
||||
# stack pointer for a literal -> printf %s crashes and the graph output is swallowed)
|
||||
br_1: str = "|-- "
|
||||
br_2: str = "`-- "
|
||||
@@ -580,7 +578,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
|
||||
line_am: bytes = stdlib.malloc(512)
|
||||
# First fill the in-memory store (no file I/O), then write _sha1_map.txt
|
||||
app_filled: int = 0
|
||||
for i in range(file_count):
|
||||
for i in range(int(file_count)):
|
||||
ea_am: uint64 = uint64(entries) + i * entry_size
|
||||
ent_am: SrcFileEntry* = (SrcFileEntry*)(ea_am)
|
||||
if ent_am is None or ent_am.Path is None or ent_am.Sha1 is None: continue
|
||||
@@ -614,7 +612,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
|
||||
td_len_vpa: sizet = string.strlen(temp_dir)
|
||||
PYI_BUF_SIZE_P2: sizet = 262144
|
||||
vpi_failed_p2: int = 0
|
||||
for i_vp2 in range(file_count):
|
||||
for i_vp2 in range(int(file_count)):
|
||||
ea_vp2: uint64 = uint64(entries) + i_vp2 * entry_size
|
||||
ent_vp2: SrcFileEntry* = (SrcFileEntry*)(ea_vp2)
|
||||
if ent_vp2 is None or ent_vp2.Path is None or ent_vp2.Sha1 is None:
|
||||
@@ -726,15 +724,15 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
|
||||
# (no silent downgrade). App files must be skipped here; Phase A-pre handles them.
|
||||
inc_fail_flags: bytes = stdlib.malloc(store_count_inc_pre)
|
||||
if inc_fail_flags is not None: string.memset(inc_fail_flags, 1, store_count_inc_pre)
|
||||
for pass_inc_i in range(PHASE_A_INC_MAX_PASSES):
|
||||
for pass_inc_i in range(int(PHASE_A_INC_MAX_PASSES)):
|
||||
struct_count_before_inc: int = GenApi.get_struct_count()
|
||||
for si_inc in range(store_count_inc_pre):
|
||||
for si_inc in range(int(store_count_inc_pre)):
|
||||
inc_sha1_pre: str = store_sha1_inc_pre + sizet(si_inc) * 17
|
||||
inc_rel_pre: str = store_rel_inc_pre + sizet(si_inc) * StubMerger.MAX_REL_PATH_LEN
|
||||
if inc_rel_pre is None or inc_rel_pre[0] == '\0': continue
|
||||
# Skip App source files (main.vp etc.): pre-registered by Phase A-pre, not a failure
|
||||
is_app_file_inc: int = 0
|
||||
for i_app_inc in range(file_count):
|
||||
for i_app_inc in range(int(file_count)):
|
||||
ea_app_inc: uint64 = uint64(entries) + i_app_inc * entry_size
|
||||
ent_app_inc: SrcFileEntry* = (SrcFileEntry*)(ea_app_inc)
|
||||
if ent_app_inc is not None and ent_app_inc.Sha1 is not None and string.strcmp(ent_app_inc.Sha1, inc_sha1_pre) == 0:
|
||||
@@ -771,7 +769,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
|
||||
if struct_count_after_inc == struct_count_before_inc: break
|
||||
# Includes still failing after all passes -> abort (no downgrade)
|
||||
if inc_fail_flags is not None:
|
||||
for si_inc_f in range(store_count_inc_pre):
|
||||
for si_inc_f in range(int(store_count_inc_pre)):
|
||||
if inc_fail_flags[si_inc_f] != 0:
|
||||
rel_f: str = store_rel_inc_pre + sizet(si_inc_f) * StubMerger.MAX_REL_PATH_LEN
|
||||
VLogger.error(f"includes pre-registration failed: {rel_f} (file cannot be translated; possible syntax/parse/compile error), aborting compilation", "prePhaseA")
|
||||
@@ -783,9 +781,9 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
|
||||
VLogger.info("pre-registering struct/enum/union...", "PhaseA")
|
||||
src_dir_len_pre: sizet = string.strlen(source_dir)
|
||||
PHASE_A_PRE_MAX_PASSES: int = 3
|
||||
for pass_i in range(PHASE_A_PRE_MAX_PASSES):
|
||||
for pass_i in range(int(PHASE_A_PRE_MAX_PASSES)):
|
||||
struct_count_before: int = GenApi.get_struct_count()
|
||||
for i in range(file_count):
|
||||
for i in range(int(file_count)):
|
||||
ea_pre: uint64 = uint64(entries) + i * entry_size
|
||||
ent_pre: SrcFileEntry* = (SrcFileEntry*)(ea_pre)
|
||||
if ent_pre is None or ent_pre.Path is None: continue
|
||||
@@ -830,14 +828,14 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
|
||||
return 1
|
||||
string.memset(done_flags, 0, file_count)
|
||||
PHASE_A_MAX_PASSES: int = 32
|
||||
for pass_a in range(PHASE_A_MAX_PASSES):
|
||||
for pass_a in range(int(PHASE_A_MAX_PASSES)):
|
||||
completed_this_pass: int = 0
|
||||
for i in range(file_count):
|
||||
for i in range(int(file_count)):
|
||||
if done_flags[i] != 0: continue
|
||||
ea: uint64 = uint64(entries) + i * entry_size
|
||||
ent: SrcFileEntry* = (SrcFileEntry*)(ea)
|
||||
# Timing point 0: start processing this file (translate phase start)
|
||||
t_pa0: uint64 = w32.win32base.GetTickCount64()
|
||||
t_pa0: uint64 = win32.win32base.GetTickCount64()
|
||||
if ent is None or ent.Path is None:
|
||||
done_flags[i] = 1
|
||||
completed_this_pass += 1
|
||||
@@ -850,7 +848,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
|
||||
pkg_pa = GenApi.compute_package_from_relpath(mb, rel_path_pa)
|
||||
tr_a: HandlesTranslator.Translator* = BuildPipeline.TranslateFileGetTrans(mb, ent.Path, ent.Sha1, pkg_pa, 0, temp_dir)
|
||||
# Timing point 1: translate returned
|
||||
t_pa1: uint64 = w32.win32base.GetTickCount64()
|
||||
t_pa1: uint64 = win32.win32base.GetTickCount64()
|
||||
if tr_a is None:
|
||||
# Translation failed (syntax/parse error): retry next pass; convergence check reports it.
|
||||
# OOM cleanup: a failed translation also produced AST/tokens, freed immediately.
|
||||
@@ -955,7 +953,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
|
||||
stdlib.free(deps_path_a)
|
||||
|
||||
# Timing point 2: stub+text dump and write done; print per-file progress (as in Phase1 1b)
|
||||
t_pa2: uint64 = w32.win32base.GetTickCount64()
|
||||
t_pa2: uint64 = win32.win32base.GetTickCount64()
|
||||
VLogger.info(f"[{i+1}/{file_count}] PhaseA: {ent.Path} (translate={t_pa1-t_pa0} ms, dump={t_pa2-t_pa1} ms, total={t_pa2-t_pa0} ms)", "PhaseA")
|
||||
|
||||
done_flags[i] = 1
|
||||
@@ -965,7 +963,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
|
||||
mb.reset_to(mark2)
|
||||
# Convergence check: stop when all files are done
|
||||
all_done: int = 1
|
||||
for i_ck in range(file_count):
|
||||
for i_ck in range(int(file_count)):
|
||||
if done_flags[i_ck] == 0:
|
||||
all_done = 0
|
||||
break
|
||||
@@ -974,7 +972,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
|
||||
# No progress: remaining files cannot finish (real compile errors). Abort and list them, each
|
||||
# with a dependency-graph dump. VLogger.error calls sys.exit(1), so it must come after all output.
|
||||
td_len_fail: sizet = string.strlen(temp_dir)
|
||||
for i_err in range(file_count):
|
||||
for i_err in range(int(file_count)):
|
||||
if done_flags[i_err] == 0:
|
||||
ea_err: uint64 = uint64(entries) + i_err * entry_size
|
||||
ent_err: SrcFileEntry* = (SrcFileEntry*)(ea_err)
|
||||
@@ -1026,12 +1024,12 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
|
||||
return 1
|
||||
llc_task_count: int = 0
|
||||
|
||||
for i in range(file_count):
|
||||
for i in range(int(file_count)):
|
||||
ea: uint64 = uint64(entries) + i * entry_size
|
||||
ent: SrcFileEntry* = (SrcFileEntry*)(ea)
|
||||
if ent is None or ent.Path is None or ent.Sha1 is None: continue
|
||||
# Start of this file's three-step timing (GetTickCount64, ms)
|
||||
t_pb0: uint64 = w32.win32base.GetTickCount64()
|
||||
t_pb0: uint64 = win32.win32base.GetTickCount64()
|
||||
# Read the in-memory FULL artifact {sha1}.full.ll -> complete IR (replaces merging stub + dep stub/text)
|
||||
combined_ir: bytes = stdlib.malloc(COMBINED_IR_SIZE)
|
||||
if combined_ir is None:
|
||||
@@ -1043,7 +1041,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
|
||||
stdlib.free(combined_ir)
|
||||
continue
|
||||
# Timing point 1: full.ll read done
|
||||
t_pb1: uint64 = w32.win32base.GetTickCount64()
|
||||
t_pb1: uint64 = win32.win32base.GetTickCount64()
|
||||
|
||||
# Write .ll (llc threads only read this file); llc itself runs parallel in batches after the loop
|
||||
ret_w: int = BuildPipeline.write_ir_to_file(combined_ir, combined_len, temp_dir, ent.Sha1)
|
||||
@@ -1052,7 +1050,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
|
||||
VLogger.error(f"failed to write .ll: {ent.Path}", "PhaseB")
|
||||
sys.exit(1)
|
||||
# Timing point 2: .ll write done
|
||||
t_pb2: uint64 = w32.win32base.GetTickCount64()
|
||||
t_pb2: uint64 = win32.win32base.GetTickCount64()
|
||||
|
||||
# Build the llc task (command string is stdlib.malloc'd so threads' lifetimes are not reset-reclaimed)
|
||||
llc_ir_path: str = BuildPipeline.build_sliced_path(temp_dir, ent.Sha1, "ll")
|
||||
@@ -1064,7 +1062,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
|
||||
VLogger.error(f"failed to build llc task: {ent.Path}", "PhaseB")
|
||||
sys.exit(1)
|
||||
# Timing point 3: llc task construction done
|
||||
t_pb3: uint64 = w32.win32base.GetTickCount64()
|
||||
t_pb3: uint64 = win32.win32base.GetTickCount64()
|
||||
llc_task_count += 1
|
||||
|
||||
compiled_count += 1
|
||||
@@ -1114,7 +1112,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
|
||||
# The obj is added to obj_paths for Phase C linking.
|
||||
od_len_as: sizet = string.strlen(output_dir)
|
||||
asm_compiled: int = 0
|
||||
for ai_as in range(asm_file_count):
|
||||
for ai_as in range(int(asm_file_count)):
|
||||
ea_as: uint64 = uint64(asm_entries) + ai_as * entry_size
|
||||
ent_as: SrcFileEntry* = (SrcFileEntry*)(ea_as)
|
||||
if ent_as is None or ent_as.Path is None or ent_as.Sha1 is None: continue
|
||||
@@ -1203,16 +1201,16 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
|
||||
else:
|
||||
topo_count = StubMergerTopo.BuildTopoSortedIndices(store_count_mi, store_sha1_arr_mi, store_mod_arr_mi, reachable_set, reachable_count, topo_idx)
|
||||
|
||||
for si_mi in range(topo_count):
|
||||
for si_mi in range(int(topo_count)):
|
||||
# Current entry (topological index; reachable modules only)
|
||||
ti: int = topo_idx[si_mi]
|
||||
ti: int = int(topo_idx[si_mi])
|
||||
# Get the current entry's SHA1 and rel_path
|
||||
inc_sha1_mi: str = store_sha1_arr_mi + sizet(ti) * 17
|
||||
inc_rel_mi: str = store_rel_arr_mi + sizet(ti) * StubMerger.MAX_REL_PATH_LEN
|
||||
# Skip App sources: an App file may be scanned as an include and be reachable; compiling
|
||||
# another .obj into includes_binary would duplicate the output/ .obj's symbols.
|
||||
app_skip_mi: int = 0
|
||||
for ai_mi in range(file_count):
|
||||
for ai_mi in range(int(file_count)):
|
||||
ea_ap: uint64 = uint64(entries) + ai_mi * entry_size
|
||||
ent_ap: SrcFileEntry* = (SrcFileEntry*)(ea_ap)
|
||||
if ent_ap is not None and ent_ap.Sha1 is not None:
|
||||
@@ -1223,13 +1221,13 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
|
||||
# Check whether the .obj already exists in includes.binary (sliced subdir)
|
||||
ibd_len_mi: sizet = string.strlen(includes_binary_dir)
|
||||
check_pat_mi: str = StubMergerCollect._sliced_path(includes_binary_dir, ibd_len_mi, inc_sha1_mi, "obj")
|
||||
check_fd_mi: w32.win32file.WIN32_FIND_DATAA* = stdlib.malloc(w32.win32file.WIN32_FIND_DATAA.__sizeof__())
|
||||
check_fd_mi: win32.win32file.WIN32_FIND_DATAA* = stdlib.malloc(win32.win32file.WIN32_FIND_DATAA.__sizeof__())
|
||||
obj_exists_mi: int = 0
|
||||
if check_pat_mi is not None and check_fd_mi is not None:
|
||||
string.memset(check_fd_mi, 0, w32.win32file.WIN32_FIND_DATAA.__sizeof__())
|
||||
check_h_mi: w32.win32base.HANDLE = w32.win32file.FindFirstFileA(check_pat_mi, check_fd_mi)
|
||||
if check_h_mi != w32.win32base.INVALID_HANDLE_VALUE:
|
||||
w32.win32file.FindClose(check_h_mi)
|
||||
string.memset(check_fd_mi, 0, win32.win32file.WIN32_FIND_DATAA.__sizeof__())
|
||||
check_h_mi: win32.win32base.HANDLE = win32.win32file.FindFirstFileA(check_pat_mi, check_fd_mi)
|
||||
if check_h_mi != win32.win32base.INVALID_HANDLE_VALUE:
|
||||
win32.win32file.FindClose(check_h_mi)
|
||||
obj_exists_mi = 1
|
||||
if obj_exists_mi != 0:
|
||||
if check_pat_mi is not None: stdlib.free(check_pat_mi)
|
||||
@@ -1452,12 +1450,12 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
|
||||
ibd_len_pc: sizet = string.strlen(includes_binary_dir)
|
||||
td_len_pc: sizet = string.strlen(temp_dir)
|
||||
ri_pc: int
|
||||
for ri_pc in range(reachable_count):
|
||||
for ri_pc in range(int(reachable_count)):
|
||||
rsha1_pc: str = reachable_set + sizet(ri_pc) * 17
|
||||
if rsha1_pc[0] == '\0': continue
|
||||
# Skip App sources: their .obj is already in final_obj_paths; collecting a same-sha1 .obj from includes_binary would duplicate symbols.
|
||||
app_skip_pc: int = 0
|
||||
for ai_pc in range(file_count):
|
||||
for ai_pc in range(int(file_count)):
|
||||
ea_pc: uint64 = uint64(entries) + ai_pc * entry_size
|
||||
ent_pc: SrcFileEntry* = (SrcFileEntry*)(ea_pc)
|
||||
if ent_pc is not None and ent_pc.Sha1 is not None:
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
import t, c
|
||||
from stdint import *
|
||||
import memhub
|
||||
import string
|
||||
import stdio
|
||||
import stdlib
|
||||
import viperlib
|
||||
import lib.core.VLogger as VLogger
|
||||
from json.__parser import parse as json_parse
|
||||
from json import JsonValue, JSON_OBJECT, JSON_INT
|
||||
import w32.fileio as fileio
|
||||
import win32.fileio as fileio
|
||||
|
||||
|
||||
# Config - project configuration
|
||||
@@ -84,10 +82,10 @@ def Load_project_config(path: str) -> int:
|
||||
# Read top-level fields (explicit __getitem__ calls for old compiler binaries)
|
||||
sd_val: JsonValue* = root["source_dir"]
|
||||
SourceDir = sd_val.as_string() if sd_val is not None else None
|
||||
# build_dir: unified build dir (default ./.tpv_build); temp and output are subdirs
|
||||
# build_dir: unified build dir (default ./__vpcbuild__); temp and output are subdirs
|
||||
bd_val: JsonValue* = root["build_dir"]
|
||||
if bd_val is not None and bd_val.is_string(): BuildDir = bd_val.as_string()
|
||||
else: BuildDir = "./.tpv_build"
|
||||
else: BuildDir = "./__vpcbuild__"
|
||||
name_val: JsonValue* = root["name"]
|
||||
ProjectName = name_val.as_string() if name_val is not None else None
|
||||
ver_val: JsonValue* = root["version"]
|
||||
@@ -112,6 +110,17 @@ def Load_project_config(path: str) -> int:
|
||||
# Read linker.flags array, join into a space-separated string
|
||||
ld_flags_val: JsonValue* = linker["flags"]
|
||||
LinkerFlags = _join_flags_array(ld_flags_val)
|
||||
# mingw-w64:同时链接 -lmsvcrt 与 -lucrt 时,GNU ld 会从两个 import 库各拉出一个都定义
|
||||
# __imp_<name> 的成员(现场:__imp_ldexp 同时出现在 libmsvcrt 与
|
||||
# libapi-ms-win-crt-math),直接报 "multiple definition"。lld 按需解析 import 库不会冲突,
|
||||
# 仓库默认链接命令也一直是 lld;项目配置漏写 -fuse-ld 时在这里补齐,避免逐个测试改配置。
|
||||
if LinkerCmd is not None and LinkerFlags is not None and string.strstr(LinkerCmd, "clang") is not None:
|
||||
if string.strstr(LinkerFlags, "-fuse-ld") is None and string.strstr(LinkerFlags, "-lucrt") is not None:
|
||||
lf_cap: sizet = string.strlen(LinkerFlags) + 32
|
||||
lf_new: str = _mbuddy.alloc(lf_cap)
|
||||
if lf_new is not None:
|
||||
viperlib.snprintf(lf_new, lf_cap, "-fuse-ld=lld %s", LinkerFlags)
|
||||
LinkerFlags = lf_new
|
||||
# Read linker.static_libs array: explicit static libs (.a/.obj); joined raw here,
|
||||
# then resolved to abs paths against project_dir in resolve_paths.
|
||||
sl_val: JsonValue* = linker["static_libs"]
|
||||
@@ -136,7 +145,7 @@ def Load_project_config(path: str) -> int:
|
||||
StrictMode = 1
|
||||
if options is not None and options.is_object():
|
||||
sl: JsonValue* = options["sha1_slice_level"]
|
||||
if sl is not None and sl.is_int(): Sha1SliceLevel = sl.as_int()
|
||||
if sl is not None and sl.is_int(): Sha1SliceLevel = int(sl.as_int())
|
||||
sm: JsonValue* = options["strict_mode"]
|
||||
if sm is not None and sm.is_bool(): StrictMode = 1 if sm.as_bool() else 0
|
||||
|
||||
@@ -148,7 +157,7 @@ def Load_project_config(path: str) -> int:
|
||||
if inc_buf is not None:
|
||||
inc_buf[0] = '\0'
|
||||
inc_pos: sizet = 0
|
||||
for inc_i in range(arr_len):
|
||||
for inc_i in range(int(arr_len)):
|
||||
inc_item: JsonValue* = includes_val.get_item(inc_i)
|
||||
if inc_item is None or not inc_item.is_string(): continue
|
||||
inc_str: str = inc_item.as_string()
|
||||
@@ -189,7 +198,7 @@ def _join_flags_array(flags_val: JsonValue*) -> str:
|
||||
|
||||
# First pass: compute total length (all flags + space separators)
|
||||
total_len: sizet = 0
|
||||
for i in range(arr_len):
|
||||
for i in range(int(arr_len)):
|
||||
item: JsonValue* = flags_val.get_item(i)
|
||||
if item is not None and item.is_string():
|
||||
s: str = item.as_string()
|
||||
@@ -204,7 +213,7 @@ def _join_flags_array(flags_val: JsonValue*) -> str:
|
||||
|
||||
# Second pass: join (flags separated by spaces)
|
||||
pos: sizet = 0
|
||||
for i in range(arr_len):
|
||||
for i in range(int(arr_len)):
|
||||
item = flags_val.get_item(i)
|
||||
if item is not None and item.is_string():
|
||||
s = item.as_string()
|
||||
@@ -282,7 +291,7 @@ def _normalize_path(path: str) -> str:
|
||||
seg_start: sizet = 0
|
||||
out_len: sizet = 0
|
||||
has_seg: int = 0
|
||||
for i in range(plen + 1):
|
||||
for i in range(int(plen + 1)):
|
||||
ch: char = '\0'
|
||||
if i < plen: ch = path[i]
|
||||
if ch == '/' or ch == '\\' or ch == '\0':
|
||||
@@ -320,7 +329,7 @@ def _normalize_path(path: str) -> str:
|
||||
if has_seg > 0:
|
||||
out[out_len] = '/'
|
||||
out_len += 1
|
||||
for jj in range(seg_len):
|
||||
for jj in range(int(seg_len)):
|
||||
out[out_len] = path[seg_start + jj]
|
||||
out_len += 1
|
||||
has_seg = 1
|
||||
@@ -589,11 +598,11 @@ def slice_subdir(sha1: str, level: int) -> str:
|
||||
buf: str = _mbuddy.alloc(buf_len)
|
||||
if buf is None: return None
|
||||
pos: int = 0
|
||||
for i in range(level):
|
||||
for i in range(int(level)):
|
||||
if i > 0:
|
||||
buf[pos] = '/'
|
||||
pos += 1
|
||||
idx: int = i * 2
|
||||
idx: int = int(i * 2)
|
||||
buf[pos] = sha1[idx]
|
||||
buf[pos + 1] = sha1[idx + 1]
|
||||
pos += 2
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import lib.core.Gen.GenApi as GenApi
|
||||
import lib.core.VLogger as VLogger
|
||||
import t, c
|
||||
from stdint import *
|
||||
import ast
|
||||
import string
|
||||
@@ -101,7 +100,7 @@ def _lookup_cdefine_for_pyi(name: str) -> int64:
|
||||
# Bound loop by _g_pyi_cdef_count; never len()/.get on i8** (len falls back
|
||||
# to strlen, .get misdispatches to HashTable.get) -- index via t.CPtr(base + i*8).
|
||||
pn: sizet = sizet(_g_pyi_cdef_count)
|
||||
for pi in range(pn):
|
||||
for pi in range(int(pn)):
|
||||
entry_pc: str* = (str*)(uint64(_g_pyi_cdef_names) + pi * 8)
|
||||
pnm: str = entry_pc[0]
|
||||
if pnm is not None and string.strcmp(pnm, name) == 0:
|
||||
@@ -144,15 +143,20 @@ def _pyi_cdefine_found() -> int:
|
||||
|
||||
|
||||
# ============================================================
|
||||
# _PyiEmit - append text to buf at pos, returning the new pos (truncates on overflow)
|
||||
# _PyiEmit - append text to buf at pos, returning the new pos (overflow is fatal)
|
||||
def _PyiEmit(buf: bytes, buf_size: sizet, pos: sizet, text: str) -> sizet:
|
||||
"""Append text to buf at pos and return the new pos"""
|
||||
if buf is None or text is None or pos >= buf_size: return pos
|
||||
"""Append text to buf at pos and return the new pos.
|
||||
|
||||
Overflow used to truncate silently, and the truncated .vpi was then parsed as if complete:
|
||||
the consuming module silently lost declarations or got half a signature.
|
||||
"""
|
||||
if buf is None or text is None: return pos
|
||||
tlen: sizet = string.strlen(text)
|
||||
if tlen == 0: return pos
|
||||
remain: sizet = buf_size - pos
|
||||
if tlen >= remain: tlen = remain - 1
|
||||
if tlen > 0: string.memcpy(buf + pos, text, tlen)
|
||||
if pos + tlen + 1 > buf_size:
|
||||
VLogger.error(f"generated .vpi stub text exceeds the {buf_size}-byte buffer; raise PYI_BUF_SIZE", "StubGen")
|
||||
return pos
|
||||
string.memcpy(buf + pos, text, tlen)
|
||||
return pos + tlen
|
||||
|
||||
|
||||
@@ -160,7 +164,7 @@ def _PyiEmit(buf: bytes, buf_size: sizet, pos: sizet, text: str) -> sizet:
|
||||
# _PyiIndent - append level*4 spaces to buf at pos
|
||||
def _PyiIndent(buf: bytes, buf_size: sizet, pos: sizet, level: int) -> sizet:
|
||||
"""Append level*4 spaces to buf at pos and return the new pos"""
|
||||
for i in range(level):
|
||||
for i in range(int(level)):
|
||||
pos = _PyiEmit(buf, buf_size, pos, " ")
|
||||
return pos
|
||||
|
||||
@@ -182,7 +186,7 @@ def _PyiBinOpOpText(buf: bytes, buf_size: sizet, pos: sizet, op: int, node: ast.
|
||||
|
||||
|
||||
# _PyiTypeStr - recursively render an AST annotation node into buf at pos.
|
||||
# Support set matches TPC; TPV additionally accepts a bare Tuple.
|
||||
# Support set matches TPC; VPC additionally accepts a bare Tuple.
|
||||
def _PyiTypeStr(buf: bytes, buf_size: sizet, pos: sizet, annot: ast.AST*, depth: int = 0) -> sizet:
|
||||
"""Recursively render a type annotation node into buf and return the new pos"""
|
||||
# Recursion guard: a self-referential AST would recurse until stack exhaustion.
|
||||
@@ -247,7 +251,7 @@ def _PyiTypeStr(buf: bytes, buf_size: sizet, pos: sizet, annot: ast.AST*, depth:
|
||||
cmps_ct: list[ast.AST*]* = cmp_t.comparators
|
||||
if ops_ct is not None:
|
||||
on_ct: sizet = len(ops_ct)
|
||||
for oi_ct in range(on_ct):
|
||||
for oi_ct in range(int(on_ct)):
|
||||
oid_ct: int = ops_ct.get(oi_ct)
|
||||
if oid_ct == ast.OpKind.Eq: pos = _PyiEmit(buf, buf_size, pos, " == ")
|
||||
elif oid_ct == ast.OpKind.Ne: pos = _PyiEmit(buf, buf_size, pos, " != ")
|
||||
@@ -268,7 +272,7 @@ def _PyiTypeStr(buf: bytes, buf_size: sizet, pos: sizet, annot: ast.AST*, depth:
|
||||
vals_bt: list[ast.AST*]* = bool_t.values
|
||||
if vals_bt is not None:
|
||||
vn_bt: sizet = len(vals_bt)
|
||||
for vi_bt in range(vn_bt):
|
||||
for vi_bt in range(int(vn_bt)):
|
||||
if vi_bt > 0:
|
||||
if bool_t.op == ast.OpKind.Or: pos = _PyiEmit(buf, buf_size, pos, " or ")
|
||||
else: pos = _PyiEmit(buf, buf_size, pos, " and ")
|
||||
@@ -303,7 +307,7 @@ def _PyiTypeStr(buf: bytes, buf_size: sizet, pos: sizet, annot: ast.AST*, depth:
|
||||
if lst.elts is not None:
|
||||
lst_elts: list[ast.AST*]* = lst.elts
|
||||
ln: sizet = len(lst_elts)
|
||||
for li in range(ln):
|
||||
for li in range(int(ln)):
|
||||
if li > 0: pos = _PyiEmit(buf, buf_size, pos, ", ")
|
||||
le: ast.AST* = lst_elts.get(li)
|
||||
pos = _PyiTypeStr(buf, buf_size, pos, le, depth + 1)
|
||||
@@ -316,7 +320,7 @@ def _PyiTypeStr(buf: bytes, buf_size: sizet, pos: sizet, annot: ast.AST*, depth:
|
||||
if tup.elts is not None:
|
||||
tup_elts: list[ast.AST*]* = tup.elts
|
||||
tn: sizet = len(tup_elts)
|
||||
for ti in range(tn):
|
||||
for ti in range(int(tn)):
|
||||
if ti > 0: pos = _PyiEmit(buf, buf_size, pos, ", ")
|
||||
el: ast.AST* = tup_elts.get(ti)
|
||||
pos = _PyiTypeStr(buf, buf_size, pos, el, depth + 1)
|
||||
@@ -330,7 +334,7 @@ def _PyiTypeStr(buf: bytes, buf_size: sizet, pos: sizet, annot: ast.AST*, depth:
|
||||
if cl.args is not None:
|
||||
cl_args: list[ast.AST*]* = cl.args
|
||||
can: sizet = len(cl_args)
|
||||
for cai in range(can):
|
||||
for cai in range(int(can)):
|
||||
if cai > 0: pos = _PyiEmit(buf, buf_size, pos, ", ")
|
||||
ca: ast.AST* = cl_args.get(cai)
|
||||
pos = _PyiTypeStr(buf, buf_size, pos, ca, depth + 1)
|
||||
@@ -442,8 +446,12 @@ def _PyiFunction(buf: bytes, buf_size: sizet, pos: sizet, fn: ast.FunctionDef*,
|
||||
if fn.decorator_list is not None:
|
||||
fn_decorator_list: list[ast.AST*]* = fn.decorator_list
|
||||
dn: sizet = len(fn_decorator_list)
|
||||
for di in range(dn):
|
||||
dec: ast.AST* = fn_decorator_list.get(di)
|
||||
# 循环变量改名:`di` 在下方默认值处理处被注解声明为 sizet(i64)。若这里先用
|
||||
# `di`(range → int/i32)绑定,注解重声明就会与既有 i32 存储冲突 —— 编译器按
|
||||
# fail-fast 报“带注解重声明冲突”,此前 TPC 是静默丢掉 store(生成的二进制在
|
||||
# list[AST*].get 处越界读)。故装饰器循环变量改用 ddi。
|
||||
for ddi in range(int(dn)):
|
||||
dec: ast.AST* = fn_decorator_list.get(ddi)
|
||||
if dec is not None:
|
||||
pos = _PyiIndent(buf, buf_size, pos, indent_level)
|
||||
pos = _PyiEmit(buf, buf_size, pos, "@")
|
||||
@@ -588,7 +596,7 @@ def _PyiClass(buf: bytes, buf_size: sizet, pos: sizet, cls: ast.ClassDef*, inden
|
||||
if cls.decorator_list is not None:
|
||||
cls_decorator_list: list[ast.AST*]* = cls.decorator_list
|
||||
dcn: sizet = len(cls_decorator_list)
|
||||
for dci in range(dcn):
|
||||
for dci in range(int(dcn)):
|
||||
ddec: ast.AST* = cls_decorator_list.get(dci)
|
||||
if ddec is not None:
|
||||
pos = _PyiIndent(buf, buf_size, pos, indent_level)
|
||||
@@ -608,7 +616,7 @@ def _PyiClass(buf: bytes, buf_size: sizet, pos: sizet, cls: ast.ClassDef*, inden
|
||||
tpn: sizet = len(cls_type_params)
|
||||
if tpn > 0:
|
||||
pos = _PyiEmit(buf, buf_size, pos, "[")
|
||||
for tpi in range(tpn):
|
||||
for tpi in range(int(tpn)):
|
||||
if tpi > 0: pos = _PyiEmit(buf, buf_size, pos, ", ")
|
||||
tpname: str = cls_type_params.get(tpi)
|
||||
if tpname is not None: pos = _PyiEmit(buf, buf_size, pos, tpname)
|
||||
@@ -620,7 +628,7 @@ def _PyiClass(buf: bytes, buf_size: sizet, pos: sizet, cls: ast.ClassDef*, inden
|
||||
bn: sizet = len(cls_bases)
|
||||
if bn > 0:
|
||||
pos = _PyiEmit(buf, buf_size, pos, "(")
|
||||
for bi in range(bn):
|
||||
for bi in range(int(bn)):
|
||||
if bi > 0: pos = _PyiEmit(buf, buf_size, pos, ", ")
|
||||
base: ast.AST* = cls_bases.get(bi)
|
||||
pos = _PyiTypeStr(buf, buf_size, pos, base)
|
||||
@@ -728,7 +736,7 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy*, tree: ast.AST*, rel_path: str, buf
|
||||
pcn: sizet = len(pre_children)
|
||||
# Pass 1: count t.CDefine declarations (C arrays need a fixed size first)
|
||||
_g_pyi_cdef_count = 0
|
||||
for pci0 in range(pcn):
|
||||
for pci0 in range(int(pcn)):
|
||||
stmt_pc0: ast.AST* = pre_children.get(pci0)
|
||||
if stmt_pc0 is not None and stmt_pc0.kind() == ast.ASTKind.AnnAssign:
|
||||
aa_pc0: ast.AnnAssign* = ast.AnnAssign*(stmt_pc0)
|
||||
@@ -743,7 +751,7 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy*, tree: ast.AST*, rel_path: str, buf
|
||||
# Pass 2: register in declaration order (BinOp Name refs depend on earlier entries)
|
||||
pci: sizet = 0
|
||||
cdi: int = 0
|
||||
for pci in range(pcn):
|
||||
for pci in range(int(pcn)):
|
||||
stmt_pc: ast.AST* = pre_children.get(pci)
|
||||
if stmt_pc is not None and stmt_pc.kind() == ast.ASTKind.AnnAssign:
|
||||
aa_pc: ast.AnnAssign* = ast.AnnAssign*(stmt_pc)
|
||||
@@ -771,14 +779,14 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy*, tree: ast.AST*, rel_path: str, buf
|
||||
# Pass 1: detect import t/c
|
||||
has_import_t: int = 0
|
||||
has_import_c: int = 0
|
||||
for i in range(n):
|
||||
for i in range(int(n)):
|
||||
stmt: ast.AST* = tree_children.get(i)
|
||||
if stmt is not None and stmt.kind() == ast.ASTKind.Import:
|
||||
imp: ast.Import* = ast.Import*(stmt)
|
||||
if imp.names is not None:
|
||||
imp_names: list[ast.AST*]* = imp.names
|
||||
an: sizet = len(imp_names)
|
||||
for ai in range(an):
|
||||
for ai in range(int(an)):
|
||||
al: ast.Alias* = ast.Alias*(imp_names.get(ai))
|
||||
if al is not None and al.name is not None:
|
||||
if string.strcmp(al.name, "t") == 0: has_import_t = 1
|
||||
@@ -791,7 +799,7 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy*, tree: ast.AST*, rel_path: str, buf
|
||||
else: pos = _PyiEmit(buf, buf_size, pos, "\n")
|
||||
|
||||
# Pass 2: emit declarations
|
||||
for i in range(n):
|
||||
for i in range(int(n)):
|
||||
stmt = tree_children.get(i)
|
||||
if stmt is not None:
|
||||
k: int = stmt.kind()
|
||||
@@ -807,7 +815,7 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy*, tree: ast.AST*, rel_path: str, buf
|
||||
if imp2.names is not None:
|
||||
imp2_names: list[ast.AST*]* = imp2.names
|
||||
an2: sizet = len(imp2_names)
|
||||
for ai2 in range(an2):
|
||||
for ai2 in range(int(an2)):
|
||||
if ai2 > 0: pos = _PyiEmit(buf, buf_size, pos, ", ")
|
||||
al2: ast.Alias* = ast.Alias*(imp2_names.get(ai2))
|
||||
# fail-fast: never silently write "import , ". Import names are the
|
||||
@@ -839,7 +847,7 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy*, tree: ast.AST*, rel_path: str, buf
|
||||
if ifr.names is not None:
|
||||
ifr_names: list[ast.AST*]* = ifr.names
|
||||
an3: sizet = len(ifr_names)
|
||||
for ai3 in range(an3):
|
||||
for ai3 in range(int(an3)):
|
||||
if ai3 > 0: pos = _PyiEmit(buf, buf_size, pos, ", ")
|
||||
al3: ast.Alias* = ast.Alias*(ifr_names.get(ai3))
|
||||
# fail-fast: same as Import (report the two causes separately).
|
||||
|
||||
@@ -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,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
|
||||
@@ -66,13 +65,13 @@ def _sliced_path(temp_dir: str, td_len: sizet, sha1: str, ext: str) -> str:
|
||||
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.
|
||||
# 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(StubMerger._sha1_store_count):
|
||||
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)
|
||||
@@ -82,7 +81,7 @@ def _load_includes_sha1_set(pool: memhub.MemBuddy*, temp_dir: str, sha1_set: str
|
||||
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):
|
||||
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).
|
||||
@@ -109,7 +108,7 @@ def WriteIncludesSha1Map(mb: memhub.MemBuddy*, temp_dir: str, scan_result: Inclu
|
||||
return 1
|
||||
|
||||
written_count: int = 0
|
||||
for i in range(scan_result.Count):
|
||||
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
|
||||
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -181,7 +180,7 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
|
||||
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)
|
||||
@@ -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)
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import t, c
|
||||
from stdint import *
|
||||
import ast
|
||||
import mlirlite
|
||||
@@ -39,7 +38,7 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
|
||||
# fail-fast: struct type unregistered; a silent return would leave fields uninitialized
|
||||
VLogger.error(f"Struct type not registered, cannot apply defaults: {class_name}", "HandlesExprCall")
|
||||
return
|
||||
for fi in range(entry.FieldCount):
|
||||
for fi in range(int(entry.FieldCount)):
|
||||
fe: GenTypes.FieldEntry* = self.Gen.StructGen._get_field_entry(entry, fi)
|
||||
if fe is None:
|
||||
# fail-fast: field index out of range (entry.FieldCount mismatch)
|
||||
@@ -137,7 +136,7 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
|
||||
new_arg_vals: sizet* = pool.alloc(8 * 32)
|
||||
if new_arg_vals is not None: string.memset(new_arg_vals, 0, 8 * 32)
|
||||
new_arg_count: int = 0
|
||||
for nai in range(can):
|
||||
for nai in range(int(can)):
|
||||
new_arg_node: ast.AST* = cargs.get(nai)
|
||||
if new_arg_node is None: continue
|
||||
new_av: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, new_arg_node, None, 0, trans)
|
||||
@@ -149,7 +148,7 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
|
||||
new_ptr: mlirlite.Value* = self.Gen.CallGenCall._call_method_on_ptr(pool, builder, mod, class_name, "__new__", tmp, new_arg_vals, new_arg_count, trans, diag_node_ct, is_volatile, cargs)
|
||||
if new_ptr is not None:
|
||||
storage_ptr = new_ptr
|
||||
# __new__ declares `-> t.CPtr` (i8*) but the class type is Ptr(struct_ty).
|
||||
# __new__ declares `-> rawptr` (i8*) but the class type is Ptr(struct_ty).
|
||||
# Using it directly degrades method dispatch; bitcast is a zero-cost type-layer fix.
|
||||
if new_ptr.Ty is not None:
|
||||
nw_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, struct_ty)
|
||||
@@ -157,8 +156,37 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
|
||||
nw_bc: mlirlite.Value* = mlirlite.build_bitcast(builder, new_ptr, nw_ty)
|
||||
if nw_bc is not None: storage_ptr = nw_bc
|
||||
|
||||
# 与泛型路径(L1166-1181 的 HasInit/存在性判据)对齐:__before_init__ 只由 ClassGenVTable 对
|
||||
# 「有方法」或「带 virtual 标记」的类合成,纯数据类没有合成本函数。因此不能在 is_oop != 0 时
|
||||
# 无条件调用它,否则对未合成的类发出悬空调用(链接期 undefined symbol)。
|
||||
# 判据(任一成立即认为该类确有 __before_init__,均为充分条件,且覆盖跨模块):
|
||||
# (1) ctor_entry.IsOOP != 0:_translate_oop_methods 正是对这类类合成 __before_init__(含跨模块推断置位);
|
||||
# (2) ctor_entry.HasInit / 模块内本类 __init__ 声明 / 全局默认参数表:有 __init__ ⇒ 有方法 ⇒ OOP;
|
||||
# (3) MRO 祖先 HasInit != 0:本类继承祖先 __init__(未合成默认 __init__),但仍合成自己的 __before_init__。
|
||||
bi_ok: int = 0
|
||||
if ctor_entry is not None and ctor_entry.IsOOP != 0: bi_ok = 1
|
||||
if bi_ok == 0 and ctor_entry is not None and ctor_entry.HasInit != 0: bi_ok = 1
|
||||
if bi_ok == 0 and class_name is not None:
|
||||
bi_lk: str = pool.alloc(160)
|
||||
if bi_lk is not None:
|
||||
viperlib.snprintf(bi_lk, 160, "%s.__init__", class_name)
|
||||
if self.find_func_in_module(mod, bi_lk) is not None: bi_ok = 1
|
||||
elif self.Gen.CallGenLeafKit.find_func_defaults(bi_lk) is not None: bi_ok = 1
|
||||
if bi_ok == 0 and class_name is not None:
|
||||
# 本类未合成默认 __init__ 的继承场景(祖先有 __init__):仍有自己的 __before_init__。
|
||||
bi_mro: sizet* = self.Gen.StructGen.get_mro_chain(pool, class_name)
|
||||
bi_mi: int = 1
|
||||
while bi_mro is not None and bi_mro[bi_mi] != 0:
|
||||
bi_anc: str = rawptr(bi_mro[bi_mi])
|
||||
if bi_anc is not None and string.strcmp(bi_anc, class_name) != 0:
|
||||
bi_ae: GenTypes.StructEntry* = self.Gen.StructGen.find_struct(bi_anc)
|
||||
if bi_ae is not None and bi_ae.HasInit != 0:
|
||||
bi_ok = 1
|
||||
break
|
||||
bi_mi += 1
|
||||
# OOP struct: call __before_init__ to zero-fill and assign defaults
|
||||
self.Gen.CallGenCall._call_method_on_ptr(pool, builder, mod, class_name,"__before_init__", storage_ptr, None, 0, trans, diag_node_ct, is_volatile)
|
||||
if bi_ok != 0:
|
||||
self.Gen.CallGenCall._call_method_on_ptr(pool, builder, mod, class_name,"__before_init__", storage_ptr, None, 0, trans, diag_node_ct, is_volatile)
|
||||
|
||||
# If __init__ exists, call __init__(ptr, args...); rely only on ctor_entry.HasInit
|
||||
# (classes like JsonValue have __new__ but no __init__ -> forced inference causes undefined refs).
|
||||
@@ -169,7 +197,7 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
|
||||
arg_vals: sizet* = pool.alloc(8 * 32)
|
||||
if arg_vals is not None: string.memset(arg_vals, 0, 8 * 32)
|
||||
real_arg_count: int = 0
|
||||
for ai in range(can):
|
||||
for ai in range(int(can)):
|
||||
arg_node: ast.AST* = cargs.get(ai)
|
||||
if arg_node is None: continue
|
||||
av: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, arg_node, None, 0, trans)
|
||||
@@ -182,7 +210,7 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
|
||||
elif has_new_flag == 0:
|
||||
# No __init__ and no __new__: store args field by field
|
||||
# (with __new__, args were already consumed and must not be stored).
|
||||
for ai in range(can):
|
||||
for ai in range(int(can)):
|
||||
arg: ast.AST* = cargs.get(ai)
|
||||
if arg is None: continue
|
||||
# Fetch the field first (fe.Ty is used for char-literal -> str materialisation
|
||||
@@ -192,7 +220,7 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
|
||||
# Field 0 is __vtable__ (vtable pointer placeholder): ctor args must skip it,
|
||||
# else an arg overwrites the vtable slot and isinstance/dispatch break.
|
||||
target_idx: int = 0
|
||||
for fi in range(ctor_entry.FieldCount):
|
||||
for fi in range(int(ctor_entry.FieldCount)):
|
||||
cand_fe: GenTypes.FieldEntry* = self.Gen.StructGen._get_field_entry(ctor_entry, fi)
|
||||
if cand_fe is None: continue
|
||||
cand_name: str = self.Gen.StructGen.get_field_name_ptr(cand_fe)
|
||||
@@ -230,7 +258,9 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
|
||||
self._apply_struct_defaults(pool, builder, mod, struct_ty, class_name, tmp, trans, is_volatile)
|
||||
|
||||
# store args field by field (overriding defaults)
|
||||
for ai in range(can):
|
||||
# `can` 是 sizet(u64):循环变量若取 u64,用作 int(i32) 的字段下标/索引即隐式窄化,
|
||||
# 故显式收窄循环上界(构造函数实参个数远小于 2^31)。
|
||||
for ai in range(int(can)):
|
||||
arg: ast.AST* = cargs.get(ai)
|
||||
if arg is None: continue
|
||||
# fetch the field first (fe.Ty is used for char-literal -> str materialisation, see translate_constant)
|
||||
@@ -279,7 +309,11 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
|
||||
# alloca the REnum type, ensuring the full cross-module struct definition is
|
||||
# available in this module.
|
||||
self.Gen.StructGen.ensure_struct_def_in_module(pool, mod, renum_ty)
|
||||
tmp: mlirlite.Value* = self.Gen.VarGen._alloca_at_entry(builder, renum_ty)
|
||||
# M2.4 `new`:`new [desc] Qual.Variant(args)` 已由 _translate_new_expr 取好存储
|
||||
# (stack=alloca / heap=malloc / memgr=x.alloc)并落在 _g_new_storage;有则复用它,
|
||||
# 与 _translate_struct_ctor_kw 同款。裸 `Qual.Variant(args)`(无 new)时为 None → 帧内 alloca。
|
||||
tmp: mlirlite.Value* = _g_new_storage
|
||||
if tmp is None: tmp = self.Gen.VarGen._alloca_at_entry(builder, renum_ty)
|
||||
if tmp is None: return mlirlite.ConstZero(pool, renum_ty)
|
||||
|
||||
# zero-fill (unset payload slots become zero)
|
||||
@@ -299,7 +333,8 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
|
||||
|
||||
# Write payload fields (index 1, 2, ... = _p1, _p2, ...); args fill payload in
|
||||
# position order, skipping __tag (index 0).
|
||||
for ai in range(can):
|
||||
# `can` 是 sizet(u64):`ai + 1` 若为 u64 赋给 payload_idx:int 即隐式窄化,故收窄上界。
|
||||
for ai in range(int(can)):
|
||||
arg: ast.AST* = cargs.get(ai)
|
||||
if arg is None: continue
|
||||
# payload field index = ai + 1 (skip __tag); fetch the field first (pfe.Ty is used
|
||||
@@ -389,6 +424,27 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
|
||||
self.Gen.TypeGenErr.fatal_error(new_node, "`new stack hlife` is contradictory: stack storage cannot outlive its frame; use `new stack slife`, or `heap`/`memgr(x)`", "LifetimeError")
|
||||
return None
|
||||
class_name: str = self.Gen.ExprGenBase.get_func_name(new_node.func)
|
||||
# REnum 变体构造:`new [desc] Qual.Variant(args)`(如 new memgr(pool) LLVMType.Int(32))。
|
||||
# callee 是 Attribute 且限定名 Qual 为已注册 REnum 类、Variant 命中该类的变体表时,把
|
||||
# class_name 归一到 **Qual(REnum 类名)**:下方 get_struct_type 命中的正是 REnum 的
|
||||
# 聚合布局 `{ i32 __tag, <payload> }`,于是取存储与构造序列与其他类走同一路径;
|
||||
# 真正的变体写入由合成 Call 触发 CallGenMixin 的 REnum 分支(_translate_renum_construct)。
|
||||
# 不归一(Qual 非 REnum / 变体缺失)时保持原样,由下方基本类型路径定夺 / fail-fast。
|
||||
if class_name is not None and new_node.func is not None and new_node.func.kind() == ast.ASTKind.Attribute:
|
||||
_rn_at: ast.Attribute* = ast.Attribute*(new_node.func)
|
||||
_rn_qual: str = None
|
||||
if _rn_at.value is not None:
|
||||
if _rn_at.value.kind() == ast.ASTKind.Name:
|
||||
_rn_qual = ast.Name*(_rn_at.value).id
|
||||
elif _rn_at.value.kind() == ast.ASTKind.Attribute:
|
||||
# 模块限定:`mod.Class.Variant` → Class 取最后一个 attr(与 CallGenMixin 同口径)
|
||||
_rn_qual = ast.Attribute*(_rn_at.value).attr
|
||||
if _rn_qual is not None:
|
||||
_rn_is: int = self.Gen.ClassGenEnum._is_renum_class_name(_rn_qual)
|
||||
if _rn_is == 0: _rn_is = self.Gen.ClassGenEnum._is_renum_by_entry(_rn_qual)
|
||||
if _rn_is != 0:
|
||||
_rn_tag: int = self.Gen.ClassGenEnum._lookup_renum_variant_in_class(_rn_qual, class_name)
|
||||
if _rn_tag >= 0: class_name = _rn_qual
|
||||
# §7.2/M4:泛型容器 callee(`new memgr(pool) hlife list[int](...)`)——先特化模板拿具体类名,
|
||||
# 再按普通类走同一条取存储路径(存储复用见 _translate_generic_ctor_call)。
|
||||
if class_name is None and new_node.func is not None and new_node.func.kind() == ast.ASTKind.Subscript:
|
||||
@@ -634,7 +690,7 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
|
||||
new_arg_vals: sizet* = pool.alloc(8 * 32)
|
||||
if new_arg_vals is not None: string.memset(new_arg_vals, 0, 8 * 32)
|
||||
new_arg_count: int = 0
|
||||
for nai in range(can):
|
||||
for nai in range(int(can)):
|
||||
new_arg_node: ast.AST* = cargs.get(nai)
|
||||
if new_arg_node is None: continue
|
||||
new_av: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, new_arg_node, None, 0, trans)
|
||||
@@ -643,7 +699,7 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
|
||||
new_arg_vals[new_arg_count] = sizet(new_av)
|
||||
new_arg_count += 1
|
||||
# keyword args are also passed to __new__
|
||||
for nki in range(kw_count):
|
||||
for nki in range(int(kw_count)):
|
||||
nkw: ast.Keyword* = ast.Keyword*(keywords.get(nki))
|
||||
if nkw is None or nkw.value is None: continue
|
||||
nkw_av: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, nkw.value, None, 0, trans)
|
||||
@@ -654,7 +710,7 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
|
||||
new_ptr: mlirlite.Value* = self.Gen.CallGenCall._call_method_on_ptr(pool, builder, mod, class_name, "__new__", tmp, new_arg_vals, new_arg_count, trans, diag_node_ct, is_volatile, cargs)
|
||||
if new_ptr is not None:
|
||||
storage_ptr = new_ptr
|
||||
# __new__ declares `-> t.CPtr` (i8*) but the class type is Ptr(struct_ty).
|
||||
# __new__ declares `-> rawptr` (i8*) but the class type is Ptr(struct_ty).
|
||||
# Using it directly degrades method dispatch; bitcast is a zero-cost type-layer fix.
|
||||
if new_ptr.Ty is not None:
|
||||
nw_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, struct_ty)
|
||||
@@ -667,7 +723,7 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
|
||||
if init_arg_vals is not None: string.memset(init_arg_vals, 0, 8 * 32)
|
||||
init_arg_count: int = 0
|
||||
# positional args
|
||||
for ai_init in range(can):
|
||||
for ai_init in range(int(can)):
|
||||
arg_node_init: ast.AST* = cargs.get(ai_init)
|
||||
if arg_node_init is None: continue
|
||||
av_init: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, arg_node_init, None, 0, trans)
|
||||
@@ -676,7 +732,7 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
|
||||
init_arg_vals[init_arg_count] = sizet(av_init)
|
||||
init_arg_count += 1
|
||||
# keyword args (appended at the end, in order of appearance)
|
||||
for ki_init in range(kw_count):
|
||||
for ki_init in range(int(kw_count)):
|
||||
kw_init: ast.Keyword* = ast.Keyword*(keywords.get(ki_init))
|
||||
if kw_init is None or kw_init.value is None: continue
|
||||
kw_av_init: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, kw_init.value, None, 0, trans)
|
||||
@@ -693,7 +749,7 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
|
||||
|
||||
# No __init__ (or not OOP): store args field by field.
|
||||
# Handle positional args first (by field index, overriding defaults).
|
||||
for ai in range(can):
|
||||
for ai in range(int(can)):
|
||||
arg: ast.AST* = cargs.get(ai)
|
||||
if arg is None: continue
|
||||
# fetch the field first (fe.Ty is used for char-literal -> str materialisation, see translate_constant)
|
||||
@@ -715,7 +771,7 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
|
||||
# Get the SHA1 from kw_entry (avoid cross-module same-name find_struct mistakes).
|
||||
kw_sha1: str = None
|
||||
if kw_entry is not None: kw_sha1 = kw_entry.ModuleSha1
|
||||
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
|
||||
fe: GenTypes.FieldEntry* = self.Gen.StructGen.lookup_field_by_class(class_name, kw.arg, kw_sha1, kw, trans._from_imports)
|
||||
@@ -756,8 +812,10 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
|
||||
if self.Gen.ExprGenBase._zext_or_sext_val(pool, val, src_node, trans) != 0: return mlirlite.build_zext(builder, val, target_ty)
|
||||
return mlirlite.build_sext(builder, val, target_ty)
|
||||
else:
|
||||
# Target i1 (bool / t.CBool): both explicit casts and implicit stores normalise
|
||||
# truth as `x != 0 -> 1/0` (not trunc, which would make x=2 false while `if 2:` is true).
|
||||
# Target i1 (bool / t.CBool) via an EXPLICIT cast `t.CBool(x)` / `(bool)(x)`:
|
||||
# normalise truth as `x != 0 -> 1/0` (not trunc, which would make x=2 false while
|
||||
# `if 2:` is true). Implicit wide-int -> bool stores no longer normalise here; they
|
||||
# fail fast in coerce_to_type (rule 2026-10-13), so only explicit casts reach this.
|
||||
if target_bits == 1:
|
||||
zero_tc: mlirlite.Value* = mlirlite.const_int(pool, val_bits, 0)
|
||||
return mlirlite.build_icmp(builder, mlirlite.ICMP_NE, val, zero_tc)
|
||||
@@ -805,7 +863,7 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
|
||||
if elts is not None:
|
||||
n: sizet = len(elts)
|
||||
i: sizet
|
||||
for i in range(n):
|
||||
for i in range(int(n)):
|
||||
elt: ast.AST* = elts.get(i)
|
||||
tn_raw = self.Gen.TypeGenResolve._get_type_name_from_annotation(pool, elt)
|
||||
tn = self.Gen.TypeGenResolve._norm_type_arg_name(pool, tn_raw, from_imports)
|
||||
@@ -923,7 +981,7 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
|
||||
children: list[ast.AST*]* = template_cd.children
|
||||
cn: sizet = len(children)
|
||||
ci: sizet
|
||||
for ci in range(cn):
|
||||
for ci in range(int(cn)):
|
||||
child: ast.AST* = children.get(ci)
|
||||
if child is None or child.kind() != ast.ASTKind.FunctionDef: continue
|
||||
fd: ast.FunctionDef* = ast.FunctionDef*(child)
|
||||
@@ -941,7 +999,7 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
|
||||
if plist is None: return -1
|
||||
pn: sizet = len(plist)
|
||||
pi: sizet
|
||||
for pi in range(pn):
|
||||
for pi in range(int(pn)):
|
||||
if pi == 0:
|
||||
continue # self: positional-arg index = param index - 1
|
||||
pa: ast.AST* = plist.get(pi)
|
||||
@@ -963,7 +1021,7 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
|
||||
if plist_sb is None: return -1
|
||||
pn_sb: sizet = len(plist_sb)
|
||||
pi_sb: sizet
|
||||
for pi_sb in range(pn_sb):
|
||||
for pi_sb in range(int(pn_sb)):
|
||||
if pi_sb == 0:
|
||||
continue # self is not an argument slot
|
||||
pa_sb: ast.AST* = plist_sb.get(pi_sb)
|
||||
@@ -987,7 +1045,7 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
|
||||
ag_kw: ast.Arg* = ast.Arg*(pa_kw)
|
||||
if ag_kw.arg is None: return -1
|
||||
ki_kw: sizet
|
||||
for ki_kw in range(kw_count):
|
||||
for ki_kw in range(int(kw_count)):
|
||||
kw_tp: ast.Keyword* = ast.Keyword*(keywords.get(ki_kw))
|
||||
if kw_tp is None or kw_tp.arg is None or kw_tp.value is None: continue
|
||||
if string.strcmp(kw_tp.arg, ag_kw.arg) == 0: return int(ki_kw)
|
||||
@@ -1062,7 +1120,7 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
|
||||
if arg_filled is None: return None
|
||||
string.memset(arg_filled, 0, 8 * 32)
|
||||
|
||||
for ai in range(can):
|
||||
for ai in range(int(can)):
|
||||
if ai >= 32:
|
||||
# fixed 8*32 slots: writing past 32 args would corrupt adjacent pool blocks; fail-fast
|
||||
self.Gen.TypeGenErr.fatal_error(cl, "Generic construction arguments exceed the limit of 32", "TypeError")
|
||||
@@ -1077,7 +1135,7 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
|
||||
arg_max_filled: int = int(can) - 1
|
||||
if kw_count > 0:
|
||||
ki_slot: sizet
|
||||
for ki_slot in range(kw_count):
|
||||
for ki_slot in range(int(kw_count)):
|
||||
kw_slot: ast.Keyword* = ast.Keyword*(keywords.get(ki_slot))
|
||||
if kw_slot is None or kw_slot.arg is None or kw_slot.value is None: continue
|
||||
slot_kw: int = self._ctor_init_arg_slot_by_name(tmpl_name, kw_slot.arg)
|
||||
@@ -1100,7 +1158,7 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
|
||||
if slot_kw > arg_max_filled: arg_max_filled = slot_kw
|
||||
# Slots must be contiguous (0..arg_max_filled all filled): a hole means a param
|
||||
# got neither positional nor keyword arg. Never pass the hole as 0; fail-fast.
|
||||
for ci_hole in range(arg_max_filled + 1):
|
||||
for ci_hole in range(int(arg_max_filled + 1)):
|
||||
if arg_filled[sizet(ci_hole)] == 0:
|
||||
self.Gen.TypeGenErr.fatal_error(cl, f"Generic class '{tmpl_name}': keyword construction is missing preceding parameters (argument slot {ci_hole + 1} has neither positional nor keyword argument)", "TypeError")
|
||||
break
|
||||
@@ -1125,7 +1183,7 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
|
||||
new_ptr: mlirlite.Value* = self.Gen.CallGenCall._call_method_on_ptr(pool, builder, mod, spec_name, "__new__", tmp, extra_args, arg_count, trans, None, gc_volatile, gc_arg_nodes)
|
||||
if new_ptr is not None:
|
||||
storage_ptr = new_ptr
|
||||
# __new__ declares `-> t.CPtr` (i8*) but the class type is Ptr(struct_ty).
|
||||
# __new__ declares `-> rawptr` (i8*) but the class type is Ptr(struct_ty).
|
||||
# Using it directly degrades method dispatch; bitcast is a zero-cost type-layer fix.
|
||||
if new_ptr.Ty is not None:
|
||||
nw_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, struct_ty)
|
||||
@@ -1147,7 +1205,7 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
|
||||
if self.find_func_in_module(mod, init_lk) is not None: gi_has_init = 1
|
||||
elif self.Gen.CallGenLeafKit.find_func_defaults(init_lk) is not None: gi_has_init = 1
|
||||
if gi_has_init != 0:
|
||||
# call __before_init__(storage_ptr) (auto-generated by TransPyV, zero-fills)
|
||||
# call __before_init__(storage_ptr) (auto-generated by ViperC, zero-fills)
|
||||
self.Gen.CallGenCall._call_method_on_ptr(pool, builder, mod, spec_name, "__before_init__", storage_ptr, None, 0, trans, None, gc_volatile)
|
||||
|
||||
# call __init__(storage_ptr, *args) (arg_count = slots 0..highest filled)
|
||||
@@ -1155,13 +1213,13 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
|
||||
elif has_new_flag == 0:
|
||||
# 无 __init__ 也无 __new__:按字段顺序逐字段 store(与非泛型路径同语义;字段 0 若是
|
||||
# __vtable__ 占位则跳过)。这条分支保证 `Box[int](7)` 仍然把 7 写进字段 v。
|
||||
for ai_sf in range(can):
|
||||
for ai_sf in range(int(can)):
|
||||
arg_sf: ast.AST* = cargs.get(ai_sf)
|
||||
if arg_sf is None: continue
|
||||
fe_sf: GenTypes.FieldEntry* = None
|
||||
if struct_entry is not None:
|
||||
tidx_sf: int = 0
|
||||
for fi_sf in range(struct_entry.FieldCount):
|
||||
for fi_sf in range(int(struct_entry.FieldCount)):
|
||||
cand_sf: GenTypes.FieldEntry* = self.Gen.StructGen._get_field_entry(struct_entry, fi_sf)
|
||||
if cand_sf is None: continue
|
||||
cn_sf: str = self.Gen.StructGen.get_field_name_ptr(cand_sf)
|
||||
|
||||
@@ -2,7 +2,6 @@ import lib.core.Gen.GenTypes as GenTypes
|
||||
import lib.core.Gen.Generator as Generator
|
||||
import lib.core.Gen.Expr.ExprGenCls as ExprGenCls
|
||||
import lib.core.Gen.Expr.ExprGenName as ExprGenName
|
||||
import t, c
|
||||
from stdint import *
|
||||
import ast
|
||||
import mlirlite
|
||||
@@ -11,7 +10,7 @@ import string
|
||||
import stdlib
|
||||
import sys
|
||||
import viperlib
|
||||
import w32.fileio as fileio
|
||||
import win32.fileio as fileio
|
||||
import lib.core.VLogger as VLogger
|
||||
import lib.core.Gen.Translator as HT
|
||||
import lib.core.Gen.Type.TypeSpec as TSpec
|
||||
@@ -28,7 +27,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
|
||||
|
||||
# _tc_h0_generic_func - generic function call: infer/specialize type args, then emit the call.
|
||||
# Must run before normal function/constructor dispatch; hit_box marks handled void calls.
|
||||
def _tc_h0_generic_func(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cl: ast.Call*, funcs_ptr: t.CPtr, func_count: int, trans: HT.Translator*, func_name: str, cargs: list[ast.AST*]*, can: sizet, hit_box: t.CPtr = None) -> mlirlite.Value*:
|
||||
def _tc_h0_generic_func(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cl: ast.Call*, funcs_ptr: rawptr, func_count: int, trans: HT.Translator*, func_name: str, cargs: list[ast.AST*]*, can: sizet, hit_box: rawptr = None) -> mlirlite.Value*:
|
||||
if cl is None or trans is None: return None
|
||||
if self.Gen.ClassGenGeneric.GenericFuncKeys is None: return None
|
||||
if cl.func is None: return None
|
||||
@@ -94,7 +93,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
|
||||
type_args = list[str](pool, 8)
|
||||
if type_args is None: return None
|
||||
tpi: sizet
|
||||
for tpi in range(tp_count):
|
||||
for tpi in range(int(tp_count)):
|
||||
tp_name: str = tp_names.get(tpi)
|
||||
ta: str = None
|
||||
slot: int = self.Gen.ClassGenGeneric._find_generic_func_param_slot(template_fd, tp_name)
|
||||
@@ -142,13 +141,13 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
|
||||
if ag.arg is None: return -1
|
||||
kn: sizet = len(keywords)
|
||||
ki: sizet
|
||||
for ki in range(kn):
|
||||
for ki in range(int(kn)):
|
||||
kw: ast.Keyword* = ast.Keyword*(keywords.get(ki))
|
||||
if kw is None or kw.arg is None: continue
|
||||
if string.strcmp(kw.arg, ag.arg) == 0: return int(ki)
|
||||
return -1
|
||||
|
||||
def _emit_generic_func_call(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, trans: HT.Translator*, cl: ast.Call*, spec_name: str, cargs: list[ast.AST*]*, can: sizet, hit_box: t.CPtr = None) -> mlirlite.Value*:
|
||||
def _emit_generic_func_call(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, trans: HT.Translator*, cl: ast.Call*, spec_name: str, cargs: list[ast.AST*]*, can: sizet, hit_box: rawptr = None) -> mlirlite.Value*:
|
||||
"""Emit the specialized-function call, coercing args to the specialized param types."""
|
||||
func: mlirlite.Function* = self.Gen.CallGenBase.find_func_in_module(mod, spec_name)
|
||||
if func is None:
|
||||
@@ -171,7 +170,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
|
||||
head: mlirlite.Value* = None
|
||||
tail: mlirlite.Value* = None
|
||||
fp: mlirlite.Param* = mlirlite.function_get_param_head(func)
|
||||
for ai in range(can):
|
||||
for ai in range(int(can)):
|
||||
arg: ast.AST* = cargs.get(ai)
|
||||
fpty: mlirlite.LLVMType* = None
|
||||
if fp is not None: fpty = mlirlite.param_get_ty(fp)
|
||||
@@ -189,7 +188,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
|
||||
# reads its first field as a string and emits illegal IR. Use the looked-up symbol name.
|
||||
return mlirlite.build_call(builder, spec_name, head, int(can), ret_ty, 0)
|
||||
|
||||
def _tc_h1_super_ctraits(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cl: ast.Call*, funcs_ptr: t.CPtr, func_count: int, trans: HT.Translator*, func_name: str, cargs: list[ast.AST*]*, can: sizet, hit_box: t.CPtr = None) -> mlirlite.Value*:
|
||||
def _tc_h1_super_ctraits(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cl: ast.Call*, funcs_ptr: rawptr, func_count: int, trans: HT.Translator*, func_name: str, cargs: list[ast.AST*]*, can: sizet, hit_box: rawptr = None) -> mlirlite.Value*:
|
||||
"""Compile-time super()/ctraits detection; sets hit_box on interception, else returns None."""
|
||||
# super().method(args) -> static call to the parent method. Must intercept before attribute
|
||||
# dispatch, else Call(super) is treated as a plain function call and lookup hits self/vtable.
|
||||
@@ -221,7 +220,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
|
||||
return mlirlite.const_int32(pool, 0)
|
||||
return None
|
||||
|
||||
def _tc_h2_c_special(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cl: ast.Call*, funcs_ptr: t.CPtr, func_count: int, trans: HT.Translator*, func_name: str, cargs: list[ast.AST*]*, can: sizet, hit_box: t.CPtr = None) -> mlirlite.Value*:
|
||||
def _tc_h2_c_special(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cl: ast.Call*, funcs_ptr: rawptr, func_count: int, trans: HT.Translator*, func_name: str, cargs: list[ast.AST*]*, can: sizet, hit_box: rawptr = None) -> mlirlite.Value*:
|
||||
"""Special c.Deref/Asm/LLVMIR calls; marks hit_box for void calls, else returns None."""
|
||||
# __asm__(...) / __llvmir__(...) sugar: bare calls equal c.Asm(...) / c.LLVMIR(...). Must
|
||||
# intercept before the c.* attribute branch, since the bare cl.func is a Name.
|
||||
@@ -250,7 +249,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
|
||||
return mlirlite.ConstInt(pool, mlirlite.Int32(pool), 0, "0")
|
||||
|
||||
# Detect c.Deref / c.DerefAs / c.Addr special calls (inline IR, no call emitted)
|
||||
# and coro.* coroutine interception (inline translation, TPV-M1).
|
||||
# and coro.* coroutine interception (inline translation, VPC-M1).
|
||||
if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute:
|
||||
at_node: ast.Attribute* = ast.Attribute*(cl.func)
|
||||
if at_node.value is not None and at_node.value.kind() == ast.ASTKind.Name:
|
||||
@@ -301,7 +300,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
|
||||
return self.Gen.AsyncGen.translate_coro_gather(builder, pool, mod, cl, trans)
|
||||
return None
|
||||
|
||||
def _tc_h3_stdarg_cast(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cl: ast.Call*, funcs_ptr: t.CPtr, func_count: int, trans: HT.Translator*, func_name: str, cargs: list[ast.AST*]*, can: sizet) -> mlirlite.Value*:
|
||||
def _tc_h3_stdarg_cast(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cl: ast.Call*, funcs_ptr: rawptr, func_count: int, trans: HT.Translator*, func_name: str, cargs: list[ast.AST*]*, can: sizet) -> mlirlite.Value*:
|
||||
"""stdarg sugar and str/int/bool built-in casts; returns None when unmatched."""
|
||||
# stdarg sugar (va_arg / arg / va_start / va_end): these are declared as pass bodies in
|
||||
# stdarg.vp and handled here at translation time by emitting LLVM va_arg / intrinsic calls.
|
||||
@@ -362,16 +361,16 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
|
||||
return None
|
||||
return None
|
||||
|
||||
def _tc_h4_type_casts(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cl: ast.Call*, funcs_ptr: t.CPtr, func_count: int, trans: HT.Translator*, func_name: str, cargs: list[ast.AST*]*, can: sizet) -> mlirlite.Value*:
|
||||
def _tc_h4_type_casts(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cl: ast.Call*, funcs_ptr: rawptr, func_count: int, trans: HT.Translator*, func_name: str, cargs: list[ast.AST*]*, can: sizet) -> mlirlite.Value*:
|
||||
"""t.XXX / typedef type casts; returns None when unmatched."""
|
||||
# Detect t.XXX type casts (uint64(ptr), t.CPtr(val), int(val), ...): when func is t.XXX and
|
||||
# Detect t.XXX type casts (uint64(ptr), rawptr(val), int(val), ...): when func is t.XXX and
|
||||
# XXX is a known type, treat it as a cast; once confirmed, never fall through to cross-module calls.
|
||||
if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute:
|
||||
tc_at: ast.Attribute* = ast.Attribute*(cl.func)
|
||||
if tc_at.value is not None and tc_at.value.kind() == ast.ASTKind.Name:
|
||||
tc_mod_nm: ast.Name* = ast.Name*(tc_at.value)
|
||||
if tc_mod_nm.id is not None and string.strcmp(tc_mod_nm.id, "t") == 0:
|
||||
# t.CType(value, TypeName, t.CPtr) casts value to TypeName*; TypeName is a type
|
||||
# t.CType(value, TypeName, rawptr) casts value to TypeName*; TypeName is a type
|
||||
# name (not a value), so it must be handled explicitly or it falls through to a FATAL.
|
||||
if string.strcmp(func_name, "CType") == 0 and can >= 2:
|
||||
cty_node: ast.AST* = cargs.get(1)
|
||||
@@ -388,7 +387,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
|
||||
cty_arg3: ast.AST* = cargs.get(2)
|
||||
cty_is_cptr: int = 0
|
||||
if cty_arg3 is not None:
|
||||
# Form 1: t.CPtr (Attribute)
|
||||
# Form 1: rawptr (Attribute)
|
||||
if cty_arg3.kind() == ast.ASTKind.Attribute:
|
||||
cty_at3: ast.Attribute* = ast.Attribute*(cty_arg3)
|
||||
if cty_at3.attr is not None and string.strcmp(cty_at3.attr, "CPtr") == 0:
|
||||
@@ -413,7 +412,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
|
||||
if cty_val is not None: return self.Gen.CallGenCtor._translate_t_type_cast(pool, builder, cty_val, ctype_target, cty_arg0, trans)
|
||||
# t.CType confirmed: even if type resolution fails, do not fall through to a cross-module FATAL
|
||||
return None
|
||||
# CConst is a const marker: CConst(val, t.CPtr) -> Ptr(i8) (const void*). Without
|
||||
# CConst is a const marker: CConst(val, rawptr) -> Ptr(i8) (const void*). Without
|
||||
# this it emits an undefined @CConst; single-arg CConst(x) returns the value unchanged.
|
||||
if string.strcmp(func_name, "CConst") == 0:
|
||||
if can >= 2:
|
||||
@@ -425,13 +424,13 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
|
||||
target_ty: mlirlite.LLVMType* = self.Gen.TypeGenResolve.map_t_type(pool, func_name)
|
||||
if target_ty is not None:
|
||||
tc_effective_can: sizet = can
|
||||
# Two-arg form: int(val, t.CPtr) -> Ptr(target_ty). void* is illegal in LLVM,
|
||||
# Two-arg form: int(val, rawptr) -> Ptr(target_ty). void* is illegal in LLVM,
|
||||
# so CVoid* must be represented as i8*.
|
||||
if can == 2:
|
||||
tc_arg2_node: ast.AST* = cargs.get(1)
|
||||
tc_arg2_is_cptr: int = 0
|
||||
if tc_arg2_node is not None:
|
||||
# Form 1: t.CPtr (Attribute form)
|
||||
# Form 1: rawptr (Attribute form)
|
||||
if tc_arg2_node.kind() == ast.ASTKind.Attribute:
|
||||
tc_arg2_at: ast.Attribute* = ast.Attribute*(tc_arg2_node)
|
||||
if tc_arg2_at.attr is not None and string.strcmp(tc_arg2_at.attr, "CPtr") == 0:
|
||||
@@ -461,7 +460,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
|
||||
# t.XXX cast confirmed but arg shape unsupported: return None, do not fall through to FATAL
|
||||
return None
|
||||
|
||||
# Module-qualified typedef cast (posix.mode_t(mode), w32.win32base.HANDLE(v), ...): func is
|
||||
# Module-qualified typedef cast (posix.mode_t(mode), win32.win32base.HANDLE(v), ...): func is
|
||||
# Attribute(Name(module), typedef); non-typedef names still go to the module function call.
|
||||
if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute:
|
||||
mt_at: ast.Attribute* = ast.Attribute*(cl.func)
|
||||
@@ -513,13 +512,13 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
|
||||
imp_gp = trans._imported_modules
|
||||
fi_gp = trans._from_imports
|
||||
bare_ty = self.Gen.TypeGenResolve.resolve_annotation_type(pool, gp_ast, imp_gp, fi_gp, trans, 0)
|
||||
# Generic two-arg form T(val, t.CPtr) -> Ptr(T), aligned with int(val, t.CPtr) -> Ptr(i32);
|
||||
# Generic two-arg form T(val, rawptr) -> Ptr(T), aligned with int(val, rawptr) -> Ptr(i32);
|
||||
# without it the two-arg call misses the can==1 branch and misreports an undefined bare call.
|
||||
if bare_ty is not None and can == 2:
|
||||
bare_arg2_node: ast.AST* = cargs.get(1)
|
||||
bare_arg2_is_cptr: int = 0
|
||||
if bare_arg2_node is not None:
|
||||
# Form 1: t.CPtr (Attribute form)
|
||||
# Form 1: rawptr (Attribute form)
|
||||
if bare_arg2_node.kind() == ast.ASTKind.Attribute:
|
||||
bare_arg2_at: ast.Attribute* = ast.Attribute*(bare_arg2_node)
|
||||
if bare_arg2_at.attr is not None and string.strcmp(bare_arg2_at.attr, "CPtr") == 0:
|
||||
@@ -552,7 +551,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
|
||||
|
||||
return None
|
||||
|
||||
def _tc_h5_sizeof_len_isinst(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cl: ast.Call*, funcs_ptr: t.CPtr, func_count: int, trans: HT.Translator*, func_name: str, cargs: list[ast.AST*]*, can: sizet) -> mlirlite.Value*:
|
||||
def _tc_h5_sizeof_len_isinst(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cl: ast.Call*, funcs_ptr: rawptr, func_count: int, trans: HT.Translator*, func_name: str, cargs: list[ast.AST*]*, can: sizet) -> mlirlite.Value*:
|
||||
"""__sizeof__/len/isinstance built-ins; returns None when unmatched."""
|
||||
# Detect ClassName.__sizeof__() and return the struct size constant (i64); it is a builtin,
|
||||
# not a cross-module call. Supports e.g. Argument.__sizeof__() and module-qualified classes.
|
||||
@@ -568,7 +567,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
|
||||
elif sz_vk == ast.ASTKind.Attribute:
|
||||
sz_inner_at: ast.Attribute* = ast.Attribute*(sz_at.value)
|
||||
sz_cls_name = sz_inner_at.attr
|
||||
# Keep the full module path (w32.win32base.SECURITY_ATTRIBUTES): using only attr
|
||||
# Keep the full module path (win32.win32base.SECURITY_ATTRIBUTES): using only attr
|
||||
# would let get_struct_type globally match a same-named class from another module.
|
||||
if sz_inner_at.value is not None and sz_inner_at.attr is not None:
|
||||
if sz_inner_at.value.kind() == ast.ASTKind.Name:
|
||||
@@ -605,7 +604,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
|
||||
if string.strcmp(sz_cls_name, sz_orig) == 0 and trans is not None and trans.GenericTypeParamNames is not None and trans.GenericTypeArgs is not None:
|
||||
gp_n: sizet = len(trans.GenericTypeParamNames)
|
||||
gp_i: sizet
|
||||
for gp_i in range(gp_n):
|
||||
for gp_i in range(int(gp_n)):
|
||||
gpn: str = trans.GenericTypeParamNames.get(gp_i)
|
||||
if gpn is not None and string.strcmp(sz_orig, gpn) == 0:
|
||||
gta: str = trans.GenericTypeArgs.get(gp_i)
|
||||
@@ -616,7 +615,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
|
||||
# dot after a sha1 prefix. Union types (X*) and rawptr[X] both have pointer size 8.
|
||||
if string.strstr(sz_cls_name, "|") is not None: return mlirlite.const_int64(pool, 8)
|
||||
if string.strncmp(sz_cls_name, "rawptr[", 7) == 0: return mlirlite.const_int64(pool, 8)
|
||||
# A trailing '*' means pointer size 8 (rawptr[T] == t.CPtr[T] == T*). Must check
|
||||
# A trailing '*' means pointer size 8 (rawptr[T] == rawptr[T] == T*). Must check
|
||||
# trailing, not strrchr, since containers like list[{sha}.AST*] also contain '*'.
|
||||
_sz_len_s: sizet = string.strlen(sz_cls_name)
|
||||
while _sz_len_s > 0 and sz_cls_name[_sz_len_s - 1] == ' ': _sz_len_s -= 1
|
||||
@@ -689,7 +688,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
|
||||
if gargs_resolved is not None:
|
||||
gai_sz: sizet = len(gen_args_sz)
|
||||
gai2_sz: sizet = 0
|
||||
for gai2_sz in range(gai_sz):
|
||||
for gai2_sz in range(int(gai_sz)):
|
||||
gres_sz: str = self.Gen.TypeGenBase.resolve_generic_type_name(gen_args_sz.get(gai2_sz))
|
||||
gargs_resolved.append(gres_sz)
|
||||
spec_sz: str = self.Gen.ClassGenGeneric._specialize_generic_class(trans, gen_nm_sz.id, gargs_resolved)
|
||||
@@ -828,7 +827,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
|
||||
return mlirlite.build_call(builder, len_mangled_x, len_self_x, 1, len_ret_x, 0)
|
||||
# fail-fast: the class has no __len__ (compile-time error, matching TPC
|
||||
# HandlesExprBuiltin._FailFastLenUnsupported wording).
|
||||
self.Gen.TypeGenErr.fatal_error(len_arg_node, f"len() argument type '{len_cls_nm}' has no __len__ method. len() only supports fixed-size arrays/array pointers (t.CArray[T, N]), i8* (str/bytes), or structs with __len__; non-i8 bare pointers/scalar pointers have no length semantics: use str/bytes for strings, record the length yourself when the element count is needed, or use t.CPtr[Element] and maintain the element count explicitly", "TypeError")
|
||||
self.Gen.TypeGenErr.fatal_error(len_arg_node, f"len() argument type '{len_cls_nm}' has no __len__ method. len() only supports fixed-size arrays/array pointers (t.CArray[T, N]), i8* (str/bytes), or structs with __len__; non-i8 bare pointers/scalar pointers have no length semantics: use str/bytes for strings, record the length yourself when the element count is needed, or use rawptr[Element] and maintain the element count explicitly", "TypeError")
|
||||
# Non-struct: str (Ptr(Int8)) uses strlen; other types fail fast
|
||||
len_arg_ty: mlirlite.LLVMType* = mlirlite.value_get_ty(len_val)
|
||||
len_is_str: int = 0
|
||||
@@ -861,7 +860,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
|
||||
return mlirlite.build_call(builder, "strlen", len_coerced, 1, i64_ty_ln2, 0)
|
||||
# fail-fast: not a string/array/object, so length is unavailable (matching TPC
|
||||
# HandlesExprBuiltin._FailFastLenUnsupported wording).
|
||||
self.Gen.TypeGenErr.fatal_error(len_arg_node, "len() argument type not supported (not a string/array/object). len() only supports fixed-size arrays/array pointers (t.CArray[T, N]), i8* (str/bytes), or structs with __len__; non-i8 bare pointers/scalar pointers have no length semantics: use str/bytes for strings, record the length yourself when the element count is needed, or use t.CPtr[Element] and maintain the element count explicitly", "TypeError")
|
||||
self.Gen.TypeGenErr.fatal_error(len_arg_node, "len() argument type not supported (not a string/array/object). len() only supports fixed-size arrays/array pointers (t.CArray[T, N]), i8* (str/bytes), or structs with __len__; non-i8 bare pointers/scalar pointers have no length semantics: use str/bytes for strings, record the length yourself when the element count is needed, or use rawptr[Element] and maintain the element count explicitly", "TypeError")
|
||||
|
||||
# Detect isinstance(): runtime vtable subclass-set comparison. obj's head vtable pointer must
|
||||
# be in {Cls and all subclass vtables}; NoVTable targets fall back to a compile-time check.
|
||||
@@ -873,7 +872,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
|
||||
|
||||
return None
|
||||
|
||||
def _tc_h6_method_dispatch(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cl: ast.Call*, funcs_ptr: t.CPtr, func_count: int, trans: HT.Translator*, func_name: str, cargs: list[ast.AST*]*, can: sizet, hit_box: t.CPtr = None) -> mlirlite.Value*:
|
||||
def _tc_h6_method_dispatch(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cl: ast.Call*, funcs_ptr: rawptr, func_count: int, trans: HT.Translator*, func_name: str, cargs: list[ast.AST*]*, can: sizet, hit_box: rawptr = None) -> mlirlite.Value*:
|
||||
"""Static class/method/receiver/self.field/list built-ins; sets hit_box, else returns None."""
|
||||
# REnum.kind() compiles to a __tag read (offset 0, type from the field declaration): bitcast obj
|
||||
# to __tag* + load. Four criteria detect REnum, incl. pointee __tag and X.field annotations.
|
||||
@@ -917,7 +916,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
|
||||
rk_ve: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, rk_nm.id)
|
||||
rk_annot_raw: str = self.Gen.VarGen.var_annot_class_name(rk_ve)
|
||||
if rk_annot_raw is not None:
|
||||
# Truncate the union/generic suffix (" | t.CPtr" / "[...]")
|
||||
# Truncate the union/generic suffix (" | rawptr" / "[...]")
|
||||
rk_ae: sizet = 0
|
||||
while rk_annot_raw[rk_ae] != '\0' and rk_annot_raw[rk_ae] != ' ' and rk_annot_raw[rk_ae] != '|' and rk_annot_raw[rk_ae] != '[': rk_ae += 1
|
||||
rk_ab: str = pool.alloc(rk_ae + 1)
|
||||
@@ -1018,7 +1017,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
|
||||
im_rem = pool.alloc(sizet(can - 1) * 8)
|
||||
if im_rem is not None:
|
||||
ri_im: sizet
|
||||
for ri_im in range(can - 1):
|
||||
for ri_im in range(int(can - 1)):
|
||||
im_rv: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, cargs.get(ri_im + 1), None, 0, trans)
|
||||
if im_rv is not None: im_rem[ri_im] = sizet(im_rv)
|
||||
im_res: mlirlite.Value* = self.Gen.CallGenCall._call_method_on_ptr(pool, builder, mod, st_nm.id, func_name, im_self, im_rem, can - 1, trans)
|
||||
@@ -1200,7 +1199,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
|
||||
mlirlite.value_set_next(obj_ptr3, None)
|
||||
head3: mlirlite.Value* = obj_ptr3
|
||||
tail3: mlirlite.Value* = obj_ptr3
|
||||
for ai3 in range(can):
|
||||
for ai3 in range(int(can)):
|
||||
arg3: ast.AST* = cargs.get(ai3)
|
||||
if arg3 is not None:
|
||||
arg_val3: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, arg3, None, 0, trans)
|
||||
@@ -1208,7 +1207,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
|
||||
mlirlite.value_set_next(arg_val3, None)
|
||||
mlirlite.value_set_next(tail3, arg_val3)
|
||||
tail3 = arg_val3
|
||||
total3: int = 1 + can
|
||||
total3: int = int(1 + can)
|
||||
ret_ty3: mlirlite.LLVMType* = self._special_method_ret_ty(pool, cls_nm2, func_name)
|
||||
if ret_ty3 is None:
|
||||
# Authoritative source: vpi stub return-annotation cache (from the method definition)
|
||||
@@ -1266,13 +1265,18 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
|
||||
if expr_cls_nm is not None:
|
||||
# Value-type receivers (embedded struct fields) must pass the field address (GEP),
|
||||
# not the loaded struct value, or the ABI mismatches and the callee dereferences it as this.
|
||||
# **只有 Attribute 接收者才是"字段"**:本分支的接收者按 L1236 的设计可以是
|
||||
# BinOp/Subscript/Call(如 `(p - q).method()`),它们绝不能进 get_attribute_ptr
|
||||
# —— 该 API 只接受 Attribute,内部直接强转成 ast.Attribute 并读 `.attr`;
|
||||
# 对别的节点就会命中偏移 9 的那个字段(BinOp 的 op=1 / Name 的 ctx=1 …),
|
||||
# 把整数 1 当字符串传给 strcmp → ASan 在 0x1 上 access-violation。
|
||||
expr_is_ptr: int = 0
|
||||
match expr_obj.Ty:
|
||||
case mlirlite.LLVMType.Ptr(expr_ptr_inner):
|
||||
expr_is_ptr = 1
|
||||
case _:
|
||||
expr_is_ptr = 0
|
||||
if expr_is_ptr == 0:
|
||||
if expr_is_ptr == 0 and expr_method_at.value.kind() == ast.ASTKind.Attribute:
|
||||
expr_addr: mlirlite.Value* = self.Gen.ExprGenAttr.get_attribute_ptr(builder, pool, mod, expr_method_at.value, trans)
|
||||
if expr_addr is not None: expr_obj = expr_addr
|
||||
if hit_box is not None: hit_box[0] = 1
|
||||
|
||||
@@ -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
|
||||
@@ -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:
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
import lib.core.Gen.GenTypes as GenTypes
|
||||
import t, c
|
||||
from stdint import *
|
||||
import ast
|
||||
import mlirlite
|
||||
import memhub
|
||||
import string
|
||||
import stdio
|
||||
import stdlib
|
||||
import viperlib
|
||||
import lib.core.VLogger as VLogger
|
||||
import lib.core.Gen.Translator as HT
|
||||
import lib.core.Gen.Type.TypeSpec as TSpec
|
||||
import lib.Stub.StubGen.VpiReader as VpiReader
|
||||
@@ -134,7 +131,7 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
|
||||
pname_kw: str = mlirlite.param_get_name(cur_param)
|
||||
if pname_kw is not None and keywords is not None:
|
||||
ki_kw: sizet
|
||||
for ki_kw in range(kw_count):
|
||||
for ki_kw in range(int(kw_count)):
|
||||
kw_node: ast.AST* = keywords.get(ki_kw)
|
||||
if kw_node is None: continue
|
||||
kw_el: ast.Keyword* = ast.Keyword*(kw_node)
|
||||
@@ -148,7 +145,7 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
|
||||
cur_param = mlirlite.param_get_next(cur_param)
|
||||
return 0
|
||||
|
||||
def translate_call(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, node: ast.AST*, funcs_ptr: t.CPtr, func_count: int, trans: HT.Translator* = None) -> mlirlite.Value*:
|
||||
def translate_call(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, node: ast.AST*, funcs_ptr: rawptr, func_count: int, trans: HT.Translator* = None) -> mlirlite.Value*:
|
||||
"""Translate a Call node and return the result Value."""
|
||||
cl: ast.Call* = ast.Call*(node)
|
||||
# Static symbols must not be used cross-module: fail fast on mod.fn(...) hitting another
|
||||
@@ -235,7 +232,7 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
|
||||
esc_mask = (esc_mask | fe_m) & (~ne_m)
|
||||
if esc_mask != 0:
|
||||
_dummy_ptr: mlirlite.LLVMType* = mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||||
for ai in range(can):
|
||||
for ai in range(int(can)):
|
||||
if ((esc_mask >> int(ai)) & 1) != 0:
|
||||
# 泄漏点:把"被调者 + 形参位"交给检查方,确认真要报错后才去采集(同模块才有行号)
|
||||
GenApi.fail_if_stack_addr_escape(cargs.get(ai), "an escaping callee parameter", trans, _dummy_ptr, func_name, int(ai))
|
||||
@@ -265,7 +262,7 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
|
||||
wf_mask = self.Gen.CallGenBase.lookup_willfree_via_vpi(pool, mod_sha_wf, None, func_name)
|
||||
# Generic function calls (PEP 695 def f[T]) are intercepted first: infer type args and
|
||||
# monomorphize here (templates emit no plain function); hit_box=1 means handled-with-void.
|
||||
h0_hit: t.CPtr = stdlib.malloc(4)
|
||||
h0_hit: rawptr = stdlib.malloc(4)
|
||||
if h0_hit is not None: h0_hit[0] = 0
|
||||
_tc_h0_generic_func_res: mlirlite.Value* = self.Gen.CallGenDispatch._tc_h0_generic_func(pool, builder, mod, cl, funcs_ptr, func_count, trans, func_name, cargs, can, h0_hit)
|
||||
h0_hit_val: int = 0
|
||||
@@ -276,7 +273,7 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
|
||||
if h0_hit_val != 0: return None
|
||||
# super()/ctraits detection: a void method yields None but is handled, so hit_box must
|
||||
# separate "handled" from "missed"; otherwise super() falls through and reports undefined.
|
||||
h1_hit: t.CPtr = stdlib.malloc(4)
|
||||
h1_hit: rawptr = stdlib.malloc(4)
|
||||
if h1_hit is not None: h1_hit[0] = 0
|
||||
_tc_h1_super_ctraits_res: mlirlite.Value* = self.Gen.CallGenDispatch._tc_h1_super_ctraits(pool, builder, mod, cl, funcs_ptr, func_count, trans, func_name, cargs, can, h1_hit)
|
||||
h1_hit_val: int = 0
|
||||
@@ -310,7 +307,7 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
|
||||
return self.Gen.CallGenCtor._translate_generic_ctor_call(pool, builder, mod, spec_name, cl, trans, gen_class_name)
|
||||
else:
|
||||
# No generic class template: maybe an explicit generic-function call xxx[T](x);
|
||||
# TPV compiles generic functions as plain functions, so drop [T] if a same-name function exists.
|
||||
# VPC compiles generic functions as plain functions, so drop [T] if a same-name function exists.
|
||||
if self.find_func_in_module(mod, gen_class_name) is not None: func_name = gen_class_name
|
||||
else:
|
||||
# Builtin pointer/type subscript cast: (rawptr[list[int]])(lst), (t.CPtr[T])(x),
|
||||
@@ -340,7 +337,7 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
|
||||
|
||||
# c.Asm/c.LLVMIR special calls return None when void but are handled, so use hit_box to
|
||||
# separate "missed"; otherwise they fall through to the external-function path and mis-report.
|
||||
h2_hit: t.CPtr = stdlib.malloc(4)
|
||||
h2_hit: rawptr = stdlib.malloc(4)
|
||||
if h2_hit is not None: h2_hit[0] = 0
|
||||
_tc_h2_c_special_res: mlirlite.Value* = self.Gen.CallGenDispatch._tc_h2_c_special(pool, builder, mod, cl, funcs_ptr, func_count, trans, func_name, cargs, can, h2_hit)
|
||||
h2_hit_val: int = 0
|
||||
@@ -363,7 +360,7 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
|
||||
|
||||
# Method dispatch may succeed yet return None (e.g. void __before_init__/__init__), so hit_box
|
||||
# must separate "recognized method" from "missed"; otherwise the object name is looked up as a module.
|
||||
h6_hit: t.CPtr = stdlib.malloc(4)
|
||||
h6_hit: rawptr = stdlib.malloc(4)
|
||||
if h6_hit is not None: h6_hit[0] = 0
|
||||
_tc_h6_method_dispatch_res: mlirlite.Value* = self.Gen.CallGenDispatch._tc_h6_method_dispatch(pool, builder, mod, cl, funcs_ptr, func_count, trans, func_name, cargs, can, h6_hit)
|
||||
h6_hit_val: int = 0
|
||||
@@ -422,7 +419,7 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
|
||||
if gen_ctor_entry is None: gen_ctor_entry = self.Gen.StructGen.find_struct(func_name)
|
||||
if gen_ctor_entry is not None: struct_ty = gen_ctor_entry.Ty
|
||||
elif at_qc.value is not None and at_qc.value.kind() == ast.ASTKind.Attribute:
|
||||
# Multi-level module path construction: w32.fileio.File(...); build the path via
|
||||
# Multi-level module path construction: win32.fileio.File(...); build the path via
|
||||
# attr_module_path_str, check the full import, then find the struct by that module's SHA1.
|
||||
mod_path_qc: str = self.Gen.ExprGenAttr._attr_module_path_str(pool, at_qc.value)
|
||||
if mod_path_qc is not None:
|
||||
@@ -445,7 +442,7 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
|
||||
while ctor_orig[co_len] != ' ' and ctor_orig[co_len] != '\0': co_len += 1
|
||||
co_buf: str = pool.alloc(co_len + 1)
|
||||
if co_buf is not None:
|
||||
for co_i in range(co_len): co_buf[co_i] = ctor_orig[co_i]
|
||||
for co_i in range(int(co_len)): co_buf[co_i] = ctor_orig[co_i]
|
||||
co_buf[co_len] = '\0'
|
||||
# Look up the struct by original name (visibility already ensured by the alias).
|
||||
gen_ctor_entry = self.Gen.StructGen.find_struct_by_module(co_buf, ctor_sha1)
|
||||
@@ -467,7 +464,7 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
|
||||
# Plain struct: check for default values (access via entry directly).
|
||||
gen_has_defaults: int = 0
|
||||
if gen_ctor_entry is not None:
|
||||
for gdi in range(gen_ctor_entry.FieldCount):
|
||||
for gdi in range(int(gen_ctor_entry.FieldCount)):
|
||||
gdf: GenTypes.FieldEntry* = self.Gen.StructGen._get_field_entry(gen_ctor_entry, gdi)
|
||||
if gdf is not None and gdf.DefaultVal is not None:
|
||||
gen_has_defaults = 1
|
||||
@@ -509,7 +506,7 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
|
||||
# Map each type param to the positional arg whose __init__ annotation matches it (not by index);
|
||||
ctx_args_bare: list[str]* = self.Gen.CallGenCtor._infer_ctor_type_args_from_ctx(pool, trans, func_name, int(tp_count_bare), cl)
|
||||
tpi_bare: sizet = 0
|
||||
for tpi_bare in range(tp_count_bare):
|
||||
for tpi_bare in range(int(tp_count_bare)):
|
||||
aip_bare: int = self.Gen.CallGenCtor._find_ctor_type_param_arg_index(func_name, tp_bare.get(tpi_bare))
|
||||
if aip_bare < 0: aip_bare = int(tpi_bare)
|
||||
ais_bare: sizet = sizet(aip_bare)
|
||||
@@ -573,7 +570,7 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
|
||||
rc_mq_tag: int = self.Gen.ClassGenEnum._lookup_renum_variant_in_class(rc_mq_cls, func_name)
|
||||
if rc_mq_tag >= 0: return self.Gen.CallGenCtor._translate_renum_construct(pool, builder, mod, rc_mq_cls, func_name, rc_mq_tag, cargs, can, trans, cl.IsVolatile)
|
||||
|
||||
# Bare async call (TPV-M1/M2): calling an async function (task(5,6)) emits create_<name>(...).
|
||||
# Bare async call (VPC-M1/M2): calling an async function (task(5,6)) emits create_<name>(...).
|
||||
# Intercept before bare-name lookup: async functions register only create_/resume_, not the bare name.
|
||||
if cl.func is not None and cl.func.kind() == ast.ASTKind.Name:
|
||||
if func_name is not None and trans is not None:
|
||||
@@ -594,7 +591,7 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
|
||||
if cl.args is not None:
|
||||
nb_an: sizet = len(cl.args)
|
||||
j_an: sizet = 0
|
||||
for j_an in range(nb_an):
|
||||
for j_an in range(int(nb_an)):
|
||||
av_an: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, cl.args.get(j_an), None, 0, trans)
|
||||
if av_an is None: av_an = mlirlite.const_int32(pool, 0)
|
||||
av_an.Next = None
|
||||
@@ -781,7 +778,7 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
|
||||
found_func = bi_decl
|
||||
# Use accessors to bypass stub type restrictions.
|
||||
found_ret_ty: mlirlite.LLVMType* = None
|
||||
found_params: t.CPtr = None
|
||||
found_params: rawptr = None
|
||||
if found_func is not None:
|
||||
found_ret_ty = mlirlite.function_get_ret_ty(found_func)
|
||||
found_params = mlirlite.function_get_params(found_func)
|
||||
@@ -798,7 +795,7 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
|
||||
if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute:
|
||||
at_ret: ast.Attribute* = ast.Attribute*(cl.func)
|
||||
# Resolve the callee module path: both one-level (stdlib.free) and multi-level
|
||||
# (w32.win32file.CreatePipe). A missing multi-level path would wrongly fail-fast a resolvable call.
|
||||
# (win32.win32file.CreatePipe). A missing multi-level path would wrongly fail-fast a resolvable call.
|
||||
if at_ret.value is not None and at_ret.value.kind() == ast.ASTKind.Name:
|
||||
mod_ret_nm: ast.Name* = ast.Name*(at_ret.value)
|
||||
mod_path_ret = mod_ret_nm.id
|
||||
@@ -912,7 +909,7 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
|
||||
return self.Gen.CallGenLeafKit.translate_closure_call(pool, builder, mod, fp_alloca, cargs, can, trans)
|
||||
# ---- M3: session-function table hit -> take the ctx function-pointer slot and call indirectly ----
|
||||
# Placed last in the found_func-is-None block so locals/closures shadow session functions; only foreign bare names reach the table.
|
||||
sfn_box: t.CPtr = None
|
||||
sfn_box: rawptr = None
|
||||
if trans is not None and trans.ReplCtx is not None and (cl.func is None or cl.func.kind() == ast.ASTKind.Name):
|
||||
sfn_box = stdlib.malloc(4)
|
||||
if sfn_box is not None:
|
||||
@@ -951,7 +948,7 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
|
||||
if self.Gen.CallGenBase.TempDir is not None and self.Gen.CallGenBase.TempDirLen > 0:
|
||||
VpiReader._ensure_vpi_param_annot_loaded(trans.ModuleSha1, self.Gen.CallGenBase.TempDir)
|
||||
same_mod_vpi = 1
|
||||
for ai in range(can):
|
||||
for ai in range(int(can)):
|
||||
arg: ast.AST* = cargs.get(ai)
|
||||
if arg is not None:
|
||||
# Fetch the param type first: a single-quoted char literal (CONST_CHAR) passed to str/pointer
|
||||
@@ -1033,12 +1030,17 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
|
||||
if sm_is_str != 0: self.Gen.ExprGenBase.fail_if_str_annot_agg_ptr(arg_val, 1, arg)
|
||||
arg_val = self.Gen.ExprGenBase.coerce_to_type(builder, arg_val, fpty, arg, trans)
|
||||
elif found_func.IsVarArg == 1:
|
||||
# Win64 variadic 8-byte slots: pass sub-64-bit integers as i64 (sext) and promote f32 to f64;
|
||||
# Win64 variadic 8-byte slots: pass sub-64-bit integers as i64 and promote f32 to f64;
|
||||
# passing the narrow type leaves old stack data in the high 4 bytes that va_arg(i64) reads as garbage.
|
||||
# The widening direction follows the argument's signedness (unsigned -> zext), matching
|
||||
# CallGenVa._check_and_promote_variadic_call: an unconditional sext turns uint8 0x8E into
|
||||
# 0xFFFFFFFFFFFFFF8E (the "sext family" recorded in Debugger/VKERNELERR.md).
|
||||
ab_va: int = self.Gen.ExprGenBase.get_llvm_type_bits(arg_val.Ty)
|
||||
af_va: int = self.Gen.ExprGenBase.get_llvm_float_bits(arg_val.Ty)
|
||||
i64_va: mlirlite.LLVMType* = mlirlite.Int64(pool)
|
||||
if ab_va != 0 and ab_va < 64: arg_val = mlirlite.build_sext(builder, arg_val, i64_va)
|
||||
if ab_va != 0 and ab_va < 64:
|
||||
if self.Gen.ExprGenOps.should_zext_widen(pool, arg_val, None, arg, trans) != 0: arg_val = mlirlite.build_zext(builder, arg_val, i64_va)
|
||||
else: arg_val = mlirlite.build_sext(builder, arg_val, i64_va)
|
||||
elif af_va == 32: arg_val = mlirlite.build_fpext(builder, arg_val, mlirlite.Double(pool))
|
||||
arg_val.Next = None
|
||||
if head is None: head = arg_val
|
||||
@@ -1054,7 +1056,7 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
|
||||
# 该槽其后被读/取址/再传(含再传 free)时由变量读取路径报 LifetimeError。
|
||||
# 关键字实参路径(_translate_call_with_kwargs)不支持该标记:见文档记录的限制。
|
||||
if wf_mask != 0 and cargs is not None and trans is not None:
|
||||
for ai_wf in range(can):
|
||||
for ai_wf in range(int(can)):
|
||||
if ((wf_mask >> int(ai_wf)) & 1) == 0: continue
|
||||
arg_wf: ast.AST* = cargs.get(ai_wf)
|
||||
if arg_wf is None or arg_wf.kind() != ast.ASTKind.Name: continue
|
||||
@@ -1064,7 +1066,7 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
|
||||
# Default-argument filling: when fewer args than params, fill the gaps; the defaults array is
|
||||
# right-aligned to args (defaults[0] maps to args[offset], offset = param_count - default_count).
|
||||
if can > 0 or (can == 0 and cargs is not None):
|
||||
df_defaults: t.CPtr = None
|
||||
df_defaults: rawptr = None
|
||||
df_default_count: int = 0
|
||||
df_param_count: int = 0
|
||||
# Compute the callee module SHA1 as a qualifier key (module.func -> that module; bare -> current),
|
||||
@@ -1130,14 +1132,14 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
|
||||
# Fill in the missing arguments.
|
||||
if df_default_count > 0 and can < df_param_count and (df_defaults is not None or gd_entry is not None):
|
||||
df_offset: int = df_param_count - df_default_count
|
||||
for df_di in range(df_default_count):
|
||||
for df_di in range(int(df_default_count)):
|
||||
df_param_idx: int = df_offset + df_di
|
||||
if df_param_idx >= can:
|
||||
# Prefer rebuilding from the stdlib snapshot (gd_entry.Defaults' in-pool AST may already be reclaimed).
|
||||
df_arg_val: mlirlite.Value* = None
|
||||
if gd_entry is not None: df_arg_val = self.Gen.CallGenLeafKit._build_const_from_snapshot(pool, mod, gd_entry, df_di, trans, builder)
|
||||
if df_arg_val is None and df_defaults is not None:
|
||||
df_defaults_list: list[ast.AST*]* = (list[ast.AST*] | t.CPtr)(uint64(df_defaults))
|
||||
df_defaults_list: list[ast.AST*]* = list[ast.AST*]*(df_defaults)
|
||||
df_default_node: ast.AST* = df_defaults_list.get(df_di)
|
||||
if df_default_node is not None: df_arg_val = self.Gen.ExprGen.translate_value(builder, pool, mod, df_default_node, None, 0, trans)
|
||||
if df_arg_val is not None:
|
||||
@@ -1356,7 +1358,7 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
|
||||
while bare_orig[bo_len] != ' ' and bare_orig[bo_len] != '\0': bo_len += 1
|
||||
bo_buf: str = pool.alloc(bo_len + 1)
|
||||
if bo_buf is not None:
|
||||
for bo_i in range(bo_len): bo_buf[bo_i] = bare_orig[bo_i]
|
||||
for bo_i in range(int(bo_len)): bo_buf[bo_i] = bare_orig[bo_i]
|
||||
bo_buf[bo_len] = '\0'
|
||||
bare_func_name = bo_buf
|
||||
# Record the original function name for later return-type inference (from json.__parser
|
||||
@@ -1441,7 +1443,7 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
|
||||
# (exact integer widths), so call args / declare / define all agree (B3 generalization).
|
||||
head = self.Gen.CallGenLib._coerce_args_to_def_params(pool, builder, call_name, head, can, cargs, 0, trans)
|
||||
# Cross-module call: create the declare to avoid llc's undefined value error.
|
||||
self._ensure_method_declare(pool, mod, call_name, call_ret_ty, head, can)
|
||||
self._ensure_method_declare(pool, mod, call_name, call_ret_ty, head, int(can))
|
||||
# Check whether the callee is variadic (*args) and, if so, promote the variadic args to the C ABI
|
||||
# (cargs is the arg AST list; self_offset=0 for a plain function call).
|
||||
is_variadic_call: int = self.Gen.CallGenVa._check_and_promote_variadic_call(pool, builder, mod, call_name, head, can, cargs, sizet(0), trans)
|
||||
|
||||
@@ -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
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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*:
|
||||
@@ -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.
|
||||
@@ -565,18 +648,33 @@ novirtual class ExprGenBase:
|
||||
# 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).
|
||||
# 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)}")
|
||||
|
||||
@@ -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
|
||||
@@ -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.
|
||||
@@ -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,11 +1,9 @@
|
||||
import t, c
|
||||
from stdint import *
|
||||
import ast
|
||||
import mlirlite
|
||||
import memhub
|
||||
import string
|
||||
import viperlib
|
||||
import stdio
|
||||
import stdlib
|
||||
import lib.core.VLogger as VLogger
|
||||
import lib.core.Gen.Translator as HT
|
||||
@@ -29,7 +27,7 @@ novirtual class FuncGenBase(CallGenBase.CallGenBase):
|
||||
attrs_buf[0] = '\0'
|
||||
found_any: int = 0
|
||||
|
||||
for di in range(dn):
|
||||
for di in range(int(dn)):
|
||||
deco: ast.AST* = decorator_list.get(di)
|
||||
if deco is None: continue
|
||||
dk: int = deco.kind()
|
||||
@@ -56,14 +54,17 @@ novirtual class FuncGenBase(CallGenBase.CallGenBase):
|
||||
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
|
||||
# 不要求 `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(an):
|
||||
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)
|
||||
@@ -83,7 +84,7 @@ novirtual class FuncGenBase(CallGenBase.CallGenBase):
|
||||
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):
|
||||
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)
|
||||
@@ -93,7 +94,7 @@ novirtual class FuncGenBase(CallGenBase.CallGenBase):
|
||||
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):
|
||||
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)
|
||||
@@ -160,7 +161,7 @@ novirtual class FuncGenBase(CallGenBase.CallGenBase):
|
||||
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);
|
||||
# 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)
|
||||
@@ -216,7 +217,7 @@ novirtual class FuncGenBase(CallGenBase.CallGenBase):
|
||||
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):
|
||||
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
|
||||
@@ -240,7 +241,7 @@ novirtual class FuncGenBase(CallGenBase.CallGenBase):
|
||||
"""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):
|
||||
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)
|
||||
@@ -268,7 +269,7 @@ novirtual class FuncGenBase(CallGenBase.CallGenBase):
|
||||
alist: list[ast.AST*]* = ags.args
|
||||
an: sizet = len(alist)
|
||||
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 not None and arg.arg is not None:
|
||||
# t.CVoid means no args: skip
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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) ----------------
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import t, c
|
||||
from stdint import *
|
||||
import ast
|
||||
import mlirlite
|
||||
@@ -343,6 +342,10 @@ novirtual class MatchGenMixin:
|
||||
# fail-fast: match cases missing.
|
||||
self.Gen.TypeGenErr.fatal_error(node, "match cases missing", "TypeError")
|
||||
case_count: sizet = len(cases_list)
|
||||
# fail-fast: the dispatch arrays are fixed at MAX_MATCH_CASES; silently dropping the excess
|
||||
# arms produced a switch with fewer branches (wrong dispatch, no diagnostic).
|
||||
if case_count > sizet(MAX_MATCH_CASES):
|
||||
self.Gen.TypeGenErr.fatal_error(node, f"match has {case_count} cases but the compiler limit is {MAX_MATCH_CASES}; split the match statement", "OverflowError")
|
||||
|
||||
# BB pointer array, case-value pointer array, default-flag array, nobreak-flag array.
|
||||
bb_arr: sizet* = pool.alloc(8 * MAX_MATCH_CASES)
|
||||
@@ -432,7 +435,8 @@ novirtual class MatchGenMixin:
|
||||
|
||||
# Create the case BB.
|
||||
if is_def == 0:
|
||||
if collected >= MAX_MATCH_CASES: break
|
||||
if collected >= MAX_MATCH_CASES:
|
||||
self.Gen.TypeGenErr.fatal_error(node, f"match case count exceeds the compiler limit {MAX_MATCH_CASES}; split the match statement", "OverflowError")
|
||||
viperlib.snprintf(name_buf, 32, "match.case.%d.%d", cnt, i)
|
||||
bb: mlirlite.BasicBlock* = mlirlite.create_block(pool, func, name_buf)
|
||||
else: bb = default_bb
|
||||
@@ -630,7 +634,7 @@ novirtual class MatchGenMixin:
|
||||
casted_ptr: mlirlite.Value* = mlirlite.build_bitcast(builder, subject_ptr, renum_ptr_ty)
|
||||
if casted_ptr is None: casted_ptr = subject_ptr
|
||||
pn: sizet = len(pats)
|
||||
for j in range(pn):
|
||||
for j in range(int(pn)):
|
||||
sub_pat: ast.AST* = pats.get(j)
|
||||
if sub_pat is not None and sub_pat.kind() == ast.ASTKind.MatchAs:
|
||||
ma: ast.MatchAs* = ast.MatchAs*(sub_pat)
|
||||
@@ -646,7 +650,7 @@ novirtual class MatchGenMixin:
|
||||
if alt_idx >= 1: payload_idx = alt_idx
|
||||
else:
|
||||
# fail-fast: payload out of range and alias lookup failed.
|
||||
# (node is not available here; locate via sub_pat. Under TPC Python an undefined name errors only at runtime, but TPV lazily compiling the method body raises NameError immediately.)
|
||||
# (node is not available here; locate via sub_pat. Under TPC Python an undefined name errors only at runtime, but VPC lazily compiling the method body raises NameError immediately.)
|
||||
self.Gen.TypeGenErr.fatal_error(sub_pat, f"REnum variant field '{bind_name}' is out of range in struct '{renum_name}' and alias lookup failed", "TypeError")
|
||||
# Get the field type.
|
||||
fe: GenTypes.FieldEntry* = (self.Gen.StructGen._get_field_entry(renum_entry, payload_idx))
|
||||
@@ -716,7 +720,7 @@ novirtual class MatchGenMixin:
|
||||
if ecount == 0: return 0
|
||||
subj_vals: sizet* = pool.alloc(8 * ecount)
|
||||
if subj_vals is None: return 0
|
||||
for ei in range(ecount):
|
||||
for ei in range(int(ecount)):
|
||||
# Pre-declaring/reassigning elts misdispatches .get to list[int].get (i32 truncation); use an inline alias receiver (like Converter arg_defaults).
|
||||
elts_inl: list[ast.AST*]* = elts
|
||||
elt: ast.AST* = elts_inl.get(ei)
|
||||
@@ -827,7 +831,7 @@ novirtual class MatchGenMixin:
|
||||
# Element count mismatch -> always false (Python semantics).
|
||||
cond = mlirlite.const_int(pool, 1, 0)
|
||||
else:
|
||||
for pi in range(ecount):
|
||||
for pi in range(int(ecount)):
|
||||
# Pre-declaring/reassigning pats misdispatches .get to list[int].get (i32 truncation); use an inline alias receiver (like Converter arg_defaults).
|
||||
pats_inl: list[ast.AST*]* = pats
|
||||
pat1: ast.AST* = pats_inl.get(pi)
|
||||
|
||||
@@ -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
|
||||
@@ -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:
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import t, c
|
||||
from stdint import *
|
||||
import memhub
|
||||
import string
|
||||
@@ -329,7 +328,7 @@ novirtual class StructGenMixin:
|
||||
if self.FileClassNames is None: return
|
||||
cn: sizet = len(ch)
|
||||
ci: sizet
|
||||
for ci in range(cn):
|
||||
for ci in range(int(cn)):
|
||||
child: ast.AST* = ch.get(ci)
|
||||
if child is None: continue
|
||||
if child.kind() != ast.ASTKind.ClassDef: continue
|
||||
@@ -431,7 +430,7 @@ novirtual class StructGenMixin:
|
||||
self.EscIndCount = 0
|
||||
cn: sizet = len(ch)
|
||||
ci: sizet
|
||||
for ci in range(cn):
|
||||
for ci in range(int(cn)):
|
||||
child: ast.AST* = ch.get(ci)
|
||||
if child is None: continue
|
||||
if child.kind() != ast.ASTKind.FunctionDef: continue
|
||||
@@ -454,14 +453,14 @@ novirtual class StructGenMixin:
|
||||
|
||||
# M2.3 覆盖面扩展:类方法用 "Class.method" 键——此前方法与嵌套函数没有摘要,
|
||||
# 它们的"存对象字段 / 返回形参"在调用点不会被拦截。
|
||||
for ci2 in range(cn):
|
||||
for ci2 in range(int(cn)):
|
||||
child2: ast.AST* = ch.get(ci2)
|
||||
if child2 is None: continue
|
||||
if child2.kind() != ast.ASTKind.ClassDef: continue
|
||||
self._RegisterClassMethods(esc_pool, ast.ClassDef*(child2))
|
||||
|
||||
# 本文件顶层变量名集合(供"函数内无 global 写同名模块变量"判据兜底)。
|
||||
for ci3 in range(cn):
|
||||
for ci3 in range(int(cn)):
|
||||
child3: ast.AST* = ch.get(ci3)
|
||||
if child3 is None: continue
|
||||
kc3: int = child3.kind()
|
||||
@@ -478,7 +477,7 @@ novirtual class StructGenMixin:
|
||||
if self.FileVarNames is None: self.FileVarNames = hashtable.HashTable(pool)
|
||||
if self.FileVarNames is None: continue
|
||||
nt3: sizet = len(tlist3)
|
||||
for ti3 in range(nt3):
|
||||
for ti3 in range(int(nt3)):
|
||||
tn3: ast.AST* = tlist3.get(ti3)
|
||||
if tn3 is None or tn3.kind() != ast.ASTKind.Name: continue
|
||||
vnm3: ast.Name* = ast.Name*(tn3)
|
||||
@@ -1208,7 +1207,7 @@ novirtual class StructGenMixin:
|
||||
if cnt == 0: return -1
|
||||
i: sizet = 0
|
||||
while i < cnt:
|
||||
v: int = self._EvalConstCond(fd, bo.values.get(i), depth + 1)
|
||||
v: int = int(self._EvalConstCond(fd, bo.values.get(i), depth + 1))
|
||||
if v < 0: return -1
|
||||
# 短路:and 遇假、or 遇真即可定论,其后的项折不动也不影响结论
|
||||
if is_and != 0 and v == 0: return 0
|
||||
@@ -1236,7 +1235,7 @@ novirtual class StructGenMixin:
|
||||
if name is None or mask == 0 or cargs is None or can == 0: return mask
|
||||
fd: ast.FunctionDef* = self._EscFuncLookup(name)
|
||||
if fd is None: return mask
|
||||
return self._RefineWithConsts(pool, fd, param_base, cargs, can, mask)
|
||||
return self._RefineWithConsts(pool, fd, param_base, cargs, can, int(mask))
|
||||
|
||||
def refine_escape_mask_method(self, pool: memhub.MemBuddy*, class_name: str, method_name: str, cargs: list[ast.AST*]*, can: sizet, mask: int) -> int:
|
||||
"""跨过程常量特化(方法):键走 "Class.method",沿 MRO 祖先回退(口径同 get_param_escapes_method)"""
|
||||
@@ -1256,7 +1255,7 @@ novirtual class StructGenMixin:
|
||||
if fd is not None: break
|
||||
mi += 1
|
||||
if fd is None: return mask
|
||||
return self._RefineWithConsts(pool, fd, 1, cargs, can, mask)
|
||||
return self._RefineWithConsts(pool, fd, 1, cargs, can, int(mask))
|
||||
|
||||
def _EnsureRefineScratch(self) -> int:
|
||||
"""确保重算 scratch 就绪(**登记路径与 .vpi 落盘路径都会用到**——后者不走 register_file_funcs,
|
||||
@@ -1323,7 +1322,7 @@ novirtual class StructGenMixin:
|
||||
cnt: sizet = len(bo.values)
|
||||
i: sizet = 0
|
||||
while i < cnt and n < max:
|
||||
n = self._HarvestGuardTest(fd, bo.values.get(i), out, n, max)
|
||||
n = self._HarvestGuardTest(fd, bo.values.get(i), out, int(n), max)
|
||||
i += 1
|
||||
return n
|
||||
p: int = -1
|
||||
@@ -1355,8 +1354,8 @@ novirtual class StructGenMixin:
|
||||
if self._ConstOfNode(fd, cmp.left) != 0: kv = int64(self.EscRefineScratch[EC_EVAL_SLOT])
|
||||
else: p = -1
|
||||
if p < 0: return n
|
||||
n = self._AddGuardCand(out, n, max, p, kv)
|
||||
if add_false != 0: n = self._AddGuardCand(out, n, max, p, 0)
|
||||
n = self._AddGuardCand(out, int(n), max, p, kv)
|
||||
if add_false != 0: n = self._AddGuardCand(out, int(n), max, p, 0)
|
||||
return n
|
||||
|
||||
def _CollectGuardCandidates(self, fd: ast.FunctionDef*, stmts: list[ast.AST*]*, out: sizet*, max: int, depth: int) -> int:
|
||||
@@ -1372,12 +1371,12 @@ novirtual class StructGenMixin:
|
||||
k: int = st.kind()
|
||||
if k == ast.ASTKind.If:
|
||||
ifn: ast.If* = ast.If*(st)
|
||||
n = self._HarvestGuardTest(fd, ifn.test, out, n, max)
|
||||
n = self._HarvestGuardTest(fd, ifn.test, out, int(n), max)
|
||||
n = self._CollectGuardCandidates(fd, ifn.children, out, max, depth + 1)
|
||||
n = self._CollectGuardCandidates(fd, ifn.orelse, out, max, depth + 1)
|
||||
elif k == ast.ASTKind.While:
|
||||
whn: ast.While* = ast.While*(st)
|
||||
n = self._HarvestGuardTest(fd, whn.test, out, n, max)
|
||||
n = self._HarvestGuardTest(fd, whn.test, out, int(n), max)
|
||||
n = self._CollectGuardCandidates(fd, whn.children, out, max, depth + 1)
|
||||
n = self._CollectGuardCandidates(fd, whn.orelse, out, max, depth + 1)
|
||||
return n
|
||||
@@ -1416,7 +1415,7 @@ novirtual class StructGenMixin:
|
||||
env[p * 2] = 1
|
||||
env[p * 2 + 1] = sizet(kv)
|
||||
if self.compute_escape_summary(pool, fd, self.EscRefineScratch, box_b, env) == 0:
|
||||
cleared: int = base_mask & (~int(box_b[0]))
|
||||
cleared: int = int(base_mask & (~int(box_b[0])))
|
||||
pk: int = self._PackEscapeClear(p, kv, cleared)
|
||||
if pk >= 0:
|
||||
out_packed[n_out] = sizet(pk)
|
||||
@@ -1432,16 +1431,16 @@ novirtual class StructGenMixin:
|
||||
"""按调用点常量环境(EscConstEnv 区)套用跨模块守卫记录:命中即清位;返回精化后的掩码"""
|
||||
if packed is None or n <= 0 or mask == 0 or self.EscRefineScratch is None: return mask
|
||||
env: sizet* = sizet*(uint64(self.EscRefineScratch) + EC_ENV_SLOT * 8)
|
||||
res: int = mask
|
||||
res: int = int(mask)
|
||||
i: int = 0
|
||||
while i < n:
|
||||
v: int = int(packed[i])
|
||||
i += 1
|
||||
if v <= 0: continue
|
||||
p: int = v & 255
|
||||
kv: int = (v >> 8) & 255
|
||||
p: int = int(v & 255)
|
||||
kv: int = int((v >> 8) & 255)
|
||||
if kv >= 128: kv -= 256
|
||||
m: int = v >> 16
|
||||
m: int = int(v >> 16)
|
||||
if p >= 0 and p < EC_ENV_MAX and env[p * 2] != 0:
|
||||
if int64(env[p * 2 + 1]) == int64(kv): res = res & (~m)
|
||||
return res
|
||||
@@ -1508,7 +1507,7 @@ novirtual class StructGenMixin:
|
||||
h: sizet = self._NameHash(nm.id)
|
||||
i: int = 0
|
||||
while i < self.EscIndCount:
|
||||
base: int = EC_IND_SLOT + i * 4
|
||||
base: int = int(EC_IND_SLOT + i * 4)
|
||||
if self.EscRefineScratch[base] == h:
|
||||
self.EscRefineScratch[EC_IND_LO] = self.EscRefineScratch[base + 1]
|
||||
self.EscRefineScratch[EC_IND_HI] = self.EscRefineScratch[base + 2]
|
||||
@@ -1776,7 +1775,7 @@ novirtual class StructGenMixin:
|
||||
"""收集本函数(含嵌套块)`global` 声明的名字到定长数组:[0]=计数,[1..]=名字指针"""
|
||||
if stmts is None or gset is None or depth > 64: return 0
|
||||
n: sizet = len(stmts)
|
||||
for i in range(n):
|
||||
for i in range(int(n)):
|
||||
st: ast.AST* = stmts.get(i)
|
||||
if st is None: continue
|
||||
k: int = st.kind()
|
||||
@@ -1784,7 +1783,7 @@ novirtual class StructGenMixin:
|
||||
gn: ast.Global* = ast.Global*(st)
|
||||
if gn is not None and gn.names is not None:
|
||||
cg: sizet = len(gn.names)
|
||||
for j in range(cg):
|
||||
for j in range(int(cg)):
|
||||
nnd: ast.AST* = gn.names.get(j)
|
||||
if nnd is not None and nnd.kind() == ast.ASTKind.Name:
|
||||
nnm: ast.Name* = ast.Name*(nnd)
|
||||
@@ -1823,7 +1822,7 @@ novirtual class StructGenMixin:
|
||||
pargs: list[ast.AST*]* = agr.args
|
||||
if pargs is None: return -1
|
||||
cnt: sizet = len(pargs)
|
||||
for i in range(cnt):
|
||||
for i in range(int(cnt)):
|
||||
an: ast.AST* = pargs.get(i)
|
||||
if an is None: continue
|
||||
pa: ast.Arg* = ast.Arg*(an)
|
||||
@@ -1877,7 +1876,7 @@ novirtual class StructGenMixin:
|
||||
"""递归扫描:return p / G = p / obj.f = p;只进 if/while/for,不进嵌套 def/class"""
|
||||
if fd is None or stmts is None or box is None or depth > 64: return 0
|
||||
n: sizet = len(stmts)
|
||||
for i in range(n):
|
||||
for i in range(int(n)):
|
||||
st: ast.AST* = stmts.get(i)
|
||||
if st is None: continue
|
||||
k: int = st.kind()
|
||||
@@ -1906,7 +1905,7 @@ novirtual class StructGenMixin:
|
||||
asg: ast.Assign* = ast.Assign*(st)
|
||||
if asg.targets is not None:
|
||||
nt: sizet = len(asg.targets)
|
||||
for t in range(nt):
|
||||
for t in range(int(nt)):
|
||||
self._MarkEscapingStore(fd, asg.targets.get(t), asg.value, gset, box)
|
||||
elif k == ast.ASTKind.AnnAssign:
|
||||
aa: ast.AnnAssign* = ast.AnnAssign*(st)
|
||||
@@ -1967,7 +1966,7 @@ novirtual class StructGenMixin:
|
||||
if gmask == 0: return 0
|
||||
if cl.args is None: return 0
|
||||
na: sizet = len(cl.args)
|
||||
for j in range(na):
|
||||
for j in range(int(na)):
|
||||
if ((gmask >> int(j)) & 1) == 0: continue
|
||||
pi: int = self._ParamIndex(fd, cl.args.get(j))
|
||||
if pi >= 0:
|
||||
@@ -1998,7 +1997,7 @@ novirtual class StructGenMixin:
|
||||
mch: list[ast.AST*]* = cd.children
|
||||
if mch is None: return 0
|
||||
mn: sizet = len(mch)
|
||||
for mi in range(mn):
|
||||
for mi in range(int(mn)):
|
||||
m_c: ast.AST* = mch.get(mi)
|
||||
if m_c is None: continue
|
||||
if m_c.kind() != ast.ASTKind.FunctionDef: continue
|
||||
@@ -2014,7 +2013,7 @@ novirtual class StructGenMixin:
|
||||
"""登记 stmts 中的嵌套 def(裸名键,与调用点一致),递归进其体与 if/while/for 块"""
|
||||
if stmts is None or depth > 64: return 0
|
||||
n: sizet = len(stmts)
|
||||
for i in range(n):
|
||||
for i in range(int(n)):
|
||||
st: ast.AST* = stmts.get(i)
|
||||
if st is None: continue
|
||||
k: int = st.kind()
|
||||
@@ -2080,7 +2079,7 @@ novirtual class StructGenMixin:
|
||||
"""重扫 stmts 里的嵌套 def(裸名键),递归进其体与 if/while/for"""
|
||||
if stmts is None or depth > 64: return 0
|
||||
n: sizet = len(stmts)
|
||||
for i in range(n):
|
||||
for i in range(int(n)):
|
||||
st: ast.AST* = stmts.get(i)
|
||||
if st is None: continue
|
||||
k: int = st.kind()
|
||||
@@ -2109,7 +2108,7 @@ novirtual class StructGenMixin:
|
||||
mch: list[ast.AST*]* = cd.children
|
||||
if mch is None: return 0
|
||||
mn: sizet = len(mch)
|
||||
for mi in range(mn):
|
||||
for mi in range(int(mn)):
|
||||
m_c: ast.AST* = mch.get(mi)
|
||||
if m_c is None: continue
|
||||
if m_c.kind() != ast.ASTKind.FunctionDef: continue
|
||||
@@ -2126,7 +2125,7 @@ novirtual class StructGenMixin:
|
||||
ch: list[ast.AST*]* = tree.children
|
||||
if ch is None: return 1
|
||||
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
|
||||
kc: int = child.kind()
|
||||
@@ -2197,13 +2196,13 @@ novirtual class StructGenMixin:
|
||||
# Reduces find_struct/find_struct_by_module (name) and find_struct_by_type (type pointer) from O(n) to expected O(1); conflicts compare keys (strcmp for names, Ty pointer value for types). Trusted only when StructIdxBuilt == StructCount; otherwise fall back to a linear scan. Neutralized entries (Name[0]=0 / Ty=None) leave stale keys that simply miss on probe.
|
||||
def _struct_entry_name(self, i: int) -> str:
|
||||
"""Return entry i's Name for index probing, or None when out of range or empty."""
|
||||
e: GenTypes.StructEntry* = self._get_struct_entry(i)
|
||||
e: GenTypes.StructEntry* = self._get_struct_entry(int(i))
|
||||
if e is None: return None
|
||||
return e.Name
|
||||
|
||||
def _struct_entry_tyval(self, i: int) -> uint64:
|
||||
"""Return entry i's Ty pointer value, or 0 when Ty is None."""
|
||||
e: GenTypes.StructEntry* = self._get_struct_entry(i)
|
||||
e: GenTypes.StructEntry* = self._get_struct_entry(int(i))
|
||||
if e is None or e.Ty is None: return 0
|
||||
return uint64(e.Ty)
|
||||
|
||||
@@ -2214,7 +2213,7 @@ novirtual class StructGenMixin:
|
||||
h: uint64 = _struct_name_hash(name)
|
||||
mask: sizet = sizet(self.StructIdxCap - 1)
|
||||
idx: sizet = sizet(h) & mask
|
||||
for i in range(self.StructIdxCap):
|
||||
for i in range(int(self.StructIdxCap)):
|
||||
slot: sizet* = (sizet*)(uint64(base) + uint64(idx * 8))
|
||||
if slot[0] == 0:
|
||||
slot[0] = sizet(ei + 1)
|
||||
@@ -2229,7 +2228,7 @@ novirtual class StructGenMixin:
|
||||
h: uint64 = _struct_ty_hash(tyval)
|
||||
mask: sizet = sizet(self.StructIdxCap - 1)
|
||||
idx: sizet = sizet(h) & mask
|
||||
for i in range(self.StructIdxCap):
|
||||
for i in range(int(self.StructIdxCap)):
|
||||
slot: sizet* = (sizet*)(uint64(base) + uint64(idx * 8))
|
||||
if slot[0] == 0:
|
||||
slot[0] = sizet(ei + 1)
|
||||
@@ -2259,7 +2258,7 @@ novirtual class StructGenMixin:
|
||||
h: uint64 = _struct_name_hash(name)
|
||||
mask: sizet = sizet(self.StructIdxCap - 1)
|
||||
idx: sizet = sizet(h) & mask
|
||||
for i in range(self.StructIdxCap):
|
||||
for i in range(int(self.StructIdxCap)):
|
||||
slot: sizet* = (sizet*)(uint64(base) + uint64(idx * 8))
|
||||
sv: sizet = slot[0]
|
||||
if sv == 0: return -1
|
||||
@@ -2277,7 +2276,7 @@ novirtual class StructGenMixin:
|
||||
h: uint64 = _struct_ty_hash(tyval)
|
||||
mask: sizet = sizet(self.StructIdxCap - 1)
|
||||
idx: sizet = sizet(h) & mask
|
||||
for i in range(self.StructIdxCap):
|
||||
for i in range(int(self.StructIdxCap)):
|
||||
slot: sizet* = (sizet*)(uint64(base) + uint64(idx * 8))
|
||||
sv: sizet = slot[0]
|
||||
if sv == 0: return -1
|
||||
@@ -2451,7 +2450,7 @@ novirtual class StructGenMixin:
|
||||
VLogger.error(f"field table full for {struct_entry.Name}", "STRUCT")
|
||||
return -1
|
||||
idx: int = struct_entry.FieldCount
|
||||
fe: GenTypes.FieldEntry* = self._get_field_entry(struct_entry, idx)
|
||||
fe: GenTypes.FieldEntry* = self._get_field_entry(struct_entry, int(idx))
|
||||
if fe is None: return -1
|
||||
# Copy the field name (StdlibAlloc=1 uses the perm pool to survive reset); otherwise lookup_field's strcmp(fe.Name, ...) reads dangling out-of-bounds memory.
|
||||
fname_pool: memhub.MemBuddy* = pool
|
||||
@@ -2493,9 +2492,25 @@ novirtual class StructGenMixin:
|
||||
|
||||
# Explicitly zero the const flag (not relying on the entry's zero-init): ClassGenDef sets it to 1 only for a confirmed const object, so explicit zeroing prevents a false "uninitialized read as 1".
|
||||
fe.IsConst = 0
|
||||
# Byte order is filled in by ClassGenDef from the field annotation (0 = native here).
|
||||
fe.ByteOrder = 0
|
||||
struct_entry.FieldCount = idx + 1
|
||||
return idx
|
||||
|
||||
def _copy_ast_vtable(self, src: ast.AST*, dst: ast.AST*) -> None:
|
||||
"""Copy the vtable word (field 0) from one AST node to another.
|
||||
|
||||
AST classes are @t.CVTable: virtual calls such as kind() dispatch through the first
|
||||
field. Both nodes are viewed as a single uint64 slot, which reads better than assigning
|
||||
a pointer to a struct-typed lvalue (TPC rejects that implicit pointer -> struct value
|
||||
conversion; it also once emitted no load/store at all, leaving vptr=0 and crashing
|
||||
kind() with `load i8*, i8** [0 + 0]`).
|
||||
"""
|
||||
if src is None or dst is None: return
|
||||
src_vt: uint64* = (uint64*)(src)
|
||||
dst_vt: uint64* = (uint64*)(dst)
|
||||
dst_vt[0] = src_vt[0]
|
||||
|
||||
def _deep_copy_default_ast(self, pool: memhub.MemBuddy*, default_val: ast.AST*) -> ast.AST*:
|
||||
"""Deep-copy a field default AST (constants and lists) into the perm pool."""
|
||||
if default_val is None: return None
|
||||
@@ -2505,10 +2520,8 @@ novirtual class StructGenMixin:
|
||||
dst_c: ast.Constant* = ast.Constant*(pool.alloc_tagged(ast.Constant.__sizeof__(), memhub.DETSITE_STRUCTGENMIXIN_641))
|
||||
if dst_c is None: return None
|
||||
string.memset(dst_c, 0, ast.Constant.__sizeof__())
|
||||
# Copy the vtable pointer (AST is @t.CVTable; kind() virtual calls depend on the first-field vptr); must match _deep_copy_list_ast (c.Deref read + c.DerefAs write).
|
||||
# The old form silently failed under TPC: no load-vptr instruction and no store emitted, leaving vptr=0, so a later node.kind() reads `load i8*, i8** [0 + 0]` -> ASan READ at 0x0. Evidence: TransPyV\output\b5883dc9c114a604.ll:10382-10384.
|
||||
vt_ptr_c: t.CPtr = t.CPtr(*((uint64*)(default_val)))
|
||||
c.DerefAs(dst_c, vt_ptr_c)
|
||||
# Copy the vtable pointer (AST is @t.CVTable; virtual kind() dispatches through field 0).
|
||||
self._copy_ast_vtable(default_val, dst_c)
|
||||
# Copy position fields (for error location).
|
||||
dst_c.lineno = src_c.lineno
|
||||
dst_c.col_offset = src_c.col_offset
|
||||
@@ -2532,9 +2545,8 @@ novirtual class StructGenMixin:
|
||||
dst_l: ast.List* = ast.List*(pool.alloc(ast.List.__sizeof__()))
|
||||
if dst_l is None: return None
|
||||
string.memset(dst_l, 0, ast.List.__sizeof__())
|
||||
# Copy the vtable pointer (AST is @t.CVTable; kind() virtual calls depend on the first-field vptr).
|
||||
vt_l: t.CPtr = t.CPtr(*((uint64*)(default_val)))
|
||||
c.DerefAs(dst_l, vt_l)
|
||||
# Copy the vtable pointer (same requirement as the Constant copy above).
|
||||
self._copy_ast_vtable(default_val, dst_l)
|
||||
# Copy position fields and ctx.
|
||||
dst_l.lineno = src_l.lineno
|
||||
dst_l.col_offset = src_l.col_offset
|
||||
@@ -2545,7 +2557,7 @@ novirtual class StructGenMixin:
|
||||
src_elts_l: list[ast.AST*]* = src_l.elts
|
||||
if src_elts_l is not None:
|
||||
cnt_l: sizet = len(src_elts_l)
|
||||
lst_copy_l: list[ast.AST*]* = (list[ast.AST*] | t.CPtr)(pool.alloc_tagged(list[ast.AST*].__sizeof__(), memhub.DETSITE_STRUCTGENMIXIN_693))
|
||||
lst_copy_l: list[ast.AST*]* = list[ast.AST*]*(pool.alloc_tagged(list[ast.AST*].__sizeof__(), memhub.DETSITE_STRUCTGENMIXIN_693))
|
||||
if lst_copy_l is not None:
|
||||
string.memset(lst_copy_l, 0, list[ast.AST*].__sizeof__())
|
||||
# The count must never be fixed from the source count up front; it must be settled by the actual written slot count (written_l below).
|
||||
@@ -2555,13 +2567,13 @@ novirtual class StructGenMixin:
|
||||
elem_sz_l: sizet = 8
|
||||
lst_copy_l.__elem_size__ = elem_sz_l
|
||||
lst_copy_l.__pool__ = None
|
||||
data_l: t.CPtr = pool.alloc_tagged(cnt_l * elem_sz_l, memhub.DETSITE_STRUCTGENMIXIN_698)
|
||||
data_l: rawptr = pool.alloc_tagged(cnt_l * elem_sz_l, memhub.DETSITE_STRUCTGENMIXIN_698)
|
||||
if data_l is not None:
|
||||
string.memset(data_l, 0, cnt_l * elem_sz_l)
|
||||
lst_copy_l.__data__ = data_l
|
||||
written_l: sizet = 0
|
||||
li_l: sizet
|
||||
for li_l in range(cnt_l):
|
||||
for li_l in range(int(cnt_l)):
|
||||
elem_src_l: ast.AST* = src_elts_l.get(li_l)
|
||||
# Explicit null check (repo hard rule: null-check pointers with `is None`). An earlier revision compared `elem_src_l == 0` and noted it was equivalent to `is None`: the IR fact was right but the conclusion misleading.
|
||||
# A null element in the source list means the producer is corrupted (count > actual fill) -> fail loudly, never backfill 0.
|
||||
@@ -2601,8 +2613,8 @@ novirtual class StructGenMixin:
|
||||
def find_struct_entry_with_ty(self, name: str, alt_name: str) -> GenTypes.StructEntry*:
|
||||
"""Find the first struct entry with a valid Ty by name or alt name, else None."""
|
||||
if self.StructTable is None: return None
|
||||
for i in range(self.StructCount):
|
||||
entry: GenTypes.StructEntry* = self._get_struct_entry(i)
|
||||
for i in range(int(self.StructCount)):
|
||||
entry: GenTypes.StructEntry* = self._get_struct_entry(int(i))
|
||||
if entry is None or entry.Name is None or entry.Ty is None: continue
|
||||
if entry.Name[0] == '\0': continue
|
||||
if string.strcmp(entry.Name, name) == 0: return entry
|
||||
@@ -2625,8 +2637,8 @@ novirtual class StructGenMixin:
|
||||
name_idx_trusted: int = 0
|
||||
if self.StructNameIdx is not None and self.StructIdxCap > 0 and self.StructIdxBuilt == self.StructCount: name_idx_trusted = 1
|
||||
if name_idx_trusted == 0 or name[0] == '\0':
|
||||
for i in range(self.StructCount):
|
||||
entry: GenTypes.StructEntry* = self._get_struct_entry(i)
|
||||
for i in range(int(self.StructCount)):
|
||||
entry: GenTypes.StructEntry* = self._get_struct_entry(int(i))
|
||||
if entry is not None and entry.Name is not None:
|
||||
if string.strcmp(entry.Name, name) == 0: return entry
|
||||
# Module-qualified name (module.Class): when the module resolves, match by module ownership exactly, to stop the bare-name fallback from grabbing a cross-module same-named class; if the module misses but the bare name is unique, fall back.
|
||||
@@ -2657,7 +2669,7 @@ novirtual class StructGenMixin:
|
||||
# Module class not registered: count same bare-named classes, fall back when unique (no conflict).
|
||||
same_cnt_f: int = 0
|
||||
same_entry_f: GenTypes.StructEntry* = None
|
||||
for i2 in range(self.StructCount):
|
||||
for i2 in range(int(self.StructCount)):
|
||||
e2: GenTypes.StructEntry* = self._get_struct_entry(i2)
|
||||
if e2 is not None and e2.Name is not None:
|
||||
if string.strcmp(e2.Name, bare_f) == 0:
|
||||
@@ -2676,8 +2688,8 @@ novirtual class StructGenMixin:
|
||||
# Otherwise an unresolvable module segment is hijacked to the wrong module and the return type resolves against the wrong text.ll.
|
||||
same_cnt_b: int = 0
|
||||
same_entry_b: GenTypes.StructEntry* = None
|
||||
for i in range(self.StructCount):
|
||||
entry: GenTypes.StructEntry* = self._get_struct_entry(i)
|
||||
for i in range(int(self.StructCount)):
|
||||
entry: GenTypes.StructEntry* = self._get_struct_entry(int(i))
|
||||
if entry is not None and entry.Name is not None:
|
||||
if string.strcmp(entry.Name, class_name) == 0:
|
||||
same_cnt_b += 1
|
||||
@@ -2698,8 +2710,8 @@ novirtual class StructGenMixin:
|
||||
start: int = 0
|
||||
fri: int = self._struct_name_idx_find(name)
|
||||
if fri >= 0: start = fri
|
||||
for i in range(start, self.StructCount):
|
||||
entry: GenTypes.StructEntry* = self._get_struct_entry(i)
|
||||
for i in range(int(start), int(self.StructCount)):
|
||||
entry: GenTypes.StructEntry* = self._get_struct_entry(int(i))
|
||||
if entry is not None and entry.Name is not None:
|
||||
# Fast path: strcmp equality implies equal first chars, so skip strcmp for differing first chars.
|
||||
if entry.Name[0] == name[0] and string.strcmp(entry.Name, name) == 0:
|
||||
@@ -2718,8 +2730,8 @@ novirtual class StructGenMixin:
|
||||
VLogger.error(f"abnormal StructCount: {self.StructCount} (reset_module_methods_emitted)", "STRUCT")
|
||||
return 0
|
||||
reset_count: int = 0
|
||||
for i in range(self.StructCount):
|
||||
entry: GenTypes.StructEntry* = self._get_struct_entry(i)
|
||||
for i in range(int(self.StructCount)):
|
||||
entry: GenTypes.StructEntry* = self._get_struct_entry(int(i))
|
||||
if entry is not None:
|
||||
# Normal classes match by ModuleSha1.
|
||||
if entry.ModuleSha1 is not None:
|
||||
@@ -2744,8 +2756,8 @@ novirtual class StructGenMixin:
|
||||
st_val: uint64 = uint64(struct_ty)
|
||||
# The Ty index is entirely disabled here (its structure is still maintained by _struct_idx_add but not used): an entry's Ty may be rewritten to a new pointer by deep copy (ClassGenDef), leaving the slot keyed by the registration-time Ty value out of sync.
|
||||
# So an index hit may not be the original scan's "first entry with equal Ty"; restore the original linear exact scan (hit/miss behavior byte-identical to before the index).
|
||||
for i in range(self.StructCount):
|
||||
entry: GenTypes.StructEntry* = self._get_struct_entry(i)
|
||||
for i in range(int(self.StructCount)):
|
||||
entry: GenTypes.StructEntry* = self._get_struct_entry(int(i))
|
||||
if entry is not None and entry.Ty is not None:
|
||||
if uint64(entry.Ty) == st_val: return entry
|
||||
return None
|
||||
@@ -2777,7 +2789,7 @@ novirtual class StructGenMixin:
|
||||
node: mlirlite.ParamNode* = mlirlite.get_struct_fields(struct_ty)
|
||||
i: int = 0
|
||||
while i < entry.FieldCount and node is not None:
|
||||
fe: GenTypes.FieldEntry* = self._get_field_entry(entry, i)
|
||||
fe: GenTypes.FieldEntry* = self._get_field_entry(entry, int(i))
|
||||
slot_ty: mlirlite.LLVMType* = mlirlite.paramnode_get_ty(node)
|
||||
if fe is not None and fe.Name is not None and fe.Ty is not None and slot_ty is not None:
|
||||
ptr_f: int = self.Gen.ExprGenBase.is_ptr_type(fe.Ty)
|
||||
@@ -2806,7 +2818,7 @@ novirtual class StructGenMixin:
|
||||
if lk_entry is not None:
|
||||
# Field-table vs struct-slot consistency guard (fail-fast): reject a mismatch (see verify_field_slot_consistency header; real crash: ast.Name.id -> slot 9).
|
||||
self.verify_field_slot_consistency(struct_ty, lk_entry)
|
||||
for fi in range(lk_entry.FieldCount):
|
||||
for fi in range(int(lk_entry.FieldCount)):
|
||||
fe: GenTypes.FieldEntry* = self._get_field_entry(lk_entry, fi)
|
||||
if fe is not None and fe.Name is not None:
|
||||
if string.strcmp(fe.Name, field_name) == 0: return fe
|
||||
@@ -2819,8 +2831,8 @@ novirtual class StructGenMixin:
|
||||
# strncmp allocates no temporary buffer; the prefix test goes through the single source GenApi.sha1_prefix_len.
|
||||
ty_has_sha1_lf: int = 0
|
||||
if GenApi.sha1_prefix_len(ty_name) != 0: ty_has_sha1_lf = 1
|
||||
for i in range(self.StructCount):
|
||||
entry: GenTypes.StructEntry* = self._get_struct_entry(i)
|
||||
for i in range(int(self.StructCount)):
|
||||
entry: GenTypes.StructEntry* = self._get_struct_entry(int(i))
|
||||
if entry is not None and entry.Name is not None:
|
||||
# Type name with a SHA1 prefix must match the first 16 chars of entry.ModuleSha1 exactly, else skip (cross-module same-named class namespace isolation).
|
||||
if ty_has_sha1_lf != 0:
|
||||
@@ -2830,7 +2842,7 @@ novirtual class StructGenMixin:
|
||||
elif string.strstr(ty_name, entry.Name) is not None: matched = 1
|
||||
if matched != 0:
|
||||
# Look up the field.
|
||||
for fi in range(entry.FieldCount):
|
||||
for fi in range(int(entry.FieldCount)):
|
||||
fe: GenTypes.FieldEntry* = self._get_field_entry(entry, fi)
|
||||
if fe is not None and fe.Name is not None:
|
||||
if string.strcmp(fe.Name, field_name) == 0: return fe
|
||||
@@ -2866,8 +2878,8 @@ novirtual class StructGenMixin:
|
||||
if dot_pos_lfbc is not None:
|
||||
bare_name = dot_pos_lfbc + 1
|
||||
if bare_name is None or bare_name[0] == '\0': bare_name = class_name
|
||||
for i in range(self.StructCount):
|
||||
entry: GenTypes.StructEntry* = self._get_struct_entry(i)
|
||||
for i in range(int(self.StructCount)):
|
||||
entry: GenTypes.StructEntry* = self._get_struct_entry(int(i))
|
||||
if entry is not None and entry.Name is not None:
|
||||
# Exact match or class-name fallback match.
|
||||
name_match: int = 0
|
||||
@@ -2877,7 +2889,7 @@ novirtual class StructGenMixin:
|
||||
if entry.ModuleSha1 is not None and string.strcmp(entry.ModuleSha1, sha1) == 0:
|
||||
sha1_matched = 1
|
||||
# SHA1 + class name matched; look up the field.
|
||||
for fi in range(entry.FieldCount):
|
||||
for fi in range(int(entry.FieldCount)):
|
||||
fe: GenTypes.FieldEntry* = self._get_field_entry(entry, fi)
|
||||
if fe is not None and fe.Name is not None and string.strcmp(fe.Name, field_name) == 0: return fe
|
||||
return None
|
||||
@@ -2909,7 +2921,7 @@ novirtual class StructGenMixin:
|
||||
if mod_entry_q.FieldCount == 0:
|
||||
stdlib.free(mod_buf_q)
|
||||
return None
|
||||
for fi_q in range(mod_entry_q.FieldCount):
|
||||
for fi_q in range(int(mod_entry_q.FieldCount)):
|
||||
fe_q: GenTypes.FieldEntry* = self._get_field_entry(mod_entry_q, fi_q)
|
||||
if fe_q is not None and fe_q.Name is not None:
|
||||
if string.strcmp(fe_q.Name, field_name) == 0:
|
||||
@@ -2954,7 +2966,7 @@ novirtual class StructGenMixin:
|
||||
# Return None so the caller marks _deps_pending and retranslates (the real definition overrides the stub once the source module compiles). SECURITY_ATTRIBUTES FieldCount=0 root cause (subprocess.vp sa.nLength).
|
||||
if entry.FieldCount == 0 and sha1 is not None: return None
|
||||
# Look up the field first (return before diagnostics/error, else the error exits immediately and the lookup always fails).
|
||||
for fi in range(entry.FieldCount):
|
||||
for fi in range(int(entry.FieldCount)):
|
||||
fe: GenTypes.FieldEntry* = self._get_field_entry(entry, fi)
|
||||
if fe is not None and fe.Name is not None:
|
||||
if string.strcmp(fe.Name, field_name) == 0: return fe
|
||||
@@ -2979,7 +2991,7 @@ novirtual class StructGenMixin:
|
||||
fb_flds_lst: str = stdlib.malloc(1024)
|
||||
if fb_flds_lst is not None:
|
||||
fb_flds_lst[0] = '\0'
|
||||
for fi in range(entry.FieldCount):
|
||||
for fi in range(int(entry.FieldCount)):
|
||||
fe: GenTypes.FieldEntry* = self._get_field_entry(entry, fi)
|
||||
if fe is not None and fe.Name is not None:
|
||||
if string.strlen(fb_flds_lst) > 0 and string.strlen(fb_flds_lst) + 3 < 1024: string.strcat(fb_flds_lst, ", ")
|
||||
@@ -3015,14 +3027,14 @@ novirtual class StructGenMixin:
|
||||
entry: GenTypes.StructEntry* = self.find_struct(class_name)
|
||||
if entry is None: return None
|
||||
if idx < 0 or idx >= entry.FieldCount: return None
|
||||
return self._get_field_entry(entry, idx)
|
||||
return self._get_field_entry(entry, int(idx))
|
||||
|
||||
def struct_has_defaults(self, class_name: str) -> int:
|
||||
"""Return 1 when the struct has any field with a default value."""
|
||||
if class_name is None: return 0
|
||||
entry: GenTypes.StructEntry* = self.find_struct(class_name)
|
||||
if entry is None: return 0
|
||||
for fi in range(entry.FieldCount):
|
||||
for fi in range(int(entry.FieldCount)):
|
||||
fe: GenTypes.FieldEntry* = self._get_field_entry(entry, fi)
|
||||
if fe is not None and fe.DefaultVal is not None: return 1
|
||||
return 0
|
||||
@@ -3065,8 +3077,8 @@ novirtual class StructGenMixin:
|
||||
if struct_ty is None or self.StructTable is None: return 0
|
||||
ty_name: str = self._extract_struct_name(struct_ty)
|
||||
st_iu: uint64 = uint64(struct_ty)
|
||||
for i in range(self.StructCount):
|
||||
entry: GenTypes.StructEntry* = self._get_struct_entry(i)
|
||||
for i in range(int(self.StructCount)):
|
||||
entry: GenTypes.StructEntry* = self._get_struct_entry(int(i))
|
||||
if entry is not None and entry.Ty is not None:
|
||||
matched: int = 0
|
||||
if uint64(entry.Ty) == st_iu: matched = 1
|
||||
@@ -3130,8 +3142,8 @@ novirtual class StructGenMixin:
|
||||
# Fallback: when the type pointer comparison fails, iterate all registered structs matching by name; extract struct_ty's Name field (may be "SHA1.ClassName").
|
||||
ty_name: str = self._extract_struct_name(struct_ty)
|
||||
if ty_name is not None:
|
||||
for j in range(self.StructCount):
|
||||
entry2: GenTypes.StructEntry* = self._get_struct_entry(j)
|
||||
for j in range(int(self.StructCount)):
|
||||
entry2: GenTypes.StructEntry* = self._get_struct_entry(int(j))
|
||||
if entry2 is not None and entry2.Name is not None:
|
||||
# Build the "SHA1.ClassName" form for comparison.
|
||||
entry2_sha1: str = entry2.ModuleSha1
|
||||
@@ -3150,7 +3162,7 @@ novirtual class StructGenMixin:
|
||||
if _tlen_cn > _nlen_cn and ty_name[_tlen_cn - _nlen_cn - 1] == '.':
|
||||
_tail_ok_cn: int = 1
|
||||
_ti_cn: sizet
|
||||
for _ti_cn in range(_nlen_cn):
|
||||
for _ti_cn in range(int(_nlen_cn)):
|
||||
if ty_name[_tlen_cn - _nlen_cn + _ti_cn] != entry2.Name[_ti_cn]:
|
||||
_tail_ok_cn = 0
|
||||
break
|
||||
@@ -3220,7 +3232,7 @@ novirtual class StructGenMixin:
|
||||
if pn_pool is None: pn_pool = pool
|
||||
arr: sizet* = pn_pool.alloc(8 * count)
|
||||
if arr is None: return 0
|
||||
for i in range(count):
|
||||
for i in range(int(count)):
|
||||
addr: sizet = names_ptr[i]
|
||||
arr[i] = 0
|
||||
if addr != 0:
|
||||
@@ -3275,7 +3287,7 @@ novirtual class StructGenMixin:
|
||||
if cur is None: continue
|
||||
# Visited check (linear search).
|
||||
dup: int = 0
|
||||
for vi in range(visit_count):
|
||||
for vi in range(int(visit_count)):
|
||||
va: sizet = visited[vi]
|
||||
if va != 0:
|
||||
vn: str = rawptr(va)
|
||||
@@ -3295,7 +3307,7 @@ novirtual class StructGenMixin:
|
||||
if entry is not None and entry.ParentCount > 0 and entry.ParentNames is not None:
|
||||
pc: int = entry.ParentCount
|
||||
parr: sizet* = (sizet*)(entry.ParentNames)
|
||||
for pi2 in range(pc):
|
||||
for pi2 in range(int(pc)):
|
||||
pi: int = pc - 1 - pi2
|
||||
if stack_count < MRO_MAX:
|
||||
stack[stack_count] = parr[pi]
|
||||
@@ -3327,7 +3339,7 @@ novirtual class StructGenMixin:
|
||||
if entry is None or entry.VTableMethods is None: return -1
|
||||
# VTableMethods is declared char* but actually stores a sizet array; it must be cast to sizet* to read the 8-byte pointer values correctly.
|
||||
methods_arr: sizet* = (sizet*)(entry.VTableMethods)
|
||||
for i in range(entry.VTableMethodCount):
|
||||
for i in range(int(entry.VTableMethodCount)):
|
||||
mname_addr: sizet = methods_arr[i]
|
||||
if mname_addr == 0: continue
|
||||
mname: str = rawptr(mname_addr)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import lib.core.Gen.GenApi as GenApi
|
||||
import lib.core.Gen.GenTypes as GenTypes
|
||||
import t, c
|
||||
from stdint import *
|
||||
import ast
|
||||
import mlirlite
|
||||
@@ -34,7 +33,7 @@ define MAX_FUNCS = 1024
|
||||
# Reference convention: cross-module t.CDefine is readable only inside functions, so write
|
||||
# qualified names there; the global/nonlocal name-table capacity must match on both guard sides.
|
||||
define MAX_GLOBAL_NAMES = 128
|
||||
# Coroutine table capacity (TPV-M1): async function count / coroutine var and local-name count
|
||||
# Coroutine table capacity (VPC-M1): async function count / coroutine var and local-name count
|
||||
define MAX_CORO_FUNCS = 256
|
||||
define MAX_CORO_VARS = 256
|
||||
define MAX_CORO_TRY = 16
|
||||
@@ -58,7 +57,7 @@ novirtual class Translator:
|
||||
_funcs: GenTypes.FuncEntry*
|
||||
_func_count: int
|
||||
|
||||
# Coroutines (TPV-M1 async def / await / coro.coro_run):
|
||||
# Coroutines (VPC-M1 async def / await / coro.coro_run):
|
||||
# tables for async function names, var->async mapping, locals, and async->frame types.
|
||||
_async_funcs: str
|
||||
_async_func_count: int
|
||||
@@ -204,7 +203,7 @@ novirtual class Translator:
|
||||
_lambda_count: int
|
||||
# Set of nonlocal variables across the function chain (collected by pre-scan).
|
||||
# The outermost function allocates their heaps and stores addresses in @nlheap.{sha1}.{name}.
|
||||
_nl_chain_names: t.CPtr
|
||||
_nl_chain_names: rawptr
|
||||
_nl_chain_count: int
|
||||
# Chain-nonlocal heap built flag (built once per function): reset to 0 on entering a function,
|
||||
# set to 1 by _prepare_chain_nonlocals. Shared by nested definitions to avoid double malloc.
|
||||
@@ -336,7 +335,7 @@ novirtual class Translator:
|
||||
self._nonlocal_types = stdlib.malloc(MAX_GLOBAL_NAMES * 8)
|
||||
if self._nonlocal_types is not None: string.memset(self._nonlocal_types, 0, MAX_GLOBAL_NAMES * 8)
|
||||
|
||||
# Coroutine tables (TPV-M1): async names / var->async map / locals / frame types
|
||||
# Coroutine tables (VPC-M1): async names / var->async map / locals / frame types
|
||||
if self._async_funcs is None:
|
||||
self._async_funcs = stdlib.malloc(MAX_CORO_FUNCS * 8)
|
||||
if self._async_funcs is not None: string.memset(self._async_funcs, 0, MAX_CORO_FUNCS * 8)
|
||||
@@ -419,7 +418,7 @@ novirtual class Translator:
|
||||
# 复用/回绕,覆盖早期分配 → 名字悬空(现场:@malloc 渲染成乱码)。
|
||||
# **但本函数跑得非常早**(每个模块翻译的开头),此时 CompilerFactory.factory 或它的
|
||||
# PermPool 可能尚未就绪:直接 `factory.PermPool.alloc(...)` 会解引用空指针
|
||||
# (现场:TransPyV.exe 编译任何工程都 access-violation on address 0x0 —— PermPool 是
|
||||
# (现场:viperc.exe 编译任何工程都 access-violation on address 0x0 —— PermPool 是
|
||||
# factory 的首字段,偏移 0)。故逐级判空并回退到模块池。
|
||||
decl_pool: memhub.MemBuddy* = None
|
||||
if CompilerFactory.factory is not None: decl_pool = CompilerFactory.factory.PermPool
|
||||
@@ -459,7 +458,7 @@ novirtual class Translator:
|
||||
ch: list[ast.AST*]* = tree.children
|
||||
if ch is not None:
|
||||
cn_count: sizet = len(ch)
|
||||
for ci in range(cn_count):
|
||||
for ci in range(int(cn_count)):
|
||||
child: ast.AST* = ch.get(ci)
|
||||
if child is not None and child.kind() == ast.ASTKind.FunctionDef:
|
||||
fd: ast.FunctionDef* = ast.FunctionDef*(child)
|
||||
@@ -512,7 +511,7 @@ novirtual class Translator:
|
||||
ch: list[ast.AST*]* = tree.children
|
||||
if ch is not None:
|
||||
cn_count: sizet = len(ch)
|
||||
for ci in range(cn_count):
|
||||
for ci in range(int(cn_count)):
|
||||
child: ast.AST* = ch.get(ci)
|
||||
if child is None: continue
|
||||
kd: int = child.kind()
|
||||
@@ -542,7 +541,7 @@ novirtual class Translator:
|
||||
# Pre-register module-level variables (AnnAssign/Assign) into SymTab to fix ordering: a class
|
||||
# may reference a module variable declared later in the AST. translate_children later skips existing ones.
|
||||
if ch is not None:
|
||||
for ci in range(cn_count):
|
||||
for ci in range(int(cn_count)):
|
||||
child: ast.AST* = ch.get(ci)
|
||||
if child is None: continue
|
||||
kd_mv: int = child.kind()
|
||||
@@ -557,7 +556,7 @@ novirtual class Translator:
|
||||
"""Return 1 if name is a registered Project macro variable."""
|
||||
if name is None or proj_vars is None: return 0
|
||||
n: sizet = len(proj_vars)
|
||||
for i in range(n):
|
||||
for i in range(int(n)):
|
||||
s: str = proj_vars.get(i)
|
||||
if s is not None and string.strcmp(s, name) == 0: return 1
|
||||
return 0
|
||||
@@ -657,7 +656,7 @@ novirtual class Translator:
|
||||
if kd == ast.ASTKind.Import:
|
||||
names: list[ast.AST*]* = ast.Import*(child).names
|
||||
if names is not None:
|
||||
for ni in range(len(names)):
|
||||
for ni in range(int(len(names))):
|
||||
al: ast.Alias* = ast.Alias*(names.get(ni))
|
||||
if al is not None and al.name is not None:
|
||||
if string.strcmp(al.name, "viperbuild") == 0:
|
||||
@@ -667,7 +666,7 @@ novirtual class Translator:
|
||||
ifr: ast.ImportFrom* = ast.ImportFrom*(child)
|
||||
if ifr is not None and ifr.level == 0 and ifr.names is not None:
|
||||
if ifr.module is not None and string.strcmp(ifr.module, "viperbuild") == 0:
|
||||
for ni in range(len(ifr.names)):
|
||||
for ni in range(int(len(ifr.names))):
|
||||
al: ast.Alias* = ast.Alias*(ifr.names.get(ni))
|
||||
if al is not None and al.name is not None:
|
||||
if string.strcmp(al.name, "Project") == 0:
|
||||
@@ -680,7 +679,7 @@ novirtual class Translator:
|
||||
kept: list[ast.AST*]* = list[ast.AST*](pool, 8)
|
||||
if proj_vars is None or kept is None: return 0
|
||||
erased: int = 0
|
||||
for ci in range(cn):
|
||||
for ci in range(int(cn)):
|
||||
child = ch.get(ci)
|
||||
if child is not None:
|
||||
if self._vb_is_macro_stmt(child, alias, ctor, proj_vars) != 0: erased = 1
|
||||
@@ -694,8 +693,8 @@ novirtual class Translator:
|
||||
"""Detect list/dict container usage and inject container-class visibility."""
|
||||
if tree is None or tree.children is None: return
|
||||
# Use stack buffers to pass detection results (4-byte int pointers across helper calls)
|
||||
need_list_box: t.CPtr = stdlib.malloc(4)
|
||||
need_dict_box: t.CPtr = stdlib.malloc(4)
|
||||
need_list_box: rawptr = stdlib.malloc(4)
|
||||
need_dict_box: rawptr = stdlib.malloc(4)
|
||||
if need_list_box is None or need_dict_box is None:
|
||||
if need_list_box is not None: stdlib.free(need_list_box)
|
||||
if need_dict_box is not None: stdlib.free(need_dict_box)
|
||||
@@ -756,7 +755,7 @@ novirtual class Translator:
|
||||
if node.children is not None:
|
||||
ch_a: list[ast.AST*]* = node.children
|
||||
n_a: sizet = len(ch_a)
|
||||
for bi_a in range(n_a):
|
||||
for bi_a in range(int(n_a)):
|
||||
if self._tree_has_assert(ch_a.get(bi_a), depth + 1) != 0: return 1
|
||||
return 0
|
||||
|
||||
@@ -765,7 +764,7 @@ novirtual class Translator:
|
||||
"""Return 1 when any node in the statement list contains an assert statement."""
|
||||
if lst is None: return 0
|
||||
n_l: sizet = len(lst)
|
||||
for i_l in range(n_l):
|
||||
for i_l in range(int(n_l)):
|
||||
if self._tree_has_assert(lst.get(i_l), depth + 1) != 0: return 1
|
||||
return 0
|
||||
|
||||
@@ -809,7 +808,7 @@ novirtual class Translator:
|
||||
stdlib.free(nm_buf)
|
||||
return 0
|
||||
|
||||
def _scan_container_usage(self, node: ast.AST*, need_list_box: t.CPtr, need_dict_box: t.CPtr):
|
||||
def _scan_container_usage(self, node: ast.AST*, need_list_box: rawptr, need_dict_box: rawptr):
|
||||
"""Recursively scan AST statement structure for list/dict container usage."""
|
||||
if node is None: return
|
||||
k: int = node.kind()
|
||||
@@ -831,7 +830,7 @@ novirtual class Translator:
|
||||
args_list_s: list[ast.AST*]* = args_s.args
|
||||
an_s: sizet = len(args_list_s)
|
||||
ai_s: sizet
|
||||
for ai_s in range(an_s):
|
||||
for ai_s in range(int(an_s)):
|
||||
arg_s: ast.AST* = args_list_s.get(ai_s)
|
||||
if arg_s is not None:
|
||||
ag_s: ast.Arg* = ast.Arg*(arg_s)
|
||||
@@ -841,9 +840,9 @@ novirtual class Translator:
|
||||
if node.children is not None:
|
||||
ch_s: list[ast.AST*]* = node.children
|
||||
n_s: sizet = len(ch_s)
|
||||
for ci_s in range(n_s): self._scan_container_usage(ch_s.get(ci_s), need_list_box, need_dict_box)
|
||||
for ci_s in range(int(n_s)): self._scan_container_usage(ch_s.get(ci_s), need_list_box, need_dict_box)
|
||||
|
||||
def _check_container_type_node(self, node: ast.AST*, need_list_box: t.CPtr, need_dict_box: t.CPtr):
|
||||
def _check_container_type_node(self, node: ast.AST*, need_list_box: rawptr, need_dict_box: rawptr):
|
||||
"""Check whether a type-position node is list/dict, recursing through Name/Subscript/BinOp."""
|
||||
if node is None: return
|
||||
k: int = node.kind()
|
||||
|
||||
@@ -1,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,6 +41,9 @@ 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))优先,
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -71,7 +65,7 @@ class TypeGenCType(TypeGenBase.TypeGenBase):
|
||||
h: uint64 = _ctypedef_idx_hash(name)
|
||||
mask: sizet = sizet(self.CTypedefNameIdxCap - 1)
|
||||
si: sizet = sizet(h) & mask
|
||||
for i in range(self.CTypedefNameIdxCap):
|
||||
for i in range(int(self.CTypedefNameIdxCap)):
|
||||
slot: sizet* = self._ctypedef_idx_slot(si)
|
||||
sv: sizet = slot[0]
|
||||
if sv == 0: return -1
|
||||
@@ -87,7 +81,7 @@ class TypeGenCType(TypeGenBase.TypeGenBase):
|
||||
h: uint64 = _ctypedef_idx_hash(name)
|
||||
mask: sizet = sizet(self.CTypedefNameIdxCap - 1)
|
||||
si: sizet = sizet(h) & mask
|
||||
for i in range(self.CTypedefNameIdxCap):
|
||||
for i in range(int(self.CTypedefNameIdxCap)):
|
||||
slot: sizet* = self._ctypedef_idx_slot(si)
|
||||
if slot[0] == 0:
|
||||
slot[0] = sizet(slot_index + 1)
|
||||
@@ -111,7 +105,7 @@ class TypeGenCType(TypeGenBase.TypeGenBase):
|
||||
def register_ctypedef(self, pool: memhub.MemBuddy*, name: str, type_str: str) -> None:
|
||||
"""Register a CTypedef name and its type expression string into the local table."""
|
||||
if name is None or type_str is None or pool is None: return
|
||||
# rawptr is built-in syntax (TPC/TPV) and cannot be redefined: ignore any
|
||||
# rawptr is built-in syntax (TPC/VPC) and cannot be redefined: ignore any
|
||||
# typedef registration for rawptr (rawptr[T] = raw pointer to T).
|
||||
if string.strcmp(name, "rawptr") == 0: return
|
||||
# Copy name/type_str into the pool: callers may pass zero-copy AST strings
|
||||
@@ -144,7 +138,7 @@ class TypeGenCType(TypeGenBase.TypeGenBase):
|
||||
# dedup: if a same-named CTypedef exists, update its type string
|
||||
exist_n: sizet = len(self.CTypedefNames)
|
||||
ei: sizet
|
||||
for ei in range(exist_n):
|
||||
for ei in range(int(exist_n)):
|
||||
en: str = self.CTypedefNames.get(ei)
|
||||
if en is not None and string.strcmp(en, name_copy) == 0:
|
||||
# already exists; update the type string
|
||||
@@ -187,7 +181,7 @@ class TypeGenCType(TypeGenBase.TypeGenBase):
|
||||
ts_hit: str = self.CTypedefTypeStrs.get(sizet(ti))
|
||||
self.CTypedefFound = 1
|
||||
return ts_hit
|
||||
for i in range(n):
|
||||
for i in range(int(n)):
|
||||
nm: str = self.CTypedefNames.get(i)
|
||||
if nm is not None and string.strcmp(nm, name) == 0:
|
||||
ts: str = self.CTypedefTypeStrs.get(i)
|
||||
@@ -205,14 +199,14 @@ class TypeGenCType(TypeGenBase.TypeGenBase):
|
||||
want: sizet = end - start
|
||||
n: sizet = len(self.CTypedefNames)
|
||||
i: sizet
|
||||
for i in range(n):
|
||||
for i in range(int(n)):
|
||||
nm: str = self.CTypedefNames.get(i)
|
||||
if nm is None: continue
|
||||
# Fast path: a hit requires nm[0]==text[start]; a differing first byte
|
||||
# rules out a match without strlen.
|
||||
if nm[0] != text[start] or string.strlen(nm) != want: continue
|
||||
hit: int = 1
|
||||
for k in range(want):
|
||||
for k in range(int(want)):
|
||||
if nm[k] != text[start + k]:
|
||||
hit = 0
|
||||
break
|
||||
@@ -345,7 +339,7 @@ class TypeGenCType(TypeGenBase.TypeGenBase):
|
||||
if self.CTypedefVisiting is None: return 0
|
||||
vn: sizet = len(self.CTypedefVisiting)
|
||||
vi: sizet
|
||||
for vi in range(vn):
|
||||
for vi in range(int(vn)):
|
||||
vs: str = self.CTypedefVisiting.get(vi)
|
||||
if vs is not None and string.strcmp(vs, name) == 0:
|
||||
return 1 # cycle detected
|
||||
@@ -389,7 +383,7 @@ class TypeGenCType(TypeGenBase.TypeGenBase):
|
||||
n: sizet = len(ch)
|
||||
count: int = 0
|
||||
i: sizet
|
||||
for i in range(n):
|
||||
for i in range(int(n)):
|
||||
child: ast.AST* = ch.get(i)
|
||||
if child is None: continue
|
||||
if child.kind() != ast.ASTKind.AnnAssign: continue
|
||||
@@ -423,7 +417,7 @@ class TypeGenCType(TypeGenBase.TypeGenBase):
|
||||
# 512 (not 320): _parse_typedef_line normalizes a C-style RHS (`const T` / `T*`) into
|
||||
# the longer canonical `|` union text, so the VALUE area must hold the expanded form.
|
||||
td_buf: str = pool.alloc_tagged(4096, memhub.DETSITE_TYPEGENCTYPE_425)
|
||||
for i in range(src_len + 1):
|
||||
for i in range(int(src_len + 1)):
|
||||
if i == src_len or src[i] == '\n':
|
||||
line_len: sizet = i - line_start
|
||||
if line_len > 0 and td_buf is not None:
|
||||
@@ -450,7 +444,8 @@ class TypeGenCType(TypeGenBase.TypeGenBase):
|
||||
cn: ast.Constant* = ast.Constant*(node)
|
||||
if cn.const_kind == ast.CONST_INT:
|
||||
self.ConstEvalOk = 1
|
||||
return cn.int_val
|
||||
# IntVal(i64) → int(i32):显式收窄(本函数返回 int)
|
||||
return int(cn.int_val)
|
||||
self.ConstEvalOk = 0
|
||||
return 0
|
||||
|
||||
@@ -489,7 +484,7 @@ class TypeGenCType(TypeGenBase.TypeGenBase):
|
||||
if fi_buf is not None:
|
||||
fi_buf[0] = '*'
|
||||
fi_buf[1] = ':'
|
||||
for mi in range(mod_len): fi_buf[2 + mi] = at_mod.id[mi]
|
||||
for mi in range(int(mod_len)): fi_buf[2 + mi] = at_mod.id[mi]
|
||||
fi_buf[2 + mod_len] = '\0'
|
||||
av: int = int(self.Gen.ExprGenCdef._lookup_cross_module_cdefine(pool, at.attr, fi_buf, imported_modules))
|
||||
if self.Gen.TypeGenCDef.is_cdefine_found() != 0:
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
import lib.core.Gen.GenTypes as GenTypes
|
||||
import lib.core.Gen.GenApi as GenApi
|
||||
import lib.core.Gen.Generator as Generator
|
||||
import lib.core.Gen.Type.TypeGenCType as TypeGenCType
|
||||
import lib.core.Gen.Type.TypeGenMixin as TypeGenMixin
|
||||
import t, c
|
||||
from stdint import *
|
||||
import ast
|
||||
import mlirlite
|
||||
@@ -11,20 +8,17 @@ import memhub
|
||||
import hashtable
|
||||
import string
|
||||
import viperlib
|
||||
import stdio
|
||||
import stdlib
|
||||
import lib.core.VLogger as VLogger
|
||||
import lib.core.Gen.Translator as HT
|
||||
import lib.Stub.StubMerger as StubMerger
|
||||
import lib.Stub.StubMerger.StubMergerStore as StubMergerStore
|
||||
import lib.Stub.StubGen.VpiReader as VpiReader
|
||||
import lib.core.CompilerFactory as CompilerFactory
|
||||
import lib.Projectrans.Config as Config
|
||||
|
||||
|
||||
import lib.core.Gen.Type.TypeGenBase as TypeGenBase
|
||||
import lib.core.Gen.Type.TypeGenCDef as TypeGenCDef
|
||||
import w32.fileio as fileio
|
||||
import win32.fileio as fileio
|
||||
|
||||
|
||||
# ============================================================
|
||||
# TypeGenResolve - split out of TypeGenMixin (stage D): type mapping / annotation resolution.
|
||||
@@ -134,7 +128,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
# and read them back (corrupts to -1 on the bootstrapped binary); re-parse instead.
|
||||
total3: sizet = 2 # open + close (1 byte each; overcounting empty args is harmless)
|
||||
cnt3: sizet = 0
|
||||
for i3 in range(n3):
|
||||
for i3 in range(int(n3)):
|
||||
e3: ast.AST* = elts.get(i3)
|
||||
if e3 is None: continue
|
||||
es3: str = self._subscript_slice_to_str(pool, e3)
|
||||
@@ -152,7 +146,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
buf4[0] = '\0'
|
||||
string.strcat(buf4, open_ch)
|
||||
wrote3: sizet = 0
|
||||
for i4 in range(n3):
|
||||
for i4 in range(int(n3)):
|
||||
e4: ast.AST* = elts.get(i4)
|
||||
if e4 is None: continue
|
||||
es4: str = self._subscript_slice_to_str(pool, e4)
|
||||
@@ -336,7 +330,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
if seg_len == 0: return type_name
|
||||
pipe_idx: sizet = seg_len
|
||||
p_depth: int = 0
|
||||
for pi in range(seg_len):
|
||||
for pi in range(int(seg_len)):
|
||||
ch_p: int = type_name[pi]
|
||||
if ch_p == '[': p_depth += 1
|
||||
elif ch_p == ']': p_depth -= 1
|
||||
@@ -379,7 +373,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
# normalize each; t.CArray / list keep their count arg verbatim.
|
||||
lb_idx: sizet = seg_len
|
||||
ld: int = 0
|
||||
for li2 in range(seg_len):
|
||||
for li2 in range(int(seg_len)):
|
||||
if type_name[li2] == '[':
|
||||
if ld == 0:
|
||||
lb_idx = li2
|
||||
@@ -415,7 +409,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
# name / literal / const expr), not a type name, so keep it verbatim.
|
||||
_cntc_g: int = self._is_count_arg_container(container_g)
|
||||
_argi_g: int = 0
|
||||
for ci_g in range(inner_len_g + 1):
|
||||
for ci_g in range(int(inner_len_g + 1)):
|
||||
is_sep_g: int = 0
|
||||
if ci_g < inner_len_g:
|
||||
if inner_buf_g[ci_g] == '[': cur_dep_g += 1
|
||||
@@ -447,7 +441,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
# Reassemble container[arg1,arg2,...]
|
||||
g_total: sizet = string.strlen(container_g) + 2
|
||||
gi_g: sizet
|
||||
for gi_g in range(len(arg_list_g)):
|
||||
for gi_g in range(int(len(arg_list_g))):
|
||||
if gi_g > 0: g_total += 1
|
||||
g_arg: str = arg_list_g.get(gi_g)
|
||||
if g_arg is not None: g_total += string.strlen(g_arg)
|
||||
@@ -457,7 +451,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
g_out[0] = '\0'
|
||||
string.strcat(g_out, container_g)
|
||||
string.strcat(g_out, "[")
|
||||
for gi_g in range(len(arg_list_g)):
|
||||
for gi_g in range(int(len(arg_list_g))):
|
||||
if gi_g > 0: string.strcat(g_out, ",")
|
||||
g_arg2: str = arg_list_g.get(gi_g)
|
||||
if g_arg2 is not None: string.strcat(g_out, g_arg2)
|
||||
@@ -477,7 +471,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
if _base_n_s is not None:
|
||||
_out_s: str = _base_n_s
|
||||
_si_s: int
|
||||
for _si_s in range(_st_n):
|
||||
for _si_s in range(int(_st_n)):
|
||||
_out_s = self._ptr_star_text(pool, _out_s)
|
||||
if _out_s is None: break
|
||||
if _out_s is not None: return _out_s
|
||||
@@ -505,7 +499,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
sha1a = self._class_true_def_sha1(pool, attr, sha1a)
|
||||
res_a: str = self._concat_sha1_class(pool, sha1a, attr)
|
||||
if res_a is not None: return res_a
|
||||
# 4b. mod is itself a full module path (no aliased import, e.g. w32.fileio)
|
||||
# 4b. mod is itself a full module path (no aliased import, e.g. win32.fileio)
|
||||
sha1b: str = self.Gen.CallGenBase._lookup_sha1_from_modname_raw(pool, mod_buf)
|
||||
if sha1b is not None:
|
||||
# Same as above: `ast.AST`'s mod is a package, so correct it to the
|
||||
@@ -628,7 +622,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
id_len: sizet = string.strlen(ident)
|
||||
if id_len == 0 or s_len < id_len: return 0
|
||||
i: sizet
|
||||
for i in range(s_len - id_len + 1):
|
||||
for i in range(int(s_len - id_len + 1)):
|
||||
if string.strncmp(s + i, ident, id_len) != 0: continue
|
||||
# Left boundary: i==0 or the previous char is not an identifier char
|
||||
left_ok: int = 1
|
||||
@@ -667,14 +661,14 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
if _base_node is not None:
|
||||
_wrap_n: ast.AST* = _base_node
|
||||
_wi_n: int
|
||||
for _wi_n in range(_st_cnt):
|
||||
for _wi_n in range(int(_st_cnt)):
|
||||
_raw_nm: ast.AST* = ast.Name(pool, "rawptr", ast.ASTCtx.Load)
|
||||
_wrap_n = ast.Subscript(pool, _raw_nm, _wrap_n, ast.ASTCtx.Load)
|
||||
return _wrap_n
|
||||
|
||||
# Check for '|' (BinOp): search only within the valid range [0, seg_len)
|
||||
pipe_idx: sizet = seg_len
|
||||
for pi in range(seg_len):
|
||||
for pi in range(int(seg_len)):
|
||||
if type_str[pi] == '|':
|
||||
pipe_idx = pi
|
||||
break
|
||||
@@ -696,14 +690,14 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
# Check for '[' (Subscript) like t.Callable[[int], int]; must precede the '.'
|
||||
# check since it is a Subscript. Parse fully via ast.parse_expression.
|
||||
bracket_idx: sizet = seg_len
|
||||
for bi in range(seg_len):
|
||||
for bi in range(int(seg_len)):
|
||||
if type_str[bi] == '[':
|
||||
bracket_idx = bi
|
||||
break
|
||||
if bracket_idx < seg_len:
|
||||
# If the inner part starts with a digit (a "{16hex}.Class" name), the lexer
|
||||
# mis-scans it as a number; split base+slice manually for a single non-nested arg.
|
||||
first_inner_ch: int = type_str[bracket_idx + 1] if (bracket_idx + 1) < seg_len else 0
|
||||
first_inner_ch: int = int(type_str[bracket_idx + 1] if (bracket_idx + 1) < seg_len else 0)
|
||||
if first_inner_ch >= 48 and first_inner_ch <= 57:
|
||||
close_idx_m: sizet = seg_len
|
||||
ci_m: sizet = bracket_idx + 1
|
||||
@@ -754,7 +748,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
|
||||
# Whether the text contains '.' (Attribute): search only within [0, seg_len).
|
||||
dot_idx: sizet = seg_len
|
||||
for di in range(seg_len):
|
||||
for di in range(int(seg_len)):
|
||||
if type_str[di] == '.':
|
||||
dot_idx = di
|
||||
break
|
||||
@@ -824,7 +818,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
# Library typedef fallback: read library sources (e.g. stdint UINT/DWORD) and
|
||||
# register them when _vpi_register_typedef_closure did not inject them in time.
|
||||
lib_ts: str = self._lookup_library_typedef(pool, "stdint", name)
|
||||
if lib_ts is None: lib_ts = self._lookup_library_typedef(pool, "w32.win32base", name)
|
||||
if lib_ts is None: lib_ts = self._lookup_library_typedef(pool, "win32.win32base", name)
|
||||
if lib_ts is not None:
|
||||
lib_node: ast.AST* = self._parse_type_string(pool, lib_ts, ast.ASTCtx.Load)
|
||||
if lib_node is not None:
|
||||
@@ -870,7 +864,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
p_buf: str = stdlib.malloc(p_len)
|
||||
if p_buf is None: return None
|
||||
mi: sizet
|
||||
for mi in range(m_len):
|
||||
for mi in range(int(m_len)):
|
||||
if mod[mi] == '.': p_buf[mi] = '/'
|
||||
else: p_buf[mi] = mod[mi]
|
||||
p_buf[m_len] = '\0'
|
||||
@@ -1014,10 +1008,10 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
if pos + ptrs * 11 >= 500: ptrs = 0
|
||||
if pos + ptrs * 11 >= 500: vlen2 = 0
|
||||
else: vlen2 = 500 - pos - ptrs * 11
|
||||
for ci2 in range(vlen2):
|
||||
for ci2 in range(int(vlen2)):
|
||||
out_buf[pos + ci2] = buf[line_start + k + ci2]
|
||||
pos += vlen2
|
||||
for pi in range(ptrs):
|
||||
for pi in range(int(ptrs)):
|
||||
string.memcpy(out_buf + pos, " | t.CPtr", 9)
|
||||
pos += 9
|
||||
out_buf[pos] = '\0'
|
||||
@@ -1029,7 +1023,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
# temporary buffer, so copy it via store_builtins_diag to avoid a dangling pointer.
|
||||
self.Gen.TypeGenCType.LastBuiltinsTypedefName = self.Gen.TypeGenCType.store_builtins_diag(0, name)
|
||||
self.Gen.TypeGenCType.LastBuiltinsTypedefResult = None
|
||||
# rawptr is built into the compiler (a reserved word on both TPC/TPV) and takes
|
||||
# rawptr is built into the compiler (a reserved word on both TPC/VPC) and takes
|
||||
# priority over any external declaration; always return t.CPtr.
|
||||
if string.strcmp(name, "rawptr") == 0:
|
||||
self.Gen.TypeGenCType.LastBuiltinsTypedefResult = "t.CPtr"
|
||||
@@ -1057,7 +1051,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
if bf.closed:
|
||||
stdlib.free(b_buf)
|
||||
return None
|
||||
b_read: LONG = bf.read_all(b_buf, ULONG(BUF_B))
|
||||
b_read: LONG = bf.read_all(b_buf, BUF_B)
|
||||
bf.close()
|
||||
if b_read <= 0:
|
||||
stdlib.free(b_buf)
|
||||
@@ -1069,7 +1063,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
# Pre-scan import lines to register aliases (import stdint as __stdint), so later
|
||||
# module-qualified typedef expansion can resolve the real module name.
|
||||
b_imp_ls: sizet = 0
|
||||
for b_imp_i in range(b_total + 1):
|
||||
for b_imp_i in range(int(b_total + 1)):
|
||||
if b_imp_i == b_total or b_buf[b_imp_i] == '\n':
|
||||
b_imp_len: sizet = b_imp_i - b_imp_ls
|
||||
if b_imp_len > 0:
|
||||
@@ -1112,7 +1106,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
# Line parse buffer ("NAME\0VALUE\0") reused for the whole file; values to keep are
|
||||
# copied out (register_ctypedef copies internally; only b_res needs copying).
|
||||
b_td_buf: str = pool.alloc(320)
|
||||
for b_i in range(b_total + 1):
|
||||
for b_i in range(int(b_total + 1)):
|
||||
if b_i == b_total or b_buf[b_i] == '\n':
|
||||
b_line_len: sizet = b_i - b_line_start
|
||||
if b_line_len > 0 and b_td_buf is not None:
|
||||
@@ -1198,10 +1192,10 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
key_buf[mod_len] = '|'
|
||||
string.strncpy(key_buf + mod_len + 1, attr, attr_len)
|
||||
key_buf[key_len] = '\0'
|
||||
v: t.CPtr = self.LibTypedefTypeMap.__getitem__(key_buf)
|
||||
v: rawptr = self.LibTypedefTypeMap.__getitem__(key_buf)
|
||||
stdlib.free(key_buf)
|
||||
if v is None: return None
|
||||
return t.CPtr(v)
|
||||
return rawptr(v)
|
||||
def _lookup_library_typedef(self, pool: memhub.MemBuddy*, mod: str, attr: str) -> str:
|
||||
"""Look up "attr: t.CTypedef = TYPE" in a library module source, returning TYPE or None."""
|
||||
if mod is None or attr is None: return None
|
||||
@@ -1243,7 +1237,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
m_td_buf: str = pool.alloc(320)
|
||||
# Scan the whole file (no early exit) to build the full persistent cache and still
|
||||
# take attr's first-hit value with the same comparison as before.
|
||||
for m_i in range(m_total + 1):
|
||||
for m_i in range(int(m_total + 1)):
|
||||
if m_i == m_total or m_buf[m_i] == '\n':
|
||||
m_line_len: sizet = m_i - m_ls
|
||||
if m_line_len > 0 and m_td_buf is not None:
|
||||
@@ -1271,7 +1265,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
if type_str is None or depth > 16: return type_str
|
||||
# Compound expressions (union/generic) are not expanded here
|
||||
if string.strchr(type_str, '[') is not None or string.strchr(type_str, '|') is not None: return type_str
|
||||
# Split "mod.attr" (use the last '.', supporting w32.win32base.HANDLE)
|
||||
# Split "mod.attr" (use the last '.', supporting win32.win32base.HANDLE)
|
||||
dot_pos: str = string.strrchr(type_str, '.')
|
||||
if dot_pos is None: return type_str
|
||||
mod_len: sizet = sizet(uint64(dot_pos) - uint64(type_str))
|
||||
@@ -1443,6 +1437,10 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
return mlirlite.Int1(pool)
|
||||
# ptrdiff_t
|
||||
elif string.strcmp(type_name, "CPtrDiffT") == 0: return mlirlite.Int64(pool)
|
||||
# Byte-order markers written alone (no real type on the other side) degrade to CInt,
|
||||
# matching TPC: `t.BigEndian` alone -> BaseType=CInt() + ByteOrder='big'.
|
||||
elif string.strcmp(type_name, "BigEndian") == 0: return mlirlite.Int32(pool)
|
||||
elif string.strcmp(type_name, "LittleEndian") == 0: return mlirlite.Int32(pool)
|
||||
|
||||
# stdint.vp aliases (INT/BYTE/DWORD/int8_t/size_t/_Bool ...) now come from its
|
||||
# CTypedef declarations; lowercase bare char/float/double stay here, int/bool use Name.
|
||||
@@ -1486,6 +1484,29 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
if self._is_t_attr(node, "CRef", imported_modules) != 0: return 1
|
||||
if self._is_t_attr(node, "CTempRef", imported_modules) != 0: return 1
|
||||
return 0
|
||||
def _is_byteorder_marker(self, node: ast.AST*, imported_modules: str) -> int:
|
||||
"""Whether a node is a byte-order marker: 0=none / 1=BigEndian / 2=LittleEndian."""
|
||||
if self._is_t_attr(node, "BigEndian", imported_modules) != 0: return 1
|
||||
if self._is_t_attr(node, "LittleEndian", imported_modules) != 0: return 2
|
||||
return 0
|
||||
def target_is_big_endian(self) -> int:
|
||||
"""Whether the compilation target is big-endian (1) or little-endian (0).
|
||||
The configured data layout wins: LLVM's first data-layout field is the endianness
|
||||
marker ('e' = little, 'E' = big; omitted means little). Without a data layout, fall
|
||||
back to the triple's `_be` arch suffix (e.g. aarch64_be-...); otherwise little, which
|
||||
is the default target. Both byte-order markers are judged against this: a `t.BigEndian`
|
||||
field on a big-endian target needs no swap, and a `t.LittleEndian` field on a
|
||||
big-endian target does."""
|
||||
dl: str = Config.TargetDataLayout
|
||||
if dl is not None and dl[0] != '\0':
|
||||
if dl[0] == 'E': return 1
|
||||
if dl[0] == 'e': return 0
|
||||
tr: str = Config.TargetTriple
|
||||
if tr is not None:
|
||||
ei: int = 0
|
||||
while tr[ei] != '\0' and tr[ei] != '-': ei += 1
|
||||
if ei >= 3 and tr[ei - 3] == '_' and tr[ei - 2] == 'b' and tr[ei - 1] == 'e': return 1
|
||||
return 0
|
||||
def _ref_marker_name_mode(self, node: ast.AST*) -> int:
|
||||
"""Syntactic value-reference marker test (no import context): 0=none / 1=CRef / 2=CTempRef."""
|
||||
if node is None: return 0
|
||||
@@ -1640,6 +1661,10 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
if string.strcmp(name, "CExport") == 0: return 1
|
||||
if string.strcmp(name, "CExtern") == 0: return 1
|
||||
if string.strcmp(name, "CInline") == 0: return 1
|
||||
# Byte-order markers are decorators, not types: never let them be extracted as a
|
||||
# class name (which would make the field degrade to an unknown struct lookup).
|
||||
if string.strcmp(name, "BigEndian") == 0: return 1
|
||||
if string.strcmp(name, "LittleEndian") == 0: return 1
|
||||
return 0
|
||||
|
||||
def extract_class_name_from_annotation(self, node: ast.AST*, imported_modules: str, pool: memhub.MemBuddy* = None, from_imports: str = None) -> str:
|
||||
@@ -1758,7 +1783,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
# Compute the total length
|
||||
tup_total: sizet = 1
|
||||
ti_sn: sizet
|
||||
for ti_sn in range(tn_sn):
|
||||
for ti_sn in range(int(tn_sn)):
|
||||
if ti_sn > 0: tup_total += 1
|
||||
elt_node: ast.AST* = tup_elts_sn.get(ti_sn)
|
||||
elt_name_raw: str = self._get_type_name_from_annotation(pool, elt_node)
|
||||
@@ -1768,7 +1793,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
tup_buf: str = pool.alloc(tup_total)
|
||||
if tup_buf is not None:
|
||||
tup_buf[0] = '\0'
|
||||
for ti_sn in range(tn_sn):
|
||||
for ti_sn in range(int(tn_sn)):
|
||||
if ti_sn > 0: string.strcat(tup_buf, ",")
|
||||
elt_node2: ast.AST* = tup_elts_sn.get(ti_sn)
|
||||
elt_name2_raw: str = self._get_type_name_from_annotation(pool, elt_node2)
|
||||
@@ -1819,7 +1844,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
tup_head: mlirlite.ParamNode* = None
|
||||
tup_tail: mlirlite.ParamNode* = None
|
||||
tui: sizet
|
||||
for tui in range(tp_count):
|
||||
for tui in range(int(tp_count)):
|
||||
tup_elt: ast.AST* = tp_elts.get(tui)
|
||||
if tup_elt is None: continue
|
||||
tup_ety: mlirlite.LLVMType* = self.resolve_annotation_type(pool, tup_elt, imported_modules, from_imports, trans, depth + 1)
|
||||
@@ -1839,7 +1864,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
if gen_elts is not None:
|
||||
gen_en: sizet = len(gen_elts)
|
||||
gen_ei: sizet
|
||||
for gen_ei in range(gen_en):
|
||||
for gen_ei in range(int(gen_en)):
|
||||
gen_elt: ast.AST* = gen_elts.get(gen_ei)
|
||||
gen_tn_raw: str = self._get_type_name_from_annotation(pool, gen_elt)
|
||||
# Normalize type args to SHA1 form (ast.AST/AST -> f467978339422f32.AST) to
|
||||
@@ -1854,7 +1879,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
gen_total: sizet = string.strlen(gen_cls_name) + 2 # "[" and "]"
|
||||
gen_an: sizet = len(gen_arg_names)
|
||||
gen_ai: sizet
|
||||
for gen_ai in range(gen_an):
|
||||
for gen_ai in range(int(gen_an)):
|
||||
if gen_ai > 0:
|
||||
gen_total += 1 # ","
|
||||
gen_atn: str = gen_arg_names.get(gen_ai)
|
||||
@@ -1865,7 +1890,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
gen_mangled[0] = '\0'
|
||||
string.strcat(gen_mangled, gen_cls_name)
|
||||
string.strcat(gen_mangled, "[")
|
||||
for gen_ai in range(gen_an):
|
||||
for gen_ai in range(int(gen_an)):
|
||||
if gen_ai > 0: string.strcat(gen_mangled, ",")
|
||||
gen_atn: str = gen_arg_names.get(gen_ai)
|
||||
if gen_atn is not None: string.strcat(gen_mangled, gen_atn)
|
||||
@@ -1992,7 +2017,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
if tpn_g is not None and tpa_g is not None:
|
||||
gp_n: sizet = len(tpn_g)
|
||||
gp_i: sizet
|
||||
for gp_i in range(gp_n):
|
||||
for gp_i in range(int(gp_n)):
|
||||
gp_tpn: str = tpn_g.get(gp_i)
|
||||
if gp_tpn is not None and string.strcmp(nm.id, gp_tpn) == 0:
|
||||
gp_ta: str = tpa_g.get(gp_i)
|
||||
@@ -2005,7 +2030,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
self.Gen.TypeGenErr.fatal_error(nm, f"Generic type argument pollution: type parameter '{gp_tpn}' maps to itself (argument='{gp_ta}'); cannot specialize", "TypeError")
|
||||
polluted: int = 0
|
||||
gp_j: sizet
|
||||
for gp_j in range(gp_n):
|
||||
for gp_j in range(int(gp_n)):
|
||||
gp_pn: str = tpn_g.get(gp_j)
|
||||
# Token-boundary match (not strstr): the param name must be an
|
||||
# independent identifier, else chars inside AST/CPtr misjudge as a param.
|
||||
@@ -2124,7 +2149,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
oa_buf: str = pool.alloc(oa_len + 1)
|
||||
if oa_buf is not None:
|
||||
oa_i: sizet
|
||||
for oa_i in range(oa_len): oa_buf[oa_i] = orig_alias[oa_i]
|
||||
for oa_i in range(int(oa_len)): oa_buf[oa_i] = orig_alias[oa_i]
|
||||
oa_buf[oa_len] = '\0'
|
||||
# Rebuild the node from the original name and recurse (depth guard reused)
|
||||
orig_node_a: ast.AST* = self._parse_type_string(pool, oa_buf, nm.ctx)
|
||||
@@ -2139,10 +2164,10 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
if btd_node_n is not None:
|
||||
btd_ty_n: mlirlite.LLVMType* = self.resolve_annotation_type(pool, btd_node_n, imported_modules, from_imports, trans, depth + 1)
|
||||
if btd_ty_n is not None: return btd_ty_n
|
||||
# Library typedef fallback: read library sources (stdint / w32.win32base bare
|
||||
# Library typedef fallback: read library sources (stdint / win32.win32base bare
|
||||
# typedefs) when _vpi_register_typedef_closure did not inject them, same as above.
|
||||
lib_ts_n: str = self._lookup_library_typedef(pool, "stdint", nm.id)
|
||||
if lib_ts_n is None: lib_ts_n = self._lookup_library_typedef(pool, "w32.win32base", nm.id)
|
||||
if lib_ts_n is None: lib_ts_n = self._lookup_library_typedef(pool, "win32.win32base", nm.id)
|
||||
if lib_ts_n is not None:
|
||||
lib_node_n: ast.AST* = self._parse_type_string(pool, lib_ts_n, nm.ctx)
|
||||
if lib_node_n is not None:
|
||||
@@ -2293,13 +2318,13 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
if ty_repl_a is not None: return ty_repl_a
|
||||
return None
|
||||
# Nested Attribute: three-or-more-level qualified names like
|
||||
# w32.win32base.SECURITY_ATTRIBUTES; use the outermost attr (simple class name).
|
||||
# win32.win32base.SECURITY_ATTRIBUTES; use the outermost attr (simple class name).
|
||||
if at.value is not None and at.value.kind() == ast.ASTKind.Attribute:
|
||||
nested_struct_ty: mlirlite.LLVMType* = self.Gen.StructGen.get_struct_type(at.attr)
|
||||
if nested_struct_ty is not None: return nested_struct_ty
|
||||
# Nested module-qualified enum class (three-level name): look up the enum base type
|
||||
if self.Gen.StmtGenDecl.is_enum_class(at.attr) == 1: return self.Gen.StmtGenDecl.get_enum_base_type(at.attr)
|
||||
# Nested module-qualified typedef (w32.win32base.HANDLE = VOIDPTR): same
|
||||
# Nested module-qualified typedef (win32.win32base.HANDLE = VOIDPTR): same
|
||||
# lookup_ctypedef + recursion + map_t_type fallback as the Name branch.
|
||||
if self.Gen.TypeGenCType.CTypedefResolveDepth < TypeGenMixin.MAX_CTYPEDEF_DEPTH:
|
||||
nested_td_str: str = self.Gen.TypeGenCType.lookup_ctypedef(at.attr)
|
||||
@@ -2315,7 +2340,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
else: self.Gen.TypeGenCType._ctypedef_pop_visiting()
|
||||
mapped_ty_na: mlirlite.LLVMType* = self.map_t_type(pool, at.attr)
|
||||
if mapped_ty_na is not None: return mapped_ty_na
|
||||
# Nested module-qualified typedef fallback (w32.win32base.HANDLE = VOIDPTR):
|
||||
# Nested module-qualified typedef fallback (win32.win32base.HANDLE = VOIDPTR):
|
||||
# build the full module path from the nested Attribute, then scan the library source.
|
||||
mod_path_na: str = self.Gen.ExprGenAttr._attr_module_path_str(pool, at.value)
|
||||
if mod_path_na is not None:
|
||||
@@ -2332,6 +2357,19 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
if k == ast.ASTKind.BinOp:
|
||||
bop: ast.BinOp* = ast.BinOp*(node)
|
||||
if bop.op == ast.OpKind.BitOr:
|
||||
# Byte-order markers: annotation `|` means "and", so `ActualType | t.BigEndian`
|
||||
# yields the actual type; the byte order itself is recorded on the field entry
|
||||
# (see ClassGenDef field_byteorder_buf), not carried in the LLVM type.
|
||||
left_bo: int = self._is_byteorder_marker(bop.left, imported_modules)
|
||||
right_bo: int = self._is_byteorder_marker(bop.right, imported_modules)
|
||||
if left_bo != 0 or right_bo != 0:
|
||||
other_node_bo: ast.AST* = bop.right
|
||||
if right_bo != 0: other_node_bo = bop.left
|
||||
if other_node_bo is not None:
|
||||
other_ty_bo: mlirlite.LLVMType* = self.resolve_annotation_type(pool, other_node_bo, imported_modules, from_imports, trans, depth + 1)
|
||||
if other_ty_bo is not None: return other_ty_bo
|
||||
# Markers on both sides (e.g. `t.BigEndian | t.LittleEndian`) -> CInt.
|
||||
return mlirlite.Int32(pool)
|
||||
# Detect the t.CPtr marker (supports t.CPtr and the bare from t import CPtr name).
|
||||
# CRef/CTempRef (value-reference parameter markers) resolve identically here: the
|
||||
# parameter ABI is Ptr(other); the ref/temp distinction only drives call-site auto-address.
|
||||
@@ -2386,7 +2424,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
other_nm_x: ast.Name* = ast.Name*(other_node)
|
||||
gtn_nx: sizet = len(tpn_g2)
|
||||
gtn_ix: sizet
|
||||
for gtn_ix in range(gtn_nx):
|
||||
for gtn_ix in range(int(gtn_nx)):
|
||||
gtn_nmx: str = tpn_g2.get(gtn_ix)
|
||||
if gtn_nmx is not None and other_nm_x.id is not None:
|
||||
if string.strcmp(gtn_nmx, other_nm_x.id) == 0:
|
||||
@@ -2464,7 +2502,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
cur_lvl = nxt_lvl
|
||||
# Result = wrap the other side's type in N layers of Ptr
|
||||
res_ty: mlirlite.LLVMType* = cptr_other_ty
|
||||
for li2 in range(cptr_level):
|
||||
for li2 in range(int(cptr_level)):
|
||||
res_ty = mlirlite.Ptr(pool, res_ty)
|
||||
return res_ty
|
||||
|
||||
@@ -2582,7 +2620,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
arr_val_name: ast.Name* = ast.Name*(arr_at.value)
|
||||
if arr_val_name.id is None: self.Gen.TypeGenErr.fatal_type_error(node, "Subscript module name is None")
|
||||
# Function pointer type: t.Callable[[A, B], Ret] -> i8* (function values are bitcast to
|
||||
# i8* for storage and the signature is rebuilt at calls, matching TPV convention).
|
||||
# i8* for storage and the signature is rebuilt at calls, matching VPC convention).
|
||||
if string.strcmp(arr_at.attr, "Callable") == 0:
|
||||
# t/c are builtin namespaces (not compiled, not in imported_modules); no import check needed
|
||||
if string.strcmp(arr_val_name.id, "t") != 0 and string.strcmp(arr_val_name.id, "c") != 0 and self.Gen.ImportsGen.is_module_imported(imported_modules, arr_val_name.id) == 0:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
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 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
|
||||
import lib.Projectrans.Config as Config
|
||||
|
||||
|
||||
# IncludesScanner - recursive scan of the includes directory (Win32 FindFirstFileA/
|
||||
@@ -119,6 +117,16 @@ def compute_file_sha1(pool: memhub.MemBuddy*, file_path: str) -> str:
|
||||
|
||||
# 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
|
||||
|
||||
@@ -166,7 +174,7 @@ def scan_directory_recursive(pool: memhub.MemBuddy*, root_dir: str, rel_prefix:
|
||||
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
|
||||
# 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
|
||||
@@ -231,7 +239,10 @@ def scan_directory_recursive(pool: memhub.MemBuddy*, root_dir: str, rel_prefix:
|
||||
# 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)
|
||||
# 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")
|
||||
|
||||
@@ -277,7 +288,7 @@ 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):
|
||||
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:
|
||||
@@ -297,8 +308,8 @@ def _PathExistsA(path: str) -> int:
|
||||
|
||||
|
||||
# _TryResolveModuleFile - try one candidate path and write outputs (1=hit, 0=miss);
|
||||
# out_abs_path/out_rel_path/out_inc_dir are t.CPtr output slots.
|
||||
def _TryResolveModuleFile(inc_dir: str, rel_seg: bytes, rel_len: sizet, suffix: str, out_abs_path: t.CPtr, out_rel_path: t.CPtr, out_inc_dir: t.CPtr) -> int:
|
||||
# out_abs_path/out_rel_path/out_inc_dir are rawptr output slots.
|
||||
def _TryResolveModuleFile(inc_dir: str, rel_seg: bytes, rel_len: sizet, suffix: str, out_abs_path: rawptr, out_rel_path: rawptr, out_inc_dir: rawptr) -> int:
|
||||
if inc_dir is None or rel_seg is None or suffix is None: return 0
|
||||
inc_len: sizet = string.strlen(inc_dir)
|
||||
suf_len: sizet = string.strlen(suffix)
|
||||
@@ -332,7 +343,7 @@ def _TryResolveModuleFile(inc_dir: str, rel_seg: bytes, rel_len: sizet, suffix:
|
||||
return 0
|
||||
string.strcpy(inc_buf, inc_dir)
|
||||
# Write output slots (stdlib deep copies; caller frees). Slots must be cast to
|
||||
# (sizet*): t.CPtr* folds to t.CPtr and [0] becomes a 1-byte access.
|
||||
# (sizet*): rawptr* folds to rawptr and [0] becomes a 1-byte access.
|
||||
if out_abs_path is not None:
|
||||
abs_slot: sizet* = (sizet*)(out_abs_path)
|
||||
abs_slot[0] = sizet(cand)
|
||||
@@ -349,7 +360,7 @@ def _TryResolveModuleFile(inc_dir: str, rel_seg: bytes, rel_len: sizet, suffix:
|
||||
|
||||
# ResolveModuleToPath - module name -> file path, resolved on demand (no full scan).
|
||||
# Tries each includes dir, preferring file modules over packages. Returns 0 found, 1 not.
|
||||
def ResolveModuleToPath(includes_dirs: str, mod_name: str, out_abs_path: t.CPtr, out_rel_path: t.CPtr, out_inc_dir: t.CPtr) -> int:
|
||||
def ResolveModuleToPath(includes_dirs: str, mod_name: str, out_abs_path: rawptr, out_rel_path: rawptr, out_inc_dir: rawptr) -> int:
|
||||
if includes_dirs is None or mod_name is None: return 1
|
||||
mn_len: sizet = string.strlen(mod_name)
|
||||
if mn_len == 0: return 1
|
||||
@@ -357,7 +368,7 @@ def ResolveModuleToPath(includes_dirs: str, mod_name: str, out_abs_path: t.CPtr,
|
||||
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):
|
||||
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
|
||||
|
||||
@@ -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
|
||||
|
||||
+31
-29
@@ -1,12 +1,10 @@
|
||||
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
|
||||
import win32.win32console as win32cmd
|
||||
import win32.win32file as win32file
|
||||
import win32.win32base as win32base
|
||||
|
||||
|
||||
# VLogger - native logger with colored Windows console output.
|
||||
@@ -22,7 +20,7 @@ extern def longjmp(buf: rawptr, val: int)
|
||||
_repl_jmpbuf: rawptr = None
|
||||
|
||||
|
||||
# repl_abort - the single "terminate after diagnosis" exit in TPV.
|
||||
# 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."""
|
||||
@@ -33,7 +31,7 @@ def repl_abort() -> int:
|
||||
return 0
|
||||
|
||||
|
||||
def set_repl_jmpbuf(buf: t.CPtr) -> int:
|
||||
def set_repl_jmpbuf(buf: rawptr) -> int:
|
||||
"""Register the REPL jmp_buf; idempotent at each fragment entry."""
|
||||
global _repl_jmpbuf
|
||||
_repl_jmpbuf = buf
|
||||
@@ -52,21 +50,25 @@ enum LogLevel:
|
||||
|
||||
|
||||
# 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
|
||||
# 值用 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 = 0x0040
|
||||
define BACKGROUND_GREEN = 0x0020
|
||||
define BACKGROUND_BLUE = 0x0010
|
||||
define BACKGROUND_INTENSITY = 0x0080
|
||||
define BACKGROUND_WHITE = BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE
|
||||
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:
|
||||
@@ -78,7 +80,7 @@ novirtual class Logger:
|
||||
|
||||
def __init__(self, level: int = 1):
|
||||
self._level = level
|
||||
self._console_handle = w32file.GetStdHandle(w32file.STD_OUTPUT_HANDLE)
|
||||
self._console_handle = win32file.GetStdHandle(win32file.STD_OUTPUT_HANDLE)
|
||||
self._use_color = 1
|
||||
self.jit_flag = False
|
||||
self.__mbuddy__ = _mbuddy
|
||||
@@ -87,12 +89,12 @@ novirtual class Logger:
|
||||
uc: int = self._use_color
|
||||
if uc:
|
||||
ch: win32base.HANDLE = self._console_handle
|
||||
ret: int = w32cmd.SetConsoleTextAttribute(ch, attr)
|
||||
ret: int = win32cmd.SetConsoleTextAttribute(ch, attr)
|
||||
return ret
|
||||
return 0
|
||||
|
||||
def _reset_color(self) -> int:
|
||||
if self._use_color: return w32cmd.SetConsoleTextAttribute(self._console_handle, FOREGROUND_WHITE)
|
||||
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).
|
||||
@@ -114,14 +116,14 @@ novirtual class Logger:
|
||||
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)
|
||||
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, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY, category)
|
||||
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, FOREGROUND_RED | FOREGROUND_INTENSITY, category)
|
||||
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()
|
||||
@@ -131,11 +133,11 @@ novirtual class Logger:
|
||||
return ret
|
||||
|
||||
def success(self, msg: str, category: str = "") -> int:
|
||||
return self._log(LogLevel.SUCCESS, "├", "SUCCESS", msg, FOREGROUND_GREEN, FOREGROUND_GREEN | FOREGROUND_INTENSITY, category)
|
||||
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, BACKGROUND_RED | FOREGROUND_WHITE | FOREGROUND_INTENSITY, category)
|
||||
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
|
||||
@@ -192,7 +194,7 @@ log: Logger*
|
||||
def init_logger():
|
||||
"""Initialize the global logger instance."""
|
||||
global logger, log
|
||||
raw: t.CPtr = _mbuddy.alloc(Logger.__sizeof__())
|
||||
raw: rawptr = _mbuddy.alloc(Logger.__sizeof__())
|
||||
# if raw is None: return None (memory management still unclear)
|
||||
logger = raw
|
||||
logger.__before_init__()
|
||||
|
||||
@@ -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