Fixed some bugs and tried to maintain the bootstrap

This commit is contained in:
2026-07-28 21:10:05 +08:00
parent 03d0bba534
commit 68481a5a7f
28 changed files with 667 additions and 1491 deletions

View File

@@ -362,29 +362,49 @@ def _ensure_c_lib_declare(pool: memhub.MemBuddy | t.CPtr,
# ============================================================
def _infer_external_func_ret_ty(pool: memhub.MemBuddy | t.CPtr,
func_name: str) -> llvmlite.LLVMType | t.CPtr:
"""根据函数名推断外部 includes 函数的返回类型"""
"""根据函数名推断外部 includes 函数的返回类型
支持三种函数名形式:
1. 裸名: "parse", "strlen"
2. 别名: "json_parse"from X import parse as json_parse
3. 带 SHA1 前缀: "240a9a4157959a9f.parse"(跨模块调用 mangled name
"""
if pool is None or func_name is None:
return llvmlite.Int32(pool)
# 提取裸函数名:去掉 SHA1 前缀(如 "240a9a4157959a9f.parse" → "parse"
# SHA1 前缀是 16 位十六进制 + '.',检查是否有 '.' 分隔
bare_name: str = func_name
dot_pos: str = string.strrchr(func_name, 46) # 46 = ord('.')
if dot_pos is not None:
# '.' 后的部分是裸函数名
bare_name = dot_pos + 1
i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, llvmlite.Int8(pool))
# 返回 i8* 的函数(指针返回值,截断会导致错误)
if func_name == "strchr":
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
if func_name == "strrchr":
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
if func_name == "strstr":
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
if func_name == "strcpy":
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
if func_name == "strncpy":
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
if func_name == "memset":
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
if func_name == "memset32":
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
if func_name == "memcpy":
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
if func_name == "memmove":
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
if bare_name == "strchr" or func_name == "strchr":
return i8_ptr_ty
if bare_name == "strrchr" or func_name == "strrchr":
return i8_ptr_ty
if bare_name == "strstr" or func_name == "strstr":
return i8_ptr_ty
if bare_name == "strcpy" or func_name == "strcpy":
return i8_ptr_ty
if bare_name == "strncpy" or func_name == "strncpy":
return i8_ptr_ty
if bare_name == "memset" or func_name == "memset":
return i8_ptr_ty
if bare_name == "memset32" or func_name == "memset32":
return i8_ptr_ty
if bare_name == "memcpy" or func_name == "memcpy":
return i8_ptr_ty
if bare_name == "memmove" or func_name == "memmove":
return i8_ptr_ty
# JSON 解析函数返回 JsonValue*(指针)
# from json.__parser import parse as json_parse → 别名 json_parse 也需覆盖
if bare_name == "parse" or func_name == "parse" or func_name == "json_parse":
return i8_ptr_ty
# MemBuddy 方法(当类型信息丢失时可能走外部函数路径)
if func_name == "alloc":
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
@@ -394,6 +414,14 @@ def _infer_external_func_ret_ty(pool: memhub.MemBuddy | t.CPtr,
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
if func_name == "alloc_buf":
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
# VLogger 模块函数(跨模块调用时 stub 未注入,返回指针被截断为 i32 导致崩溃)
if func_name == "get_logger":
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
if func_name == "fmt_buf":
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
# Config 模块函数(跨模块调用时 stub 未注入,返回 i8* 被 inttoptr i32 截断导致路径损坏)
if func_name == "get_includes_binary_dir":
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
# 返回 i64 的函数
if func_name == "strlen":
@@ -442,6 +470,17 @@ def _infer_external_func_ret_ty(pool: memhub.MemBuddy | t.CPtr,
return i8_ptr_ty
if func_name == "LoadLibraryW":
return i8_ptr_ty
# Win32 Handle 返回函数HANDLE = void* = i8*
if func_name == "GetStdHandle":
return i8_ptr_ty
if func_name == "CreateFileA":
return i8_ptr_ty
if func_name == "CreateFileW":
return i8_ptr_ty
if func_name == "FindFirstFileA":
return i8_ptr_ty
if func_name == "FindFirstFileW":
return i8_ptr_ty
# Win32 API 返回 i64 (SIZE_T) 的函数
if func_name == "VirtualQuery":
@@ -486,7 +525,11 @@ def _infer_external_func_ret_ty(pool: memhub.MemBuddy | t.CPtr,
func_name == "function_get_param_head" or func_name == "param_get_next":
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
# 默认 i32
# 默认 i32整数与本模块函数定义的默认返回类型一致HandlesFunctions.py line 324
# 已知返回指针的函数在上方显式列出(返回 i8*),已知返回 i64 的函数也显式列出
# 避免使用 i64 导致 stub 声明declare i64与实际定义define i32类型不匹配
# x86-64 ABI 中 i32 返回值在 rax 低 32 位,与 define i32 完全一致
# 与 _infer_method_ret_ty 的默认策略保持一致
return llvmlite.Int32(pool)
# ============================================================
@@ -2154,6 +2197,29 @@ def _translate_struct_ctor(pool: memhub.MemBuddy | t.CPtr,
if ctor_entry is not None:
is_oop = ctor_entry.IsOOP
# 跨模块 OOP 类推断IsOOP 可能未设置Phase1 缓存跳过 _translate_oop_methods
# 方案1: 检查当前模块是否有 ClassName.__init__ 的声明stub 注入)
# 方案2: 检查全局默认参数表是否有 ClassName.__init__ 的条目(翻译时填充,更可靠)
if is_oop == 0 and class_name is not None:
init_lookup_name: t.CChar | t.CPtr = pool.alloc(128)
if init_lookup_name is not None:
viperlib.snprintf(init_lookup_name, 128, "%s.__init__", class_name)
# 方案1: 检查当前模块的函数声明
init_found_func: llvmlite.Function | t.CPtr = find_func_in_module(mod, init_lookup_name)
if init_found_func is not None:
is_oop = 1
if ctor_entry is not None:
ctor_entry.IsOOP = 1
ctor_entry.HasInit = 1
else:
# 方案2: 检查全局默认参数表(翻译时填充,不依赖 stub 注入)
init_defaults_entry: FuncDefaultsEntry | t.CPtr = find_func_defaults(init_lookup_name)
if init_defaults_entry is not None:
is_oop = 1
if ctor_entry is not None:
ctor_entry.IsOOP = 1
ctor_entry.HasInit = 1
if is_oop != 0:
# 存储指针:默认用 alloca如果有 __new__ 则用 __new__ 返回的指针
storage_ptr: llvmlite.Value | t.CPtr = tmp
@@ -2773,7 +2839,13 @@ def _ensure_method_declare(pool: memhub.MemBuddy | t.CPtr,
ret_ty: llvmlite.LLVMType | t.CPtr,
param_head: llvmlite.ParamNode | t.CPtr,
param_count: int):
"""为跨模块方法调用创建 declare 声明"""
"""为跨模块方法调用创建 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
# 检查模块中是否已有同名函数declare 或 define
@@ -2781,7 +2853,32 @@ def _ensure_method_declare(pool: memhub.MemBuddy | t.CPtr,
while existing is not None:
if existing.Name is not None:
if string.strcmp(existing.Name, call_name) == 0:
return # 已存在,无需重复声明
# 已存在:仅当是 declare 且参数数量不足时追加缺失参数
if existing.IsDeclared == 1:
# 遍历现有参数链表计数(避免依赖 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
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
# 创建函数声明
func: llvmlite.Function | t.CPtr = llvmlite.new_function(pool, call_name, ret_ty)
@@ -2832,18 +2929,35 @@ def _infer_method_ret_ty(pool: memhub.MemBuddy | t.CPtr,
return i8_ptr_ty
if string.strcmp(method_name, "__enter__") == 0:
return i8_ptr_ty
# 工厂方法:返回对象指针(截断会导致后续方法调用崩溃)
if string.strcmp(method_name, "parse_args") == 0:
return i8_ptr_ty
# 魔术方法:返回对象指针(如 JsonValue.__getitem__ 返回 JsonValue*
if string.strcmp(method_name, "__getitem__") == 0:
return i8_ptr_ty
# 返回 i8* 的方法(返回字符串/类型名/描述)
if string.strcmp(method_name, "type_name") == 0:
return i8_ptr_ty
if string.strcmp(method_name, "get_name") == 0:
return i8_ptr_ty
if string.strcmp(method_name, "get_str") == 0:
return i8_ptr_ty
if string.strcmp(method_name, "get_path") == 0:
return i8_ptr_ty
if string.strcmp(method_name, "get_buf") == 0:
return i8_ptr_ty
if string.strcmp(method_name, "to_string") == 0:
return i8_ptr_ty
if string.strcmp(method_name, "as_string") == 0:
return i8_ptr_ty
if string.strcmp(method_name, "__repr__") == 0:
return i8_ptr_ty
if string.strcmp(method_name, "__str__") == 0:
return i8_ptr_ty
# 返回 i8* 的方法(返回对象指针,如 JsonValue.get_item → JsonValue*
if string.strcmp(method_name, "get_item") == 0:
return i8_ptr_ty
# 返回 void 的方法
if string.strcmp(method_name, "__before_init__") == 0:
@@ -2853,10 +2967,12 @@ def _infer_method_ret_ty(pool: memhub.MemBuddy | t.CPtr,
if string.strcmp(method_name, "__exit__") == 0:
return llvmlite.Void(pool)
# 默认 i64(整数):既可安全截断为 i32trunc也可转换为指针inttoptr
# 避免使用指针类型i8*)导致 coerce_to_type 生成 load解引用造成崩溃
# x86-64 ABI 中 i32 返回值在 rax 低 32 位i64 读取后 trunc 取低 32 位是安全的
return llvmlite.Int64(pool)
# 默认 i32(整数):与本模块函数定义的默认返回类型一致HandlesFunctions.py line 324
# 已知返回指针的方法在上方显式列出(返回 i8*),已知返回 void 的方法也显式列出
# 避免使用 i64 导致 stub 声明declare i64与实际定义define i32类型不匹配
# x86-64 ABI 中 i32 返回值在 rax 低 32 位,与 define i32 完全一致
# 与 _infer_external_func_ret_ty 的默认策略保持一致
return llvmlite.Int32(pool)
# ============================================================
# _translate_method_call - 翻译方法调用 obj.method(args)
@@ -2981,6 +3097,65 @@ def _translate_method_call(pool: memhub.MemBuddy | t.CPtr,
total_count: int = 1 + can
# 默认参数填充:如果调用点参数少于函数定义参数,用默认值填充缺失参数
# 查找函数的 defaults 信息
mdf_defaults: t.CVoid | t.CPtr = None
mdf_default_count: int = 0
mdf_param_count: int = 0
if trans is not None and trans._funcs is not None:
mdf_entry: FuncEntry | t.CPtr = find_func_entry_in_table(
trans._funcs, trans._func_count, lookup_name)
if mdf_entry is not None:
mdf_defaults = mdf_entry.Defaults
mdf_default_count = mdf_entry.DefaultCount
mdf_param_count = mdf_entry.ParamCount
if mdf_defaults is None:
mdf_gd: FuncDefaultsEntry | t.CPtr = find_func_defaults(lookup_name)
if mdf_gd is not None:
mdf_defaults = mdf_gd.Defaults
mdf_default_count = mdf_gd.DefaultCount
mdf_param_count = mdf_gd.ParamCount
# 计算需要填充的参数(不含 self
# can = 已提供的非 self 参数数量
# mdf_param_count = 函数定义的非 self 参数数量
if mdf_param_count > can:
mdf_offset: int = mdf_param_count - mdf_default_count
mdf_pi: int = can
while mdf_pi < mdf_param_count:
# 优先用默认值
if mdf_defaults is not None and mdf_default_count > 0 and mdf_pi >= mdf_offset:
mdf_di: int = mdf_pi - mdf_offset
if mdf_di < mdf_default_count:
mdf_defaults_list: list[ast.AST | t.CPtr] | t.CPtr = (list[ast.AST | t.CPtr] | t.CPtr)(t.CVoid(t.CUInt64T(mdf_defaults), t.CPtr))
mdf_node: ast.AST | t.CPtr = mdf_defaults_list.get(mdf_di)
if mdf_node is not None:
mdf_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
builder, pool, mod, mdf_node, None, 0, trans)
if mdf_val is not None:
llvmlite.value_set_next(mdf_val, None)
llvmlite.value_set_next(tail, mdf_val)
tail = mdf_val
total_count += 1
mdf_pi += 1
continue
# 无默认值时用零值兜底(需要 found_func 获取参数类型)
if found_func is not None:
param_node_z: llvmlite.Param | t.CPtr = llvmlite.function_get_param_head(found_func)
skip_z: int = 0
while skip_z <= mdf_pi and param_node_z is not None:
param_node_z = llvmlite.param_get_next(param_node_z)
skip_z += 1
if param_node_z is not None:
zero_ty: llvmlite.LLVMType | t.CPtr = llvmlite.param_get_ty(param_node_z)
zero_val: llvmlite.Value | t.CPtr = llvmlite.ConstZero(pool, zero_ty)
if zero_val is not None:
llvmlite.value_set_next(zero_val, None)
llvmlite.value_set_next(tail, zero_val)
tail = zero_val
total_count += 1
mdf_pi += 1
# 虚方法分发:如果类有虚表且该方法在虚表中,走间接调用
# (不依赖 found_func — 继承的虚方法可能没有子类实现)
# 复用前面已获取的 mc_entryfind_struct_by_type 规避跨模块同名找错)
@@ -3061,6 +3236,30 @@ def _translate_struct_ctor_kw(pool: memhub.MemBuddy | t.CPtr,
is_oop: int = 0
if kw_entry is not None:
is_oop = kw_entry.IsOOP
# 跨模块 OOP 类推断IsOOP 可能未设置Phase1 缓存跳过 _translate_oop_methods
# 方案1: 检查当前模块是否有 ClassName.__init__ 的声明stub 注入)
# 方案2: 检查全局默认参数表是否有 ClassName.__init__ 的条目(翻译时填充,更可靠)
if is_oop == 0 and class_name is not None:
kw_init_lookup: t.CChar | t.CPtr = pool.alloc(128)
if kw_init_lookup is not None:
viperlib.snprintf(kw_init_lookup, 128, "%s.__init__", class_name)
# 方案1: 检查当前模块的函数声明
kw_init_found: llvmlite.Function | t.CPtr = find_func_in_module(mod, kw_init_lookup)
if kw_init_found is not None:
is_oop = 1
if kw_entry is not None:
kw_entry.IsOOP = 1
kw_entry.HasInit = 1
else:
# 方案2: 检查全局默认参数表(翻译时填充,不依赖 stub 注入)
kw_init_defaults: FuncDefaultsEntry | t.CPtr = find_func_defaults(kw_init_lookup)
if kw_init_defaults is not None:
is_oop = 1
if kw_entry is not None:
kw_entry.IsOOP = 1
kw_entry.HasInit = 1
if is_oop != 0:
_call_method_on_ptr(pool, builder, mod, class_name,
"__before_init__", tmp, None, 0, trans)
@@ -3693,13 +3892,27 @@ def translate_call(pool: memhub.MemBuddy | t.CPtr,
# 检测 obj.__len__() — 返回 list 对象的 __count__ 字段(偏移 8i64
# __len__ 是 list[T] 的内置方法list 是泛型类不注册 struct
# 注意: 必须用 translate_value 而非 translate_name_value因为 len_at.value
# 可能是 Attribute 节点(如 md_ags.argstranslate_name_value 只处理 Name 节点,
# 对 Attribute 会错误返回 i32 0导致 GEP base 不是指针base of getelementptr must be a pointer
if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute:
len_at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(cl.func)
if len_at.attr is not None and string.strcmp(len_at.attr, "__len__") == 0:
if len_at.value is not None and can == 0:
obj_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_name_value(
builder, pool, len_at.value, trans)
if obj_val is not None:
obj_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
builder, pool, mod, len_at.value, funcs_ptr, func_count, trans)
if obj_val is not None and obj_val.Ty is not None:
# 防御性类型检查: obj_val 必须是指针类型才能做 GEP
# 如果 translate_value 返回 i32 (如 CDefine 常量被误解析、
# 模块属性前向引用回退为 i32)GEP base 会是 i32 而非指针,
# 导致 llc 报错 "base of getelementptr must be a pointer"
if HandlesExpr.is_ptr_type(obj_val.Ty) == 0:
# 诊断: 输出节点类型信息帮助定位根本原因
len_node_kind: int = len_at.value.kind()
stdio.printf(
"[LEN-DIAG] __len__ on non-ptr (kind=%d), fallback to 0\n",
len_node_kind)
return llvmlite.const_int64(pool, 0)
i64_ty_len: llvmlite.LLVMType | t.CPtr = llvmlite.Int64(pool)
# 注意: 必须用 const_int64 而非 ConstInt(...,"0")
# ConstInt 的 name 参数 "0" 会被当作 IR 文本值输出 i64 0
@@ -3917,6 +4130,20 @@ def translate_call(pool: memhub.MemBuddy | t.CPtr,
lm_obj: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
builder, pool, mod, lm_at.value, funcs_ptr, func_count, trans)
if lm_obj is not None:
# 防御性类型检查: lm_obj 必须是指针类型才能做 GEP
# translate_value 对某些 Attribute 节点(如 ast.Arguments 的属性)
# 可能返回 i32 0 (非指针),直接 GEP 会导致
# "base of getelementptr must be a pointer" 错误
if lm_obj.Ty is None or HandlesExpr.is_ptr_type(lm_obj.Ty) == 0:
lm_node_kind: int = lm_at.value.kind()
fb_lm: t.CChar | t.CPtr = VLogger.fmt_buf()
if fb_lm is not None:
viperlib.snprintf(fb_lm, 1024,
"list 方法 '%s' 的目标对象非指针 (node kind=%d)"
"translate_value 类型推断失败",
lm_name, lm_node_kind)
VLogger.critical(fb_lm, "LIST-METHOD")
return None
i64_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int64(pool)
i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty)
@@ -4245,6 +4472,9 @@ def translate_call(pool: memhub.MemBuddy | t.CPtr,
# 旧逻辑IsDeclared==1 时用 func_name 裸名是错误的stub 注入的 declare Name 已带 SHA1 前缀,
# 用裸名会导致 call @_PathToModuleName 而 define 是 @"sha1._PathToModuleName",链接报 undefined reference
call_name: str = func_name
# 原始函数名(别名导入时记录,如 from X import parse as json_parse → orig_func_name="parse"
# 用于在别名处理后重新推断返回类型,避免 _infer_external_func_ret_ty 用别名匹配失败
orig_func_name: str = None
if found_func is not None:
found_name: t.CChar | t.CPtr = llvmlite.function_get_name(found_func)
if found_name is not None:
@@ -4357,6 +4587,11 @@ def translate_call(pool: memhub.MemBuddy | t.CPtr,
bo_buf[bo_i] = bare_orig[bo_i]
bo_buf[bo_len] = '\0'
bare_func_name = bo_buf
# 记录原始函数名,供后续重新推断返回类型
# 例如 from json.__parser import parse as json_parse
# func_name="json_parse", bare_func_name="parse"
# _infer_external_func_ret_ty 用 "parse" 能正确匹配,用 "json_parse" 则返回默认 i32
orig_func_name = bare_func_name
if is_cexport_func(bare_sha1, bare_func_name) != 0:
call_name = bare_func_name
else:
@@ -4382,6 +4617,14 @@ def translate_call(pool: memhub.MemBuddy | t.CPtr,
# memcpy → @llvm.memcpy 内联函数(避免 monomorphization 跨模块 @memcpy 声明缺失)
if func_name == "memcpy" and can >= 3:
return _emit_llvm_memcpy_intrinsic(pool, builder, mod, mc_dst2, mc_src2, mc_num2)
# 别名导入重新推断返回类型:
# from X import parse as json_parse → func_name="json_parse" 时 _infer_external_func_ret_ty 返回默认 i32
# 用 orig_func_name="parse" 重新推断,能正确匹配返回 i8*JsonValue*
# 避免指针被截断为 i32 后 inttoptr 丢失高 32 位导致崩溃
if orig_func_name is not None and orig_func_name != func_name:
re_inferred_ty: llvmlite.LLVMType | t.CPtr = _infer_external_func_ret_ty(pool, orig_func_name)
if re_inferred_ty is not None:
call_ret_ty = re_inferred_ty
# 跨模块调用:创建 declare 声明,避免 llc 报 undefined value
_ensure_method_declare(pool, mod, call_name, call_ret_ty, head, can)
return llvmlite.build_call(builder, call_name, head, can, call_ret_ty, 0)