perf(prof,gen): 编译热点优化 + 内存池 OOM 诊断接线 + REnum 指针载荷修正
CallGenBase: 模块返回类型文本表改单趟登记+查找,find_func_in_module 合并为单趟链表遍历(PROF 热点) BuildPipeline: ensure_dir 增加快速路径(已存在目录直接返回) AssignGenMixin/Translator: alloc 失败打印 Pool/PermPool 统计与阶段上下文(SetAllocCtx) CallGenCtor: REnum 指针载荷补显式 build_ptrtoint 再 coerce/store;新增 DebugInfoGen.vp 其余为历史累积改动一并入库
This commit is contained in:
1 parent
676c19c362
commit
373a20c3e5
20 files changed
+1984
-165
No files matched your search
+1115
-9
File diff suppressed because it is too large.
Load diff
@@ -27,6 +27,7 @@ import lib.Phase.Phase1.Phase1Topo as Phase1Topo
|
||||
import lib.Phase.Phase1 as Phase1
|
||||
|
||||
import lib.core.Gen.GenApi as GenApi
|
||||
import lib.core.Gen.DebugInfoGen as DebugInfoGen
|
||||
|
||||
|
||||
# ============================================================
|
||||
@@ -1027,6 +1028,11 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str,
|
||||
tr.ModuleSha1 = sha1
|
||||
tr.CurrentPackage = GenApi.compute_package_from_relpath(mb, entry.RelPath)
|
||||
GenApi.set_current_file_type(file_path)
|
||||
# --traceback: mirror the DWARF switch into the Gen-layer emitter (the
|
||||
# same BuildPipeline._traceback_mode that gates -frame-pointer=all /
|
||||
# dbghelp / the Install() ctor). include/*.vp modules are compiled here,
|
||||
# so this is what gives their frames DWARF line info.
|
||||
DebugInfoGen.SetEnabled(BuildPipeline._traceback_mode)
|
||||
GenApi.set_current_file_imports(file_path)
|
||||
GenApi.set_current_module_sha1(sha1)
|
||||
GenApi.clear_cdefine_constants()
|
||||
@@ -1058,6 +1064,13 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str,
|
||||
failed += 1
|
||||
continue
|
||||
|
||||
# --traceback: inject the startup Install() ctor when translating traceback.vp
|
||||
# (its stub/text.ll below are what Phase2 reuses to build the .obj). The
|
||||
# injector lives in the Gen layer (already depends on llvmlite), so this
|
||||
# module gains no cross-module llvmlite symbol dependency.
|
||||
if BuildPipeline._traceback_mode != 0 and BuildPipeline._tb_basename_is_traceback(file_path) != 0:
|
||||
HandlesTranslator.InjectTracebackInstallCtor(tr)
|
||||
|
||||
# 诊断:pre_rc 哨兵在 translate 前、覆盖不到 translate 期间的 AST/IR 写穿,故 dump 前立即检查存活块 header/canary
|
||||
# (rc=6 块尾 canary 损坏 = 越界写穿数据区),把「dump 时链表成环卡死」变成「定位到 translate 期间写穿发生的文件」。
|
||||
post_rc: int = mb.check_alive_blocks(mark1)
|
||||
|
||||
@@ -1694,6 +1694,10 @@ def RunMultiFileProject(mb: memhub.MemBuddy*,
|
||||
|
||||
if lret == 0:
|
||||
VLogger.success(f"输出: {exe_path}", "PhaseC")
|
||||
# --traceback: export <exe>.stackmap (skipped under --phase2command,
|
||||
# where nothing was actually linked).
|
||||
if BuildPipeline._traceback_mode != 0 and BuildPipeline._phase2_cmd_path is None:
|
||||
BuildPipeline.WriteStackMap(mb, exe_path)
|
||||
if args.get_bool("run") and BuildPipeline._phase2_cmd_path is not None:
|
||||
# phase2command 模式:exe 尚未链接(命令在 bat 中),跳过 --run
|
||||
VLogger.info("phase2command 模式:跳过 --run(exe 尚未链接)", "phase2cmd")
|
||||
|
||||
@@ -2043,7 +2043,6 @@ def _ParseVpiIntoModuleImpl(pool: memhub.MemBuddy*,
|
||||
# 解析注解——根治跨上下文误报(如 t.CArray[str] 需要 't' 已导入)。
|
||||
stub_imported_modules: str = None
|
||||
stub_from_imports: str = None
|
||||
si: sizet
|
||||
for si in range(n):
|
||||
schild: ast.AST* = ch.get(si)
|
||||
if schild is None:
|
||||
@@ -2053,15 +2052,12 @@ def _ParseVpiIntoModuleImpl(pool: memhub.MemBuddy*,
|
||||
simp: ast.Import* = ast.Import*(schild)
|
||||
if simp.names is not None:
|
||||
sn_n: sizet = len(simp.names)
|
||||
sni: sizet
|
||||
for sni in range(sn_n):
|
||||
sal: ast.Alias* = ast.Alias*(simp.names.get(sni))
|
||||
if sal is not None and sal.name is not None:
|
||||
stub_imported_modules = _stub_add_imported(
|
||||
pool, stub_imported_modules, sal.name)
|
||||
stub_imported_modules = _stub_add_imported(pool, stub_imported_modules, sal.name)
|
||||
if sal.asname is not None:
|
||||
stub_imported_modules = _stub_add_imported(
|
||||
pool, stub_imported_modules, sal.asname)
|
||||
stub_imported_modules = _stub_add_imported(pool, stub_imported_modules, sal.asname)
|
||||
elif sk == ast.ASTKind.ImportFrom:
|
||||
sifr: ast.ImportFrom* = ast.ImportFrom*(schild)
|
||||
sifr_mod: str = sifr.module
|
||||
@@ -2083,8 +2079,7 @@ def _ParseVpiIntoModuleImpl(pool: memhub.MemBuddy*,
|
||||
string.strcpy(qbuf_r + pkg_len_r + 1, sifr_mod)
|
||||
sifr_mod_q = qbuf_r
|
||||
if sifr_mod is not None:
|
||||
stub_imported_modules = _stub_add_imported(
|
||||
pool, stub_imported_modules, sifr_mod_q)
|
||||
stub_imported_modules = _stub_add_imported(pool, stub_imported_modules, sifr_mod_q)
|
||||
# 方案A:包 __init__.vpi 的重导出类以包 sha1 注册别名 entry
|
||||
# (ast.Attribute 定义在 ast.exprs 但归属 ast 包;查找端按包
|
||||
# sha1 匹配,注册端按定义文件 sha1 → name+sha1 错位 fail-fast。
|
||||
|
||||
@@ -897,6 +897,40 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy*, source_dir: str,
|
||||
stdlib.free(worklist)
|
||||
return -1
|
||||
|
||||
# 2b. --traceback: force the traceback runtime module into the reachable set
|
||||
# (TPC: used_includes.add('traceback')). Its Install() symbol and the
|
||||
# @llvm.global_ctors ctor are only produced by compiling this module, so a
|
||||
# missing file is fail-fast rather than a silent "feature off" build.
|
||||
if BuildPipeline._traceback_mode != 0:
|
||||
tb_abs: t.CPtr = stdlib.malloc(8)
|
||||
tb_rel: t.CPtr = stdlib.malloc(8)
|
||||
tb_inc: t.CPtr = stdlib.malloc(8)
|
||||
tb_found: int = -1
|
||||
if tb_abs is not None and tb_rel is not None and tb_inc is not None:
|
||||
tb_abs_slot: sizet* = (sizet*)(tb_abs)
|
||||
tb_rel_slot: sizet* = (sizet*)(tb_rel)
|
||||
tb_inc_slot: sizet* = (sizet*)(tb_inc)
|
||||
tb_abs_slot[0] = 0
|
||||
tb_rel_slot[0] = 0
|
||||
tb_inc_slot[0] = 0
|
||||
tb_found = IncludesScanner.ResolveModuleToPath(
|
||||
includes_dirs, "traceback", tb_abs, tb_rel, tb_inc)
|
||||
if tb_abs is not None:
|
||||
stdlib.free(tb_abs)
|
||||
if tb_rel is not None:
|
||||
stdlib.free(tb_rel)
|
||||
if tb_inc is not None:
|
||||
stdlib.free(tb_inc)
|
||||
if tb_found != 0:
|
||||
stdlib.free(worklist)
|
||||
VLogger.error(f"--traceback: 在 includes 目录中找不到 traceback.vp,无法编译并链接 Install()(请确认 project.vpj 的 includes 包含仓库 includes 目录;已扫描: {includes_dirs})", "Traceback")
|
||||
return -1
|
||||
tb_wl: int = _AppendDepsToWorklist(worklist, wl_len, "traceback", None)
|
||||
if tb_wl < 0:
|
||||
stdlib.free(worklist)
|
||||
return -1
|
||||
wl_len = sizet(tb_wl)
|
||||
|
||||
# 3. 工作列表 BFS:模块名 → 文件系统解析 → 读+hash+parse import
|
||||
processed: bytes = stdlib.malloc(StubMerger.REACH_WORKLIST_BUF)
|
||||
if processed is None:
|
||||
@@ -1069,6 +1103,10 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy*, source_dir: str,
|
||||
tokens_r: ast.Token* = ast.tokenize(lx_r)
|
||||
tree_r = ast.parse_tokens(mb, tokens_r)
|
||||
if tree_r is None:
|
||||
# parse_tokens keeps the error detail (line/col/message) in the global
|
||||
# channel only, so print the unified parse traceback before the one-line
|
||||
# log; without it a reachable-includes syntax error is undiagnosable.
|
||||
GenApi.dump_parse_traceback(abs_path)
|
||||
VLogger.error(f"可达 includes 解析失败: {abs_path}", "Reachable")
|
||||
stdlib.free(abs_path)
|
||||
stdlib.free(rel_path)
|
||||
|
||||
@@ -580,24 +580,23 @@ def GetSha1StoreIncdirArrPtr() -> bytes:
|
||||
# 调用者直接遍历数组:sha1_arr[i*17]、mod_arr[i*64]、rel_arr[i*StubMerger.MAX_REL_PATH_LEN]
|
||||
# 内存归全局存储器所有,调用者不要 free
|
||||
# ============================================================
|
||||
def GetSha1StoreArr(out_sha1_arr: t.CPtr, out_mod_arr: t.CPtr, out_rel_arr: t.CPtr) -> int:
|
||||
def GetSha1StoreArr(out_sha1_arr: rawptr[t.CPtr], out_mod_arr: rawptr[t.CPtr], out_rel_arr: rawptr[t.CPtr]) -> int:
|
||||
"""获取全局存储器数组指针
|
||||
|
||||
out_sha1_arr/out_mod_arr/out_rel_arr 是调用者提供的 t.CPtr 指针,
|
||||
函数将存储器数组指针写入这些位置。
|
||||
返回条目数,-1 失败
|
||||
out_sha1_arr/out_mod_arr/out_rel_arr 是调用者提供的 t.CPtr 槽的地址(t.CPtr*),
|
||||
函数把存储器数组指针写入这些槽。返回条目数,-1 失败
|
||||
"""
|
||||
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: t.CPtr = (t.CPtr | t.CPtr)(out_sha1_arr) # 存疑
|
||||
sp: rawptr[t.CPtr] = out_sha1_arr
|
||||
if sp is not None:
|
||||
sp[0] = StubMerger._sha1_store_arr
|
||||
mp: t.CPtr = (t.CPtr | t.CPtr)(out_mod_arr)
|
||||
mp: rawptr[t.CPtr] = out_mod_arr
|
||||
if mp is not None:
|
||||
mp[0] = StubMerger._sha1_store_mod
|
||||
rp: t.CPtr = (t.CPtr | t.CPtr)(out_rel_arr)
|
||||
rp: rawptr[t.CPtr] = out_rel_arr
|
||||
if rp is not None:
|
||||
rp[0] = StubMerger._sha1_store_rel
|
||||
return StubMerger._sha1_store_count
|
||||
|
||||
@@ -13,6 +13,7 @@ import lib.core.Gen.Translator as HT
|
||||
import lib.Stub.StubMerger.StubMergerStore as StubMergerStore
|
||||
import lib.core.Gen.GenTypes as GenTypes
|
||||
import lib.core.Gen.Generator as Generator
|
||||
import lib.core.CompilerFactory as CompilerFactory
|
||||
import lib.Stub.StubGen.VpiReader as VpiReader
|
||||
import lib.core.Gen.GenApi as GenApi
|
||||
|
||||
@@ -2632,6 +2633,16 @@ novirtual class AssignGenMixin:
|
||||
asg_target_ty = self.Gen.VarGen.repl_slot_ty(pool, asg_re)
|
||||
rhs_val: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, asgn.value, None, 0, trans, asg_target_ty)
|
||||
if rhs_val is None:
|
||||
# 归属:RHS 为 None 多数是分配失败(池耗尽)。“[MEM] alloc 失败”只给池基址,
|
||||
# 分不清是主池(mark/reset 回收)还是常驻池(从不 reset)满,也无法知道
|
||||
# 当前阶段/文件。故此处把两个池的用量与池标识一并打出来。
|
||||
mb_diag: memhub.MemBuddy* = CompilerFactory.factory.Pool
|
||||
pp_diag: memhub.MemBuddy* = CompilerFactory.factory.PermPool
|
||||
if mb_diag is not None:
|
||||
mb_diag.dump_stats("主池(Pool)")
|
||||
if pp_diag is not None:
|
||||
pp_diag.dump_stats("常驻池(PermPool)")
|
||||
stdio.fflush(None)
|
||||
# 增强错误信息:包含文件名 + lineno + AST 节点详情,便于定位
|
||||
if asgn.value is not None:
|
||||
sha1: str = trans.ModuleSha1
|
||||
|
||||
@@ -514,50 +514,42 @@ novirtual class CallGenBase:
|
||||
if mod is None or name is None:
|
||||
return None
|
||||
name_len: sizet = string.strlen(name)
|
||||
# 热点优化:原 4 趟完整链表扫描合并为 2 趟——趟 1 精确匹配(命中即返回)+ 收集首个本模块同名 mangled 候选;趟 2 收集首个 IsDeclared==0 后缀命中与首个任意后缀命中,扫描后按原优先级返回(语义不变)
|
||||
# 热点优化:原「趟 1 精确匹配+本模块同名、趟 2 declare/任意后缀」两趟完整链表扫描
|
||||
# 合并为一趟——同一节点只取一次 function_get_name/NameLen/next(原两趟各取一次,
|
||||
# 该链表平均数百节点且本函数每次调用都全链遍历,PROF 中本函数及其访问器约占 10%)。
|
||||
# 优先级与逐趟结果完全一致:精确匹配命中即返回 > 本模块同名 > declare 后缀 > 任意后缀;
|
||||
# 同类候选均取链表首个命中。
|
||||
cur: llvmlite.Function* = mod.FuncHead
|
||||
mangle_hit: llvmlite.Function* = None
|
||||
declare_hit: llvmlite.Function* = None
|
||||
any_hit: llvmlite.Function* = None
|
||||
while cur is not None:
|
||||
cur_name: str = llvmlite.function_get_name(cur)
|
||||
if cur_name is not None:
|
||||
# 快速路径:strcmp 判等蕴含首字符相等,首字符不同者跳过 strcmp(mangled 名以 sha1 十六进制打头,裸名多为字母/下划线)
|
||||
if cur_name[0] == name[0] and string.strcmp(cur_name, name) == 0:
|
||||
return cur
|
||||
# 本模块同名候选:仅接受恰为 "{owner_sha1}.{name}" 的形态(不匹配 "{sha1}.Class.method");declare 与 define 同名同符号,是否 IsDeclared==0 均可返回
|
||||
if owner_sha1 is not None and mangle_hit is None:
|
||||
# 名字长度改取 Function.NameLen(创建时一次算好),免去对热链
|
||||
# 每个节点重复 strlen(cur_name)(全程序 strlen 热点来源)。
|
||||
cur_len_o: sizet = llvmlite.function_get_name_len(cur)
|
||||
if cur_len_o == 17 + name_len:
|
||||
if string.strncmp(cur_name, owner_sha1, 16) == 0 and cur_name[16] == '.':
|
||||
suffix_start_o: sizet = cur_len_o - name_len
|
||||
if cur_name[suffix_start_o - 1] == '.':
|
||||
match_o: int = 1
|
||||
for i in range(name_len):
|
||||
if cur_name[suffix_start_o + i] != name[i]:
|
||||
match_o = 0
|
||||
break
|
||||
if match_o == 1:
|
||||
mangle_hit = cur
|
||||
cur = llvmlite.function_get_next(cur)
|
||||
if mangle_hit is not None:
|
||||
return mangle_hit
|
||||
# 后缀匹配(.name):不跳过 declare(方法 stub 也是 declare,由调用处判断 IsDeclared 决定用裸名或 sha1 前缀名)。本模块同名 define 必须优先于跨模块 stub declare,无同名 define 时回退 stub declare
|
||||
declare_hit: llvmlite.Function* = None
|
||||
any_hit: llvmlite.Function* = None
|
||||
cur = mod.FuncHead
|
||||
while cur is not None:
|
||||
cur_name2: str = llvmlite.function_get_name(cur)
|
||||
if cur_name2 is not None:
|
||||
# 名字长度改取 Function.NameLen(创建时一次算好),免去热链上
|
||||
# 每个节点重复 strlen(cur_name2);与 strlen(Name) 恒等,不改匹配语义。
|
||||
# 名字长度取 Function.NameLen(创建时一次算好),免去对热链每个节点重复 strlen
|
||||
cur_len: sizet = llvmlite.function_get_name_len(cur)
|
||||
# 本模块同名候选:仅接受恰为 "{owner_sha1}.{name}" 的形态(不匹配 "{sha1}.Class.method");declare 与 define 同名同符号,是否 IsDeclared==0 均可返回
|
||||
if owner_sha1 is not None and mangle_hit is None and cur_len == 17 + name_len:
|
||||
if string.strncmp(cur_name, owner_sha1, 16) == 0 and cur_name[16] == '.':
|
||||
suffix_start_o: sizet = cur_len - name_len
|
||||
if cur_name[suffix_start_o - 1] == '.':
|
||||
match_o: int = 1
|
||||
for i in range(name_len):
|
||||
if cur_name[suffix_start_o + i] != name[i]:
|
||||
match_o = 0
|
||||
break
|
||||
if match_o == 1:
|
||||
mangle_hit = cur
|
||||
# 后缀匹配(.name):不跳过 declare(方法 stub 也是 declare,由调用处判断 IsDeclared 决定用裸名或 sha1 前缀名)
|
||||
if cur_len > name_len + 1:
|
||||
suffix_start: sizet = cur_len - name_len
|
||||
if cur_name2[suffix_start - 1] == '.':
|
||||
if cur_name[suffix_start - 1] == '.':
|
||||
match: int = 1
|
||||
for i in range(name_len):
|
||||
if cur_name2[suffix_start + i] != name[i]:
|
||||
if cur_name[suffix_start + i] != name[i]:
|
||||
match = 0
|
||||
break
|
||||
if match == 1:
|
||||
@@ -568,6 +560,8 @@ novirtual class CallGenBase:
|
||||
if cur.IsDeclared == 0 and declare_hit is None:
|
||||
declare_hit = cur
|
||||
cur = llvmlite.function_get_next(cur)
|
||||
if mangle_hit is not None:
|
||||
return mangle_hit
|
||||
if declare_hit is not None:
|
||||
return declare_hit
|
||||
return any_hit
|
||||
@@ -947,23 +941,32 @@ novirtual class CallGenBase:
|
||||
return None
|
||||
return (str | t.CPtr)(v)
|
||||
|
||||
# _CacheModuleRetTyTexts:把某模块 text.ll 全部 define 行的返回类型写入持久缓存(键/值深拷贝进 PermPool,同名只记首次,只登记 "{sha1}." 开头的本模块 mangled 名);仅在成功读到 text.ll 后调用,负结果绝不缓存
|
||||
# _CacheModuleRetTyTexts:单趟扫描某模块 text.ll——把全部 define 行的返回类型写入持久缓存(键/值深拷贝进 PermPool,同名只记首次,只登记 "{sha1}." 开头的本模块 mangled 名),并在同一趟里返回首个匹配 search_buf 的 define 的返回类型(未匹配返回 None);仅在成功读到 text.ll 后调用,负结果绝不缓存
|
||||
def _CacheModuleRetTyTexts(self, class_sha1: str, text_buf: bytes,
|
||||
text_len: sizet) -> None:
|
||||
"""登记 text.ll 全部 define 行到持久缓存(建表/分配失败即不缓存,回退原扫描)"""
|
||||
if self.Gen is None or class_sha1 is None or text_buf is None or text_len == 0:
|
||||
return
|
||||
perm: memhub.MemBuddy* = CompilerFactory.factory.PermPool
|
||||
if perm is None:
|
||||
return
|
||||
ht: hashtable.HashTable* = self.Gen.CallGenBase.MethodRetTyMap
|
||||
if ht is None:
|
||||
ht = hashtable.HashTable(perm)
|
||||
if ht is None:
|
||||
return
|
||||
self.Gen.CallGenBase.MethodRetTyMap = ht
|
||||
sha1_len: sizet = string.strlen(class_sha1)
|
||||
text_len: sizet, pool: memhub.MemBuddy*,
|
||||
search_buf: str, search_str_len: sizet) -> llvmlite.LLVMType*:
|
||||
"""单趟「登记 + 查找」text.ll 全部 define 行(建表/分配失败时只查找不登记)"""
|
||||
if text_buf is None or text_len == 0:
|
||||
return None
|
||||
# 合并原「先全量登记一遍、再二次扫描查找」的两趟全文件遍历(PROF:两次扫描合计约 14%)。
|
||||
# 建表失败(PermPool 未就绪等)不影响查找:cache_on=0 时只走同趟查找。
|
||||
cache_on: int = 0
|
||||
ht: hashtable.HashTable* = None
|
||||
perm: memhub.MemBuddy* = None
|
||||
sha1_len: sizet = 0
|
||||
if self.Gen is not None and class_sha1 is not None:
|
||||
perm = CompilerFactory.factory.PermPool
|
||||
if perm is not None:
|
||||
ht = self.Gen.CallGenBase.MethodRetTyMap
|
||||
if ht is None:
|
||||
ht = hashtable.HashTable(perm)
|
||||
if ht is not None:
|
||||
self.Gen.CallGenBase.MethodRetTyMap = ht
|
||||
if ht is not None:
|
||||
cache_on = 1
|
||||
sha1_len = string.strlen(class_sha1)
|
||||
pos: sizet = 0
|
||||
result_ty: llvmlite.LLVMType* = None
|
||||
while pos < text_len:
|
||||
ls: sizet = pos
|
||||
le: sizet = pos
|
||||
@@ -998,8 +1001,8 @@ novirtual class CallGenBase:
|
||||
break
|
||||
name_end += 1
|
||||
name_len: sizet = name_end - name_start
|
||||
# 只登记本模块 mangled 名 "{sha1}.{后缀}"(查询键恒为此前缀)
|
||||
if name_len > sha1_len + 1:
|
||||
# 只登记本模块 mangled 名 "{sha1}.{后缀}"(查询键恒为此前缀);表不可用时跳过登记
|
||||
if cache_on != 0 and name_len > sha1_len + 1:
|
||||
if string.strncmp(text_buf + name_start, class_sha1, sha1_len) == 0 \
|
||||
and text_buf[name_start + sha1_len] == '.':
|
||||
key_buf: bytes = stdlib.malloc(name_len + 1)
|
||||
@@ -1018,7 +1021,16 @@ novirtual class CallGenBase:
|
||||
tcopy[rt_len] = '\0'
|
||||
ht.set_str(kcopy, tcopy)
|
||||
stdlib.free(key_buf)
|
||||
# 同趟查找:首个与 search_buf 匹配、且后随 '"' / '(' / 行尾的 define(判定与原二次扫描逐字一致),先命中者为准
|
||||
if result_ty is None and name_start + search_str_len <= ls + ll:
|
||||
if string.strncmp(text_buf + name_start, search_buf, search_str_len) == 0:
|
||||
end_ch: int = 0
|
||||
if name_start + search_str_len < ls + ll:
|
||||
end_ch = text_buf[name_start + search_str_len]
|
||||
if end_ch == 34 or end_ch == 40 or end_ch == 0:
|
||||
result_ty = self._ParseLLVMRetTyStr(pool, text_buf, ls + 7, at_pos)
|
||||
pos = le + 1
|
||||
return result_ty
|
||||
|
||||
# _LookupMethodRetTyFromTextLL:读取 {temp_dir}/{sha1[0:2]}/{sha1}.text.ll (level=1),按 "{sha1}.{class_name}.{method_name}" 搜索 define 行并解析返回类型;未找到/文件不存在返回 None
|
||||
|
||||
@@ -1108,51 +1120,10 @@ novirtual class CallGenBase:
|
||||
class_sha1, class_name, method_name)
|
||||
search_str_len: sizet = string.strlen(search_buf)
|
||||
|
||||
# 在 text.ll 中搜索 define 行包含 search_buf
|
||||
buf_len: sizet = string.strlen(read_buf)
|
||||
|
||||
# 已成功读到本模块 text.ll:登记其全部 define 行供后续 O(1) 命中(下方原扫描保留为未命中路径)
|
||||
self._CacheModuleRetTyTexts(class_sha1, read_buf, buf_len)
|
||||
|
||||
pos: sizet = 0
|
||||
result_ty: llvmlite.LLVMType* = None
|
||||
|
||||
while pos < buf_len and result_ty is None:
|
||||
ls: sizet = pos
|
||||
le: sizet = pos
|
||||
while le < buf_len:
|
||||
if read_buf[le] == '\n':
|
||||
break
|
||||
le += 1
|
||||
ll: sizet = le - ls
|
||||
|
||||
# define 行判定:快速路径先看行首字节是否 'd'(仅否定短路,不改变命中结果)
|
||||
if ll >= 7 and read_buf[ls] == 'd' and string.strncmp(read_buf + ls, "define ", 7) == 0:
|
||||
# 找 @ 符号
|
||||
at_pos: sizet = ls + 7
|
||||
while at_pos < ls + ll:
|
||||
if read_buf[at_pos] == '@':
|
||||
break
|
||||
at_pos += 1
|
||||
if at_pos < ls + ll:
|
||||
# 跳过可选引号
|
||||
name_start: sizet = at_pos + 1
|
||||
if name_start < ls + ll and read_buf[name_start] == '"':
|
||||
name_start += 1
|
||||
# 检查是否匹配 search_buf
|
||||
if name_start + search_str_len <= ls + ll:
|
||||
if string.strncmp(read_buf + name_start, search_buf, search_str_len) == 0:
|
||||
# 函数名边界检查:search 后必须是 '"'(带引号名)、'('(参数表)或行尾,
|
||||
# 否则 "sha1.func" 会误匹配 "sha1.func_helper" 等前缀同名函数
|
||||
name_end: sizet = name_start + search_str_len
|
||||
end_ch: int = 0
|
||||
if name_end < ls + ll:
|
||||
end_ch = read_buf[name_end]
|
||||
if end_ch == 34 or end_ch == 40 or end_ch == 0:
|
||||
# 匹配!提取返回类型: [ls+7, at_pos)
|
||||
result_ty = self._ParseLLVMRetTyStr(pool, read_buf, ls + 7, at_pos)
|
||||
|
||||
pos = le + 1
|
||||
# 单趟扫描 text.ll:登记其全部 define 行供后续 O(1) 命中,并同趟取出首个匹配项。
|
||||
# 文件长度直接用 read_all 的返回值,省掉一次整文件 strlen。
|
||||
result_ty: llvmlite.LLVMType* = self._CacheModuleRetTyTexts(
|
||||
class_sha1, read_buf, sizet(text_len), pool, search_buf, search_str_len)
|
||||
|
||||
stdlib.free(search_buf)
|
||||
stdlib.free(read_buf)
|
||||
|
||||
@@ -392,6 +392,18 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
|
||||
if arg_val is None: continue
|
||||
payload_ptr: llvmlite.Value* = llvmlite.build_gep_struct(builder, renum_ty, pfe.Ty, tmp, payload_idx)
|
||||
if payload_ptr is None: continue
|
||||
# payload 槽位是 i32/i64(布局见 ClassGenEnum:{i32 __tag, _p1, _p2, ...}),
|
||||
# 而变体成员可能是指针(Ptr.Pointee / Func.Ret / Array.ElemTy / Struct.Fields …)。
|
||||
# 这是编译器自身的表示转换(tagged union 打包),不是用户代码的隐式转换:
|
||||
# 必须显式 ptrtoint 后存入,读取侧按成员类型 bitcast 槽位地址再 load(对称)。
|
||||
# 禁止隐式转换后此处不能再依赖 coerce_to_type 的静默 ptrtoint。
|
||||
if (self.Gen.ExprGenBase.is_ptr_type(arg_val.Ty) != 0
|
||||
and self.Gen.ExprGenBase.is_ptr_type(pfe.Ty) == 0
|
||||
and self.Gen.ExprGenBase.get_llvm_type_bits(pfe.Ty) != 0):
|
||||
casted_arg: llvmlite.Value* = llvmlite.build_ptrtoint(builder, arg_val, pfe.Ty)
|
||||
if casted_arg is None:
|
||||
continue
|
||||
arg_val = casted_arg
|
||||
# 类型转换并 store(payload 位置可能是 i32 或 i64,按字段类型 coerce)
|
||||
arg_val = self.Gen.ExprGenBase.coerce_to_type(builder, arg_val, pfe.Ty, arg)
|
||||
llvmlite.build_store(builder, arg_val, payload_ptr)
|
||||
|
||||
@@ -19,9 +19,9 @@ from . import ClassGenBase
|
||||
# register_enum 永不调用 → 所有 CEnum 注册失败 → 裸名枚举访问 NameError)
|
||||
# ============================================================
|
||||
define ENUM_MEMBER_MAX_LOCAL = 128
|
||||
define RENUM_ENUM_CLASS_MAX = 64
|
||||
define RENUM_ENUM_VARIANT_PER_CLASS = 32
|
||||
define RENUM_ENUM_ALIAS_MAX = 512
|
||||
define RENUM_CLASS_MAX = 64
|
||||
define RENUM_VARIANT_PER_CLASS = 32
|
||||
define RENUM_ALIAS_MAX = 512
|
||||
|
||||
# ============================================================
|
||||
# ClassGenEnum - ClassGenMixin 拆分(阶段 D):enum/union/renum 定义与变体表
|
||||
@@ -1112,7 +1112,12 @@ novirtual class ClassGenEnum(ClassGenBase.ClassGenBase):
|
||||
self.RenumVariantTags = CompilerFactory.factory.PermPool.alloc(8 * RENUM_CLASS_MAX * RENUM_VARIANT_PER_CLASS)
|
||||
if (self.RenumClassNames is None or self.RenumClassVariantCounts is None
|
||||
or self.RenumVariantNames is None or self.RenumVariantTags is None):
|
||||
# A None table means the perm-pool request failed (size 0 or pool
|
||||
# exhausted). Returning here left the table empty while the caller
|
||||
# still reported "registered", so match dispatch silently lost every
|
||||
# REnum case.
|
||||
self.RenumClassNames = None
|
||||
self.Gen.TypeGenErr.fatal_error(cd, f"REnum 变体表分配失败(class='{class_name}'):PermPool 返回 NULL,变体表不可用", "InternalCompilerError")
|
||||
return
|
||||
string.memset(self.RenumClassNames, 0, 8 * RENUM_CLASS_MAX)
|
||||
string.memset(self.RenumClassVariantCounts, 0, 8 * RENUM_CLASS_MAX)
|
||||
@@ -1140,6 +1145,9 @@ novirtual class ClassGenEnum(ClassGenBase.ClassGenBase):
|
||||
|
||||
# 表已满
|
||||
if self.RenumClassCount >= RENUM_CLASS_MAX:
|
||||
# Silently dropping the class made its later `match` lose every case; a
|
||||
# real limit breach must be reported instead.
|
||||
self.Gen.TypeGenErr.fatal_error(cd, f"REnum 类数超过上限 {RENUM_CLASS_MAX}:无法注册 class='{class_name}'", "InternalCompilerError")
|
||||
return
|
||||
|
||||
# 注册类名(strcpy 到 perm_pool:1a 每文件 reset 后 AST 裸指针悬空,
|
||||
@@ -1148,6 +1156,7 @@ novirtual class ClassGenEnum(ClassGenBase.ClassGenBase):
|
||||
cn_len_rv: sizet = string.strlen(class_name)
|
||||
cn_buf_rv: str = CompilerFactory.factory.PermPool.alloc(cn_len_rv + 1)
|
||||
if cn_buf_rv is None:
|
||||
self.Gen.TypeGenErr.fatal_error(cd, f"REnum 类名副本分配失败(class='{class_name}'):PermPool 返回 NULL", "InternalCompilerError")
|
||||
return
|
||||
string.strcpy(cn_buf_rv, class_name)
|
||||
self.RenumClassNames[idx] = sizet(cn_buf_rv)
|
||||
@@ -1238,6 +1247,9 @@ novirtual class ClassGenEnum(ClassGenBase.ClassGenBase):
|
||||
vn_len_rv: sizet = string.strlen(vname_src)
|
||||
vn_buf_rv: str = CompilerFactory.factory.PermPool.alloc(vn_len_rv + 1)
|
||||
if vn_buf_rv is None:
|
||||
# Breaking here registered a partial variant table: the class would
|
||||
# keep some variants and silently lose every later one.
|
||||
self.Gen.TypeGenErr.fatal_error(cd, f"REnum 变体名副本分配失败(class='{class_name}'):PermPool 返回 NULL", "InternalCompilerError")
|
||||
break
|
||||
string.strcpy(vn_buf_rv, vname_src)
|
||||
self.RenumVariantNames[name_offset] = sizet(vn_buf_rv)
|
||||
@@ -1359,7 +1371,10 @@ novirtual class ClassGenEnum(ClassGenBase.ClassGenBase):
|
||||
if (self.RenumAliasClass is None or self.RenumAliasField is None
|
||||
or self.RenumAliasIndex is None or self.RenumAliasTy is None
|
||||
or self.RenumAliasVariant is None):
|
||||
# Same as the variant table: a silent bail left the alias table empty,
|
||||
# so match bindings later failed with an unrelated NameError.
|
||||
self.RenumAliasClass = None
|
||||
self.Gen.TypeGenErr.fatal_error(None, f"REnum 字段别名表分配失败(class='{class_name}'):PermPool 返回 NULL", "InternalCompilerError")
|
||||
return
|
||||
string.memset(self.RenumAliasClass, 0, 8 * RENUM_ALIAS_MAX)
|
||||
string.memset(self.RenumAliasField, 0, 8 * RENUM_ALIAS_MAX)
|
||||
@@ -1376,6 +1391,9 @@ novirtual class ClassGenEnum(ClassGenBase.ClassGenBase):
|
||||
CompilerFactory.factory.RenumAliasCount = self.RenumAliasCount
|
||||
|
||||
if self.RenumAliasCount >= RENUM_ALIAS_MAX:
|
||||
# Dropping the record silently made the variant field bind to a wrong-typed
|
||||
# slot (or fail with NameError) instead of reporting the limit breach.
|
||||
self.Gen.TypeGenErr.fatal_error(None, f"REnum 字段别名数超过上限 {RENUM_ALIAS_MAX}:无法注册 {class_name}.{field_name}", "InternalCompilerError")
|
||||
return
|
||||
|
||||
# 去重检查(去指针化):class 槽位存 perm_pool 字符串拷贝,用 strcmp 内容比较——
|
||||
|
||||
@@ -14,6 +14,7 @@ import lib.core.CompilerFactory as CompilerFactory
|
||||
|
||||
from . import ClassGenBase
|
||||
import lib.core.Gen.GenApi as GenApi
|
||||
import lib.core.Gen.DebugInfoGen as DebugInfoGen
|
||||
|
||||
|
||||
# 方法元数据 flag:TLS(@t.TLS 装饰器,对齐 ClassGenBase 本地常量;本地副本避免跨模块 CDefine 查找)
|
||||
@@ -757,6 +758,11 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase):
|
||||
return 0
|
||||
llvmlite.position_at_end(func_builder, entry_blk)
|
||||
|
||||
# --traceback: attach a DISubprogram to this method define (same as the
|
||||
# module-level function path). No-op when DI is disabled.
|
||||
dbg_pushed: int = DebugInfoGen.PushFunction(pool, func_builder, func,
|
||||
fd.lineno, fd.col_offset + 1)
|
||||
|
||||
# @t.TLS 一次性初始化守卫(对齐 TPC HandlesFunctions.py L1763-1792;
|
||||
# 与 FuncGenMixin.translate_function_def 同一实现,类方法同样支持)
|
||||
if is_tls_m != 0:
|
||||
@@ -1024,6 +1030,9 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase):
|
||||
trans._closure_env_count = old_env_count
|
||||
trans._stop_iter_flag_ptr = old_stop_iter_flag
|
||||
|
||||
if dbg_pushed != 0:
|
||||
DebugInfoGen.PopFunction(func_builder)
|
||||
|
||||
return 0
|
||||
# ============================================================
|
||||
# _generate_before_init — 生成 __before_init__ 函数
|
||||
|
||||
@@ -0,0 +1,443 @@
|
||||
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
|
||||
@@ -363,12 +363,13 @@ novirtual class ExprGenBase:
|
||||
return llvmlite.build_inttoptr(builder, llvmlite.const_int64(builder.Pool, 0), target_ty)
|
||||
self.Gen.TypeGenErr.fatal_error(node, "整数隐式转指针已禁止:必须显式强转为指针(如 t.CPtr(x))才能把整数赋给指针类型", "CastError")
|
||||
return val
|
||||
# 指针 → 整数: ptrtoint(当目标明确是整数而非指针时)
|
||||
# 注意: target_bits == 8 时跳过 ptrtoint,走后面的 build_load 解引用首字符
|
||||
# 修复: buf[idx] = '\0' 中 '\0' 是 CONST_STR → i8* 指针,
|
||||
# ptrtoint 会截断地址低 8 位(非 0),应 load 解引用取首字符 0
|
||||
if self.is_ptr_type(val.Ty) != 0 and target_bits != 0 and target_bits != 8 and self.is_ptr_type(target_ty) == 0:
|
||||
return llvmlite.build_ptrtoint(builder, val, target_ty)
|
||||
# Pointer -> integer: rejected (symmetric with the integer -> pointer rule above).
|
||||
# No implicit pointer <-> integer conversion is allowed; an explicit cast is
|
||||
# required. (The old code silently did ptrtoint, and for 8-bit targets fell
|
||||
# through to the build_load branch below, copying the pointee's first byte.)
|
||||
if self.is_ptr_type(val.Ty) != 0 and target_bits != 0 and self.is_ptr_type(target_ty) == 0:
|
||||
self.Gen.TypeGenErr.fatal_error(node, "指针隐式转整数已禁止:必须显式强转(如 uint64(p) / t.CInt64T(p) / int(p))才能把指针赋给整数类型", "CastError")
|
||||
return val
|
||||
if val_bits != 0 and target_bits != 0:
|
||||
if val_bits == target_bits:
|
||||
return val
|
||||
@@ -432,11 +433,15 @@ novirtual class ExprGenBase:
|
||||
if bc_agg is not None:
|
||||
return bc_agg
|
||||
return tmp_agg
|
||||
# 指针 → 非指针值: build_load 解引用
|
||||
# 适用于: 指针 → 整数 (如 i8* → i8), 指针 → 结构体值
|
||||
# 当构造器返回 Ptr(Struct) 但目标变量是 Struct 值类型时,需要 load
|
||||
# Pointer -> non-pointer value: only aggregate targets keep the implicit load
|
||||
# (a constructor returning Ptr(Struct) held in a Struct-typed variable is
|
||||
# reinterpreted in place). Scalar integer targets were already rejected above.
|
||||
if self.is_ptr_type(val.Ty) != 0 and self.is_ptr_type(target_ty) == 0:
|
||||
return llvmlite.build_load(builder, target_ty, val)
|
||||
target_kind: int = target_ty.kind()
|
||||
if target_kind == llvmlite.LLVMType.Struct or target_kind == llvmlite.LLVMType.Array:
|
||||
return llvmlite.build_load(builder, target_ty, val)
|
||||
self.Gen.TypeGenErr.fatal_error(node, "指针隐式转非指针值已禁止:必须显式解引用(c.Deref(p) / *p)或显式强转(uint64(p))", "CastError")
|
||||
return val
|
||||
# 指针 → 指针: bitcast (如 i8* → i8** 当目标是全局变量存储指针)
|
||||
if self.is_ptr_type(val.Ty) != 0 and self.is_ptr_type(target_ty) != 0:
|
||||
return llvmlite.build_bitcast(builder, val, target_ty)
|
||||
|
||||
@@ -951,30 +951,14 @@ novirtual class ExprGenName(ExprGenBase.ExprGenBase):
|
||||
return None
|
||||
case _:
|
||||
pass
|
||||
# [FIX 2026-08-31] 64 位标量 | t.CPtr 联合字段下标读回
|
||||
# (如 RenumClassNames: sizet*,联合解析为 i64* 8 字节
|
||||
# 槽数组):槽位按 Int64 load 得位模式,赋给 str(i8*)触发 int→ptr
|
||||
# 禁令误报(ClassGenEnum.vp existing: str = self.RenumClassNames[i]
|
||||
# 根因)。槽里实际存的是 perm_pool 字符串指针——联合声明即"槽位可能
|
||||
# 存指针",合法 C 联合读回应按指针读回。统一按 i8* load(同宽 8 字节,
|
||||
# 位模式不变):指针目标直接 bitcast;整数目标隐式 ptrtoint 同样
|
||||
# 保持位模式(对齐 MatchGenMixin 的 REnum 槽位按声明类型读机制)。
|
||||
if trans is not None and sub.value is not None and sub.value.kind() == ast.ASTKind.Attribute:
|
||||
fa_u64: str = self.Gen.ExprGenAttr._resolve_attr_field_class(pool, trans, sub.value)
|
||||
if fa_u64 is not None and self._annot_is_64bit_scalar(fa_u64) != 0:
|
||||
match elem_ty2:
|
||||
case llvmlite.LLVMType.Int(eb_u64):
|
||||
if eb_u64 == 64:
|
||||
cell_p_u64: llvmlite.Value* = llvmlite.build_gep(builder, elem_ty2, ptr_val, idx_val)
|
||||
if cell_p_u64 is not None:
|
||||
i8_ty_u64: llvmlite.LLVMType* = llvmlite.Int8(pool)
|
||||
i8p_ty_u64: llvmlite.LLVMType* = llvmlite.Ptr(pool, i8_ty_u64)
|
||||
i8pp_ty_u64: llvmlite.LLVMType* = llvmlite.Ptr(pool, i8p_ty_u64)
|
||||
cast_cell_u64: llvmlite.Value* = llvmlite.build_bitcast(builder, cell_p_u64, i8pp_ty_u64)
|
||||
if cast_cell_u64 is not None:
|
||||
return llvmlite.build_load(builder, i8p_ty_u64, cast_cell_u64, vol_sub_gen)
|
||||
case _:
|
||||
pass
|
||||
# 64-bit scalar pointer fields (e.g. `sizet*`, `uint64*`, written as
|
||||
# `t.CUInt64T | t.CPtr`) keep the declared element type here: the slot is
|
||||
# a u64, so it is read as i64 -- same element type the write path uses
|
||||
# (see get_subscript_ptr). Loading it as i8* fabricated a pointer value
|
||||
# out of an integer slot and forced the caller through an implicit
|
||||
# ptrtoint / inttoptr, which the language forbids (it also made
|
||||
# `MemBuddy.free_lists[i]` mis-type its element). Sources that store or
|
||||
# read a pointer through such a slot must cast explicitly.
|
||||
# 自定义结构体 (如 self._ht[key]): 仅当类定义了 __getitem__ 才转发方法调用;
|
||||
# 否则普通结构体指针按下标做 GEP 数组访问
|
||||
cls_nm_rd_gen: str = self._get_custom_struct_cls_nm(pool, elem_ty2)
|
||||
|
||||
@@ -13,6 +13,7 @@ import lib.core.VLogger as VLogger
|
||||
import lib.core.Gen.Translator as HT
|
||||
import lib.core.Gen.Call.CallGenBase as CallGenBase
|
||||
import lib.core.Gen.GenApi as GenApi
|
||||
import lib.core.Gen.DebugInfoGen as DebugInfoGen
|
||||
|
||||
|
||||
# ============================================================
|
||||
@@ -658,6 +659,12 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
|
||||
return 0
|
||||
llvmlite.position_at_end(func_builder, entry_blk)
|
||||
|
||||
# --traceback: attach a DISubprogram to this define and set every
|
||||
# instruction in the body to the entry DILocation (the statement hook
|
||||
# refines it per statement). No-op when DI is disabled.
|
||||
dbg_pushed: int = DebugInfoGen.PushFunction(pool, func_builder, func,
|
||||
fd.lineno, fd.col_offset + 1)
|
||||
|
||||
# @t.TLS 一次性初始化守卫(对齐 TPC HandlesFunctions.py):首次调用执行函数体并置
|
||||
# 全局标志 __tls_init_done_{name}=1,后续调用直接返回默认值。不实现则每次调用都
|
||||
# TlsAlloc,provider 栈跨调用丢失。
|
||||
@@ -1030,6 +1037,9 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
|
||||
self.Gen.ClassGenBase.register_decorator(pool, fd.name, wb_name, decor_arg0)
|
||||
self._gen_decor_wrapper(trans, pool, mod, func, fd.name, wb_name, decor_handler, decor_arg0)
|
||||
|
||||
if dbg_pushed != 0:
|
||||
DebugInfoGen.PopFunction(func_builder)
|
||||
|
||||
return 0
|
||||
# ============================================================
|
||||
# _gen_decor_wrapper - 生成装饰器 wrapper 函数(对齐 TPC DecoratorPass.py
|
||||
|
||||
@@ -12,6 +12,7 @@ import lib.core.VLogger as VLogger
|
||||
import lib.core.Gen.Translator as HT
|
||||
import lib.core.Gen.Call.CallGenBase as CallGenBase
|
||||
import lib.core.Gen.GenApi as GenApi
|
||||
import lib.core.Gen.DebugInfoGen as DebugInfoGen
|
||||
|
||||
|
||||
# ============================================================
|
||||
@@ -107,6 +108,10 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
if func_builder is None: return 0
|
||||
llvmlite.position_at_end(func_builder, entry_blk)
|
||||
|
||||
# --traceback: promoted nested closure gets its own DISubprogram.
|
||||
dbg_pushed_n: int = DebugInfoGen.PushFunction(pool, func_builder, func,
|
||||
node.lineno, node.col_offset + 1)
|
||||
|
||||
# 4. 进入嵌套函数作用域(嵌套符号表)
|
||||
self.Gen.VarGen.enter_scope(trans.SymTab, SCOPE_FUNCTION)
|
||||
|
||||
@@ -249,6 +254,8 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
self.Gen.VarGen.exit_scope(trans.SymTab)
|
||||
trans._cur_func = old_func
|
||||
trans._cur_builder = old_builder
|
||||
if dbg_pushed_n != 0:
|
||||
DebugInfoGen.PopFunction(func_builder)
|
||||
# NOTE: _nonlocal_names 和 _nonlocal_name_count 仍为嵌套函数的值
|
||||
|
||||
# 13. 在父函数中创建闭包对象
|
||||
@@ -1120,6 +1127,10 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
return None
|
||||
llvmlite.position_at_end(func_builder, entry_blk)
|
||||
|
||||
# --traceback: promoted lambda gets its own DISubprogram.
|
||||
dbg_pushed_l: int = DebugInfoGen.PushFunction(pool, func_builder, func,
|
||||
node.lineno, node.col_offset + 1)
|
||||
|
||||
# 6. 进入 lambda 作用域
|
||||
self.Gen.VarGen.enter_scope(trans.SymTab, SCOPE_FUNCTION)
|
||||
|
||||
@@ -1209,6 +1220,8 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
|
||||
trans._global_name_count = old_global_count
|
||||
trans._nonlocal_name_count = old_nonlocal_count
|
||||
trans._closure_env_count = old_env_count
|
||||
if dbg_pushed_l != 0:
|
||||
DebugInfoGen.PopFunction(func_builder)
|
||||
|
||||
# 13. 在父函数中创建 env(每个捕获变量 8 字节堆存储,env[i] = 堆地址)
|
||||
env_ptr: llvmlite.Value* = None
|
||||
|
||||
@@ -660,6 +660,14 @@ novirtual class StmtGenCifMixin:
|
||||
# 传 trans:无符号源类型(uint16/uint32/CSizeT 等)扩展用 zext,
|
||||
# 缺 trans 时 _node_type_unsigned 查不到变量注解 → 误 sext
|
||||
val = self.Gen.ExprGenBase.coerce_to_type(builder, val, ret_ty_rt, node, trans)
|
||||
elif self.Gen.ExprGenBase.get_llvm_float_bits(val.Ty) != 0 \
|
||||
or self.Gen.ExprGenBase.get_llvm_float_bits(ret_ty_rt) != 0:
|
||||
# Float alignment: SpecTypeBits reports 0 for Float/Double, so the
|
||||
# integer branch above never runs and a double literal returned from a
|
||||
# `-> float` function would emit `ret double` (llc: value doesn't match
|
||||
# function result type 'float'). Coerce here (fpext/fptrunc/si2fp/fptosi),
|
||||
# same as TPC's emit_return.
|
||||
val = self.Gen.ExprGenBase.coerce_to_type(builder, val, ret_ty_rt, node, trans)
|
||||
llvmlite.build_ret(builder, val)
|
||||
return 0
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ import lib.core.Gen.Translator as HT
|
||||
import lib.core.CompilerFactory as CompilerFactory
|
||||
import lib.Projectrans.Config as Config
|
||||
import lib.core.Gen.Stmt.StmtGenDeclMixin as StmtGenDeclMixin
|
||||
import lib.core.Gen.DebugInfoGen as DebugInfoGen
|
||||
|
||||
|
||||
# ============================================================
|
||||
@@ -32,6 +33,12 @@ novirtual class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
|
||||
node: ast.AST*) -> int:
|
||||
"""翻译单条语句,返回新增的变量数(深度守卫包装器,原逻辑见 _translate_stmt_impl)"""
|
||||
if trans is not None:
|
||||
# --traceback: statement-level DWARF location (no-op outside a
|
||||
# DI-attached define or when DI is disabled). 此后本语句生成的每条
|
||||
# 指令自动带 ", !dbg !N",保证函数体内每个 call 都有合法 !dbg。
|
||||
if node is not None and trans._cur_builder is not None:
|
||||
DebugInfoGen.SetStatement(trans.Pool, trans._cur_builder,
|
||||
node.lineno, node.col_offset + 1)
|
||||
if trans.TranslateDepth >= HT.TRANSLATE_MAX_DEPTH:
|
||||
self.Gen.TypeGenErr.fatal_error(node, "语句翻译嵌套过深(疑似 AST 自引用环或翻译 bug)", "RecursionError")
|
||||
trans.TranslateDepth += 1
|
||||
|
||||
@@ -14,6 +14,7 @@ import lib.Projectrans.Config as Config
|
||||
import lib.Stub.StubMerger.StubMergerBuild as StubMergerBuild
|
||||
import lib.Stub.StubMerger.StubMergerStore as StubMergerStore
|
||||
import lib.core.Gen.Generator as Generator
|
||||
import lib.core.Gen.DebugInfoGen as DebugInfoGen
|
||||
|
||||
# ============================================================
|
||||
# Gen/Translator.vp - 翻译器状态管理 + 主入口
|
||||
@@ -441,6 +442,11 @@ novirtual class Translator:
|
||||
|
||||
pool: memhub.MemBuddy* = _mbuddy
|
||||
|
||||
# OOM 归属:把「阶段/当前模块」写进 memhub 的分配上下文。池耗尽时 memhub 会打印
|
||||
# ctx=阶段/模块,从而知道是在翻译哪个模块时被吃满(SetAllocCtx 只存指针不拷贝,
|
||||
# ModuleSha1 由调用方在 translate 前设置且与本次翻译同寿命)。此前无人设置该上下文。
|
||||
memhub.SetAllocCtx("translate", self.ModuleSha1)
|
||||
|
||||
# 初始化状态
|
||||
self._init_state(pool)
|
||||
|
||||
@@ -451,6 +457,9 @@ novirtual class Translator:
|
||||
return 1
|
||||
self.Module = mod
|
||||
|
||||
# --traceback: reset per-module DWARF state (no-op when DI is disabled).
|
||||
DebugInfoGen.BeginModule(mod, GenApi.get_current_file())
|
||||
|
||||
# 设置目标(优先使用 project.vpj 中的配置)
|
||||
triple: str = Config.TargetTriple
|
||||
if triple is None:
|
||||
@@ -511,8 +520,12 @@ novirtual class Translator:
|
||||
# 用户已定义 main → 委托 HandlesMain 翻译模块级
|
||||
self._translate_module_level(pool, mod, tree)
|
||||
|
||||
return 0
|
||||
# --traceback: register module-level !llvm.dbg.cu / !llvm.module.flags
|
||||
# before the caller dumps the IR. Idempotent; no-op when no define got a
|
||||
# DISubprogram (stub / _declare_only) or when DI is disabled.
|
||||
DebugInfoGen.Finalize(pool)
|
||||
|
||||
return 0
|
||||
# ============================================================
|
||||
# 包装 main 函数(无用户 main 时)→ 委托 HandlesMain
|
||||
# ============================================================
|
||||
@@ -808,6 +821,137 @@ novirtual class Translator:
|
||||
llvmlite.LLVMModulePrint(buf, size, self.Module, _mbuddy, mode)
|
||||
|
||||
|
||||
# ============================================================
|
||||
# --traceback: startup Install() ctor injection
|
||||
#
|
||||
# Lives here (not in BuildPipeline) because this module already imports
|
||||
# llvmlite. Calling llvmlite.* from a module without that import mangles the
|
||||
# calls with the package (__init__) SHA1, which no object defines, instead of
|
||||
# the defining submodule's SHA1 (link-time undefined symbol). BuildPipeline
|
||||
# only decides whether to trigger the injection.
|
||||
#
|
||||
# Injects into the traceback module's IR:
|
||||
# define void @__traceback_install_ctor() { entry: call @Install(); ret void }
|
||||
# plus an appending-linkage @llvm.global_ctors so the CRT runs Install() once at
|
||||
# process start (mirrors TPC BaseGen._RegisterTracebackInstallViaCtor).
|
||||
# fail-fast: a pre-existing @llvm.global_ctors, or a missing Install symbol,
|
||||
# aborts the build (silent degradation would leave the product without a map).
|
||||
# ============================================================
|
||||
|
||||
# _tb_module_has_global_ctors - does the module already define @llvm.global_ctors
|
||||
def _tb_module_has_global_ctors(mod: llvmlite.LLVMModule*) -> int:
|
||||
if mod is None:
|
||||
return 0
|
||||
g: llvmlite.GlobalVariable* = mod.GlobalHead
|
||||
steps: sizet = 0
|
||||
while g is not None:
|
||||
steps += 1
|
||||
if steps > 100000:
|
||||
break
|
||||
if g.Name is not None and string.strcmp(g.Name, "llvm.global_ctors") == 0:
|
||||
return 1
|
||||
g = g.Next
|
||||
return 0
|
||||
|
||||
|
||||
# _tb_find_install_function - locate this module's own Install()
|
||||
#
|
||||
# Accepts "<16hex>.Install" (the usual SHA1-mangled form) or the bare name.
|
||||
def _tb_find_install_function(mod: llvmlite.LLVMModule*) -> llvmlite.Function*:
|
||||
if mod is None:
|
||||
return None
|
||||
cur: llvmlite.Function* = mod.FuncHead
|
||||
steps: sizet = 0
|
||||
while cur is not None:
|
||||
steps += 1
|
||||
if steps > 100000:
|
||||
return None
|
||||
nm: str = llvmlite.function_get_name(cur)
|
||||
if nm is not None:
|
||||
if string.strcmp(nm, "Install") == 0:
|
||||
return cur
|
||||
nl: sizet = llvmlite.function_get_name_len(cur)
|
||||
if nl == 24 and string.strcmp(nm + 16, ".Install") == 0:
|
||||
return cur
|
||||
cur = llvmlite.function_get_next(cur)
|
||||
return None
|
||||
|
||||
|
||||
# InjectTracebackInstallCtor - inject startup Install() ctor into the module
|
||||
#
|
||||
# NOTE: FunctionPrint has no linkage field, so the ctor is emitted with default
|
||||
# (external) linkage instead of TPC's 'internal'; only traceback.vp defines it,
|
||||
# so there is no symbol clash.
|
||||
def InjectTracebackInstallCtor(tr: rawptr[Translator]) -> int:
|
||||
if tr is None or tr.Module is None:
|
||||
return 0
|
||||
if tr._declare_only != 0:
|
||||
return 0
|
||||
pool: memhub.MemBuddy* = tr.Pool
|
||||
if pool is None:
|
||||
VLogger.error("--traceback: 内存池未初始化,无法注入 Install 构造器", "TRACEBACK")
|
||||
return 1
|
||||
mod: llvmlite.LLVMModule* = tr.Module
|
||||
if _tb_module_has_global_ctors(mod) != 0:
|
||||
VLogger.error("--traceback: 模块内已存在 @llvm.global_ctors,无法注入 traceback 启动期构造器(不静默降级)", "TRACEBACK")
|
||||
return 1
|
||||
install_fn: llvmlite.Function* = _tb_find_install_function(mod)
|
||||
if install_fn is None:
|
||||
VLogger.error("--traceback: 在 traceback 模块内找不到 Install() 函数,无法注入启动期构造器(不静默降级)", "TRACEBACK")
|
||||
return 1
|
||||
|
||||
# 1) ctor: entry { call <ret> @Install(); ret void }
|
||||
void_ty: llvmlite.LLVMType* = llvmlite.Void(pool)
|
||||
ctor: llvmlite.Function* = llvmlite.new_function(pool, "__traceback_install_ctor", void_ty)
|
||||
if ctor is None:
|
||||
VLogger.error("--traceback: 构造器函数分配失败", "TRACEBACK")
|
||||
return 1
|
||||
blk: llvmlite.BasicBlock* = llvmlite.new_basic_block(pool, "entry")
|
||||
if blk is None:
|
||||
VLogger.error("--traceback: 构造器基本块分配失败", "TRACEBACK")
|
||||
return 1
|
||||
llvmlite.function_add_block(ctor, blk)
|
||||
llvmlite.module_add_function(mod, ctor)
|
||||
builder: llvmlite.IRBuilder* = llvmlite.new_builder(pool, ctor)
|
||||
if builder is None:
|
||||
VLogger.error("--traceback: 构造器 IRBuilder 分配失败", "TRACEBACK")
|
||||
return 1
|
||||
llvmlite.position_at_end(builder, blk)
|
||||
iname: str = llvmlite.function_get_name(install_fn)
|
||||
install_ret: llvmlite.LLVMType* = llvmlite.function_get_ret_ty(install_fn)
|
||||
llvmlite.build_call(builder, iname, None, 0, install_ret, 0)
|
||||
llvmlite.build_ret_void(builder)
|
||||
|
||||
# 2) @llvm.global_ctors = appending global [1 x {i32, void ()*, i8*}] [ ... ]
|
||||
i32_ty: llvmlite.LLVMType* = llvmlite.Int32(pool)
|
||||
i8_ty: llvmlite.LLVMType* = llvmlite.Int8(pool)
|
||||
i8p_ty: llvmlite.LLVMType* = llvmlite.Ptr(pool, i8_ty)
|
||||
fnv_ty: llvmlite.LLVMType* = llvmlite.Func(pool, void_ty, None, 0)
|
||||
fnp_ty: llvmlite.LLVMType* = llvmlite.Ptr(pool, fnv_ty)
|
||||
f1: llvmlite.ParamNode* = llvmlite.new_param_node(pool, i32_ty)
|
||||
f2: llvmlite.ParamNode* = llvmlite.new_param_node(pool, fnp_ty)
|
||||
f3: llvmlite.ParamNode* = llvmlite.new_param_node(pool, i8p_ty)
|
||||
if f1 is None or f2 is None or f3 is None:
|
||||
VLogger.error("--traceback: global_ctors 结构体字段分配失败", "TRACEBACK")
|
||||
return 1
|
||||
llvmlite.paramnode_set_next(f1, f2)
|
||||
llvmlite.paramnode_set_next(f2, f3)
|
||||
elem_ty: llvmlite.LLVMType* = llvmlite.Struct(pool, f1, 3, None)
|
||||
arr_ty: llvmlite.LLVMType* = llvmlite.Array(pool, elem_ty, 1)
|
||||
if elem_ty is None or arr_ty is None:
|
||||
VLogger.error("--traceback: global_ctors 类型构造失败", "TRACEBACK")
|
||||
return 1
|
||||
gv: llvmlite.GlobalVariable* = llvmlite.new_global_variable(pool, "llvm.global_ctors", arr_ty)
|
||||
if gv is None:
|
||||
VLogger.error("--traceback: @llvm.global_ctors 全局变量分配失败", "TRACEBACK")
|
||||
return 1
|
||||
llvmlite.global_set_linkage(gv, "appending")
|
||||
gv.Initializer = "[{ i32, void ()*, i8* } { i32 65535, void ()* @__traceback_install_ctor, i8* null }]"
|
||||
llvmlite.module_add_global(mod, gv)
|
||||
VLogger.info("--traceback: 已注入启动期 Install() 构造器(@llvm.global_ctors)", "TRACEBACK")
|
||||
return 0
|
||||
|
||||
|
||||
# ============================================================
|
||||
# Mixin - Handler 非多态基类(自 HandlesBase 迁入)
|
||||
#
|
||||
|
||||
+39
-10
@@ -148,7 +148,8 @@ def main() -> int:
|
||||
parser.add_argument("--run", None, argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "Run the generated executable after compilation")
|
||||
parser.add_argument("--rebuild-includes", None, argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "Delete the includes.binary precompiled cache and rebuild all the includes")
|
||||
parser.add_argument("--clear-cache", None, argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "Clear .transpyc_cache global cache")
|
||||
parser.add_argument("--asan", None, argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "ASan mode: .ll first opt -passes=asan; ASan runtime libs must be declared in project.vpj (linker.static_libs)")
|
||||
parser.add_argument("--asan", None, argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "ASan mode: .ll first opt -passes=asan; ASan runtime libs must be declared in project.vpj (linker.static_libs)")
|
||||
parser.add_argument("--traceback", None, argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "Traceback mode: force-compile includes/traceback.vp + Install() ctor, llc -frame-pointer=all, link -ldbghelp, export <exe>.stackmap")
|
||||
parser.add_argument("--phase2command", None, argparse.STRING, 0, None, False, argparse.STORE, "Do not run llc/link: write Phase2 llc(opt+llc) and clang link commands to the given .bat path")
|
||||
parser.add_argument("--nowarn-register", None, argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "Suppress Phase A dependency WARN")
|
||||
parser.add_argument("--detection", None, argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "Enable MemBuddy allocation detection (0xCDCD markers + record-table probe for out-of-bounds writes; applies to both mb and perm_pool)")
|
||||
@@ -214,6 +215,15 @@ Target: %s
|
||||
stdio.printf(" 处理: 去掉 --asan(如需排查内存问题,请用工程模式构建 ASan 版 TransPyV)\n")
|
||||
argparse.release(args)
|
||||
return 2
|
||||
# --traceback is also rejected under --repl: the fragment is a runtime-loaded
|
||||
# DLL (DI metadata / startup ctor semantics undefined) and it has no <exe>.stackmap.
|
||||
if args.get_bool("traceback"):
|
||||
stdio.printf("FAIL: --repl 与 --traceback 互斥\n")
|
||||
stdio.printf(" 原因: --traceback 依赖启动期构造器注入 + 本模块 DWARF 旁文件,\n")
|
||||
stdio.printf(" 而片段是运行期 LoadLibraryA 载入的 DLL,二者语义未定义\n")
|
||||
stdio.printf(" 处理: 去掉 --traceback(如需崩溃栈,请用工程模式构建 --traceback 版 TransPyV)\n")
|
||||
argparse.release(args)
|
||||
return 2
|
||||
|
||||
# === --repl: M1 交互式循环(表达式片段 → DLL → 调用回显)===
|
||||
# 分支位置:见下方"工程配置加载"之后(Repl 需要 Config 的 compiler/linker/target 通道),
|
||||
@@ -255,19 +265,38 @@ Target: %s
|
||||
else: stdio.printf(" --clear-cache: False\n")
|
||||
stdio.printf("参数解析完成。\n\n")
|
||||
|
||||
# ASan 模式:includes 缓存用独立目录 .binary_asan 隔离 asan 插桩 obj,避免与非 asan obj 混链(undefined reference)
|
||||
if args.get_bool("asan"):
|
||||
# 模式开关:includes 缓存用独立后缀目录隔离,避免不同模式产物的 obj 混链
|
||||
# (ASan 插桩 obj / traceback 帧指针+ctor obj 与非对应模式不兼容)。
|
||||
# --asan 与 --traceback 可同开 ⇒ 后缀必须一次性按两个开关组合计算,
|
||||
# 后续 --clean/--rebuild-includes 经 Config.get_includes_binary_dir() 自动覆盖。
|
||||
gb_asan: bool = args.get_bool("asan")
|
||||
gb_traceback: bool = args.get_bool("traceback")
|
||||
if gb_asan:
|
||||
BuildPipeline._asan_mode = True
|
||||
Config._inc_bin_suffix = ".binary_asan"
|
||||
log.info("ASan 模式已启用(opt -passes=asan 插桩; 运行库需 project.vpj 声明)", "asan")
|
||||
# ASan 模式清理 asan includes 缓存(强制重建 asan 插桩 obj)
|
||||
mf_inc_bin_asan: str = Config.get_includes_binary_dir()
|
||||
if mf_inc_bin_asan is not None:
|
||||
n_asan: int = Utils.CleanDir(mf_inc_bin_asan)
|
||||
log.info(f"ASan 模式清理 includes 缓存: {n_asan} 个文件(将重建 asan obj)", "asan")
|
||||
else:
|
||||
BuildPipeline._asan_mode = False
|
||||
if gb_traceback:
|
||||
BuildPipeline._traceback_mode = True
|
||||
else:
|
||||
BuildPipeline._traceback_mode = False
|
||||
if gb_asan and gb_traceback:
|
||||
Config._inc_bin_suffix = ".binary_traceback_asan"
|
||||
elif gb_traceback:
|
||||
Config._inc_bin_suffix = ".binary_traceback"
|
||||
elif gb_asan:
|
||||
Config._inc_bin_suffix = ".binary_asan"
|
||||
else:
|
||||
Config._inc_bin_suffix = ".binary"
|
||||
if gb_asan:
|
||||
log.info("ASan 模式已启用(opt -passes=asan 插桩; 运行库需 project.vpj 声明)", "asan")
|
||||
if gb_traceback:
|
||||
log.info("Traceback 模式已启用(llc 保留帧指针 + 链 dbghelp + Install 构造器 + <exe>.stackmap)", "traceback")
|
||||
# 开启任一模式都清空对应后缀的 includes 缓存,强制重建匹配当前模式的 obj
|
||||
if gb_asan or gb_traceback:
|
||||
mf_inc_bin_mode: str = Config.get_includes_binary_dir()
|
||||
if mf_inc_bin_mode is not None:
|
||||
n_mode: int = Utils.CleanDir(mf_inc_bin_mode)
|
||||
log.info(f"模式缓存清理: {mf_inc_bin_mode} 删除 {n_mode} 个文件(将按 {Config._inc_bin_suffix} 重建)", "mode")
|
||||
|
||||
# === --phase2command: 只生成命令写入 bat(Phase B/B+ 的 llc 与 Phase C 的 clang 链接命令),不执行 llc/链接 ===
|
||||
p2c: str = args.get_str("phase2command")
|
||||
|
||||
Reference in new issue
Block a user