Files
ViperC/App/lib/core/Gen/Call/CallGenBase.vp
T
TermiNexus e7f47735ed perf(callgen): find_func_in_module 以 function_get_name_len 替代 strlen 热点
第二趟后缀匹配对每个函数节点调用 strlen(cur_name)(实测 2373 万次、占全程序 strlen 73.7%)改为读取 Function.NameLen;两处替换点:CallGenBase 约 563 与 597。

并在外部函数返回类型白名单登记 function_get_name_len 返回 i64(CSizeT),避免 stub 未注入时按默认 i32 导致签名不一致。匹配语义与返回顺序不变。
2026-09-14 08:26:18 +08:00

1279 lines
65 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import lib.core.Gen.GenTypes as GenTypes
import lib.core.Gen.Generator as Generator
import lib.core.Gen.GenApi as GenApi
import lib.core.Gen.Translator as HT
import t, c
from stdint import *
import ast
import llvmlite
import memhub
import string
import stdio
import stdlib
import viperlib
import w32.fileio as fileio
import lib.core.VLogger as VLogger
import lib.Stub.StubMerger as StubMerger
import lib.core.CompilerFactory as CompilerFactory
import lib.Stub.StubMerger.StubMergerCollect as StubMergerCollect
import lib.Stub.StubMerger.StubMergerStore as StubMergerStore
import lib.Stub.StubGen.VpiReader as VpiReader
# ============================================================
# CallGenBase - CallGenMixin 拆分 1/9(最底层父类):模块符号查找 +
# 返回类型解析基础设施
# ============================================================
# ============================================================
# 常量(与 HandlesExprCall 保持一致,本地副本避免跨模块 CDefine 查找 bug)
# ============================================================
GLOBAL_FUNC_MAX: t.CDefine = 8192
GLOBAL_DEFAULTS_MAX: t.CDefine = 4096
_CEXPORT_MAX: t.CDefine = 256
ASM_MAX_OPERANDS: t.CDefine = 16
ASM_TEXT_BUF_SIZE: t.CDefine = 2048
ASM_CONST_BUF_SIZE: t.CDefine = 512
ASM_ARGS_BUF_SIZE: t.CDefine = 512
ASM_LINE_BUF_SIZE: t.CDefine = 4096
LLVMIR_MAX_OPS: t.CDefine = 16
LLVMIR_TEMPLATE_BUF: t.CDefine = 512
# 闭包名表大小(原 HandlesExprCall 模块级常量)
MAX_CLOSURE_NAMES: t.CDefine = 128
# 函数指针签名表大小(原 HandlesExprCall 模块级常量)
MAX_FUNC_PTR_SIGS: t.CDefine = 64
# isinstance 子类收集上限(防数组越界;t.CDefine 注解由 TPC 解析为 i32)
ISINSTANCE_CLASS_MAX: t.CDefine = 64
# ============================================================
# CallGenBase - 最底层父类(字段 + 模块查找 + 返回类型解析)
# ============================================================
@t.NoVTable
class CallGenBase:
"""调用分派最底层父类(字段 + 模块符号查找 + 返回类型解析基础设施)"""
# --- c.cexport 注册表(原 _g_cexport_sha1_arr/_g_cexport_func_arr/_g_cexport_count) ---
Gen: Generator.Generator | t.CPtr
CexportSha1Arr: bytes
CexportFuncArr: bytes
CexportCount: int
# --- LLVMIR 寄存器序号(原 _g_llvmir_reg_seq) ---
LLVMIRRegSeq: int
# --- temp 目录(原 _g_temp_dir/_g_temp_dir_len) ---
TempDir: str
TempDirLen: sizet
# --- 闭包名表(原 _g_closure_names/_g_closure_count) ---
ClosureNames: str
ClosureCount: int
# --- 函数指针签名表(原 _g_funcptr_names/_g_funcptr_rets/_g_funcptr_count) ---
FuncptrNames: str
FuncptrRets: uint64 | t.CPtr
FuncptrCount: int
# --- 全局函数注册表(原 _global_funcs/_global_func_count) ---
GlobalFuncs: GenTypes.FuncEntry | t.CPtr
GlobalFuncCount: int
# --- 全局函数表"名 → 槽下标"索引(开放寻址 | 线性探测;槽存 index+1,0=空槽) ---
# GlobalFuncs 在 PermPool 固定容量分配、只追加不重排、从不 reset ⇒ 存槽下标
# 稳定(远优于存会被回收的节点指针);GlobalFuncCount 单调递增,
# Built == GlobalFuncCount 才可信,不可信/未命中一律回退线性扫描。
GlobalFuncNameIdx: sizet | t.CPtr
GlobalFuncNameIdxCap: int
GlobalFuncNameIdxBuilt: int
# --- 全局默认参数表(原 _global_defaults/_global_defaults_count) ---
GlobalDefaults: GenTypes.FuncDefaultsEntry | t.CPtr
GlobalDefaultsCount: int
def InitCallGen(self) -> int:
"""初始化调用 Mixin 字段(子类构造时内联调用,规避 NoVTable 继承符号不转发)"""
if self is None:
return 1
self.CexportSha1Arr = None
self.CexportFuncArr = None
self.CexportCount = 0
self.LLVMIRRegSeq = 0
self.TempDir = None
self.TempDirLen = 0
self.ClosureNames = None
self.ClosureCount = 0
self.FuncptrNames = None
self.FuncptrRets = None
self.FuncptrCount = 0
self.GlobalFuncs = None
self.GlobalFuncCount = 0
self.GlobalFuncNameIdx = None
self.GlobalFuncNameIdxCap = 0
self.GlobalFuncNameIdxBuilt = -1
self.GlobalDefaults = None
self.GlobalDefaultsCount = 0
return 0
# ============================================================
# P4 簇 1+2 迁移方法(函数体来自 HandlesExprCall.vp:
# 模块 SHA1 查询/CExport 注册表/c.* 系列/内联汇编/LLVMIR)
# 本模块函数调用改 self.,外部模块调用保留模块前缀
# ============================================================
def _lookup_module_sha1(self, mod_name: str) -> str:
"""查找模块名对应的 SHA1,找不到返回 None
委托给 StubMerger.LookupSha1ByModName(精确匹配 + 包名回退)。
"""
return StubMergerStore.LookupSha1ByModName(mod_name)
def _lookup_module_sha1_suffix(self, mod_suffix: str) -> str:
"""后缀匹配查找模块 SHA1,如 mod_suffix="__types" 匹配 "llvmlite.__types"
用于 from .X import Y 的相对导入:impf.module 是 ".X"(带前导点),
去掉前导点后为 "X",SHA1 映射表中键是 "pkg.X",需要后缀匹配。
委托给 StubMerger.LookupSha1ByModNameSuffix(精确 + 后缀 + 反向后缀)。
"""
return StubMergerStore.LookupSha1ByModNameSuffix(mod_suffix)
def _module_package_from_sha1(self, pool: rowptr[memhub.MemBuddy],
mod_sha1: str) -> str:
"""从 SHA1 存储器的 rel_path 计算模块所属包名(目录部分,'/' → '.')"""
if pool is None or mod_sha1 is None:
return None
rel_arr_ptr: bytes = StubMergerStore.GetSha1StoreRelArrPtr()
sha1_arr_ptr: bytes = StubMergerStore.GetSha1StoreArrPtr()
count: int = StubMergerStore.GetSha1StoreCount()
if rel_arr_ptr is None or sha1_arr_ptr is None or count <= 0:
return None
for i in range(count):
sidx: sizet = sizet(i) * 17
if string.strcmp(sha1_arr_ptr + sidx, mod_sha1) != 0:
continue
ridx: sizet = sizet(i) * StubMerger.MAX_REL_PATH_LEN
rel: str = rel_arr_ptr + ridx
last_slash: str = string.strrchr(rel, 47) # 47 = ord('/')
if last_slash is None:
return None
pkg_len: sizet = sizet(uint64(last_slash) - uint64(rel))
if pkg_len == 0:
return None
pkg_buf: str = pool.alloc(pkg_len + 1)
if pkg_buf is None:
return None
for pi in range(pkg_len):
if rel[pi] == '/':
pkg_buf[pi] = '.'
else:
pkg_buf[pi] = rel[pi]
pkg_buf[pkg_len] = '\0'
return pkg_buf
return None
def _lookup_reexport_from_vpi(self, pool: rowptr[memhub.MemBuddy],
mod_sha1: str, func_name: str) -> str:
"""re-export 表未命中时,从模块 .vpi 按需解析 from-import 定位定义模块
includes 模块(如 llvmlite/__init__)的 stub/text 已存在时不会被按需
全量翻译(Phase2 3.5 仅在 stub/text 缺失时翻译),而 re-export 表只在
declare_only=0 的全量翻译中注册(HandleImportFromNames),因此表可能为空。
本函数解析该模块 .vpi 的 "from .X import ... func_name ..." 语句,
返回定义模块名(如 llvmlite/__init__ 的 .__builder → "llvmlite.__builder")。
"""
if pool is None or mod_sha1 is None or func_name is None:
return None
# star import 候选模块累积(空格分隔):from X import * 无法确定
# func_name 归属,收集全部候选由调用方逐个验证(is_cexport_func /
# _is_state_func_from_vpi 按 vpi 返回注解确认定义模块)。
star_cands: str = None
star_cands_len: sizet = 0
temp_dir: str = self.Gen.TypeGenBase.get_temp_dir()
if temp_dir is None:
return None
td_len: sizet = string.strlen(temp_dir)
vpi_path: str = StubMergerCollect._sliced_path(temp_dir, td_len, mod_sha1, "vpi")
if vpi_path is None:
return None
READ_SZ: sizet = 1048576
buf: bytes = stdlib.malloc(READ_SZ)
if buf is None:
stdlib.free(vpi_path)
return None
vf: fileio.File | t.CPtr = fileio.File(vpi_path, fileio.MODE.R)
stdlib.free(vpi_path)
if vf.closed:
stdlib.free(buf)
return None
nrd: LONG = vf.read_all(buf, READ_SZ)
vf.close()
if nrd <= 0:
stdlib.free(buf)
return None
if nrd < READ_SZ:
buf[nrd] = 0
name_len: sizet = string.strlen(func_name)
# 模块自含定义标志:vpi 中行首 "def {func_name}(" 命中且未被同名
# from-import 覆盖 → 该模块自身定义 func_name,不是 re-export 源。
has_own_def: int = 0
total: sizet = string.strlen(buf)
pos: sizet = 0
while pos < total:
ls: sizet = pos
while pos < total and buf[pos] != '\n':
pos += 1
ll: sizet = pos - ls
if pos < total:
pos += 1
# 自含定义检测(须在 from-import 行匹配之前,对所有行生效):
# __values.vp 自含 def ConstFloat 且 `from stdint import *`——
# star 候选 stdint 会把函数归属劫持为 f5522571bcce7bcb.ConstFloat
# (跨模块返回类型解析 fail-fast 根因)。模块级 def 行首无缩进
# (类内方法 def 有缩进,首字符为空格,不会误命中)。
if ll > 4 and buf[ls] == 'd' and buf[ls + 1] == 'e' \
and buf[ls + 2] == 'f' and buf[ls + 3] == ' ':
fstart_od: sizet = ls + 4
fend_od: sizet = fstart_od
while fend_od < ls + ll and buf[fend_od] != '(' and buf[fend_od] != ' ':
fend_od += 1
fl2_od: sizet = fend_od - fstart_od
if fl2_od == name_len:
fd_eq_od: int = 1
fdi_od: sizet = 0
while fdi_od < fl2_od:
if buf[fstart_od + fdi_od] != func_name[fdi_od]:
fd_eq_od = 0
break
fdi_od += 1
if fd_eq_od != 0:
has_own_def = 1
continue
# 行必须以 "from " 开头
if ll < 6:
continue
if buf[ls] != 'f' or buf[ls + 1] != 'r' or buf[ls + 2] != 'o' or buf[ls + 3] != 'm' or buf[ls + 4] != ' ':
continue
# 找 " import " 分隔符
imp_pos: sizet = ls + 5
while imp_pos + 7 <= ls + ll:
if buf[imp_pos] == ' ' and buf[imp_pos + 1] == 'i' and buf[imp_pos + 2] == 'm' and \
buf[imp_pos + 3] == 'p' and buf[imp_pos + 4] == 'o' and buf[imp_pos + 5] == 'r' and \
buf[imp_pos + 6] == 't' and buf[imp_pos + 7] == ' ':
break
imp_pos += 1
if imp_pos + 7 > ls + ll:
continue
mod_start: sizet = ls + 5
mod_end: sizet = imp_pos
# 名称列表从 "import " 之后(逗号分隔,含 "A as B" 取原名 A)
nm_start: sizet = imp_pos + 8
# star import 支持:from X import * → 任意函数都可能属 X 模块。
# 不立即返回(首个 star 行未必是 func_name 的定义模块,如
# fltk/__init__ 依次 from _types/_enums/_cfl import *),累积
# 全部候选,扫描完由调用方逐个验证(is_cexport_func /
# _is_state_func_from_vpi 按 vpi 返回注解确认定义模块)。
if nm_start < ls + ll and buf[nm_start] == '*':
star_dot: int = 0
if buf[mod_start] == '.':
star_dot = 1
star_buf: str = pool.alloc(mod_end - mod_start + 1)
if star_buf is not None:
si2: sizet = 0
while mod_start + si2 < mod_end:
star_buf[si2] = buf[mod_start + si2]
si2 += 1
star_buf[si2] = '\0'
while star_buf[0] == '.':
star_buf = star_buf + 1
if string.strlen(star_buf) > 0:
if star_dot != 0:
spkg: str = self._module_package_from_sha1(pool, mod_sha1)
if spkg is not None:
srl: sizet = string.strlen(spkg) + string.strlen(star_buf) + 2
sres: str = pool.alloc(srl)
if sres is not None:
viperlib.snprintf(sres, srl, "%s.%s", spkg, star_buf)
star_buf = sres
# 累积候选(空格分隔)
sbl: sizet = string.strlen(star_buf)
new_cands: str = pool.alloc(star_cands_len + sbl + 2)
if new_cands is not None:
if star_cands is not None and star_cands_len > 0:
string.strcpy(new_cands, star_cands)
new_cands[star_cands_len] = ' '
string.strcpy(new_cands + star_cands_len + 1, star_buf)
star_cands_len += sbl + 1
else:
string.strcpy(new_cands, star_buf)
star_cands_len = sbl
star_cands = new_cands
continue
scan: sizet = nm_start
while scan < ls + ll:
while scan < ls + ll and (buf[scan] == ' ' or buf[scan] == ','):
scan += 1
if scan >= ls + ll:
break
n_end: sizet = scan
while n_end < ls + ll and buf[n_end] != ',' and buf[n_end] != ' ':
n_end += 1
cand_start: sizet = scan
cand_len: sizet = n_end - scan
as_pos: sizet = scan
while as_pos + 4 <= n_end:
if buf[as_pos] == ' ' and buf[as_pos + 1] == 'a' and buf[as_pos + 2] == 's' and buf[as_pos + 3] == ' ':
cand_start = as_pos + 4
cand_len = n_end - cand_start
break
as_pos += 1
if cand_len == name_len:
m_ok: int = 1
for ki in range(name_len):
if buf[cand_start + ki] != func_name[ki]:
m_ok = 0
break
if m_ok == 1:
# 命中:解析模块名(前导点 → 相对导入,补包前缀)
has_dot: int = 0
if buf[mod_start] == '.':
has_dot = 1
mod_buf: str = pool.alloc(mod_end - mod_start + 1)
if mod_buf is None:
stdlib.free(buf)
return None
mi: sizet = 0
while mod_start + mi < mod_end:
mod_buf[mi] = buf[mod_start + mi]
mi += 1
mod_buf[mi] = '\0'
while mod_buf[0] == '.':
mod_buf = mod_buf + 1
if string.strlen(mod_buf) == 0:
stdlib.free(buf)
return None
if has_dot != 0:
pkg: str = self._module_package_from_sha1(pool, mod_sha1)
if pkg is None:
stdlib.free(buf)
return None
rl: sizet = string.strlen(pkg) + string.strlen(mod_buf) + 2
res: str = pool.alloc(rl)
if res is None:
stdlib.free(buf)
return None
viperlib.snprintf(res, rl, "%s.%s", pkg, mod_buf)
stdlib.free(buf)
return res
stdlib.free(buf)
return mod_buf
scan = n_end
# 全部行扫描完毕:显式 from-import 精确匹配已提前返回;仅有 star
# import 时返回累积候选(空格分隔,调用方逐个验证定义模块)。
# 但模块自含 def {func_name}(未遇同名 from-import 覆盖,否则上面
# 已提前返回 import 源)时本模块就是定义者,不是 re-export——star
# 候选(如 __values.vp `from stdint import *` 的 stdint)会劫持归属,
# 生成错误 SHA1(f5522571bcce7bcb.ConstFloat)→ 跨模块返回类型解析
# fail-fast。返回 None 保持原模块定义归属。
if star_cands is not None:
stdlib.free(buf)
if has_own_def == 0:
return star_cands
return None
stdlib.free(buf)
return None
def register_cexport_func(self, sha1: str, func_name: str) -> int:
"""注册 CExport 函数到全局表(统一写入 Gen.CallGenBase 真源,跨子对象共享)"""
if self.Gen is None or sha1 is None or func_name is None:
return 0
if self.Gen.CallGenBase.CexportSha1Arr is None:
self.Gen.CallGenBase.CexportSha1Arr = CompilerFactory.factory.PermPool.alloc(_CEXPORT_MAX * 17)
self.Gen.CallGenBase.CexportFuncArr = CompilerFactory.factory.PermPool.alloc(_CEXPORT_MAX * 64)
if self.Gen.CallGenBase.CexportSha1Arr is None or self.Gen.CallGenBase.CexportFuncArr is None:
return 0
string.memset(self.Gen.CallGenBase.CexportSha1Arr, 0, _CEXPORT_MAX * 17)
string.memset(self.Gen.CallGenBase.CexportFuncArr, 0, _CEXPORT_MAX * 64)
if self.Gen.CallGenBase.CexportCount < _CEXPORT_MAX:
idx: sizet = sizet(self.Gen.CallGenBase.CexportCount) * 17
# 槽位定长 17/64 字节,strcpy 无界会越界写相邻槽(stdlib 堆区)
string.strncpy(self.Gen.CallGenBase.CexportSha1Arr + idx, sha1, 16)
self.Gen.CallGenBase.CexportSha1Arr[idx + 16] = '\0'
idx2: sizet = sizet(self.Gen.CallGenBase.CexportCount) * 64
string.strncpy(self.Gen.CallGenBase.CexportFuncArr + idx2, func_name, 63)
self.Gen.CallGenBase.CexportFuncArr[idx2 + 63] = '\0'
self.Gen.CallGenBase.CexportCount += 1
return 1
return 0
def is_cexport_func(self, sha1: str, func_name: str) -> int:
"""检查 (sha1, func_name) 是否是 CExport 函数(统一读 Gen.CallGenBase 真源)
注册表未命中时回退查 vpi 返回注解:跨模块编译存在时序差(如 apic.vp
先于 mm.vp 翻译时 mm 的 CExport 尚未 register_cexport_func → mm.malloc
误用 @1fdad1c76fb116bc.malloc 而非裸名 @malloc → ld.lld undefined
symbol: 1fdad1c76fb116bc.malloc)。
"""
if self.Gen is None or sha1 is None or func_name is None:
return 0
if self.Gen.CallGenBase.CexportSha1Arr is not None:
for i in range(self.Gen.CallGenBase.CexportCount):
idx: sizet = sizet(i) * 17
if string.strcmp(self.Gen.CallGenBase.CexportSha1Arr + idx, sha1) == 0:
idx2: sizet = sizet(i) * 64
if string.strcmp(self.Gen.CallGenBase.CexportFuncArr + idx2, func_name) == 0:
return 1
return self._is_cexport_func_from_vpi(sha1, func_name)
def _is_cexport_func_from_vpi(self, sha1: str, func_name: str) -> int:
"""vpi 返回注解含 t.CExport → 定义端用裸名(跨模块调用无 sha1 头)
对齐 _is_state_func_from_vpi(State 标记)的 vpi 按需解析路径。
"""
if sha1 is None or func_name is None:
return 0
if self.Gen is None or self.Gen.CallGenBase.TempDir is None \
or self.Gen.CallGenBase.TempDirLen <= 0:
return 0
VpiReader._ensure_vpi_ret_annot_loaded(sha1, self.Gen.CallGenBase.TempDir)
cexp_annot: str = VpiReader._lookup_vpi_ret_annot(sha1, None, func_name)
if cexp_annot is None:
return 0
if string.strstr(cexp_annot, "CExport") is not None:
return 1
return 0
def _is_state_func_from_vpi(self, pool: rowptr[memhub.MemBuddy],
sha1: str, func_name: str) -> int:
"""vpi 返回注解含 t.State → 外部 C 函数(定义端用裸名,跨模块调用无 sha1 头)
t.State(同 t.CExtern/t.CExport)是外部函数标记:定义端生成裸名
@PyList_Size(链接外部库 libpython),跨模块调用若生成 @{sha1}.PyList_Size
→ ld.lld undefined symbol(CPythonTest PyList_Size 根因)。CExport 走
is_cexport_func 注册表,State 函数未注册 → 按需解析 vpi 返回注解判断。
"""
if pool is None or sha1 is None or func_name is None:
return 0
if self.Gen.CallGenBase.TempDir is not None and self.Gen.CallGenBase.TempDirLen > 0:
VpiReader._ensure_vpi_ret_annot_loaded(sha1, self.Gen.CallGenBase.TempDir)
state_annot: str = VpiReader._lookup_vpi_ret_annot(sha1, None, func_name)
if state_annot is None:
return 0
# 返回注解含 "State"(如 "sizet | t.State")→ 外部 C 函数
if string.strstr(state_annot, "State") is not None:
return 1
return 0
def _lookup_mod_sha1(self, pool: rowptr[memhub.MemBuddy],
from_imports: str, class_name: str,
allow_star_fallback: int = 1) -> str:
"""从 from_imports 查找类所属模块的 SHA1,返回 None=未找到
allow_star_fallback: 传 0 禁用 star import 回退(用于明确模块名/函数名查找)
"""
mod_name_raw: str = self.Gen.ImportsGen.lookup_from_import(
from_imports, class_name, allow_star_fallback)
if mod_name_raw is None:
return None
return self._lookup_sha1_from_modname_raw(pool, mod_name_raw)
def _lookup_sha1_from_modname_raw(self, pool: rowptr[memhub.MemBuddy],
mod_name_raw: str) -> str:
"""从原始模块名查找 SHA1,返回 None=未找到"""
# 手动找到模块名结束位置(空格、null、或 ':' 别名分隔符),复制到新缓冲区
mod_len: sizet = 0
while mod_name_raw[mod_len] != ' ' and mod_name_raw[mod_len] != '\0' and mod_name_raw[mod_len] != ':':
mod_len += 1
mod_name_buf: str = pool.alloc(mod_len + 1)
if mod_name_buf is None:
return None
for mi in range(mod_len):
mod_name_buf[mi] = mod_name_raw[mi]
mod_name_buf[mod_len] = '\0'
# 去掉相对导入的前导点
base_mod: str = mod_name_buf
while base_mod[0] == '.':
base_mod = base_mod + 1
sha1: str = self._lookup_module_sha1(base_mod)
if sha1 is None:
sha1 = self._lookup_module_sha1_suffix(base_mod)
return sha1
def find_func_in_module(self, mod: llvmlite.LLVMModule | t.CPtr,
name: str,
owner_sha1: str = None) -> llvmlite.Function | t.CPtr:
"""在模块的函数链表中按名称查找函数
查找策略:
1. 精确匹配(如 "strlen" 匹配 @strlen,有 t.CExport 的函数)
2. 后缀匹配(如 "atoi" 匹配 @"83210a72e829a086.atoi",无 t.CExport 的函数)
owner_sha1: 当前模块 SHA1。非 None 时在本模块 define 判定之前先做
"本模块同名函数"优先扫描(mangled 名以 "{owner_sha1}." 开头且恰为
"{owner_sha1}.{name}")。裸名调用应优先解析为当前模块自身函数——
其前向 declare 在函数体翻译时可能尚未激活(IsDeclared 仍为 1),
pass2 的 IsDeclared==0 判定会跳过它,pass3 又会按链表顺序误绑排在
FuncHead 前面的跨模块 stub declare(lexer.vp _advance 根因)。
使用 llvmlite.function_get_name/next 访问器绕过 stub 类型限制
(stub 类型字段不足且字段类型可能错误,直接字段访问会被静默跳过)。
"""
if mod is None or name is None:
return None
name_len: sizet = string.strlen(name)
# 热点优化:原实现把「精确匹配」+「本模块同名 mangled」+「后缀匹配
# define 优先」+「后缀匹配 declare 兜底」拆成 4 趟完整链表扫描,每趟
# 对每个节点调用一次 function_get_name/function_get_next(实测二者各
# ~1.046 亿次的热点来源)。合并为 2 趟,链表顺序与优先级语义不变:
# 趟 1:精确匹配(命中即返回,优先级最高,与原第一遍一致)+ 收集
# 首个本模块同名 mangled 候选(原"第一遍半",仅在无精确匹配
# 时使用);
# 趟 2:收集首个 IsDeclared==0 后缀命中(原第二遍优先)与首个任意
# 后缀命中(原第三遍兜底),扫描结束后按原优先级返回。
cur: llvmlite.Function | t.CPtr = mod.FuncHead
mangle_hit: llvmlite.Function | t.CPtr = 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}" 的形态
# (mangled 模块级函数名含且只含一个 '.'),不匹配
# "{sha1}.Class.method" 等方法名。是否 IsDeclared==0 均可返回:
# 前向 declare 与 define 同名同符号。只在 owner_sha1 提供且尚未
# 命中时计算,避免多做一次名字长度读取。
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,需要通过后缀匹配找到
# 调用处(translate_call 等)负责判断 IsDeclared 并决定使用裸名还是 sha1 前缀名
#
# 修复:本模块 define 优先于跨模块 stub declare。
# 根因:跨模块同名函数(如 lexer.vp 的 _advance 与 parser.vp 的 _advance)
# 的 stub declare 在 stub 注入时排在 FuncHead 前面,裸名调用 _advance 后缀
# 匹配按链表顺序先命中跨模块 declare → 参数按 Lexer* coerce → Parser* 被
# bitcast 成 Lexer* → lexer 版 _advance 按 Lexer 布局操作 Parser 指针,
# cur 永不推进 → parse 无限循环。本模块同名 define 必须优先。
# 无同名 define 时回退到 stub declare(跨模块方法/函数调用)。
declare_hit: llvmlite.Function | t.CPtr = None
any_hit: llvmlite.Function | t.CPtr = 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) 恒等,不改匹配语义。
cur_len: sizet = llvmlite.function_get_name_len(cur)
if cur_len > name_len + 1:
suffix_start: sizet = cur_len - name_len
if cur_name2[suffix_start - 1] == '.':
match: int = 1
for i in range(name_len):
if cur_name2[suffix_start + i] != name[i]:
match = 0
break
if match == 1:
# 首个任意后缀命中(原第三遍兜底)
if any_hit is None:
any_hit = cur
# 首个 IsDeclared==0 命中(原第二遍优先)
if cur.IsDeclared == 0 and declare_hit is None:
declare_hit = cur
cur = llvmlite.function_get_next(cur)
if declare_hit is not None:
return declare_hit
return any_hit
# ============================================================
# _ensure_method_declare - 为跨模块方法调用创建 declare 声明
#
# 当 found_func 为 None(stub 未注入或跨模块调用)时,
# 在当前模块中创建函数声明,避免 llc 报 undefined value。
# ============================================================
def _ensure_method_declare(self, pool: rowptr[memhub.MemBuddy],
mod: llvmlite.LLVMModule | t.CPtr,
call_name: str,
ret_ty: llvmlite.LLVMType | t.CPtr,
param_head: llvmlite.ParamNode | t.CPtr,
param_count: int):
"""为跨模块方法调用创建 declare 声明
若模块中已存在同名 declare 且参数数量较少(例如 File.__init__ 的
stub 仅 3 个参数,而实际调用传入 4 个),则向现有 declare 追加缺失
参数,避免 llc 报 `argument invalid for parameter type` 之类的错误。
已存在的 define 不修改(其参数由定义决定)。
"""
if pool is None or mod is None or call_name is None:
return
# 链遍历哨兵(fail-fast):FuncHead/Params 的 Next 被越界写破坏成环时
# 无上限遍历会死循环(builtins.vp dump 卡死根因排查),超限即报错终止。
DECLARE_CHAIN_LIMIT: int = 100000
# 检查模块中是否已有同名函数(declare 或 define)
existing: llvmlite.Function | t.CPtr = mod.FuncHead
existing_steps: int = 0
while existing is not None:
existing_steps += 1
if existing_steps > DECLARE_CHAIN_LIMIT:
stdio.printf("[CALLGEN] 链表异常: module funcs(call_name=%s,遍历超限,终止)\n", call_name)
stdlib.exit(1)
if existing.Name is not None:
# 快速路径:strcmp 判等蕴含首字符相等,首字符不同者跳过 strcmp
# (热点:本函数对模块函数链逐节点命名比对)。
if existing.Name[0] == call_name[0] \
and string.strcmp(existing.Name, call_name) == 0:
# 已存在:仅当是 declare 且参数数量不足时追加缺失参数
if existing.IsDeclared == 1:
# 变长函数(*args,IsVarArg=1)不追加参数:多余实参由
# 变参机制(...)承接,追加有类型参数会导致调用侧把变参
# 强转为追加的类型(如 i8 → i64),%d/%x 等格式读错。
if existing.IsVarArg == 1:
return
# 遍历现有参数链表计数(避免依赖 GSList.Count 字段的类型推断)
cur_count: int = 0
cur_p: llvmlite.Param | t.CPtr = llvmlite.function_get_param_head(existing)
while cur_p is not None:
cur_count += 1
if cur_count > DECLARE_CHAIN_LIMIT:
stdio.printf("[CALLGEN] 链表异常: func params(call_name=%s,参数链遍历超限,终止)\n", call_name)
stdlib.exit(1)
cur_p = cur_p.Next
# 只追加缺失的参数(cur_count < param_count 时)
if cur_count < param_count:
# 定位到 param_head 的第 cur_count 个节点开始追加
pnode2: llvmlite.ParamNode | t.CPtr = param_head
pi2: int = 0
# 跳过已有的 cur_count 个参数
while pnode2 is not None and pi2 < cur_count:
pnode2 = pnode2.Next
pi2 += 1
# 追加剩余参数
while pnode2 is not None and pi2 < param_count:
if pnode2.Ty is not None:
param2: llvmlite.Param | t.CPtr = llvmlite.new_param(pool, pnode2.Ty, None)
if param2 is not None:
llvmlite.function_add_param(existing, param2)
pnode2 = pnode2.Next
pi2 += 1
return
existing = existing.Next
# 裸名 C 库函数(如 string.memset → @memset、stdlib.memcpy → @memcpy):
# call_name 无 sha1 前缀 → 无法提取定义侧模块查 vpi 参数注解 →
# 此前用实参类型建 declare(字面量 0=i32 把定义 sizet(i64) 固化窄,
# VKernel process.vp string.memset 声明 @memset(i8*,i32,i32) vs 定义
# @memset(i8*,i32,i64),跨模块 ABI 宽度不一致,优化路径读高位垃圾
# 写穿内存)。已知 C 库函数直接按标准签名建 declare(与 CallGenLib
# _ensure_c_lib_declare 白名单一致:memset/memcpy/memmove/memcmp/
# strcmp/strncmp/strcpy/strncpy/strcat/strlen/puts/atoi/atol/abs/labs)。
if call_name is not None and string.strchr(call_name, 46) is None:
std_lib_fn: llvmlite.Function | t.CPtr = self.Gen.CallGenLib._ensure_c_lib_declare(
pool, mod, call_name)
if std_lib_fn is not None:
return
# 创建函数声明
func: llvmlite.Function | t.CPtr = llvmlite.new_function(pool, call_name, ret_ty)
if func is None:
return
func.IsDeclared = 1
# 定义侧参数类型优先(根治实参字面量固化错误宽度):
# declare 此前用调用点实参类型(param_head)建参数,字面量 0=i32 会把
# 定义 sizet(i64) 的参数固化窄(SlotsBugTest _translate_struct_ctor
# can=i32 vs 定义 i64 → 调用侧 trunc i64→i32、callee 按 i64 读栈残留
# 垃圾高位 → can=巨值读未写槽位崩溃根因)。从 call_name 提取
# {sha1}.{cls}.{method} 查 vpi 参数注解缓存取定义侧类型:仅当实参与
# 定义都是整数且定义更宽时采用定义宽度(指针/联合保持实参类型,避免
# 字符串路径把 list*/Struct* 降级为 i8*)。
# 注:反方向(定义侧更窄,如 su8250.putchar 定义 (i16,i8) 而字面量
# 1016=i32)由调用点 CallGenLib._coerce_args_to_def_params 先把实参
# 窄化到定义宽度,本处依 param_head 建 declare 即已对齐(此块做补强,
# 对已窄化的实参不再触发)。
sha1_d: str = None
cls_d: str = None
meth_d: str = None
if call_name is not None:
# [C10 收敛] SHA1 前缀判定走唯一真源 `GenApi.sha1_prefix_len`
# (旧实现自带一份 hex+'.' 校验,判据 `>= 17` 与真源一致);
# 前缀长度取返回值,不再硬编码 17/16。
sha1_pfx_d: int = GenApi.sha1_prefix_len(call_name)
if sha1_pfx_d != 0:
pfx_len_d: sizet = sizet(sha1_pfx_d)
sha1_buf_d: bytes = stdlib.malloc(pfx_len_d)
if sha1_buf_d is not None:
string.strncpy(sha1_buf_d, call_name, pfx_len_d - 1)
sha1_buf_d[pfx_len_d - 1] = '\0'
sha1_d = sha1_buf_d
rest_d: str = call_name + pfx_len_d
dot2_d: str = string.strchr(rest_d, '.')
if dot2_d is not None:
cls_len_d: sizet = uint64(dot2_d) - uint64(rest_d)
if cls_len_d > 0:
cls_buf_d: bytes = stdlib.malloc(cls_len_d + 1)
if cls_buf_d is not None:
string.strncpy(cls_buf_d, rest_d, cls_len_d)
cls_buf_d[cls_len_d] = '\0'
cls_d = cls_buf_d
meth_d = dot2_d + 1
else:
meth_d = rest_d
prm_annots_d: str = None
if sha1_d is not None and meth_d is not None:
prm_annots_d = self._vpi_method_param_annot(pool, sha1_d, cls_d, meth_d)
i32_ty_d: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
i8_ptr_d: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, llvmlite.Int8(pool))
# 添加参数(declare 中参数名无意义,设为 None 只输出类型)
pnode: llvmlite.ParamNode | t.CPtr = param_head
pi: int = 0
while pnode is not None and pi < param_count:
param_ty_d: llvmlite.LLVMType | t.CPtr = pnode.Ty
if prm_annots_d is not None and prm_annots_d[0] != '\0' and pnode.Ty is not None:
seg_ty_d: llvmlite.LLVMType | t.CPtr = VpiReader._resolve_vpi_param_annot_seg(
pool, sha1_d, cls_d, meth_d, pi, i32_ty_d, i8_ptr_d)
if seg_ty_d is not None:
match pnode.Ty:
case llvmlite.LLVMType.Int(bits_a):
match seg_ty_d:
case llvmlite.LLVMType.Int(bits_b):
if bits_b > bits_a:
param_ty_d = seg_ty_d
case _:
pass
case _:
pass
if param_ty_d is not None:
param: llvmlite.Param | t.CPtr = llvmlite.new_param(pool, param_ty_d, None)
if param is not None:
llvmlite.function_add_param(func, param)
pnode = pnode.Next
pi += 1
if sha1_d is not None:
stdlib.free(sha1_d)
if cls_d is not None:
stdlib.free(cls_d)
llvmlite.module_add_function(mod, func)
# ============================================================
# _lookup_method_ret_ty_via_vpi - 从 vpi stub 返回注解缓存精确获取方法返回类型
#
# 方法定义处(.vpi stub)的返回注解(如 json.JsonValue._pool_alloc →
# "str")在 stub 注入时由 VpiReader 缓存(_cache_vpi_return_annots_text)。
# 此函数按 模块SHA1 + 类名 + 方法名 查缓存并纯字符串解析为 LLVMType。
# 无需硬编码白名单——返回类型始终来自方法定义处。
# ============================================================
# ============================================================
def _lookup_method_ret_ty_via_vpi(self, pool: rowptr[memhub.MemBuddy],
cls_sha1: str,
cls_name: str,
method_name: str) -> llvmlite.LLVMType | t.CPtr:
"""从 vpi stub 返回注解缓存获取方法返回类型,未命中返回 None"""
if pool is None or cls_sha1 is None or method_name is None:
return None
# 函数内懒 import:VpiReader imports HandlesExprCall,顶层 import 会成环
i32_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, llvmlite.Int8(pool))
# 按需加载:缓存未命中时直接读 {temp_dir}/{sha1[0:2]}/{sha1}.vpi
# 覆盖"模块自身翻译时 stub 尚未注入"(同模块方法调用,定义在后)的场景
if self.Gen.CallGenBase.TempDir is not None and self.Gen.CallGenBase.TempDirLen > 0:
VpiReader._ensure_vpi_ret_annot_loaded(cls_sha1, self.Gen.CallGenBase.TempDir)
annot: str = VpiReader._lookup_vpi_ret_annot(cls_sha1, cls_name, method_name)
if annot is None:
return None
return VpiReader._resolve_vpi_ret_annot_str(pool, annot, i32_ty, i8_ptr_ty)
# ============================================================
# _vpi_method_param_annot - 从 vpi 参数注解缓存获取方法参数注解串
#
# 跨模块方法调用在 stub 未注入(依赖收集不加载被调模块 vpi,如
# self.Gen.ExprGenCdef.method 经 Generator 属性访问)时,函数表无
# ParamCount/默认参数信息 → 调用点缺参 → 被调函数读栈残留。此函数
# 按需加载并查询 vpi 参数注解缓存('@' 分隔,含首参 self 空串占位),
# 调用方据此补全 declare/call 缺失参数(默认参数零值兜底)。
# 未命中返回 None。
# ============================================================
def _vpi_method_param_annot(self, pool: rowptr[memhub.MemBuddy],
cls_sha1: str,
cls_name: str,
method_name: str) -> str:
"""按需加载并查询 vpi 参数注解串,未命中返回 None"""
if pool is None or cls_sha1 is None or cls_name is None or method_name is None:
return None
# 按需加载:缓存未命中时直接读 {temp_dir}/{sha1[0:2]}/{sha1}.vpi
if self.Gen.CallGenBase.TempDir is not None and self.Gen.CallGenBase.TempDirLen > 0:
VpiReader._ensure_vpi_param_annot_loaded(cls_sha1, self.Gen.CallGenBase.TempDir)
return VpiReader._lookup_vpi_param_annots(cls_sha1, cls_name, method_name)
# ============================================================
# _vpi_resolve_param_annot_ty - 解析 vpi 参数注解段为 LLVMType
#
# 与 _lookup_method_ret_ty_via_vpi 的返回注解解析同源;解析失败返回
# None,调用方以 i8* 兜底(ABI 兼容,参数槽位宽度一致)。
# ============================================================
def _vpi_resolve_param_annot_ty(self, pool: rowptr[memhub.MemBuddy],
seg: str) -> llvmlite.LLVMType | t.CPtr:
"""解析单个 vpi 参数注解段为 LLVMType,失败返回 None"""
if pool is None or seg is None:
return None
i32_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, llvmlite.Int8(pool))
return VpiReader._resolve_vpi_ret_annot_str(pool, seg, i32_ty, i8_ptr_ty)
# ============================================================
# _special_method_ret_ty - 编译器内置特殊方法的固定返回类型
#
# __before_init__ / __init__:编译器生成/约定返回 void
# __new__:返回 Ptr(StructTy)(存储空间指针)
#
# 这些方法不存在于源码(vpi stub 无条目),跨模块调用时
# found_func/text.ll/vpi 缓存必然未命中,但返回类型是编译器
# 确定性语义(与 HandlesClassDef._translate_method 3543-3548 一致),
# 在此直接固定,不触发 fail-fast 误报。非特殊方法返回 None。
# ============================================================
# ============================================================
def _special_method_ret_ty(self, pool: rowptr[memhub.MemBuddy],
class_name: str,
method_name: str) -> llvmlite.LLVMType | t.CPtr:
"""编译器内置特殊方法的固定返回类型;非特殊返回 None"""
if pool is None or method_name is None:
return None
if string.strcmp(method_name, "__before_init__") == 0 or \
string.strcmp(method_name, "__init__") == 0:
return llvmlite.Void(pool)
if string.strcmp(method_name, "__new__") == 0:
if class_name is not None:
sp_struct_ty: llvmlite.LLVMType | t.CPtr = self.Gen.StructGen.get_struct_type(class_name)
if sp_struct_ty is not None:
return llvmlite.Ptr(pool, sp_struct_ty)
return None
return None
# ============================================================
# _fail_method_ret_ty - 方法返回类型无法精确获取时 fail-fast
#
# 返回类型来源(按序):当前模块函数 → text.ll define → vpi stub 返回注解缓存。
# 全部未命中时立即 ERROR + 错误堆栈终止,绝不按名推断白名单/静默默认 i32。
#
# node: 调用点 AST 节点(可选,用于 ERROR 堆栈定位)
# ============================================================
# ============================================================
def _fail_method_ret_ty(self, method_name: str,
node: ast.AST | t.CPtr = None) -> llvmlite.LLVMType | t.CPtr:
"""方法返回类型无法精确获取时 fail-fast(ERROR + 错误堆栈)
返回类型来源(按序):当前模块函数 → text.ll define → vpi stub 返回注解缓存。
全部未命中说明方法定义缺失或 stub 损坏,立即终止暴露,绝不静默默认 i32。
"""
VLogger.error(
f"方法返回类型无法精确获取(函数/text.ll/vpi stub 均未命中): {method_name}",
"HandlesExprCall")
self.Gen.TypeGenErr.fatal_error(
node,
f"方法返回类型无法精确获取,默认 i32 会截断指针/i64: {method_name}",
"TypeError")
return None
# ============================================================
# SetTempDir - 设置 temp 目录路径
#
# 在 Phase2.py 的 Phase A 翻译前调用,使 CallGenMixin
# 能够读取依赖模块的 text.ll 文件。
# ============================================================
# ============================================================
def SetTempDir(self, temp_dir: str) -> None:
"""设置 temp 目录路径(统一写入 Gen.CallGenBase 真源,跨子对象共享)"""
if self.Gen is None:
return
self.Gen.CallGenBase.TempDir = temp_dir
if temp_dir is not None:
self.Gen.CallGenBase.TempDirLen = string.strlen(temp_dir)
else:
self.Gen.CallGenBase.TempDirLen = 0
# ============================================================
# _ParseLLVMRetTyStr - 将 LLVM IR 返回类型字符串转换为 llvmlite.LLVMType
#
# 从 define 行中提取的返回类型字符串(define 与 @ 之间的内容),
# 转换为 llvmlite.LLVMType 对象。
#
# 支持的类型:
# void → Void
# i8/i16/i32/i64 → Int8/Int16/Int32/Int64
# double → Double, float → Float
# 任何含 * 的类型 → Ptr(Int8)(通用指针,保留 64 位地址)
#
# Args:
# pool: 内存池
# buf: 包含类型字符串的缓冲区
# start: 类型字符串起始位置
# end: 类型字符串结束位置(不含)
#
# Returns:
# LLVM 类型对象,或 None(无法解析)
# ============================================================
# ============================================================
def _ParseLLVMRetTyStr(self, pool: rowptr[memhub.MemBuddy],
buf: bytes,
start: sizet,
end: sizet) -> llvmlite.LLVMType | t.CPtr:
"""将 LLVM IR 返回类型字符串转换为 llvmlite.LLVMType"""
if pool is None or buf is None or start >= end:
return None
# 去除前导空白
s: sizet = start
while s < end:
c: int = buf[s]
if c != ' ' and c != '\t':
break
s += 1
# 去除尾部空白
e: sizet = end
while e > s:
c2: int = buf[e - 1]
if c2 != ' ' and c2 != '\t':
break
e -= 1
ty_len: sizet = e - s
if ty_len == 0:
return None
# struct 字面量 {T1, T2, ...}(tuple 打包返回类型,如 b2a_hqx ->
# tuple[str, t.CUnsignedShort] 编译为 {i8*, i16})。必须在 * 检查
# 之前——否则 "{i8*, i16}" 内含 * 会被误判为 i8*(b2a_hqx 调用侧
# 返回类型退化 i8* → HandlesAssign tuple 解包静默跳过 → free 未初始化
# 指针崩溃,HashLibTest hqx 段)。
if s < e and buf[s] == '{':
th_st: llvmlite.ParamNode | t.CPtr = None
tt_st: llvmlite.ParamNode | t.CPtr = None
tc_st: int = 0
ps_st: sizet = s + 1
while ps_st < e and buf[ps_st] != '}':
while ps_st < e and (buf[ps_st] == ' ' or buf[ps_st] == ','):
ps_st += 1
if ps_st >= e or buf[ps_st] == '}':
break
es_st: sizet = ps_st
while ps_st < e and buf[ps_st] != ',' and buf[ps_st] != '}':
ps_st += 1
ee_st: sizet = ps_st
if ee_st > es_st:
et_st: llvmlite.LLVMType | t.CPtr = self._ParseLLVMRetTyStr(
pool, buf, es_st, ee_st)
if et_st is None:
return None
en_st: llvmlite.ParamNode | t.CPtr = llvmlite.new_param_node(
pool, et_st)
if en_st is None:
return None
if th_st is None:
th_st = en_st
else:
llvmlite.paramnode_set_next(tt_st, en_st)
tt_st = en_st
tc_st += 1
if th_st is None or tc_st == 0:
return None
st_ty_st: llvmlite.LLVMType | t.CPtr = llvmlite.Struct(pool, th_st, tc_st, None)
# 指针到 struct({i32, i8}*):返回 Ptr(Struct);纯 tuple 打包
# ({i8*, i16})无尾部 *,返回 Struct 本身。
if ps_st < e and buf[ps_st] == '*':
return llvmlite.Ptr(pool, st_ty_st)
return st_ty_st
# 检查是否含 * (指针类型) → 返回 i8* (通用指针)
# 包括 i8*, i32*, %"name"*, { i32, i8 }* 等
ci: sizet = s
while ci < e:
if buf[ci] == '*':
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
ci += 1
# void
if ty_len == 4 and string.strncmp(buf + s, "void", 4) == 0:
return llvmlite.Void(pool)
# double
if ty_len == 6 and string.strncmp(buf + s, "double", 6) == 0:
return llvmlite.Double(pool)
# float
if ty_len == 5 and string.strncmp(buf + s, "float", 5) == 0:
return llvmlite.Float(pool)
# iN (i8, i16, i32, i64)
if ty_len >= 2 and buf[s] == 'i':
bits: int = 0
di: sizet = s + 1
valid: int = 1
while di < e:
d: int = buf[di]
if d >= '0' and d <= '9':
bits = bits * 10 + (d - '0')
else:
valid = 0
break
di += 1
if valid != 0 and bits > 0:
if bits == 8:
return llvmlite.Int8(pool)
if bits == 16:
return llvmlite.Int16(pool)
if bits == 32:
return llvmlite.Int32(pool)
if bits == 64:
return llvmlite.Int64(pool)
# 无法解析
return None
# ============================================================
# _LookupMethodRetTyFromTextLL - 从依赖模块的 text.ll 查找方法返回类型
#
# 读取 {temp_dir}/{sha1[0:2]}/{sha1}.text.ll (level=1),搜索
# define 行中包含 "{sha1}.{class_name}.{method_name}" 的函数,
# 提取返回类型并转换为 llvmlite.LLVMType。
#
# 解决问题: 旧实现 _infer_method_ret_ty(已移除)默认返回 i32,导致指针返回值
# 被 inttoptr 截断为 32 位(coerce_to_type line 238-239)。
# 本函数从 text.ll 获取实际返回类型,避免截断。
#
# Args:
# pool: 内存池
# class_sha1: 类所属模块的 SHA1
# class_name: 类名
# method_name: 方法名
#
# Returns:
# LLVM 类型对象,或 None(未找到/text.ll 不存在)
# ============================================================
# ============================================================
def _LookupMethodRetTyFromTextLL(self, pool: rowptr[memhub.MemBuddy],
class_sha1: str,
class_name: str,
method_name: str,
use_bare: int = 0) -> llvmlite.LLVMType | t.CPtr:
"""从依赖模块的 text.ll 中查找方法返回类型
use_bare: 1=按模块级函数查找(搜索 "{sha1}.{method_name}" define,
用于 new_lexer/tokenize 等模块函数;class_name 忽略)。
"""
if pool is None or class_sha1 is None or method_name is None:
return None
if self.Gen.CallGenBase.TempDir is None or self.Gen.CallGenBase.TempDirLen == 0:
return None
sha1_len: sizet = string.strlen(class_sha1)
if sha1_len < 2:
return None
# 构造 text.ll 路径 (level=1): {temp_dir}/{sha1[0:2]}/{sha1}.text.ll
# 使用 subdir 缓冲区避免 %c 格式化问题(与 StubMerger._sliced_path 一致)
path_len: sizet = self.Gen.CallGenBase.TempDirLen + sha1_len + 14
path_buf: bytes = stdlib.malloc(path_len)
if path_buf is None:
return None
subdir_buf: bytes = stdlib.malloc(3)
if subdir_buf is None:
stdlib.free(path_buf)
return None
subdir_buf[0] = class_sha1[0]
subdir_buf[1] = class_sha1[1]
subdir_buf[2] = '\0'
viperlib.snprintf(path_buf, path_len, "%s/%s/%s.text.ll",
self.Gen.CallGenBase.TempDir, subdir_buf, class_sha1)
stdlib.free(subdir_buf)
# 读取 text.ll(按文件实际大小分配:固定 _TEXT_LL_READ_BUF_SIZE(256KB)
# 会截断大模块的 text.ll,末尾方法定义查不到 → 误报"方法返回类型无法
# 精确获取"(追加80b 根因,同追加74 _read_source_line 截断)。64MB 上限
# 防御异常超大文件整体读取。)
tf: fileio.File | t.CPtr = fileio.File(path_buf, fileio.MODE.R)
if tf.closed:
stdlib.free(path_buf)
return None
tfsize: LONGLONG = tf.size()
if tfsize <= 0 or tfsize > 67108864:
tf.close()
stdlib.free(path_buf)
return None
read_size: sizet = sizet(tfsize)
read_buf: bytes = stdlib.malloc(read_size + 1)
if read_buf is None:
tf.close()
stdlib.free(path_buf)
return None
text_len: int64 = tf.read_all(read_buf, ULONG(read_size + 1))
tf.close()
stdlib.free(path_buf)
if text_len <= 0:
stdlib.free(read_buf)
return None
read_buf[text_len] = '\0'
# 构造搜索模式: "{sha1}.{class_name}.{method_name}" 或 bare 模式 "{sha1}.{method_name}"
cls_len: sizet = 0
if class_name is not None:
cls_len = string.strlen(class_name)
meth_len: sizet = string.strlen(method_name)
search_len: sizet = sha1_len + meth_len + 2
if use_bare == 0:
search_len = sha1_len + cls_len + meth_len + 4
search_buf: bytes = stdlib.malloc(search_len)
if search_buf is None:
stdlib.free(read_buf)
return None
if use_bare != 0:
viperlib.snprintf(search_buf, search_len, "%s.%s",
class_sha1, method_name)
else:
viperlib.snprintf(search_buf, search_len, "%s.%s.%s",
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)
pos: sizet = 0
result_ty: llvmlite.LLVMType | t.CPtr = 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' 时不可能等于 "define "(strncmp 判等
# 蕴含首个字节相等),直接短路省去绝大多数非 define 行的 strncmp 调用;
# 仅做否定判定,不改变任何原本会命中的行。
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
stdlib.free(search_buf)
stdlib.free(read_buf)
return result_ty
# ============================================================
# _dep_text_ll_exists - 探测依赖模块 text.ll 是否已生成
#
# 用于区分跨模块返回类型精确解析失败的两种场景:
# 1. text.ll 存在但函数未命中 → 真错误(依赖已编译,函数缺失)
# 2. text.ll 不存在 → 依赖模块尚未翻译(Phase A 按文件顺序翻译,
# main.vp 先于其 import 的 mbuddytest.vp),标记重试待下遍
# ============================================================
# ============================================================
def _dep_text_ll_exists(self, pool: rowptr[memhub.MemBuddy],
sha1: str) -> int:
"""探测 {temp_dir}/{sha1[0:2]}/{sha1}.text.ll 是否存在,1=存在"""
if self.Gen.CallGenBase.TempDir is None or self.Gen.CallGenBase.TempDirLen == 0 or sha1 is None:
return 0
sha1_len: sizet = string.strlen(sha1)
if sha1_len < 2:
return 0
path_len: sizet = self.Gen.CallGenBase.TempDirLen + sha1_len + 14
path_buf: bytes = stdlib.malloc(path_len)
subdir_buf: bytes = stdlib.malloc(3)
if path_buf is None or subdir_buf is None:
if path_buf is not None:
stdlib.free(path_buf)
if subdir_buf is not None:
stdlib.free(subdir_buf)
return 0
subdir_buf[0] = sha1[0]
subdir_buf[1] = sha1[1]
subdir_buf[2] = '\0'
viperlib.snprintf(path_buf, path_len, "%s/%s/%s.text.ll",
self.Gen.CallGenBase.TempDir, subdir_buf, sha1)
stdlib.free(subdir_buf)
tf: fileio.File | t.CPtr = fileio.File(path_buf, fileio.MODE.R)
exists: int = 0
if not tf.closed:
tf.close()
exists = 1
stdlib.free(path_buf)
return exists
# ============================================================
# 属性方法(@property/@x.setter/@x.deleter)与静态/类方法调用辅助
#
# 元数据表由 HandlesClassDef 在类翻译/预注册阶段填充,
# 这里负责把 obj.prop / obj.prop=v / del obj.prop / Class.method()
# 翻译为对应的 {sha1}.ClassName.prop 系列函数调用。
# ============================================================
# ============================================================
# _translate_property_call — 翻译属性方法调用
#
# 与 _translate_method_call 的调用名/返回类型解析逻辑一致:
# 1. 优先用模块中已有的函数(精确/后缀匹配,来自 stub 注入)
# 2. 否则用类所属模块的 SHA1 构造 "{sha1}.ClassName.method" 调用名
# 3. 返回类型:函数实类型 → text.ll 查找 → 按方法名推断(默认 i32)
#
# call_head/call_count: 已构建好的参数链表(如 getter 只有 obj 一个参数)
# ============================================================