refactor: remove dead ViperC compiler code
Drop unreachable struct/type helpers, the legacy wrapper-main and build pipeline paths, debug-only scaffolding, unused logging helpers/constants, and the no-op LSP signature map. Add the audit report for the remaining duplication and logging work.
This commit is contained in:
1 parent
5a0d61a163
commit
5b68732a94
35 files changed
+164
-1388
No files matched your search
@@ -409,20 +409,6 @@ def TranslateFileGetTrans(mb: memhub.MemBuddy*, file_path: str, sha1_val: str, c
|
||||
return tr
|
||||
|
||||
|
||||
class BuildResult:
|
||||
Success: int
|
||||
OutputPath: str
|
||||
ErrorMsg: str
|
||||
|
||||
def __new__(self) -> rawptr:
|
||||
return rawptr(_mbuddy.alloc(BuildResult.__sizeof__()))
|
||||
|
||||
def __init__(self):
|
||||
self.Success = 0
|
||||
self.OutputPath = None
|
||||
self.ErrorMsg = None
|
||||
|
||||
|
||||
def ensure_dir(path: str) -> int:
|
||||
"""Recursively create a directory (like mkdir -p); an existing directory counts as success."""
|
||||
if path is None: return 1
|
||||
@@ -1217,85 +1203,6 @@ def _append_host_export_implib(linker_flags: str, implib_path: str) -> str:
|
||||
return buf
|
||||
|
||||
|
||||
def link_obj_to_exe(output_dir: str, module_name: str, linker_cmd: str, linker_flags: str, linker_output: str, includes_binary_dir: str, linker_static_libs: str = None, linker_dynamic_libs: str = None) -> int:
|
||||
"""Link .obj files into an .exe via clang++; 0 on success, non-zero on failure."""
|
||||
if output_dir is None or linker_cmd is None or module_name is None: return 1
|
||||
|
||||
# build sliced .obj path: output_dir/{sha1 prefix}/{module_name}.obj
|
||||
obj_path: str = build_sliced_path(output_dir, module_name, "obj")
|
||||
if obj_path is None: return 1
|
||||
|
||||
# Host-exe export (when linker.flags has --export-all-symbols) also emits an import lib
|
||||
# {output_dir}/{linker_output}.dll.a; REPL fragments link it to import the host.
|
||||
if linker_output is not None:
|
||||
implib_len: sizet = string.strlen(output_dir) + string.strlen(linker_output) + 8
|
||||
implib_path: str = _mbuddy.alloc(implib_len)
|
||||
if implib_path is not None:
|
||||
viperlib.snprintf(implib_path, implib_len, "%s/%s.dll.a", output_dir, linker_output)
|
||||
linker_flags = _append_host_export_implib(linker_flags, implib_path)
|
||||
|
||||
# --traceback: link dbghelp (after the host-export rewrite, so both survive)
|
||||
linker_flags = _append_traceback_dbghelp(linker_flags)
|
||||
|
||||
# collect .obj paths from includes.binary
|
||||
EXTRA_BUF_SIZE: sizet = 32768
|
||||
extra_objs: bytes = _mbuddy.alloc(EXTRA_BUF_SIZE)
|
||||
if extra_objs is None: return 1
|
||||
extra_len: sizet = 0
|
||||
if includes_binary_dir is not None:
|
||||
extra_len = collect_obj_files(includes_binary_dir, extra_objs, EXTRA_BUF_SIZE)
|
||||
if extra_len > 0: VLogger.info(f"appended {extra_len} bytes of includes.binary .obj files", "link")
|
||||
else: VLogger.warning(f"includes.binary has no .obj files: {includes_binary_dir}", "link")
|
||||
|
||||
# Command: clang++ {obj_path} extra_objs [libs] -o {...}/{linker_output} linker_flags. Objects
|
||||
# and explicit libs must precede -l flags. The ASan runtime is declared in project.vpj, not hardcoded.
|
||||
use_flags: str = linker_flags
|
||||
static_libs: str = linker_static_libs
|
||||
if static_libs is None: static_libs = ""
|
||||
if linker_dynamic_libs is not None and len(linker_dynamic_libs) > 0:
|
||||
# Merge static + dynamic into one space-separated string (Viper str += would emit
|
||||
# invalid add i8* IR, so build it with snprintf).
|
||||
merged_sl: str = _mbuddy.alloc(len(static_libs) + len(linker_dynamic_libs) + 2)
|
||||
if merged_sl is not None:
|
||||
if len(static_libs) > 0:
|
||||
viperlib.snprintf(merged_sl, len(static_libs) + len(linker_dynamic_libs) + 2, "%s %s", static_libs, linker_dynamic_libs)
|
||||
else:
|
||||
viperlib.snprintf(merged_sl, len(static_libs) + len(linker_dynamic_libs) + 2, "%s", linker_dynamic_libs)
|
||||
static_libs = merged_sl
|
||||
sl_len: sizet = len(static_libs)
|
||||
cmd_len: sizet = len(linker_cmd) + len(obj_path) + len(use_flags) + len(output_dir) + len(linker_output) + extra_len + sl_len + 128
|
||||
cmd: bytes = _mbuddy.alloc(cmd_len)
|
||||
if cmd is None: return 1
|
||||
if sl_len > 0 and extra_len > 0:
|
||||
viperlib.snprintf(cmd, cmd_len, "%s %s %s %s -o %s/%s %s", linker_cmd, obj_path, extra_objs, static_libs, output_dir, linker_output, use_flags)
|
||||
elif sl_len > 0:
|
||||
viperlib.snprintf(cmd, cmd_len, "%s %s %s -o %s/%s %s", linker_cmd, obj_path, static_libs, output_dir, linker_output, use_flags)
|
||||
elif extra_len > 0:
|
||||
viperlib.snprintf(cmd, cmd_len, "%s %s %s -o %s/%s %s", linker_cmd, obj_path, extra_objs, output_dir, linker_output, use_flags)
|
||||
else:
|
||||
viperlib.snprintf(cmd, cmd_len, "%s %s -o %s/%s %s", linker_cmd, obj_path, output_dir, linker_output, use_flags)
|
||||
|
||||
# --phase2command mode: do not link; write the command to the bat
|
||||
if _phase2_cmd_path is not None:
|
||||
_phase2_write_cmd(cmd)
|
||||
return 0
|
||||
|
||||
result: subprocess.CompletedProcess* = subprocess.run(cmd, True, True)
|
||||
if result is None:
|
||||
VLogger.error("subprocess.run returned None", "link")
|
||||
return 1
|
||||
if result.returncode != 0:
|
||||
VLogger.error(f"link failed, return code: {result.returncode}", "link")
|
||||
VLogger.error(f"command: {cmd}", "link")
|
||||
# Show linker error output.
|
||||
# Note: subprocess merges stderr into stdout on Windows, so result.stderr is always None.
|
||||
if result.stdout is not None: VLogger.error(result.stdout, "link")
|
||||
else: VLogger.error("no output captured", "link")
|
||||
if result.stderr is not None: VLogger.error(result.stderr, "link")
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
def asm_to_obj(asm_path: str, sha1_val: str, output_dir: str, triple: str) -> int:
|
||||
"""Assemble a .s source into .obj with llvm-mc on the sliced path; 0 on success, non-zero on failure."""
|
||||
if asm_path is None or sha1_val is None or output_dir is None: return 1
|
||||
@@ -2893,47 +2800,6 @@ def WriteStackMap(mb: memhub.MemBuddy*, exe_path: str) -> int:
|
||||
return 0
|
||||
|
||||
|
||||
def run_pipeline(ir_buf: bytes, ir_len: sizet, temp_dir: str, output_dir: str, module_name: str, cc_cmd: str, cc_flags: str, linker_cmd: str, linker_flags: str, linker_output: str, includes_binary_dir: str, linker_static_libs: str = None, linker_dynamic_libs: str = None) -> BuildResult*:
|
||||
"""Run the full compile pipeline; returns a BuildResult object."""
|
||||
result: BuildResult* = BuildResult()
|
||||
if result is None: return None
|
||||
|
||||
# Step 0: ensure temp/output dirs exist (auto-create to avoid write failures)
|
||||
ensure_dir(temp_dir)
|
||||
ensure_dir(output_dir)
|
||||
|
||||
# Step 1: write the .ll file
|
||||
ret: int = write_ir_to_file(ir_buf, ir_len, temp_dir, module_name)
|
||||
if ret != 0:
|
||||
result.Success = 0
|
||||
result.ErrorMsg = "failed to write .ll file"
|
||||
return result
|
||||
|
||||
# Step 2: llc-compile .ll -> .obj.
|
||||
# build the sliced .ll file path
|
||||
ir_path: str = build_sliced_path(temp_dir, module_name, "ll")
|
||||
if ir_path is None:
|
||||
result.Success = 0
|
||||
result.ErrorMsg = "memory allocation failed"
|
||||
return result
|
||||
|
||||
ret = compile_ll_to_obj(ir_path, output_dir, module_name, cc_cmd, cc_flags)
|
||||
if ret != 0:
|
||||
result.Success = 0
|
||||
result.ErrorMsg = "llc compilation failed"
|
||||
return result
|
||||
|
||||
# Step 3: clang++ link .obj -> .exe (plus includes.binary prebuilt .obj and static/dynamic libs)
|
||||
ret = link_obj_to_exe(output_dir, module_name, linker_cmd, linker_flags, linker_output, includes_binary_dir, linker_static_libs, linker_dynamic_libs)
|
||||
if ret != 0:
|
||||
result.Success = 0
|
||||
result.ErrorMsg = "link failed"
|
||||
return result
|
||||
|
||||
result.Success = 1
|
||||
return result
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Runtime dependency deployment (project.vpj runtime.*)
|
||||
# After a successful link, copy the declared runtime deps next to the exe.
|
||||
|
||||
@@ -30,47 +30,6 @@ import lib.core.Gen.GenApi as GenApi
|
||||
import lib.core.Gen.DebugInfoGen as DebugInfoGen
|
||||
|
||||
|
||||
# _InjectedCreatesCycle - cycle check before auto-injecting a container module
|
||||
# (_list/_dict); a hit (e.g. memhub<->_list) skips injection to avoid a Phase A deadlock.
|
||||
def _InjectedCreatesCycle(entry: IncludesScanner.FileEntry*, mod_name: str) -> int:
|
||||
"""Return 1 if injecting mod_name into the current module would create a cycle"""
|
||||
if entry is None or entry.RelPath is None or mod_name is None: return 0
|
||||
inj_sha1: str = StubMergerStore.LookupSha1ByModName(mod_name)
|
||||
if inj_sha1 is None: return 0
|
||||
inj_deps: str = StubMergerStore.LookupIncludesDeps(inj_sha1)
|
||||
if inj_deps is None: return 0
|
||||
rp: str = entry.RelPath
|
||||
# Current module name = basename of RelPath (after the last '/' or '\'), without extension
|
||||
base: str = string.strrchr(rp, '/')
|
||||
if base is None: base = string.strrchr(rp, '\\')
|
||||
if base is not None: base = base + 1
|
||||
else: base = rp
|
||||
dot: str = string.strchr(base, '.')
|
||||
nm_buf: bytes = stdlib.malloc(128)
|
||||
if nm_buf is None: return 0
|
||||
if dot is not None:
|
||||
nm_len: sizet = sizet(uint64(dot) - uint64(base))
|
||||
if nm_len > 127: nm_len = 127
|
||||
string.strncpy(nm_buf, base, nm_len)
|
||||
nm_buf[nm_len] = '\0'
|
||||
else:
|
||||
string.strncpy(nm_buf, base, 127)
|
||||
nm_buf[127] = '\0'
|
||||
nm_len2: sizet = string.strlen(nm_buf)
|
||||
d_len: sizet = string.strlen(inj_deps)
|
||||
dp: sizet = 0
|
||||
while dp < d_len:
|
||||
ts: sizet = dp
|
||||
while dp < d_len and inj_deps[dp] != ' ': dp += 1
|
||||
tl: sizet = dp - ts
|
||||
if dp < d_len: dp += 1
|
||||
if tl > 0 and tl == nm_len2 and string.strncmp(inj_deps + ts, nm_buf, tl) == 0:
|
||||
stdlib.free(nm_buf)
|
||||
return 1
|
||||
stdlib.free(nm_buf)
|
||||
return 0
|
||||
|
||||
|
||||
def _DepsContainsName(deps: str, name: str) -> int:
|
||||
"""Return 1 if the space-separated deps string contains the name token exactly"""
|
||||
if deps is None or name is None: return 0
|
||||
@@ -332,36 +291,6 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: VLogg
|
||||
p1a_skp: int = 0
|
||||
p1a_fl: int = 0
|
||||
|
||||
# ============================================================
|
||||
# [temp diagnostic] Snapshot mb state if the early Phase1 flat buffer allocation fails.
|
||||
if mb is not None:
|
||||
# Run a cycle-detecting self_check first: a corrupted free list would loop forever in
|
||||
# dump_stats, so skip dump_stats when self_check returns 4 (cycle).
|
||||
p1_sc: int = mb.self_check()
|
||||
if p1_sc != 4: mb.dump_stats("phase1-pre")
|
||||
else: VLogger.info("[EVIDENCE] self_check=4 (free list cycle); skipping dump_stats to prevent an infinite loop", "Phase1")
|
||||
p1_mo: int = mb.max_order
|
||||
if p1_mo < 0 or p1_mo > 32:
|
||||
p1_mo = 32 # Clamp: prevents this probe from out-of-bounds reading free_lists if max_order is corrupted
|
||||
p1_fl_head: uint64 = uint64(mb.free_lists[p1_mo])
|
||||
p1_mb_addr: uint64 = uint64(mb)
|
||||
p1_base_addr: uint64 = uint64(mb.base)
|
||||
p1_usable_addr: uint64 = uint64(mb.usable)
|
||||
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_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 = 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)
|
||||
# 取消整数提升后,模块属性 DefineT 按最小位宽物化为 i16/i8;512*64=32768 恰好越出 i16 有符号上限而回绕成负值,
|
||||
# 首乘数显式 sizet() 加宽到 64 位。下面 def_sha1s_buf 的 512*17=8704 未溢出,无需改。
|
||||
|
||||
@@ -285,130 +285,3 @@ def _FindClassDefSha1(class_names_buf: bytes, def_sha1s_buf: bytes, 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
|
||||
# _ScanTreeForContainers - recursively scan the AST for list/dict container usage:
|
||||
# recurse expression/annotation struct fields first, then statement-block children.
|
||||
|
||||
def _AnnIsContainer(ann: ast.AST*) -> int:
|
||||
"""Return 1 if the annotation is a list/dict container, else 0."""
|
||||
while ann is not None:
|
||||
ak_c: int = ann.kind()
|
||||
if ak_c == ast.ASTKind.Name:
|
||||
nm_c: ast.Name* = ast.Name*(ann)
|
||||
if nm_c.id is not None:
|
||||
if string.strcmp(nm_c.id, "list") == 0: return 1
|
||||
if string.strcmp(nm_c.id, "dict") == 0: return 1
|
||||
return 0
|
||||
elif ak_c == ast.ASTKind.Subscript:
|
||||
sub_c: ast.Subscript* = ast.Subscript*(ann)
|
||||
ann = sub_c.value
|
||||
else: return 0
|
||||
return 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.
|
||||
if depth > 128: VLogger.error("container scan recursion too deep (AST cycle or nesting over 128 levels)", "Phase1")
|
||||
if node is None: return
|
||||
k_s: int = node.kind()
|
||||
if k_s == ast.ASTKind.Name:
|
||||
nm_s: ast.Name* = ast.Name*(node)
|
||||
if nm_s.id is not None:
|
||||
if string.strcmp(nm_s.id, "list") == 0: need_list_box[0] = 1
|
||||
elif string.strcmp(nm_s.id, "dict") == 0: need_dict_box[0] = 1
|
||||
elif k_s == ast.ASTKind.Subscript:
|
||||
sub_s: ast.Subscript* = ast.Subscript*(node)
|
||||
_ScanTreeForContainers(sub_s.value, need_list_box, need_dict_box, depth + 1)
|
||||
_ScanTreeForContainers(sub_s.slice, need_list_box, need_dict_box, depth + 1)
|
||||
elif k_s == ast.ASTKind.BinOp:
|
||||
bop_s: ast.BinOp* = ast.BinOp*(node)
|
||||
_ScanTreeForContainers(bop_s.left, need_list_box, need_dict_box, depth + 1)
|
||||
_ScanTreeForContainers(bop_s.right, need_list_box, need_dict_box, depth + 1)
|
||||
elif k_s == ast.ASTKind.Call:
|
||||
cl_s2: ast.Call* = ast.Call*(node)
|
||||
_ScanTreeForContainers(cl_s2.func, need_list_box, need_dict_box, depth + 1)
|
||||
cl_args_s: list[ast.AST*]* = cl_s2.args
|
||||
if cl_args_s is not None:
|
||||
na_s: sizet = len(cl_args_s)
|
||||
i_a2: sizet
|
||||
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(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:
|
||||
asn_s: ast.Assign* = ast.Assign*(node)
|
||||
_ScanTreeForContainers(asn_s.value, need_list_box, need_dict_box, depth + 1)
|
||||
tgts_s: list[ast.AST*]* = asn_s.targets
|
||||
if tgts_s is not None:
|
||||
nt_s: sizet = len(tgts_s)
|
||||
i_t2: sizet
|
||||
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)
|
||||
_ScanTreeForContainers(aa_s2.value, need_list_box, need_dict_box, depth + 1)
|
||||
elif k_s == ast.ASTKind.FunctionDef:
|
||||
fn_s: ast.FunctionDef* = ast.FunctionDef*(node)
|
||||
_ScanTreeForContainers(fn_s.returns, need_list_box, need_dict_box, depth + 1)
|
||||
fn_args_s: ast.Arguments* = ast.Arguments*(fn_s.args)
|
||||
if fn_args_s is not None:
|
||||
fa_s: list[ast.AST*]* = fn_args_s.args
|
||||
if fa_s is not None:
|
||||
nf_s: sizet = len(fa_s)
|
||||
i_f2: sizet
|
||||
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:
|
||||
cd_s: ast.ClassDef* = ast.ClassDef*(node)
|
||||
bases_s: list[ast.AST*]* = cd_s.bases
|
||||
if bases_s is not None:
|
||||
nb_s: sizet = len(bases_s)
|
||||
i_b2: sizet
|
||||
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)
|
||||
elif k_s == ast.ASTKind.Tuple:
|
||||
tu_s: ast.Tuple* = ast.Tuple*(node)
|
||||
elts_s: list[ast.AST*]* = tu_s.elts
|
||||
if elts_s is not None:
|
||||
ne_s: sizet = len(elts_s)
|
||||
i_e2: sizet
|
||||
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)
|
||||
elif k_s == ast.ASTKind.Expr:
|
||||
ex_s: ast.Expr* = ast.Expr*(node)
|
||||
_ScanTreeForContainers(ex_s.value, need_list_box, need_dict_box, depth + 1)
|
||||
elif k_s == ast.ASTKind.Return:
|
||||
rt_s: ast.Return* = ast.Return*(node)
|
||||
_ScanTreeForContainers(rt_s.value, need_list_box, need_dict_box, depth + 1)
|
||||
elif k_s == ast.ASTKind.Compare:
|
||||
cp_s: ast.Compare* = ast.Compare*(node)
|
||||
_ScanTreeForContainers(cp_s.left, need_list_box, need_dict_box, depth + 1)
|
||||
cps_s: list[ast.AST*]* = cp_s.comparators
|
||||
if cps_s is not None:
|
||||
nc_s: sizet = len(cps_s)
|
||||
i_c2: sizet
|
||||
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(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(int(n_s)): _ScanTreeForContainers(ch_s.get(ci_s), need_list_box, need_dict_box, depth + 1)
|
||||
@@ -39,7 +39,6 @@ _REPL_STUB_NOCOUNT_OPAQUE: int = -2 # key is OPAQUE in meta (no body)
|
||||
define _REPL_SU_NOSTUB = 1 # missing data: module has no {sha1}.meta
|
||||
define _REPL_SU_NOCLASS = 2 # refused: meta exists but has no STRUCT line for this class
|
||||
define _REPL_SU_OPAQUE = 3 # refused: class is OPAQUE in meta (no body)
|
||||
define _REPL_SU_PARENT = 4 # upstream no data: base layout unavailable/not registered (goes to _repl_stub_warn_parent)
|
||||
|
||||
# ReplStubClass.Flags / MethodFlags bits
|
||||
define _REPL_SF_CVTABLE = 1
|
||||
@@ -572,8 +571,8 @@ def _cache_vpi_return_annots_text(vpi_buf: bytes, src_sha1: str) -> int:
|
||||
break
|
||||
rpos += 1
|
||||
if dend > dstart:
|
||||
# Return type: extract "-> Ret" annotation; unannotated non-special
|
||||
# methods default to "i32" (matches HandlesType.infer_return_type).
|
||||
# Return type: extract the "-> Ret" annotation; unannotated
|
||||
# non-special methods default to i32.
|
||||
ret_src: str = None
|
||||
rtype_len: sizet = 0
|
||||
if arrow_pos < le:
|
||||
|
||||
@@ -64,19 +64,6 @@ 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 ViperC's own lib/* stubs.
|
||||
def _load_includes_sha1_set(pool: memhub.MemBuddy*, temp_dir: str, sha1_set: str) -> int:
|
||||
"""Copy includes SHA1s from the global store into sha1_set; returns the count, -1 on error."""
|
||||
if sha1_set is None: return -1
|
||||
if StubMerger._sha1_store_arr is None: return -1
|
||||
count: int = 0
|
||||
for i in range(int(StubMerger._sha1_store_count)):
|
||||
if count >= StubMerger.MAX_INCLUDES_SHA1: break
|
||||
idx: sizet = sizet(i) * 17
|
||||
string.strcpy(sha1_set + sizet(count) * 17, StubMerger._sha1_store_arr + idx)
|
||||
count += 1
|
||||
return count
|
||||
# _is_in_sha1_set - check whether a SHA1 is in the set.
|
||||
def _is_in_sha1_set(sha1: str, sha1_set: str, set_count: int) -> int:
|
||||
"""Whether sha1 is present in the set."""
|
||||
|
||||
@@ -166,23 +166,6 @@ def IsDeclOnlyModule(temp_dir: str, td_len: sizet, sha1: str) -> int:
|
||||
if h < 0: return -1
|
||||
if h == 1: return 0
|
||||
return 1
|
||||
# _is_submodule_of_imported - check whether a module name is a submodule of an imported package.
|
||||
# deps_buf holds only direct imports; a name with '.' is matched by its top-level package (e.g. "ast.lexer" -> 1).
|
||||
def _is_submodule_of_imported(deps_buf: str, mod_name: str) -> int:
|
||||
"""Whether mod_name is a submodule of a package imported in deps_buf."""
|
||||
if deps_buf is None or mod_name is None: return 0
|
||||
# Find the first '.' and extract the top-level package name
|
||||
dot_pos: str = string.strchr(mod_name, 46) # '.' = 46
|
||||
if dot_pos is None: return 0
|
||||
pkg_len: sizet = sizet(uint64(dot_pos) - uint64(mod_name))
|
||||
if pkg_len == 0 or pkg_len >= 256: return 0
|
||||
# Build the top-level package name (truncated before '.') by temporary in-place
|
||||
# truncation to avoid an allocation
|
||||
saved_ch: char = mod_name[pkg_len]
|
||||
mod_name[pkg_len] = '\0'
|
||||
result: int = GenApi.is_module_imported(deps_buf, mod_name)
|
||||
mod_name[pkg_len] = saved_ch
|
||||
return result
|
||||
# _BuildIncludesSha1Map - build the sha1 -> module_name map from the global in-memory store.
|
||||
# Source is the store filled by PopulateSha1MapStore (no _sha1_map.txt read); returns the entry count.
|
||||
def _BuildIncludesSha1Map(temp_dir: str, td_len: sizet, sha1_list: str, mod_list: str) -> int:
|
||||
|
||||
@@ -532,9 +532,6 @@ def LookupIncludesDeps(sha1: str) -> str:
|
||||
def GetSha1StoreArrPtr() -> bytes:
|
||||
"""Return the SHA1 array pointer (17 bytes per entry)"""
|
||||
return StubMerger._sha1_store_arr
|
||||
def GetSha1StoreModArrPtr() -> bytes:
|
||||
"""Return the module-name array pointer (64 bytes per entry)"""
|
||||
return StubMerger._sha1_store_mod
|
||||
def GetSha1StoreRelArrPtr() -> bytes:
|
||||
"""Return the rel-path array pointer (StubMerger.MAX_REL_PATH_LEN bytes per entry)"""
|
||||
return StubMerger._sha1_store_rel
|
||||
@@ -542,20 +539,6 @@ def GetSha1StoreIncdirArrPtr() -> bytes:
|
||||
"""Return the owning includes-root array pointer (empty string = not in any includes dir)"""
|
||||
return StubMerger._sha1_store_incdir
|
||||
# ============================================================
|
||||
# GetSha1StoreArr - return the store's sha1/mod/rel array pointers and entry count; do not free.
|
||||
def GetSha1StoreArr(out_sha1_arr: void**, out_mod_arr: void**, out_rel_arr: void**) -> int:
|
||||
"""Get the global store array pointers and entry count; returns -1 on failure"""
|
||||
if out_sha1_arr is None or out_mod_arr is None or out_rel_arr is None: return -1
|
||||
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None or StubMerger._sha1_store_rel is None: return -1
|
||||
sp: void** = out_sha1_arr
|
||||
if sp is not None: sp[0] = StubMerger._sha1_store_arr
|
||||
mp: void** = out_mod_arr
|
||||
if mp is not None: mp[0] = StubMerger._sha1_store_mod
|
||||
rp: void** = out_rel_arr
|
||||
if rp is not None: rp[0] = StubMerger._sha1_store_rel
|
||||
return StubMerger._sha1_store_count
|
||||
|
||||
# ============================================================
|
||||
# LookupSha1RelPath - return the read-only rel_path pointer for a SHA1 (no free), None if missing.
|
||||
def LookupSha1RelPath(sha1: str) -> str:
|
||||
"""Return the read-only rel-path pointer for a SHA1, or None if missing"""
|
||||
|
||||
@@ -22,8 +22,6 @@ novirtual class AssignGenMixin:
|
||||
Gen: Generator.Generator*
|
||||
"""Assignment translation mixin."""
|
||||
|
||||
RecoverFound: int
|
||||
|
||||
# Check whether an annotation is the DefineT marker (module-level function)
|
||||
def is_cdefine_annotation(self, annot: ast.AST*) -> int:
|
||||
"""Return 1 if the annotation is the DefineT marker (bare or DefineT[T]), else 0."""
|
||||
@@ -315,20 +313,6 @@ novirtual class AssignGenMixin:
|
||||
if uint64(v) >= uint64(lhs_bits):
|
||||
self.Gen.TypeGenErr.fatal_error(node, f"[VPC] shift count out of range: the count {v} is >= the left operand width {lhs_bits} bits; " f"the result would be 0 or undefined (not a value the compiler may guess). " f"Fix: use a count < {lhs_bits}, or widen the left operand.", "ValueError")
|
||||
|
||||
def _is_cdefine_marker_node(self, node: ast.AST*) -> int:
|
||||
"""Return 1 if the node is the DefineT marker node (bare or DefineT[T])."""
|
||||
if node is None: return 0
|
||||
k: int = node.kind()
|
||||
if k == ast.ASTKind.Name:
|
||||
nm: ast.Name* = ast.Name*(node)
|
||||
if nm.id is not None and string.strcmp(nm.id, "DefineT") == 0: return 1
|
||||
if k == ast.ASTKind.Subscript:
|
||||
sub_cm: ast.Subscript* = ast.Subscript*(node)
|
||||
if sub_cm.value is not None and sub_cm.value.kind() == ast.ASTKind.Name:
|
||||
mk_cm: ast.Name* = ast.Name*(sub_cm.value)
|
||||
if mk_cm.id is not None and string.strcmp(mk_cm.id, "DefineT") == 0: return 1
|
||||
return 0
|
||||
|
||||
def _define_explicit_annot(self, annot: ast.AST*) -> ast.AST*:
|
||||
"""Return the explicit type T of a `DefineT[T]` annotation, or None."""
|
||||
if annot is None: return None
|
||||
@@ -962,13 +946,8 @@ novirtual class AssignGenMixin:
|
||||
|
||||
# Recover a DefineT float value by re-reading its source line with atof
|
||||
|
||||
def _recover_found(self) -> int:
|
||||
"""Return whether the last _recover_cdefine_float succeeded."""
|
||||
return self.RecoverFound
|
||||
|
||||
def _recover_float_literal(self, name: str, out: double*, require_cdefine: int) -> int:
|
||||
"""Recover a float literal from the module source into out[0]; 1 on success."""
|
||||
self.RecoverFound = 0
|
||||
fp: str = self.Gen.TypeGenResolve.get_current_file()
|
||||
if fp is None or name is None or out is None: return 0
|
||||
f: io.File* = io.File(fp, io.MODE.R)
|
||||
@@ -1061,7 +1040,6 @@ novirtual class AssignGenMixin:
|
||||
stdlib.free(buf)
|
||||
# Use atof_out (an out param) to avoid the cross-module double return bug
|
||||
if string.atof_out(val_txt, out) != 0:
|
||||
self.RecoverFound = 1
|
||||
return 1
|
||||
return 0
|
||||
|
||||
@@ -1966,19 +1944,6 @@ novirtual class AssignGenMixin:
|
||||
# Unknown class name => not a scalar, use an 8-byte pointer slot
|
||||
return 0
|
||||
|
||||
def _is_generic_tp_or_nested(self, node: ast.AST*) -> int:
|
||||
"""Return 1 for a generic type-param name or nested union (BinOp)."""
|
||||
if node is None: return 0
|
||||
nk: int = node.kind()
|
||||
if nk == ast.ASTKind.BinOp: return 1
|
||||
if nk == ast.ASTKind.Name:
|
||||
nm_tp: ast.Name* = ast.Name*(node)
|
||||
if nm_tp.id is None: return 0
|
||||
tp_len: sizet = string.strlen(nm_tp.id)
|
||||
if tp_len == 1:
|
||||
if nm_tp.id[0] >= 65 and nm_tp.id[0] <= 90: return 1
|
||||
return 0
|
||||
|
||||
# _init_global_array_from_list - initialize a global array from a list literal
|
||||
def _init_global_array_from_list(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, array_alloca: mlirlite.Value*, list_node: ast.AST*, trans: HT.Translator*, owner_ctx: str = None, byte_order: int = 0, arr_ty_opt: mlirlite.LLVMType* = None) -> int:
|
||||
"""Initialize a global array from a list literal via per-element GEP+store.
|
||||
@@ -4320,5 +4285,4 @@ novirtual class AssignGenMixin:
|
||||
def InitAssignGen(self) -> int:
|
||||
"""Initialize AssignGen fields (called by Generator at construction)."""
|
||||
if self is None: return 1
|
||||
self.RecoverFound = 0
|
||||
return 0
|
||||
@@ -1636,39 +1636,3 @@ novirtual class CallGenLeafKit(CallGenCBuiltin.CallGenCBuiltin):
|
||||
if fn_nm_o is not None:
|
||||
if string.strncmp(fn_nm_o, owner_sha1, 16) == 0 and fn_nm_o[16] == '.': return entry
|
||||
return None
|
||||
|
||||
|
||||
def NewExprCallHandle(self, pool: memhub.MemBuddy*, trans: HT.Translator*) -> ExprCallHandle*:
|
||||
h: ExprCallHandle* = pool.alloc(ExprCallHandle.__sizeof__())
|
||||
if h is None: return None
|
||||
string.memset(h, 0, ExprCallHandle.__sizeof__())
|
||||
h.Trans = trans
|
||||
return h
|
||||
|
||||
|
||||
# ============================================================
|
||||
# ExprCallHandle - function-call handler (moved from HandlesExprCall); delegates to GenApi.
|
||||
novirtual class ExprCallHandle(HT.Mixin):
|
||||
"""Function-call handler: inherits the mixin to get the Trans back-pointer"""
|
||||
|
||||
def __init__(self, trans: HT.Translator*):
|
||||
self.Trans = trans
|
||||
|
||||
# ============================================================
|
||||
# HandleCall - translate a function call, returning the call result Value
|
||||
def HandleCall(self, node: ast.AST*) -> mlirlite.Value*:
|
||||
"""Translate a function call using shared state from self.Trans"""
|
||||
return GenApi.translate_call(self.Trans.Pool, self.Trans._cur_builder, self.Trans.Module, node, self.Trans._funcs, self.Trans._func_count, self.Trans)
|
||||
|
||||
# ============================================================
|
||||
# HandlePrintfCall - translate a printf call
|
||||
def HandlePrintfCall(self, cl: ast.Call*):
|
||||
"""Translate a printf call using shared state from self.Trans"""
|
||||
GenApi.translate_printf_call(self.Trans.Pool, self.Trans._cur_builder, self.Trans.Module, cl, self.Trans)
|
||||
|
||||
# ============================================================
|
||||
# HandlePrintCall - translate a print call
|
||||
def HandlePrintCall(self, cl: ast.Call*):
|
||||
"""Translate a print call (type-aware formatting + trailing newline)"""
|
||||
GenApi.translate_print_call(self.Trans.Pool, self.Trans._cur_builder, self.Trans.Module, cl, self.Trans)
|
||||
|
||||
@@ -1515,8 +1515,8 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
|
||||
# Only real modules reach this path; a missing module segment falls back to a bare name.
|
||||
if xmod_path is None: call_name = func_name
|
||||
else:
|
||||
# Check whether it is an imported module name (e.g. BuildPipeline.run_pipeline): if imported
|
||||
# but its SHA1 is unregistered the module was not compiled, so error out instead of a misleading from_imports lookup.
|
||||
# If this is an imported module name whose SHA1 is unregistered, the module was
|
||||
# not compiled: error out instead of doing a misleading from_imports lookup.
|
||||
is_imported_mod: int = 0
|
||||
if trans is not None and trans._imported_modules is not None: is_imported_mod = self.Gen.ImportsGen.is_module_imported(trans._imported_modules, xmod_path)
|
||||
if is_imported_mod != 0:
|
||||
|
||||
@@ -25,9 +25,6 @@ define EXCEPTION_MAX = 64
|
||||
define EXCEPTION_NAME_LEN = 128
|
||||
# Builtin exception codes (mirrors TPC EXCEPTION_CODE_MAP); user classes start at 100.
|
||||
# KeyError=6 IOError=7 OSError=8 AssertionError=9 Exception=99.
|
||||
define BUILTIN_EH_VALUE_ERROR = 1
|
||||
define BUILTIN_EH_RUNTIME_ERROR = 3
|
||||
define BUILTIN_EH_EXCEPTION = 99
|
||||
define EH_USER_CODE_START = 100
|
||||
# Method metadata table sizes (formerly HandlesClassDef.META_MAX/META_KEY_LEN).
|
||||
define META_MAX = 512
|
||||
@@ -425,12 +422,6 @@ novirtual class ClassGenBase:
|
||||
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(int(self.DecoratorFuncCount)):
|
||||
if string.strcmp(self.DecoratorFuncNames + i * DECOR_NAME_LEN, func_name) == 0: return self.DecoratorArgs[i]
|
||||
return 0
|
||||
# ============================================================
|
||||
# Exception class registry for try/except (mirrors TPC HandlesClassDef).
|
||||
def _ensure_exception_tables(self) -> int:
|
||||
@@ -490,15 +481,6 @@ novirtual class ClassGenBase:
|
||||
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(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
|
||||
return None
|
||||
return None
|
||||
def is_exception_name(self, name: str) -> int:
|
||||
"""Return 1 if name is a builtin or user exception class name."""
|
||||
if name is None: return 0
|
||||
|
||||
@@ -93,19 +93,6 @@ novirtual class ClassGenEnum(ClassGenBase.ClassGenBase):
|
||||
# fail-fast: unknown annotation node kind; no silent i32 fallback
|
||||
self.Gen.TypeGenErr.fatal_error(ann_node, f"failed to resolve enum member type annotation: {self.Gen.TypeGenResolve.annotation_to_str(ann_node)}", "TypeError")
|
||||
return mlirlite.Int32(pool) # unreachable (fatal_error already exited)
|
||||
def _is_state_annotation(self, node: ast.AST*) -> int:
|
||||
"""Check whether an annotation node is t.State, 1=yes / 0=no"""
|
||||
if node is None: return 0
|
||||
k: int = node.kind()
|
||||
if k == ast.ASTKind.Attribute:
|
||||
at: ast.Attribute* = ast.Attribute*(node)
|
||||
if at.attr is not None and string.strcmp(at.attr, "State") == 0: return 1
|
||||
return 0
|
||||
if k == ast.ASTKind.Name:
|
||||
nm: ast.Name* = ast.Name*(node)
|
||||
if nm.id is not None and string.strcmp(nm.id, "State") == 0: return 1
|
||||
return 0
|
||||
return 0
|
||||
def _enum_annotation_name_is_ptr(self, node: ast.AST*) -> int:
|
||||
"""Whether the annotation's top name (Name/Attribute/Subscript) is a pointer form, 1=yes / 0=no"""
|
||||
if node is None: return 0
|
||||
|
||||
@@ -104,13 +104,6 @@ novirtual class ClassGenGeneric(ClassGenBase.ClassGenBase):
|
||||
string.strcat(buf, tp_name)
|
||||
return buf
|
||||
|
||||
# Generic function templates (PEP 695 def f[T](...)): mirrors the generic class path; keys include
|
||||
# the defining module SHA1, call sites infer args, and specialization emits a define (or a declare).
|
||||
def _is_generic_function(self, tp: list[str]*) -> int:
|
||||
"""Return 1 when the function type_params mark a generic function; 0 otherwise."""
|
||||
if tp is None: return 0
|
||||
return 1
|
||||
|
||||
def _register_generic_func_template(self, pool: memhub.MemBuddy*, fd: ast.FunctionDef*, module_sha1: str) -> None:
|
||||
"""Register a generic function template keyed by "{module_sha1}.{name}"."""
|
||||
if fd is None or fd.name is None or module_sha1 is None: return
|
||||
|
||||
@@ -41,7 +41,7 @@ _g_loc_ref: str = None
|
||||
_g_loc_used: bytes = None
|
||||
|
||||
|
||||
# SetEnabled / IsEnabled - mirror the --traceback switch (flag -> _g_enabled, forwarded to mlirlite's debug call-site recorder).
|
||||
# SetEnabled - mirror the --traceback switch (flag -> _g_enabled, forwarded to mlirlite's debug call-site recorder).
|
||||
def SetEnabled(flag: bool) -> int:
|
||||
"""Enable/disable DI emission (mirrors BuildPipeline._traceback_mode)."""
|
||||
global _g_enabled
|
||||
@@ -52,10 +52,6 @@ def SetEnabled(flag: bool) -> int:
|
||||
return 0
|
||||
|
||||
|
||||
def IsEnabled() -> int:
|
||||
return _g_enabled
|
||||
|
||||
|
||||
# _EnsureStorage - lazily allocate the long-lived stack / cache buffers.
|
||||
def _EnsureStorage() -> int:
|
||||
global _g_stk_sp, _g_stk_ref, _g_stk_bld, _g_loc_key, _g_loc_ref, _g_loc_used
|
||||
|
||||
@@ -358,13 +358,6 @@ def get_struct_count() -> int:
|
||||
return 0
|
||||
|
||||
|
||||
# ---------------- reset_module_methods_emitted (delegates to StructGenMixin.reset_module_methods_emitted) ----------------
|
||||
def reset_module_methods_emitted(sha1: str) -> int:
|
||||
"""Delegate to gen.reset_module_methods_emitted"""
|
||||
if _g_gen is not None: return _g_gen.StructGen.reset_module_methods_emitted(sha1)
|
||||
return 0
|
||||
|
||||
|
||||
# ---------------- get_struct_type (delegates to StructGenMixin.get_struct_type) ----------------
|
||||
def get_struct_type(class_name: str) -> mlirlite.LLVMType*:
|
||||
"""Delegate to gen.get_struct_type"""
|
||||
@@ -470,12 +463,6 @@ def get_param_escapes(name: str) -> int:
|
||||
return 0
|
||||
|
||||
|
||||
def get_ret_borrow(name: str) -> int:
|
||||
"""返回借用标记(1=该函数直接返回某形参);无记录返回 0"""
|
||||
if _g_gen is not None: return _g_gen.StructGen.get_ret_borrow(name)
|
||||
return 0
|
||||
|
||||
|
||||
def get_param_escapes_method(pool: memhub.MemBuddy*, class_name: str, method_name: str) -> int:
|
||||
"""方法形参逃逸掩码:查 "Class.method" 并沿 MRO 回退;无记录返回 0"""
|
||||
if _g_gen is not None: return _g_gen.StructGen.get_param_escapes_method(pool, class_name, method_name)
|
||||
@@ -903,12 +890,6 @@ def build_const_env(cargs: list[ast.AST*]*, can: sizet, param_base: int) -> int:
|
||||
return 0
|
||||
|
||||
|
||||
def apply_escape_clears(packed: sizet*, n: int, mask: int) -> int:
|
||||
"""按常量环境套用守卫记录(清位);返回精化后的掩码"""
|
||||
if _g_gen is not None: return _g_gen.StructGen.apply_escape_clears(packed, n, mask)
|
||||
return mask
|
||||
|
||||
|
||||
def const_env_ptr() -> sizet*:
|
||||
"""取常量环境区指针(`build_const_env` 装填的那块,2 槽/形参);None = 不可用"""
|
||||
if _g_gen is not None: return _g_gen.StructGen.const_env_ptr()
|
||||
@@ -1113,13 +1094,6 @@ def fail_if_stack_addr_escape(node: ast.AST*, where: str, trans: HT.Translator*,
|
||||
|
||||
|
||||
|
||||
# ---------------- create_wrapper_main (delegates to TranslatorGenMixin.create_wrapper_main) ----------------
|
||||
def create_wrapper_main(trans: HT.Translator*, tree: ast.AST*, i32_ty: mlirlite.LLVMType*) -> int:
|
||||
"""Delegate to gen.create_wrapper_main"""
|
||||
if _g_gen is not None: return _g_gen.TranslatorGen.create_wrapper_main(trans, tree, i32_ty)
|
||||
return 0
|
||||
|
||||
|
||||
# ---------------- handle_module_level_var (delegates to TranslatorGenMixin.handle_module_level_var) ----------------
|
||||
def handle_module_level_var(trans: HT.Translator*, node: ast.AST*) -> int:
|
||||
"""Delegate to gen.handle_module_level_var"""
|
||||
|
||||
@@ -154,8 +154,8 @@ novirtual class StructEntry:
|
||||
ModuleSha1: str # Module SHA1 defining this class (None=no SHA1)
|
||||
MethodsEmitted: int # 1=method bodies translated / 0=not (Phase 1a mark_only; Phase B fills in)
|
||||
SpecTriggerSha1: str # Trigger-file SHA1 for a generic specialization (None=not specialized).
|
||||
# ModuleSha1 is the template sha1; reset_module_methods_emitted resets
|
||||
# MethodsEmitted by trigger sha1 to avoid leftover 1 / undefined symbol.
|
||||
# ModuleSha1 is the template sha1; MethodsEmitted is reset by trigger sha1
|
||||
# to avoid a leftover 1 / undefined symbol.
|
||||
IsForwardDecl: int # 1=forward-declaration placeholder (Ty is opaque named struct, FieldCount=0).
|
||||
# The definition side later fills the same Ty in place and clears this flag.
|
||||
|
||||
@@ -207,9 +207,8 @@ novirtual class FuncEntry:
|
||||
RetSign: int
|
||||
|
||||
|
||||
# FuncEntry.RetSign encoding. 0 must be "unset" because FuncEntry arrays are zero-initialized;
|
||||
# FuncEntry.RetSign encoding. 0 is "unset" because FuncEntry arrays are zero-initialized;
|
||||
# TSpec.TS_SIGN_UNSIGNED is 0, so the mapping is explicit.
|
||||
define FUNC_RET_SIGN_UNSET = 0
|
||||
define FUNC_RET_SIGN_UNSIGNED = 1
|
||||
define FUNC_RET_SIGN_SIGNED = 2
|
||||
|
||||
@@ -243,8 +242,6 @@ define REPL_VAR_CAP = 64 # Max session variables (var area = 6
|
||||
define REPL_FUNC_BASE = 1088 # = REPL_SLOT_BASE + REPL_VAR_CAP*REPL_SLOT_SIZE
|
||||
define REPL_FUNC_SLOT_SIZE = 16 # Bytes per session function (stride)
|
||||
define REPL_FUNC_CAP = 32 # Max session functions (func area = 32*16 = 512 bytes)
|
||||
# Total ctx bytes = 64 + 64*16 + 32*16 = 1600 (host Repl._REPL_CTX_SIZE must match)
|
||||
define REPL_CTX_SIZE = 1600
|
||||
# Max session function params (fixed-size signature token array; overflow errors at registration)
|
||||
define REPL_FUNC_PARAM_MAX = 8
|
||||
# Fragment entry function name prefix (Repl.vp generates "__repl_frag_%d"); distinguishes
|
||||
@@ -258,8 +255,6 @@ define REPL_SESSION_SHA1 = "71f67b7cb405f0e4"
|
||||
define REPL_RES_KIND_OFF = 8 # i32
|
||||
define REPL_RES_A_OFF = 16 # u64
|
||||
define REPL_RES_B_OFF = 24 # u64
|
||||
define REPL_RES_VARCOUNT_OFF = 32 # i32
|
||||
define REPL_RES_LASTFRAG_OFF = 36 # i32
|
||||
|
||||
# Result kind (the host echoes accordingly)
|
||||
define REPL_RES_NONE = 0 # No result for this fragment (e.g. declaration only)
|
||||
|
||||
@@ -250,11 +250,6 @@ novirtual class StmtGenDeclMixin(StmtGenCifMixin.StmtGenCifMixin):
|
||||
mlirlite.position_at_end(builder, dead_bb)
|
||||
return 0
|
||||
|
||||
def get_stmt_kind_name(self, node: ast.AST*) -> str:
|
||||
"""Return the statement type name"""
|
||||
if node is None: return None
|
||||
return node.type_name()
|
||||
|
||||
# ============================================================
|
||||
# Cluster D: nonlocal/closure env access (migrated from HandlesNonlocal.vp)
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ import lib.core.Gen.GenApi as GenApi
|
||||
import lib.core.Gen.Translator as HT
|
||||
|
||||
# StructGenMixin - struct registration / field lookup mixin.
|
||||
# Registers and looks up structs (name/type indexes), finds fields, and matches cross-module ownership; @t.NoVTable uses static binding (MRO calls + this adjustment, no vtable cost).
|
||||
# Registers and looks up structs by name, finds fields, and matches cross-module ownership; @t.NoVTable uses static binding (MRO calls + this adjustment, no vtable cost).
|
||||
|
||||
|
||||
# Constants (kept in sync with HandlesStruct).
|
||||
@@ -25,12 +25,11 @@ define STRUCT_MAX = 512
|
||||
# ── 跨过程常量特化(P3 收尾)的 scratch 槽位布局 ──
|
||||
# 一块定长 sizet 数组,只分配一次;调用点用**常量实参**把被调者体重算一遍逃逸摘要。
|
||||
# [0] global 名计数(gset,与 EscScratch 口径一致)
|
||||
# [1..EC_GLOBAL_MAX] global 名哈希
|
||||
# [1..64] global 名哈希
|
||||
# [EC_BOX_SLOT] / [EC_BOX_SLOT+1] box[0]=逃逸掩码、box[1]=返回借用位(compute_escape_summary 约定 box = gset+65 槽)
|
||||
# [EC_EVAL_SLOT] 表达式常量求值的临时槽(递归求值每层算完立刻拷出,见 _EvalConstInt)
|
||||
# [EC_ENV_SLOT..] 常量绑定环境:2 槽/形参([0]=是否已知、[1]=值),下标 = 形参位(self/cls 计入)
|
||||
# [EC_CAND_SLOT..] 守卫候选:2 槽/候选(形参位 + 常量值),供 _CollectGuardCandidates 收
|
||||
define EC_GLOBAL_MAX = 64
|
||||
define EC_BOX_SLOT = 65
|
||||
define EC_EVAL_SLOT = 246
|
||||
define EC_ENV_SLOT = 68
|
||||
@@ -48,7 +47,7 @@ define EC_SCRATCH_SLOTS = 256
|
||||
# 超出范围(形参 > 255 个、常量超出 int8)就不记——只影响精度,不影响正确性。
|
||||
define EC_PACK_K_MIN = -128
|
||||
define EC_PACK_K_MAX = 127
|
||||
# Name | type index slot count (power of two and > 2*STRUCT_MAX so load factor < 0.5 and probing terminates).
|
||||
# Name-index slot count (power of two and > 2*STRUCT_MAX so load factor < 0.5 and probing terminates).
|
||||
define STRUCT_IDX_CAP = 1024
|
||||
# Field-slot upper limit (kept in sync with HandlesStruct): Generator flattens ~120 fields across 43 mixins, so a limit of 64 would drop 9th+ parent fields and break __init__ attribute assignment.
|
||||
define FIELD_MAX = 256
|
||||
@@ -63,8 +62,7 @@ define FNV32_PRIME = 16777619
|
||||
define FNV32_MASK = 4294967295
|
||||
|
||||
|
||||
# Index hash functions (name | Ty pointer value -> 64-bit hash).
|
||||
# Used by StructTable's "name | Ty pointer value -> first registration index" index probing (see _struct_name_idx_insert / _struct_name_idx_find etc.).
|
||||
# Name-index hash function used by StructTable probing.
|
||||
def _struct_name_hash(name: str) -> uint64:
|
||||
"""Hash a name with 32-bit FNV-1a over bytes up to NUL; empty name returns the offset basis."""
|
||||
h: uint64 = uint64(FNV32_OFFSET)
|
||||
@@ -77,18 +75,6 @@ def _struct_name_hash(name: str) -> uint64:
|
||||
return h
|
||||
|
||||
|
||||
def _struct_ty_hash(tyval: uint64) -> uint64:
|
||||
"""Hash a Ty pointer value with 32-bit FNV-1a over its 8 bytes."""
|
||||
h: uint64 = uint64(FNV32_OFFSET)
|
||||
v: uint64 = tyval
|
||||
for j in range(8):
|
||||
h = (h ^ (v & uint64(255))) * uint64(FNV32_PRIME)
|
||||
h = h & uint64(FNV32_MASK)
|
||||
v = v >> 8
|
||||
return h
|
||||
|
||||
|
||||
|
||||
# StructGenMixin - struct registry / field lookup / visibility.
|
||||
novirtual class StructGenMixin:
|
||||
"""Mixin for struct registration and lookup."""
|
||||
@@ -133,9 +119,8 @@ novirtual class StructGenMixin:
|
||||
# 于是赋值被静默当成本地影子(现场:漏写 global → 全局仍 NULL → 下游 ASan 写 0x208 崩)。
|
||||
FileVarNames: hashtable.HashTable*
|
||||
|
||||
# --- Name | type index (fast lookup path; falls back to a linear scan when the index is untrusted) ---
|
||||
# --- Name index (fast lookup path; falls back to a linear scan when the index is untrusted) ---
|
||||
StructNameIdx: sizet* # name -> first registration index (u64 slot: 0 = empty, else index+1)
|
||||
StructTypeIdx: sizet* # Ty pointer value -> first registration index (same layout)
|
||||
StructIdxCap: int # index slot count (power of two, > 2*STRUCT_MAX)
|
||||
StructIdxBuilt: int # indexed entry count (-1 = untrusted; trusted only when == StructCount)
|
||||
|
||||
@@ -216,7 +201,6 @@ novirtual class StructGenMixin:
|
||||
self.EscDirCount = 0
|
||||
self.NllCurFunc = None
|
||||
self.StructNameIdx = None
|
||||
self.StructTypeIdx = None
|
||||
self.StructIdxCap = 0
|
||||
self.StructIdxBuilt = 0
|
||||
return 0
|
||||
@@ -588,12 +572,6 @@ novirtual class StructGenMixin:
|
||||
if self._EscLookup(name) >= 0: return 1
|
||||
return 0
|
||||
|
||||
def get_ret_borrow(self, name: str) -> int:
|
||||
"""返回借用标记:1=该函数直接返回某形参;0=无记录"""
|
||||
idx: int = self._EscLookup(name)
|
||||
if idx < 0: return 0
|
||||
return int(self.EscTable[idx * 3 + 2])
|
||||
|
||||
# ---- 声明式指令(@life(escape/noescape/ret)):调用点的**最终裁决**表 ----
|
||||
# 与 EscTable 分开存:EscTable 的掩码是"推断结果"(能被常量特化/同命裁剪改动),
|
||||
# 指令是"契约"(不能被任何推断推翻),调用点在所有推断之后最后套用一次。
|
||||
@@ -1428,24 +1406,6 @@ novirtual class StructGenMixin:
|
||||
if self.EscRefineScratch is None: return None
|
||||
return sizet*(uint64(self.EscRefineScratch) + EC_ENV_SLOT * 8)
|
||||
|
||||
def apply_escape_clears(self, packed: sizet*, n: int, mask: int) -> int:
|
||||
"""按调用点常量环境(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 = int(mask)
|
||||
i: int = 0
|
||||
while i < n:
|
||||
v: int = int(packed[i])
|
||||
i += 1
|
||||
if v <= 0: continue
|
||||
p: int = int(v & 255)
|
||||
kv: int = int((v >> 8) & 255)
|
||||
if kv >= 128: kv -= 256
|
||||
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
|
||||
|
||||
# ---- 循环归纳变量区间:`for i in range(<常量>)` 让 `if i == k` 这类比较能按区间判定 ----
|
||||
def _IsNameNode(self, node: ast.AST*, name: str) -> int:
|
||||
"""node 是名字且等于 name;1=是 / 0=否"""
|
||||
@@ -2167,21 +2127,17 @@ novirtual class StructGenMixin:
|
||||
if self.StructTable is None: return 0
|
||||
string.memset(self.StructTable, 0, entry_size * STRUCT_MAX)
|
||||
self.StructCount = 0
|
||||
# Build the name | type index (allocated from the same pool as the table, so they live and die together).
|
||||
# The index is an optional accelerator: on allocation failure keep None with StructIdxBuilt=-1 and lookups fall back to the original linear scan (identical semantics), so initialization does not fail.
|
||||
# Build the name index from the same pool as the table. Allocation failure only
|
||||
# disables the accelerator; lookups fall back to the linear scan.
|
||||
idx_bytes: sizet = sizet(STRUCT_IDX_CAP) * 8
|
||||
name_idx: sizet* = table_pool.alloc(idx_bytes)
|
||||
type_idx: sizet* = table_pool.alloc(idx_bytes)
|
||||
if name_idx is None or type_idx is None:
|
||||
if name_idx is None:
|
||||
self.StructNameIdx = None
|
||||
self.StructTypeIdx = None
|
||||
self.StructIdxCap = 0
|
||||
self.StructIdxBuilt = -1
|
||||
else:
|
||||
string.memset(name_idx, 0, idx_bytes)
|
||||
string.memset(type_idx, 0, idx_bytes)
|
||||
self.StructNameIdx = name_idx
|
||||
self.StructTypeIdx = type_idx
|
||||
self.StructIdxCap = STRUCT_IDX_CAP
|
||||
self.StructIdxBuilt = 0
|
||||
return 1
|
||||
@@ -2193,20 +2149,15 @@ novirtual class StructGenMixin:
|
||||
addr: uint64 = uint64(self.StructTable) + i * entry_size
|
||||
return GenTypes.StructEntry*(addr)
|
||||
|
||||
# Name | type index (open addressing with linear probing; slot stores index+1, 0 = empty).
|
||||
# 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.
|
||||
# Name index (open addressing with linear probing; slot stores index+1, 0 = empty).
|
||||
# Reduces name lookups from O(n) to expected O(1). The index is trusted only when
|
||||
# StructIdxBuilt == StructCount; otherwise lookups fall back to a linear scan.
|
||||
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(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(int(i))
|
||||
if e is None or e.Ty is None: return 0
|
||||
return uint64(e.Ty)
|
||||
|
||||
def _struct_name_idx_insert(self, ei: int, name: str) -> int:
|
||||
"""Insert (name, ei) into the name index; returns 0 on success, 1 on failure."""
|
||||
base: sizet* = self.StructNameIdx
|
||||
@@ -2222,29 +2173,13 @@ novirtual class StructGenMixin:
|
||||
idx = (idx + 1) & mask
|
||||
return 1
|
||||
|
||||
def _struct_type_idx_insert(self, ei: int, tyval: uint64) -> int:
|
||||
"""Insert (Ty pointer value, ei) into the type index; returns 0 on success, 1 on failure."""
|
||||
base: sizet* = self.StructTypeIdx
|
||||
if base is None or self.StructIdxCap <= 0 or tyval == 0: return 1
|
||||
h: uint64 = _struct_ty_hash(tyval)
|
||||
mask: sizet = sizet(self.StructIdxCap - 1)
|
||||
idx: sizet = sizet(h) & mask
|
||||
for i in range(int(self.StructIdxCap)):
|
||||
slot: sizet* = (sizet*)(uint64(base) + uint64(idx * 8))
|
||||
if slot[0] == 0:
|
||||
slot[0] = sizet(ei + 1)
|
||||
return 0
|
||||
idx = (idx + 1) & mask
|
||||
return 1
|
||||
|
||||
def _struct_idx_add(self, ei: int, entry: GenTypes.StructEntry*) -> None:
|
||||
"""Sync both indexes after adding an entry; mark the index untrusted on any write failure."""
|
||||
if self.StructNameIdx is None or self.StructTypeIdx is None or self.StructIdxCap <= 0:
|
||||
"""Sync the name index after adding an entry; mark it untrusted on write failure."""
|
||||
if self.StructNameIdx is None or self.StructIdxCap <= 0:
|
||||
self.StructIdxBuilt = -1
|
||||
return
|
||||
failed: int = 0
|
||||
if entry is not None and entry.Name is not None: failed = failed + self._struct_name_idx_insert(ei, entry.Name)
|
||||
if entry is not None and entry.Ty is not None: failed = failed + self._struct_type_idx_insert(ei, uint64(entry.Ty))
|
||||
if failed != 0: self.StructIdxBuilt = -1
|
||||
else:
|
||||
# ei = StructCount-1, so after syncing indexed entries == StructCount.
|
||||
@@ -2269,23 +2204,6 @@ novirtual class StructGenMixin:
|
||||
idx = (idx + 1) & mask
|
||||
return -1
|
||||
|
||||
def _struct_type_idx_find(self, tyval: uint64) -> int:
|
||||
"""Return the first registration index for a Ty value (>=0 hit), else -1 to fall back to a scan."""
|
||||
base: sizet* = self.StructTypeIdx
|
||||
if base is None or self.StructIdxCap <= 0 or tyval == 0: return -1
|
||||
if self.StructIdxBuilt != self.StructCount: return -1
|
||||
h: uint64 = _struct_ty_hash(tyval)
|
||||
mask: sizet = sizet(self.StructIdxCap - 1)
|
||||
idx: sizet = sizet(h) & mask
|
||||
for i in range(int(self.StructIdxCap)):
|
||||
slot: sizet* = (sizet*)(uint64(base) + uint64(idx * 8))
|
||||
sv: sizet = slot[0]
|
||||
if sv == 0: return -1
|
||||
se: int = int(sv) - 1
|
||||
if self._struct_entry_tyval(se) == tyval: return se
|
||||
idx = (idx + 1) & mask
|
||||
return -1
|
||||
|
||||
def _get_field_entry(self, struct_entry: GenTypes.StructEntry*, i: int) -> GenTypes.FieldEntry*:
|
||||
"""Return field entry i of a struct, or None when out of range."""
|
||||
if struct_entry is None or i < 0 or i >= FIELD_MAX: return None
|
||||
@@ -2326,7 +2244,8 @@ novirtual class StructGenMixin:
|
||||
entry.Name = name_buf
|
||||
entry.Ty = struct_ty
|
||||
entry.FieldCount = 0
|
||||
# Set SHA1 directly at registration (avoids set_struct_sha1 picking the wrong entry for cross-module same-named classes); SHA1 is also allocated per StdlibAlloc to survive 1a/1b and prevent a dangling strcmp.
|
||||
# Set SHA1 directly at registration so cross-module same-named classes stay unambiguous.
|
||||
# The copy uses the registration pool and survives 1a/1b resets.
|
||||
if sha1 is not None:
|
||||
sha1_len: sizet = string.strlen(sha1)
|
||||
sha1_buf: str = name_pool.alloc(sha1_len + 1)
|
||||
@@ -2335,7 +2254,7 @@ novirtual class StructGenMixin:
|
||||
entry.ModuleSha1 = sha1_buf
|
||||
|
||||
self.StructCount += 1
|
||||
# Sync the name | type index (new entry index = StructCount-1).
|
||||
# Sync the name index (new entry index = StructCount-1).
|
||||
self._struct_idx_add(self.StructCount - 1, entry)
|
||||
return entry
|
||||
|
||||
@@ -2416,27 +2335,10 @@ novirtual class StructGenMixin:
|
||||
entry.MethodsEmitted = 0
|
||||
entry.SpecTriggerSha1 = None
|
||||
self.StructCount += 1
|
||||
# Sync the name | type index (the alias entry has the same name/Ty as the source, so the index keeps the first registration index).
|
||||
# Sync the name index (the alias keeps the first registration index for this name).
|
||||
self._struct_idx_add(self.StructCount - 1, entry)
|
||||
return entry
|
||||
|
||||
def set_struct_sha1(self, pool: memhub.MemBuddy*, class_name: str, sha1: str) -> int:
|
||||
"""Set the owning module SHA1 of a struct; returns 1 on success, 0 on failure."""
|
||||
if class_name is None or sha1 is None: return 0
|
||||
entry: GenTypes.StructEntry* = self.find_struct(class_name)
|
||||
if entry is None: return 0
|
||||
# Copy the SHA1 string (StdlibAlloc=1 uses the perm pool to survive 1a/1b and prevent a dangling strcmp).
|
||||
sha1_len: sizet = string.strlen(sha1)
|
||||
sha1_pool: memhub.MemBuddy* = pool
|
||||
if self.StdlibAlloc != 0:
|
||||
sha1_pool = CompilerFactory.factory.PermPool
|
||||
if sha1_pool is None: sha1_pool = pool
|
||||
sha1_buf: str = sha1_pool.alloc(sha1_len + 1)
|
||||
if sha1_buf is None: return 0
|
||||
string.strcpy(sha1_buf, sha1)
|
||||
entry.ModuleSha1 = sha1_buf
|
||||
return 1
|
||||
|
||||
def get_struct_sha1(self, class_name: str) -> str:
|
||||
"""Return the owning module SHA1 of a struct, or None when unset."""
|
||||
if class_name is None or self.StructTable is None: return None
|
||||
@@ -2752,29 +2654,6 @@ novirtual class StructGenMixin:
|
||||
# No SHA1: find the first by class name.
|
||||
return self.find_struct(name)
|
||||
|
||||
def reset_module_methods_emitted(self, sha1: str) -> int:
|
||||
"""Clear MethodsEmitted for classes registered by the given module sha1."""
|
||||
if sha1 is None or self.StructTable is None: return 0
|
||||
if self.StructCount < 0 or self.StructCount > 200000:
|
||||
VLogger.error(f"abnormal StructCount: {self.StructCount} (reset_module_methods_emitted)", "STRUCT")
|
||||
return 0
|
||||
reset_count: int = 0
|
||||
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:
|
||||
if string.strcmp(entry.ModuleSha1, sha1) == 0:
|
||||
entry.MethodsEmitted = 0
|
||||
reset_count += 1
|
||||
continue
|
||||
# Generic-specialized classes match by the triggering file's sha1: their ModuleSha1 is the template sha1 (method names carry the template prefix) and cannot match the current obsolete file; without resetting, the specialized body keeps MethodsEmitted=1 and is not retranslated, leaving text.ll with only calls and no define.
|
||||
if entry.SpecTriggerSha1 is not None:
|
||||
if string.strcmp(entry.SpecTriggerSha1, sha1) == 0:
|
||||
entry.MethodsEmitted = 0
|
||||
reset_count += 1
|
||||
return reset_count
|
||||
|
||||
def find_struct_by_type(self, struct_ty: mlirlite.LLVMType*) -> GenTypes.StructEntry*:
|
||||
"""Find a struct by type pointer value comparison, or None."""
|
||||
if struct_ty is None or self.StructTable is None: return None
|
||||
@@ -2783,8 +2662,8 @@ novirtual class StructGenMixin:
|
||||
VLogger.error(f"abnormal StructCount: {self.StructCount} (find_struct_by_type)", "STRUCT")
|
||||
stdlib.exit(1)
|
||||
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).
|
||||
# Linear scan by exact Ty pointer value. An entry's Ty may be rewritten by a deep
|
||||
# copy (ClassGenDef), so a pointer-keyed index would go stale.
|
||||
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:
|
||||
@@ -3116,21 +2995,6 @@ novirtual class StructGenMixin:
|
||||
cur_ty = cur.Ty
|
||||
return cur
|
||||
|
||||
def mark_as_union(self, name: str) -> int:
|
||||
"""Mark a registered struct as a union; returns 1 on success, 0 on failure."""
|
||||
if name is None: return 0
|
||||
entry: GenTypes.StructEntry* = self.find_struct(name)
|
||||
if entry is None: return 0
|
||||
entry.IsUnion = 1
|
||||
return 1
|
||||
|
||||
def is_union_by_name(self, name: str) -> int:
|
||||
"""Return 1 when the named struct is a union, else 0."""
|
||||
if name is None: return 0
|
||||
entry: GenTypes.StructEntry* = self.find_struct(name)
|
||||
if entry is None: return 0
|
||||
return entry.IsUnion
|
||||
|
||||
def is_union_by_type(self, struct_ty: mlirlite.LLVMType*) -> int:
|
||||
"""Return 1 when the type-pointer struct is a union, falling back to name matching.
|
||||
|
||||
@@ -3153,55 +3017,10 @@ novirtual class StructGenMixin:
|
||||
if matched != 0: return entry.IsUnion
|
||||
return 0
|
||||
|
||||
def mark_as_oop(self, name: str) -> int:
|
||||
"""Mark a registered struct as OOP; returns 1 on success, 0 on failure."""
|
||||
if name is None: return 0
|
||||
entry: GenTypes.StructEntry* = self.find_struct(name)
|
||||
if entry is None: return 0
|
||||
entry.IsOOP = 1
|
||||
return 1
|
||||
|
||||
def is_oop_by_name(self, name: str) -> int:
|
||||
"""Return 1 when the named struct is OOP, else 0."""
|
||||
if name is None: return 0
|
||||
entry: GenTypes.StructEntry* = self.find_struct(name)
|
||||
if entry is None: return 0
|
||||
return entry.IsOOP
|
||||
|
||||
def mark_has_init(self, name: str) -> int:
|
||||
"""Mark a struct as having an __init__ method; returns 1 on success, 0 on failure."""
|
||||
if name is None: return 0
|
||||
entry: GenTypes.StructEntry* = self.find_struct(name)
|
||||
if entry is None: return 0
|
||||
entry.HasInit = 1
|
||||
return 1
|
||||
|
||||
def has_init_by_name(self, name: str) -> int:
|
||||
"""Return 1 when the named struct has an __init__ method, else 0."""
|
||||
if name is None: return 0
|
||||
entry: GenTypes.StructEntry* = self.find_struct(name)
|
||||
if entry is None: return 0
|
||||
return entry.HasInit
|
||||
|
||||
def mark_has_new(self, name: str) -> int:
|
||||
"""Mark a struct as having a __new__ method; returns 1 on success, 0 on failure."""
|
||||
if name is None: return 0
|
||||
entry: GenTypes.StructEntry* = self.find_struct(name)
|
||||
if entry is None: return 0
|
||||
entry.HasNew = 1
|
||||
return 1
|
||||
|
||||
def has_new_by_name(self, name: str) -> int:
|
||||
"""Return 1 when the named struct has a __new__ method, else 0."""
|
||||
if name is None: return 0
|
||||
entry: GenTypes.StructEntry* = self.find_struct(name)
|
||||
if entry is None: return 0
|
||||
return entry.HasNew
|
||||
|
||||
def get_class_name_by_type(self, pool: memhub.MemBuddy*, struct_ty: mlirlite.LLVMType*) -> str:
|
||||
"""Return the class name for a type pointer, or None."""
|
||||
if struct_ty is None or self.StructTable is None: return None
|
||||
# find_struct_by_type's semantics = "first entry with equal Ty pointer" (first registration wins); return its Name. The Ty index is disabled and the linear scan restored, so it is byte-identical.
|
||||
# find_struct_by_type returns the first entry with an equal Ty pointer; use its Name.
|
||||
fast_cn: GenTypes.StructEntry* = self.find_struct_by_type(struct_ty)
|
||||
if fast_cn is not None: return fast_cn.Name
|
||||
# Fallback: when the type pointer comparison fails, iterate all registered structs matching by name; extract struct_ty's Name field (may be "SHA1.ClassName").
|
||||
@@ -3243,14 +3062,6 @@ novirtual class StructGenMixin:
|
||||
case _:
|
||||
return None
|
||||
|
||||
def mark_has_vtable(self, name: str) -> int:
|
||||
"""Mark a struct as having a vtable; returns 1 on success, 0 on failure."""
|
||||
if name is None: return 0
|
||||
entry: GenTypes.StructEntry* = self.find_struct(name)
|
||||
if entry is None: return 0
|
||||
entry.HasVTable = 1
|
||||
return 1
|
||||
|
||||
def has_vtable_by_name(self, name: str) -> int:
|
||||
"""Return 1 when the named struct has a vtable, else 0."""
|
||||
if name is None: return 0
|
||||
@@ -3258,14 +3069,6 @@ novirtual class StructGenMixin:
|
||||
if entry is None: return 0
|
||||
return entry.HasVTable
|
||||
|
||||
def mark_novtable(self, name: str) -> int:
|
||||
"""Mark a struct as NoVTable; returns 1 on success, 0 on failure."""
|
||||
if name is None: return 0
|
||||
entry: GenTypes.StructEntry* = self.find_struct(name)
|
||||
if entry is None: return 0
|
||||
entry.IsNoVTable = 1
|
||||
return 1
|
||||
|
||||
def is_novtable_by_name(self, name: str) -> int:
|
||||
"""Return 1 when the named struct is NoVTable, else 0."""
|
||||
if name is None: return 0
|
||||
@@ -3273,14 +3076,6 @@ novirtual class StructGenMixin:
|
||||
if entry is None: return 0
|
||||
return entry.IsNoVTable
|
||||
|
||||
def set_parent_name(self, name: str, parent_name: str) -> int:
|
||||
"""Set a struct's single parent class name; returns 1 on success, 0 on failure."""
|
||||
if name is None: return 0
|
||||
entry: GenTypes.StructEntry* = self.find_struct(name)
|
||||
if entry is None: return 0
|
||||
entry.ParentName = parent_name
|
||||
return 1
|
||||
|
||||
def set_parent_names(self, pool: memhub.MemBuddy*, name: str, names_ptr: sizet*, count: int) -> int:
|
||||
"""Set a struct's parent-name array, deep-copied into the perm pool."""
|
||||
if name is None: return 0
|
||||
@@ -3381,22 +3176,6 @@ novirtual class StructGenMixin:
|
||||
arr[out_count] = 0
|
||||
return arr
|
||||
|
||||
def get_parent_name(self, name: str) -> str:
|
||||
"""Return a struct's single parent class name, or None."""
|
||||
if name is None: return None
|
||||
entry: GenTypes.StructEntry* = self.find_struct(name)
|
||||
if entry is None: return None
|
||||
return entry.ParentName
|
||||
|
||||
def set_vtable_methods(self, name: str, methods: str, count: int) -> int:
|
||||
"""Set the vtable method-name array; returns 1 on success, 0 on failure."""
|
||||
if name is None: return 0
|
||||
entry: GenTypes.StructEntry* = self.find_struct(name)
|
||||
if entry is None: return 0
|
||||
entry.VTableMethods = methods
|
||||
entry.VTableMethodCount = count
|
||||
return 1
|
||||
|
||||
def get_vtable_method_index(self, name: str, method_name: str) -> int:
|
||||
"""Return the method's vtable index, or -1 when not found."""
|
||||
if name is None or method_name is None: return -1
|
||||
@@ -3429,13 +3208,6 @@ novirtual class StructGenMixin:
|
||||
if mname_addr == 0: return None
|
||||
return rawptr(mname_addr)
|
||||
|
||||
def get_field_count(self, name: str) -> int:
|
||||
"""Return a struct's field count, or -1 when not found."""
|
||||
if name is None: return -1
|
||||
entry: GenTypes.StructEntry* = self.find_struct(name)
|
||||
if entry is None: return -1
|
||||
return entry.FieldCount
|
||||
|
||||
def ensure_struct_def_in_module(self, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, ty: mlirlite.LLVMType*):
|
||||
"""Ensure a cross-module struct definition is available in the current module."""
|
||||
if ty is None or mod is None or pool is None: return
|
||||
|
||||
@@ -517,12 +517,6 @@ novirtual class Translator:
|
||||
|
||||
return 0
|
||||
# ============================================================
|
||||
# Wrapper main (when there is no user main) -> delegate to HandlesMain
|
||||
def _translate_wrapper_main(self, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, tree: ast.AST*, i32_ty: mlirlite.LLVMType*):
|
||||
"""Delegate to GenApi.create_wrapper_main() to build the wrapper main (trans arg)."""
|
||||
added: int = GenApi.create_wrapper_main(self, tree, i32_ty)
|
||||
|
||||
# ============================================================
|
||||
# Module-level translation (user main defined) -> delegate to HandlesMain
|
||||
def _translate_module_level(self, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, tree: ast.AST*):
|
||||
"""Delegate to GenApi.translate_children() to translate module-level statements (trans arg)."""
|
||||
@@ -779,7 +773,7 @@ novirtual class Translator:
|
||||
GenApi.add_visible_struct(pool, "dict")
|
||||
if self._imported_modules is None or GenApi.is_module_imported(self._imported_modules, "_dict") == 0:
|
||||
# Break cycles: skip injection when _dict already depends on the current module (avoids a
|
||||
# memhub<->_list 2-cycle that stops Phase A from converging). Mirrors Phase1Main._InjectedCreatesCycle.
|
||||
# memhub<->_list 2-cycle that stops Phase A from converging).
|
||||
if self._ContainerInjectionCreatesCycle("_dict") == 0: self._imported_modules = GenApi.add_imported_module(pool, self._imported_modules, "_dict")
|
||||
if need_list_box[0] != 0:
|
||||
GenApi.add_visible_struct(pool, "list")
|
||||
|
||||
@@ -14,7 +14,7 @@ import lib.core.Gen.GenApi as GenApi
|
||||
|
||||
|
||||
# TranslatorGenMixin - translation driver Mixin: module-level translation entry points
|
||||
# (translate_children/create_wrapper_main/handle_module_level_var) and scope name management.
|
||||
# (translate_children/handle_module_level_var) and scope name management.
|
||||
|
||||
|
||||
# TranslatorGenMixin - translation driver (module-level entry + scope name management)
|
||||
@@ -1009,62 +1009,6 @@ novirtual class TranslatorGenMixin:
|
||||
return 1
|
||||
return 0
|
||||
|
||||
# create_wrapper_main - create a wrapper main() -> i32 when the user defines none; sets the
|
||||
# current func/builder, prescans allocas, translates children and returns 0.
|
||||
def create_wrapper_main(self, trans: HT.Translator*, tree: ast.AST*, i32_ty: mlirlite.LLVMType*) -> int:
|
||||
"""Create a wrapper main function and translate all top-level statements."""
|
||||
pool: memhub.MemBuddy* = trans.Pool
|
||||
mod: mlirlite.LLVMModule* = trans.Module
|
||||
|
||||
main_func: mlirlite.Function* = mlirlite.create_function(pool, mod, "main", i32_ty)
|
||||
if main_func is None:
|
||||
VLogger.error("CreateFunction main returned NULL", "TR")
|
||||
return 0
|
||||
|
||||
entry_blk: mlirlite.BasicBlock* = mlirlite.create_block(pool, main_func, "entry")
|
||||
if entry_blk is None:
|
||||
VLogger.error("CreateBlock returned NULL", "TR")
|
||||
return 0
|
||||
|
||||
builder: mlirlite.IRBuilder* = mlirlite.new_builder(pool, main_func)
|
||||
if builder is None:
|
||||
VLogger.error("NewBuilder returned NULL", "TR")
|
||||
return 0
|
||||
mlirlite.position_at_end(builder, entry_blk)
|
||||
|
||||
# Set the current translation context (Handles access it via self.Trans._cur_builder)
|
||||
trans._cur_func = main_func
|
||||
trans._cur_builder = builder
|
||||
# P2 willFree 失效表:进入 main 体前复位(避免上一翻译单元残留的栈槽失效态泄漏进来)
|
||||
self.Gen.VarGen.freed_clear()
|
||||
|
||||
# Pre-process module-level variables (create LLVM globals) before pre_scan_allocas, or
|
||||
# PreScan would create local allocas that other functions reference by SSA number.
|
||||
ch: list[ast.AST*]* = tree.children
|
||||
if ch is not None:
|
||||
cn_count: sizet = len(ch)
|
||||
for ci in range(int(cn_count)):
|
||||
child: ast.AST* = ch.get(ci)
|
||||
if child is not None:
|
||||
kd: int = child.kind()
|
||||
if kd == ast.ASTKind.AnnAssign or kd == ast.ASTKind.Assign: self.handle_module_level_var(trans, child)
|
||||
|
||||
# Prescan top-level statements to pre-create allocas for in-function AnnAssign; module-level
|
||||
# variables are already registered so PreScan's lookup_current skips them.
|
||||
if ch is not None:
|
||||
for ci in range(int(cn_count)):
|
||||
child: ast.AST* = ch.get(ci)
|
||||
if child is not None: self.Gen.StmtGenDecl.pre_scan_allocas(trans, child)
|
||||
|
||||
# Translate children
|
||||
added_total: int = self.translate_children(trans, tree)
|
||||
|
||||
# Return 0
|
||||
zero_val: mlirlite.Value* = mlirlite.const_int32(pool, 0)
|
||||
mlirlite.build_ret(builder, zero_val)
|
||||
|
||||
return added_total
|
||||
|
||||
# global/nonlocal name management (migrated from HandlesTranslator.vp module-level functions)
|
||||
def add_global_name(self, trans: HT.Translator*, name: str):
|
||||
"""Add a global variable name."""
|
||||
|
||||
@@ -245,9 +245,6 @@ novirtual class TypeGenBase:
|
||||
def set_current_module_sha1(self, sha1: str) -> None:
|
||||
"""Set the SHA1 of the module currently being compiled."""
|
||||
self.CurrentModuleSha1 = sha1
|
||||
def _dbg_check_ast_floats(self, mb: memhub.MemBuddy*, tree: ast.AST*, tag: str):
|
||||
"""Removed debug hook; signature kept as a placeholder for external callers."""
|
||||
pass
|
||||
def set_generic_context(self, tp_names: list[str]*, type_args: list[str]*):
|
||||
"""Push and set the generic specialization context (nesting-safe)."""
|
||||
if self.GenericCtxStack is None:
|
||||
|
||||
@@ -31,8 +31,8 @@ def _ctypedef_idx_hash(name: str) -> uint64:
|
||||
return h
|
||||
|
||||
|
||||
# TypeGenCType - TypedefT table (register/lookup/ScanCTypedefDeclarations/
|
||||
# _eval_const_int_expr); inheritance chain is one level; @t.NoVTable static binding.
|
||||
# TypeGenCType - TypedefT table (register/lookup/constant evaluation);
|
||||
# inheritance chain is one level; @t.NoVTable static binding.
|
||||
@t.NoVTable
|
||||
class TypeGenCType(TypeGenBase.TypeGenBase):
|
||||
"""TypedefT table: register, lookup, scan declarations, eval const ints."""
|
||||
@@ -363,35 +363,6 @@ class TypeGenCType(TypeGenBase.TypeGenBase):
|
||||
sep_ch_d: char = nm_id_v[8]
|
||||
if sep_ch_d != '\0' and sep_ch_d != ' ' and sep_ch_d != '\n' and sep_ch_d != '\r' and sep_ch_d != '\t' and sep_ch_d != '|': return 0
|
||||
return 1
|
||||
def ScanCTypedefDeclarations(self, pool: memhub.MemBuddy*, tree: ast.AST*) -> int:
|
||||
"""Scan top-level AST nodes and register all NAME: t.TypedefT = Type declarations."""
|
||||
if pool is None or tree is None: return 0
|
||||
ch: list[ast.AST*]* = tree.children
|
||||
if ch is None: return 0
|
||||
n: sizet = len(ch)
|
||||
count: int = 0
|
||||
i: sizet
|
||||
for i in range(int(n)):
|
||||
child: ast.AST* = ch.get(i)
|
||||
if child is None: continue
|
||||
if child.kind() != ast.ASTKind.AnnAssign: continue
|
||||
aa: ast.AnnAssign* = ast.AnnAssign*(child)
|
||||
if aa.annotation is None: continue
|
||||
# check whether the annotation is t.TypedefT
|
||||
if self._is_ctypedef_annotation(aa.annotation) == 0: continue
|
||||
# extract the target name (must be a Name node)
|
||||
if aa.target is None or aa.target.kind() != ast.ASTKind.Name: continue
|
||||
tgt: ast.Name* = ast.Name*(aa.target)
|
||||
tgt_id_v: str = ast.name_get_id(tgt)
|
||||
if tgt_id_v is None: continue
|
||||
# extract the type expression string (from the value AST node)
|
||||
if aa.value is None: continue
|
||||
type_str: str = self.Gen.TypeGenResolve._get_type_name_from_annotation(pool, aa.value)
|
||||
if type_str is None: continue
|
||||
# register the TypedefT
|
||||
self.register_ctypedef(pool, tgt_id_v, type_str)
|
||||
count += 1
|
||||
return count
|
||||
def register_ctypedef_from_source(self, pool: memhub.MemBuddy*, src: str) -> int:
|
||||
"""Register CTypedefs by scanning raw source lines without reading AST fields."""
|
||||
if pool is None or src is None: return 0
|
||||
|
||||
@@ -168,80 +168,11 @@ novirtual class TypeGenErr(TypeGenBase.TypeGenBase):
|
||||
if var_ty.kind() == mlirlite.LLVMType.Ptr: return 0
|
||||
return 1
|
||||
|
||||
def is_pass_body(self, body: list[ast.AST*]*) -> int:
|
||||
"""Test whether a function body is pass (empty or only a pass statement)"""
|
||||
if body is None: return 1
|
||||
bn: sizet = len(body)
|
||||
if bn == 0: return 1
|
||||
if bn == 1:
|
||||
stmt: ast.AST* = body.get(0)
|
||||
if stmt is not None and stmt.kind() == ast.ASTKind.Pass: return 1
|
||||
return 0
|
||||
# Get the LLVM integer type bit width. Delegates to TSpec.SpecTypeBits (the sole implementation
|
||||
# in TypeSpec); private call sites (self.get_llvm_type_bits) are unaffected.
|
||||
def get_llvm_type_bits(self, ty: mlirlite.LLVMType*) -> int:
|
||||
"""Get an LLVM integer type's bit width, 0 for non-integers (delegates to SpecTypeBits)"""
|
||||
return TSpec.SpecTypeBits(ty)
|
||||
def infer_expr_type(self, pool: memhub.MemBuddy*, node: ast.AST*, param_types: str) -> mlirlite.LLVMType*:
|
||||
"""Infer an expression's LLVM type; param_types format is "name:bits,name:bits,..."."""
|
||||
if node is None: return mlirlite.Int32(pool)
|
||||
|
||||
k: int = node.kind()
|
||||
|
||||
if k == ast.ASTKind.Constant: return mlirlite.Int32(pool)
|
||||
|
||||
elif k == ast.ASTKind.Name:
|
||||
nm: ast.Name* = ast.Name*(node)
|
||||
if nm.id is not None and param_types is not None:
|
||||
# Look up "name:bits" in param_types. Allocate the key at exact length: a fixed 64
|
||||
# buffer truncated long names and could match another param's prefix, giving wrong bits.
|
||||
id_len: sizet = string.strlen(nm.id)
|
||||
search_key: str = pool.alloc(id_len + 2)
|
||||
if search_key is not None:
|
||||
string.strcpy(search_key, nm.id)
|
||||
search_key[id_len] = ':'
|
||||
search_key[id_len + 1] = '\0'
|
||||
found: str = string.strstr(param_types, search_key)
|
||||
if found is not None:
|
||||
colon_pos: sizet = id_len + 1
|
||||
bits_str: str = found + colon_pos
|
||||
bits: int = 0
|
||||
for bi in range(4):
|
||||
ch: char = bits_str[bi]
|
||||
if ch >= '0' and ch <= '9': bits = bits * 10 + (ch - '0')
|
||||
else: break
|
||||
if bits == 64: return mlirlite.Int64(pool)
|
||||
elif bits == 16: return mlirlite.Int16(pool)
|
||||
elif bits == 8: return mlirlite.Int8(pool)
|
||||
return mlirlite.Int32(pool)
|
||||
|
||||
elif k == ast.ASTKind.BinOp:
|
||||
binop: ast.BinOp* = ast.BinOp*(node)
|
||||
lhs_ty: mlirlite.LLVMType* = self.infer_expr_type(pool, binop.left, param_types)
|
||||
rhs_ty: mlirlite.LLVMType* = self.infer_expr_type(pool, binop.right, param_types)
|
||||
lhs_bits: int = self.get_llvm_type_bits(lhs_ty)
|
||||
rhs_bits: int = self.get_llvm_type_bits(rhs_ty)
|
||||
if lhs_bits >= rhs_bits: return lhs_ty
|
||||
return rhs_ty
|
||||
|
||||
return mlirlite.Int32(pool)
|
||||
def infer_return_type(self, pool: memhub.MemBuddy*, body: list[ast.AST*]*, param_types: str) -> mlirlite.LLVMType*:
|
||||
"""Scan a function body's return statements to infer the return type"""
|
||||
if body is None: return mlirlite.Int32(pool)
|
||||
|
||||
ret_ty: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
||||
bn: sizet = len(body)
|
||||
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)
|
||||
if rt.value is not None:
|
||||
expr_ty: mlirlite.LLVMType* = self.infer_expr_type(pool, rt.value, param_types)
|
||||
if expr_ty is not None:
|
||||
cur_bits: int = self.get_llvm_type_bits(ret_ty)
|
||||
new_bits: int = self.get_llvm_type_bits(expr_ty)
|
||||
if new_bits > cur_bits: ret_ty = expr_ty
|
||||
return ret_ty
|
||||
def body_has_value_return(self, pool: memhub.MemBuddy*, body: list[ast.AST*]*) -> int:
|
||||
"""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
|
||||
@@ -275,53 +206,3 @@ novirtual class TypeGenErr(TypeGenBase.TypeGenBase):
|
||||
if sub is not None and len(sub) > 0:
|
||||
if self.body_has_value_return(pool, sub) != 0: return 1
|
||||
return 0
|
||||
def build_param_types_str(self, pool: memhub.MemBuddy*, args_node: ast.AST*) -> str:
|
||||
"""Build the param type encoding string "name:bits,name:bits,..." with upper-bound sizing and single-pass in-place concatenation."""
|
||||
if args_node is None: return None
|
||||
ags: ast.Arguments* = ast.Arguments*(args_node)
|
||||
if ags.args is None: return None
|
||||
alist: list[ast.AST*]* = ags.args
|
||||
an: sizet = len(alist)
|
||||
if an == 0: return None
|
||||
|
||||
# 1) Upper-bound sizing (scan only param names, no type resolution): each entry is
|
||||
# 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(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
|
||||
buf: str = pool.alloc(bound)
|
||||
if buf is None: return None
|
||||
buf[0] = '\0'
|
||||
|
||||
# 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(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
|
||||
if arg.annotation is not None:
|
||||
# Unified: prefer the TypedefT dynamic table, fall back to map_builtin_type
|
||||
# (stage 2 removed hardcoded aliases; BYTE etc. now rely on library typedefs)
|
||||
if arg.annotation.kind() == ast.ASTKind.Attribute:
|
||||
at: ast.Attribute* = ast.Attribute*(arg.annotation)
|
||||
if at.attr is not None:
|
||||
resolved: mlirlite.LLVMType* = self.Gen.TypeGenResolve.resolve_typedef_or_map(pool, at.attr, None, None)
|
||||
if resolved is not None: param_ty = resolved
|
||||
bits: int = self.get_llvm_type_bits(param_ty)
|
||||
if bits == 0: bits = 32
|
||||
nlen: sizet = string.strlen(arg.arg)
|
||||
# Entry buffer: name + ':' + bits (16 bytes reserved, enough for any %d) + NUL
|
||||
ent: str = pool.alloc(nlen + 1 + 16 + 1)
|
||||
if ent is None: continue
|
||||
string.strcpy(ent, arg.arg)
|
||||
ent[nlen] = ':'
|
||||
viperlib.snprintf(ent + nlen + 1, 16, "%d", bits)
|
||||
if cnt > 0: string.strcat(buf, ",")
|
||||
string.strcat(buf, ent)
|
||||
cnt += 1
|
||||
if cnt == 0: return None
|
||||
return buf
|
||||
@@ -1209,13 +1209,6 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
ts_x: str = self.Gen.TypeGenCType.lookup_ctypedef(attr)
|
||||
if ts_x is not None and self.Gen.TypeGenCType.CTypedefFound != 0 and string.strcmp(ts_x, type_str) != 0: return self._expand_typedef_ref(pool, ts_x, depth + 1)
|
||||
return type_str
|
||||
def _long_llvm_ty(self, pool: memhub.MemBuddy*) -> mlirlite.LLVMType*:
|
||||
"""Target-platform LLVM type for C long / unsigned long (32-bit on Windows LLP64)."""
|
||||
triple: str = Config.TargetTriple
|
||||
if triple is None: triple = "x86_64-pc-windows-gnu"
|
||||
if string.strstr(triple, "windows") is not None or string.strstr(triple, "win32") is not None or string.strstr(triple, "mingw") is not None or string.strstr(triple, "msvc") is not None:
|
||||
return mlirlite.Int32(pool)
|
||||
return mlirlite.Int64(pool)
|
||||
# _IsCallableSubscriptValue - whether a Subscript value is a function-pointer type
|
||||
# constructor (t.Callable / callable / Callable); used to allow the unsigned forms.
|
||||
def _IsCallableSubscriptValue(self, value_node: ast.AST*) -> int:
|
||||
@@ -1354,23 +1347,6 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
if nm.id is None: return 0
|
||||
if string.strcmp(nm.id, attr_name) != 0: return 0
|
||||
return 1
|
||||
def _is_cptr_marker(self, node: ast.AST*, imported_modules: str) -> int:
|
||||
"""Whether a node is a PtrT pointer marker: 1=yes / 0=no."""
|
||||
if self._is_builtin_marker(node, "PtrT", imported_modules) != 0: return 1
|
||||
if node is not None and node.kind() == ast.ASTKind.Name:
|
||||
nm: ast.Name* = ast.Name*(node)
|
||||
if nm.id is not None and string.strcmp(nm.id, "PtrT") == 0: return 1
|
||||
return 0
|
||||
def _is_ref_marker(self, node: ast.AST*, imported_modules: str) -> int:
|
||||
"""Whether a node is a RefT/TempRefT value-reference marker: 1=yes / 0=no."""
|
||||
if self._is_builtin_marker(node, "RefT", imported_modules) != 0: return 1
|
||||
if self._is_builtin_marker(node, "TempRefT", 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_builtin_marker(node, "BigEndian", imported_modules) != 0: return 1
|
||||
if self._is_builtin_marker(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
|
||||
@@ -1409,14 +1385,6 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
if self.ref_param_mode(node, imported_modules) == 0: return 0
|
||||
self.Gen.TypeGenErr.fatal_error(node, f"value-reference parameter annotation '&T' / '&temp T' is only allowed in function parameter position, not for {where}", "TypeError")
|
||||
return 1
|
||||
def _is_cptr_subscript(self, node: ast.AST*) -> int:
|
||||
"""Whether a node is a PtrT[T] subscript pointer type (e.g. PtrT[PtrT])."""
|
||||
if node is None or node.kind() != ast.ASTKind.Subscript: return 0
|
||||
sub: ast.Subscript* = ast.Subscript*(node)
|
||||
if sub.value is None or sub.value.kind() != ast.ASTKind.Name: return 0
|
||||
nm: ast.Name* = ast.Name*(sub.value)
|
||||
if nm.id is None or string.strcmp(nm.id, "PtrT") != 0: return 0
|
||||
return 1
|
||||
def _is_cattr_marker(self, node: ast.AST*) -> int:
|
||||
"""Whether a node is an `attr.<name>` builtin marker (including the call form)."""
|
||||
if node is None: return 0
|
||||
@@ -1545,20 +1513,6 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
if TSpec.SpecSignIsSigned(s) != 0: return GenTypes.FUNC_RET_SIGN_SIGNED
|
||||
return 0
|
||||
|
||||
def _is_storage_class_qualifier(self, name: str) -> int:
|
||||
"""Whether a name is a storage-class qualifier macro (not a type): 1=yes / 0=no."""
|
||||
if name is None: return 0
|
||||
if string.strcmp(name, "StaticT") == 0: return 1
|
||||
if string.strcmp(name, "VolatileT") == 0: return 1
|
||||
if string.strcmp(name, "ExportT") == 0: return 1
|
||||
if string.strcmp(name, "ExternT") == 0: return 1
|
||||
if string.strcmp(name, "InlineT") == 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:
|
||||
"""Extract the class name from an annotation AST; None means not extractable."""
|
||||
if node is None: return None
|
||||
|
||||
@@ -152,13 +152,6 @@ def SpecSignFromTypeNameSpan(text: str, start: sizet, end: sizet) -> int:
|
||||
return TS_SIGN_UNKNOWN
|
||||
|
||||
|
||||
# SpecSignFromTypeName - exact signedness from a NUL-terminated type name
|
||||
def SpecSignFromTypeName(name: str) -> int:
|
||||
"""Exact signedness of a type name; TS_SIGN_UNKNOWN if not covered"""
|
||||
if name is None: return TS_SIGN_UNKNOWN
|
||||
return SpecSignFromTypeNameSpan(name, 0, string.strlen(name))
|
||||
|
||||
|
||||
# TypeSpec - type descriptor. PtrLevel alone is ambiguous for Ptr(i8), so element
|
||||
# semantics use the (ElemBits, IsElemPtr, DerefBase) triple replacing IsPtrElement.
|
||||
novirtual class TypeSpec:
|
||||
@@ -573,127 +566,6 @@ def _spec_param_at(sp: TypeSpec*, index: int) -> TypeSpec*:
|
||||
return (TypeSpec*)(uint64(slot[0]))
|
||||
|
||||
|
||||
# TextToSpec - decode canonical text (inverse of SpecToText; not interned).
|
||||
# Returns None on parse failure (caller decides fail-fast).
|
||||
def TextToSpec(text: str) -> TypeSpec*:
|
||||
"""Decode canonical text into a TypeSpec (PermPool, not interned); None on failure"""
|
||||
if text is None: return None
|
||||
return _text_to_spec(text, 0)
|
||||
|
||||
|
||||
def _text_to_spec(text: str, depth: int) -> TypeSpec*:
|
||||
"""Recursive implementation of TextToSpec (depth limit 32)"""
|
||||
if text is None or depth > 32: return None
|
||||
# pointer: p:INNER
|
||||
if string.strncmp(text, "p:", 2) == 0:
|
||||
inner: TypeSpec* = _text_to_spec(text + 2, depth + 1)
|
||||
if inner is None: return None
|
||||
sp_p: TypeSpec* = _spec_make(TS_PTR, 0, TS_SIGN_UNKNOWN)
|
||||
if sp_p is None: return None
|
||||
sp_p.Elem = inner
|
||||
sp_p.PtrLevel = inner.PtrLevel + 1
|
||||
sp_p.ElemBits = inner.Bits
|
||||
if inner.Kind == TS_PTR: sp_p.IsElemPtr = 1
|
||||
return sp_p
|
||||
# array: arr[N]:INNER
|
||||
if string.strncmp(text, "arr[", 4) == 0:
|
||||
rb: str = string.strchr(text, ']')
|
||||
if rb is None: return None
|
||||
count: int = string.atoi(text + 4)
|
||||
if count < 0: return None
|
||||
colon: str = string.strchr(rb, ':')
|
||||
if colon is None: return None
|
||||
inner_a: TypeSpec* = _text_to_spec(colon + 1, depth + 1)
|
||||
if inner_a is None: return None
|
||||
sp_a: TypeSpec* = _spec_make(TS_ARRAY, 0, TS_SIGN_UNKNOWN)
|
||||
if sp_a is None: return None
|
||||
sp_a.Elem = inner_a
|
||||
sp_a.ArrayCount = count
|
||||
sp_a.ElemBits = inner_a.Bits
|
||||
if inner_a.Kind == TS_PTR: sp_a.IsElemPtr = 1
|
||||
return sp_a
|
||||
# function: fn(...)->RET
|
||||
if string.strncmp(text, "fn(", 3) == 0: return _text_to_func(text, depth)
|
||||
# struct/union/enum: st:NAME / un:NAME / en:NAME
|
||||
if string.strncmp(text, "st:", 3) == 0: return _spec_make_struct(TS_STRUCT, text + 3)
|
||||
if string.strncmp(text, "un:", 3) == 0: return _spec_make_struct(TS_UNION, text + 3)
|
||||
if string.strncmp(text, "en:", 3) == 0: return _spec_make_struct(TS_ENUM, text + 3)
|
||||
if string.strcmp(text, "bool") == 0: return _spec_make(TS_BOOL, 1, TS_SIGN_UNSIGNED)
|
||||
if string.strcmp(text, "void") == 0: return _spec_make(TS_VOID, 0, TS_SIGN_UNKNOWN)
|
||||
# i32 / u64 / f64
|
||||
if text[0] == 'u' or text[0] == 'i' or text[0] == 'f':
|
||||
bits: int = string.atoi(text + 1)
|
||||
if bits <= 0: return None
|
||||
if text[0] == 'u': return _spec_make(TS_UINT, bits, TS_SIGN_UNSIGNED)
|
||||
if text[0] == 'i': return _spec_make(TS_INT, bits, TS_SIGN_SIGNED)
|
||||
return _spec_make(TS_FLOAT, bits, TS_SIGN_SIGNED)
|
||||
return None
|
||||
|
||||
|
||||
# _spec_make_struct - build a named struct/union/enum (deep-copies the name)
|
||||
def _spec_make_struct(kind: int, name: str) -> TypeSpec*:
|
||||
"""Build a struct/union/enum type (not interned); None on failure"""
|
||||
if name is None or name[0] == '\0': return None
|
||||
sp: TypeSpec* = _spec_make(kind, 0, TS_SIGN_UNKNOWN)
|
||||
if sp is None: return None
|
||||
if _spec_set_struct_name(sp, name) != 0: return None
|
||||
return sp
|
||||
|
||||
|
||||
# _text_to_func - parse "fn(p1,p2,...)->RET"
|
||||
def _text_to_func(text: str, depth: int) -> TypeSpec*:
|
||||
"""Parse a function-signature text; None on failure"""
|
||||
lp: str = string.strchr(text, '(')
|
||||
if lp is None: return None
|
||||
arrow: str = string.strstr(text, ")->")
|
||||
if arrow is None: return None
|
||||
# parameter span (lp+1 .. arrow-1)
|
||||
params_buf: str = CompilerFactory.factory.PermPool.alloc(SPEC_TEXT_MAX)
|
||||
if params_buf is None: return None
|
||||
params_buf[0] = '\0'
|
||||
nlen: sizet = sizet(uint64(arrow) - uint64(lp)) - 1
|
||||
if nlen > 0:
|
||||
string.strncpy(params_buf, lp + 1, nlen)
|
||||
params_buf[nlen] = '\0'
|
||||
is_vararg: int = 0
|
||||
if string.strstr(params_buf, "...") is not None: is_vararg = 1
|
||||
# parse params segment by segment (split on top-level ','; skip "..." vararg)
|
||||
param_specs: sizet* = CompilerFactory.factory.PermPool.alloc(8 * 64)
|
||||
if param_specs is None: return None
|
||||
pcount: int = 0
|
||||
if nlen > 0:
|
||||
cur: str = params_buf
|
||||
while cur is not None:
|
||||
comma: str = string.strchr(cur, ',')
|
||||
seg: str = cur
|
||||
if comma is not None:
|
||||
seg_make: str = CompilerFactory.factory.PermPool.alloc(SPEC_TEXT_MAX)
|
||||
if seg_make is None: return None
|
||||
seg_len: sizet = sizet(uint64(comma) - uint64(cur))
|
||||
string.strncpy(seg_make, cur, seg_len)
|
||||
seg_make[seg_len] = '\0'
|
||||
seg = seg_make
|
||||
cur = comma + 1
|
||||
else: cur = None
|
||||
if string.strcmp(seg, "...") == 0: continue
|
||||
if pcount >= 64: return None
|
||||
psp: TypeSpec* = _text_to_spec(seg, depth + 1)
|
||||
if psp is None: return None
|
||||
slot_p: sizet* = (sizet*)(uint64(param_specs) + uint64(pcount * 8))
|
||||
if slot_p is None: return None
|
||||
slot_p[0] = sizet(psp)
|
||||
pcount += 1
|
||||
ret: TypeSpec* = _text_to_spec(arrow + 3, depth + 1)
|
||||
if ret is None: return None
|
||||
sp_f: TypeSpec* = _spec_make(TS_FUNC, 0, TS_SIGN_UNKNOWN)
|
||||
if sp_f is None: return None
|
||||
sp_f.Ret = ret
|
||||
sp_f.Params = param_specs
|
||||
sp_f.ParamCount = pcount
|
||||
sp_f.IsVarArg = is_vararg
|
||||
return sp_f
|
||||
|
||||
|
||||
# _spec_discard_temp - return a dedup-hit temporary TypeSpec to PermPool.
|
||||
# The temporary's BaseName is an exclusive deep copy made by NewTypeStruct; a clone
|
||||
# (_spec_clone) instead shares the pointer with the interned entry, so only a distinct
|
||||
@@ -775,19 +647,6 @@ def SpecParamAt(sp: TypeSpec*, i: int) -> TypeSpec*:
|
||||
return _spec_param_at(sp, i)
|
||||
|
||||
|
||||
def SpecIsPtr(sp: TypeSpec*) -> int:
|
||||
"""Whether this is a pointer type (PtrLevel>0)"""
|
||||
if sp is None: return 0
|
||||
if sp.PtrLevel > 0: return 1
|
||||
return 0
|
||||
|
||||
|
||||
def SpecPointedTo(sp: TypeSpec*) -> TypeSpec*:
|
||||
"""Pointee of a pointer; None if not a pointer"""
|
||||
if sp is None or sp.Kind != TS_PTR: return None
|
||||
return sp.Elem
|
||||
|
||||
|
||||
# _spec_clone - shallow-copy a TypeSpec (PermPool, not interned); Elem/Ret/Params shared.
|
||||
# Reused by SpecWithSign/SpecWithSource/SpecWithElemBits to avoid field-copy drift.
|
||||
def _spec_clone(sp: TypeSpec*) -> TypeSpec*:
|
||||
@@ -903,12 +762,6 @@ def SpecSignIsSigned(sign: int) -> int:
|
||||
return 0
|
||||
|
||||
|
||||
def SpecBitWidth(sp: TypeSpec*) -> int:
|
||||
"""Bit width; 0 for non-integer/float"""
|
||||
if sp is None: return 0
|
||||
return sp.Bits
|
||||
|
||||
|
||||
# SpecTypePtrLevel - LLVM type -> pointer depth (delegated from ExprGenBase.get_ptr_level).
|
||||
# Bootstrap match-codegen workaround: update the loop var outside match (case binds only `nxt`).
|
||||
def SpecTypePtrLevel(ty: mlirlite.LLVMType*) -> int:
|
||||
@@ -1097,69 +950,3 @@ def _spec_from_struct(arena: TypeSpecArena*, sname: str) -> TypeSpec*:
|
||||
"""Named struct to TS_STRUCT + BaseName; anonymous struct to a nameless TS_STRUCT"""
|
||||
if sname is not None: return NewTypeStruct(arena, TS_STRUCT, sname)
|
||||
return NewTypeSpec(arena, TS_STRUCT, 0, TS_SIGN_UNKNOWN)
|
||||
|
||||
|
||||
# SpecSelfCheck - S1 self-check (not part of the compile flow; manual / unit tests).
|
||||
# Build types -> encode -> decode -> re-encode and compare (round-trip); returns 0=pass.
|
||||
def SpecSelfCheck() -> int:
|
||||
"""TypeSpec encode/decode self-check; 0=pass / failing case number"""
|
||||
arena: TypeSpecArena* = (TypeSpecArena*)(CompilerFactory.factory.PermPool.alloc(TypeSpecArena.__sizeof__()))
|
||||
if arena is None: return 1
|
||||
string.memset(arena, 0, TypeSpecArena.__sizeof__())
|
||||
if SpecArenaInit(arena, 64) != 0: return 1
|
||||
|
||||
# 1) u64
|
||||
s_u64: TypeSpec* = NewTypeSpec(arena, TS_UINT, 64, TS_SIGN_UNSIGNED)
|
||||
if s_u64 is None: return 1
|
||||
if _check_roundtrip(s_u64, "u64") != 0: return 1
|
||||
|
||||
# 2) i32
|
||||
s_i32: TypeSpec* = NewTypeSpec(arena, TS_INT, 32, TS_SIGN_SIGNED)
|
||||
if s_i32 is None: return 2
|
||||
if _check_roundtrip(s_i32, "i32") != 0: return 2
|
||||
|
||||
# 3) u64*
|
||||
s_p: TypeSpec* = NewTypePtr(arena, s_u64)
|
||||
if s_p is None: return 3
|
||||
if _check_roundtrip(s_p, "p:u64") != 0: return 3
|
||||
|
||||
# 4) i8[4]
|
||||
s_arr: TypeSpec* = NewTypeArray(arena, s_i32, 4)
|
||||
if s_arr is None: return 4
|
||||
if _check_roundtrip(s_arr, "arr[4]:i32") != 0: return 4
|
||||
|
||||
# 5) fn(i32,u64)->i32 (vararg)
|
||||
params: sizet* = CompilerFactory.factory.PermPool.alloc(8 * 2)
|
||||
if params is None: return 5
|
||||
slot0: sizet* = (sizet*)(uint64(params))
|
||||
if slot0 is None: return 5
|
||||
slot0[0] = sizet(s_i32)
|
||||
slot1: sizet* = (sizet*)(uint64(params) + 8)
|
||||
if slot1 is None: return 5
|
||||
slot1[0] = sizet(s_u64)
|
||||
s_fn: TypeSpec* = NewTypeFunc(arena, s_i32, params, 2, 1)
|
||||
if s_fn is None: return 5
|
||||
if _check_roundtrip(s_fn, "fn(i32,u64,...)->i32") != 0: return 5
|
||||
|
||||
# 6) interning dedup: same text must return the same instance
|
||||
s_u64_again: TypeSpec* = NewTypeSpec(arena, TS_UINT, 64, TS_SIGN_UNSIGNED)
|
||||
if s_u64_again is None: return 6
|
||||
if uint64(s_u64_again) != uint64(s_u64): return 6
|
||||
return 0
|
||||
|
||||
|
||||
# _check_roundtrip - self-check helper: encoding equals expect and decode-reencode matches
|
||||
def _check_roundtrip(sp: TypeSpec*, expect: str) -> int:
|
||||
"""Encoding equality plus decode round-trip; 0=pass / 1=failure"""
|
||||
if sp is None or expect is None: return 1
|
||||
buf1: str = CompilerFactory.factory.PermPool.alloc(SPEC_TEXT_MAX)
|
||||
if buf1 is None: return 1
|
||||
if SpecToText(sp, buf1, SPEC_TEXT_MAX) != 0: return 1
|
||||
if string.strcmp(buf1, expect) != 0: return 1
|
||||
back: TypeSpec* = TextToSpec(buf1)
|
||||
if back is None: return 1
|
||||
buf2: str = CompilerFactory.factory.PermPool.alloc(SPEC_TEXT_MAX)
|
||||
if buf2 is None: return 1
|
||||
if SpecToText(back, buf2, SPEC_TEXT_MAX) != 0: return 1
|
||||
if string.strcmp(buf2, expect) != 0: return 1
|
||||
return 0
|
||||
@@ -20,7 +20,6 @@ import viperlib
|
||||
# Scope kinds: module / function / block.
|
||||
define SCOPE_MODULE = 0
|
||||
define SCOPE_FUNCTION = 1
|
||||
define SCOPE_BLOCK = 2
|
||||
# Max variables per scope: the single source of truth is Translator.vp; this file always uses HT.MAX_VARS
|
||||
|
||||
|
||||
@@ -474,16 +473,6 @@ novirtual class VarGenMixin:
|
||||
if builder is None or ty is None: return None
|
||||
return mlirlite.build_alloca_at_entry(builder, ty)
|
||||
|
||||
# get_or_create_var - find or create a variable alloca (legacy-compatible)
|
||||
def get_or_create_var(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, vars_ptr: GenTypes.VarEntry*, var_count: int, name: str, ty: mlirlite.LLVMType*) -> mlirlite.Value*:
|
||||
"""Find or create a variable alloca"""
|
||||
existing: mlirlite.Value* = self.find_var(vars_ptr, var_count, name)
|
||||
if existing is not None: return existing
|
||||
alloca: mlirlite.Value* = self._alloca_at_entry(builder, ty)
|
||||
if alloca is None: return None
|
||||
if self.add_var(vars_ptr, var_count, name, alloca) != 0: return None
|
||||
return alloca
|
||||
|
||||
# _create_scope - create a new scope node (internal helper)
|
||||
# ============================================================
|
||||
def _create_scope(self, pool: memhub.MemBuddy*, parent: GenTypes.Scope*, kind: int) -> GenTypes.Scope*:
|
||||
@@ -968,12 +957,6 @@ novirtual class VarGenMixin:
|
||||
if tag == GenTypes.REPL_TY_STR: return mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||||
return None
|
||||
|
||||
# ---- repl_func_sig_label - signature token -> human-readable type name (for error messages) ----
|
||||
def repl_func_sig_label(self, tag: int, bits: int) -> str:
|
||||
"""Map a signature token to a display name; "?" if unknown (VOID displays as void)"""
|
||||
if tag == GenTypes.REPL_TY_VOID: return "void"
|
||||
return self.repl_tag_label(tag, bits)
|
||||
|
||||
# ---- 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: rawptr) -> mlirlite.Value*:
|
||||
|
||||
@@ -17,8 +17,6 @@ import lib.core.VLogger as VLogger
|
||||
# global mbuddy pointer
|
||||
_mbuddy: memhub.MemBuddy*
|
||||
|
||||
# max file path length
|
||||
define MAX_PATH_LEN = 512
|
||||
# max files per scan
|
||||
define MAX_FILES = 1024
|
||||
|
||||
@@ -283,20 +281,6 @@ def scan_includes(pool: memhub.MemBuddy*, includes_dirs: str) -> ScanResult*:
|
||||
return result
|
||||
|
||||
|
||||
# find_entry_by_module - find a file entry by module name
|
||||
def find_entry_by_module(result: ScanResult*, module_name: str) -> FileEntry*:
|
||||
"""Find a file entry by module name."""
|
||||
if result is None or module_name is None: return None
|
||||
entry_size: sizet = FileEntry.__sizeof__()
|
||||
for i in range(int(result.Count)):
|
||||
entry_addr: uint64 = uint64(result.Entries) + i * entry_size
|
||||
entry: FileEntry* = (FileEntry*)(entry_addr)
|
||||
if entry is not None:
|
||||
if entry.ModuleName is not None:
|
||||
if string.strcmp(entry.ModuleName, module_name) == 0: return entry
|
||||
return None
|
||||
|
||||
|
||||
# _PathExistsA - path existence check (GetFileAttributesA)
|
||||
def _PathExistsA(path: str) -> int:
|
||||
"""Return 1 if the path exists, else 0, using GetFileAttributesA."""
|
||||
|
||||
@@ -102,7 +102,7 @@ define _REPL_INC_CLS_BUDGET_MS = 20000
|
||||
# Temporary LLVM module name for includes class registration (.vpi injection target).
|
||||
define _REPL_INC_MOD_NAME = "repl_inc_init"
|
||||
# ctx session buffer size = header 64 + 64 var slots x 16 + 32 func slots x 16 = 1600.
|
||||
# Must stay in sync with the GenTypes.REPL_CTX_SIZE / REPL_* layout constants.
|
||||
# Must stay in sync with the GenTypes REPL_* layout constants.
|
||||
define _REPL_CTX_SIZE = 1600
|
||||
# Template project file name (searched in cwd then exe dir when --project is absent).
|
||||
define _REPL_PROJECT_FILE = "repl.vpj"
|
||||
@@ -1837,8 +1837,7 @@ def ReplExecFragment(mb: memhub.MemBuddy*, src: str) -> int:
|
||||
stdio.printf("fragment %s: llc failed to compile .obj (command/path buffer allocation failed; see diagnostics above)\n", ReplNameBuf)
|
||||
return 1
|
||||
|
||||
# 11) clang++ -shared -> .dll (command shape matches BuildPipeline.link_obj_to_exe; process
|
||||
# launch reuses BuildPipeline._run_cmd_wait).
|
||||
# 11) clang++ -shared -> .dll; process launch reuses BuildPipeline._run_cmd_wait.
|
||||
obj_path: str = BuildPipeline.build_sliced_path(ReplTmpDir, ReplNameBuf, "obj")
|
||||
dll_path: str = BuildPipeline.build_sliced_path(ReplTmpDir, ReplNameBuf, "dll")
|
||||
if obj_path is None or dll_path is None:
|
||||
|
||||
+1
-52
@@ -46,7 +46,7 @@ def clear_repl_jmpbuf() -> int:
|
||||
|
||||
# Log levels
|
||||
enum LogLevel:
|
||||
DEBUG, INFO, WARNING, ERROR, SUCCESS, CRITICAL
|
||||
DEBUG, INFO, WARNING, ERROR, SUCCESS
|
||||
|
||||
|
||||
# Win32 console foreground color attributes
|
||||
@@ -59,17 +59,7 @@ define FOREGROUND_GREEN: WORD = WORD(0x0002)
|
||||
define FOREGROUND_RED: WORD = WORD(0x0004)
|
||||
define FOREGROUND_INTENSITY: WORD = WORD(0x0008)
|
||||
define FOREGROUND_WHITE: WORD = WORD(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE)
|
||||
define FOREGROUND_CYAN: WORD = WORD(FOREGROUND_BLUE | FOREGROUND_GREEN)
|
||||
define FOREGROUND_YELLOW: WORD = WORD(FOREGROUND_RED | FOREGROUND_GREEN)
|
||||
define FOREGROUND_MAGENTA: WORD = WORD(FOREGROUND_RED | FOREGROUND_BLUE)
|
||||
|
||||
# Win32 console background color attributes
|
||||
define BACKGROUND_RED: WORD = WORD(0x0040)
|
||||
define BACKGROUND_GREEN: WORD = WORD(0x0020)
|
||||
define BACKGROUND_BLUE: WORD = WORD(0x0010)
|
||||
define BACKGROUND_INTENSITY: WORD = WORD(0x0080)
|
||||
define BACKGROUND_WHITE: WORD = WORD(BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE)
|
||||
|
||||
|
||||
novirtual class Logger:
|
||||
_level: int
|
||||
@@ -135,14 +125,6 @@ novirtual class Logger:
|
||||
def success(self, msg: str, category: str = "") -> int:
|
||||
return self._log(LogLevel.SUCCESS, "├", "SUCCESS", msg, FOREGROUND_GREEN, WORD(FOREGROUND_GREEN | FOREGROUND_INTENSITY), category)
|
||||
|
||||
def critical(self, msg: str, category: str = "") -> int:
|
||||
# CRITICAL uses the ╠ prefix (red-background white label, aligned with TPC style).
|
||||
return self._log(LogLevel.CRITICAL, "╠", "CRITICAL", msg, FOREGROUND_RED, WORD(BACKGROUND_RED | FOREGROUND_WHITE | FOREGROUND_INTENSITY), category)
|
||||
|
||||
def set_level(self, level: int) -> int:
|
||||
self._level = level
|
||||
return 0
|
||||
|
||||
def set_jit_flag(self, v: bool) -> int:
|
||||
"""Set the JIT/REPL flag: True makes error longjmp back to the REPL loop."""
|
||||
self.jit_flag = v
|
||||
@@ -157,33 +139,6 @@ novirtual class Logger:
|
||||
self._reset_color()
|
||||
return 0
|
||||
|
||||
def compile_error(self, msg: str, file: str = "", line: int = 0) -> int:
|
||||
"""Format a compile error with a red-background header, location and message."""
|
||||
self._set_color(BACKGROUND_RED | FOREGROUND_WHITE | FOREGROUND_INTENSITY)
|
||||
stdio.printf(" COMPILE ERROR ")
|
||||
self._set_color(FOREGROUND_RED | FOREGROUND_INTENSITY)
|
||||
stdio.printf("\n")
|
||||
if file is not None:
|
||||
if file[0] != 0 and line > 0: stdio.printf(" location: %s:%d\n", file, line)
|
||||
elif file[0] != 0: stdio.printf(" file: %s\n", file)
|
||||
stdio.printf(" error: %s\n", msg)
|
||||
self._reset_color()
|
||||
return 0
|
||||
|
||||
def compile_warning(self, msg: str, file: str = "", line: int = 0) -> int:
|
||||
"""Format a compile warning with a yellow background, location and message."""
|
||||
self._set_color(BACKGROUND_RED | BACKGROUND_GREEN | FOREGROUND_INTENSITY)
|
||||
stdio.printf(" COMPILE WARNING ")
|
||||
self._set_color(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY)
|
||||
stdio.printf("\n")
|
||||
if file is not None:
|
||||
if file[0] != 0 and line > 0: stdio.printf(" location: %s:%d\n", file, line)
|
||||
elif file[0] != 0: stdio.printf(" file: %s\n", file)
|
||||
stdio.printf(" warning: %s\n", msg)
|
||||
self._reset_color()
|
||||
return 0
|
||||
|
||||
|
||||
# Global mbuddy pointer (injected by lib.InitLib).
|
||||
_mbuddy: memhub.MemBuddy*
|
||||
|
||||
@@ -234,12 +189,6 @@ def success(msg: str, category: str = "") -> int:
|
||||
return 0
|
||||
|
||||
|
||||
def critical(msg: str, category: str = "") -> int:
|
||||
"""Log at CRITICAL level (fatal, red background)."""
|
||||
if log is not None: return log.critical(msg, category)
|
||||
return 0
|
||||
|
||||
|
||||
def debug(msg: str, category: str = "") -> int:
|
||||
"""Log at DEBUG level."""
|
||||
if log is not None: return log.debug(msg, category)
|
||||
|
||||
@@ -531,13 +531,6 @@ def Roots() -> list[str]*:
|
||||
return idx.roots
|
||||
|
||||
|
||||
def SymbolsOf(path: str) -> list[LspSymbols.LspSymbol*]*:
|
||||
e: LspFileEntry* = FileEntry(path)
|
||||
if e is None:
|
||||
return None
|
||||
return e.syms
|
||||
|
||||
|
||||
def CollectRefs(pool: memhub.MemManager*, tfile: str, tcontainer: str,
|
||||
tname: str, exclude_file: str) -> list[LspOccRef*]*:
|
||||
"""Cross-file resolved occurrences of (tfile, tcontainer, tname)."""
|
||||
|
||||
@@ -4,8 +4,6 @@ from stdint import *
|
||||
define LSP_JSONRPC_VERSION = "2.0"
|
||||
define LSP_ERROR_PARSE = -32700
|
||||
define LSP_ERROR_INVALID_REQUEST = -32600
|
||||
define LSP_ERROR_METHOD_NOT_FOUND = -32601
|
||||
define LSP_ERROR_INVALID_PARAMS = -32602
|
||||
define LSP_ERROR_INTERNAL = -32603
|
||||
define LSP_ERROR_REQUEST_CANCELLED = -32800
|
||||
|
||||
|
||||
@@ -24,12 +24,9 @@ define ST_METHOD = 3
|
||||
define ST_PARAMETER = 4
|
||||
define ST_VARIABLE = 5
|
||||
define ST_PROPERTY = 6
|
||||
define ST_KEYWORD = 7
|
||||
define ST_COMMENT = 8
|
||||
define ST_STRING = 9
|
||||
define ST_NUMBER = 10
|
||||
define ST_OPERATOR = 11
|
||||
define ST_DECORATOR = 12
|
||||
define ST_MACRO = 13
|
||||
define ST_TYPE = 14
|
||||
|
||||
|
||||
@@ -21,7 +21,6 @@ import lib.lsp.LspPools as LspPools
|
||||
# LspSymbols.Analyze, stores its module/class signatures, and returns the requested entry.
|
||||
|
||||
define LSP_SIG_CACHE_DIR = ".viperc-lsp-cache"
|
||||
define LSP_SIG_CACHE_LINE_CAP = 4096
|
||||
|
||||
|
||||
class LspSigEntry:
|
||||
@@ -43,10 +42,6 @@ _entries: LspSigEntry** = None
|
||||
_entry_count: int = 0
|
||||
_work: LspPools.LspPool* = None
|
||||
_ready: int = 0
|
||||
_map_vals: LspSigEntry** = None
|
||||
|
||||
define LSP_SIG_MAP_CAP = 262144
|
||||
define LSP_SIG_MAP_MASK = 262143
|
||||
|
||||
|
||||
def _HashPath(path: str) -> uint32:
|
||||
@@ -121,7 +116,7 @@ def _Dup(pool: memhub.MemManager*, s: str) -> str:
|
||||
|
||||
|
||||
def Ensure(ws: LspWorkspace.LspWorkspace*, table_pool: memhub.MemManager*) -> int:
|
||||
global _cache_dir, _entries, _entry_count, _work, _ready, _map_vals
|
||||
global _cache_dir, _entries, _entry_count, _work, _ready
|
||||
if _ready != 0:
|
||||
return 0
|
||||
base: str = None
|
||||
@@ -164,19 +159,6 @@ def _CachePath(pool: memhub.MemManager*, file: str) -> str:
|
||||
return LspFiles.Join(pool, _cache_dir, _Cat(pool, h, ".sig"))
|
||||
|
||||
|
||||
def _MapHash(file: str, name: str) -> int:
|
||||
return int(_HashPath(file) ^ _HashPath(name)) & LSP_SIG_MAP_MASK
|
||||
|
||||
|
||||
def _InsertMap(e: LspSigEntry*):
|
||||
# Linear entry lookup is used; this hook stays for call-site symmetry.
|
||||
return
|
||||
|
||||
|
||||
def _RebuildMap():
|
||||
return
|
||||
|
||||
|
||||
def _FindEntry(file: str, name: str) -> LspSigEntry*:
|
||||
if _entries is None or file is None or name is None:
|
||||
return None
|
||||
@@ -208,7 +190,6 @@ def _AddEntry(pool: memhub.MemManager*, file: str, container: str, name: str,
|
||||
e.detail = _Dup(pool, detail)
|
||||
_entries[_entry_count] = e
|
||||
_entry_count += 1
|
||||
_InsertMap(e)
|
||||
return e
|
||||
|
||||
|
||||
@@ -386,7 +367,6 @@ def Invalidate(pool: memhub.MemManager*, table_pool: memhub.MemManager*, file: s
|
||||
_entries[j] = e
|
||||
j += 1
|
||||
_entry_count = j
|
||||
_RebuildMap()
|
||||
if _cache_dir is not None and file is not None:
|
||||
cp: str = _CachePath(pool, file)
|
||||
if cp is not None and os.isfile(cp):
|
||||
|
||||
@@ -24,11 +24,6 @@ def MinUnsignedBits(v: uint64) -> int:
|
||||
return n
|
||||
|
||||
|
||||
def MinSignedBits(v: uint64) -> int:
|
||||
"""Bit width needed by a non-negative magnitude as a two's-complement value."""
|
||||
return MinUnsignedBits(v) + 1
|
||||
|
||||
|
||||
def Pow2Width(bits: int) -> int:
|
||||
"""Smallest power-of-two width >= bits, starting at the language's 8-bit minimum.
|
||||
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
# ViperC dead code / duplication / logging audit
|
||||
|
||||
Scope: `ViperC/App`, 110 `.vp` files, 90,389 lines.
|
||||
|
||||
Method: identifier references excluding comments and strings, call graph reachability from `main`, function-body similarity, and consecutive log-block scanning.
|
||||
|
||||
Status: the dead code in section 1 has been removed in this commit. Sections 2 and 3 are follow-up work.
|
||||
|
||||
---
|
||||
|
||||
## 1. Dead code
|
||||
|
||||
### 1.1 Large removable blocks
|
||||
|
||||
| Location | Size | Finding |
|
||||
|---|---:|---|
|
||||
| `lib/Phase/Phase1/Phase1Scan.vp` | 124 lines | `_AnnIsContainer` + `_ScanTreeForContainers` only recurse into each other; no external caller. |
|
||||
| `lib/Phase/BuildPipeline.vp` | 120 lines | `run_pipeline` has no caller; `link_obj_to_exe` is only called by `run_pipeline`. |
|
||||
| `lib/core/Gen/Type/TypeGenErr.vp` | 119 lines | `infer_expr_type` / `infer_return_type` / `build_param_types_str` / `is_pass_body` form a closed, unreachable cluster. |
|
||||
| `lib/core/Gen/Translator.vp`, `GenApi.vp`, `TranslatorGenMixin.vp` | 64 lines | Old wrapper-main chain; `Translator` no longer calls `_translate_wrapper_main`. |
|
||||
| `lib/core/Gen/StructGenMixin.vp` | about 120 lines | Legacy struct accessors (`mark_as_union`, `mark_has_*`, `set_parent_name`, `set_vtable_methods`, `get_field_count`, `set_struct_sha1`, ...) have zero references. |
|
||||
| `lib/core/Gen/StructGenMixin.vp` | about 50 lines | The struct type index is disabled in `find_struct_by_type` (see the comment there), but insertion and storage were still maintained. |
|
||||
| `lib/core/Gen/Call/CallGenLeafKit.vp` | 20 lines | Old `ExprCallHandle` shim (`NewExprCallHandle`, `HandleCall`, `HandlePrintfCall`, `HandlePrintCall`). |
|
||||
| `lib/Phase/Phase1/Phase1Main.vp` | 29 lines | `[temp diagnostic]` / `[EVIDENCE]` block, including an unconditional `mb.dump_stats`. |
|
||||
|
||||
### 1.2 Manual-test-only cluster
|
||||
|
||||
`lib/core/Gen/Type/TypeSpec.vp`: `TextToSpec`, `_text_to_spec`, `_spec_make_struct`, `_text_to_func`, `_check_roundtrip`, `SpecSelfCheck`. This cluster is not part of the compile flow and was removed with the other dead code.
|
||||
|
||||
### 1.3 Zero-reference functions
|
||||
|
||||
- `AssignGenMixin.vp`: `_is_cdefine_marker_node`, `_recover_found`, `_is_generic_tp_or_nested`
|
||||
- `ClassGenBase.vp`: `lookup_decor_arg0`, `lookup_exception_parent`
|
||||
- `ClassGenEnum.vp`: `_is_state_annotation`
|
||||
- `ClassGenGeneric.vp`: `_is_generic_function`
|
||||
- `StmtGenDeclMixin.vp`: `get_stmt_kind_name`
|
||||
- `TypeGenBase.vp`: `_dbg_check_ast_floats`
|
||||
- `TypeGenCType.vp`: `ScanCTypedefDeclarations`
|
||||
- `TypeGenResolve.vp`: `_long_llvm_ty`, `_is_cptr_marker`, `_is_ref_marker`, `_is_byteorder_marker`, `_is_cptr_subscript`, `_is_storage_class_qualifier`
|
||||
- `TypeSpec.vp`: `SpecSignFromTypeName`, `SpecIsPtr`, `SpecPointedTo`, `SpecBitWidth`
|
||||
- `VarGenMixin.vp`: `get_or_create_var`, `repl_func_sig_label`
|
||||
- `IncludesScanner.vp`: `find_entry_by_module`
|
||||
- `Phase1Main.vp`: `_InjectedCreatesCycle`
|
||||
- `StubMergerCollect.vp`: `_load_includes_sha1_set`
|
||||
- `StubMergerIR.vp`: `_is_submodule_of_imported`
|
||||
- `StubMergerStore.vp`: `GetSha1StoreModArrPtr`, `GetSha1StoreArr`
|
||||
- `IntFacts.vp`: `MinSignedBits`
|
||||
- `DebugInfoGen.vp`: `IsEnabled`
|
||||
- `LspIndex.vp`: `SymbolsOf`
|
||||
- `LspSigCache.vp`: `_MapHash`, `_InsertMap`, `_RebuildMap`
|
||||
- `GenApi.vp`: `reset_module_methods_emitted`, `get_ret_borrow`, `apply_escape_clears`
|
||||
- `VLogger.vp`: `set_level`, `compile_error`, `compile_warning`, `critical` (method and module wrapper)
|
||||
|
||||
Retained: `ClassGenEnum.RenumTableDump` is referenced from a diagnostic f-string, so it is not dead despite having no direct call site.
|
||||
|
||||
### 1.4 Unused constants and fields
|
||||
|
||||
- `ClassGenBase.vp`: `BUILTIN_EH_VALUE_ERROR`, `BUILTIN_EH_RUNTIME_ERROR`, `BUILTIN_EH_EXCEPTION`
|
||||
- `GenTypes.vp`: `FUNC_RET_SIGN_UNSET`, `REPL_CTX_SIZE`, `REPL_RES_VARCOUNT_OFF`, `REPL_RES_LASTFRAG_OFF`
|
||||
- `StructGenMixin.vp`: `EC_GLOBAL_MAX`
|
||||
- `VarGenMixin.vp`: `SCOPE_BLOCK`
|
||||
- `IncludesScanner.vp`: `MAX_PATH_LEN`
|
||||
- `LspProtocol.vp`: `LSP_ERROR_METHOD_NOT_FOUND`, `LSP_ERROR_INVALID_PARAMS`
|
||||
- `LspSemantic.vp`: `ST_KEYWORD`, `ST_OPERATOR`, `ST_DECORATOR`
|
||||
- `LspSigCache.vp`: `LSP_SIG_CACHE_LINE_CAP`, `LSP_SIG_MAP_CAP`, `LSP_SIG_MAP_MASK`
|
||||
- `VLogger.vp`: `FOREGROUND_CYAN`, `FOREGROUND_MAGENTA`, `BACKGROUND_*`, `LogLevel.CRITICAL`
|
||||
- `StubGen/VpiReader.vp`: `_REPL_SU_PARENT`
|
||||
- `AssignGenMixin.RecoverFound` write-only field after `_recover_found` removal
|
||||
|
||||
---
|
||||
|
||||
## 2. Duplicate code
|
||||
|
||||
### 2.1 `VpiReader.vp`
|
||||
|
||||
| Duplicate pair | Location | Suggested refactor |
|
||||
|---|---|---|
|
||||
| `PreRegisterVpiDefaults` / `PreRegisterVpiEnums` | `2776-2807` / `2941-2972` | Shared `.vpi` file walker with a scan-mode parameter. |
|
||||
| `_ensure_vpi_ret_annot_loaded` / `_ensure_vpi_param_annot_loaded` | `1077-1116` / `1705-1743` | Shared cache-load helper for the two annotation tables. |
|
||||
| `_cache_vpi_escape_annots_text` / `_cache_vpi_willfree_annots_text` | `1390-1476` / `1506-1593` | Shared line scanner plus marker-specific parser. |
|
||||
| `_lookup_vpi_escape_dir` / `_lookup_vpi_escape_packed` / `_lookup_vpi_willfree` | `1288-1314` / `1478-1503` / `1595-1620` | Shared key builder and table lookup. |
|
||||
|
||||
### 2.2 `CallGenCall.vp` / `CallGenDispatch.vp`
|
||||
|
||||
`CallGenCall._translate_list_builtin_call` (`1470-1627`) duplicates the list `get` / `pop` / `append` lowering inside `CallGenDispatch._tc_h6_method_dispatch` (`796-1456`), about 125 lines.
|
||||
|
||||
### 2.3 `StubMergerStore.vp`
|
||||
|
||||
- `AppendToSha1MapStore` (`202-257`) / `AddIncludeToSha1MapStore` (`260-310`): same initialization, deduplication, and writes.
|
||||
- `LookupSha1ByAlias` (`411-449`) / `LookupModNameByAlias` (`452-488`): same alias parsing.
|
||||
|
||||
### 2.4 `Phase2/__init__.vp`
|
||||
|
||||
`_ScanDirForPyFiles` (`50-131`) and `_ScanDirForAsmFiles` (`134-210`) share the whole directory walk; only the suffix filter and entry logging differ.
|
||||
|
||||
### 2.5 Other duplicate pairs
|
||||
|
||||
| Pair | Location |
|
||||
|---|---|
|
||||
| `ViperBuild._JoinSep` / `_JoinSpace` | `232-260` / `261-288` |
|
||||
| `ClassGenGeneric._find_generic_func_tp_params` / `_find_generic_template_tp_params` | `197-226` / `619-649` |
|
||||
| `AssignGenMixin._normalize_cdefine_bits` / `ExprGenCdef._cdefine_bits_value` | `196-219` / `873-896` |
|
||||
| `AssignGenMixin` / `ExprGenCls` `_extract_annot_class_name`, `_cls_has_dunder` | `4211-4259` / `326-383`, `4298-4319` / `303-323` |
|
||||
| `CallGenBase` / `ExprGenAttr` `_lookup_module_sha1` | `333-350` / `185-197` |
|
||||
| BuildPipeline path setup | `662-670` / `840-848` |
|
||||
| MatchGen guard translation | `862-869` / `1400-1407` |
|
||||
| Async coroutine frame prologue | `1648-1675` / `1676-1691` |
|
||||
| LSP `_Doc`, `_Cat`, `_Trim`, `_HexVal`, `_SliceStr`, `_ByteAt`, `_HashText` | several LSP files |
|
||||
|
||||
---
|
||||
|
||||
## 3. Unnecessary large logging
|
||||
|
||||
### P0: remove
|
||||
|
||||
1. `lib/Phase/Phase1/Phase1Main.vp`: `[temp diagnostic]` block with `mb.dump_stats("phase1-pre")` and long `[EVIDENCE]` lines.
|
||||
2. `lib/core/Gen/Type/TypeGenResolve.vp`: four `[GENERIC-DBG]` info logs; the following `fatal_error` already reports the failure.
|
||||
3. `lib/Stub/StubGen/VpiReader.vp`: `dbg_tn` / `dbg_ct` / `dbg_cd` debug block marked `# questionable`.
|
||||
|
||||
### P1: log level is not enforced
|
||||
|
||||
`VLogger` stores `_level` but `_log` never compares it, so `VLogger.debug` prints at the default INFO level. Either add `if level < self._level: return 0` at the start of `_log`, or remove the two debug call sites.
|
||||
|
||||
### P2: verbose but intentional
|
||||
|
||||
- REPL 11-line startup banner.
|
||||
- `main.vp` 21-line argument dump.
|
||||
- `VpiReader` includes-layout refusal explanations; these are rate-limited but can be aggregated behind verbose mode.
|
||||
Reference in new issue
Block a user