diff --git a/App/lib/Phase/BuildPipeline.vp b/App/lib/Phase/BuildPipeline.vp index d809386..2e9193c 100644 --- a/App/lib/Phase/BuildPipeline.vp +++ b/App/lib/Phase/BuildPipeline.vp @@ -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. diff --git a/App/lib/Phase/Phase1/Phase1Main.vp b/App/lib/Phase/Phase1/Phase1Main.vp index 4e467b0..16448f2 100644 --- a/App/lib/Phase/Phase1/Phase1Main.vp +++ b/App/lib/Phase/Phase1/Phase1Main.vp @@ -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 未溢出,无需改。 diff --git a/App/lib/Phase/Phase1/Phase1Scan.vp b/App/lib/Phase/Phase1/Phase1Scan.vp index b20c8e9..3003c19 100644 --- a/App/lib/Phase/Phase1/Phase1Scan.vp +++ b/App/lib/Phase/Phase1/Phase1Scan.vp @@ -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) diff --git a/App/lib/Stub/StubGen/VpiReader.vp b/App/lib/Stub/StubGen/VpiReader.vp index 69f38c8..318b245 100644 --- a/App/lib/Stub/StubGen/VpiReader.vp +++ b/App/lib/Stub/StubGen/VpiReader.vp @@ -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: diff --git a/App/lib/Stub/StubMerger/StubMergerCollect.vp b/App/lib/Stub/StubMerger/StubMergerCollect.vp index 93ad2d8..8fe1663 100644 --- a/App/lib/Stub/StubMerger/StubMergerCollect.vp +++ b/App/lib/Stub/StubMerger/StubMergerCollect.vp @@ -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.""" diff --git a/App/lib/Stub/StubMerger/StubMergerIR.vp b/App/lib/Stub/StubMerger/StubMergerIR.vp index 2615930..198aae6 100644 --- a/App/lib/Stub/StubMerger/StubMergerIR.vp +++ b/App/lib/Stub/StubMerger/StubMergerIR.vp @@ -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: diff --git a/App/lib/Stub/StubMerger/StubMergerStore.vp b/App/lib/Stub/StubMerger/StubMergerStore.vp index 7cfe7b5..aca6642 100644 --- a/App/lib/Stub/StubMerger/StubMergerStore.vp +++ b/App/lib/Stub/StubMerger/StubMergerStore.vp @@ -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""" diff --git a/App/lib/core/Gen/AssignGenMixin.vp b/App/lib/core/Gen/AssignGenMixin.vp index 2866011..dc4e50c 100644 --- a/App/lib/core/Gen/AssignGenMixin.vp +++ b/App/lib/core/Gen/AssignGenMixin.vp @@ -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 diff --git a/App/lib/core/Gen/Call/CallGenLeafKit.vp b/App/lib/core/Gen/Call/CallGenLeafKit.vp index e82a6a1..3d2ed04 100644 --- a/App/lib/core/Gen/Call/CallGenLeafKit.vp +++ b/App/lib/core/Gen/Call/CallGenLeafKit.vp @@ -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) - diff --git a/App/lib/core/Gen/Call/CallGenMixin.vp b/App/lib/core/Gen/Call/CallGenMixin.vp index e845cab..d261727 100644 --- a/App/lib/core/Gen/Call/CallGenMixin.vp +++ b/App/lib/core/Gen/Call/CallGenMixin.vp @@ -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: diff --git a/App/lib/core/Gen/Class/ClassGenBase.vp b/App/lib/core/Gen/Class/ClassGenBase.vp index 0d93f63..9092ba3 100644 --- a/App/lib/core/Gen/Class/ClassGenBase.vp +++ b/App/lib/core/Gen/Class/ClassGenBase.vp @@ -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 diff --git a/App/lib/core/Gen/Class/ClassGenEnum.vp b/App/lib/core/Gen/Class/ClassGenEnum.vp index b979a40..d874a13 100644 --- a/App/lib/core/Gen/Class/ClassGenEnum.vp +++ b/App/lib/core/Gen/Class/ClassGenEnum.vp @@ -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 diff --git a/App/lib/core/Gen/Class/ClassGenGeneric.vp b/App/lib/core/Gen/Class/ClassGenGeneric.vp index afd3f95..3493c15 100644 --- a/App/lib/core/Gen/Class/ClassGenGeneric.vp +++ b/App/lib/core/Gen/Class/ClassGenGeneric.vp @@ -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 diff --git a/App/lib/core/Gen/DebugInfoGen.vp b/App/lib/core/Gen/DebugInfoGen.vp index 3b01450..d4cc270 100644 --- a/App/lib/core/Gen/DebugInfoGen.vp +++ b/App/lib/core/Gen/DebugInfoGen.vp @@ -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 diff --git a/App/lib/core/Gen/GenApi.vp b/App/lib/core/Gen/GenApi.vp index a66000e..48afe30 100644 --- a/App/lib/core/Gen/GenApi.vp +++ b/App/lib/core/Gen/GenApi.vp @@ -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""" diff --git a/App/lib/core/Gen/GenTypes.vp b/App/lib/core/Gen/GenTypes.vp index 2b39bf2..14af800 100644 --- a/App/lib/core/Gen/GenTypes.vp +++ b/App/lib/core/Gen/GenTypes.vp @@ -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) diff --git a/App/lib/core/Gen/Stmt/StmtGenDeclMixin.vp b/App/lib/core/Gen/Stmt/StmtGenDeclMixin.vp index 2e80e61..96bf781 100644 --- a/App/lib/core/Gen/Stmt/StmtGenDeclMixin.vp +++ b/App/lib/core/Gen/Stmt/StmtGenDeclMixin.vp @@ -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) diff --git a/App/lib/core/Gen/StructGenMixin.vp b/App/lib/core/Gen/StructGenMixin.vp index c4393a8..14bb81b 100644 --- a/App/lib/core/Gen/StructGenMixin.vp +++ b/App/lib/core/Gen/StructGenMixin.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 diff --git a/App/lib/core/Gen/Translator.vp b/App/lib/core/Gen/Translator.vp index b703afc..996ed1e 100644 --- a/App/lib/core/Gen/Translator.vp +++ b/App/lib/core/Gen/Translator.vp @@ -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") diff --git a/App/lib/core/Gen/TranslatorGenMixin.vp b/App/lib/core/Gen/TranslatorGenMixin.vp index 2269fa6..e48041a 100644 --- a/App/lib/core/Gen/TranslatorGenMixin.vp +++ b/App/lib/core/Gen/TranslatorGenMixin.vp @@ -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.""" diff --git a/App/lib/core/Gen/Type/TypeGenBase.vp b/App/lib/core/Gen/Type/TypeGenBase.vp index 9202b52..57986de 100644 --- a/App/lib/core/Gen/Type/TypeGenBase.vp +++ b/App/lib/core/Gen/Type/TypeGenBase.vp @@ -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: diff --git a/App/lib/core/Gen/Type/TypeGenCType.vp b/App/lib/core/Gen/Type/TypeGenCType.vp index 33dbd23..cc4292b 100644 --- a/App/lib/core/Gen/Type/TypeGenCType.vp +++ b/App/lib/core/Gen/Type/TypeGenCType.vp @@ -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 diff --git a/App/lib/core/Gen/Type/TypeGenErr.vp b/App/lib/core/Gen/Type/TypeGenErr.vp index c352298..c97eabd 100644 --- a/App/lib/core/Gen/Type/TypeGenErr.vp +++ b/App/lib/core/Gen/Type/TypeGenErr.vp @@ -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 \ No newline at end of file diff --git a/App/lib/core/Gen/Type/TypeGenResolve.vp b/App/lib/core/Gen/Type/TypeGenResolve.vp index 8eaa999..c73bf69 100644 --- a/App/lib/core/Gen/Type/TypeGenResolve.vp +++ b/App/lib/core/Gen/Type/TypeGenResolve.vp @@ -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.` 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 diff --git a/App/lib/core/Gen/Type/TypeSpec.vp b/App/lib/core/Gen/Type/TypeSpec.vp index d3deacb..d41da2e 100644 --- a/App/lib/core/Gen/Type/TypeSpec.vp +++ b/App/lib/core/Gen/Type/TypeSpec.vp @@ -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 diff --git a/App/lib/core/Gen/VarGenMixin.vp b/App/lib/core/Gen/VarGenMixin.vp index 87efc33..e8751b2 100644 --- a/App/lib/core/Gen/VarGenMixin.vp +++ b/App/lib/core/Gen/VarGenMixin.vp @@ -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*: diff --git a/App/lib/core/IncludesScanner.vp b/App/lib/core/IncludesScanner.vp index 573ce81..ced7271 100644 --- a/App/lib/core/IncludesScanner.vp +++ b/App/lib/core/IncludesScanner.vp @@ -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.""" diff --git a/App/lib/core/Repl.vp b/App/lib/core/Repl.vp index 23a7510..4721927 100644 --- a/App/lib/core/Repl.vp +++ b/App/lib/core/Repl.vp @@ -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: diff --git a/App/lib/core/VLogger.vp b/App/lib/core/VLogger.vp index 57e632b..8d35936 100644 --- a/App/lib/core/VLogger.vp +++ b/App/lib/core/VLogger.vp @@ -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) diff --git a/App/lib/lsp/LspIndex.vp b/App/lib/lsp/LspIndex.vp index b22b923..fe3fd04 100644 --- a/App/lib/lsp/LspIndex.vp +++ b/App/lib/lsp/LspIndex.vp @@ -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).""" diff --git a/App/lib/lsp/LspProtocol.vp b/App/lib/lsp/LspProtocol.vp index cf37645..3b28f5f 100644 --- a/App/lib/lsp/LspProtocol.vp +++ b/App/lib/lsp/LspProtocol.vp @@ -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 diff --git a/App/lib/lsp/LspSemantic.vp b/App/lib/lsp/LspSemantic.vp index 10c53ca..987627d 100644 --- a/App/lib/lsp/LspSemantic.vp +++ b/App/lib/lsp/LspSemantic.vp @@ -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 diff --git a/App/lib/lsp/LspSigCache.vp b/App/lib/lsp/LspSigCache.vp index f352302..9a5eb6d 100644 --- a/App/lib/lsp/LspSigCache.vp +++ b/App/lib/lsp/LspSigCache.vp @@ -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): diff --git a/App/lib/types/IntFacts.vp b/App/lib/types/IntFacts.vp index a583023..93d9689 100644 --- a/App/lib/types/IntFacts.vp +++ b/App/lib/types/IntFacts.vp @@ -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. diff --git a/DEAD_CODE_REPORT.md b/DEAD_CODE_REPORT.md new file mode 100644 index 0000000..4e34535 --- /dev/null +++ b/DEAD_CODE_REPORT.md @@ -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.