fix(tpv): 修 VKernel 暴露的 codegen 缺陷(浮点 CDefine/__sizeof__ packed/CONST_STR/alloca/无符号/ABI)

- 浮点 t.CDefine 初值序列化与源恢复(gfx PI 编成 0.0)

- __sizeof__ 按 packed 计算(cpu _tss memset 越界写 4B)

- 数组初始化器支持 CONST_STR 单字符;模块级别名不再产出 global i32 0 假全局

- 临时缓冲/构造临时量 alloca 提升至入口块(B6 未覆盖路径)

- 无符号判定补 CSizeT/小写别名/结构体字段 + udiv/urem + range 位宽与符号 + zext 扩展

- 跨模块 declare 形参按 vpi 定义侧收敛(修 _vpi_method_param_annot 拒绝 cls_name=None 导致 coerce 恒失效)
This commit is contained in:
TermiNexus committed 2026-09-11 14:26:54 +08:00
1 parent bd46b47b22
commit e20a6f3d11
23 files changed
+655 -101

No files matched your search

+22 -6
View File
@@ -337,9 +337,22 @@ def _normalize_path(path: str) -> str:
# 单段 ".":跳过
if seg_len == 1 and path[seg_start] == '.':
pass
# 单段 "..":弹出上一段(无段可弹时保留,如 ../includes)
# 单段 "..":弹出上一段;但**前导 ".." 不可弹**,必须保留。
# 旧实现只判 out_len > 0 就回退,于是 "无段可弹时保留" 的那一个
# 前导 ".." 会被下一个 ".." 当成普通段吃掉:
# ./VKernel/../../../includes → 缓冲反复清空 → "./includes"
# (cwd 在项目目录时,语义应得 "../../includes")。这正是
# ViperOS 各子工程 import 找不到模块的根因。
# 与 Python os.path.normpath / TPC 的行为对齐。
elif seg_len == 2 and path[seg_start] == '.' and path[seg_start + 1] == '.':
if out_len > 0:
# 末段是否已经是前导 ".."(独立一段:要么是开头,要么前面是分隔符)
last_is_dotdot: int = 0
if out_len >= 2 and out[out_len - 1] == '.' and out[out_len - 2] == '.':
if out_len == 2:
last_is_dotdot = 1
elif out[out_len - 3] == '/' or out[out_len - 3] == '\\':
last_is_dotdot = 1
if out_len > 0 and last_is_dotdot == 0:
# 回退到上一个分隔符
k: t.CSizeT = out_len
while k > 0 and out[k - 1] != '/' and out[k - 1] != '\\':
@@ -354,10 +367,13 @@ def _normalize_path(path: str) -> str:
else:
has_seg = 0
else:
# 无段可弹:保留 ..
out[0] = '.'
out[1] = '.'
out_len = 2
# 没有普通段可弹:把 ".." 当普通段追加(前导 .. 累积保留)
if has_seg > 0:
out[out_len] = '/'
out_len += 1
out[out_len] = '.'
out[out_len + 1] = '.'
out_len += 2
has_seg = 1
else:
# 普通段:追加分隔符 + 段
+16
View File
@@ -914,6 +914,14 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy | t.CPtr, tree: ast.AST | t.CPtr,
if ca_cv.const_kind == ast.CONST_INT and num_buf_c is not None:
viperlib.snprintf(num_buf_c, 32, " = %lld", ca_cv.int_val)
pos = _PyiEmit(buf, buf_size, pos, num_buf_c)
elif ca_cv.const_kind == ast.CONST_FLOAT:
# Call 包装的浮点常量(如 PI: t.CDefine = float(3.14159...)):
# stub 必须输出值,否则读回端 vpi 无值 → 退化 0.0。
# %.17g 保证 double 全精度往返(与上方 CONST_FLOAT 分支一致)。
fbuf_cc: bytes = mb.alloc(64)
if fbuf_cc is not None:
viperlib.snprintf(fbuf_cc, 64, " = %.17g", ca_cv.float_val)
pos = _PyiEmit(buf, buf_size, pos, fbuf_cc)
elif arg_kind == ast.ASTKind.UnaryOp:
uop_ca: ast.UnaryOp | t.CPtr = (ast.UnaryOp | t.CPtr)(first_arg)
num_buf_cu: bytes = mb.alloc(32)
@@ -924,6 +932,14 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy | t.CPtr, tree: ast.AST | t.CPtr,
neg_c_val: t.CInt64T = -uoc.int_val
viperlib.snprintf(num_buf_cu, 32, " = %lld", neg_c_val)
pos = _PyiEmit(buf, buf_size, pos, num_buf_cu)
elif uoc.const_kind == ast.CONST_FLOAT:
# 负浮点 Call 包装(如 t.CDefine = float(-1.5)):
# 同 CONST_FLOAT,%.17g 全精度往返
neg_cf: double = -uoc.float_val
fbuf_cu: bytes = mb.alloc(64)
if fbuf_cu is not None:
viperlib.snprintf(fbuf_cu, 64, " = %.17g", neg_cf)
pos = _PyiEmit(buf, buf_size, pos, fbuf_cu)
elif arg_kind == ast.ASTKind.BinOp:
# Call 内部的 BinOp(如 t.CUnsignedLong(A | B))
# 递归计算 BinOp 值
+116 -25
View File
@@ -297,6 +297,22 @@ class AssignGenMixin:
eq += 1
while eq < line_start + line_len and buf[eq] == ' ':
eq += 1
# Call 包装的浮点常量(如 PI: t.CDefine = float(3.14159...) /
# t.CDouble(1.0)):值的 token 是整段 "float(3.14159...)"。
# atof 只认数字/符号/'.',首字符 'f' → 恒得 0.0,导致浮点 CDefine
# 被注册为 0(gfx.vp PI 编成 0.0 的直接根因)。此处跳过 "float("
# 之类前缀,从括号内首个数字/符号/'.' 起取值。
# 裸字面量(3.14 / -1.5,首字符已是数字/符号/'.')不受影响。
if eq < line_start + line_len:
c0_rf: t.CChar = buf[eq]
if not ((c0_rf >= '0' and c0_rf <= '9') or c0_rf == '-' or c0_rf == '+' or c0_rf == '.'):
pfx_rf: t.CSizeT = eq
while pfx_rf < line_start + line_len:
cp_rf: t.CChar = buf[pfx_rf]
if (cp_rf >= '0' and cp_rf <= '9') or cp_rf == '.' or cp_rf == '-' or cp_rf == '+':
eq = pfx_rf
break
pfx_rf += 1
val_start: t.CSizeT = eq
while eq < line_start + line_len and buf[eq] != ' ' and buf[eq] != '\t' \
and buf[eq] != '#' and buf[eq] != '\r' and buf[eq] != '\n':
@@ -623,11 +639,13 @@ class AssignGenMixin:
if arr_ty is None:
return 1
# 匹配数组类型获取元素类型
# 匹配数组类型获取元素类型与声明长度
elem_ty: llvmlite.LLVMType | t.CPtr = None
arr_count: int = 0
match arr_ty:
case llvmlite.LLVMType.Array(et, _):
case llvmlite.LLVMType.Array(et, cnt):
elem_ty = et
arr_count = cnt
if elem_ty is None:
return 1
@@ -638,6 +656,42 @@ class AssignGenMixin:
elts: list[ast.AST | t.CPtr] | t.CPtr = lst.elts
elts_count: t.CSizeT = len(elts)
# 特例:单个字符串字面量初始化字符数组([N x i8],N = strlen+1)
# 逐字节写入字符串内容;调用方已先 store zeroinitializer,末字节 NUL 由零保持。
if self.Gen.ExprGenBase.get_llvm_type_bits(elem_ty) == 8 and elts_count == 1 and arr_count >= 1:
e0_s: ast.AST | t.CPtr = elts.get(0)
if e0_s is not None and e0_s.kind() == ast.ASTKind.Constant:
c0_s: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(e0_s)
if c0_s.const_kind == ast.CONST_STR:
sv_s: str = ast.constant_get_str_val(c0_s)
if sv_s is not None:
n_s: t.CSizeT = string.strlen(sv_s)
if t.CSizeT(arr_count) < n_s:
self.Gen.TypeGenErr.fatal_error(
list_node,
f"字符串字面量长度 {n_s} 超过字符数组声明长度 {arr_count},不允许越界写入",
"OverflowError")
bi_s: t.CSizeT = 0
while bi_s < n_s:
bidx_s: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, bi_s)
bp_s: llvmlite.Value | t.CPtr = llvmlite.build_gep_array(
builder, arr_ty, elem_ty, array_alloca, bidx_s)
if bp_s is None:
break
bvv: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, sv_s[bi_s])
bsv: llvmlite.Value | t.CPtr = self.Gen.ExprGenBase.coerce_to_type(
builder, bvv, elem_ty, e0_s)
llvmlite.build_store(builder, bsv, bp_s)
bi_s += 1
return 0
# 元素数超过声明长度 → fail-fast(否则会生成 idx>=N 的越界 GEP 写)
if arr_count > 0 and t.CSizeT(arr_count) < elts_count:
self.Gen.TypeGenErr.fatal_error(
list_node,
f"数组初始化元素数 {elts_count} 超过数组声明长度 {arr_count},不允许越界写入",
"OverflowError")
# 为每个元素生成 GEP + store
for i in range(elts_count):
elem_node: ast.AST | t.CPtr = elts.get(i)
@@ -721,16 +775,30 @@ class AssignGenMixin:
# 整数运算:类型提升
if lhs_bits > rhs_bits and rhs_bits > 0:
rhs = self.Gen.ExprGenBase.coerce_to_type(builder, rhs, lhs.Ty, node)
rhs = self.Gen.ExprGenBase.coerce_to_type(builder, rhs, lhs.Ty, node, trans)
elif rhs_bits > lhs_bits and lhs_bits > 0:
lhs = self.Gen.ExprGenBase.coerce_to_type(builder, lhs, rhs.Ty, node)
lhs = self.Gen.ExprGenBase.coerce_to_type(builder, lhs, rhs.Ty, node, trans)
# 无符号操作数(如 u_num_l: t.CUInt64T 的 //= / %=)→ udiv/urem。
# 此前一律 sdiv/srem(translate_binop 的 Div/Mod 已修,但 AugAssign 走本
# 函数独立发射 → viperlib %lu/%lx 的 //= 仍为 sdiv,T3 未覆盖根因)。
aug_unsigned: int = 0
if lhs_node is not None:
if self.Gen.ExprGenOps._node_type_unsigned(pool, lhs_node, trans) != 0:
aug_unsigned = 1
if op == ast.OpKind.Add: return llvmlite.build_add(builder, lhs, rhs)
elif op == ast.OpKind.Sub: return llvmlite.build_sub(builder, lhs, rhs)
elif op == ast.OpKind.Mult: return llvmlite.build_mul(builder, lhs, rhs)
elif op == ast.OpKind.Div: return llvmlite.build_sdiv(builder, lhs, rhs)
elif op == ast.OpKind.FloorDiv: return llvmlite.build_sdiv(builder, lhs, rhs)
elif op == ast.OpKind.Mod: return llvmlite.build_srem(builder, lhs, rhs)
elif op == ast.OpKind.Div:
if aug_unsigned != 0: return llvmlite.build_udiv(builder, lhs, rhs)
return llvmlite.build_sdiv(builder, lhs, rhs)
elif op == ast.OpKind.FloorDiv:
if aug_unsigned != 0: return llvmlite.build_udiv(builder, lhs, rhs)
return llvmlite.build_sdiv(builder, lhs, rhs)
elif op == ast.OpKind.Mod:
if aug_unsigned != 0: return llvmlite.build_urem(builder, lhs, rhs)
return llvmlite.build_srem(builder, lhs, rhs)
elif op == ast.OpKind.BitAnd: return llvmlite.build_and(builder, lhs, rhs)
elif op == ast.OpKind.BitOr: return llvmlite.build_or(builder, lhs, rhs)
elif op == ast.OpKind.BitXor: return llvmlite.build_xor(builder, lhs, rhs)
@@ -1041,6 +1109,17 @@ class AssignGenMixin:
cnt_arr: int = 0
if lst_arr.elts is not None:
cnt_arr = len(lst_arr.elts)
# 特例:单个字符串字面量(如 hex_digits: t.CArray[t.CChar, None] = ["0123.."])
# 语义是"以字符串内容初始化字符数组",长度应取 strlen+1(含结尾 NUL),
# 而不是列表元素个数 1——否则 alloca [1 x i8] 且只写首字节 → 下标 0..15 越界读。
if elem_ty_arr is not None and cnt_arr == 1 and lst_arr.elts is not None:
e0_arr: ast.AST | t.CPtr = lst_arr.elts.get(0)
if e0_arr is not None and e0_arr.kind() == ast.ASTKind.Constant:
c0_arr: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(e0_arr)
if c0_arr.const_kind == ast.CONST_STR:
sv0_arr: str = ast.constant_get_str_val(c0_arr)
if sv0_arr is not None and self.Gen.ExprGenBase.get_llvm_type_bits(elem_ty_arr) == 8:
return llvmlite.Array(pool, elem_ty_arr, int(string.strlen(sv0_arr)) + 1)
if elem_ty_arr is not None and cnt_arr > 0:
return llvmlite.Array(pool, elem_ty_arr, cnt_arr)
return resolved_ty
@@ -1145,26 +1224,38 @@ class AssignGenMixin:
var_ty: llvmlite.LLVMType | t.CPtr = None
annot: ast.AST | t.CPtr = aa.annotation
if annot is not None:
# 泛型特化补救(在解析之前无条件执行,_specialize_generic_class 幂等):
# 注解可能是 list[X](Subscript)或 list[X] | t.CPtr(含 CPtr 的 BinOp)。
# 若泛型类尚未特化,resolve_annotation_type 会把 list[X] 退化为不透明
# 基类 list*(丢掉元素类型)——PreScan 预建的 alloca 因此变成 list*,
# 之后 body_list.get(i) 会分派到默认 list[int] 特化(返回 i32)→
# inttoptr i32→AST* 截断指针 → StmtGenMixin._translate_stmt_impl
# 虚调用读 vtable 崩溃。故先提取 Subscript 一侧主动特化再解析,保证
# PreScan 与 Handle 两处解析结果一致。
spec_annot: ast.Subscript | t.CPtr = None
if annot.kind() == ast.ASTKind.Subscript:
spec_annot = (ast.Subscript | t.CPtr)(annot)
elif annot.kind() == ast.ASTKind.BinOp:
bop_ps: ast.BinOp | t.CPtr = (ast.BinOp | t.CPtr)(annot)
if bop_ps.op == ast.OpKind.BitOr:
if bop_ps.left is not None and bop_ps.left.kind() == ast.ASTKind.Subscript:
spec_annot = (ast.Subscript | t.CPtr)(bop_ps.left)
elif bop_ps.right is not None and bop_ps.right.kind() == ast.ASTKind.Subscript:
spec_annot = (ast.Subscript | t.CPtr)(bop_ps.right)
if spec_annot is not None:
sv_ps: ast.AST | t.CPtr = spec_annot.value
if sv_ps is not None and sv_ps.kind() == ast.ASTKind.Name:
sub_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(sv_ps)
if sub_nm.id is not None:
template_cd: ast.ClassDef | t.CPtr = self.Gen.ClassGenGeneric._find_generic_template(sub_nm.id)
if template_cd is not None:
type_args_ps: list[str] | t.CPtr = self.Gen.CallGenCtor._extract_type_args_from_slice(
pool, spec_annot.slice, trans._from_imports)
if type_args_ps is not None and len(type_args_ps) > 0:
self.Gen.ClassGenGeneric._specialize_generic_class(
trans, sub_nm.id, type_args_ps)
resolved: llvmlite.LLVMType | t.CPtr = self.Gen.TypeGenResolve.resolve_annotation_type(
pool, annot, trans._imported_modules, trans._from_imports, trans, 0)
if resolved is None:
# 尝试特化泛型类注解(如 list[str])
# resolve_annotation_type 返回 None 可能是因为泛型类未特化
if annot.kind() == ast.ASTKind.Subscript:
sub_annot: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(annot)
if sub_annot.value is not None and sub_annot.value.kind() == ast.ASTKind.Name:
sub_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(sub_annot.value)
if sub_nm.id is not None:
template_cd: ast.ClassDef | t.CPtr = self.Gen.ClassGenGeneric._find_generic_template(sub_nm.id)
spec_name_ps: str = None
if template_cd is not None:
type_args_ps: list[str] | t.CPtr = self.Gen.CallGenCtor._extract_type_args_from_slice(pool, sub_annot.slice, trans._from_imports)
if type_args_ps is not None and len(type_args_ps) > 0:
spec_name_ps = self.Gen.ClassGenGeneric._specialize_generic_class(
trans, sub_nm.id, type_args_ps)
if spec_name_ps is not None:
resolved = self.Gen.TypeGenResolve.resolve_annotation_type(
pool, annot, trans._imported_modules, trans._from_imports, trans, 0)
if resolved is not None:
var_ty = resolved
# 单参 t.CArray[elem_ty] + 列表字面量 → 静态数组(与 Handle 推断一致)。
+4
View File
@@ -692,6 +692,10 @@ class CallGenBase:
# {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
+34 -5
View File
@@ -241,11 +241,40 @@ class CallGenCBuiltin(CallGenBase.CallGenBase):
ptr_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, d_args.get(0), None, 0, trans)
if ptr_val is not None:
val_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, b_val.Ty)
casted_ptr: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(
builder, ptr_val, val_ptr_ty)
if casted_ptr is not None:
llvmlite.build_store(builder, b_val, casted_ptr)
# 写入宽度以「左值指针自身的元素类型」为准:
# c.Set(c.Deref(<T*>(p)), v) 应先把 v 转换到 T,再按 T 宽度写入。
# 此前用 Ptr(b_val.Ty) 重解释指针并按 RHS 宽度 store,导致
# c.Deref(uint16(p)) 被 i32 字面量写成 4 字节(越界 2 字节)、
# c.Deref(uint32(p)) 被 0xFFFFFFFF(i64) 写成 8 字节(越界 4 字节)。
# 无类型指针(元素为 i8,如 t.CPtr/i8* 形参)维持原行为:
# 按 RHS 类型重解释指针 store(如 paging save_count 写 i32)。
store_ptr: llvmlite.Value | t.CPtr = ptr_val
store_val: llvmlite.Value | t.CPtr = b_val
typed_elem: int = 0
elem_ty: llvmlite.LLVMType | t.CPtr = None
if ptr_val.Ty is not None:
elem_ty = ptr_val.Ty.Pointee
if elem_ty is not None:
eb: int = self.Gen.ExprGenBase.get_llvm_type_bits(elem_ty)
if eb != 0 and eb != 8:
typed_elem = 1
elif self.Gen.ExprGenBase.get_llvm_float_bits(elem_ty) != 0:
typed_elem = 1
elif self.Gen.ExprGenBase.is_ptr_type(elem_ty) != 0:
typed_elem = 1
elif self.Gen.ExprGenBase.is_array_type(elem_ty) != 0:
typed_elem = 1
if typed_elem != 0:
store_val = self.Gen.ExprGenBase.coerce_to_type(
builder, b_val, elem_ty, a_node)
else:
val_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, b_val.Ty)
casted_ptr: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(
builder, ptr_val, val_ptr_ty)
if casted_ptr is not None:
store_ptr = casted_ptr
if store_ptr is not None and store_val is not None:
llvmlite.build_store(builder, store_val, store_ptr)
return b_val
# 其他 Call 形态不可作左值
return b_val
+8 -2
View File
@@ -1644,6 +1644,9 @@ class CallGenCall(CallGenBase.CallGenBase):
obj_ptr)
# 跨模块调用:创建 declare 声明,避免 llc 报 undefined value
# 先按 vpi 定义侧形参 coerce 实参(宽度对齐),再建 declare(ABI 通用化)
head = self.Gen.CallGenLib._coerce_args_to_def_params(
pool, builder, call_name, head, total_count)
self._ensure_method_declare(pool, mod, call_name, call_ret_ty, head, total_count)
# 检查变长函数 + C ABI 提升(cargs 是实参 AST,self_offset=1 跳过 self)
is_variadic_3441: int = self.Gen.CallGenVa._check_and_promote_variadic_call(
@@ -2136,6 +2139,9 @@ class CallGenCall(CallGenBase.CallGenBase):
match entry.Ty:
case llvmlite.LLVMType.Struct(fields, fcount, name):
offset_fb: int = 0
# packed(@c.Attribute(t.attr.packed))结构体无字段填充:
# 偏移 = 前序字段尺寸之和(LLVM packed struct 布局)。
is_packed_fb: int = entry.IsPacked
cur_fb: llvmlite.ParamNode | t.CPtr = fields
i_fb: int = 0
while cur_fb is not None and i_fb < field_index:
@@ -2143,7 +2149,7 @@ class CallGenCall(CallGenBase.CallGenBase):
fty_fb: llvmlite.LLVMType | t.CPtr = (llvmlite.LLVMType | t.CPtr)(cur_fb.Ty)
fsize_fb: int = self.Gen.ClassGenEnum._get_type_size(fty_fb)
falign_fb: int = self.Gen.ClassGenEnum._get_type_align(fty_fb)
if falign_fb > 1:
if is_packed_fb == 0 and falign_fb > 1:
rem_fb: int = offset_fb % falign_fb
if rem_fb != 0:
offset_fb += falign_fb - rem_fb
@@ -2154,7 +2160,7 @@ class CallGenCall(CallGenBase.CallGenBase):
if cur_fb is not None and cur_fb.Ty is not None:
fty2_fb: llvmlite.LLVMType | t.CPtr = (llvmlite.LLVMType | t.CPtr)(cur_fb.Ty)
falign2_fb: int = self.Gen.ClassGenEnum._get_type_align(fty2_fb)
if falign2_fb > 1:
if is_packed_fb == 0 and falign2_fb > 1:
rem2_fb: int = offset_fb % falign2_fb
if rem2_fb != 0:
offset_fb += falign2_fb - rem2_fb
+4 -4
View File
@@ -122,7 +122,7 @@ class CallGenCtor(CallGenBase.CallGenBase):
# 创建临时 alloca
# 确保跨模块结构体的完整定义在当前模块中可用(供 alloca 分配空间)
self.Gen.StructGen.ensure_struct_def_in_module(pool, mod, struct_ty)
tmp: llvmlite.Value | t.CPtr = llvmlite.build_alloca(builder, struct_ty)
tmp: llvmlite.Value | t.CPtr = self.Gen.VarGen._alloca_at_entry(builder, struct_ty)
if tmp is None:
return llvmlite.ConstZero(pool, struct_ty)
# 诊断节点:取第一个构造实参(有行号),供 _call_method_on_ptr 的
@@ -397,7 +397,7 @@ class CallGenCtor(CallGenBase.CallGenBase):
# alloca REnum 类型
# 确保跨模块结构体的完整定义在当前模块中可用(供 alloca 分配空间)
self.Gen.StructGen.ensure_struct_def_in_module(pool, mod, renum_ty)
tmp: llvmlite.Value | t.CPtr = llvmlite.build_alloca(builder, renum_ty)
tmp: llvmlite.Value | t.CPtr = self.Gen.VarGen._alloca_at_entry(builder, renum_ty)
if tmp is None:
return llvmlite.ConstZero(pool, renum_ty)
@@ -476,7 +476,7 @@ class CallGenCtor(CallGenBase.CallGenBase):
# 创建临时 alloca
# 确保跨模块结构体的完整定义在当前模块中可用(供 alloca 分配空间)
self.Gen.StructGen.ensure_struct_def_in_module(pool, mod, struct_ty)
tmp: llvmlite.Value | t.CPtr = llvmlite.build_alloca(builder, struct_ty)
tmp: llvmlite.Value | t.CPtr = self.Gen.VarGen._alloca_at_entry(builder, struct_ty)
if tmp is None:
return llvmlite.ConstZero(pool, struct_ty)
# 诊断节点:取第一个构造实参(有行号),供 _call_method_on_ptr 的
@@ -928,7 +928,7 @@ class CallGenCtor(CallGenBase.CallGenBase):
self.Gen.StructGen.ensure_struct_def_in_module(pool, mod, struct_ty)
# alloca tmp(self 指针)
tmp: llvmlite.Value | t.CPtr = llvmlite.build_alloca(builder, struct_ty)
tmp: llvmlite.Value | t.CPtr = self.Gen.VarGen._alloca_at_entry(builder, struct_ty)
if tmp is None:
return None
+3
View File
@@ -1432,6 +1432,9 @@ class CallGenDispatch(CallGenBase.CallGenBase):
# 依赖未就绪:保持默认 i32(build_call(Void)
# 返回 None 破坏表达式翻译;本遍不写盘)
ret_ty3 = llvmlite.Int32(pool)
# 先按 vpi 定义侧形参 coerce 实参(宽度对齐),再建 declare
head3 = self.Gen.CallGenLib._coerce_args_to_def_params(
pool, builder, mangled3, head3, total3)
self._ensure_method_declare(pool, mod, mangled3, ret_ty3, head3, total3)
return llvmlite.build_call(builder, mangled3, head3, total3, ret_ty3, 0)
else:
+34 -9
View File
@@ -121,8 +121,8 @@ class CallGenLeafKit(CallGenCBuiltin.CallGenCBuiltin):
msg_ptr = trans._eh_msg_out_param
code_ptr = trans._eh_code_out_param
if msg_ptr is None:
msg_ptr = llvmlite.build_alloca(builder, i8_ptr_ty_g)
code_ptr = llvmlite.build_alloca(builder, i32_ty_g)
msg_ptr = self.Gen.VarGen._alloca_at_entry(builder, i8_ptr_ty_g)
code_ptr = self.Gen.VarGen._alloca_at_entry(builder, i32_ty_g)
# ---- 2. 调用前清零 ----
null_msg_g: llvmlite.Value | t.CPtr = llvmlite.ConstNull(pool, i8_ptr_ty_g, "null")
zero_code_g: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, 0)
@@ -1182,8 +1182,14 @@ class CallGenLeafKit(CallGenCBuiltin.CallGenCBuiltin):
return 1
if self._init_global_defaults_table() == 0:
return 1
# 已存在则跳过
if self.find_func_defaults(name) is not None:
# 当前模块 SHA1 作为限定键:同一函数名在不同模块各存一条,
# 查询时按「名 + 定义模块」精确匹配,消除跨模块同名串味
# (usb.uhci.init 1 参误命中 mm.init 5 参/2 默认值根因)。
cur_sha: str = None
if self.Gen is not None and self.Gen.TypeGenBase is not None:
cur_sha = self.Gen.TypeGenBase.CurrentModuleSha1
# 同模块同名已存在则跳过
if self.find_func_defaults(name, cur_sha) is not None:
return 0
if self.GlobalDefaultsCount >= GLOBAL_DEFAULTS_MAX:
return 1
@@ -1197,6 +1203,13 @@ class CallGenLeafKit(CallGenCBuiltin.CallGenCBuiltin):
return 1
string.strcpy(nm_copy, name)
entry.Name = nm_copy
entry.ModuleSha1 = None
if cur_sha is not None:
sh_len: t.CSizeT = string.strlen(cur_sha)
sh_copy: str = CompilerFactory.factory.PermPool.alloc(sh_len + 1)
if sh_copy is not None:
string.strcpy(sh_copy, cur_sha)
entry.ModuleSha1 = sh_copy
entry.Defaults = defaults
entry.DefaultCount = default_count
entry.ParamCount = param_count
@@ -1269,17 +1282,29 @@ class CallGenLeafKit(CallGenCBuiltin.CallGenCBuiltin):
return 0
def find_func_defaults(self, name: str) -> GenTypes.FuncDefaultsEntry | t.CPtr:
"""按函数名查找默认参数信息,返回 FuncDefaultsEntry 或 None"""
def find_func_defaults(self, name: str,
module_sha1: str = None) -> GenTypes.FuncDefaultsEntry | t.CPtr:
"""按函数名(可选按定义模块 SHA1)查找默认参数信息,返回 FuncDefaultsEntry 或 None
module_sha1 非 None 时要求条目定义模块完全一致(限定名匹配,消除跨模块同名串味);
为 None 时退化为按名匹配(兼容旧调用点)。
"""
if name is None or self.GlobalDefaults is None:
return None
entry_size: t.CSizeT = GenTypes.FuncDefaultsEntry.__sizeof__()
for i in range(self.GlobalDefaultsCount):
entry_addr: t.CUInt64T = t.CUInt64T(self.GlobalDefaults) + i * entry_size
entry: GenTypes.FuncDefaultsEntry | t.CPtr = (GenTypes.FuncDefaultsEntry | t.CPtr)(t.CVoid(entry_addr, t.CPtr))
if entry.Name is not None:
if string.strcmp(entry.Name, name) == 0:
return entry
if entry.Name is None:
continue
if string.strcmp(entry.Name, name) != 0:
continue
if module_sha1 is not None:
if entry.ModuleSha1 is None:
continue
if string.strcmp(entry.ModuleSha1, module_sha1) != 0:
continue
return entry
return None
+159 -14
View File
@@ -4,6 +4,7 @@ import ast
import llvmlite
import memhub
import string
import stdlib
import lib.core.Gen.Translator as HT
from . import CallGenBase
@@ -130,21 +131,25 @@ class CallGenLib(CallGenBase.CallGenBase):
# 仅裸名(无 '.' 分隔 → 无 sha1 前缀);带前缀走 vpi 定义侧解析
if string.strchr(call_name, 46) is not None:
return head
# 白名单:第三参 size_t(i64) 的函数(与 _ensure_c_lib_declare 一致)
is_3rd_i64: int = 0
if string.strcmp(call_name, "memset") == 0: is_3rd_i64 = 1
elif string.strcmp(call_name, "memcpy") == 0: is_3rd_i64 = 1
elif string.strcmp(call_name, "memmove") == 0: is_3rd_i64 = 1
elif string.strcmp(call_name, "memcmp") == 0: is_3rd_i64 = 1
elif string.strcmp(call_name, "strncmp") == 0: is_3rd_i64 = 1
elif string.strcmp(call_name, "strncpy") == 0: is_3rd_i64 = 1
elif string.strcmp(call_name, "strncat") == 0: is_3rd_i64 = 1
if is_3rd_i64 == 0 or count < 3:
# 白名单:size_t(i64) 长度参数所在下标(与 _ensure_c_lib_declare 一致)。
# vsnprintf 非变参(第二参即 t.CSizeT 长度)——viperlib.vp `export def
# vsnprintf(buf, size: t.CSizeT, fmt, ap)`,跨模块裸名调用时字面量 1024
# 固化 i32 vs 定义 i64,与 memset 同族。
size_idx: int = -1
if string.strcmp(call_name, "memset") == 0: size_idx = 2
elif string.strcmp(call_name, "memcpy") == 0: size_idx = 2
elif string.strcmp(call_name, "memmove") == 0: size_idx = 2
elif string.strcmp(call_name, "memcmp") == 0: size_idx = 2
elif string.strcmp(call_name, "strncmp") == 0: size_idx = 2
elif string.strcmp(call_name, "strncpy") == 0: size_idx = 2
elif string.strcmp(call_name, "strncat") == 0: size_idx = 2
elif string.strcmp(call_name, "vsnprintf") == 0: size_idx = 1
if size_idx < 0 or count <= size_idx:
return head
# 定位第三参(index 2)
# 定位长度参数(index size_idx)
cur: llvmlite.Value | t.CPtr = head
idx: int = 0
while cur is not None and idx < 2:
while cur is not None and idx < size_idx:
cur = cur.Next
idx += 1
if cur is None or cur.Ty is None:
@@ -165,14 +170,14 @@ class CallGenLib(CallGenBase.CallGenBase):
return head
if promoted is None:
return head
# 重建实参链表:index 2 替换为提升后的值
# 重建实参链表:index size_idx 替换为提升后的值
new_head: llvmlite.Value | t.CPtr = None
new_tail: llvmlite.Value | t.CPtr = None
cur2: llvmlite.Value | t.CPtr = head
i2: int = 0
while cur2 is not None:
node_v: llvmlite.Value | t.CPtr = cur2
if i2 == 2:
if i2 == size_idx:
node_v = promoted
if new_head is None:
new_head = node_v
@@ -183,6 +188,146 @@ class CallGenLib(CallGenBase.CallGenBase):
i2 += 1
return new_head
# ============================================================
# _coerce_args_to_def_params - 跨模块调用实参按定义侧形参类型 coerce
#
# B3 同族通用化:_ensure_method_declare 用调用点实参类型建 declare,字面量
# 实参(i32 0/1016/512)把定义侧窄整型形参(i8/i16)固化宽(或把宽形参固化
# 窄)→ declare 与 define 宽度不一致(跨模块 IR/ABI 失配,x86-64 下侥幸正确:
# 调用侧写 32 位寄存器清高 32 位,callee 按 i64 读高位是 0)。本函数在调用点
# 按定义侧(vpi 参数注解)重建/转换实参,随后 _ensure_method_declare 依 head
# 建 declare → call/declare/define 三者一致。
#
# 仅处理"带 sha1 前缀的模块级函数调用"({sha1}.{func},2 段):该形态下实参
# 下标与 vpi 参数注解段下标一一对应(段 0 即首参,无 self 占位)。类方法
# ({sha1}.{cls}.{method},3 段)含 self 偏移,交由既有路径(CallGenCall/stub
# 注入)处理,不在本条通用化范围内。裸名 C 库函数由 _promote_clib_call_args
# 与 _ensure_c_lib_declare 标准签名处理。未命中/不可解原样保留(不兜底改型)。
# ============================================================
def _coerce_args_to_def_params(self, pool: memhub.MemBuddy | t.CPtr,
builder: llvmlite.IRBuilder | t.CPtr,
call_name: str,
head: llvmlite.Value | t.CPtr,
count: int) -> llvmlite.Value | t.CPtr:
"""把跨模块模块级函数实参 coerce 到 vpi 定义侧形参类型,返回重建的实参链"""
if pool is None or builder is None or call_name is None or head is None:
return head
# 函数内懒 import:与 _lookup_method_ret_ty_via_vpi 同源,规避顶层循环依赖
import lib.Stub.StubGen.VpiReader as VpiReader
if self._is_sha1_mangled_call(call_name) == 0:
return head
# 仅 {sha1}.{func}(2 段);3 段(含类名/self)跳过
rest: str = call_name + 17
if string.strchr(rest, 46) is not None:
return head
meth_nm: str = rest
if meth_nm is None or meth_nm[0] == '\0':
return head
sha1_buf: bytes = stdlib.malloc(17)
if sha1_buf is None:
return head
string.strncpy(sha1_buf, call_name, 16)
sha1_buf[16] = '\0'
# 注意:不能用 self._vpi_method_param_annot —— 其形参校验含
# `cls_name is None → return None`,而模块级函数必传 cls_name=None,
# 于是恒返回 None → 本函数永不生效(ABI 收敛首轮/二轮均未生效的根因)。
# 改为直接 ensure 加载 + 直查缓存(键 {sha1}.{func},与 cls_name=None 匹配)。
if self.Gen.CallGenBase.TempDir is not None and self.Gen.CallGenBase.TempDirLen > 0:
VpiReader._ensure_vpi_param_annot_loaded(sha1_buf, self.Gen.CallGenBase.TempDir)
prm_annots: str = VpiReader._lookup_vpi_param_annots(sha1_buf, None, meth_nm)
if prm_annots is None:
stdlib.free(sha1_buf)
return head
i32_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, llvmlite.Int8(pool))
new_head: llvmlite.Value | t.CPtr = None
new_tail: llvmlite.Value | t.CPtr = None
cur: llvmlite.Value | t.CPtr = head
idx: int = 0
while cur is not None and idx < count:
node_v: llvmlite.Value | t.CPtr = cur
tgt_ty: llvmlite.LLVMType | t.CPtr = VpiReader._resolve_vpi_param_annot_seg(
pool, sha1_buf, None, meth_nm, idx, i32_ty, i8_ptr_ty)
if tgt_ty is not None and cur.Ty is not None:
node_v = self._coerce_arg_value_to_ty(pool, builder, cur, tgt_ty)
if new_head is None:
new_head = node_v
else:
new_tail.Next = node_v
new_tail = node_v
cur = cur.Next
idx += 1
stdlib.free(sha1_buf)
return new_head
# ============================================================
# _coerce_arg_value_to_ty - 单个实参 Value 转换到目标形参类型
#
# 整型:
# - 同宽 → 原样;
# - 窄化(目标更窄,本簇主场景 i32→i8/i16)→ 常量按目标宽度重建常量,
# SSA 用 trunc(trunc 与符号无关,窄化安全);
# - 扩宽交由 _ensure_method_declare 既有"定义侧更宽才采用"逻辑 + 下遍
# 收敛时按形参 coerce(需 AST 判定符号性,本函数无 AST,不越权处理)。
# 指针:实参与目标皆指针且"泛型 i8* 性"不同时 bitcast(如 i32*→i8*),
# 使 declare 指针类型与 define 一致;同为 i8* 或同为具体 struct 指针
# 则原样保留(避免把 list*/Struct* 无谓降级为 i8* 的 IR 噪声)。
# 其它(结构体值/void 等)→ 原样保留。不可转换亦原样返回(不兜底改型)。
# ============================================================
def _coerce_arg_value_to_ty(self, pool: memhub.MemBuddy | t.CPtr,
builder: llvmlite.IRBuilder | t.CPtr,
val: llvmlite.Value | t.CPtr,
tgt_ty: llvmlite.LLVMType | t.CPtr) -> llvmlite.Value | t.CPtr:
"""把单个实参 Value 转换到目标形参类型,无法安全转换时原样返回"""
if pool is None or builder is None or val is None or tgt_ty is None or val.Ty is None:
return val
match val.Ty:
case llvmlite.LLVMType.Int(bits_a):
match tgt_ty:
case llvmlite.LLVMType.Int(bits_b):
if bits_b == bits_a:
return val
if bits_b < bits_a:
if val.IsConst != 0:
nv_c: llvmlite.Value | t.CPtr = llvmlite.const_int(
pool, bits_b, val.IntVal)
if nv_c is not None:
return nv_c
else:
nv_t: llvmlite.Value | t.CPtr = llvmlite.build_trunc(
builder, val, tgt_ty)
if nv_t is not None:
return nv_t
case _:
pass
case llvmlite.LLVMType.Ptr(pointee_a):
match tgt_ty:
case llvmlite.LLVMType.Ptr(pointee_b):
ia: int = 0
match pointee_a:
case llvmlite.LLVMType.Int(pa_bits):
if pa_bits == 8:
ia = 1
case _:
pass
ib: int = 0
match pointee_b:
case llvmlite.LLVMType.Int(pb_bits):
if pb_bits == 8:
ib = 1
case _:
pass
if ia != ib:
nv_p: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(
builder, val, tgt_ty)
if nv_p is not None:
return nv_p
case _:
pass
case _:
pass
return val
def _infer_external_func_ret_ty(self, pool: memhub.MemBuddy | t.CPtr,
func_name: str,
node: ast.AST | t.CPtr,
+39 -2
View File
@@ -947,7 +947,7 @@ class CallGenMixin(CallGenBase.CallGenBase):
# alloca 暂存
i8_ty_nl: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
i8_ptr_ty_nl: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty_nl)
tmp_alloca_nl: llvmlite.Value | t.CPtr = llvmlite.build_alloca(
tmp_alloca_nl: llvmlite.Value | t.CPtr = self.Gen.VarGen._alloca_at_entry(
builder, i8_ptr_ty_nl)
if tmp_alloca_nl is not None:
llvmlite.build_store(builder, fp_alloca, tmp_alloca_nl)
@@ -1104,11 +1104,42 @@ class CallGenMixin(CallGenBase.CallGenBase):
# 快照才是跨文件永久存活。同模块命中时 gd_entry 也必须已赋值,
# 否则下方 _build_const_from_snapshot 读到未初始化栈垃圾指针崩溃。
# 与 _translate_method_call 的 mdf_gd 无条件查询(L2677)保持一致。
gd_entry: GenTypes.FuncDefaultsEntry | t.CPtr = self.Gen.CallGenLeafKit.find_func_defaults(func_name)
# 计算被调模块 SHA1 作限定键(消除跨模块同名函数串味):
# module.func(...) → 该 module 的 SHA1;裸名调用 → 当前模块 SHA1。
# 全局默认参数表按「名 + 定义模块 SHA1」精确匹配,避免 usb.uhci.init(1 参)
# 误命中 mm.init(5 参/2 默认值) 而补出多余实参。
dfx_callee_sha: str = None
if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute:
dfx_at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(cl.func)
if dfx_at.value is not None and dfx_at.value.kind() == ast.ASTKind.Name:
dfx_mn: ast.Name | t.CPtr = (ast.Name | t.CPtr)(dfx_at.value)
if dfx_mn.id is not None:
dfx_callee_sha = self._lookup_module_sha1(dfx_mn.id)
if dfx_callee_sha is None and self.Gen.TypeGenBase is not None:
dfx_callee_sha = self.Gen.TypeGenBase.CurrentModuleSha1
gd_entry: GenTypes.FuncDefaultsEntry | t.CPtr = self.Gen.CallGenLeafKit.find_func_defaults(func_name, dfx_callee_sha)
if df_defaults is None and gd_entry is not None:
df_defaults = gd_entry.Defaults
df_default_count = gd_entry.DefaultCount
df_param_count = gd_entry.ParamCount
# 交叉校验:全局默认参数表按「裸函数名」登记/查询,跨模块同名函数会串味
# (如 usb.uhci.init 只有 1 形参,却命中 mm.init 的 5 形参/2 默认值记录
# → 被补 2 个 i32 0 → call 3 实参 vs declare 1 形参 → ABI 错位)。
# 若被调声明(found_func)的实际形参数与表记录 ParamCount 不符,则丢弃该表,
# 交由下方 `elif found_func is not None` 分支按真实形参数零值兜底。
if found_func is not None and df_default_count > 0:
dfx_cnt: int = 0
dfx_p: llvmlite.Param | t.CPtr = llvmlite.function_get_param_head(found_func)
while dfx_p is not None:
dfx_nm: str = llvmlite.param_get_name(dfx_p)
if dfx_nm is None or (string.strcmp(dfx_nm, "%__eh_msg_out__") != 0
and string.strcmp(dfx_nm, "%__eh_code_out__") != 0):
dfx_cnt += 1
dfx_p = llvmlite.param_get_next(dfx_p)
if dfx_cnt > 0 and dfx_cnt != df_param_count:
df_defaults = None
df_default_count = 0
df_param_count = 0
# 填充缺失参数
if df_defaults is not None and df_default_count > 0 and can < df_param_count:
df_offset: int = df_param_count - df_default_count
@@ -1561,6 +1592,12 @@ class CallGenMixin(CallGenBase.CallGenBase):
# declare/定义宽度一致(B3:process 固化 i32 vs 定义 t.CSizeT=i64)。
head = self.Gen.CallGenLib._promote_clib_call_args(
pool, builder, mod, call_name, head, can)
# 跨模块模块级调用({sha1}.{func}):实参按 vpi 定义侧形参类型 coerce
# (整型宽度精确对齐,i32 字面量 → 定义侧 i8/i16),再建 declare →
# call 实参 / declare / define 三者宽度一致(B3 同族通用化;反例:
# su8250.putchar 定义 (i16,i8) 而调用点字面量 1016=i32 固化宽)。
head = self.Gen.CallGenLib._coerce_args_to_def_params(
pool, builder, call_name, head, can)
# 跨模块调用:创建 declare 声明,避免 llc 报 undefined value
self._ensure_method_declare(pool, mod, call_name, call_ret_ty, head, can)
# 检查被调用函数是否为变长函数(*args),若是则对变长参数进行 C ABI 提升
+1 -1
View File
@@ -342,7 +342,7 @@ class CallGenProp(CallGenBase.CallGenBase):
struct_ty_cc: llvmlite.LLVMType | t.CPtr = self.Gen.StructGen.get_struct_type(class_name)
if struct_ty_cc is None:
return None
cls_ptr: llvmlite.Value | t.CPtr = llvmlite.build_alloca(builder, struct_ty_cc)
cls_ptr: llvmlite.Value | t.CPtr = self.Gen.VarGen._alloca_at_entry(builder, struct_ty_cc)
if cls_ptr is None:
return None
llvmlite.value_set_next(cls_ptr, None)
+12 -4
View File
@@ -308,6 +308,9 @@ class ClassGenEnum(ClassGenBase.ClassGenBase):
# 规则: 每个字段的对齐 = 该字段类型的自然对齐
# 指针/i64 → 8, i32 → 4, i16 → 2, i8 → 1
# 结构体总大小需对齐到最大字段对齐的倍数
# packed(@c.Attribute(t.attr.packed))结构体无字段填充:
# 大小 = 字段尺寸之和(LLVM packed struct 语义)。
is_packed: int = self.Gen.StructGen.struct_type_is_packed(ty)
total: int = 0
max_align: int = 1
cur: llvmlite.ParamNode | t.CPtr = fields
@@ -317,17 +320,19 @@ class ClassGenEnum(ClassGenBase.ClassGenBase):
fty: llvmlite.LLVMType | t.CPtr = (llvmlite.LLVMType | t.CPtr)(cur.Ty)
fsize: int = self._get_type_size(fty)
falign: int = self._get_type_align(fty)
# 对齐当前偏移到字段对齐边界
if falign > 0:
# 对齐当前偏移到字段对齐边界(packed 不插填充)
if is_packed == 0 and falign > 0:
rem: int = total % falign
if rem != 0:
total += falign - rem
total += fsize
if falign > max_align:
if is_packed == 0 and falign > max_align:
max_align = falign
cur = cur.Next
i += 1
# 结构体总大小对齐到最大字段对齐的倍数
# packed 无尾部对齐;非 packed 总大小对齐到最大字段对齐的倍数
if is_packed != 0:
return total
rem2: int = total % max_align
if rem2 != 0:
total += max_align - rem2
@@ -358,6 +363,9 @@ class ClassGenEnum(ClassGenBase.ClassGenBase):
case llvmlite.LLVMType.Array(elem_ty, count):
return self._get_type_align(elem_ty)
case llvmlite.LLVMType.Struct(fields, fcount, name):
# packed 结构体整体按字节对齐(对齐 1,无填充)
if self.Gen.StructGen.struct_type_is_packed(ty) != 0:
return 1
# 结构体的对齐 = 最大字段对齐
max_align: int = 1
cur: llvmlite.ParamNode | t.CPtr = fields
+10 -3
View File
@@ -439,7 +439,10 @@ class ExprGenBase:
# 此前一律 sext(i1 除外)→ t.CUInt32T 等无符号值赋值/传参被符号
# 扩展(高位置 1,如 0xFFFFFFFF 变 0xFFFFFFFFFFFFFFFF),与比较路径
# should_zext_widen(i8 默认 zext)规则相反 → 隐蔽数据损坏。
if self._zext_or_sext_val(pool, val, node) != 0:
# trans 必须下传:_node_type_unsigned 对 Name/Subscript 形态需经
# trans.SymTab 查变量注解(uint16/sec:uint32 等)才能判定无符号,
# 否则返回未知 → 退回 sext(无符号扩展缺陷根因)。
if self._zext_or_sext_val(pool, val, node, trans) != 0:
return llvmlite.build_zext(builder, val, target_ty)
return llvmlite.build_sext(builder, val, target_ty)
# 窄化截断诊断(_Type.md 问题 8):宽→窄 trunc 静默丢位。
@@ -488,7 +491,8 @@ class ExprGenBase:
def _zext_or_sext_val(self, pool: memhub.MemBuddy | t.CPtr,
val: llvmlite.Value | t.CPtr,
node: ast.AST | t.CPtr = None) -> int:
node: ast.AST | t.CPtr = None,
trans: HT.Translator | t.CPtr = None) -> int:
"""统一整数拓宽方向:1=zext(无符号语义),0=sext
规则(对齐 ExprGenOps.should_zext_widen / _node_type_unsigned):
@@ -496,6 +500,9 @@ class ExprGenBase:
2. i8 → zext(系统编程中 i8 几乎总是无符号字节)
3. AST 节点解析为无符号类型(t.CUIntXXT 强转/无符号类型名/BYTE 等)→ zext
4. 否则 sext
trans 供 _node_type_unsigned 经 SymTab 解析变量注解的符号性(Name/
Subscript 形态必需);省略则仅能识别显式 t.CUIntXXT 强转。
"""
if val is None or val.Ty is None:
return 0
@@ -505,7 +512,7 @@ class ExprGenBase:
if bits == 8:
return 1
if node is not None:
if self.Gen.ExprGenOps._node_type_unsigned(pool, node) != 0:
if self.Gen.ExprGenOps._node_type_unsigned(pool, node, trans) != 0:
return 1
return 0
+1 -1
View File
@@ -336,7 +336,7 @@ class ExprGenConst(ExprGenBase.ExprGenBase):
# alloca 临时 list 空间
# 确保跨模块结构体的完整定义在当前模块中可用(供 alloca 分配空间)
self.Gen.StructGen.ensure_struct_def_in_module(pool, mod, struct_ty)
tmp: llvmlite.Value | t.CPtr = llvmlite.build_alloca(builder, struct_ty)
tmp: llvmlite.Value | t.CPtr = self.Gen.VarGen._alloca_at_entry(builder, struct_ty)
if tmp is None:
return None
+1 -1
View File
@@ -172,7 +172,7 @@ class ExprGenFstr(ExprGenBase.ExprGenBase):
return None
# 固定栈缓冲 [FSTR_BUF_SIZE x i8],GEP(0,0) 取 i8*
buf_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Array(pool, i8_ty, FSTR_BUF_SIZE)
buf_alloca: llvmlite.Value | t.CPtr = llvmlite.build_alloca(builder, buf_ty)
buf_alloca: llvmlite.Value | t.CPtr = self.Gen.VarGen._alloca_at_entry(builder, buf_ty)
if buf_alloca is None:
return None
zero_i32: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, 0)
+85 -5
View File
@@ -190,8 +190,11 @@ class ExprGenOps(ExprGenBase.ExprGenBase):
if wide_val.IntVal > signed_max:
return 1
# 窄侧 AST 节点解析为无符号类型(如 t.CUIntXXT 字面量/强转)
# trans 必须下沉:Name/Subscript 形态靠 trans.SymTab 查变量/数组元素
# 注解的无符号性,缺 trans 时恒返回 0 → 无符号值被误用 sext 扩展
# (ide.vp identify_data[60] / timer.vp sec 的 uint16/uint32→更宽 根因)
if narrow_node is not None:
if self._node_type_unsigned(pool, narrow_node) != 0:
if self._node_type_unsigned(pool, narrow_node, trans) != 0:
return 1
# 窄侧是高位比特置位的 CDefine 常量(0x80000000..0xFFFFFFFF)→ zext。
# CDefine 常量是编译期值而非变量,_node_type_unsigned 查不到类型注解;
@@ -272,6 +275,46 @@ class ExprGenOps(ExprGenBase.ExprGenBase):
if self._annot_str_unsigned(ve_su.AnnotClassName) != 0:
return 1
return 0
if k == ast.ASTKind.Attribute:
# 结构体字段(如 viperio.Buf 的 self.capacity/length: t.CSizeT):
# 经接收者变量的 AnnotClassName 取类名,再按类名+字段名查字段注解,
# 判定无符号(CSizeT/uint*)。此前 Attribute 落到末尾 return 0 →
# `self.capacity > 0` / `self.length < self.capacity` 生成 icmp sgt/slt
# (viperio 字段比较误用有符号,T1 残留根因)。
# 注意:本函数在 translate_binop 对每个操作数调用,必须**只读失败返回 0**;
# 故不走 lookup_field_by_class(其模块限定名分支会 fatal_error),
# 改用 find_struct + 自遍历字段(未命中一律返回 0)。
at_u: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(node)
if at_u.value is not None and at_u.attr is not None and trans is not None:
cls_nm_u: str = None
if at_u.value.kind() == ast.ASTKind.Name:
vn_u: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at_u.value)
if vn_u.id is not None:
ve_u: GenTypes.VarEntry | t.CPtr = self.Gen.VarGen.lookup_var_entry(
trans.SymTab, vn_u.id)
if ve_u is not None:
cls_nm_u = ve_u.AnnotClassName
if cls_nm_u is not None:
# 取裸类名(点前部分):只做裸名结构体查找,避免模块限定名解析
bare_u: str = cls_nm_u
dot_u: str = self.Gen.StructGen._last_dot_outside_brackets(cls_nm_u)
if dot_u is not None:
bare_u = dot_u + 1
if bare_u is not None:
st_u: GenTypes.StructEntry | t.CPtr = self.Gen.StructGen.find_struct(bare_u)
if st_u is not None:
fi_u: int = 0
while fi_u < st_u.FieldCount:
fe_u: GenTypes.FieldEntry | t.CPtr = \
self.Gen.StructGen._get_field_entry(st_u, fi_u)
if fe_u is not None and fe_u.Name is not None \
and string.strcmp(fe_u.Name, at_u.attr) == 0:
if fe_u.AnnotClassName is not None:
if self._annot_str_unsigned(fe_u.AnnotClassName) != 0:
return 1
break
fi_u += 1
return 0
return 0
def _type_name_unsigned(self, type_name: str) -> int:
"""类型名是否为无符号整数类型(精确匹配,避免子串误伤如 nUIntCount)"""
@@ -282,6 +325,12 @@ class ExprGenOps(ExprGenBase.ExprGenBase):
if string.strcmp(type_name, "CUInt16T") == 0: return 1
if string.strcmp(type_name, "CUInt32T") == 0: return 1
if string.strcmp(type_name, "CUInt64T") == 0: return 1
# 内建无符号别名(非 CTypedef,动态兜底查不到 → 必须显式登记)。
# CSizeT 此前缺失 → viperio.Buf.write 的 capacity/length 比较生成
# icmp sgt/sle/slt(应 ult/ule/ult),A5 同族残留根因。
if string.strcmp(type_name, "CSizeT") == 0: return 1
if string.strcmp(type_name, "SSizeT") == 0: return 1
if string.strcmp(type_name, "CUIntPtrT") == 0: return 1
# stdint 裸别名(from stdint import *)
if string.strcmp(type_name, "UInt8T") == 0: return 1
if string.strcmp(type_name, "UInt16T") == 0: return 1
@@ -318,6 +367,17 @@ class ExprGenOps(ExprGenBase.ExprGenBase):
if string.strstr(annot, "CUInt16T") is not None: return 1
if string.strstr(annot, "CUInt32T") is not None: return 1
if string.strstr(annot, "CUInt64T") is not None: return 1
# 内建无符号别名(同 _type_name_unsigned)
if string.strstr(annot, "CSizeT") is not None: return 1
if string.strstr(annot, "CUIntPtrT") is not None: return 1
# 小写 stdint 别名(uint16/uint32/uint64/uint8):复合注解如
# t.CArray[uint16, 256] 的注解串用大写规则扫不到,需显式识别小写
# (ide.vp identify_data: t.CArray[uint16,256] 的 word60 误 sext 根因)。
# 注意不含 "size_t":ssize_t(有符号)包含该子串会误判。
if string.strstr(annot, "uint8") is not None: return 1
if string.strstr(annot, "uint16") is not None: return 1
if string.strstr(annot, "uint32") is not None: return 1
if string.strstr(annot, "uint64") is not None: return 1
if string.strstr(annot, "CUnsigned") is not None: return 1
if string.strstr(annot, "BYTE") is not None: return 1
if string.strstr(annot, "UCHAR") is not None: return 1
@@ -1279,16 +1339,26 @@ class ExprGenOps(ExprGenBase.ExprGenBase):
if op == ast.OpKind.Sub and lhs_bits == 0:
return self.translate_ptr_arith(pool, builder, lhs, rhs, op)
# 拓宽方向(zext/sext)与后续除/模谓词都需要"操作数是否无符号",
# 统一在此求值一次并下沉 trans(Name/Subscript 靠 SymTab 注解判定)。
binop_unsigned: int = 0
if lhs_node is not None:
if self._node_type_unsigned(pool, lhs_node, trans) != 0:
binop_unsigned = 1
if binop_unsigned == 0 and rhs_node is not None:
if self._node_type_unsigned(pool, rhs_node, trans) != 0:
binop_unsigned = 1
if lhs_bits > rhs_bits:
if self.should_zext_widen(pool, rhs, lhs, rhs_node) != 0:
if self.should_zext_widen(pool, rhs, lhs, rhs_node, trans) != 0:
rhs = llvmlite.build_zext(builder, rhs, lhs.Ty)
else:
rhs = self.coerce_to_type(builder, rhs, lhs.Ty, node)
rhs = self.coerce_to_type(builder, rhs, lhs.Ty, node, trans)
elif rhs_bits > lhs_bits:
if self.should_zext_widen(pool, lhs, rhs, lhs_node) != 0:
if self.should_zext_widen(pool, lhs, rhs, lhs_node, trans) != 0:
lhs = llvmlite.build_zext(builder, lhs, rhs.Ty)
else:
lhs = self.coerce_to_type(builder, lhs, rhs.Ty, node)
lhs = self.coerce_to_type(builder, lhs, rhs.Ty, node, trans)
# 位运算类型安全检查:两边都是指针(lhs_bits==0 and rhs_bits==0)时,
# 位运算(BitOr/BitAnd/BitXor/LShift/RShift)非法,LLVM 要求整数操作数。
@@ -1308,10 +1378,20 @@ class ExprGenOps(ExprGenBase.ExprGenBase):
elif op == ast.OpKind.Mult:
return llvmlite.build_mul(builder, lhs, rhs)
elif op == ast.OpKind.Div:
# 无符号操作数 → udiv(binop_unsigned 经 _node_type_unsigned 判定,
# 覆盖 t.CSizeT/t.CUInt64T 及变量/数组元素的注解无符号性)。
# 此前一律 sdiv → 无符号值 ≥ 2^63 时结果错误(viperlib %lu/%lx、
# fat32_mkfs 扇区运算等根因)。
if binop_unsigned != 0:
return llvmlite.build_udiv(builder, lhs, rhs)
return llvmlite.build_sdiv(builder, lhs, rhs)
elif op == ast.OpKind.FloorDiv:
if binop_unsigned != 0:
return llvmlite.build_udiv(builder, lhs, rhs)
return llvmlite.build_sdiv(builder, lhs, rhs)
elif op == ast.OpKind.Mod:
if binop_unsigned != 0:
return llvmlite.build_urem(builder, lhs, rhs)
return llvmlite.build_srem(builder, lhs, rhs)
elif op == ast.OpKind.BitAnd:
return llvmlite.build_and(builder, lhs, rhs)
+1 -1
View File
@@ -459,7 +459,7 @@ class FuncGenMixin(CallGenBase.CallGenBase):
llvmlite.build_store(parent_builder, null_val, env_slot)
# 13e. 将闭包指针存储到父函数的局部变量 {func_name}
closure_alloca: llvmlite.Value | t.CPtr = llvmlite.build_alloca(parent_builder, i8_ptr_ty)
closure_alloca: llvmlite.Value | t.CPtr = self.Gen.VarGen._alloca_at_entry(parent_builder, i8_ptr_ty)
if closure_alloca is not None:
llvmlite.build_store(parent_builder, closure_ptr, closure_alloca)
self.Gen.VarGen.define_var(trans.SymTab, func_name, closure_alloca)
+1
View File
@@ -184,6 +184,7 @@ class FuncEntry:
class FuncDefaultsEntry:
"""默认参数表条目"""
Name: str
ModuleSha1: str # 定义该函数的模块 SHA1(限定名去重,避免跨模块同名串味)
Defaults: t.CVoid | t.CPtr # defaults 列表节点指针 (list[AST | CPtr])(pool 内,可能被 reset)
DefaultCount: int
ParamCount: int # 形参数量(不含 self)
+55 -3
View File
@@ -659,7 +659,9 @@ class StmtGenCifMixin:
if val_bits == 1 and val_bits < ret_bits:
val = llvmlite.build_zext(builder, val, ret_ty_rt)
else:
val = self.Gen.ExprGenBase.coerce_to_type(builder, val, ret_ty_rt, node)
# 传 trans:无符号源类型(uint16/uint32/CSizeT 等)扩展用 zext,
# 缺 trans 时 _node_type_unsigned 查不到变量注解 → 误 sext
val = self.Gen.ExprGenBase.coerce_to_type(builder, val, ret_ty_rt, node, trans)
llvmlite.build_ret(builder, val)
return 0
@@ -1238,6 +1240,48 @@ class StmtGenCifMixin:
if skc_r.int_val < 0:
step_neg_const = 1
# 3.5 循环变量存储位宽:随 range 参数类型(uint64→i64 / uint16→i16 /
# uint8→i8 / 其余→i32),不固定 i32。此前固定 i32 alloca,使
# uint64 上界的循环变量被截断(syscall MMAP 的
# range(heap_end, new_end, 4096) 生成 alloca i32 + trunc i64→i32
# → 高 32 位丢失 → 映射地址错误)。仅当循环变量尚未声明时生效;
# 已声明变量沿用其 alloca 元素类型(见下方 loop_var_ty 解引用)。
loop_store_ty: llvmlite.LLVMType | t.CPtr = i32_ty
range_arg_bits: int = 0
if arg_count == 1:
if stop_val is not None and stop_val.Ty is not None:
range_arg_bits = self.Gen.ExprGenBase.get_llvm_type_bits(stop_val.Ty)
else:
start_bits_rt: int = 0
stop_bits_rt: int = 0
if start_val is not None and start_val.Ty is not None:
start_bits_rt = self.Gen.ExprGenBase.get_llvm_type_bits(start_val.Ty)
if stop_val is not None and stop_val.Ty is not None:
stop_bits_rt = self.Gen.ExprGenBase.get_llvm_type_bits(stop_val.Ty)
range_arg_bits = start_bits_rt if start_bits_rt > stop_bits_rt else stop_bits_rt
if range_arg_bits == 64:
loop_store_ty = llvmlite.Int64(pool)
elif range_arg_bits == 16:
loop_store_ty = llvmlite.Int16(pool)
elif range_arg_bits == 8:
loop_store_ty = llvmlite.Int8(pool)
# 3.6 循环无符号性:循环变量注解或任一 range 边界为无符号整型 →
# icmp ult/ugt 而非 slt/sgt(复用 ExprGenOps._node_type_unsigned:
# Name 查 SymTab 注解 / Call 转换目标 / Subscript 元素 / BinOp 递归)。
# syscall 的 va 未声明,靠边界 heap_end/new_end(uint64)判定;
# fat32 的 i/s 已声明 uint32/uint8,靠循环变量注解判定。
loop_unsigned: int = 0
if tk == ast.ASTKind.Name:
if self.Gen.ExprGenOps._node_type_unsigned(pool, target, trans) != 0:
loop_unsigned = 1
if loop_unsigned == 0:
args_u: t.CSizeT
for args_u in range(arg_count):
if self.Gen.ExprGenOps._node_type_unsigned(pool, args.get(args_u), trans) != 0:
loop_unsigned = 1
break
# 4. 创建/查找循环变量 alloca
# 必须用 lookup_current(仅当前作用域):闭包内 lookup_var 会跨函数
# 命中父函数同名变量(如父函数自己的循环计数器 i)→ 循环计数写到
@@ -1253,7 +1297,9 @@ class StmtGenCifMixin:
# uses!",Terminal _cmd_cd 的 for i in range(1,64) 根因)。
# 闭包跨函数命中父函数变量的根因已由 lookup_current 修复,
# 故 entry 插入路径安全。
var_alloca = self.Gen.VarGen._alloca_at_entry(builder, i32_ty)
# 位宽随 range 参数类型(loop_store_ty),不再固定 i32:
# uint64 上界 → i64 alloca,避免 trunc i64→i32 丢失高位。
var_alloca = self.Gen.VarGen._alloca_at_entry(builder, loop_store_ty)
if self.Gen.VarGen.define_var(
trans.SymTab, var_name, var_alloca) == 0:
new_vars = 1
@@ -1320,10 +1366,16 @@ class StmtGenCifMixin:
# "invalid cast opcode for cast from 'i32' to 'i64'"
# (VpiReader.vp range(arg_count) 追加86 根因)。
cmp_lhs = llvmlite.build_trunc(builder, cur_i, stop_val.Ty)
# step 为负时条件反转:i > stop(ICMP_SGT);正/缺省 step 保持 i < stop
# 有符号基准谓词:step 为负时条件反转(i > stop,SGT);正/缺省 i < stop(SLT)
range_cmp_op: int = llvmlite.ICMP_SLT
if step_neg_const != 0:
range_cmp_op = llvmlite.ICMP_SGT
# 无符号循环:改用无符号谓词(正步长 ult / 负步长 ugt)
if loop_unsigned != 0:
if step_neg_const != 0:
range_cmp_op = llvmlite.ICMP_UGT
else:
range_cmp_op = llvmlite.ICMP_ULT
cond_i1: llvmlite.Value | t.CPtr = llvmlite.build_icmp(
builder, range_cmp_op, cmp_lhs, cmp_rhs)
llvmlite.build_cond_br(builder, cond_i1, body_bb, end_bb)
+21
View File
@@ -286,6 +286,7 @@ class StructGenMixin:
entry.FieldCount = src_entry.FieldCount
entry.Fields = src_entry.Fields
entry.IsUnion = src_entry.IsUnion
entry.IsPacked = src_entry.IsPacked
entry.IsOOP = src_entry.IsOOP
entry.HasInit = src_entry.HasInit
entry.HasNew = src_entry.HasNew
@@ -720,6 +721,26 @@ class StructGenMixin:
return entry
return None
def struct_type_is_packed(self, struct_ty: llvmlite.LLVMType | t.CPtr) -> int:
"""判断命名结构体是否 packed(@c.Attribute(t.attr.packed)),返回 1/0
先按类型指针身份匹配(find_struct_by_type,跨模块 deep copy 后仍
指向同一 entry.Ty)、miss 时按结构体名回退(等价类型实例),最终读
StructEntry.IsPacked(B5 在定义侧/stub 注入侧写入)。供 __sizeof__
尺寸与字段偏移计算:packed 结构体无字段填充,大小=字段尺寸之和。
"""
if struct_ty is None:
return 0
sname: str = llvmlite.get_struct_name(struct_ty)
if sname is None:
return 0
entry: GenTypes.StructEntry | t.CPtr = self.find_struct_by_type(struct_ty)
if entry is None:
entry = self.find_struct(sname)
if entry is None:
return 0
return entry.IsPacked
def lookup_field(self, struct_ty: llvmlite.LLVMType | t.CPtr,
field_name: str) -> GenTypes.FieldEntry | t.CPtr:
"""按结构体类型和字段名查找字段信息
+28
View File
@@ -245,6 +245,16 @@ class TranslatorGenMixin:
return None
elts_count: t.CSizeT = len(elts)
# 元素数超过声明长度 → fail-fast(此前在下方循环中按声明长度截断、静默丢弃
# 多余元素:apic._trampoline_bin 声明 [128 x i8] 却给出 138 个字节 → 末尾
# mov rax,[0x7F28]; jmp rax 被丢弃,AP trampoline 失效)。
if t.CSizeT(arr_count) < elts_count:
self.Gen.TypeGenErr.fatal_error(
list_node,
f"数组初始化元素数 {elts_count} 超过数组声明长度 {arr_count},不允许静默截断",
"OverflowError")
return None
# 非整数元素类型(struct/指针等聚合):不存在 "%s %lld" 文本展开。
# 仅当列表全零(元素缺失按零填充)时返回整体 "zeroinitializer"——
# 对任意聚合类型合法(devfs.directories: t.CArray[devfs_directory, N]
@@ -363,6 +373,16 @@ class TranslatorGenMixin:
# 0(128 字节 PS/2 扫描码表全 0 → 键盘输入失效根因)。
if ec is not None and (ec.const_kind == ast.CONST_INT or ec.const_kind == ast.CONST_CHAR):
elem_val = ec.int_val
# CONST_STR 单字符(双引号如 "'" / '"'):parser 把双引号
# 一律判为 CONST_STR(见 ast/parser.vp 字符串常量判定),
# 此前只认 CONST_INT/CONST_CHAR → 双引号单字符元素被编成 0
# (keyboard.vp scancode_set1[40]='\'' 、scancode_set1_shift[40]
# ='"' 丢失根因;A2 的修复只覆盖了单引号字符)。
# strlen == 1 时取首字符码;多字符字符串无法表达为整型元素
# → 保持 0(由下方按声明长度补零)。
elif ec is not None and ec.const_kind == ast.CONST_STR:
if ec.str_val is not None and string.strlen(ec.str_val) == 1:
elem_val = t.CInt64T(ec.str_val[0])
elif ek == ast.ASTKind.UnaryOp:
# 负数: UnaryOp(USub, Constant)
elem_val = self.Gen.AssignGen.extract_cdefine_int_value(elem_node)
@@ -659,6 +679,14 @@ class TranslatorGenMixin:
elif val_kind2 == ast.ASTKind.List:
init_list_node = (ast.List | t.CPtr)(asgn.value)
init_kind = 2
elif val_kind2 == ast.ASTKind.Attribute:
# 模块级 re-export 别名(如 fat32_core.vp `_read32 = types._read32`、
# fat32_dir.vp/fat32_file.vp 的同型 shim):RHS 是函数/实体引用,
# 无值语义可落入 LLVM 全局。此前未识别 → var_ty 默认 i32 →
# 生成假符号 `global i32 zeroinitializer`(被引用即调用地址 0)。
# 与 __all__ 同类:别名/元数据实体不产出 LLVM 全局(详见 L693)。
# 真正的函数别名机制(可调用 + 可 re-export)属独立特性,待后续实现。
return 0
if var_name is None:
return 0
-15
View File
@@ -1,15 +0,0 @@
fix(tpv-call): 裸名调用同模块优先,修复跨模块同名 stub 抢占致 _advance 误绑
根因:LoadDepVpiIntoModule 在 forward_declare_functions 之前把依赖模块
(lexer)函数以裸名注册进本文件 _funcs(槽位在前),find_func_in_table
与 find_func_in_module 后缀扫描均先命中跨模块 stub declare → parser.vp
内部 163 处 _advance(ps) 全部绑成 lexer._advance,并以 Parser→Lexer**
bitcast 传参(运行期游标永不推进,AstTest 无限 SyntaxError 至 pool 耗尽)。
修复:find_func_in_table / find_func_in_module 增加 owner_sha1(当前模块)
前缀优先遍,仅接受恰为 "{sha1}.{name}" 的本模块函数(declare/define 均可,
前向 declare 未激活时同样命中);translate_call 裸名分支传 trans.ModuleSha1。
本模块无同名定义(纯 from-import/外部 stub)时回落原语义,不改变 CExport/
方法拒绝/装饰器分支行为。
附带同批入库:list 内置方法内联分派与裸名模块表匹配等本会话修复。