回滚 13080f6 的 110 处访问器调用,恢复裸字段访问 `x.Next`。
理由:该批访问器是把访问移到定义模块内执行,**绕开**了"继承字段名解析",
属于规避而非修复;缺陷本体在 TPC 侧(导入侧继承展平把泛型基类的继承字段
记成 __inherited_N 占位名,名字丢失)。TPC 已按语义修正:
· e6451a07 继承展平沿继承链取真实字段名(泛型特化回退模板名),
展平表直接落 `[("Next", <槽位类型>), ...]`,属性访问是普通表命中;
· 3ca46c01 顺带修掉 _get_or_create_struct 里"同名枚举成员遮蔽类定义"的 i32 出口。
因此源码侧恢复为唯一写法(裸字段访问),由编译器负责解析。
注:本 commit 只回滚访问器;13080f6 里夹带的 CallGenProp.vp 续行合并(非访问器改动)
已在工作区保留(未提交)。
1528 lines
89 KiB
Plaintext
1528 lines
89 KiB
Plaintext
import lib.core.Gen.GenTypes as GenTypes
|
||
import t, c
|
||
from stdint import *
|
||
import ast
|
||
import llvmlite
|
||
import memhub
|
||
import string
|
||
import viperlib
|
||
import lib.core.Gen.Translator as HT
|
||
import lib.core.Gen.Type.TypeSpec as TSpec
|
||
|
||
|
||
import lib.core.Gen.Expr.ExprGenBase as ExprGenBase
|
||
|
||
# ============================================================
|
||
# ExprGenOps - ExprGenMixin 拆分(阶段 D):比较/一元/布尔/binop 运算
|
||
# ============================================================
|
||
novirtual class ExprGenOps(ExprGenBase.ExprGenBase):
|
||
"""比较/一元/布尔/binop 运算"""
|
||
|
||
# ============================================================
|
||
# 翻译三元表达式 IfExp(test, body, orelse)
|
||
#
|
||
# 生成: cond ? body : orelse
|
||
# then_blk: 翻译 body → br merge
|
||
# else_blk: 翻译 orelse → br merge
|
||
# merge_blk: phi [body_val, then_blk], [orelse_val, else_blk]
|
||
# ============================================================
|
||
def translate_ifexp(self, builder: llvmlite.IRBuilder*,
|
||
pool: memhub.MemBuddy*,
|
||
mod: llvmlite.LLVMModule*,
|
||
node: ast.AST*,
|
||
trans: HT.Translator* = None) -> llvmlite.Value*:
|
||
"""翻译三元表达式 (body if test else orelse),返回 phi 结果 Value"""
|
||
if node is None or builder is None or trans is None: return None
|
||
ie: rawptr[ast.IfExp] = rawptr[ast.IfExp](node)
|
||
if ie is None: return None
|
||
|
||
func: llvmlite.Function* = trans._cur_func
|
||
if func is None: return None
|
||
|
||
# 1. 求值条件
|
||
cond_val: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, ie.test, None, 0, trans)
|
||
if cond_val is None: return None
|
||
# 转换为 i1
|
||
cond_bits: int = self.get_llvm_type_bits(cond_val.Ty)
|
||
if cond_bits == 1:
|
||
cond_i1: llvmlite.Value* = cond_val
|
||
else:
|
||
if self.is_ptr_type(cond_val.Ty) != 0:
|
||
# 指针类型:与 null 比较
|
||
null_cond: llvmlite.Value* = llvmlite.ConstNull(pool, cond_val.Ty, "null")
|
||
cond_i1 = llvmlite.build_icmp(builder, llvmlite.ICMP_NE, cond_val, null_cond)
|
||
else:
|
||
zero: llvmlite.Value* = llvmlite.const_int32(pool, 0)
|
||
cond_i1 = llvmlite.build_icmp(builder, llvmlite.ICMP_NE, cond_val, zero)
|
||
|
||
# 2. 创建三个基本块
|
||
cnt: int = trans._label_counter
|
||
trans._label_counter = cnt + 1
|
||
name_buf: str = pool.alloc(32)
|
||
if name_buf is None:
|
||
return None
|
||
viperlib.snprintf(name_buf, 32, "ifexp.then.%d", cnt)
|
||
then_bb: rawptr[llvmlite.BasicBlock] = llvmlite.create_block(pool, func, name_buf)
|
||
viperlib.snprintf(name_buf, 32, "ifexp.else.%d", cnt)
|
||
else_bb: rawptr[llvmlite.BasicBlock] = llvmlite.create_block(pool, func, name_buf)
|
||
viperlib.snprintf(name_buf, 32, "ifexp.merge.%d", cnt)
|
||
merge_bb: rawptr[llvmlite.BasicBlock] = llvmlite.create_block(pool, func, name_buf)
|
||
|
||
if then_bb is None or else_bb is None or merge_bb is None: return None
|
||
|
||
# 3. 条件跳转
|
||
llvmlite.build_cond_br(builder, cond_i1, then_bb, else_bb)
|
||
|
||
# 4. then 分支
|
||
llvmlite.position_at_end(builder, then_bb)
|
||
body_val: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, ie.body, None, 0, trans)
|
||
then_blk_for_phi: rawptr[llvmlite.BasicBlock] = trans._cur_builder.CurBlock
|
||
if body_val is None:
|
||
# fail-fast:三元表达式 then 分支翻译失败,立即终止编译(暴露上游根因)
|
||
self.Gen.TypeGenErr.fatal_error(ie.body, "三元表达式分支翻译失败", "TypeError")
|
||
# 暂不 br,等类型对齐后再 br
|
||
|
||
# 5. else 分支
|
||
# then 分支为指针时,把它的类型作为 else 分支的目标类型上下文下传:单引号
|
||
# 单字符字面量(CONST_CHAR,如 '?')在指针目标下物化为 1 字符字符串
|
||
# (对齐 TPC 引号推断,见 ExprGenConst.translate_constant 的 CONST_CHAR 分支),
|
||
# 否则物化为 i32 → 下方类型对齐 coerce 整数→指针被 fail-fast 拒绝
|
||
# (VpiReader.vp `rel_path_rt if rel_path_rt is not None else '?'` 根因:then 是
|
||
# str(i8*)、else '?' 恒 i32 → CastError)。非指针 then 分支不下传,既有
|
||
# 常量物化与位宽行为保持不变。
|
||
orelse_target_ty: llvmlite.LLVMType* = None
|
||
if self.is_ptr_type(body_val.Ty) != 0:
|
||
orelse_target_ty = body_val.Ty
|
||
llvmlite.position_at_end(builder, else_bb)
|
||
orelse_val: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, ie.orelse, None, 0, trans, orelse_target_ty)
|
||
else_blk_for_phi: rawptr[llvmlite.BasicBlock] = trans._cur_builder.CurBlock
|
||
if orelse_val is None:
|
||
# fail-fast:三元表达式 else 分支翻译失败,立即终止编译(暴露上游根因)
|
||
self.Gen.TypeGenErr.fatal_error(ie.orelse, "三元表达式分支翻译失败", "TypeError")
|
||
# 暂不 br,等类型对齐后再 br
|
||
|
||
# 类型对齐 (两分支类型可能不同)
|
||
# 必须在各自分支中执行 coerce_to_type,确保 merge 块中 PHI 是第一条指令
|
||
# (否则 coerce_to_type 生成的 sext/zext/ptrtoint/inttoptr 指令在 PHI 之前,违反 LLVM 规则)
|
||
body_bits: int = self.get_llvm_type_bits(body_val.Ty)
|
||
orelse_bits: int = self.get_llvm_type_bits(orelse_val.Ty)
|
||
body_is_ptr: int = self.is_ptr_type(body_val.Ty)
|
||
orelse_is_ptr: int = self.is_ptr_type(orelse_val.Ty)
|
||
# 情况0: 一侧是指针,另一侧是整数(如 i64 vs i8* null)
|
||
# 必须优先处理,因为 Ptr 的 bits=0 会导致下面三个分支全部跳过
|
||
# 选择 body_val.Ty 作为 phi_ty(then 分支优先),orelse_val 对齐到它
|
||
# coerce_to_type 内部会调用 ptrtoint(指针→整数)或 inttoptr(整数→指针)
|
||
if body_is_ptr != orelse_is_ptr:
|
||
llvmlite.position_at_end(builder, else_blk_for_phi)
|
||
orelse_val = self.coerce_to_type(builder, orelse_val, body_val.Ty, node)
|
||
elif body_bits > orelse_bits and orelse_bits > 0:
|
||
# 在 else 块中转换 orelse_val
|
||
llvmlite.position_at_end(builder, else_blk_for_phi)
|
||
orelse_val = self.coerce_to_type(builder, orelse_val, body_val.Ty, node)
|
||
elif orelse_bits > body_bits and body_bits > 0:
|
||
# 回到 then 块转换 body_val
|
||
llvmlite.position_at_end(builder, then_blk_for_phi)
|
||
body_val = self.coerce_to_type(builder, body_val, orelse_val.Ty, node)
|
||
elif body_bits == orelse_bits and body_bits > 0:
|
||
# bits 相同但类型不同(如 i64 vs Ptr):null 不能用于 i64 PHI
|
||
# 此分支理论上不会触发(已被情况0覆盖),保留作为防御
|
||
if self.is_ptr_type(body_val.Ty) != self.is_ptr_type(orelse_val.Ty):
|
||
llvmlite.position_at_end(builder, else_blk_for_phi)
|
||
orelse_val = self.coerce_to_type(builder, orelse_val, body_val.Ty, node)
|
||
|
||
# 现在分别在 then/else 块中 br 到 merge(在类型转换之后)
|
||
llvmlite.position_at_end(builder, then_blk_for_phi)
|
||
llvmlite.build_br(builder, merge_bb)
|
||
llvmlite.position_at_end(builder, else_blk_for_phi)
|
||
llvmlite.build_br(builder, merge_bb)
|
||
|
||
# 6. merge 块: phi(PHI 是 merge 块的第一条指令)
|
||
llvmlite.position_at_end(builder, merge_bb)
|
||
|
||
phi_ty: llvmlite.LLVMType* = body_val.Ty
|
||
if phi_ty is None:
|
||
# fail-fast:三元表达式分支类型缺失,立即终止编译(暴露上游根因)
|
||
self.Gen.TypeGenErr.fatal_error(node, "三元表达式分支类型缺失", "TypeError")
|
||
|
||
inc1: llvmlite.PhiIncoming* = llvmlite.new_phi_incoming(pool, body_val, then_blk_for_phi)
|
||
inc2: llvmlite.PhiIncoming* = llvmlite.new_phi_incoming(pool, orelse_val, else_blk_for_phi)
|
||
if inc1 is None or inc2 is None:
|
||
return None
|
||
# 链表顺序: inc1 (head) -> inc2 (tail)
|
||
# 之前错误写成 inc2.Next = inc1,导致 build_phi 从 inc1 遍历时
|
||
# inc1.Next 为 None,只输出一个 phi 条目,引发 LLVM 验证错误:
|
||
# "PHINode should have one entry for each predecessor"
|
||
inc1.Next = inc2
|
||
return llvmlite.build_phi(builder, phi_ty, inc1, 2)
|
||
# ============================================================
|
||
# 翻译比较表达式 Compare(left, ops, comparators)
|
||
# ============================================================
|
||
def should_zext_widen(self, pool: memhub.MemBuddy*,
|
||
narrow_val: llvmlite.Value*,
|
||
wide_val: llvmlite.Value*,
|
||
narrow_node: ast.AST* = None,
|
||
trans: HT.Translator* = None) -> int:
|
||
"""判断整数拓宽方向:1=zext(无符号语义),0=sext
|
||
|
||
**拓宽方向的单一真源**:ExprGenBase._zext_or_sext_val(coerce 路径)委托到此。
|
||
trans 必须下传:_node_type_unsigned 对 Name/Subscript 形态需经 trans.SymTab 查
|
||
变量/数组元素注解才能判定无符号。此前本函数签名缺 trans 形参,而
|
||
translate_binop 两处调用**已在传第 5 参**(被静默丢弃),函数体内的 `trans`
|
||
退化为隐式全局(恒 None)⇒ 「源无符号」规则对 Name/Subscript 恒不命中,
|
||
无符号值被误用 sext 扩展(本注释原记的 ide.vp identify_data[60] /
|
||
timer.vp sec 的 uint16/uint32→更宽 根因,补救从未真正接上)。
|
||
|
||
对齐 TPC 的 _check_node_unsigned / HandlesExprOps 拓宽规则:
|
||
1. i1 (bool) → zext(1 保持 1,而非 sext 的 0xFF...)
|
||
2. i8 → zext(系统编程中 i8 几乎总是无符号字节);
|
||
例外:源注解**明确有符号**(int8/int8/i8/char…)→ sext 保号
|
||
(判据 _node_type_signed,见下)
|
||
3. 宽侧是常量且其值超出窄侧位宽的有符号范围 → zext
|
||
(常量"想要"无符号解释,如 0xAABBCCDD > INT32_MAX 与
|
||
uint32 值比较,sext 会把 0xAABBCCDD 变成
|
||
0xFFFFFFFFAABBCCDD 导致恒不等)
|
||
"""
|
||
if narrow_val is None or narrow_val.Ty is None:
|
||
return 0
|
||
narrow_bits: int = self.get_llvm_type_bits(narrow_val.Ty)
|
||
if narrow_bits == 1:
|
||
return 1
|
||
if narrow_bits == 8:
|
||
# i8 默认按「无符号字节」处理(系统编程惯例,保持既有行为);
|
||
# 但**源注解明确为有符号类型**时必须 sext 保号:
|
||
# TypeCastTest 回归 —— `int32(v)`(v: int8 = -50)期望 -50,
|
||
# 此前 b8470c1 把强转路径统一到本函数后撞上无条件 zext 而变成 206。
|
||
if self._node_type_signed(pool, narrow_node, trans) != 0:
|
||
return 0
|
||
return 1
|
||
# 宽侧常量启发式
|
||
if wide_val is not None and wide_val.IsConst == 1:
|
||
wide_bits: int = self.get_llvm_type_bits(wide_val.Ty)
|
||
if wide_bits > narrow_bits and narrow_bits < 64:
|
||
signed_max: int64 = (int64(1) << (narrow_bits - 1)) - 1
|
||
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, trans) != 0:
|
||
return 1
|
||
# 窄侧是高位比特置位的 CDefine 常量(0x80000000..0xFFFFFFFF)→ zext。
|
||
# CDefine 常量是编译期值而非变量,_node_type_unsigned 查不到类型注解;
|
||
# 值落在有符号负数区表示无符号哨兵(如 INVALID_FILE_ATTRIBUTES =
|
||
# 0xFFFFFFFF = 4294967295)。与宽侧(如 ULONG=CUnsignedLong→i64 的
|
||
# Win32 属性值)比较时须 zext 到 4294967295,否则 sext 成 -1 与运行时
|
||
# 零扩展的 32 位 API 返回值恒不等(SysLibTest exists/rmdir/rename/rmtree
|
||
# 全 FAIL 根因)。负值 CDefine(如 -1)不在此区间(-1 > 0x7FFFFFFF 为假),
|
||
# 保持 sext 语义不变。
|
||
if narrow_node is not None and narrow_node.kind() == ast.ASTKind.Name:
|
||
cdef_nm_zw: ast.Name* = ast.Name*(narrow_node)
|
||
if cdef_nm_zw.id is not None:
|
||
cdv_zw: int64 = self.Gen.TypeGenCDef.lookup_cdefine_constant(cdef_nm_zw.id)
|
||
if self.Gen.TypeGenCDef.is_cdefine_found() != 0:
|
||
if cdv_zw > 0x7FFFFFFF:
|
||
return 1
|
||
return 0
|
||
def _node_type_unsigned(self, pool: memhub.MemBuddy*,
|
||
node: ast.AST*,
|
||
trans: HT.Translator* = None) -> int:
|
||
"""轻量判断 AST 节点是否解析为无符号整数类型(1=是,0=否/未知)
|
||
|
||
覆盖常见形态(对齐 TPC _check_node_unsigned 的 Call/Subscript 分支):
|
||
- uint8/CUInt16T/CUInt32T/CUInt64T/CUnsigned* 等显式无符号类型强转调用
|
||
- 无符号类型名的 Name 引用(如 UInt32T 别名)
|
||
- 变量名(trans 可用时):查询 SymTab 中变量的 AnnotClassName(如 v: uint32)
|
||
"""
|
||
if node is None:
|
||
return 0
|
||
k: int = node.kind()
|
||
if k == ast.ASTKind.Call:
|
||
cl: ast.Call* = ast.Call*(node)
|
||
if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute:
|
||
at: ast.Attribute* = ast.Attribute*(cl.func)
|
||
if at.value is not None and at.value.kind() == ast.ASTKind.Name:
|
||
mod_nm: ast.Name* = ast.Name*(at.value)
|
||
if mod_nm.id is not None and string.strcmp(mod_nm.id, "t") == 0 \
|
||
and at.attr is not None:
|
||
if self._type_name_unsigned(at.attr) != 0:
|
||
return 1
|
||
return 0
|
||
if k == ast.ASTKind.Name:
|
||
nm: ast.Name* = ast.Name*(node)
|
||
if nm.id is not None:
|
||
# 1) 名字本身是无符号类型名(如 UInt32T 别名)
|
||
if self._type_name_unsigned(nm.id) != 0:
|
||
return 1
|
||
# 2) 变量注解类型为无符号类型(trans 可用时查 SymTab)
|
||
if trans is not None:
|
||
ve: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm.id)
|
||
if ve is not None:
|
||
# [S4 切读] Spec 明确无符号(TS_UINT)优先于字符串判定
|
||
if self._entry_spec_unsigned(ve.Spec) != 0:
|
||
return 1
|
||
vcn_u: str = self.Gen.VarGen.var_annot_class_name(ve)
|
||
if vcn_u is not None:
|
||
if self._type_name_unsigned(vcn_u) != 0:
|
||
return 1
|
||
return 0
|
||
if k == ast.ASTKind.BinOp:
|
||
bop: ast.BinOp* = ast.BinOp*(node)
|
||
if self._node_type_unsigned(pool, bop.left, trans) != 0:
|
||
return 1
|
||
if self._node_type_unsigned(pool, bop.right, trans) != 0:
|
||
return 1
|
||
return 0
|
||
if k == ast.ASTKind.UnaryOp:
|
||
uop: ast.UnaryOp* = ast.UnaryOp*(node)
|
||
if self._node_type_unsigned(pool, uop.operand, trans) != 0:
|
||
return 1
|
||
return 0
|
||
if k == ast.ASTKind.Subscript:
|
||
# 数组/指针下标: md5_buf[i](t.CArray[uint8, N])或 buf[i]
|
||
# (形参 uint8 | t.CPtr)元素是无符号整型 → printf("%02x", ...) zext。
|
||
sub_su: ast.Subscript* = ast.Subscript*(node)
|
||
if sub_su.value is not None and sub_su.value.kind() == ast.ASTKind.Name:
|
||
snm_su: ast.Name* = ast.Name*(sub_su.value)
|
||
if snm_su.id is not None and trans is not None:
|
||
ve_su: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, snm_su.id)
|
||
if ve_su is not None:
|
||
# [S4 切读] Spec 明确无符号优先(数组元素为无符号的联合注解场景
|
||
# 时 Spec 仍是 Ptr/Array ⇒ 返回 0,自动回退字符串路径)
|
||
if self._entry_spec_unsigned(ve_su.Spec) != 0:
|
||
return 1
|
||
scn_u: str = self.Gen.VarGen.var_annot_class_name(ve_su)
|
||
if scn_u is not None:
|
||
if self._annot_str_unsigned(scn_u) != 0:
|
||
return 1
|
||
return 0
|
||
if k == ast.ASTKind.Attribute:
|
||
# 结构体字段(如 viperio.Buf 的 self.capacity/length: sizet):经接收者变量的 AnnotClassName 取类名,再按类名+字段名查字段注解,判定无符号(CSizeT/uint*)。本函数在 translate_binop 对每个操作数调用,必须只读、失败返回 0——故不走 lookup_field_by_class(其模块限定名分支会 fatal_error),改用 find_struct + 自遍历字段(未命中一律返回 0)
|
||
at_u: ast.Attribute* = ast.Attribute*(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* = ast.Name*(at_u.value)
|
||
if vn_u.id is not None:
|
||
ve_u: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, vn_u.id)
|
||
if ve_u is not None:
|
||
cls_nm_u = self.Gen.VarGen.var_annot_class_name(ve_u)
|
||
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* = 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* = \
|
||
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:
|
||
# [S4 切读] 字段 Spec 明确无符号优先
|
||
if self._entry_spec_unsigned(fe_u.Spec) != 0:
|
||
return 1
|
||
fcn_u: str = self.Gen.StructGen.get_field_annot_class_name(fe_u)
|
||
if fcn_u is not None:
|
||
if self._annot_str_unsigned(fcn_u) != 0:
|
||
return 1
|
||
break
|
||
fi_u += 1
|
||
return 0
|
||
return 0
|
||
def _node_type_signed(self, pool: memhub.MemBuddy*,
|
||
node: ast.AST*,
|
||
trans: HT.Translator* = None) -> int:
|
||
"""源节点是否**明确有符号**整数(独立真源的 SIGNED 分支;未知返回 0)
|
||
|
||
只服务 should_zext_widen 的 i8 分支:i8 默认按「无符号字节」zext,
|
||
但源注解明确有符号(int8 / int8 / i8 / char …)时须 sext 保号。
|
||
判据走独立符号性真源(精确名 + CTypedef 链),与 _node_type_unsigned 的
|
||
遍历形态对应,但只覆盖**注解直接可见**的节点形态(Call(t.X) / Name /
|
||
Subscript);其余返回 0,即保持 i8 的既有默认行为(不扩大改动面)。
|
||
"""
|
||
if node is None:
|
||
return 0
|
||
k: int = node.kind()
|
||
if k == ast.ASTKind.Call:
|
||
cl: ast.Call* = ast.Call*(node)
|
||
if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute:
|
||
at: ast.Attribute* = ast.Attribute*(cl.func)
|
||
if at.value is not None and at.value.kind() == ast.ASTKind.Name:
|
||
mod_nm: ast.Name* = ast.Name*(at.value)
|
||
if mod_nm.id is not None and string.strcmp(mod_nm.id, "t") == 0 \
|
||
and at.attr is not None:
|
||
if self._spec_sign_from_annot(at.attr) == TSpec.TS_SIGN_SIGNED:
|
||
return 1
|
||
return 0
|
||
if k == ast.ASTKind.Name:
|
||
nm: ast.Name* = ast.Name*(node)
|
||
if nm.id is not None:
|
||
if self._spec_sign_from_annot(nm.id) == TSpec.TS_SIGN_SIGNED:
|
||
return 1
|
||
if trans is not None:
|
||
ve: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm.id)
|
||
if ve is not None:
|
||
ncn_s: str = self.Gen.VarGen.var_annot_class_name(ve)
|
||
if ncn_s is not None:
|
||
if self._spec_sign_from_annot(ncn_s) == TSpec.TS_SIGN_SIGNED:
|
||
return 1
|
||
return 0
|
||
if k == ast.ASTKind.Subscript:
|
||
sub_s: ast.Subscript* = ast.Subscript*(node)
|
||
if sub_s.value is not None and sub_s.value.kind() == ast.ASTKind.Name:
|
||
snm: ast.Name* = ast.Name*(sub_s.value)
|
||
if snm.id is not None and trans is not None:
|
||
ve_s: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, snm.id)
|
||
if ve_s is not None:
|
||
scn_s: str = self.Gen.VarGen.var_annot_class_name(ve_s)
|
||
if scn_s is not None:
|
||
if self._spec_sign_from_annot(scn_s) == TSpec.TS_SIGN_SIGNED:
|
||
return 1
|
||
return 0
|
||
return 0
|
||
# ============================================================
|
||
# S4 去字符串化(无符号判定块)——**真源唯一**,旧字符串路径已删
|
||
#
|
||
# 真源:TypeGenCType.spec_sign_of_annot(顶层 '|' 分段 → 去空白/外层括号 →
|
||
# '*' 后缀 → IDENT[INNER, ...] 取元素 → 去模块限定前缀 → 精确名白名单 →
|
||
# CTypedef 链逐层展开,限深 16 防环)。
|
||
# 它已完整覆盖旧路径的能力(含复合注解与别名链),故 S4 直接删掉旧字符串兜底:
|
||
# `_type_name_unsigned_legacy` / `_annot_str_unsigned_scan` /
|
||
# `_annot_str_unsigned_legacy` / `_last_dot_tail` / `_is_simple_ident`
|
||
# —— 那套 strstr 子串扫描正是 A4/A5 假阳性的源头(`BYTEPTR` 含 BYTE 被判
|
||
# 无符号;任何含 `uint32` 的串(含 sha1 前缀 mangled 名)都会命中)。
|
||
#
|
||
# 现语义:真源 UNKNOWN = **不表态** ⇒ 两个入口一律返回 0(保持编译器默认的
|
||
# zext/sext 取向)。若日后发现某名字确实是无符号却没被判出,正确处理是
|
||
# **补进 SpecSignFromTypeNameSpan 白名单 / 补 CTypedef 声明**,
|
||
# 而不是恢复子串扫描(那会把假阳性一并放回来)。
|
||
# ============================================================
|
||
def _spec_sign_from_annot(self, annot: str) -> int:
|
||
"""真源入口:注解 → TS_SIGN_*(UNKNOWN 表示不表态)"""
|
||
return self.Gen.TypeGenCType.spec_sign_of_annot(annot, 0)
|
||
# ============================================================
|
||
# _entry_spec_unsigned - 变量/字段条目的 Spec 是否**明确无符号整数**
|
||
#
|
||
# S4 切读:条目的 Spec 由 SpecFromLLVMType 反推(恒 TS_INT)+ SpecWithSign
|
||
# 按注解切 TS_UINT。因此 `Kind == TS_UINT` 是**注解明确表态无符号**的强信号,
|
||
# 可优先于注解名字符串判定直接采用。
|
||
#
|
||
# ⚠️ 反向不成立:`Kind == TS_INT` 既可能是"注解有符号"也可能是"未表态"
|
||
# (SpecFromLLVMType 默认),**不可**据此断言 SIGNED —— 故只切无符号方向,
|
||
# 有符号仍走注解名(Spec.SourceName)真源(避免把未表态误判成 signed)。
|
||
# ============================================================
|
||
def _entry_spec_unsigned(self, sp: TSpec.TypeSpec*) -> int:
|
||
"""Spec 是否明确无符号整数(TS_UINT);其它一律 0(未表态/不适用)"""
|
||
if sp is None:
|
||
return 0
|
||
if sp.Kind == TSpec.TS_UINT:
|
||
return 1
|
||
return 0
|
||
def _type_name_unsigned(self, type_name: str) -> int:
|
||
"""[S4] 类型名无符号判定:**真源唯一**(旧字符串兜底已删)
|
||
|
||
真源 UNKNOWN = 不表态 ⇒ 返回 0(非无符号),保持编译器默认的 zext/sext 取向。
|
||
"""
|
||
if type_name is not None:
|
||
if self._spec_sign_from_annot(type_name) == TSpec.TS_SIGN_UNSIGNED:
|
||
return 1
|
||
return 0
|
||
def _annot_str_unsigned(self, annot: str) -> int:
|
||
"""[S4] 注解串无符号判定:**真源唯一**(旧子串扫描已删)
|
||
|
||
真源按顶层 '|' 分段、去括号/空白、取 `IDENT[INNER]` 的元素、去模块限定前缀,
|
||
再走精确名白名单 + CTypedef 链展开 —— 已覆盖原 `_annot_str_unsigned_scan`
|
||
对复合注解(`t.CArray[uint8, 16]` / `uint8 | t.CPtr`)的全部能力,
|
||
且无子串假阳性。UNKNOWN = 不表态 ⇒ 返回 0。
|
||
"""
|
||
if annot is not None:
|
||
if self._spec_sign_from_annot(annot) == TSpec.TS_SIGN_UNSIGNED:
|
||
return 1
|
||
return 0
|
||
def translate_compare(self, builder: llvmlite.IRBuilder*,
|
||
pool: memhub.MemBuddy*,
|
||
mod: llvmlite.LLVMModule*,
|
||
node: ast.AST*,
|
||
trans: HT.Translator* = None) -> llvmlite.Value*:
|
||
"""翻译比较表达式,返回 i1 结果"""
|
||
cmp: rawptr[ast.Compare] = rawptr[ast.Compare](node)
|
||
if cmp is None:
|
||
return None
|
||
|
||
ops_list: list[int] | t.CPtr = (list[int] | t.CPtr)(cmp.ops)
|
||
if ops_list is None or len(ops_list) == 0:
|
||
return None
|
||
op: int = ops_list.get(0)
|
||
|
||
comparators: list[ast.AST*]* = cmp.comparators
|
||
if comparators is None or len(comparators) == 0:
|
||
return None
|
||
rhs_node: ast.AST* = comparators.get(0)
|
||
rhs: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, rhs_node, None, 0, trans)
|
||
if rhs is None:
|
||
return None
|
||
|
||
# === None 比较优化: x == None / x != None 走身份比较(icmp eq/ne null)===
|
||
# 避免对结构体指针生成无效的 __eq__/__ne__ 调用(方法未定义但类有 SHA1 时
|
||
# try_operator_overload 会误生成 call void @...__eq__(...) 导致链接错误)
|
||
# 语义依据: Python 中 == None / != None 默认走身份比较,不调用 __eq__/__ne__
|
||
lhs_node: ast.AST* = cmp.left
|
||
cmp_with_none: int = 0
|
||
if op == ast.OpKind.Eq or op == ast.OpKind.Ne or op == ast.OpKind.Is or op == ast.OpKind.IsNot:
|
||
if rhs_node is not None and rhs_node.kind() == ast.ASTKind.Constant:
|
||
rhs_cn: ast.Constant* = ast.Constant*(rhs_node)
|
||
if rhs_cn is not None and rhs_cn.const_kind == ast.CONST_NONE:
|
||
cmp_with_none = 1
|
||
if cmp_with_none == 0 and lhs_node is not None and lhs_node.kind() == ast.ASTKind.Constant:
|
||
lhs_cn: ast.Constant* = ast.Constant*(lhs_node)
|
||
if lhs_cn is not None and lhs_cn.const_kind == ast.CONST_NONE:
|
||
cmp_with_none = 1
|
||
|
||
# === 指针判空模式: ptr != 0 / ptr == 0 ===
|
||
# 跳过运算符重载走身份比较(icmp null)。跨模块类(如 cpython.PyObject)
|
||
# 未定义 __eq__/__ne__ 时,try_operator_overload 无法判定方法不存在
|
||
# (类在依赖模块,cls_sha1 != trans.ModuleSha1),会误发幽灵
|
||
# `call void @...__ne__` → 链接 undefined reference(CPythonTest main.vp
|
||
# `py_num != 0` 根因)。C 语义:指针与字面量 0 比较 = 判空。
|
||
cmp_zero: int = 0
|
||
if op == ast.OpKind.Eq or op == ast.OpKind.Ne:
|
||
if rhs_node is not None and self._is_zero_int_literal(rhs_node) != 0:
|
||
cmp_zero = 1
|
||
if cmp_zero == 0 and lhs_node is not None and self._is_zero_int_literal(lhs_node) != 0:
|
||
cmp_zero = 1
|
||
|
||
# === 比较运算符重载路径 1: lhs 是 Name 且对应结构体变量 ===
|
||
# 对于值类型变量(如 cnt: Counter),用 alloca 指针尝试重载
|
||
# None 比较跳过重载(走身份比较)
|
||
if cmp_with_none == 0 and lhs_node is not None and lhs_node.kind() == ast.ASTKind.Name and trans is not None:
|
||
nm: ast.Name* = ast.Name*(lhs_node)
|
||
if nm is not None and nm.id is not None:
|
||
lhs_alloca: llvmlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, nm.id)
|
||
if lhs_alloca is not None:
|
||
if cmp_zero == 0 or self.get_ptr_level(lhs_alloca.Ty) == 0:
|
||
ovl_self1: llvmlite.Value* = lhs_alloca
|
||
# Ptr(Ptr(Struct)) 对象存储槽:load 出对象指针作 self
|
||
# (与 translate_binop 路径 1 一致,避免槽地址当对象地址)
|
||
if lhs_alloca.Ty is not None and lhs_alloca.Ty.Pointee != 0 and self.Gen.ExprGenBase.is_ptr_type(lhs_alloca.Ty.Pointee) != 0:
|
||
load_self1: llvmlite.Value* = llvmlite.build_load(builder, lhs_alloca.Ty.Pointee, lhs_alloca)
|
||
if load_self1 is not None:
|
||
ovl_self1 = load_self1
|
||
ovl_result: llvmlite.Value* = self.try_operator_overload(pool, builder, mod, ovl_self1, rhs, op, trans, 1)
|
||
if ovl_result is not None:
|
||
return ovl_result
|
||
|
||
# 正常翻译 lhs
|
||
lhs: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, cmp.left, None, 0, trans)
|
||
if lhs is None:
|
||
return None
|
||
|
||
# === in / not in:容器右操作数调用 __contains__(对齐 TPC
|
||
# HandlesExprOps.py L261-281)。x in y → y.__contains__(x);
|
||
# x not in y → !y.__contains__(x)。容器无 __contains__ →
|
||
# 返回 i1 0(TPC 同:恒 False)。
|
||
if op == ast.OpKind.In or op == ast.OpKind.NotIn:
|
||
cn_st: llvmlite.LLVMType* = self.Gen.StructGen.get_struct_type_from_value(rhs)
|
||
if cn_st is not None:
|
||
cn_cls: str = self.Gen.StructGen.get_class_name_by_type(pool, cn_st)
|
||
if cn_cls is not None:
|
||
cn_lookup: str = pool.alloc(128)
|
||
if cn_lookup is not None:
|
||
viperlib.snprintf(cn_lookup, 128, "%s.__contains__", cn_cls)
|
||
cn_fn: llvmlite.Function* = self.Gen.CallGenBase.find_func_in_module(mod, cn_lookup)
|
||
if cn_fn is not None:
|
||
cn_ret: llvmlite.LLVMType* = llvmlite.function_get_ret_ty(cn_fn)
|
||
cn_fn_name: str = llvmlite.function_get_name(cn_fn)
|
||
if cn_ret is not None and cn_fn_name is not None:
|
||
# self 参数(容器)按 __contains__ 首参类型对齐
|
||
cn_self: llvmlite.Value* = rhs
|
||
cn_first: llvmlite.Param* = llvmlite.function_get_param_head(cn_fn)
|
||
if cn_first is not None:
|
||
cn_self_ty: llvmlite.LLVMType* = llvmlite.param_get_ty(cn_first)
|
||
if cn_self_ty is not None:
|
||
cn_self = llvmlite.build_bitcast(builder, rhs, cn_self_ty)
|
||
if cn_self is not None:
|
||
# 参数链表: self → key(独立 null 结尾)
|
||
cn_self.Next = lhs
|
||
lhs.Next = None
|
||
cn_res: llvmlite.Value* = llvmlite.build_call(builder, cn_fn_name, cn_self, 2, cn_ret, 0)
|
||
if cn_res is not None:
|
||
# 归一化到 i1(__contains__ 返回 int/i32 等)
|
||
cn_i1: llvmlite.Value* = cn_res
|
||
match cn_ret:
|
||
case llvmlite.LLVMType.Int(cn_bits):
|
||
if cn_bits > 1:
|
||
cn_zero: llvmlite.Value* = llvmlite.ConstInt(pool, cn_ret, 0, "0")
|
||
if cn_zero is not None:
|
||
cn_i1 = llvmlite.build_icmp(builder, llvmlite.ICMP_NE, cn_res, cn_zero)
|
||
case _:
|
||
pass
|
||
if op == ast.OpKind.NotIn:
|
||
i1_ty_ni: llvmlite.LLVMType* = llvmlite.Int1(pool)
|
||
if i1_ty_ni is not None and cn_i1 is not None:
|
||
one_i1_ni: llvmlite.Value* = llvmlite.ConstInt(pool, i1_ty_ni, 1, "1")
|
||
if one_i1_ni is not None:
|
||
cn_i1 = llvmlite.build_xor(builder, cn_i1, one_i1_ni)
|
||
if cn_i1 is not None:
|
||
return cn_i1
|
||
# 容器无 __contains__:TPC 语义恒 False(i1 0)
|
||
i1_ty_in: llvmlite.LLVMType* = llvmlite.Int1(pool)
|
||
if i1_ty_in is not None:
|
||
return llvmlite.ConstInt(pool, i1_ty_in, 0, "0")
|
||
return None
|
||
|
||
# === 比较运算符重载路径 2: lhs 是 Ptr(Struct) ===
|
||
# Is/IsNot 不在 _cmpop_to_dunder 映射中,会自动返回 None 回退原生比较
|
||
# None 比较跳过重载(走身份比较);指针判空(cmp_zero + 指针)同样跳过
|
||
if cmp_with_none == 0 and (cmp_zero == 0 or self.get_ptr_level(lhs.Ty) == 0):
|
||
ovl_result2: llvmlite.Value* = self.try_operator_overload(pool, builder, mod, lhs, rhs, op, trans, 1)
|
||
if ovl_result2 is not None:
|
||
return ovl_result2
|
||
|
||
# === 无符号比较(A5):操作数为无符号整数类型(CUInt64T/CUInt32T 等)时
|
||
# 用 ULT/ULE/UGT/UGE。此前固定有符号 predicate → uint64 地址比较在
|
||
# 跨 2^63 边界时语义错误(paging MakeRangeRO/save_pt_range 的
|
||
# `while va < vaddr_end` 生成 icmp slt,A5 根因)。复用 _node_type_unsigned
|
||
# (Name→变量 AnnotClassName / Call→转换目标 / Attribute→类型名 / 递归)。
|
||
unsigned_l: int = 0
|
||
unsigned_r: int = 0
|
||
if lhs_node is not None:
|
||
unsigned_l = self._node_type_unsigned(pool, lhs_node, trans)
|
||
if rhs_node is not None:
|
||
unsigned_r = self._node_type_unsigned(pool, rhs_node, trans)
|
||
cmp_unsigned: int = 1 if (unsigned_l != 0 or unsigned_r != 0) else 0
|
||
|
||
predicate: int = llvmlite.ICMP_SLT
|
||
if op == ast.OpKind.Lt:
|
||
predicate = llvmlite.ICMP_ULT if cmp_unsigned != 0 else llvmlite.ICMP_SLT
|
||
elif op == ast.OpKind.Le:
|
||
predicate = llvmlite.ICMP_ULE if cmp_unsigned != 0 else llvmlite.ICMP_SLE
|
||
elif op == ast.OpKind.Gt:
|
||
predicate = llvmlite.ICMP_UGT if cmp_unsigned != 0 else llvmlite.ICMP_SGT
|
||
elif op == ast.OpKind.Ge:
|
||
predicate = llvmlite.ICMP_UGE if cmp_unsigned != 0 else llvmlite.ICMP_SGE
|
||
elif op == ast.OpKind.Eq:
|
||
predicate = llvmlite.ICMP_EQ
|
||
elif op == ast.OpKind.Ne:
|
||
predicate = llvmlite.ICMP_NE
|
||
elif op == ast.OpKind.Is:
|
||
# `x is y` → 指针/值相等比较
|
||
predicate = llvmlite.ICMP_EQ
|
||
elif op == ast.OpKind.IsNot:
|
||
# `x is not y` → 指针/值不等比较
|
||
predicate = llvmlite.ICMP_NE
|
||
|
||
# 类型安全检查:数值 vs 数值 → 允许(按位宽 sext 对齐);一级指针 vs 一级指针 → 允许(地址比较);数值 vs 指针(1+) → 禁止(fast-fail,除 None 比较外);不同级指针(1 vs 2 等)→ 禁止。None 比较跳过此检查(None 译为 null 指针或 0,与任意指针/数值比较合法)
|
||
if cmp_with_none == 0:
|
||
lhs_ptr_level: int = self.get_ptr_level(lhs.Ty)
|
||
rhs_ptr_level: int = self.get_ptr_level(rhs.Ty)
|
||
if lhs_ptr_level != rhs_ptr_level:
|
||
# 指针 vs 整数字面量 0(判空模式 ptr != 0 / ptr == 0):
|
||
# 0 视为 NULL,转成与指针侧同类型的 null 指针(icmp 需要同类型),
|
||
# 不报类型不匹配(ClassArrayTest 的 if ptr != 0 等常见模式)。
|
||
zero_is_rhs: int = 0
|
||
zero_is_lhs: int = 0
|
||
if rhs_ptr_level == 0 and self._is_zero_int_literal(rhs_node) != 0:
|
||
zero_is_rhs = 1
|
||
if lhs_ptr_level == 0 and self._is_zero_int_literal(lhs_node) != 0:
|
||
zero_is_lhs = 1
|
||
if zero_is_rhs != 0 or zero_is_lhs != 0:
|
||
if zero_is_rhs != 0:
|
||
rhs = llvmlite.ConstNull(pool, lhs.Ty, "null")
|
||
else:
|
||
lhs = llvmlite.ConstNull(pool, rhs.Ty, "null")
|
||
else:
|
||
# 指针 vs 非零整数(C 哨兵值模式,如 w32.win32base.INVALID_HANDLE_VALUE = -1):
|
||
# 对齐 TPC HandlesExprOps.py L388-398 —— 指针 ptrtoint 成整数做地址值比较。
|
||
# (指针 vs 整数字面量 0 已在上方转 null 处理;此处处理非零整数哨兵)
|
||
if lhs_ptr_level != 0 and rhs_ptr_level == 0:
|
||
lhs = llvmlite.build_ptrtoint(builder, lhs, rhs.Ty)
|
||
elif rhs_ptr_level != 0 and lhs_ptr_level == 0:
|
||
rhs = llvmlite.build_ptrtoint(builder, rhs, lhs.Ty)
|
||
else:
|
||
# 不同级指针(1 vs 2 等)→ 禁止(fast-fail)
|
||
self.Gen.TypeGenErr.fatal_error(cmp, f"比较类型不匹配: 不同级指针比较 (lhs 层级={lhs_ptr_level}, rhs 层级={rhs_ptr_level})", "TypeError")
|
||
else:
|
||
# None 比较: None 被翻译为 i8* null(一级指针)
|
||
# 如果 lhs 是指针,将 rhs (i8* null) bitcast 为 lhs 的指针类型
|
||
# 如果 lhs 是数值,将 rhs 转为数值 0(走正常整数比较)
|
||
# 如果 lhs 是结构体值类型,结构体永远不等于 None,返回常量 i1
|
||
lhs_ptr_level_none: int = self.get_ptr_level(lhs.Ty)
|
||
rhs_ptr_level_none: int = self.get_ptr_level(rhs.Ty)
|
||
if lhs_ptr_level_none != 0 and rhs_ptr_level_none != 0:
|
||
# 两边都是指针,类型可能不同(如 lhs=Struct*, rhs=i8* null)
|
||
# 由下方 bitcast 对齐处理,此处不处理
|
||
pass
|
||
elif lhs_ptr_level_none != 0 and rhs_ptr_level_none == 0:
|
||
# lhs 是指针, rhs 是数值 → 将 rhs 转为同层级 null 指针
|
||
rhs = llvmlite.ConstNull(pool, lhs.Ty, "null")
|
||
elif rhs_ptr_level_none != 0 and lhs_ptr_level_none == 0:
|
||
# 检查 lhs 是否是结构体值类型(非指针的 Struct)
|
||
lhs_is_struct_none: int = 0
|
||
match lhs.Ty:
|
||
case llvmlite.LLVMType.Struct(_, _, _):
|
||
lhs_is_struct_none = 1
|
||
case _:
|
||
lhs_is_struct_none = 0
|
||
if lhs_is_struct_none != 0:
|
||
# 结构体值永远不等于 None:==/is 返回 false,!=/is not 返回 true
|
||
if op == ast.OpKind.Eq or op == ast.OpKind.Is:
|
||
return llvmlite.const_int(pool, 1, 0)
|
||
else: # Ne, IsNot
|
||
return llvmlite.const_int(pool, 1, 1)
|
||
# lhs 是数值, rhs 是 i8* null → 将 rhs 转为数值 0(保持 lhs 不变)
|
||
rhs = llvmlite.const_int32(pool, 0)
|
||
|
||
# 类型对齐:icmp 要求两边类型相同
|
||
# 仅对数值类型做对齐(指针类型无需对齐,LLVM 允许同层级指针直接 icmp)
|
||
# i1 (bool) 侧用 zext 对齐(1 保持 1,而非 sext 的 0xFF...)
|
||
lhs_bits: int = self.get_llvm_type_bits(lhs.Ty)
|
||
rhs_bits: int = self.get_llvm_type_bits(rhs.Ty)
|
||
if lhs_bits != 0 and rhs_bits != 0 and lhs_bits != rhs_bits:
|
||
if lhs_bits < rhs_bits:
|
||
if self.should_zext_widen(pool, lhs, rhs, cmp.left, trans) != 0:
|
||
lhs = llvmlite.build_zext(builder, lhs, rhs.Ty)
|
||
else:
|
||
lhs = llvmlite.build_sext(builder, lhs, rhs.Ty)
|
||
else:
|
||
if self.should_zext_widen(pool, rhs, lhs, rhs_node, trans) != 0:
|
||
rhs = llvmlite.build_zext(builder, rhs, lhs.Ty)
|
||
else:
|
||
rhs = llvmlite.build_sext(builder, rhs, lhs.Ty)
|
||
|
||
# 指针类型对齐:同层级但不同 pointee 类型的指针需要 bitcast 统一类型
|
||
# 例如 i8* vs Struct* → 将 rhs bitcast 为 lhs 的类型
|
||
if lhs_bits == 0 and rhs_bits == 0:
|
||
lhs_is_ptr_now: int = self.is_ptr_type(lhs.Ty)
|
||
rhs_is_ptr_now: int = self.is_ptr_type(rhs.Ty)
|
||
if lhs_is_ptr_now != 0 and rhs_is_ptr_now != 0:
|
||
# 两边都是指针,用 TypePrint 比较类型字符串
|
||
lhs_ty_buf: str = pool.alloc(128)
|
||
rhs_ty_buf: str = pool.alloc(128)
|
||
if lhs_ty_buf is not None and rhs_ty_buf is not None:
|
||
llvmlite.TypePrint(lhs_ty_buf, 128, lhs.Ty, pool)
|
||
llvmlite.TypePrint(rhs_ty_buf, 128, rhs.Ty, pool)
|
||
if string.strcmp(lhs_ty_buf, rhs_ty_buf) != 0:
|
||
rhs = llvmlite.build_bitcast(builder, rhs, lhs.Ty)
|
||
|
||
# 浮点比较:任一操作数为浮点类型时使用 fcmp 而非 icmp
|
||
lhs_fbits: int = self.get_llvm_float_bits(lhs.Ty)
|
||
rhs_fbits: int = self.get_llvm_float_bits(rhs.Ty)
|
||
if lhs_fbits != 0 or rhs_fbits != 0:
|
||
# 浮点类型对齐(fpext/fptrunc)
|
||
if lhs_fbits != 0 and rhs_fbits != 0 and lhs_fbits != rhs_fbits:
|
||
if lhs_fbits < rhs_fbits:
|
||
lhs = llvmlite.build_fpext(builder, lhs, rhs.Ty)
|
||
else:
|
||
rhs = llvmlite.build_fptrunc(builder, rhs, lhs.Ty)
|
||
elif lhs_fbits != 0 and rhs_fbits == 0:
|
||
# int → float
|
||
rhs = llvmlite.build_si2fp(builder, rhs, lhs.Ty)
|
||
elif rhs_fbits != 0 and lhs_fbits == 0:
|
||
lhs = llvmlite.build_si2fp(builder, lhs, rhs.Ty)
|
||
# 映射比较谓词到 fcmp
|
||
fpredicate: int = llvmlite.FCMP_OEQ
|
||
if op == ast.OpKind.Eq or op == ast.OpKind.Is:
|
||
fpredicate = llvmlite.FCMP_OEQ
|
||
elif op == ast.OpKind.Ne or op == ast.OpKind.IsNot:
|
||
fpredicate = llvmlite.FCMP_ONE
|
||
elif op == ast.OpKind.Lt:
|
||
fpredicate = llvmlite.FCMP_OLT
|
||
elif op == ast.OpKind.Le:
|
||
fpredicate = llvmlite.FCMP_OLE
|
||
elif op == ast.OpKind.Gt:
|
||
fpredicate = llvmlite.FCMP_OGT
|
||
elif op == ast.OpKind.Ge:
|
||
fpredicate = llvmlite.FCMP_OGE
|
||
return llvmlite.build_fcmp(builder, fpredicate, lhs, rhs)
|
||
|
||
return llvmlite.build_icmp(builder, predicate, lhs, rhs)
|
||
# ============================================================
|
||
# 翻译一元运算表达式 UnaryOp(op, operand)
|
||
# ============================================================
|
||
def translate_unaryop(self, builder: llvmlite.IRBuilder*,
|
||
pool: memhub.MemBuddy*,
|
||
mod: llvmlite.LLVMModule*,
|
||
node: ast.AST*,
|
||
trans: HT.Translator* = None) -> llvmlite.Value*:
|
||
"""翻译一元运算(-x, +x, not x, ~x)"""
|
||
uo: ast.UnaryOp* = ast.UnaryOp*(node)
|
||
if uo is None: return None
|
||
|
||
operand: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, uo.operand, None, 0, trans)
|
||
if operand is None: return None
|
||
|
||
# === 一元运算符重载: -obj/+obj/~obj 调用 __neg__/__pos__/__invert__ ===
|
||
# 仅 USub/UAdd/Invert 有重载语义(not 无 dunder 方法)
|
||
if uo.op == ast.OpKind.USub or uo.op == ast.OpKind.UAdd or uo.op == ast.OpKind.Invert:
|
||
ovl_u: llvmlite.Value* = self.try_unary_operator_overload(pool, builder, mod, operand, uo.op, trans)
|
||
if ovl_u is not None: return ovl_u
|
||
|
||
if uo.op == ast.OpKind.USub:
|
||
# -x
|
||
# 浮点类型用 fneg(直接生成 fneg 指令,避免 0.0 - x 的类型不匹配)
|
||
operand_fbits: int = self.get_llvm_float_bits(operand.Ty)
|
||
if operand_fbits > 0:
|
||
return llvmlite.build_fneg(builder, operand)
|
||
# 整数类型:-x = 0 - x,根据位宽选择零值常量(避免 i32 与 i64 类型不匹配)
|
||
operand_bits: int = self.get_llvm_type_bits(operand.Ty)
|
||
zero: llvmlite.Value* = llvmlite.const_int32(pool, 0)
|
||
if operand_bits == 64:
|
||
zero = llvmlite.const_int64(pool, 0)
|
||
elif operand_bits > 0 and operand_bits != 32:
|
||
# 其他位宽(i8/i16 等):用 i32 零值,operand 会被 coerce
|
||
zero = llvmlite.const_int32(pool, 0)
|
||
return llvmlite.build_sub(builder, zero, operand)
|
||
elif uo.op == ast.OpKind.UAdd:
|
||
# +x = x
|
||
return operand
|
||
elif uo.op == ast.OpKind.Not:
|
||
# not x = (x == 0/null),返回 i1
|
||
if self.is_ptr_type(operand.Ty) != 0:
|
||
# 指针类型:与 null 比较
|
||
null_op: llvmlite.Value* = llvmlite.ConstNull(pool, operand.Ty, "null")
|
||
return llvmlite.build_icmp(builder, llvmlite.ICMP_EQ, operand, null_op)
|
||
# 结构体值类型:永远为真(非空),not 返回 false
|
||
operand_is_struct_not: int = 0
|
||
match operand.Ty:
|
||
case llvmlite.LLVMType.Struct(_, _, _):
|
||
operand_is_struct_not = 1
|
||
case _:
|
||
operand_is_struct_not = 0
|
||
if operand_is_struct_not != 0:
|
||
return llvmlite.const_int(pool, 1, 0)
|
||
operand_bits_not: int = self.get_llvm_type_bits(operand.Ty)
|
||
zero = llvmlite.const_int32(pool, 0)
|
||
if operand_bits_not == 64:
|
||
zero = llvmlite.const_int64(pool, 0)
|
||
return llvmlite.build_icmp(builder, llvmlite.ICMP_EQ, operand, zero)
|
||
elif uo.op == ast.OpKind.Invert:
|
||
# ~x = x ^ -1
|
||
operand_bits_inv: int = self.get_llvm_type_bits(operand.Ty)
|
||
neg1: llvmlite.Value* = llvmlite.const_int32(pool, -1)
|
||
if operand_bits_inv == 64:
|
||
neg1 = llvmlite.const_int64(pool, -1)
|
||
return llvmlite.build_xor(builder, operand, neg1)
|
||
elif uo.op == ast.OpKind.Deref:
|
||
# *p → load 按 pointee 类型读取(对齐 translate_c_deref)
|
||
if self.Gen.ExprGenBase.is_ptr_type(operand.Ty) != 0:
|
||
d_pointee: llvmlite.LLVMType* = operand.Ty.Pointee
|
||
if d_pointee is not None:
|
||
return llvmlite.build_load(builder, d_pointee, operand)
|
||
# 非指针即类型推断退化:fail-fast,绝不按 i8 兜底读取
|
||
self.Gen.TypeGenErr.fatal_error(node, "单目 '*' 解引用的操作数必须是指针类型(类型推断退化)", "TypeError")
|
||
return None
|
||
elif uo.op == ast.OpKind.AddrOf:
|
||
# &x → 左值地址(不 load):Name 变量 alloca / 模块全局 / 模块函数 /
|
||
# Attribute 字段 GEP / Subscript 元素地址(对齐 translate_c_addr 语义)
|
||
if trans is None:
|
||
return None
|
||
if uo.operand.kind() == ast.ASTKind.Name:
|
||
nm_ao: ast.Name* = ast.Name*(uo.operand)
|
||
if nm_ao.id is not None:
|
||
# self/cls 对象引用:返回对象指针本身(Ptr(Struct))而非
|
||
# 指针变量的地址(Ptr(Ptr(Struct)))
|
||
if string.strcmp(nm_ao.id, "self") == 0 or string.strcmp(nm_ao.id, "cls") == 0:
|
||
se_ao: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm_ao.id)
|
||
if se_ao is not None and se_ao.Alloca is not None and se_ao.Alloca.Ty is not None:
|
||
match se_ao.Alloca.Ty:
|
||
case llvmlite.LLVMType.Ptr(sa1):
|
||
match sa1:
|
||
case llvmlite.LLVMType.Ptr(sa2):
|
||
obj_ty_sa: llvmlite.LLVMType* = llvmlite.Ptr(pool, sa2)
|
||
return llvmlite.build_load(builder, obj_ty_sa, se_ao.Alloca)
|
||
case _:
|
||
pass
|
||
case _:
|
||
pass
|
||
v_ao: llvmlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, nm_ao.id)
|
||
if v_ao is None and mod is not None:
|
||
# 模块全局变量:经 make_global_ref 转成带引号的 Ptr(Ty) 引用
|
||
gv_ao: llvmlite.GlobalVariable* = self.Gen.ExprGenAttr.find_global_in_module(mod, nm_ao.id)
|
||
if gv_ao is not None and gv_ao.Name is not None and gv_ao.Ty is not None:
|
||
v_ao = self.Gen.ExprGenAttr.make_global_ref(pool, gv_ao.Name, gv_ao.Ty)
|
||
if v_ao is not None and v_ao.Ty is not None:
|
||
# 对象指针变量(alloca 是 Ptr(Ptr(Struct)),存对象指针):
|
||
# 变量语义即对象,返回对象指针(load 一次),对齐 translate_c_addr
|
||
match v_ao.Ty:
|
||
case llvmlite.LLVMType.Ptr(ao1):
|
||
match ao1:
|
||
case llvmlite.LLVMType.Ptr(ao2):
|
||
match ao2:
|
||
case llvmlite.LLVMType.Struct(_, _, _):
|
||
obj_ty_ao: llvmlite.LLVMType* = llvmlite.Ptr(pool, ao2)
|
||
return llvmlite.build_load(builder, obj_ty_ao, v_ao)
|
||
case _:
|
||
pass
|
||
case _:
|
||
pass
|
||
case _:
|
||
pass
|
||
return v_ao
|
||
# 模块函数地址:函数符号本身
|
||
if v_ao is None and mod is not None:
|
||
f_ao: llvmlite.Function* = self.Gen.CallGenBase.find_func_in_module(mod, nm_ao.id)
|
||
if f_ao is not None:
|
||
fname_ao: str = llvmlite.function_get_name(f_ao)
|
||
if fname_ao is not None:
|
||
# 形参链是 Param 链,而 llvmlite.Func 的类型构造链是 ParamNode 链
|
||
# (不相关类)→ 经 _FuncTyFromFunction 重建(同 CallGenCBuiltin)。
|
||
fn_ty_ao: llvmlite.LLVMType* = self.Gen.CallGenBase._FuncTyFromFunction(pool, f_ao)
|
||
# 函数引用名按需加引号(数字开头/含 '.' 的 sha1 前缀名)
|
||
fnlen_ao: sizet = string.strlen(fname_ao)
|
||
needs_q_ao: int = self.Gen.ClassGenVTable._vtable_name_needs_quote(fname_ao)
|
||
fbuf_ao: str = pool.alloc(fnlen_ao + 4)
|
||
if fbuf_ao is not None and fn_ty_ao is not None:
|
||
if needs_q_ao != 0:
|
||
viperlib.snprintf(fbuf_ao, fnlen_ao + 4, "@\"%s\"", fname_ao)
|
||
else:
|
||
viperlib.snprintf(fbuf_ao, fnlen_ao + 4, "@%s", fname_ao)
|
||
return llvmlite.SSAValue(pool, llvmlite.Ptr(pool, fn_ty_ao), fbuf_ao)
|
||
elif uo.operand.kind() == ast.ASTKind.Attribute:
|
||
# 字段地址(GEP,不 load)
|
||
fptr_ao: llvmlite.Value* = self.Gen.ExprGenAttr.get_attribute_ptr(builder, pool, mod, uo.operand, trans)
|
||
if fptr_ao is not None:
|
||
return fptr_ao
|
||
elif uo.operand.kind() == ast.ASTKind.Subscript:
|
||
# 元素地址(GEP,不 load)
|
||
sptr_ao: llvmlite.Value* = self.Gen.ExprGenName.get_subscript_ptr(builder, pool, mod, uo.operand, trans)
|
||
if sptr_ao is not None:
|
||
return sptr_ao
|
||
elif uo.operand.kind() == ast.ASTKind.Call:
|
||
# &<call>() → rvalue 取址。复用上面已求值的 operand(绝不能二次求值:
|
||
# 构造调用会再跑一遍 __before_init__/__init__)。判定见 _addrof_of_call。
|
||
ac_ao: llvmlite.Value* = self._addrof_of_call(builder, pool, operand)
|
||
if ac_ao is not None:
|
||
return ac_ao
|
||
self.Gen.TypeGenErr.fatal_error(node, "单目 '&' 取地址的操作数必须是左值(Name/Attribute/Subscript)或构造调用/函数调用结果", "TypeError")
|
||
return None
|
||
return operand
|
||
|
||
def _addrof_of_call(self, builder: llvmlite.IRBuilder*,
|
||
pool: memhub.MemBuddy*,
|
||
call_val: llvmlite.Value*) -> llvmlite.Value*:
|
||
"""`&<call>()` 的取址结果(call_val 是调用结果,已由 translate_unaryop 求值一次)
|
||
|
||
两种形态:
|
||
· 结构体/类构造调用(`&ViperKernel()`):CallGenCtor._translate_struct_ctor
|
||
末尾明写"返回指针(而非结构体值)"——结果**就是**入口 alloca 的栈存储
|
||
指针 Ptr(Struct),即"栈构造结构体的地址"本身,直接原样返回。
|
||
不能在这里再套 alloca+store:那会得到 Ptr(Ptr(Struct))(存放地址的槽
|
||
地址),赋值端还得 load 一次才回落,语义也从"对象地址"变成"槽地址"。
|
||
· 其它返回值(标量、按值返回的结构体值等):rvalue 的地址只能靠临时槽
|
||
承载——入口 alloca 同类型槽 → store → 返回槽地址(与 c.Addr(expr) 的
|
||
Call 形式同口径,见 CallGenCBuiltin.translate_c_addr)。
|
||
"""
|
||
if call_val is None or call_val.Ty is None:
|
||
return None
|
||
if self.Gen.ExprGenBase.is_ptr_type(call_val.Ty) != 0 and call_val.Ty.Pointee is not None:
|
||
pointee_is_struct: int = 0
|
||
match call_val.Ty.Pointee:
|
||
case llvmlite.LLVMType.Struct(_, _, _):
|
||
pointee_is_struct = 1
|
||
case _:
|
||
pointee_is_struct = 0
|
||
if pointee_is_struct != 0:
|
||
return call_val
|
||
tmp_slot_ao: llvmlite.Value* = llvmlite.build_alloca_at_entry(builder, call_val.Ty)
|
||
if tmp_slot_ao is None:
|
||
return None
|
||
llvmlite.build_store(builder, call_val, tmp_slot_ao)
|
||
return tmp_slot_ao
|
||
# ============================================================
|
||
# 翻译布尔运算 BoolOp(op, values) — 短路求值,返回 i1
|
||
#
|
||
# and: a and b and c → 若 a 为假短路到 merge(返回 a 的 i1),否则求 b,
|
||
# 最后一个值直接求值并跳 merge
|
||
# or: a or b or c → 若 a 为真短路到 merge(返回 a 的 i1),否则求 b,
|
||
# 最后一个值直接求值并跳 merge
|
||
# merge 块用 phi 合并所有入边的 i1
|
||
# ============================================================
|
||
def translate_boolop(self, builder: llvmlite.IRBuilder*,
|
||
pool: memhub.MemBuddy*,
|
||
mod: llvmlite.LLVMModule*,
|
||
node: ast.AST*,
|
||
funcs_ptr: t.CPtr = None,
|
||
func_count: int = 0,
|
||
trans: HT.Translator* = None) -> llvmlite.Value*:
|
||
"""翻译布尔运算(and/or)的短路求值,返回 i1"""
|
||
bo: rawptr[ast.BoolOp] = rawptr[ast.BoolOp](node)
|
||
if bo is None:
|
||
return None
|
||
|
||
op: int = bo.op
|
||
values: list[ast.AST*]* = bo.values
|
||
if values is None:
|
||
return None
|
||
count: sizet = len(values)
|
||
if count == 0:
|
||
return None
|
||
if count == 1:
|
||
return self.Gen.ExprGen.translate_value(builder, pool, mod, values.get(0),
|
||
funcs_ptr, func_count, trans)
|
||
|
||
func: llvmlite.Function* = builder.Func
|
||
if func is None:
|
||
return None
|
||
|
||
# 创建 merge BB(用 builder.Counter 生成唯一标签名,position_at_end 的
|
||
# move_to_end 保证 BB 文本顺序与控制流顺序一致,标签名用 counter 安全)
|
||
cnt: int = builder.Counter
|
||
builder.Counter = cnt + 1
|
||
name_buf: str = pool.alloc(32)
|
||
if name_buf is None:
|
||
return None
|
||
viperlib.snprintf(name_buf, 32, "bool.merge.%d", cnt)
|
||
merge_bb: rawptr[llvmlite.BasicBlock] = llvmlite.create_block(pool, func, name_buf)
|
||
|
||
# phi 入边链表头/尾
|
||
phi_head: llvmlite.PhiIncoming* = None
|
||
phi_tail: llvmlite.PhiIncoming* = None
|
||
phi_count: int = count - 1 # 循环次数
|
||
|
||
# 对每个值(除最后一个)求值并短路
|
||
for i in range(count - 1):
|
||
val_node: ast.AST* = values.get(i)
|
||
val: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, val_node, funcs_ptr, func_count, trans)
|
||
if val is None:
|
||
return None
|
||
|
||
# 转为 i1(已经是 i1 的直接用,否则与 0/null 比较)
|
||
val_bits: int = self.get_llvm_type_bits(val.Ty)
|
||
val_i1: llvmlite.Value* = val
|
||
if val_bits != 1:
|
||
if self.is_ptr_type(val.Ty) != 0:
|
||
# 指针类型:与 null 比较(避免 i8* 与 i32 类型不匹配)
|
||
null_val: llvmlite.Value* = llvmlite.ConstNull(pool, val.Ty, "null")
|
||
val_i1 = llvmlite.build_icmp(builder, llvmlite.ICMP_NE, val, null_val)
|
||
else:
|
||
zero: llvmlite.Value* = llvmlite.const_int32(pool, 0)
|
||
val_i1 = llvmlite.build_icmp(builder, llvmlite.ICMP_NE, val, zero)
|
||
|
||
# 创建 next BB(求值下一个值)
|
||
cnt = builder.Counter
|
||
builder.Counter = cnt + 1
|
||
viperlib.snprintf(name_buf, 32, "bool.next.%d", cnt)
|
||
next_bb: rawptr[llvmlite.BasicBlock] = llvmlite.create_block(pool, func, name_buf)
|
||
|
||
# and: 短路假→merge,真→next;or: 短路真→merge,假→next
|
||
if op == ast.OpKind.And:
|
||
llvmlite.build_cond_br(builder, val_i1, next_bb, merge_bb)
|
||
else:
|
||
llvmlite.build_cond_br(builder, val_i1, merge_bb, next_bb)
|
||
|
||
# 添加 phi 入边:(val_i1, cond_br 所在 BB)
|
||
cur_bb: rawptr[llvmlite.BasicBlock] = builder.CurBlock
|
||
inc: llvmlite.PhiIncoming* = llvmlite.new_phi_incoming(pool, val_i1, cur_bb)
|
||
if phi_head is None:
|
||
phi_head = inc
|
||
phi_tail = inc
|
||
else:
|
||
phi_tail.Next = inc
|
||
phi_tail = inc
|
||
|
||
# 定位到 next BB,求值下一个值
|
||
llvmlite.position_at_end(builder, next_bb)
|
||
|
||
# 最后一个值:求值后直接跳 merge
|
||
last_node: ast.AST* = values.get(count - 1)
|
||
last_val: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, last_node, funcs_ptr, func_count, trans)
|
||
if last_val is None:
|
||
return None
|
||
last_bits: int = self.get_llvm_type_bits(last_val.Ty)
|
||
last_i1: llvmlite.Value* = last_val
|
||
if last_bits != 1:
|
||
if self.is_ptr_type(last_val.Ty) != 0:
|
||
# 指针类型:与 null 比较(避免 i8* 与 i32 类型不匹配)
|
||
null_val2: llvmlite.Value* = llvmlite.ConstNull(pool, last_val.Ty, "null")
|
||
last_i1 = llvmlite.build_icmp(builder, llvmlite.ICMP_NE, last_val, null_val2)
|
||
else:
|
||
zero = llvmlite.const_int32(pool, 0)
|
||
last_i1 = llvmlite.build_icmp(builder, llvmlite.ICMP_NE, last_val, zero)
|
||
|
||
llvmlite.build_br(builder, merge_bb)
|
||
|
||
cur_bb = builder.CurBlock
|
||
inc = llvmlite.new_phi_incoming(pool, last_i1, cur_bb)
|
||
if phi_head is None:
|
||
phi_head = inc
|
||
else:
|
||
phi_tail.Next = inc
|
||
phi_count += 1
|
||
|
||
# 定位到 merge,创建 phi 合并所有入边
|
||
llvmlite.position_at_end(builder, merge_bb)
|
||
i1_ty: llvmlite.LLVMType* = llvmlite.Int1(pool)
|
||
return llvmlite.build_phi(builder, i1_ty, phi_head, phi_count)
|
||
# ============================================================
|
||
# BinOp 运算符 → dunder 方法名
|
||
# ============================================================
|
||
def _binop_to_dunder(self, op: int) -> str:
|
||
"""将 BinOp 运算符映射到 dunder 方法名,无映射时返回 None
|
||
|
||
Python 3 语义:/ 映射 __truediv__(Python 2 的 __div__ 已废弃)。
|
||
"""
|
||
if op == ast.OpKind.Add: return "__add__"
|
||
if op == ast.OpKind.Sub: return "__sub__"
|
||
if op == ast.OpKind.Mult: return "__mul__"
|
||
if op == ast.OpKind.Div: return "__truediv__"
|
||
if op == ast.OpKind.Mod: return "__mod__"
|
||
if op == ast.OpKind.BitAnd: return "__and__"
|
||
if op == ast.OpKind.BitOr: return "__or__"
|
||
if op == ast.OpKind.BitXor: return "__xor__"
|
||
if op == ast.OpKind.LShift: return "__lshift__"
|
||
if op == ast.OpKind.RShift: return "__rshift__"
|
||
if op == ast.OpKind.FloorDiv: return "__floordiv__"
|
||
return None
|
||
# ============================================================
|
||
# Compare 运算符 → dunder 方法名
|
||
# ============================================================
|
||
def _cmpop_to_dunder(self, op: int) -> str:
|
||
"""将 Compare 运算符映射到 dunder 方法名,无映射时返回 None"""
|
||
if op == ast.OpKind.Eq: return "__eq__"
|
||
if op == ast.OpKind.Ne: return "__ne__"
|
||
if op == ast.OpKind.Lt: return "__lt__"
|
||
if op == ast.OpKind.Le: return "__le__"
|
||
if op == ast.OpKind.Gt: return "__gt__"
|
||
if op == ast.OpKind.Ge: return "__ge__"
|
||
return None
|
||
# ============================================================
|
||
# UnaryOp 运算符 → dunder 方法名
|
||
# ============================================================
|
||
def _unaryop_to_dunder(self, op: int) -> str:
|
||
"""将 UnaryOp 运算符映射到 dunder 方法名,无映射时返回 None"""
|
||
if op == ast.OpKind.USub: return "__neg__"
|
||
if op == ast.OpKind.UAdd: return "__pos__"
|
||
if op == ast.OpKind.Invert: return "__invert__"
|
||
return None
|
||
# ============================================================
|
||
# try_operator_overload - 尝试运算符重载
|
||
#
|
||
# 检查 lhs 是否为结构体指针,并在该类(含继承链)中查找对应的
|
||
# dunder 方法。找到则生成方法调用 lhs.__dunder__(rhs),返回
|
||
# 调用结果;未找到返回 None,调用方回退到原生运算。
|
||
# ============================================================
|
||
def try_operator_overload(self, pool: memhub.MemBuddy*,
|
||
builder: llvmlite.IRBuilder*,
|
||
mod: llvmlite.LLVMModule*,
|
||
lhs: llvmlite.Value*,
|
||
rhs: llvmlite.Value*,
|
||
op: int,
|
||
trans: HT.Translator*,
|
||
is_compare: int) -> llvmlite.Value*:
|
||
"""尝试运算符重载:lhs 是结构体指针时调用 dunder 方法,否则返回 None"""
|
||
if lhs is None or rhs is None:
|
||
return None
|
||
# 映射运算符到 dunder 方法名
|
||
dunder: str = None
|
||
if is_compare != 0:
|
||
dunder = self._cmpop_to_dunder(op)
|
||
else:
|
||
dunder = self._binop_to_dunder(op)
|
||
if dunder is None:
|
||
return None
|
||
|
||
# 检查 lhs 是否为结构体指针
|
||
struct_ty: llvmlite.LLVMType* = self.Gen.StructGen.get_struct_type_from_value(lhs)
|
||
if struct_ty is None:
|
||
return None
|
||
|
||
# 获取类名
|
||
class_name: str = self.Gen.StructGen.get_class_name_by_type(pool, struct_ty)
|
||
if class_name is None:
|
||
return None
|
||
|
||
# 在继承链中查找 dunder 方法
|
||
lookup_name: str = pool.alloc(128)
|
||
if lookup_name is None:
|
||
return None
|
||
viperlib.snprintf(lookup_name, 128, "%s.%s", class_name, dunder)
|
||
found_func: llvmlite.Function* = self.Gen.CallGenBase.find_func_in_module(mod, lookup_name)
|
||
|
||
search_class: str = class_name
|
||
if found_func is None:
|
||
# 多继承:沿 MRO 祖先链查找(先声明父类优先)
|
||
mro_ptr: sizet* = self.Gen.StructGen.get_mro_chain(pool, class_name)
|
||
mi: int = 1
|
||
while mro_ptr is not None and mro_ptr[mi] != 0:
|
||
anc: str = rawptr(mro_ptr[mi])
|
||
if anc is not None:
|
||
parent_lookup: str = pool.alloc(128)
|
||
if parent_lookup is not None:
|
||
viperlib.snprintf(parent_lookup, 128, "%s.%s", anc, dunder)
|
||
parent_func: llvmlite.Function* = self.Gen.CallGenBase.find_func_in_module(mod, parent_lookup)
|
||
if parent_func is not None:
|
||
found_func = parent_func
|
||
search_class = anc
|
||
break
|
||
mi += 1
|
||
|
||
# 方法不在当前模块中时,检查类(含父类)是否有 SHA1(stub 可能未注入)
|
||
# 优先用类型指针定位 entry 获取 SHA1(规避跨模块同名 find_struct 找错)
|
||
cls_sha1: str = None
|
||
op_entry: GenTypes.StructEntry* = self.Gen.StructGen.find_struct_by_type(struct_ty)
|
||
if op_entry is not None:
|
||
cls_sha1 = op_entry.ModuleSha1
|
||
if cls_sha1 is None:
|
||
cls_sha1 = self.Gen.StructGen.get_struct_sha1(search_class)
|
||
if cls_sha1 is None:
|
||
mro_p: sizet* = self.Gen.StructGen.get_mro_chain(pool, search_class)
|
||
mj: int = 1
|
||
while mro_p is not None and mro_p[mj] != 0 and cls_sha1 is None:
|
||
anc_p: str = rawptr(mro_p[mj])
|
||
if anc_p is not None:
|
||
cls_sha1 = self.Gen.StructGen.get_struct_sha1(anc_p)
|
||
mj += 1
|
||
|
||
# 既无函数定义也无 SHA1 → 该类没有此 dunder 方法,返回 None 回退原生运算
|
||
if found_func is None and cls_sha1 is None: return None
|
||
|
||
# 方法在"定义它的模块"中确实不存在(同模块类但模块内无此 dunder):
|
||
# 返回 None 回退原生运算,绝不发出幽灵 call——否则 IR 残留
|
||
# `call void @...__div__`(副作用)且调用方回退原生 sdiv 双重发射,
|
||
# llc 报 invalid operand type for instruction(CPythonTest PyFloatObj 根因)
|
||
if found_func is None and trans is not None and trans.ModuleSha1 is not None:
|
||
if cls_sha1 is not None and string.strcmp(cls_sha1, trans.ModuleSha1) == 0: return None
|
||
# 跨模块类:stub 可能未注入,方法存在性以依赖模块 vpi stub 为准。
|
||
# vpi 未命中(依赖模块确实无此 dunder)→ 回退原生比较(icmp),
|
||
# 绝不发幽灵 call——`call @{cls_sha1}.{Class}.{dunder}` 链接 undefined
|
||
# (MemBuddy 无 __eq__ 时 os._mbuddy == sys._mbuddy,SysLibTest 根因)。
|
||
vpi_dup: llvmlite.LLVMType* = self.Gen.CallGenBase._lookup_method_ret_ty_via_vpi(pool, cls_sha1, search_class, dunder)
|
||
if vpi_dup is None: return None
|
||
|
||
# 构建 extra_args 数组(仅含 rhs 一个参数)
|
||
extra_args: sizet* = pool.alloc(8)
|
||
if extra_args is None: return None
|
||
extra_args[0] = sizet(rhs)
|
||
|
||
# 调用方法: lhs.__dunder__(rhs)
|
||
return self.Gen.CallGenCall._call_method_on_ptr(pool, builder, mod, search_class, dunder, lhs, extra_args, 1, trans)
|
||
# ============================================================
|
||
# try_unary_operator_overload - 尝试一元运算符重载
|
||
#
|
||
# 检查 operand 是否为结构体指针,并在该类(含继承链)中查找对应的
|
||
# dunder 方法(__neg__/__pos__/__invert__)。找到则生成方法调用
|
||
# operand.__dunder__(),返回调用结果;未找到返回 None,调用方回退到原生运算。
|
||
# ============================================================
|
||
def try_unary_operator_overload(self, pool: memhub.MemBuddy*,
|
||
builder: llvmlite.IRBuilder*,
|
||
mod: llvmlite.LLVMModule*,
|
||
operand: llvmlite.Value*,
|
||
op: int,
|
||
trans: HT.Translator*) -> llvmlite.Value*:
|
||
"""尝试一元运算符重载:operand 是结构体指针时调用 dunder 方法,否则返回 None"""
|
||
if operand is None:
|
||
return None
|
||
dunder: str = self._unaryop_to_dunder(op)
|
||
if dunder is None:
|
||
return None
|
||
|
||
# 检查 operand 是否为结构体指针
|
||
struct_ty: llvmlite.LLVMType* = self.Gen.StructGen.get_struct_type_from_value(operand)
|
||
if struct_ty is None:
|
||
return None
|
||
|
||
# 获取类名
|
||
class_name: str = self.Gen.StructGen.get_class_name_by_type(pool, struct_ty)
|
||
if class_name is None:
|
||
return None
|
||
|
||
# 在继承链中查找 dunder 方法
|
||
lookup_name: str = pool.alloc(128)
|
||
if lookup_name is None:
|
||
return None
|
||
viperlib.snprintf(lookup_name, 128, "%s.%s", class_name, dunder)
|
||
found_func: llvmlite.Function* = self.Gen.CallGenBase.find_func_in_module(mod, lookup_name)
|
||
|
||
search_class: str = class_name
|
||
if found_func is None:
|
||
# 多继承:沿 MRO 祖先链查找(先声明父类优先)
|
||
mro_ptr: sizet* = self.Gen.StructGen.get_mro_chain(pool, class_name)
|
||
mi: int = 1
|
||
while mro_ptr is not None and mro_ptr[mi] != 0:
|
||
anc: str = rawptr(mro_ptr[mi])
|
||
if anc is not None:
|
||
parent_lookup: str = pool.alloc(128)
|
||
if parent_lookup is not None:
|
||
viperlib.snprintf(parent_lookup, 128, "%s.%s", anc, dunder)
|
||
parent_func: llvmlite.Function* = self.Gen.CallGenBase.find_func_in_module(mod, parent_lookup)
|
||
if parent_func is not None:
|
||
found_func = parent_func
|
||
search_class = anc
|
||
break
|
||
mi += 1
|
||
|
||
# 方法不在当前模块中时,检查类(含父类)是否有 SHA1(stub 可能未注入)
|
||
cls_sha1: str = None
|
||
uop_entry: GenTypes.StructEntry* = self.Gen.StructGen.find_struct_by_type(struct_ty)
|
||
if uop_entry is not None:
|
||
cls_sha1 = uop_entry.ModuleSha1
|
||
if cls_sha1 is None:
|
||
cls_sha1 = self.Gen.StructGen.get_struct_sha1(search_class)
|
||
if cls_sha1 is None:
|
||
mro_p: sizet* = self.Gen.StructGen.get_mro_chain(pool, search_class)
|
||
mj: int = 1
|
||
while mro_p is not None and mro_p[mj] != 0 and cls_sha1 is None:
|
||
anc_p: str = rawptr(mro_p[mj])
|
||
if anc_p is not None:
|
||
cls_sha1 = self.Gen.StructGen.get_struct_sha1(anc_p)
|
||
mj += 1
|
||
|
||
# 既无函数定义也无 SHA1 → 该类没有此 dunder 方法,返回 None 回退原生运算
|
||
if found_func is None and cls_sha1 is None:
|
||
return None
|
||
|
||
# 同模块类但模块内无此 dunder → 返回 None 回退原生运算,不发幽灵 call
|
||
# (与 try_operator_overload 一致,防 IR 残留 call void @...__neg__)
|
||
if found_func is None and trans is not None and trans.ModuleSha1 is not None:
|
||
if cls_sha1 is not None and string.strcmp(cls_sha1, trans.ModuleSha1) == 0:
|
||
return None
|
||
|
||
# 一元方法无额外参数(extra_args 为空)
|
||
return self.Gen.CallGenCall._call_method_on_ptr(pool, builder, mod, search_class, dunder, operand, None, 0, trans)
|
||
# ============================================================
|
||
# fail_if_double_ptr_binop - 双指针算术 fail-fast(**唯一实现**)
|
||
#
|
||
# BinOp(translate_binop)与 AugAssign(AssignGenMixin._apply_aug_op)共用此
|
||
# 判据与报文,避免两处各写一份导致分叉(此前只有 translate_binop 有一份,
|
||
# 且只覆盖 Add/Sub,AugAssign 与 Mult/Div/Mod/Shift 均未受保护)。
|
||
#
|
||
# 判据:运算符属二元算术/位运算/移位集合 **且** 两操作数都是 LLVM 指针。
|
||
# 本语言的 str 是 i8*,没有 `+` 拼接语义;指针 ± 指针也不是 GEP 语义
|
||
# (GEP 只接受「一指针一整数」,由调用方在此之前用 translate_ptr_arith 分流)。
|
||
# 落到 build_add/build_sub/… 会直出非法 IR(`add i8*, i8*` ⇒ opt 报
|
||
# invalid operand type for instruction),故在 IR 生成前 fail-fast。
|
||
#
|
||
# is_aug != 0 表示增量赋值(+=、-=、…):报文把符号补成 "+=" 形态并标注
|
||
# 「(增量赋值)」,避免只报裸 "+",便于定位。
|
||
# 命中即 fatal_error 终止编译;未命中返回 0。
|
||
# ============================================================
|
||
def fail_if_double_ptr_binop(self, pool: memhub.MemBuddy*,
|
||
op: int,
|
||
lhs: llvmlite.Value*,
|
||
rhs: llvmlite.Value*,
|
||
node: ast.AST* = None,
|
||
is_aug: int = 0) -> int:
|
||
"""双指针算术 fail-fast;命中即终止编译,未命中返回 0"""
|
||
if lhs is None or rhs is None:
|
||
return 0
|
||
if self.is_ptr_type(lhs.Ty) == 0 or self.is_ptr_type(rhs.Ty) == 0:
|
||
return 0
|
||
op_ch: str = None
|
||
if op == ast.OpKind.Add: op_ch = "+"
|
||
elif op == ast.OpKind.Sub: op_ch = "-"
|
||
elif op == ast.OpKind.Mult: op_ch = "*"
|
||
elif op == ast.OpKind.Div: op_ch = "/"
|
||
elif op == ast.OpKind.FloorDiv: op_ch = "//"
|
||
elif op == ast.OpKind.Mod: op_ch = "%"
|
||
elif op == ast.OpKind.Pow: op_ch = "**"
|
||
elif op == ast.OpKind.BitAnd: op_ch = "&"
|
||
elif op == ast.OpKind.BitOr: op_ch = "|"
|
||
elif op == ast.OpKind.BitXor: op_ch = "^"
|
||
elif op == ast.OpKind.LShift: op_ch = "<<"
|
||
elif op == ast.OpKind.RShift: op_ch = ">>"
|
||
else:
|
||
return 0
|
||
op_lbl: str = op_ch
|
||
aug_note: str = ""
|
||
if is_aug != 0:
|
||
# 增量赋值:用 f-string 拼 "+=" 形态(本语言 str 无 `+` 拼接语义,
|
||
# 禁止 op_ch + "=" 这种裸字符串加法)
|
||
op_lbl = f"{op_ch}="
|
||
aug_note = "(增量赋值)"
|
||
lty_buf: str = pool.alloc(64)
|
||
rty_buf: str = pool.alloc(64)
|
||
lty_lbl: str = "?"
|
||
rty_lbl: str = "?"
|
||
if lty_buf is not None:
|
||
llvmlite.TypePrint(lty_buf, 64, lhs.Ty, pool)
|
||
lty_lbl = lty_buf
|
||
if string.strcmp(lty_buf, "i8*") == 0:
|
||
lty_lbl = "str(i8*)"
|
||
if rty_buf is not None:
|
||
llvmlite.TypePrint(rty_buf, 64, rhs.Ty, pool)
|
||
rty_lbl = rty_buf
|
||
if string.strcmp(rty_buf, "i8*") == 0:
|
||
rty_lbl = "str(i8*)"
|
||
col_s: int = 0
|
||
if node is not None:
|
||
col_s = node.col_offset + 1
|
||
self.Gen.TypeGenErr.fatal_error(
|
||
node,
|
||
f"[TPV] 双指针运算类型非法: op = '{op_lbl}'{aug_note},"
|
||
f"左操作数类型 = {lty_lbl},右操作数类型 = {rty_lbl}。"
|
||
f"本语言的 str 是 i8*,没有 `+` 拼接语义(`str + str` 与指针 ± 指针"
|
||
f"都会退化成裸指针 add/sub ⇒ 非法 IR: opt 报 "
|
||
f"invalid operand type for instruction)。"
|
||
f"禁止静默降级,已终止编译。"
|
||
f"修法: 用 f-string 插值、string.strcat 或 viperlib.snprintf 构建字符串。",
|
||
"TypeError", col_s, col_s)
|
||
return 1
|
||
# ============================================================
|
||
# 翻译二元运算(自动类型提升)
|
||
# ============================================================
|
||
def translate_binop(self, pool: memhub.MemBuddy*,
|
||
builder: llvmlite.IRBuilder*,
|
||
mod: llvmlite.LLVMModule*,
|
||
node: ast.AST*,
|
||
trans: HT.Translator* = None) -> llvmlite.Value*:
|
||
"""翻译二元运算(自动类型提升)"""
|
||
binop: ast.BinOp* = ast.BinOp*(node)
|
||
if binop is None:
|
||
return None
|
||
|
||
lhs_node: ast.AST* = binop.left
|
||
rhs_node: ast.AST* = binop.right
|
||
op: int = binop.op
|
||
|
||
# 先翻译 rhs(只翻译一次,避免副作用重复)
|
||
rhs: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, rhs_node, None, 0, trans)
|
||
if rhs is None:
|
||
return None
|
||
|
||
# === 运算符重载路径 1: lhs 是 Name 且对应结构体变量 ===
|
||
# 对于值类型变量(如 cnt: Counter),translate_value 会 load 返回 Struct 值,
|
||
# 但 dunder 方法需要 Ptr(Struct) 作为 self。直接用 alloca 指针尝试重载。
|
||
if lhs_node is not None and lhs_node.kind() == ast.ASTKind.Name and trans is not None:
|
||
nm: ast.Name* = ast.Name*(lhs_node)
|
||
if nm is not None and nm.id is not None:
|
||
lhs_alloca: llvmlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, nm.id)
|
||
if lhs_alloca is not None:
|
||
ovl_self1: llvmlite.Value* = lhs_alloca
|
||
# alloca 是 Ptr(Ptr(Struct))(X|t.CPtr 对象存储槽)时,
|
||
# load 出对象指针作为 self——若把槽地址当 self,dunder 内
|
||
# load 槽内容(对象指针值)当字段读 → 垃圾(MultiInheritTest
|
||
# Vec2Both __add__ 输出 (1.0,2.0) 根因;get_struct_type_from_value
|
||
# 递归解指针后路径 1 能识别 alloca,需同步修正 self 语义)
|
||
if lhs_alloca.Ty is not None and lhs_alloca.Ty.Pointee != 0 and self.Gen.ExprGenBase.is_ptr_type(lhs_alloca.Ty.Pointee) != 0:
|
||
load_self1: llvmlite.Value* = llvmlite.build_load(builder, lhs_alloca.Ty.Pointee, lhs_alloca)
|
||
if load_self1 is not None:
|
||
ovl_self1 = load_self1
|
||
ovl_result: llvmlite.Value* = self.try_operator_overload(pool, builder, mod, ovl_self1, rhs, op, trans, 0)
|
||
if ovl_result is not None:
|
||
return ovl_result
|
||
|
||
# 正常翻译 lhs
|
||
lhs: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, lhs_node, None, 0, trans)
|
||
if lhs is None:
|
||
return None
|
||
|
||
# === 运算符重载路径 2: lhs 是 Ptr(Struct)(如 Counter|t.CPtr 变量 load 后)===
|
||
ovl_result2: llvmlite.Value* = self.try_operator_overload(pool, builder, mod, lhs, rhs, op, trans, 0)
|
||
if ovl_result2 is not None:
|
||
return ovl_result2
|
||
|
||
# 获取操作数类型信息
|
||
lhs_bits: int = self.get_llvm_type_bits(lhs.Ty)
|
||
rhs_bits: int = self.get_llvm_type_bits(rhs.Ty)
|
||
lhs_fbits: int = self.get_llvm_float_bits(lhs.Ty)
|
||
rhs_fbits: int = self.get_llvm_float_bits(rhs.Ty)
|
||
|
||
# 浮点运算: 任一操作数为浮点时,使用浮点指令(必须在指针算术之前检查,
|
||
# 因为 float 的 int bits 为 0,会被误认为指针)
|
||
if lhs_fbits != 0 or rhs_fbits != 0:
|
||
# 确定目标浮点类型(使用较大的位宽)
|
||
target_fbits: int = lhs_fbits
|
||
if rhs_fbits > target_fbits:
|
||
target_fbits = rhs_fbits
|
||
target_float_ty: llvmlite.LLVMType* = llvmlite.Double(pool)
|
||
if target_fbits == 32:
|
||
target_float_ty = llvmlite.Float(pool)
|
||
# 将两个操作数转换为目标浮点类型(int→float via si2fp, float→float via fpext/fptrunc)
|
||
lhs = self.coerce_to_type(builder, lhs, target_float_ty, node)
|
||
rhs = self.coerce_to_type(builder, rhs, target_float_ty, node)
|
||
if op == ast.OpKind.Add:
|
||
return llvmlite.build_fadd(builder, lhs, rhs)
|
||
if op == ast.OpKind.Sub:
|
||
return llvmlite.build_fsub(builder, lhs, rhs)
|
||
if op == ast.OpKind.Mult:
|
||
return llvmlite.build_fmul(builder, lhs, rhs)
|
||
if op == ast.OpKind.Div:
|
||
return llvmlite.build_fdiv(builder, lhs, rhs)
|
||
if op == ast.OpKind.Mod:
|
||
return llvmlite.build_frem(builder, lhs, rhs)
|
||
return None
|
||
|
||
# 指针算术: ptr + int / ptr - int / int + ptr → getelementptr(按元素大小缩放)
|
||
# 对齐 TPC ExprGen.emit_binary_op L50-67;int - ptr 无 GEP 形式(C 语义非法)
|
||
if (lhs_bits == 0 and rhs_bits != 0) or (lhs_bits != 0 and rhs_bits == 0):
|
||
if op == ast.OpKind.Add:
|
||
if lhs_bits == 0:
|
||
return self.translate_ptr_arith(pool, builder, lhs, rhs, op)
|
||
# int + ptr(交换)
|
||
return self.translate_ptr_arith(pool, builder, rhs, lhs, op)
|
||
if op == ast.OpKind.Sub and lhs_bits == 0:
|
||
return self.translate_ptr_arith(pool, builder, lhs, rhs, op)
|
||
|
||
# 双指针算术(含 AugAssign)fail-fast:判据/报文唯一实现见
|
||
# fail_if_double_ptr_binop(本文件;BinOp 与 AssignGenMixin._apply_aug_op 共用)。
|
||
# 指针 ± 整数(GEP 语义)已在上方 translate_ptr_arith 分流;此处的双指针
|
||
# Add/Sub/Mult/Div/Mod/Shift/位运算在本语言非法(str 是 i8*,无 `+` 拼接语义),
|
||
# 落到 build_add/build_sub/… 会直出非法 IR(`add i8*, i8*`)。
|
||
self.fail_if_double_ptr_binop(pool, op, lhs, rhs, node, 0)
|
||
|
||
# 拓宽方向(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, trans) != 0:
|
||
rhs = llvmlite.build_zext(builder, rhs, lhs.Ty)
|
||
else:
|
||
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, trans) != 0:
|
||
lhs = llvmlite.build_zext(builder, lhs, rhs.Ty)
|
||
else:
|
||
lhs = self.coerce_to_type(builder, lhs, rhs.Ty, node, trans)
|
||
|
||
# 位运算类型安全检查:两边都是指针(lhs_bits==0 and rhs_bits==0)时,
|
||
# 位运算(BitOr/BitAnd/BitXor/LShift/RShift)非法,LLVM 要求整数操作数。
|
||
# 常见触发场景:CDefine 常量查找失败返回指针 null,与另一指针做 `|`。
|
||
if lhs_bits == 0 and rhs_bits == 0:
|
||
if op == ast.OpKind.BitOr or op == ast.OpKind.BitAnd or op == ast.OpKind.BitXor or op == ast.OpKind.LShift or op == ast.OpKind.RShift:
|
||
# fail-fast:位运算操作数类型非法,立即终止编译(暴露上游根因)
|
||
self.Gen.TypeGenErr.fatal_error(node, "位运算操作数类型非法: lhs/rhs 均为非整数类型(指针或结构体),无法执行位运算", "TypeError")
|
||
|
||
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:
|
||
# 无符号操作数 → udiv(binop_unsigned 经 _node_type_unsigned 判定,
|
||
# 覆盖 sizet/uint64 及变量/数组元素的注解无符号性)。
|
||
# 此前一律 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)
|
||
elif op == ast.OpKind.BitOr: return llvmlite.build_or(builder, lhs, rhs)
|
||
elif op == ast.OpKind.BitXor: return llvmlite.build_xor(builder, lhs, rhs)
|
||
elif op == ast.OpKind.LShift: return llvmlite.build_shl(builder, lhs, rhs)
|
||
elif op == ast.OpKind.RShift:
|
||
# 无符号 >> → lshr(逻辑右移),有符号 >> → ashr(算术右移)
|
||
# 对齐 TPC ExprGen.emit_binary_op:i8/i16 默认无符号;其余查操作数无符号性
|
||
if lhs_bits == 8 or lhs_bits == 16:
|
||
return llvmlite.build_lshr(builder, lhs, rhs)
|
||
if self._node_type_unsigned(pool, lhs_node, trans) != 0:
|
||
return llvmlite.build_lshr(builder, lhs, rhs)
|
||
return llvmlite.build_ashr(builder, lhs, rhs)
|
||
return None |