fix(lookup): 索引未命中一律回退原线性扫描,停用不可信的 Ty 索引判定
降低「索引可信且未命中即判定确无」语义带来的风险面(该语义是本轮构建 fail-fast 的最高嫌疑):索引只用于加速命中,未命中一律回退原线性扫描,使各查找函数在所有输入上的返回值与优化前逐字节一致。
StructGenMixin.find_struct:去掉「索引可信且查询名非空则跳过 O(n) 扫描」分支,未命中恢复原精确匹配全表扫描。
StructGenMixin.find_struct_by_type:整体停用 Ty 索引判定 —— 条目 Ty 会被深拷贝改写,槽按注册时 Ty 落位,改写后与 live 键脱钩,命中不保证是原线性首命中(不等价),故恢复原线性精确扫描;get_class_name_by_type(REnum.kind 识别路径)、register_struct 去重、lookup_field 首遍随之回到优化前语义。
CallGenLeafKit.find_func_in_table/find_func_entry_global、TypeGenCType.lookup_ctypedef、TypeGenCDef.lookup_cdefine_{constant,double,string}、VpiReader._lookup_vpi_param_annots/_lookup_vpi_ret_annot:可信未命中不再直接返回,一律落入原线性扫描。
未触碰:Function.NameLen/FUNC_SIZE、各处首字符预筛、includes/string.vp。
This commit is contained in:
1 parent
e7f47735ed
commit
4ed7f3909a
5 files changed
+57
-68
No files matched your search
@@ -687,15 +687,16 @@ def _lookup_vpi_param_annots(src_sha1: str, cls_name: str, method_name: str) ->
|
||||
r: str = None
|
||||
ki: int
|
||||
# 索引快速路径:索引可信(Built==Count)时一次探测返回命中槽下标(= 原线性
|
||||
# 扫描首个同名,首注册优先);未命中 | 索引不可信 → 回退原线性扫描(正确性保证)。
|
||||
# 扫描首个同名,首注册优先);未命中一律回退下方原线性扫描(索引只加速命中,
|
||||
# 未命中行为与未加索引前逐字节一致);此前"可信未命中即确无"的语义已回退。
|
||||
hit_p: int = -1
|
||||
if _g_vpi_param_idx_built == _g_vpi_param_count \
|
||||
and _g_vpi_param_idx is not None and _g_vpi_param_idx_cap > 0:
|
||||
hit_p = _vpi_idx_find(_g_vpi_param_keys, VPI_PARAM_KEY_LEN,
|
||||
_g_vpi_param_idx, _g_vpi_param_idx_cap, key_buf)
|
||||
else:
|
||||
# 快速路径:strcmp 判等蕴含首字节相等;键首字节 = src_sha1[0],缓存含多模块
|
||||
# 条目,首字节不同者必不相等,直接跳过其 strcmp(仅做否定判定,不改变命中)。
|
||||
if hit_p < 0:
|
||||
# 回退原线性扫描:strcmp 判等蕴含首字节相等;键首字节 = src_sha1[0],缓存含
|
||||
# 多模块条目,首字节不同者必不相等,直接跳过其 strcmp(仅做否定判定,不改命中)。
|
||||
key_first: int = key_buf[0]
|
||||
for ki in range(_g_vpi_param_count):
|
||||
ent_p: bytes = _g_vpi_param_keys + ki * VPI_PARAM_KEY_LEN
|
||||
@@ -1063,14 +1064,15 @@ def _lookup_vpi_ret_annot(src_sha1: str, cls_name: str, method_name: str) -> str
|
||||
viperlib.snprintf(key_buf, key_len + 1, "%s.%s", src_sha1, method_name)
|
||||
i: int
|
||||
# 索引快速路径:索引可信(Built==Count)时一次探测返回命中槽下标(= 原线性
|
||||
# 扫描首个同名,首注册优先);未命中 | 索引不可信 → 回退原线性扫描(正确性保证)。
|
||||
# 扫描首个同名,首注册优先);未命中一律回退下方原线性扫描(索引只加速命中,
|
||||
# 未命中行为与未加索引前逐字节一致);此前"可信未命中即确无"的语义已回退。
|
||||
hit_r: int = -1
|
||||
if _g_vpi_ret_idx_built == _g_vpi_ret_count \
|
||||
and _g_vpi_ret_idx is not None and _g_vpi_ret_idx_cap > 0:
|
||||
hit_r = _vpi_idx_find(_g_vpi_ret_keys, VPI_KEY_LEN,
|
||||
_g_vpi_ret_idx, _g_vpi_ret_idx_cap, key_buf)
|
||||
else:
|
||||
# 快速路径:strcmp 判等蕴含首字节相等(键首字节 = src_sha1[0]),首字节不同者跳过。
|
||||
if hit_r < 0:
|
||||
# 回退原线性扫描:strcmp 判等蕴含首字节相等(键首字节 = src_sha1[0]),跳过首字节不同者。
|
||||
key_first: int = key_buf[0]
|
||||
for i in range(_g_vpi_ret_count):
|
||||
ent_r: bytes = _g_vpi_ret_keys + i * VPI_KEY_LEN
|
||||
|
||||
@@ -1051,7 +1051,8 @@ class CallGenLeafKit(CallGenCBuiltin.CallGenCBuiltin):
|
||||
t.CVoid(uint64(self.GlobalFuncs) + fi_t * entry_size, t.CPtr))
|
||||
if fe_t is not None:
|
||||
return fe_t.Func
|
||||
return None
|
||||
# 索引未命中一律回退下方原线性扫描(索引只加速命中,未命中行为与未加
|
||||
# 索引前逐字节一致);此前"可信未命中即 return None"的语义已回退。
|
||||
# 单遍扫描(合并原 owner 优先遍 + 通用遍,语义完全一致但省一半遍历):
|
||||
# 扫描中同时记录"首个同裸名条目"作通用兜底;一旦发现本模块条目
|
||||
# (mangled 名以 "{owner_sha1}." 开头,16 位前缀 + '.')立即返回。
|
||||
@@ -1272,7 +1273,8 @@ class CallGenLeafKit(CallGenCBuiltin.CallGenCBuiltin):
|
||||
return None
|
||||
entry_size: sizet = GenTypes.FuncEntry.__sizeof__()
|
||||
# 名索引快速路径:可信(Built==Count)时一次探测返回首个同名条目
|
||||
# (= 原线性扫描首个命中);未命中/索引不可信 → 回退原线性扫描(正确性保证)。
|
||||
# (= 原线性扫描首个命中);未命中一律回退下方原线性扫描(索引只加速命中,
|
||||
# 未命中行为与未加索引前逐字节一致);此前"可信未命中即 return None"已回退。
|
||||
if self.GlobalFuncNameIdxBuilt == self.GlobalFuncCount \
|
||||
and self.GlobalFuncNameIdx is not None and self.GlobalFuncNameIdxCap > 0:
|
||||
gi: int = _gfn_idx_find(self.GlobalFuncs, self.GlobalFuncNameIdx,
|
||||
@@ -1281,7 +1283,6 @@ class CallGenLeafKit(CallGenCBuiltin.CallGenCBuiltin):
|
||||
entry_g: GenTypes.FuncEntry | t.CPtr = (GenTypes.FuncEntry | t.CPtr)(
|
||||
t.CVoid(uint64(self.GlobalFuncs) + gi * entry_size, t.CPtr))
|
||||
return entry_g
|
||||
return None
|
||||
for i in range(self.GlobalFuncCount):
|
||||
entry_addr: uint64 = uint64(self.GlobalFuncs) + i * entry_size
|
||||
entry: GenTypes.FuncEntry | t.CPtr = (GenTypes.FuncEntry | t.CPtr)(t.CVoid(entry_addr, t.CPtr))
|
||||
|
||||
@@ -393,10 +393,8 @@ class StructGenMixin:
|
||||
|
||||
# 用类型指针去重(同一类型不重复注册,支持跨模块同名类)
|
||||
# 用 uint64 指针值比较(is 比较会触发类型安全检查解引用悬空 Ty)
|
||||
# 类型索引快速路径:find_struct_by_type 的 Ty 指针身份语义 = 原去重扫描
|
||||
# "首个 Ty 指针相等条目"(首注册优先),且其索引未命中 | 不可信时会回退
|
||||
# 原线性扫描(正确性保证,非兜底)⇒ 与原扫描结果逐字节一致。
|
||||
# 注册途中索引恒与 StructCount 同步(每次注册后 _struct_idx_add 同步)。
|
||||
# 走 find_struct_by_type(其 Ty 索引判定已整体停用、恢复原线性扫描),
|
||||
# 去重语义 = 原去重扫描"首个 Ty 指针相等条目"(首注册优先),逐字节一致。
|
||||
existing: GenTypes.StructEntry | t.CPtr = self.find_struct_by_type(struct_ty)
|
||||
if existing is not None:
|
||||
return existing
|
||||
@@ -786,27 +784,18 @@ class StructGenMixin:
|
||||
VLogger.error(f"StructCount 异常: {self.StructCount} (find_struct)", "STRUCT")
|
||||
stdlib.exit(1)
|
||||
# 名索引快速路径:命中即首注册下标(语义 = 原精确匹配扫描首个命中)。
|
||||
# 未命中/索引不可信 → 回退下方原精确匹配扫描(保证正确性);
|
||||
# 精确匹配确无时继续走模块限定名/裸名回退逻辑(不提前 return None)。
|
||||
# 未命中一律回退下方原精确匹配扫描(索引只加速命中,未命中行为与未加索引
|
||||
# 前逐字节一致);精确匹配确无时继续走模块限定名/裸名回退逻辑(不提前
|
||||
# return None)。此前"索引可信且查询名非空 ⇒ 未命中即确无、跳过 O(n) 扫描"
|
||||
# 的语义已回退(一旦判据有未覆盖情形就会把"找得到"变成"找不到")。
|
||||
fi: int = self._struct_name_idx_find(name)
|
||||
if fi >= 0:
|
||||
return self._get_struct_entry(fi)
|
||||
# 名索引可信(Built==Count | 索引存在 | 查询名非空)时,索引未命中即
|
||||
# "确无同名条目":条目在注册时即命名并同步入索引,之后名至多被中和为 ""
|
||||
# (ClassGenDef/ClassGenEnum 置 Name[0]=0),故任何非空查询名的命中项必在
|
||||
# 索引探测链上 ⇒ 可跳过下方 O(n) 精确扫描(热点:本扫描占 find_struct 的
|
||||
# 全部 strcmp)。索引不可信 / 无索引 / 空名查询时仍回退原线性扫描
|
||||
# (正确性保证,非兜底)。
|
||||
name_idx_trusted: int = 0
|
||||
if self.StructNameIdx is not None and self.StructIdxCap > 0 \
|
||||
and self.StructIdxBuilt == self.StructCount:
|
||||
name_idx_trusted = 1
|
||||
if name_idx_trusted == 0 or name[0] == '\0':
|
||||
for i in range(self.StructCount):
|
||||
entry: GenTypes.StructEntry | t.CPtr = self._get_struct_entry(i)
|
||||
if entry is not None and entry.Name is not None:
|
||||
if string.strcmp(entry.Name, name) == 0:
|
||||
return entry
|
||||
for i in range(self.StructCount):
|
||||
entry: GenTypes.StructEntry | t.CPtr = self._get_struct_entry(i)
|
||||
if entry is not None and entry.Name is not None:
|
||||
if string.strcmp(entry.Name, name) == 0:
|
||||
return entry
|
||||
# 模块限定名(module.Class):模块可解析时按模块归属精确匹配,
|
||||
# 防止裸名回退抓跨模块同名类。模块未命中时若裸名类唯一(无冲突)
|
||||
# 仍回退,避免误杀未登记模块的既有解析。
|
||||
@@ -969,12 +958,11 @@ class StructGenMixin:
|
||||
VLogger.error(f"StructCount 异常: {self.StructCount} (find_struct_by_type)", "STRUCT")
|
||||
stdlib.exit(1)
|
||||
st_val: uint64 = uint64(struct_ty)
|
||||
# 类型索引快速路径:命中即首注册下标(语义 = 原扫描首个 Ty 指针相等条目)。
|
||||
# 未命中/索引不可信 → 回退原线性扫描(Ty 可能被深拷贝改写,索引未覆盖全部
|
||||
# Ty 值,故此处未命中不能断言"确无",必须回退)。
|
||||
ti: int = self._struct_type_idx_find(st_val)
|
||||
if ti >= 0:
|
||||
return self._get_struct_entry(ti)
|
||||
# Ty 索引整体停用判定(索引结构仍由 _struct_idx_add 维护,但不用于此处):
|
||||
# 条目 Ty 可能被深拷贝改写为新指针(ClassGenDef),索引槽按"注册时的 Ty 值"
|
||||
# 落位,改写后该条目的 live 键与槽位脱钩 ⇒ 索引命中返回的未必是"原线性扫描
|
||||
# 首个 Ty 相等条目",命中路径与原语义不等价、且无法低成本保证等价。故此处
|
||||
# 恢复原线性精确扫描(未命中/命中行为均与未加索引前逐字节一致)。
|
||||
for i in range(self.StructCount):
|
||||
entry: GenTypes.StructEntry | t.CPtr = self._get_struct_entry(i)
|
||||
if entry is not None and entry.Ty is not None:
|
||||
@@ -1026,10 +1014,9 @@ class StructGenMixin:
|
||||
# ============================================================
|
||||
|
||||
# 第一遍: is 身份比较(用 uint64 指针值比较,避免 is 编译路径解引用悬空 Ty)
|
||||
# 类型索引快速路径:find_struct_by_type 的语义 = 原扫描"首个 Ty 指针相等
|
||||
# 条目"(首注册优先);索引未命中 | 不可信时其内部回退原线性扫描
|
||||
# (正确性保证,非兜底)⇒ 选中的 entry 与原 is 身份扫描完全一致。
|
||||
# 类型唯一确定 entry:命中后查字段,字段未找到直接返回 None(不继续遍历)。
|
||||
# find_struct_by_type 的语义 = 原扫描"首个 Ty 指针相等条目"(首注册优先);
|
||||
# 其 Ty 索引判定已整体停用、恢复原线性扫描 ⇒ 选中的 entry 与原 is 身份扫描
|
||||
# 完全一致。类型唯一确定 entry:命中后查字段,字段未找到直接返回 None。
|
||||
lk_entry: GenTypes.StructEntry | t.CPtr = self.find_struct_by_type(struct_ty)
|
||||
if lk_entry is not None:
|
||||
for fi in range(lk_entry.FieldCount):
|
||||
@@ -1513,9 +1500,8 @@ class StructGenMixin:
|
||||
"""按类型指针查找类名,返回类名字符串或 None"""
|
||||
if struct_ty is None or self.StructTable is None:
|
||||
return None
|
||||
# 类型索引快速路径:find_struct_by_type 的 Ty 指针身份语义 = 原扫描
|
||||
# "首个 Ty 指针相等条目"(首注册优先),返回其 Name;索引未命中 |
|
||||
# 不可信时其内部回退原线性扫描(正确性保证,非兜底)⇒ 逐字节一致。
|
||||
# find_struct_by_type 的语义 = 原扫描"首个 Ty 指针相等条目"(首注册优先),
|
||||
# 返回其 Name;其 Ty 索引判定已整体停用、恢复原线性扫描 ⇒ 逐字节一致。
|
||||
fast_cn: GenTypes.StructEntry | t.CPtr = self.find_struct_by_type(struct_ty)
|
||||
if fast_cn is not None:
|
||||
return fast_cn.Name
|
||||
|
||||
@@ -340,16 +340,16 @@ class TypeGenCDef(TypeGenBase.TypeGenBase):
|
||||
n: sizet = len(self.CDefineNames)
|
||||
i: sizet
|
||||
# 名索引快速路径:可信(Built==n)时直接定位唯一候选下标(= 原扫描首个
|
||||
# 命中);未命中 → 确实不存在,直接返回;不可用 → 回退原全表线性扫描。
|
||||
# 命中);未命中一律保持全表范围、回退下方原全表线性扫描(索引只加速命中,
|
||||
# 未命中行为与未加索引前逐字节一致);此前"未命中即确无、直接 return"已回退。
|
||||
cd_start: sizet = 0
|
||||
cd_end: sizet = n
|
||||
if name is not None and self.CDefineNameIdxBuilt == int(n) \
|
||||
and self.CDefineNameIdx is not None and self.CDefineNameIdxCap > 0:
|
||||
cd_hit: int = self._cdef_idx_find(name)
|
||||
if cd_hit < 0:
|
||||
return 0
|
||||
cd_start = sizet(cd_hit)
|
||||
cd_end = sizet(cd_hit + 1)
|
||||
if cd_hit >= 0:
|
||||
cd_start = sizet(cd_hit)
|
||||
cd_end = sizet(cd_hit + 1)
|
||||
for i in range(cd_start, cd_end):
|
||||
nm: str = self.CDefineNames.get(i)
|
||||
if nm is not None and string.strcmp(nm, name) == 0:
|
||||
@@ -380,16 +380,16 @@ class TypeGenCDef(TypeGenBase.TypeGenBase):
|
||||
n: sizet = len(self.CDefineNames)
|
||||
i: sizet
|
||||
# 名索引快速路径:可信(Built==n)时直接定位唯一候选下标(= 原扫描首个
|
||||
# 命中);未命中 → 确实不存在,直接返回;不可用 → 回退原全表线性扫描。
|
||||
# 命中);未命中一律保持全表范围、回退下方原全表线性扫描(索引只加速命中,
|
||||
# 未命中行为与未加索引前逐字节一致);此前"未命中即确无、直接 return"已回退。
|
||||
cd_start: sizet = 0
|
||||
cd_end: sizet = n
|
||||
if name is not None and self.CDefineNameIdxBuilt == int(n) \
|
||||
and self.CDefineNameIdx is not None and self.CDefineNameIdxCap > 0:
|
||||
cd_hit: int = self._cdef_idx_find(name)
|
||||
if cd_hit < 0:
|
||||
return 0.0
|
||||
cd_start = sizet(cd_hit)
|
||||
cd_end = sizet(cd_hit + 1)
|
||||
if cd_hit >= 0:
|
||||
cd_start = sizet(cd_hit)
|
||||
cd_end = sizet(cd_hit + 1)
|
||||
for i in range(cd_start, cd_end):
|
||||
nm: str = self.CDefineNames.get(i)
|
||||
# 快速路径:strcmp 判等蕴含首字符相等,首字符不同者跳过 strcmp。
|
||||
@@ -421,16 +421,16 @@ class TypeGenCDef(TypeGenBase.TypeGenBase):
|
||||
n: sizet = len(self.CDefineNames)
|
||||
i: sizet
|
||||
# 名索引快速路径:可信(Built==n)时直接定位唯一候选下标(= 原扫描首个
|
||||
# 命中);未命中 → 确实不存在,直接返回;不可用 → 回退原全表线性扫描。
|
||||
# 命中);未命中一律保持全表范围、回退下方原全表线性扫描(索引只加速命中,
|
||||
# 未命中行为与未加索引前逐字节一致);此前"未命中即确无、直接 return"已回退。
|
||||
cd_start: sizet = 0
|
||||
cd_end: sizet = n
|
||||
if name is not None and self.CDefineNameIdxBuilt == int(n) \
|
||||
and self.CDefineNameIdx is not None and self.CDefineNameIdxCap > 0:
|
||||
cd_hit: int = self._cdef_idx_find(name)
|
||||
if cd_hit < 0:
|
||||
return None
|
||||
cd_start = sizet(cd_hit)
|
||||
cd_end = sizet(cd_hit + 1)
|
||||
if cd_hit >= 0:
|
||||
cd_start = sizet(cd_hit)
|
||||
cd_end = sizet(cd_hit + 1)
|
||||
for i in range(cd_start, cd_end):
|
||||
nm: str = self.CDefineNames.get(i)
|
||||
if nm is not None and string.strcmp(nm, name) == 0:
|
||||
|
||||
@@ -219,17 +219,17 @@ class TypeGenCType(TypeGenBase.TypeGenBase):
|
||||
return None
|
||||
n: sizet = len(self.CTypedefNames)
|
||||
i: sizet
|
||||
# 名索引快速路径:可信(Built==n)时直接定位唯一候选槽下标。CTypedef 表
|
||||
# 注册时按名去重(register_ctypedef 已存在则原地更新类型串)⇒ 名唯一,
|
||||
# 索引未命中 ⇒ 确实不存在,直接返回;索引不可用/不可信 → 回退原全表扫描。
|
||||
# 名索引快速路径:可信(Built==n)时直接定位唯一候选槽下标(CTypedef 表
|
||||
# 注册时按名去重 ⇒ 名唯一,命中即原全表扫描首个命中)。未命中一律回退下方
|
||||
# 原全表线性扫描(索引只加速命中,未命中行为与未加索引前逐字节一致);
|
||||
# 此前"可信未命中即确无、直接 return None"的语义已回退。
|
||||
if self.CTypedefNameIdxBuilt == int(n) \
|
||||
and self.CTypedefNameIdx is not None and self.CTypedefNameIdxCap > 0:
|
||||
ti: int = self._ctypedef_idx_find(name)
|
||||
if ti < 0:
|
||||
return None
|
||||
ts_hit: str = self.CTypedefTypeStrs.get(sizet(ti))
|
||||
self.CTypedefFound = 1
|
||||
return ts_hit
|
||||
if ti >= 0:
|
||||
ts_hit: str = self.CTypedefTypeStrs.get(sizet(ti))
|
||||
self.CTypedefFound = 1
|
||||
return ts_hit
|
||||
for i in range(n):
|
||||
nm: str = self.CTypedefNames.get(i)
|
||||
if nm is not None and string.strcmp(nm, name) == 0:
|
||||
|
||||
Reference in new issue
Block a user