From 38a521fcd2701ad337770933a8a2e68bcbe5d561 Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Mon, 2 Nov 2026 13:24:30 +0800 Subject: [PATCH] refactor: filter debug logs and deduplicate VPI loaders Suppress DEBUG output at the default INFO level, share the VPI pre-registration walker, and share the .vpi read path between the annotation cache loaders. Source-only change: 53 added, 81 deleted (net -28 lines). --- App/lib/Stub/StubGen/VpiReader.vp | 132 ++++++++++++------------------ App/lib/core/VLogger.vp | 2 + DEAD_CODE_REPORT.md | 10 ++- 3 files changed, 59 insertions(+), 85 deletions(-) diff --git a/App/lib/Stub/StubGen/VpiReader.vp b/App/lib/Stub/StubGen/VpiReader.vp index dc6ee89..e381e47 100644 --- a/App/lib/Stub/StubGen/VpiReader.vp +++ b/App/lib/Stub/StubGen/VpiReader.vp @@ -1071,6 +1071,33 @@ def _grow_vpi_ret_cache() -> int: return 1 +# _read_vpi_for_cache - read one .vpi file into a malloc buffer. +# status[0]: 0 = path/open/allocation failure, 1 = read attempted (None means empty). +def _read_vpi_for_cache(src_sha1: str, temp_dir: str, status: int*) -> bytes: + if status is not None: status[0] = 0 + if src_sha1 is None or temp_dir is None: return None + vpi_path: str = _vpi_build_path(temp_dir, src_sha1, ".vpi") + if vpi_path is None: return None + f: io.File* = io.File(vpi_path, io.MODE.R) + if f.closed: + stdlib.free(vpi_path) + return None + vpi_buf: bytes = stdlib.malloc(VPI_READ_BUF_SIZE) + if vpi_buf is None: + f.close() + stdlib.free(vpi_path) + return None + bytes_read: LONG = f.read_all(vpi_buf, VPI_READ_BUF_SIZE) + f.close() + stdlib.free(vpi_path) + if status is not None: status[0] = 1 + if bytes_read <= 0: + stdlib.free(vpi_buf) + return None + if bytes_read < VPI_READ_BUF_SIZE: vpi_buf[bytes_read] = 0 + return vpi_buf + + # _ensure_vpi_ret_annot_loaded - lazily load a module's vpi stub return-annotation cache. # On cache miss reads {temp_dir}/{sha1[0:2]}/{sha1}.vpi directly; idempotent. def _ensure_vpi_ret_annot_loaded(src_sha1: str, temp_dir: str) -> int: @@ -1088,28 +1115,11 @@ def _ensure_vpi_ret_annot_loaded(src_sha1: str, temp_dir: str) -> int: for pre_i in range(int(_g_vpi_ret_count)): pre_key: bytes = _g_vpi_ret_keys + pre_i * VPI_KEY_LEN if pre_key[0] == s0 and pre_key[1] == s1 and string.strncmp(pre_key, src_sha1, sha1_len) == 0 and pre_key[sha1_len] == '.': return 1 - # build {temp_dir}/{slice}/{sha1}.vpi — the slice must follow Config.Sha1SliceLevel, - # exactly like the writer (StubMergerCollect._sliced_path); hardcoding two hex chars - # makes every lookup miss when the project uses slice level 0 or 2. - vpi_path: str = _vpi_build_path(temp_dir, src_sha1, ".vpi") - if vpi_path is None: return 0 - f: io.File* = io.File(vpi_path, io.MODE.R) - if f.closed: - stdlib.free(vpi_path) - return 0 - vpi_buf: bytes = stdlib.malloc(VPI_READ_BUF_SIZE) - if vpi_buf is None: - f.close() - stdlib.free(vpi_path) - return 0 - bytes_read: LONG = f.read_all(vpi_buf, VPI_READ_BUF_SIZE) - f.close() - if bytes_read > 0: - if bytes_read < VPI_READ_BUF_SIZE: vpi_buf[bytes_read] = 0 - # 单一入口:一次喂齐 ret / param / 逃逸摘要(见 _cache_vpi_annots_text 注释) - _cache_vpi_annots_text(vpi_buf, src_sha1) + st_r: int = 0 + vpi_buf: bytes = _read_vpi_for_cache(src_sha1, temp_dir, &st_r) + if vpi_buf is None: return st_r + _cache_vpi_annots_text(vpi_buf, src_sha1) stdlib.free(vpi_buf) - stdlib.free(vpi_path) return 1 @@ -1703,27 +1713,11 @@ def _ensure_vpi_param_annot_loaded(src_sha1: str, temp_dir: str) -> int: for pre_i in range(int(_g_vpi_param_count)): pre_key: bytes = _g_vpi_param_keys + pre_i * VPI_PARAM_KEY_LEN if pre_key[0] == s0 and pre_key[1] == s1 and string.strncmp(pre_key, src_sha1, sha1_len) == 0 and pre_key[sha1_len] == '.': return 1 - # build {temp_dir}/{slice}/{sha1}.vpi — the slice must follow Config.Sha1SliceLevel, - # exactly like the writer (StubMergerCollect._sliced_path); hardcoding two hex chars - # makes every lookup miss when the project uses slice level 0 or 2. - vpi_path: str = _vpi_build_path(temp_dir, src_sha1, ".vpi") - if vpi_path is None: return 0 - f: io.File* = io.File(vpi_path, io.MODE.R) - if f.closed: - stdlib.free(vpi_path) - return 0 - vpi_buf: bytes = stdlib.malloc(VPI_READ_BUF_SIZE) - if vpi_buf is None: - f.close() - stdlib.free(vpi_path) - return 0 - bytes_read: LONG = f.read_all(vpi_buf, VPI_READ_BUF_SIZE) - f.close() - if bytes_read > 0: - if bytes_read < VPI_READ_BUF_SIZE: vpi_buf[bytes_read] = 0 - _cache_vpi_param_annots_text(vpi_buf, src_sha1) + st_p: int = 0 + vpi_buf: bytes = _read_vpi_for_cache(src_sha1, temp_dir, &st_p) + if vpi_buf is None: return st_p + _cache_vpi_param_annots_text(vpi_buf, src_sha1) stdlib.free(vpi_buf) - stdlib.free(vpi_path) return 1 @@ -2744,39 +2738,6 @@ def _ScanVpiDefaults(pool: memhub.MemBuddy*, vbuf: bytes, buf_len: sizet, sha1: stdlib.free(strs) return reg -# PreRegisterVpiDefaults - 在翻译开始前,把 store 里所有模块的 .vpi 缺省值一次性登记 -def PreRegisterVpiDefaults(pool: memhub.MemBuddy*, temp_dir: str) -> int: - """预登记所有模块 .vpi 里的函数缺省值;返回登记的函数个数""" - if pool is None or temp_dir is None: return 0 - n: int = StubMergerStore.GetSha1StoreCount() - if n <= 0: return 0 - sha1_arr: bytes = StubMergerStore.GetSha1StoreArrPtr() - if sha1_arr is None: return 0 - total_reg: int = 0 - si: int - for si in range(int(n)): - sha1: str = sha1_arr + sizet(si) * 17 - if sha1[0] == '\0': continue - fpath: str = _vpi_build_path(temp_dir, sha1, ".vpi") - if fpath is None: continue - vf: io.File* = io.File(fpath, io.MODE.R) - stdlib.free(fpath) - if vf.closed: continue - vbuf: bytes = stdlib.malloc(VPI_READ_BUF_SIZE) - if vbuf is None: - vf.close() - continue - nrd: LONG = vf.read_all(vbuf, VPI_READ_BUF_SIZE) - vf.close() - if nrd <= 0: - stdlib.free(vbuf) - continue - if nrd < VPI_READ_BUF_SIZE: vbuf[nrd] = 0 - total_reg += _ScanVpiDefaults(pool, vbuf, string.strlen(vbuf), sha1) - stdlib.free(vbuf) - return total_reg - - # 单个 .vpi 的枚举成员上限(与 ENUM_MEMBER_MAX 同量级) define VPI_ENUM_MEMBER_MAX = 128 @@ -2908,10 +2869,8 @@ def _ScanVpiEnumsText(pool: memhub.MemBuddy*, vbuf: bytes, vlen: sizet, sha1: st return reg -# PreRegisterVpiEnums - 翻译开始前,把所有模块 .vpi 里的 `enum X:` 一次性登记进 EnumTable。 -# 与 PreRegisterVpiDefaults 同理:消除"谁先注入谁登记"的时序依赖。 -def PreRegisterVpiEnums(pool: memhub.MemBuddy*, temp_dir: str) -> int: - """预登记所有模块 .vpi 里的枚举;返回登记的枚举个数""" +# _PreRegisterVpiFiles - run one pre-registration scanner over all registered .vpi files. +def _PreRegisterVpiFiles(pool: memhub.MemBuddy*, temp_dir: str, scan_enums: int) -> int: if pool is None or temp_dir is None: return 0 n: int = StubMergerStore.GetSha1StoreCount() if n <= 0: return 0 @@ -2937,12 +2896,24 @@ def PreRegisterVpiEnums(pool: memhub.MemBuddy*, temp_dir: str) -> int: stdlib.free(vbuf) continue if nrd < VPI_READ_BUF_SIZE: vbuf[nrd] = 0 - total_reg += _ScanVpiEnumsText(pool, vbuf, string.strlen(vbuf), sha1) + if scan_enums != 0: total_reg += _ScanVpiEnumsText(pool, vbuf, string.strlen(vbuf), sha1) + else: total_reg += _ScanVpiDefaults(pool, vbuf, string.strlen(vbuf), sha1) stdlib.free(vbuf) return total_reg -# _VpiParamCountAndVararg - 数参数个数;含 *args / **kwargs 时返回 -1(调用方跳过 arity 校验) +# PreRegisterVpiDefaults - pre-register defaults from all registered .vpi files. +def PreRegisterVpiDefaults(pool: memhub.MemBuddy*, temp_dir: str) -> int: + """Pre-register .vpi default values; returns the number of registrations.""" + return _PreRegisterVpiFiles(pool, temp_dir, 0) + + +# PreRegisterVpiEnums - pre-register enums from all registered .vpi files. +def PreRegisterVpiEnums(pool: memhub.MemBuddy*, temp_dir: str) -> int: + """Pre-register .vpi enums; returns the number of registrations.""" + return _PreRegisterVpiFiles(pool, temp_dir, 1) + + def _VpiParamCountAndVararg(vbuf: bytes, ps: sizet, pe: sizet) -> int: """Number of parameters; -1 when the list contains *args/**kwargs.""" n: int = VpiText.ParamCount(vbuf, ps, pe) @@ -2951,7 +2922,6 @@ def _VpiParamCountAndVararg(vbuf: bytes, ps: sizet, pe: sizet) -> int: return n # LookupSignDefaultsByName - 直接在定义模块 .vpi 文本里查一个函数/方法的缺省参数快照 -# 与 PreRegisterVpiDefaults 的区别:不登记进全局表,只按名字现查现用(表键不可靠时的兜底)。 # 返回 (参数个数 << 8) | 缺省值个数;-1 = 未找到或有不可重建的缺省值。 # --- signature range cache -------------------------------------------------- # LookupSignDefaultsByName re-scanned the whole .vpi text per query (one diff --git a/App/lib/core/VLogger.vp b/App/lib/core/VLogger.vp index 8d35936..22d7f6e 100644 --- a/App/lib/core/VLogger.vp +++ b/App/lib/core/VLogger.vp @@ -90,6 +90,8 @@ novirtual class Logger: # _log - core log output with segmented coloring (prefix symbol / level tag / message). # Format: ├ LEVEL: msg or ├ LEVEL[category]: msg. def _log(self, level: int, prefix_sym: str, level_str: str, msg: str, sym_color: WORD, level_color: WORD, category: str = "") -> int: + # Default level is INFO; DEBUG messages stay silent unless the level is lowered. + if level < self._level: return 0 # Color the prefix symbol self._set_color(sym_color) stdio.printf("%s ", prefix_sym) diff --git a/DEAD_CODE_REPORT.md b/DEAD_CODE_REPORT.md index d0cc016..5a4728a 100644 --- a/DEAD_CODE_REPORT.md +++ b/DEAD_CODE_REPORT.md @@ -114,7 +114,7 @@ Retained: `ClassGenEnum.RenumTableDump` is referenced from a diagnostic f-string The following duplicate implementations were merged or redirected: - `Phase2`: one directory walker for `.vp` and `.s` sources. -- `VpiReader`: shared `_vpi_method_key` for all cached-annotation lookups. +- `VpiReader`: shared `_vpi_method_key` for all cached-annotation lookups, a shared pre-registration walker for defaults/enums, and a shared `.vpi` read helper for the two annotation cache loaders. - `StubMergerStore`: shared store initialization/append and alias parsing. - `AssignGenMixin` / `ExprGenCdef`: one `TypeGenCDef.NormalizeDefineBits`. - `AssignGenMixin` / `ExprGenCls`: removed the weaker duplicate class-annotation helpers; calls now use `ExprGenCls`. @@ -124,9 +124,11 @@ The following duplicate implementations were merged or redirected: - `ViperBuild`: one list-join helper. - LSP: removed duplicate `_Doc`, `_Cat`, `_Trim`, and `_HexVal` implementations; `_HashText` keeps its local None semantics. -Measured source-only reduction for this batch: **235 added / 793 deleted = net -558 lines** (excluding `App/main.vp` and this report). +Measured source-only reduction for the main consolidation batch: **235 added / 793 deleted = net -558 lines** (excluding `App/main.vp` and this report). -Remaining candidates: `VpiReader` annotation-file scanners and cache loaders, `BuildPipeline` MLIR path setup, `LSP _SliceStr/_ByteAt`, and the list/dict iteration helpers. +Follow-up batch: VLogger level filtering and the VpiReader pre-registration/read helpers, **53 added / 81 deleted = net -28 lines**. + +Remaining candidates: `VpiReader` escape/willfree annotation scanners, `BuildPipeline` MLIR path setup, `LSP _SliceStr/_ByteAt`, and the list/dict iteration helpers. --- @@ -140,7 +142,7 @@ Remaining candidates: `VpiReader` annotation-file scanners and cache loaders, `B ### 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. +Applied: `VLogger._log` now returns early when `level < self._level`, so DEBUG messages stay silent at the default INFO level. The two debug call sites remain available for diagnostic builds. ### P2: verbose but intentional