Files
ViperC/App/lib/core/Gen/DebugInfoGen.vp
T
TermiNexus c2d7480f3d fix(transpyv): 槽视图统一 cast 到声明类型(47 处,含 codegen 自身)
同 includes 的同类修正:把一级指针 i8* 隐式塞进二级注解(i8** / str*)。
TransPyV 侧 coerce(ExprGenBase._ptr_cast_reason)没有 TPC 那样的「值侧是显式
指针 cast ⇒ 按 bitcast 重解释」豁免,故一旦用新构建的 TransPyV 编到这些文件
必然 fail-fast(回归测试首爆点在 includes/llvmlite 侧,本仓同形态站点随后必然
同样触发)。

涉及的两种形态:
  · `slot: str | t.CPtr = (t.CPtr)(addr)`   —— cast 目标是一级 t.CPtr
  · `nm_slot: str* = (t.CPtr)(addr)`        —— 注解 str* 即 i8**
统一改为 cast 到声明类型:`(str | t.CPtr)(addr)` / `str*(addr)`(后者与
includes/sys.vp `argv_arr: str* = str*(...)` 同一写法)。

本仓 47 处 / 6 文件:
  App/lib/core/Gen/VarGenMixin.vp 3
  App/lib/core/Gen/TranslatorGenMixin.vp 8
  App/lib/core/Gen/Func/FuncGenMixin.vp 13
  App/lib/core/Gen/AsyncGenMixin.vp 15
  App/lib/core/Gen/DebugInfoGen.vp 6
  App/lib/Stub/StubGen/Converter.vp 2
Converter.vp:107 的 `_g_pyi_cdef_names: str | t.CPtr = None` 保持原样
(None 即空指针,任何指针类型均可,不属本类问题)。

只提交本 6 个文件;本仓其余未提交改动(CallGenBase/CallGenDispatch/CallGenFd/
CallGenProp/Generator/Translator/VLogger)与本迁移无关,保持不动。
2026-09-29 15:10:33 +08:00

472 lines
19 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import t, c
from stdint import *
import llvmlite
import memhub
import string
import stdlib
import viperlib
import w32.win32file as w32file
import lib.core.VLogger as VLogger
# ============================================================
# DebugInfoGen - function-level DWARF metadata emitter (llvmlite text IR)
#
# Node layout mirrors TPC lib/core/LLVMCG/DebugInfo.py node-for-node:
# !DIFile -> !{ null } -> !DISubroutineType -> distinct !DICompileUnit
# -> distinct !DISubprogram (per define) -> !DILocation (per statement)
# module tail: !llvm.dbg.cu + !llvm.module.flags (Debug Info Version 3)
#
# Enabled only while --traceback is on: the Phase layer mirrors
# BuildPipeline._traceback_mode here via SetEnabled(). While disabled every
# entry point returns before touching any llvmlite DI API, so the emitted IR
# stays byte-identical to the pre-DWARF output.
#
# Scope/statement hooks are driven from the Gen layer:
# Translator.translate -> BeginModule / Finalize
# FuncGenDef.translate_function_def / ClassGenVTable._translate_method /
# FuncGenMixin (nested + lambda) -> PushFunction / PopFunction
# StmtGenMixin.translate_stmt -> SetStatement
# ============================================================
# ---- module-scope state (reset per module by BeginModule) ----
_g_enabled: int = 0
_g_finalized: int = 0
_g_mod: llvmlite.LLVMModule* = None
_g_file_path: str = None
_g_file_id: int = -1
_g_null_id: int = -1
_g_subtype_id: int = -1
_g_cu_id: int = -1
_g_cur_sp: int = -1
_g_cur_entry_ref: str = None
_g_cur_builder: llvmlite.IRBuilder* = None
_g_depth: int = 0
# Push/Pop save stack: enclosing (sp id + 1, entry DILocation ref, builder)
# per level. +1 so 0 means "no enclosing function" (sp ids are >= 0). The saved
# builder is what makes SetStatement reject a builder that is not the one the
# current DISubprogram was pushed on (e.g. an async state machine emitted inside
# a bracketed function): such instructions must carry no !dbg, otherwise LLVM's
# verifier rejects "!dbg attachment points at wrong subprogram for function".
DBG_STACK_MAX: t.CDefine = 256
_g_stk_sp: sizet* = None
_g_stk_ref: str = None
_g_stk_bld: str = None
# DILocation cache: direct-mapped, up to 8-way linear probe, key (sp, line, col).
DBG_LOC_SLOTS: t.CDefine = 8192
DBG_LOC_PROBE: t.CDefine = 8
DBG_LOC_KEY_STRIDE: t.CDefine = 3
_g_loc_key: sizet* = None
_g_loc_ref: str = None
_g_loc_used: bytes = None
# ============================================================
# SetEnabled / IsEnabled - mirror the --traceback switch
# ============================================================
def SetEnabled(flag: bool) -> int:
"""Enable/disable DI emission (mirrors BuildPipeline._traceback_mode)."""
global _g_enabled
if flag:
_g_enabled = 1
else:
_g_enabled = 0
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
if _g_stk_sp is not None:
return 0
sbuf1: bytes = stdlib.malloc(DBG_STACK_MAX * 8)
sbuf2: bytes = stdlib.malloc(DBG_STACK_MAX * 8)
sbuf3: bytes = stdlib.malloc(DBG_STACK_MAX * 8)
kbuf: bytes = stdlib.malloc(DBG_LOC_SLOTS * DBG_LOC_KEY_STRIDE * 8)
rbuf: bytes = stdlib.malloc(DBG_LOC_SLOTS * 8)
ubuf: bytes = stdlib.malloc(DBG_LOC_SLOTS)
if sbuf1 is None or sbuf2 is None or sbuf3 is None or kbuf is None or rbuf is None or ubuf is None:
VLogger.error("--traceback: DWARF 元数据缓冲分配失败,终止编译", "DWARF")
return 1
string.memset(sbuf1, 0, DBG_STACK_MAX * 8)
string.memset(sbuf2, 0, DBG_STACK_MAX * 8)
string.memset(sbuf3, 0, DBG_STACK_MAX * 8)
string.memset(kbuf, 0, DBG_LOC_SLOTS * DBG_LOC_KEY_STRIDE * 8)
string.memset(rbuf, 0, DBG_LOC_SLOTS * 8)
string.memset(ubuf, 0, DBG_LOC_SLOTS)
_g_stk_sp = (sizet*)(sbuf1)
_g_stk_ref = sbuf2
_g_stk_bld = sbuf3
_g_loc_key = (sizet*)(kbuf)
_g_loc_ref = rbuf
_g_loc_used = ubuf
return 0
def _RefOf(pool: memhub.MemBuddy*, node_id: int) -> str:
"""Allocate a persistent "!N" reference string for a DI node id."""
buf: str = pool.alloc(24)
if buf is None:
return None
viperlib.snprintf(buf, 24, "!%d", node_id)
return buf
# ============================================================
# BeginModule - start of one module translation
# ============================================================
def BeginModule(mod: llvmlite.LLVMModule*, file_path: str) -> int:
"""Reset per-module DI state. Call once at the start of each module."""
global _g_mod, _g_file_path, _g_finalized, _g_file_id, _g_null_id
global _g_subtype_id, _g_cu_id, _g_cur_sp, _g_cur_entry_ref, _g_cur_builder, _g_depth
if _g_enabled == 0:
return 0
if _EnsureStorage() != 0:
return 1
_g_mod = mod
_g_file_path = file_path
_g_finalized = 0
_g_file_id = -1
_g_null_id = -1
_g_subtype_id = -1
_g_cu_id = -1
_g_cur_sp = -1
_g_cur_entry_ref = None
_g_cur_builder = None
_g_depth = 0
if _g_loc_used is not None:
string.memset(_g_loc_used, 0, DBG_LOC_SLOTS)
return 0
# ============================================================
# _EnsureModuleNodes - DIFile / !{ null } / DISubroutineType / DICompileUnit
# (created lazily at the first define, once per module)
# ============================================================
def _EnsureModuleNodes(pool: memhub.MemBuddy*) -> int:
global _g_file_id, _g_null_id, _g_subtype_id, _g_cu_id
if _g_cu_id >= 0:
return 0
mod: llvmlite.LLVMModule* = _g_mod
if mod is None:
return 1
# DIFile: **绝对**路径(正斜杠),filename 保留整条路径。
# 为什么必须绝对:本工程给 CurrentFile 的是**项目相对**路径(如
# "./Test/LLvmLiteTest/App/main.vp"),LLVM 会把该目录写成 CU 的 DW_AT_comp_dir,
# 而主文件条目用 `dir_index: 0` 指向 comp_dir;stackmap 写入端只解析
# `llvm-dwarfdump --debug-line`(其中**没有** comp_dir)⇒ 行记录只能退化成裸文件名
# "main.vp":运行期既显示不全,也无法按路径重读源码行(实证:曾整段栈都打印
# `File "main.vp", line N` 而无源码行)。
# 绝对化后,无论 LLVM 保留整条 name(写入端 `_tb_is_abs_path` 命中 ⇒ 原样采用)
# 还是把它拆成 dir+basename(dir_index 指向同一个绝对目录 ⇒ 拼回绝对路径),
# 结果都是绝对路径 —— 故本改动不需要动写入端。
src: str = _g_file_path
if src is None:
src = ""
slen: sizet = string.strlen(src)
cap_n: sizet = slen + 4112
nbuf: str = pool.alloc(cap_n)
if nbuf is None:
VLogger.error("--traceback: DIFile 路径缓冲分配失败,终止编译", "DWARF")
return 1
if slen > 0:
string.strncpy(nbuf, src, slen)
nbuf[slen] = '\0'
if slen > 0 and nbuf[0] != '/' and not (slen >= 2 and nbuf[1] == ':'):
# 相对路径 → 前置当前工作目录。用 w32file 低层调用(同 Repl.vp),
# 不经 os._mbuddy:编译器进程未必初始化过那个内存管理器。
CwdBuf: bytes = pool.alloc(4112)
if CwdBuf is not None:
CwdBuf[0] = '\0'
nc: ULONG = w32file.GetCurrentDirectoryA(ULONG(4111), CwdBuf)
if nc > 0:
# 防御:缓冲区不足时 GetCurrentDirectoryA 的写入内容未定义,强制补 NUL
CwdBuf[4110] = '\0'
viperlib.snprintf(nbuf, cap_n, "%s/%s", CwdBuf, src)
# 归一所有反斜杠(CWD 段同样可能含 '\')
flen: sizet = string.strlen(nbuf)
ni: sizet = 0
for ni in range(flen):
if nbuf[ni] == '\\':
nbuf[ni] = '/'
# filename 取整条路径;directory 另存其目录前缀(两段各自 NUL 结尾,互不污染)
fname: str = nbuf
directory: str = ""
slash: str = string.strrchr(nbuf, '/')
if slash is not None:
dlen: sizet = string.strlen(nbuf) - string.strlen(slash + 1) - 1
dirbuf: str = pool.alloc(dlen + 1)
if dirbuf is not None:
string.strncpy(dirbuf, nbuf, dlen)
dirbuf[dlen] = '\0'
directory = dirbuf
dsize: sizet = string.strlen(fname) + string.strlen(directory) + 96
dbuf: str = pool.alloc(dsize)
if dbuf is None:
VLogger.error("--traceback: DIFile 节点缓冲分配失败,终止编译", "DWARF")
return 1
viperlib.snprintf(dbuf, dsize, "!DIFile(filename: \"%s\", directory: \"%s\")", fname, directory)
fid: int = llvmlite.module_add_di_node(mod, pool, dbuf)
if fid < 0:
VLogger.error("--traceback: DIFile 元数据节点分配失败,终止编译", "DWARF")
return 1
_g_file_id = fid
# types: !{ null } placeholder list, then DISubroutineType referencing it
nid: int = llvmlite.module_add_di_node(mod, pool, "!{ null }")
if nid < 0:
VLogger.error("--traceback: DISubroutineType 类型表分配失败,终止编译", "DWARF")
return 1
_g_null_id = nid
tbuf: str = pool.alloc(64)
if tbuf is None:
VLogger.error("--traceback: DISubroutineType 缓冲分配失败,终止编译", "DWARF")
return 1
viperlib.snprintf(tbuf, 64, "!DISubroutineType(types: !%d)", _g_null_id)
tid: int = llvmlite.module_add_di_node(mod, pool, tbuf)
if tid < 0:
VLogger.error("--traceback: DISubroutineType 元数据节点分配失败,终止编译", "DWARF")
return 1
_g_subtype_id = tid
cbuf: str = pool.alloc(256)
if cbuf is None:
VLogger.error("--traceback: DICompileUnit 缓冲分配失败,终止编译", "DWARF")
return 1
viperlib.snprintf(cbuf, 256,
"distinct !DICompileUnit(language: DW_LANG_C99, file: !%d, producer: \"TPC\", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug)",
_g_file_id)
cid: int = llvmlite.module_add_di_node(mod, pool, cbuf)
if cid < 0:
VLogger.error("--traceback: DICompileUnit 元数据节点分配失败,终止编译", "DWARF")
return 1
_g_cu_id = cid
return 0
# ============================================================
# _LocRefAt - read a cached DILocation ref string pointer
# ============================================================
def _LocRefAt(slot: int) -> str:
slotp: str | t.CPtr = (str | t.CPtr)(uint64(_g_loc_ref) + sizet(slot) * 8)
r: str = slotp[0]
return r
# ============================================================
# _EnsureLocation - DILocation ref for (sp, line, col), created + cached once
# ============================================================
def _EnsureLocation(pool: memhub.MemBuddy*, sp: int, line: int, col: int) -> str:
if line <= 0:
return _g_cur_entry_ref
c: int = col
if c <= 0:
c = 1
if _g_loc_used is None or _g_mod is None:
return None
h: uint64 = (uint64(sp) * 1000003 + uint64(line) * 31 + uint64(c)) & uint64(DBG_LOC_SLOTS - 1)
pi: int = 0
while pi < DBG_LOC_PROBE:
slot: int = int((h + uint64(pi)) & uint64(DBG_LOC_SLOTS - 1))
if _g_loc_used[slot] == 0:
break
kbase: int = slot * DBG_LOC_KEY_STRIDE
if int(_g_loc_key[kbase]) == sp and int(_g_loc_key[kbase + 1]) == line \
and int(_g_loc_key[kbase + 2]) == c:
return _LocRefAt(slot)
pi += 1
lbuf: str = pool.alloc(96)
if lbuf is None:
return None
viperlib.snprintf(lbuf, 96, "!DILocation(line: %d, column: %d, scope: !%d)", line, c, sp)
lid: int = llvmlite.module_add_di_node(_g_mod, pool, lbuf)
if lid < 0:
VLogger.error("--traceback: DILocation 元数据节点分配失败,终止编译", "DWARF")
return None
lref: str = _RefOf(pool, lid)
if lref is None:
VLogger.error("--traceback: DILocation 引用缓冲分配失败,终止编译", "DWARF")
return None
if pi < DBG_LOC_PROBE:
slot2: int = int((h + uint64(pi)) & uint64(DBG_LOC_SLOTS - 1))
kbase2: int = slot2 * DBG_LOC_KEY_STRIDE
_g_loc_key[kbase2] = sizet(sp)
_g_loc_key[kbase2 + 1] = sizet(line)
_g_loc_key[kbase2 + 2] = sizet(c)
rslot: str | t.CPtr = (str | t.CPtr)(uint64(_g_loc_ref) + sizet(slot2) * 8)
rslot[0] = lref
_g_loc_used[slot2] = 1
return lref
# ============================================================
# PushFunction - create the DISubprogram for one define
# ============================================================
def PushFunction(pool: memhub.MemBuddy*, builder: llvmlite.IRBuilder*,
func: llvmlite.Function*, line: int, col: int) -> int:
"""Attach a DISubprogram to one define and set the builder's entry DILocation.
Returns 1 when pushed (caller must call PopFunction), 0 when DI is disabled
or not applicable. Every instruction emitted afterwards on `builder` carries
", !dbg !N" until the next SetStatement.
"""
global _g_cur_sp, _g_cur_entry_ref, _g_cur_builder, _g_depth
if _g_enabled == 0:
return 0
if pool is None or builder is None or func is None or _g_mod is None:
return 0
if _g_depth >= DBG_STACK_MAX:
VLogger.error("--traceback: DWARF 函数嵌套深度超上限,终止编译", "DWARF")
return 0
if _EnsureModuleNodes(pool) != 0:
return 0
name: str = llvmlite.function_get_name(func)
if name is None:
VLogger.error("--traceback: 函数名为空,无法发射 DISubprogram,终止编译", "DWARF")
return 0
dline: int = line
if dline <= 0:
dline = 1
dcol: int = col
if dcol <= 0:
dcol = 1
ssize: sizet = string.strlen(name) + 320
spbuf: str = pool.alloc(ssize)
if spbuf is None:
VLogger.error("--traceback: DISubprogram 缓冲分配失败,终止编译", "DWARF")
return 0
viperlib.snprintf(spbuf, ssize,
"distinct !DISubprogram(name: \"%s\", scope: !%d, file: !%d, type: !%d, line: %d, scopeLine: %d, unit: !%d, spFlags: DISPFlagDefinition)",
name, _g_file_id, _g_file_id, _g_subtype_id, dline, dline, _g_cu_id)
sp_id: int = llvmlite.module_add_di_node(_g_mod, pool, spbuf)
if sp_id < 0:
VLogger.error("--traceback: DISubprogram 元数据节点分配失败,终止编译", "DWARF")
return 0
sp_ref: str = _RefOf(pool, sp_id)
if sp_ref is None:
VLogger.error("--traceback: DISubprogram 引用缓冲分配失败,终止编译", "DWARF")
return 0
llvmlite.function_set_dbg(func, sp_ref)
# save the enclosing function's DI context (LIFO), then switch to this one
_g_stk_sp[_g_depth] = sizet(_g_cur_sp + 1)
rslot: str | t.CPtr = (str | t.CPtr)(uint64(_g_stk_ref) + sizet(_g_depth) * 8)
rslot[0] = _g_cur_entry_ref
bslot: str | t.CPtr = (str | t.CPtr)(uint64(_g_stk_bld) + sizet(_g_depth) * 8)
bslot[0] = _g_cur_builder
_g_depth += 1
_g_cur_sp = sp_id
_g_cur_builder = builder
entry_ref: str = _EnsureLocation(pool, sp_id, dline, dcol)
if entry_ref is None:
VLogger.error("--traceback: 入口 DILocation 生成失败,终止编译", "DWARF")
return 0
_g_cur_entry_ref = entry_ref
llvmlite.builder_set_debug_location(builder, entry_ref)
return 1
# ============================================================
# PopFunction - restore the enclosing function's DI context
# ============================================================
def PopFunction(builder: llvmlite.IRBuilder*) -> int:
global _g_cur_sp, _g_cur_entry_ref, _g_cur_builder, _g_depth
if _g_enabled == 0:
return 0
if _g_depth <= 0:
return 0
_g_depth -= 1
_g_cur_sp = int(_g_stk_sp[_g_depth]) - 1
rslot: str | t.CPtr = (str | t.CPtr)(uint64(_g_stk_ref) + sizet(_g_depth) * 8)
_g_cur_entry_ref = rslot[0]
bslot: str | t.CPtr = (str | t.CPtr)(uint64(_g_stk_bld) + sizet(_g_depth) * 8)
_g_cur_builder = (llvmlite.IRBuilder*)(bslot[0])
if builder is not None:
llvmlite.builder_clear_debug_location(builder)
return 0
# ============================================================
# SetStatement - point the current builder at a statement DILocation
# ============================================================
def SetStatement(pool: memhub.MemBuddy*, builder: llvmlite.IRBuilder*,
line: int, col: int) -> int:
"""Update the builder's DILocation to the current statement (line precision).
No-op outside a DI-attached define (module-level code / synthetic defines),
so instructions outside a function body never reference a function's scope.
"""
if _g_enabled == 0 or pool is None or builder is None:
return 0
if _g_cur_sp < 0:
return 0
ref: str = _EnsureLocation(pool, _g_cur_sp, line, col)
if ref is None:
return 0
llvmlite.builder_set_debug_location(builder, ref)
return 1
# ============================================================
# _ModuleHasDefine - does the module emit any define (IsDeclared == 0)?
# ============================================================
def _ModuleHasDefine(mod: llvmlite.LLVMModule*) -> int:
if mod is None:
return 0
cur: llvmlite.Function* = mod.FuncHead
steps: int = 0
while cur is not None:
steps += 1
if steps > 100000:
break
if llvmlite.function_is_declared(cur) == 0:
return 1
cur = llvmlite.function_get_next(cur)
return 0
# ============================================================
# Finalize - register !llvm.dbg.cu / !llvm.module.flags before dump_ir
# ============================================================
def Finalize(pool: memhub.MemBuddy*) -> int:
"""Module-level DI tail; idempotent per module, no-op when no SP was emitted."""
global _g_finalized
if _g_enabled == 0:
return 0
if _g_finalized != 0:
return 0
mod: llvmlite.LLVMModule* = _g_mod
if mod is None or pool is None:
return 0
_g_finalized = 1
# Self-check (mirrors TPC Phase2Translator.py:3444): a module that emits a
# define but carries no DI at all loses line info for its frames.
if mod.DIMetaCount == 0 and _ModuleHasDefine(mod) != 0:
VLogger.warning("--traceback: 本模块发射了 define 但 IR 未携带 DWARF (!llvm.dbg.cu)——该模块帧在 Ctrl+C 栈里将只显示地址", "DWARF")
if _g_cu_id < 0:
return 0
cu_ref: str = _RefOf(pool, _g_cu_id)
if cu_ref is None:
VLogger.error("--traceback: DICompileUnit 引用缓冲分配失败,终止编译", "DWARF")
return 1
llvmlite.module_add_named_metadata(mod, pool, "llvm.dbg.cu", cu_ref)
fbuf: str = pool.alloc(64)
if fbuf is None:
VLogger.error("--traceback: 模块级调试标志缓冲分配失败,终止编译", "DWARF")
return 1
viperlib.snprintf(fbuf, 64, "!{i32 2, !\"Debug Info Version\", i32 3}")
flags_id: int = llvmlite.module_add_di_node(mod, pool, fbuf)
if flags_id < 0:
VLogger.error("--traceback: llvm.module.flags 元数据节点分配失败,终止编译", "DWARF")
return 1
fl_ref: str = _RefOf(pool, flags_id)
if fl_ref is None:
VLogger.error("--traceback: llvm.module.flags 引用缓冲分配失败,终止编译", "DWARF")
return 1
llvmlite.module_add_named_metadata(mod, pool, "llvm.module.flags", fl_ref)
return 0