CallGenBase: 模块返回类型文本表改单趟登记+查找,find_func_in_module 合并为单趟链表遍历(PROF 热点) BuildPipeline: ensure_dir 增加快速路径(已存在目录直接返回) AssignGenMixin/Translator: alloc 失败打印 Pool/PermPool 统计与阶段上下文(SetAllocCtx) CallGenCtor: REnum 指针载荷补显式 build_ptrtoint 再 coerce/store;新增 DebugInfoGen.vp 其余为历史累积改动一并入库
444 lines
17 KiB
Plaintext
444 lines
17 KiB
Plaintext
import t, c
|
|
from stdint import *
|
|
import llvmlite
|
|
import memhub
|
|
import string
|
|
import stdlib
|
|
import viperlib
|
|
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: basename + absolute directory, backslashes normalised to '/'
|
|
src: str = _g_file_path
|
|
if src is None:
|
|
src = ""
|
|
slen: sizet = string.strlen(src)
|
|
nbuf: str = pool.alloc(slen + 4)
|
|
if nbuf is None:
|
|
VLogger.error("--traceback: DIFile 路径缓冲分配失败,终止编译", "DWARF")
|
|
return 1
|
|
if slen > 0:
|
|
string.strncpy(nbuf, src, slen)
|
|
nbuf[slen] = '\0'
|
|
ni: sizet = 0
|
|
for ni in range(slen):
|
|
if nbuf[ni] == '\\':
|
|
nbuf[ni] = '/'
|
|
fname: str = nbuf
|
|
directory: str = ""
|
|
slash: str = string.strrchr(nbuf, '/')
|
|
if slash is not None:
|
|
fname = slash + 1
|
|
slash[0] = '\0'
|
|
directory = nbuf
|
|
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 = (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 = (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 = (t.CPtr)(uint64(_g_stk_ref) + sizet(_g_depth) * 8)
|
|
rslot[0] = _g_cur_entry_ref
|
|
bslot: str | t.CPtr = (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 = (t.CPtr)(uint64(_g_stk_ref) + sizet(_g_depth) * 8)
|
|
_g_cur_entry_ref = rslot[0]
|
|
bslot: str | t.CPtr = (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
|