自举实验失败,C1 编译 Test 成功,暂未达到自举收敛点

This commit is contained in:
2026-07-30 15:44:32 +08:00
parent 68481a5a7f
commit 32af3f93fa
22 changed files with 2158 additions and 442 deletions

View File

@@ -8,6 +8,7 @@ import stdio
import stdlib
import sys
import viperlib
import w32.fileio as fileio
import lib.core.VLogger as VLogger
import lib.core.Handles.HandlesBase as HandlesBase
import lib.core.Handles.HandlesTranslator as HT
@@ -2257,13 +2258,11 @@ def _translate_struct_ctor(pool: memhub.MemBuddy | t.CPtr,
"__before_init__", storage_ptr, None, 0, trans)
# 如果有 __init__调用 __init__(ptr, args...)
# 注意: 仅依据 ctor_entry.HasInit 判断,不强制推断
# JsonValue 等类有 __new__ 但无 __init__强制推断会导致 undefined reference
has_init_flag: int = 0
if ctor_entry is not None:
has_init_flag = ctor_entry.HasInit
# 跨模块 OOP 类: HasInit 可能未设置_translate_oop_methods 未被调用)
# 但 IsOOP=1 说明类有方法,推断 HasInit=1
if is_oop != 0 and has_init_flag == 0:
has_init_flag = 1
if has_init_flag != 0:
# 翻译构造函数参数
arg_vals: t.CSizeT | t.CPtr = pool.alloc(8 * 32)
@@ -2283,8 +2282,9 @@ def _translate_struct_ctor(pool: memhub.MemBuddy | t.CPtr,
_call_method_on_ptr(pool, builder, mod, class_name,
"__init__", storage_ptr, arg_vals, real_arg_count, trans)
else:
# 无 __init__逐字段 store 参数值
elif has_new_flag == 0:
# 无 __init__ 且无 __new__:逐字段 store 参数值
# (有 __new__ 时参数已由 __new__ 消费,不应存入字段)
for ai in range(can):
arg: ast.AST | t.CPtr = cargs.get(ai)
if arg is None:
@@ -2974,6 +2974,262 @@ def _infer_method_ret_ty(pool: memhub.MemBuddy | t.CPtr,
# 与 _infer_external_func_ret_ty 的默认策略保持一致
return llvmlite.Int32(pool)
# ============================================================
# 跨模块方法返回类型查找(从 text.ll
#
# Phase A 翻译每个源文件后生成 text.ll其中 define 行包含
# 方法的实际返回类型。当 Phase A 翻译后续文件调用跨模块方法时,
# _infer_method_ret_ty 默认返回 i32导致指针返回值被截断。
# _LookupMethodRetTyFromTextLL 读取依赖模块的 text.ll从 define
# 行提取实际返回类型,避免指针截断。
# ============================================================
_g_temp_dir: str = None
_g_temp_dir_len: t.CSizeT = 0
# text.ll 读取缓冲区大小
_TEXT_LL_READ_BUF_SIZE: t.CDefine = 262144
# ============================================================
# SetTempDir - 设置 temp 目录路径
#
# 在 Phase2.py 的 Phase A 翻译前调用,使 HandlesExprCall
# 能够读取依赖模块的 text.ll 文件。
# ============================================================
def SetTempDir(temp_dir: str):
"""设置 temp 目录路径(供跨模块方法返回类型查找使用)"""
global _g_temp_dir
global _g_temp_dir_len
_g_temp_dir = temp_dir
if temp_dir is not None:
_g_temp_dir_len = string.strlen(temp_dir)
else:
_g_temp_dir_len = 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(pool: memhub.MemBuddy | t.CPtr,
buf: bytes,
start: t.CSizeT,
end: t.CSizeT) -> llvmlite.LLVMType | t.CPtr:
"""将 LLVM IR 返回类型字符串转换为 llvmlite.LLVMType"""
if pool is None or buf is None or start >= end:
return None
# 去除前导空白
s: t.CSizeT = start
while s < end:
c: int = buf[s]
if c != ' ' and c != '\t':
break
s += 1
# 去除尾部空白
e: t.CSizeT = end
while e > s:
c2: int = buf[e - 1]
if c2 != ' ' and c2 != '\t':
break
e -= 1
ty_len: t.CSizeT = e - s
if ty_len == 0:
return None
# 检查是否含 * (指针类型) → 返回 i8* (通用指针)
# 包括 i8*, i32*, %"name"*, { i32, i8 }* 等
ci: t.CSizeT = 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: t.CSizeT = 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(pool: memhub.MemBuddy | t.CPtr,
class_sha1: str,
class_name: str,
method_name: str) -> llvmlite.LLVMType | t.CPtr:
"""从依赖模块的 text.ll 中查找方法返回类型"""
if pool is None or class_sha1 is None or class_name is None or method_name is None:
return None
if _g_temp_dir is None or _g_temp_dir_len == 0:
return None
sha1_len: t.CSizeT = 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: t.CSizeT = _g_temp_dir_len + 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",
_g_temp_dir, subdir_buf, class_sha1)
stdlib.free(subdir_buf)
# 读取 text.ll
read_buf: bytes = stdlib.malloc(_TEXT_LL_READ_BUF_SIZE)
if read_buf is None:
stdlib.free(path_buf)
return None
tf: fileio.File | t.CPtr = fileio.File(path_buf, fileio.MODE.R)
if tf.closed:
stdlib.free(path_buf)
stdlib.free(read_buf)
return None
text_len: t.CInt64T = tf.read_all(read_buf, _TEXT_LL_READ_BUF_SIZE)
tf.close()
stdlib.free(path_buf)
if text_len <= 0:
stdlib.free(read_buf)
return None
if text_len < _TEXT_LL_READ_BUF_SIZE:
read_buf[text_len] = '\0'
else:
read_buf[_TEXT_LL_READ_BUF_SIZE - 1] = '\0'
# 构造搜索模式: "{sha1}.{class_name}.{method_name}"
cls_len: t.CSizeT = string.strlen(class_name)
meth_len: t.CSizeT = string.strlen(method_name)
search_len: t.CSizeT = sha1_len + cls_len + meth_len + 3
search_buf: bytes = stdlib.malloc(search_len)
if search_buf is None:
stdlib.free(read_buf)
return None
viperlib.snprintf(search_buf, search_len, "%s.%s.%s",
class_sha1, class_name, method_name)
search_str_len: t.CSizeT = string.strlen(search_buf)
# 在 text.ll 中搜索 define 行包含 search_buf
buf_len: t.CSizeT = string.strlen(read_buf)
pos: t.CSizeT = 0
result_ty: llvmlite.LLVMType | t.CPtr = None
while pos < buf_len and result_ty is None:
ls: t.CSizeT = pos
le: t.CSizeT = pos
while le < buf_len:
if read_buf[le] == '\n':
break
le += 1
ll: t.CSizeT = le - ls
# 检查是否是 define 行
if ll >= 7 and string.strncmp(read_buf + ls, "define ", 7) == 0:
# 找 @ 符号
at_pos: t.CSizeT = ls + 7
while at_pos < ls + ll:
if read_buf[at_pos] == '@':
break
at_pos += 1
if at_pos < ls + ll:
# 跳过可选引号
name_start: t.CSizeT = 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:
# 匹配!提取返回类型: [ls+7, at_pos)
result_ty = _ParseLLVMRetTyStr(pool, read_buf, ls + 7, at_pos)
pos = le + 1
stdlib.free(search_buf)
stdlib.free(read_buf)
return result_ty
# ============================================================
# _translate_method_call - 翻译方法调用 obj.method(args)
#
@@ -3069,8 +3325,14 @@ def _translate_method_call(pool: memhub.MemBuddy | t.CPtr,
if frt is not None:
call_ret_ty = frt
else:
# found_func 为 Nonestub 未注入):根据方法名推断返回类型
call_ret_ty = _infer_method_ret_ty(pool, method_name)
# found_func 为 Nonestub 未注入):尝试从 text.ll 查找实际返回类型
# 避免指针返回值被 _infer_method_ret_ty 默认为 i32 导致 inttoptr 截断
looked_up_ty: llvmlite.LLVMType | t.CPtr = _LookupMethodRetTyFromTextLL(
pool, cls_sha1_mc, class_name, method_name)
if looked_up_ty is not None:
call_ret_ty = looked_up_ty
else:
call_ret_ty = _infer_method_ret_ty(pool, method_name)
# 翻译调用参数
cargs: list[ast.AST | t.CPtr] | t.CPtr = cl.args
@@ -3521,6 +3783,9 @@ def _translate_call_with_kwargs(pool: memhub.MemBuddy | t.CPtr,
# - 整数 → 指针: inttoptr (如 t.CPtr(int_val) → i8*)
# - 整数 → 整数: trunc/zext (如 t.CInt8T(i32_val) → i8)
# - 指针 → 指针: bitcast (如 t.CPtr(ptr_val) → i8*)
# - 整数 → 浮点: sitofp (如 t.CDouble(i32_val) → double)
# - 浮点 → 整数: fptosi (如 t.CInt(double_val) → i32)
# - 浮点 → 浮点: fpext/fptrunc (如 t.CFloat(double_val) → float)
# ============================================================
def _translate_t_type_cast(pool: memhub.MemBuddy | t.CPtr,
builder: llvmlite.IRBuilder | t.CPtr,
@@ -3532,6 +3797,8 @@ def _translate_t_type_cast(pool: memhub.MemBuddy | t.CPtr,
val_bits: int = HandlesExpr.get_llvm_type_bits(val.Ty)
target_bits: int = HandlesExpr.get_llvm_type_bits(target_ty)
val_fbits: int = HandlesExpr.get_llvm_float_bits(val.Ty)
target_fbits: int = HandlesExpr.get_llvm_float_bits(target_ty)
# 整数 → 整数: trunc/zext
if val_bits != 0 and target_bits != 0:
@@ -3542,12 +3809,29 @@ def _translate_t_type_cast(pool: memhub.MemBuddy | t.CPtr,
else:
return llvmlite.build_trunc(builder, val, target_ty)
# 整数 → 浮点: sitofp
if val_bits != 0 and target_fbits != 0:
return llvmlite.build_si2fp(builder, val, target_ty)
# 浮点 → 整数: fptosi
if val_fbits != 0 and target_bits != 0:
return llvmlite.build_fp2si(builder, val, target_ty)
# 浮点 → 浮点: fpext/fptrunc
if val_fbits != 0 and target_fbits != 0:
if val_fbits == target_fbits:
return val
elif val_fbits < target_fbits:
return llvmlite.build_fpext(builder, val, target_ty)
else:
return llvmlite.build_fptrunc(builder, val, target_ty)
# 指针 → 整数: ptrtoint
if val_bits == 0 and target_bits != 0:
if val_bits == 0 and val_fbits == 0 and target_bits != 0:
return llvmlite.build_ptrtoint(builder, val, target_ty)
# 整数 → 指针: inttoptr
if val_bits != 0 and target_bits == 0:
# 整数 → 指针: inttoptr(必须排除浮点 target浮点 target_bits 也是 0
if val_bits != 0 and target_bits == 0 and target_fbits == 0:
return llvmlite.build_inttoptr(builder, val, target_ty)
# 指针 → 指针: bitcast
@@ -3763,6 +4047,28 @@ def translate_call(pool: memhub.MemBuddy | t.CPtr,
if cargs is not None:
can = cargs.__len__()
# 内置强转函数: str(x)/bytes(x) → i8*, int(x) → i32
# 等价于 (t.CChar | t.CPtr)(x) / (t.CInt32T)(x)
# 单参数str(val) → bitcast val to i8*
# int(val) → trunc/sext val to i32
if func_name is not None and can == 1:
if string.strcmp(func_name, "str") == 0 or string.strcmp(func_name, "bytes") == 0:
# str(x) / bytes(x) → i8* (bitcast)
cast_arg: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
builder, pool, mod, cargs.get(0), funcs_ptr, func_count, trans)
if cast_arg is not None:
i8_ptr_ty_builtin: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, llvmlite.Int8(pool))
return _translate_t_type_cast(pool, builder, cast_arg, i8_ptr_ty_builtin)
return None
elif string.strcmp(func_name, "int") == 0:
# int(x) → i32
cast_arg_int: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
builder, pool, mod, cargs.get(0), funcs_ptr, func_count, trans)
if cast_arg_int is not None:
i32_ty_builtin: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
return _translate_t_type_cast(pool, builder, cast_arg_int, i32_ty_builtin)
return None
# 检测 t.XXX 类型转换: t.CUInt64T(ptr), t.CPtr(val), t.CInt(val) 等
# 当 func 是 t.XXX 形式且 XXX 是已知类型名时,当作类型转换处理(而非函数调用)
# 支持:
@@ -3805,6 +4111,19 @@ def translate_call(pool: memhub.MemBuddy | t.CPtr,
# t.XXX 类型转换已确认但不满足参数条件:返回 None不跌落到跨模块 FATAL
return None
# 裸名 typedef 调用: BYTEPTR(ptr1), HANDLE(val), CHARPTR(str) 等
# 用户通过 `from stdint import *` 导入 typedef 后,可直接用裸名做类型转换
# map_t_type 包含所有内置 typedef 名称BYTEPTR/HANDLE/LPCSTR/size_t 等)
if cl.func is not None and cl.func.kind() == ast.ASTKind.Name:
bare_ty: llvmlite.LLVMType | t.CPtr = HandlesType.map_t_type(pool, func_name)
if bare_ty is not None and can == 1:
bare_arg_node: ast.AST | t.CPtr = cargs.get(0)
if bare_arg_node is not None:
bare_arg_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
builder, pool, mod, bare_arg_node, None, 0, trans)
if bare_arg_val is not None:
return _translate_t_type_cast(pool, builder, bare_arg_val, bare_ty)
# 检测 ClassName.__sizeof__() — 返回结构体大小常量i64
# __sizeof__ 是内置方法,不走跨模块调用路径
# 支持: Argument.__sizeof__() 和 w32.win32file.WIN32_FIND_DATAA.__sizeof__()
@@ -3907,11 +4226,6 @@ def translate_call(pool: memhub.MemBuddy | t.CPtr,
# 模块属性前向引用回退为 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")
@@ -4049,7 +4363,10 @@ def translate_call(pool: memhub.MemBuddy | t.CPtr,
llvmlite.value_set_next(tail3, arg_val3)
tail3 = arg_val3
total3: int = 1 + can
ret_ty3: llvmlite.LLVMType | t.CPtr = _infer_method_ret_ty(pool, func_name)
ret_ty3: llvmlite.LLVMType | t.CPtr = _LookupMethodRetTyFromTextLL(
pool, sha1_fc, cls_nm2, func_name)
if ret_ty3 is None:
ret_ty3 = _infer_method_ret_ty(pool, func_name)
_ensure_method_declare(pool, mod, mangled3, ret_ty3, head3, total3)
return llvmlite.build_call(builder, mangled3, head3, total3, ret_ty3, 0)