fix(hotspot): 深度栈改用 common linkage,修复跨模块 self 记账漏扣
症状:--hotspot-sites 下跨模块调用链的调用方 self 约等于 inc(子调用 inc 未被扣除),而同模块链正常,导致所有基于 self 的结论带偏差(实测 _translate_module_level 421.9e9/422.0e9、translate_children 164.5e9/164.9e9 等)。 根因:__fhs_tdepth/__fhs_tstart/__fhs_tchild 原为 internal linkage,Projectrans 多模块模式下每个 .vp 翻译单元各持一份独立深度栈;跨模块调用时被调方读到的深度为 0,slot=0,ret 时 slot>0 判定为假,其 inc 永远写不到调用方的 tchild[slot-1],于是调用方 self = inc - 0 = inc。 修复:三件套改 common linkage,由链接器合并为全程序唯一一份(与本仓库多模块同名全局的既有做法一致);__fhs_tscratch 与每函数计数器仍保持 internal。已用逐指令 Python 模拟验证:同模块各形态均正确、跨模块独立栈必然漏扣、共享栈后全部正确。 仅 --hotspot-sites 启用时生效,未启用路径逐字节不变。旧的 hotspot_time.txt 在跨模块链上的 self 列不可信,需重新测量。
This commit is contained in:
1 parent
136e9bc33e
commit
bf4bab093a
1 file changed
+19
-7
@@ -1099,8 +1099,10 @@ class BaseGenMixin:
|
||||
tick(x86_64 上即一个 CPU 周期,~0.3ns 量级),故耗时单位记为 tick。
|
||||
|
||||
exclusive(自身耗时)算法——深度栈:
|
||||
- 全局 internal i64 __fhs_tdepth:当前插桩深度,初值 0;
|
||||
- 全局 internal [DEPTH x i64] __fhs_tstart / __fhs_tchild(零初始化,
|
||||
- 全局 common i64 __fhs_tdepth:当前插桩深度,初值 0;common linkage 让每个
|
||||
翻译单元各自发射同名 tentative 定义、由链接器合并为**全程序唯一一份**,
|
||||
故跨模块调用也共用同一张深度栈(独立翻译单元链接不产生重复符号)。
|
||||
- 全局 common [DEPTH x i64] __fhs_tstart / __fhs_tchild(零初始化,
|
||||
DEPTH = _HotspotDepth = 512):按深度索引的"本帧起始 tick"与"本帧内被
|
||||
插桩子调用 inclusive 之和";另有一个 internal i64 抖动槽 __fhs_tscratch,
|
||||
用于把"不应记账"的写入导向一个永不读取的位置(等价于不写,但不分支)。
|
||||
@@ -1136,10 +1138,14 @@ class BaseGenMixin:
|
||||
函数如 strcmp/memcpy、名字以 __fhs_ 开头的辅助函数、深度溢出而跳过的调用)
|
||||
其耗时不会被本方法从调用者 self 中扣除,一律计入**调用者自身 self**。故 self
|
||||
是"该函数自身指令 + 未插桩子调用"的墙钟时间,只是比 inc 更贴近自身成本。
|
||||
深度栈为 common(全程序一份)后,跨模块被调方只要被插桩即能正确扣除;修复前
|
||||
各模块各持一份 internal 深度栈,跨模块子调用的 inc 记不进调用方 tchild,
|
||||
表现为调用方 self ≈ inc 的假象(见 common linkage 说明)。
|
||||
|
||||
插桩规则与跳过条件:
|
||||
- 只处理有基本块(即已定义)且名字不以 __fhs_ 开头的函数;计数器/深度栈
|
||||
一律 internal,跨模块(独立翻译单元)链接不产生重复符号,初值 0。
|
||||
- 只处理有基本块(即已定义)且名字不以 __fhs_ 开头的函数;每函数计数器一律
|
||||
internal(同名索引跨模块并存,不可合并);深度栈用 common 由链接器合并为
|
||||
全程序唯一一份,初值 0。
|
||||
- 入口代码置于入口块最前(无终结指令,不改变 CFG);条件写入用 select 实现,
|
||||
不新增基本块、不修改任何既有分支/终结指令。
|
||||
- 只在该函数的 ret 终结指令之前插桩;以 unreachable 等其它终结指令结尾的
|
||||
@@ -1154,18 +1160,24 @@ class BaseGenMixin:
|
||||
c0_64: ir.Constant = ir.Constant(i64, 0)
|
||||
c1_64: ir.Constant = ir.Constant(i64, 1)
|
||||
cneg1_64: ir.Constant = ir.Constant(i64, -1)
|
||||
# 深度栈三件套用 common linkage:每模块各自发射一份同名 tentative 定义,
|
||||
# 由链接器合并成"全程序唯一一份"(与 HandlesAssign 中多模块同名全局同法,
|
||||
# 避免 ld.lld duplicate symbol)。跨模块调用链必须共用同一张深度栈,否则
|
||||
# 被调方在自模块栈里 slot=0(或 slot 落在错误父帧)→ 其 inc 永远记不到
|
||||
# 调用方 __fhs_tchild[slot-1] → 调用方 self ≈ inc(子时间一分不扣)。
|
||||
# 抖动槽 __fhs_tscratch 只写不读,留 internal 即可(各模块独立无影响)。
|
||||
depth_gv: ir.GlobalVariable = ir.GlobalVariable(
|
||||
self.module, i64, name=self._UniqueGlobalName('__fhs_tdepth'))
|
||||
depth_gv.linkage = 'internal'
|
||||
depth_gv.linkage = 'common'
|
||||
depth_gv.initializer = c0_64
|
||||
arr_ty: ir.ArrayType = ir.ArrayType(i64, depth)
|
||||
tstart_gv: ir.GlobalVariable = ir.GlobalVariable(
|
||||
self.module, arr_ty, name=self._UniqueGlobalName('__fhs_tstart'))
|
||||
tstart_gv.linkage = 'internal'
|
||||
tstart_gv.linkage = 'common'
|
||||
tstart_gv.initializer = ir.Constant(arr_ty, None)
|
||||
tchild_gv: ir.GlobalVariable = ir.GlobalVariable(
|
||||
self.module, arr_ty, name=self._UniqueGlobalName('__fhs_tchild'))
|
||||
tchild_gv.linkage = 'internal'
|
||||
tchild_gv.linkage = 'common'
|
||||
tchild_gv.initializer = ir.Constant(arr_ty, None)
|
||||
scratch_gv: ir.GlobalVariable = ir.GlobalVariable(
|
||||
self.module, i64, name=self._UniqueGlobalName('__fhs_tscratch'))
|
||||
|
||||
Reference in new issue
Block a user