Files
TransPyC/TransPyV/App/lib/core/Handles/HandlesExpr.py
2026-07-18 19:25:40 +08:00

1526 lines
67 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import t, c
from stdint import *
import ast
import memhub
import string
import llvmlite
import viperlib
import lib.core.Handles.HandlesBase as HandlesBase
import lib.core.Handles.HandlesTranslator as HT
import lib.core.Handles.HandlesVar as HandlesVar
import lib.core.Handles.HandlesExprOps as HandlesExprOps
import lib.core.Handles.HandlesExprCall as HandlesExprCall
import lib.core.Handles.HandlesNonlocal as HandlesNonlocal
import lib.core.Handles.HandlesType as HandlesType
import lib.core.Handles.HandlesStruct as HandlesStruct
import lib.core.Handles.HandlesEnum as HandlesEnum
import lib.core.Handles.HandlesClassDef as HandlesClassDef
import lib.core.Handles.HandlesImports as HandlesImports
# ============================================================
# HandlesExpr - 表达式处理Mixin 继承模式)
#
# 工具函数保留为模块级供外部调用ExprHandle.HandleValue 提供 trans 接口
# ============================================================
# ============================================================
# 从 AST 节点提取函数名
# ============================================================
def get_func_name(func_node: ast.AST | t.CPtr) -> str:
"""从函数节点提取函数名"""
if func_node is None:
return None
k: int = func_node.kind()
if k == ast.ASTKind.Name:
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(func_node)
return nm.id
elif k == ast.ASTKind.Attribute:
at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(func_node)
return at.attr
return None
# ============================================================
# 转义 LLVM IR 字符串字面量
# ============================================================
def escape_llvm_string(pool: memhub.MemBuddy | t.CPtr,
src: t.CChar | t.CPtr) -> t.CChar | t.CPtr:
"""将字符串转换为 LLVM IR 字符串字面量 c"...\\00" """
if src is None:
return None
slen: t.CSizeT = string.strlen(src)
alloc_size: t.CSizeT = 4 * slen + 8
buf: t.CChar | t.CPtr = pool.alloc(alloc_size)
if buf is None:
return None
pos: t.CSizeT = 0
buf[pos] = 'c'
pos += 1
buf[pos] = '"'
pos += 1
for i in range(slen):
ch: t.CChar = src[i]
if ch == '\0':
break
if ch == '\"' or ch == '\\' or ch < 0x20:
buf[pos] = '\\'
pos += 1
hi: int = (ch >> 4) & 0xF
lo: int = ch & 0xF
if hi < 10:
buf[pos] = '0' + hi
else:
buf[pos] = 'A' + (hi - 10)
pos += 1
if lo < 10:
buf[pos] = '0' + lo
else:
buf[pos] = 'A' + (lo - 10)
pos += 1
else:
buf[pos] = ch
pos += 1
buf[pos] = '\\'
pos += 1
buf[pos] = '0'
pos += 1
buf[pos] = '0'
pos += 1
buf[pos] = '"'
pos += 1
buf[pos] = '\0'
return buf
# ============================================================
# 获取 LLVM 整数类型的位宽
# ============================================================
def get_llvm_type_bits(ty: llvmlite.LLVMType | t.CPtr) -> int:
"""获取 LLVM 整数类型的位宽,非整数类型返回 0"""
if ty is None:
return 0
match ty:
case llvmlite.LLVMType.Int(bits):
return bits
case _:
return 0
# ============================================================
# 获取 LLVM 浮点类型的位宽
# ============================================================
def get_llvm_float_bits(ty: llvmlite.LLVMType | t.CPtr) -> int:
"""获取 LLVM 浮点类型的位宽,非浮点类型返回 0"""
if ty is None:
return 0
match ty:
case llvmlite.LLVMType.Float(bits):
return bits
case _:
return 0
# ============================================================
# 检查类型是否为 Ptr独立函数避免嵌套 match 的编译器 BUG
#
# 嵌套 match 的 REnum 检测在宿主编译器中有 BUG当 match 嵌套在
# 另一个 match 的 case 块中时,内层 match 走非 REnum 路径,
# 导致 case 列表为空。提取为独立函数可规避此问题。
# ============================================================
def is_ptr_type(ty: llvmlite.LLVMType | t.CPtr) -> int:
"""检查 ty 是否是 Ptr 类型,返回 1=是, 0=否"""
if ty is None:
return 0
match ty:
case llvmlite.LLVMType.Ptr(pointee):
return 1
case _:
return 0
# ============================================================
# _deref_if_ptr_ptr - 如果 obj_ptr 是 Ptr(Ptr(...))load 解引用
#
# 用于支持 X|t.CPtr 类型变量的属性访问:
# - 值类型变量 (cnt: Counter): alloca 类型是 Ptr(Struct) → 不解引用
# - 指针类型变量 (r: Vec2|t.CPtr): alloca 类型是 Ptr(Ptr(Struct)) → load 解引用
#
# 用 is_ptr_type 避免嵌套 match 的编译器 BUG
# ============================================================
def _deref_if_ptr_ptr(builder: llvmlite.IRBuilder | t.CPtr,
obj_ptr: llvmlite.Value | t.CPtr) -> llvmlite.Value | t.CPtr:
"""如果 obj_ptr 是 Ptr(Ptr(...))load 解引用获取内层指针"""
if obj_ptr is None or obj_ptr.Ty is None:
return obj_ptr
if is_ptr_type(obj_ptr.Ty) == 0:
return obj_ptr
pointee: llvmlite.LLVMType | t.CPtr = obj_ptr.Ty.Pointee
if pointee is None:
return obj_ptr
if is_ptr_type(pointee) == 0:
return obj_ptr
# obj_ptr 是 Ptr(Ptr(...))load 解引用
loaded: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, pointee, obj_ptr)
if loaded is None:
return obj_ptr
return loaded
# ============================================================
# 类型强制转换:整数 sext/trunc浮点 fpext/fptruncint↔float si2fp/fp2si
# ============================================================
def coerce_to_type(builder: llvmlite.IRBuilder | t.CPtr,
val: llvmlite.Value | t.CPtr,
target_ty: llvmlite.LLVMType | t.CPtr) -> llvmlite.Value | t.CPtr:
"""将 val 强制转换为 target_ty
整数: sext扩展/ trunc截断
浮点: fpext扩展/ fptrunc截断
int→float: sitofp
float→int: fptosi
"""
if val is None or target_ty is None:
return val
# 整数转换
val_bits: int = get_llvm_type_bits(val.Ty)
target_bits: int = get_llvm_type_bits(target_ty)
if val_bits != 0 and target_bits != 0:
if val_bits == target_bits:
return val
if val_bits < target_bits:
return llvmlite.build_sext(builder, val, target_ty)
return llvmlite.build_trunc(builder, val, target_ty)
# 浮点转换
val_fbits: int = get_llvm_float_bits(val.Ty)
target_fbits: int = get_llvm_float_bits(target_ty)
if val_fbits != 0 and target_fbits != 0:
if val_fbits == target_fbits:
return val
if val_fbits < target_fbits:
return llvmlite.build_fpext(builder, val, target_ty)
return llvmlite.build_fptrunc(builder, val, target_ty)
# int → float
if val_bits != 0 and target_fbits != 0:
return llvmlite.build_si2fp(builder, val, target_ty)
# float → int
if val_fbits != 0 and target_bits != 0:
return llvmlite.build_fp2si(builder, val, target_ty)
# 指针 → 非指针值: build_load 解引用
# 适用于: 指针 → 整数 (如 i8* → i8), 指针 → 结构体值
# 当构造器返回 Ptr(Struct) 但目标变量是 Struct 值类型时,需要 load
# 注意:使用独立函数 is_ptr_type 检查,避免嵌套 match 的编译器 BUG
if is_ptr_type(val.Ty) != 0 and is_ptr_type(target_ty) == 0:
return llvmlite.build_load(builder, target_ty, val)
# 指针 → 指针: bitcast (如 i8* → i8** 当目标是全局变量存储指针)
if is_ptr_type(val.Ty) != 0 and is_ptr_type(target_ty) != 0:
return llvmlite.build_bitcast(builder, val, target_ty)
return val
# ============================================================
# 创建全局字符串常量
# ============================================================
def create_global_string(builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
str_val: str,
trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
"""创建全局字符串常量并返回 i8* bitcast"""
escaped: t.CChar | t.CPtr = escape_llvm_string(pool, str_val)
if escaped is None:
return None
slen: t.CSizeT = string.strlen(str_val)
count: int = slen + 1
i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
arr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Array(pool, i8_ty, count)
# 使用模块级计数器trans._label_counter避免跨函数命名冲突
str_idx: int = trans._label_counter
trans._label_counter += 1
# 字符串名加 SHA1 前缀,和函数导出规则一致,避免跨模块重名
gv_name: t.CChar | t.CPtr = pool.alloc(48)
if gv_name is None:
return None
if trans.ModuleSha1 is not None:
viperlib.snprintf(gv_name, 48, ".str.%s.%d", trans.ModuleSha1, str_idx)
else:
viperlib.snprintf(gv_name, 48, ".str.%d", str_idx)
gv: llvmlite.GlobalVariable | t.CPtr = llvmlite.new_global_variable(pool, gv_name, arr_ty)
if gv is None:
return None
llvmlite.module_add_global(mod, gv)
# 去掉 private 链接和函数一致stub 中 declaretext 中 define
llvmlite.global_set_unnamed_addr(gv, 1)
llvmlite.global_set_constant(gv, 1)
gv.Initializer = escaped
arr_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, arr_ty)
gv_ref_name: t.CChar | t.CPtr = pool.alloc(64)
if gv_ref_name is None:
return None
viperlib.snprintf(gv_ref_name, 64, "@%s", gv.Name)
gv_ref: llvmlite.Value | t.CPtr = llvmlite.SSAValue(pool, arr_ptr_ty, gv_ref_name)
i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty)
return llvmlite.build_bitcast(builder, gv_ref, i8_ptr_ty)
# ============================================================
# 翻译常量表达式
# ============================================================
def translate_constant(builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
node: ast.AST | t.CPtr,
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
"""翻译常量int/str/bool"""
cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(node)
if cn is None:
return None
if cn.const_kind == ast.CONST_INT:
# 超出 i32 范围则用 i64避免常量创建时被截断
iv: t.CInt64T = cn.int_val
if iv > 2147483647 or iv < -2147483648:
return llvmlite.const_int64(pool, iv)
return llvmlite.const_int32(pool, iv)
elif cn.const_kind == ast.CONST_FLOAT:
# 浮点常量默认创建为 double64 位),赋值时由 coerce_to_type 自动 fptrunc
double_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Double(pool)
return llvmlite.ConstFloat(pool, double_ty, cn.float_val)
elif cn.const_kind == ast.CONST_STR:
return create_global_string(builder, pool, mod, cn.str_val, trans)
elif cn.const_kind == ast.CONST_BOOL:
if cn.int_val != 0:
return llvmlite.const_int32(pool, 1)
return llvmlite.const_int32(pool, 0)
elif cn.const_kind == ast.CONST_NONE:
# None → i8* null空指针常量用于 `p is None` / `p is not None` 比较
i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty)
return llvmlite.ConstNull(pool, i8_ptr_ty, "null")
return None
# ============================================================
# _infer_elem_type_from_value — 从 LLVM Value 推断 list 元素类型名
#
# 用于 list 字面量 [a, b, c] 的元素类型推断
# ============================================================
def _infer_elem_type_from_value(pool: memhub.MemBuddy | t.CPtr,
value: llvmlite.Value | t.CPtr) -> str:
"""从 LLVM Value 的类型推断 list 元素类型名"""
if value is None or value.Ty is None:
return "str"
ty: llvmlite.LLVMType | t.CPtr = value.Ty
bits: int = get_llvm_type_bits(ty)
if bits != 0:
if bits == 32:
return "int"
if bits == 64:
return "CSizeT"
if bits == 8:
return "CInt8T"
if bits == 16:
return "CInt16T"
return "int"
if is_ptr_type(ty) != 0:
return "str"
fbits: int = get_llvm_float_bits(ty)
if fbits != 0:
if fbits == 64:
return "CDouble"
if fbits == 32:
return "CFloat"
return "str"
# ============================================================
# _find_pool_var — 查找上下文中的 pool 变量
#
# 依次尝试 "pool", "_mbuddy", "mbuddy", "mb"
# ============================================================
def _find_pool_var(trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
"""查找上下文中的 pool 变量,返回 alloca 或 None"""
if trans is None:
return None
alloca: llvmlite.Value | t.CPtr = HandlesVar.lookup_var(trans.SymTab, "pool")
if alloca is not None:
return alloca
alloca = HandlesVar.lookup_var(trans.SymTab, "_mbuddy")
if alloca is not None:
return alloca
alloca = HandlesVar.lookup_var(trans.SymTab, "mbuddy")
if alloca is not None:
return alloca
alloca = HandlesVar.lookup_var(trans.SymTab, "mb")
if alloca is not None:
return alloca
return None
# ============================================================
# translate_list_literal — 翻译 list 字面量 [a, b, c]
#
# 将 list 字面量翻译为 list[T](pool) 构造 + append 调用序列
#
# Args:
# builder: IRBuilder
# pool: 编译器内存池MemBuddy
# mod: LLVMModule
# node: ast.List 节点
# elem_type_name: 元素类型名(如 "str", "int"None 表示从元素推断
# trans: Translator 对象
#
# Returns:
# list 对象指针Ptr(list[T] struct)None 失败
# ============================================================
def translate_list_literal(builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
node: ast.AST | t.CPtr,
elem_type_name: str,
trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
"""翻译 list 字面量 [a, b, c] → list[T](pool) + append 序列"""
if node is None or trans is None:
return None
list_node: ast.List | t.CPtr = (ast.List | t.CPtr)(node)
if list_node is None:
return None
elts: list[ast.AST | t.CPtr] | t.CPtr = list_node.elts
elts_count: t.CSizeT = 0
if elts is not None:
elts_count = elts.__len__()
# 推断元素类型
inferred_type_name: str = elem_type_name
first_elem_val: llvmlite.Value | t.CPtr = None
if inferred_type_name is None:
inferred_type_name = "str"
if elts_count > 0:
first_elem_node: ast.AST | t.CPtr = elts.get(0)
first_elem_val = translate_value(builder, pool, mod, first_elem_node, None, 0, trans)
if first_elem_val is not None:
inferred_type_name = _infer_elem_type_from_value(pool, first_elem_val)
# 查找 pool 变量
pool_alloca: llvmlite.Value | t.CPtr = _find_pool_var(trans)
if pool_alloca is None:
HandlesType.fatal_error(node, "list literal requires pool variable in context")
return None
# load pool 变量值i8*
i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty)
pool_val: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, i8_ptr_ty, pool_alloca)
if pool_val is None:
return None
# 特化 list[T] 类
type_args: list[str] | t.CPtr = list[str](pool, 4)
type_args.append(inferred_type_name)
spec_name: str = HandlesClassDef._specialize_generic_class(trans, "list", type_args)
if spec_name is None:
HandlesType.fatal_error(node, "list literal generic specialization failed")
return None
# 查找 struct entry
struct_entry: HandlesStruct.StructEntry | t.CPtr = HandlesStruct.find_struct(spec_name)
if struct_entry is None:
HandlesType.fatal_error(node, "list literal specialized class not registered")
return None
struct_ty: llvmlite.LLVMType | t.CPtr = struct_entry.Ty
if struct_ty is None:
return None
# alloca 临时 list 空间
tmp: llvmlite.Value | t.CPtr = llvmlite.build_alloca(builder, struct_ty)
if tmp is None:
return None
# 调用 __new__(tmp, pool_val) → storage_ptr
new_args: t.CSizeT | t.CPtr = pool.alloc(8 * 32)
if new_args is None:
return None
string.memset(new_args, 0, 8 * 32)
new_args[0] = t.CSizeT(pool_val)
storage_ptr: llvmlite.Value | t.CPtr = HandlesExprCall._call_method_on_ptr(
pool, builder, mod, spec_name, "__new__", tmp, new_args, 1, trans)
if storage_ptr is None:
storage_ptr = tmp
# 调用 __before_init__(storage_ptr)
HandlesExprCall._call_method_on_ptr(
pool, builder, mod, spec_name, "__before_init__", storage_ptr, None, 0, trans)
# 调用 __init__(storage_ptr, pool_val, elem_size=0)
init_args: t.CSizeT | t.CPtr = pool.alloc(8 * 32)
if init_args is None:
return None
string.memset(init_args, 0, 8 * 32)
init_args[0] = t.CSizeT(pool_val)
elem_size_val: llvmlite.Value | t.CPtr = llvmlite.const_int64(pool, 0)
init_args[1] = t.CSizeT(elem_size_val)
HandlesExprCall._call_method_on_ptr(
pool, builder, mod, spec_name, "__init__", storage_ptr, init_args, 2, trans)
# 对每个元素调用 append(storage_ptr, elem_val)
if elts_count > 0:
# 第一个元素可能已经翻译过
if first_elem_val is not None:
append_args: t.CSizeT | t.CPtr = pool.alloc(8 * 32)
if append_args is not None:
string.memset(append_args, 0, 8 * 32)
append_args[0] = t.CSizeT(first_elem_val)
HandlesExprCall._call_method_on_ptr(
pool, builder, mod, spec_name, "append", storage_ptr, append_args, 1, trans)
# 翻译剩余元素
i: t.CSizeT = 1
while i < elts_count:
elem_node_i: ast.AST | t.CPtr = elts.get(i)
elem_val_i: llvmlite.Value | t.CPtr = translate_value(builder, pool, mod, elem_node_i, None, 0, trans)
if elem_val_i is not None:
ap_args: t.CSizeT | t.CPtr = pool.alloc(8 * 32)
if ap_args is not None:
string.memset(ap_args, 0, 8 * 32)
ap_args[0] = t.CSizeT(elem_val_i)
HandlesExprCall._call_method_on_ptr(
pool, builder, mod, spec_name, "append", storage_ptr, ap_args, 1, trans)
i += 1
return storage_ptr
# ============================================================
# 翻译值表达式RHS 分派)— 模块级版本
# ============================================================
def translate_value(builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
node: ast.AST | t.CPtr,
funcs_ptr: t.CPtr = None,
func_count: int = 0,
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
"""翻译值表达式为 LLVM Value"""
if node is None:
return None
k: int = node.kind()
if k == ast.ASTKind.Constant:
return translate_constant(builder, pool, mod, node, trans)
elif k == ast.ASTKind.Name:
return translate_name_value(builder, pool, node, trans)
elif k == ast.ASTKind.BinOp:
return HandlesExprOps.translate_binop(pool, builder, mod, node, trans)
elif k == ast.ASTKind.Call:
return HandlesExprCall.translate_call(pool, builder, mod, node,
funcs_ptr, func_count, trans)
elif k == ast.ASTKind.Compare:
return translate_compare(builder, pool, mod, node, trans)
elif k == ast.ASTKind.UnaryOp:
return translate_unaryop(builder, pool, mod, node, trans)
elif k == ast.ASTKind.BoolOp:
return translate_boolop(builder, pool, mod, node,
funcs_ptr, func_count, trans)
elif k == ast.ASTKind.Subscript:
return translate_subscript(builder, pool, mod, node, trans)
elif k == ast.ASTKind.Attribute:
return translate_attribute(builder, pool, mod, node, trans)
elif k == ast.ASTKind.List:
return translate_list_literal(builder, pool, mod, node, None, trans)
return None
# ============================================================
# 翻译比较表达式 Compare(left, ops, comparators)
# ============================================================
def translate_compare(builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
node: ast.AST | t.CPtr,
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
"""翻译比较表达式,返回 i1 结果"""
cmp: ast.Compare | t.CPtr = (ast.Compare | t.CPtr)(node)
if cmp is None:
return None
ops_list: list[t.CInt] | t.CPtr = (list[t.CInt] | t.CPtr)(cmp.ops)
if ops_list is None or ops_list.__len__() == 0:
return None
op: int = ops_list.get(0)
comparators: list[ast.AST | t.CPtr] | t.CPtr = cmp.comparators
if comparators is None or comparators.__len__() == 0:
return None
rhs: llvmlite.Value | t.CPtr = translate_value(
builder, pool, mod, comparators.get(0), None, 0, trans)
if rhs is None:
return None
# === 比较运算符重载路径 1: lhs 是 Name 且对应结构体变量 ===
# 对于值类型变量(如 cnt: Counter用 alloca 指针尝试重载
lhs_node: ast.AST | t.CPtr = cmp.left
if lhs_node is not None and lhs_node.kind() == ast.ASTKind.Name and trans is not None:
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(lhs_node)
if nm is not None and nm.id is not None:
lhs_alloca: llvmlite.Value | t.CPtr = HandlesVar.lookup_var(
trans.SymTab, nm.id)
if lhs_alloca is not None:
ovl_result: llvmlite.Value | t.CPtr = HandlesExprOps.try_operator_overload(
pool, builder, mod, lhs_alloca, rhs, op, trans, 1)
if ovl_result is not None:
return ovl_result
# 正常翻译 lhs
lhs: llvmlite.Value | t.CPtr = translate_value(
builder, pool, mod, cmp.left, None, 0, trans)
if lhs is None:
return None
# === 比较运算符重载路径 2: lhs 是 Ptr(Struct) ===
# Is/IsNot 不在 _cmpop_to_dunder 映射中,会自动返回 None 回退原生比较
ovl_result2: llvmlite.Value | t.CPtr = HandlesExprOps.try_operator_overload(
pool, builder, mod, lhs, rhs, op, trans, 1)
if ovl_result2 is not None:
return ovl_result2
predicate: int = llvmlite.ICMP_SLT
if op == ast.OpKind.Lt:
predicate = llvmlite.ICMP_SLT
elif op == ast.OpKind.Le:
predicate = llvmlite.ICMP_SLE
elif op == ast.OpKind.Gt:
predicate = llvmlite.ICMP_SGT
elif op == ast.OpKind.Ge:
predicate = 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
# 隐式类型转换:当一侧是指针(i8*),另一侧是整数时,
# 从指针加载第一个字节,使两侧类型一致
lhs_is_ptr: int = is_ptr_type(lhs.Ty)
rhs_is_ptr: int = is_ptr_type(rhs.Ty)
if lhs_is_ptr != 0 and rhs_is_ptr == 0:
i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
lhs = llvmlite.build_load(builder, i8_ty, lhs)
elif rhs_is_ptr != 0 and lhs_is_ptr == 0:
i8_ty2: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
rhs = llvmlite.build_load(builder, i8_ty2, rhs)
# 类型对齐icmp 要求两边类型相同,将较窄的整数 sext 到较宽的类型
lhs_bits: int = get_llvm_type_bits(lhs.Ty)
rhs_bits: int = get_llvm_type_bits(rhs.Ty)
if lhs_bits != 0 and rhs_bits != 0 and lhs_bits != rhs_bits:
if lhs_bits < rhs_bits:
lhs = llvmlite.build_sext(builder, lhs, rhs.Ty)
else:
rhs = llvmlite.build_sext(builder, rhs, lhs.Ty)
return llvmlite.build_icmp(builder, predicate, lhs, rhs)
# ============================================================
# 翻译一元运算表达式 UnaryOp(op, operand)
# ============================================================
def translate_unaryop(builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
node: ast.AST | t.CPtr,
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
"""翻译一元运算(-x, +x, not x, ~x"""
uo: ast.UnaryOp | t.CPtr = (ast.UnaryOp | t.CPtr)(node)
if uo is None:
return None
operand: llvmlite.Value | t.CPtr = translate_value(
builder, pool, mod, uo.operand, None, 0, trans)
if operand is None:
return None
if uo.op == ast.OpKind.USub:
# -x = 0 - x
zero: llvmlite.Value | t.CPtr = 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),返回 i1
zero = llvmlite.const_int32(pool, 0)
return llvmlite.build_icmp(builder, llvmlite.ICMP_EQ, operand, zero)
elif uo.op == ast.OpKind.Invert:
# ~x = x ^ -1
neg1: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, -1)
return llvmlite.build_xor(builder, operand, neg1)
return operand
# ============================================================
# 翻译布尔运算 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(builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
node: ast.AST | t.CPtr,
funcs_ptr: t.CPtr = None,
func_count: int = 0,
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
"""翻译布尔运算and/or的短路求值返回 i1"""
bo: ast.BoolOp | t.CPtr = (ast.BoolOp | t.CPtr)(node)
if bo is None:
return None
op: int = bo.op
values: list[ast.AST | t.CPtr] | t.CPtr = bo.values
if values is None:
return None
count: t.CSizeT = values.__len__()
if count == 0:
return None
if count == 1:
return translate_value(builder, pool, mod, values.get(0),
funcs_ptr, func_count, trans)
func: llvmlite.Function | t.CPtr = 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: t.CChar | t.CPtr = pool.alloc(32)
if name_buf is None:
return None
viperlib.snprintf(name_buf, 32, "bool.merge.%d", cnt)
merge_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, name_buf)
# phi 入边链表头/尾
phi_head: llvmlite.PhiIncoming | t.CPtr = None
phi_tail: llvmlite.PhiIncoming | t.CPtr = None
phi_count: int = count - 1 # 循环次数
# 对每个值(除最后一个)求值并短路
for i in range(count - 1):
val_node: ast.AST | t.CPtr = values.get(i)
val: llvmlite.Value | t.CPtr = translate_value(
builder, pool, mod, val_node,
funcs_ptr, func_count, trans)
if val is None:
return None
# 转为 i1已经是 i1 的直接用,否则与 0 比较)
val_bits: int = get_llvm_type_bits(val.Ty)
val_i1: llvmlite.Value | t.CPtr = val
if val_bits != 1:
zero: llvmlite.Value | t.CPtr = 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: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, name_buf)
# and: 短路假→merge真→nextor: 短路真→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: llvmlite.BasicBlock | t.CPtr = builder.CurBlock
inc: llvmlite.PhiIncoming | t.CPtr = 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 | t.CPtr = values.get(count - 1)
last_val: llvmlite.Value | t.CPtr = translate_value(
builder, pool, mod, last_node,
funcs_ptr, func_count, trans)
if last_val is None:
return None
last_bits: int = get_llvm_type_bits(last_val.Ty)
last_i1: llvmlite.Value | t.CPtr = last_val
if last_bits != 1:
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 | t.CPtr = llvmlite.Int1(pool)
return llvmlite.build_phi(builder, i1_ty, phi_head, phi_count)
# ============================================================
# 翻译变量引用Name 节点)→ load
# ============================================================
def translate_name_value(builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
node: ast.AST | t.CPtr,
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
"""翻译变量引用Name 节点)→ load"""
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(node)
if nm is None:
return None
nm_id: str = nm.id
if nm_id is None:
return None
# CDefine 编译期常量: 直接返回整数常量值(不生成运行时代码)
# NAME: t.CDefine = value 形式定义的常量在编译期已注册到全局表
cdef_val: int = HandlesType.lookup_cdefine_constant(nm_id)
if cdef_val >= 0:
return llvmlite.const_int32(pool, cdef_val)
# global 变量:从模块作用域查找
if trans is not None:
if HT.is_global_name(trans, nm_id) != 0:
mod_alloca: llvmlite.Value | t.CPtr = HandlesVar.lookup_module_var(
trans.SymTab, nm_id)
if mod_alloca is not None:
load_ty: llvmlite.LLVMType | t.CPtr = None
if mod_alloca.Ty is not None:
load_ty = mod_alloca.Ty.Pointee
if load_ty is None:
load_ty = llvmlite.Int32(pool)
return llvmlite.build_load(builder, load_ty, mod_alloca)
# nonlocal 变量:通过闭包 env 访问
if HT.is_nonlocal_name(trans, nm_id) != 0:
return HandlesNonlocal.load_nonlocal_var(trans, nm_id)
alloca: llvmlite.Value | t.CPtr = HandlesVar.lookup_var(trans.SymTab, nm_id)
if alloca is None:
return None
load_ty: llvmlite.LLVMType | t.CPtr = None
if alloca.Ty is not None:
load_ty = alloca.Ty.Pointee
if load_ty is None:
load_ty = llvmlite.Int32(pool)
return llvmlite.build_load(builder, load_ty, alloca)
# ============================================================
# ExprHandle - 表达式处理器Mixin 继承模式)
#
# HandleValue 提供 trans 接口,委托到模块级 translate_value
# ============================================================
@t.NoVTable
class ExprHandle(HandlesBase.Mixin):
"""表达式处理器:继承 Mixin 获得 Trans 回指针"""
def __init__(self, trans: HT.Translator | t.CPtr):
self.Trans = trans
# ============================================================
# HandleValue - 翻译值表达式为 LLVM Value
# ============================================================
def HandleValue(self, node: ast.AST | t.CPtr) -> llvmlite.Value | t.CPtr:
"""翻译值表达式,从 self.Trans 获取共享状态"""
return translate_value(
self.Trans._cur_builder, self.Trans.Pool, self.Trans.Module,
node,
self.Trans._funcs, self.Trans._func_count, self.Trans)
# ============================================================
# NewExprHandle - 工厂函数
# ============================================================
def NewExprHandle(pool: memhub.MemBuddy | t.CPtr,
trans: HT.Translator | t.CPtr) -> ExprHandle | t.CPtr:
h: ExprHandle | t.CPtr = pool.alloc(ExprHandle.__sizeof__())
if h is None:
return None
string.memset(h, 0, ExprHandle.__sizeof__())
h.Trans = trans
return h
# ============================================================
# list_getitem_inline — 内联生成 list[T] __getitem__ 逻辑
#
# list 结构布局 (6 字段 × 8 字节):
# __data__(0) __count__(8) __capacity__(16) __pool__(24) __elem_size__(32) __iter_index__(40)
# 返回元素地址 (i8*),调用方根据元素类型 load 正确的值
# ============================================================
def list_getitem_inline(builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
lm_obj: llvmlite.Value | t.CPtr,
idx_val: llvmlite.Value | t.CPtr) -> llvmlite.Value | t.CPtr:
"""内联生成 list __getitem__ 逻辑,返回元素地址 (i8*)"""
if builder is None or pool is None or lm_obj is None or idx_val is None:
return None
i64_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int64(pool)
i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty)
# 加载 __data__ (偏移 0, index 0)
g_idx0: llvmlite.Value | t.CPtr = llvmlite.const_int64(pool, 0)
g_dpp: llvmlite.Value | t.CPtr = llvmlite.build_gep(builder, i64_ty, lm_obj, g_idx0)
if g_dpp is None:
return None
g_data: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, i8_ptr_ty, g_dpp)
# 加载 __elem_size__ (偏移 32, index 4)
g_idx4: llvmlite.Value | t.CPtr = llvmlite.const_int64(pool, 4)
g_epp: llvmlite.Value | t.CPtr = llvmlite.build_gep(builder, i64_ty, lm_obj, g_idx4)
if g_epp is None:
return None
g_esize: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, i64_ty, g_epp)
if g_data is None or g_esize is None:
return None
g_idx_i64: llvmlite.Value | t.CPtr = coerce_to_type(builder, idx_val, i64_ty)
if g_idx_i64 is None:
return None
g_offset: llvmlite.Value | t.CPtr = llvmlite.build_mul(builder, g_idx_i64, g_esize)
if g_offset is None:
return None
# 返回元素地址 (i8*),不 load
return llvmlite.build_gep(builder, i8_ty, g_data, g_offset)
# ============================================================
# _list_elem_type_from_name — 从 list 类型名提取元素 LLVM 类型
#
# 类型名格式: <sha1>.list[<elem_type>]
# 用 strstr 检查完整模式,避免子串提取
# 支持: list[int]→i32, list[str]/list[bytes]→i8*, list[CInt]→i32 等
# 默认: i32
# ============================================================
def _list_elem_type_from_name(pool: memhub.MemBuddy | t.CPtr,
struct_name: str) -> llvmlite.LLVMType | t.CPtr:
"""从 list 类型名提取元素 LLVM 类型"""
if pool is None or struct_name is None:
return None
# 用 strstr 检查完整模式
if string.strstr(struct_name, "list[int]") is not None:
return llvmlite.Int32(pool)
if string.strstr(struct_name, "list[CInt]") is not None:
return llvmlite.Int32(pool)
if string.strstr(struct_name, "list[CInt8T]") is not None:
return llvmlite.Int8(pool)
if string.strstr(struct_name, "list[CInt16T]") is not None:
return llvmlite.Int16(pool)
if string.strstr(struct_name, "list[CInt32T]") is not None:
return llvmlite.Int32(pool)
if string.strstr(struct_name, "list[CInt64T]") is not None:
return llvmlite.Int64(pool)
if string.strstr(struct_name, "list[CSizeT]") is not None:
return llvmlite.Int64(pool)
if string.strstr(struct_name, "list[CDouble]") is not None:
return llvmlite.Double(pool)
if string.strstr(struct_name, "list[CFloat]") is not None:
return llvmlite.Float(pool)
if string.strstr(struct_name, "list[CChar]") is not None:
return llvmlite.Int8(pool)
# str/bytes/CPtr/其他指针类型 → i8*
i8_ty_d: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
return llvmlite.Ptr(pool, i8_ty_d)
# ============================================================
# list_setitem_inline — 内联生成 list[T] __setitem__ 逻辑
#
# 直接存储 rhs_val 到元素地址 (coerce 为 i8* 后 store)
# ============================================================
def list_setitem_inline(builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
lm_obj: llvmlite.Value | t.CPtr,
idx_val: llvmlite.Value | t.CPtr,
rhs_val: llvmlite.Value | t.CPtr) -> int:
"""内联生成 list __setitem__ 逻辑,返回 0"""
if builder is None or pool is None or lm_obj is None or idx_val is None or rhs_val is None:
return 0
i64_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int64(pool)
i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty)
# 加载 __data__ (偏移 0, index 0)
s_idx0: llvmlite.Value | t.CPtr = llvmlite.const_int64(pool, 0)
s_dpp: llvmlite.Value | t.CPtr = llvmlite.build_gep(builder, i64_ty, lm_obj, s_idx0)
if s_dpp is None:
return 0
s_data: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, i8_ptr_ty, s_dpp)
# 加载 __elem_size__ (偏移 32, index 4)
s_idx4: llvmlite.Value | t.CPtr = llvmlite.const_int64(pool, 4)
s_epp: llvmlite.Value | t.CPtr = llvmlite.build_gep(builder, i64_ty, lm_obj, s_idx4)
if s_epp is None:
return 0
s_esize: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, i64_ty, s_epp)
if s_data is None or s_esize is None:
return 0
s_idx_i64: llvmlite.Value | t.CPtr = coerce_to_type(builder, idx_val, i64_ty)
if s_idx_i64 is None:
return 0
s_offset: llvmlite.Value | t.CPtr = llvmlite.build_mul(builder, s_idx_i64, s_esize)
if s_offset is None:
return 0
s_addr: llvmlite.Value | t.CPtr = llvmlite.build_gep(builder, i8_ty, s_data, s_offset)
if s_addr is None:
return 0
s_val_ptr: llvmlite.Value | t.CPtr = coerce_to_type(builder, rhs_val, i8_ptr_ty)
if s_val_ptr is None:
return 0
llvmlite.build_store(builder, s_val_ptr, s_addr)
return 0
# ============================================================
# is_list_subscript — 检查 Subscript 节点是否是 list[T] 类型
#
# 返回 list 对象指针 (Ptr(list[T])) 或 None
# ============================================================
def is_list_subscript(node: ast.AST | t.CPtr,
trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
"""检查 Subscript 节点是否是 list[T] 类型,返回 list 对象指针或 None"""
if node is None or trans is None:
return None
sub: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(node)
if sub is None or sub.value is None:
return None
if sub.value.kind() != ast.ASTKind.Name:
return None
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(sub.value)
if nm is None or nm.id is None:
return None
alloca: llvmlite.Value | t.CPtr = HandlesVar.lookup_var(trans.SymTab, nm.id)
if alloca is None or alloca.Ty is None:
return None
if is_ptr_type(alloca.Ty) == 0:
return None
pointee: llvmlite.LLVMType | t.CPtr = alloca.Ty.Pointee
if pointee is None:
return None
# 检查 pointee 是否是 Ptr(Struct)
inner_ty: llvmlite.LLVMType | t.CPtr = None
match pointee:
case llvmlite.LLVMType.Ptr(it):
inner_ty = it
case _:
return None
if inner_ty is None:
return None
# 检查 inner_ty 是否是 Struct 且名称包含 "list["
struct_name: str = None
match inner_ty:
case llvmlite.LLVMType.Struct(_, _, sname):
struct_name = sname
case _:
return None
if struct_name is None:
return None
if string.strstr(struct_name, "list[") is None:
return None
# 是 list 类型: load 出 list 对象指针
builder: llvmlite.IRBuilder | t.CPtr = trans._cur_builder
return llvmlite.build_load(builder, pointee, alloca)
# ============================================================
# 翻译下标表达式 Subscript(value, slice, ctx) — ptr[i] / arr[i]
#
# 指针遍历: ptr 是指针变量load 出指针值后 GEP + load
# 数组遍历: arr 是数组变量,用 alloca 指针做双索引 GEP [0, i] + load
# list[T]: 泛型类不注册 structsubscript 走 __getitem__ 内联路径
# ============================================================
def translate_subscript(builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
node: ast.AST | t.CPtr,
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
"""翻译下标表达式,返回加载的元素值"""
if node is None or builder is None:
return None
sub: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(node)
if sub is None:
return None
# 翻译索引
idx_val: llvmlite.Value | t.CPtr = translate_value(
builder, pool, mod, sub.slice, None, 0, trans)
if idx_val is None:
return None
# 如果 value 是 Name尝试从 alloca 类型推断
if sub.value is not None and sub.value.kind() == ast.ASTKind.Name:
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(sub.value)
if nm.id is not None and trans is not None:
alloca: llvmlite.Value | t.CPtr = HandlesVar.lookup_var(trans.SymTab, nm.id)
if alloca is not None and alloca.Ty is not None:
if is_ptr_type(alloca.Ty) != 0:
pointee: llvmlite.LLVMType | t.CPtr = alloca.Ty.Pointee
if pointee is not None:
# 单层 match避免嵌套 match 的编译器 bug
match pointee:
case llvmlite.LLVMType.Array(elem_ty, count):
# 数组遍历: getelementptr [N x elem_ty], ... , i32 0, i32 %idx
elem_ptr: llvmlite.Value | t.CPtr = llvmlite.build_gep_array(
builder, pointee, elem_ty, alloca, idx_val)
if elem_ptr is not None:
return llvmlite.build_load(builder, elem_ty, elem_ptr)
return None
case llvmlite.LLVMType.Ptr(inner_ty):
# 检查 inner_ty 是否是 list[T] 类型(泛型类不注册 struct
# list 的 subscript 应该走 __getitem__ 内联路径,而非指针遍历
list_struct_name: str = None
match inner_ty:
case llvmlite.LLVMType.Struct(_, _, lsn):
list_struct_name = lsn
case _:
pass
if list_struct_name is not None:
if string.strstr(list_struct_name, "list[") is not None:
# list[T] 类型: 内联生成 __getitem__ 逻辑
ptr_val_list: llvmlite.Value | t.CPtr = llvmlite.build_load(
builder, pointee, alloca)
if ptr_val_list is not None:
elem_addr_list: llvmlite.Value | t.CPtr = list_getitem_inline(
builder, pool, ptr_val_list, idx_val)
if elem_addr_list is not None:
# 根据类型名提取元素类型bitcast 后 load
elem_ty_list: llvmlite.LLVMType | t.CPtr = _list_elem_type_from_name(
pool, list_struct_name)
if elem_ty_list is not None:
elem_ptr_ty_list: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(
pool, elem_ty_list)
casted_addr_list: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(
builder, elem_addr_list, elem_ptr_ty_list)
if casted_addr_list is not None:
return llvmlite.build_load(
builder, elem_ty_list, casted_addr_list)
return None
# 普通指针遍历: 先 load 指针值,再 GEP
ptr_val: llvmlite.Value | t.CPtr = llvmlite.build_load(
builder, pointee, alloca)
if ptr_val is not None:
elem_ptr2: llvmlite.Value | t.CPtr = llvmlite.build_gep(
builder, inner_ty, ptr_val, idx_val)
if elem_ptr2 is not None:
return llvmlite.build_load(builder, inner_ty, elem_ptr2)
return None
case _:
# 自定义类(结构体类型): 转发到 __getitem__ 方法调用
# 如 hashtable[key] → hashtable.__getitem__(key)
cls_nm_sub: str = HandlesStruct.get_class_name_by_type(pool, pointee)
if cls_nm_sub is not None:
obj_val_sub: llvmlite.Value | t.CPtr = llvmlite.build_load(
builder, pointee, alloca)
if obj_val_sub is not None:
arg_vals_sub: t.CSizeT | t.CPtr = pool.alloc(8)
if arg_vals_sub is not None:
arg_vals_sub[0] = t.CSizeT(idx_val)
ret_sub: llvmlite.Value | t.CPtr = HandlesExprCall._call_method_on_ptr(
pool, builder, mod, cls_nm_sub, "__getitem__",
obj_val_sub, arg_vals_sub, 1, trans)
if ret_sub is not None:
return ret_sub
pass
# 通用路径:翻译 value 获取指针
ptr_val: llvmlite.Value | t.CPtr = translate_value(
builder, pool, mod, sub.value, None, 0, trans)
if ptr_val is None or ptr_val.Ty is None:
return None
# 检查是否是指针类型
if is_ptr_type(ptr_val.Ty) != 0:
elem_ty2: llvmlite.LLVMType | t.CPtr = ptr_val.Ty.Pointee
if elem_ty2 is not None:
elem_ptr3: llvmlite.Value | t.CPtr = llvmlite.build_gep(
builder, elem_ty2, ptr_val, idx_val)
if elem_ptr3 is not None:
return llvmlite.build_load(builder, elem_ty2, elem_ptr3)
return None
# ============================================================
# find_global_in_module - 在模块全局变量链表中按名称查找
#
# 查找策略:
# 1. 精确匹配 (如 "_mbuddy" 匹配 @_mbuddy)
# 2. 后缀匹配 (如 "_mbuddy" 匹配 @"sha1._mbuddy")
# ============================================================
def find_global_in_module(mod: llvmlite.LLVMModule | t.CPtr,
name: str) -> llvmlite.GlobalVariable | t.CPtr:
"""在模块全局变量链表中按名称查找全局变量"""
if mod is None or name is None:
return None
name_len: t.CSizeT = string.strlen(name)
cur: llvmlite.GlobalVariable | t.CPtr = mod.GlobalHead
# 第一遍: 精确匹配
while cur is not None:
if cur.Name is not None:
if string.strcmp(cur.Name, name) == 0:
return cur
cur = cur.Next
# 第二遍: 后缀匹配 (.name)
cur = mod.GlobalHead
while cur is not None:
if cur.Name is not None:
cur_len: t.CSizeT = string.strlen(cur.Name)
if cur_len > name_len + 1:
suffix_start: t.CSizeT = cur_len - name_len
if cur.Name[suffix_start - 1] == '.':
match: int = 1
for i in range(name_len):
if cur.Name[suffix_start + i] != name[i]:
match = 0
break
if match == 1:
return cur
cur = cur.Next
return None
# ============================================================
# make_global_ref - 创建全局变量引用 (@name类型为 ty*)
#
# 用于跨模块全局变量访问: stub 未注入时,翻译阶段生成 @name 引用,
# 链接时由 stub 提供 external global 声明。
# ============================================================
def make_global_ref(pool: memhub.MemBuddy | t.CPtr,
name: str,
ty: llvmlite.LLVMType | t.CPtr) -> llvmlite.Value | t.CPtr:
"""创建全局变量引用 (类型为 ty*,名称为 @name)"""
if name is None or ty is None:
return None
ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, ty)
ref_name: t.CChar | t.CPtr = pool.alloc(64)
if ref_name is None:
return None
viperlib.snprintf(ref_name, 64, "@%s", name)
return llvmlite.SSAValue(pool, ptr_ty, ref_name)
# ============================================================
# _resolve_module_attribute_global - 解析模块属性访问的全局变量引用
#
# 当 obj 是已导入模块名 (如 sys._mbuddy) 时:
# 1. 在当前模块全局变量链表中查找 (stub 已注入的情况)
# 2. 未找到则创建前向引用 (@attr_name, 类型为 ty 参数)
#
# 参数:
# ty_hint: 类型提示 (写路径用 rhs 类型, 读路径用 i8* 回退)
# 返回: Value 指针 (类型为 ty*),未识别为模块属性返回 None
# ============================================================
def _resolve_module_attribute_global(pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
trans: HT.Translator | t.CPtr,
at: ast.Attribute | t.CPtr,
ty_hint: llvmlite.LLVMType | t.CPtr) -> llvmlite.Value | t.CPtr:
"""解析模块属性访问,返回全局变量引用 (用于跨模块 global 访问)"""
if at is None or at.value is None or at.attr is None:
return None
if at.value.kind() != ast.ASTKind.Name:
return None
if trans is None or trans._imported_modules is None:
return None
mod_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value)
if mod_nm.id is None:
return None
# 检查是否是已导入模块
if HandlesImports.is_module_imported(trans._imported_modules, mod_nm.id) == 0:
return None
# 在当前模块查找全局变量 (stub 可能已注入)
gv: llvmlite.GlobalVariable | t.CPtr = find_global_in_module(mod, at.attr)
if gv is not None and gv.Ty is not None:
return make_global_ref(pool, at.attr, gv.Ty)
# 未找到: 创建前向引用 (stub 后续注入时提供声明)
use_ty: llvmlite.LLVMType | t.CPtr = ty_hint
if use_ty is None:
use_ty = llvmlite.Int8(pool)
return make_global_ref(pool, at.attr, use_ty)
# ============================================================
# 翻译属性访问 Attribute(value, attr, ctx) — obj.field
#
# 优先级:
# 1. 枚举成员访问 (State.Idle → 常量值)
# 2. 模块属性访问 (sys._mbuddy → 加载全局变量)
# 3. 结构体字段访问 (obj.field → GEP + load)
#
# 对于 Name 类型的 obj直接使用 alloca 指针(不 load 结构体)
# ============================================================
def translate_attribute(builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
node: ast.AST | t.CPtr,
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
"""翻译属性访问,返回加载的字段值"""
if node is None or builder is None:
return None
at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(node)
if at is None or at.value is None or at.attr is None:
return None
# 枚举成员访问: EnumName.MemberName → 常量值
# 当 value 是 Name 且 Name.id 是已注册枚举时,查找成员并返回常量
if at.value.kind() == ast.ASTKind.Name:
enum_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value)
if enum_nm.id is not None:
if HandlesEnum.is_enum_class(enum_nm.id) == 1:
member: HandlesEnum.EnumMember | t.CPtr = HandlesEnum.lookup_enum_member(
enum_nm.id, at.attr)
if member is not None:
base_ty: llvmlite.LLVMType | t.CPtr = HandlesEnum.get_enum_base_type(
enum_nm.id)
if base_ty is not None:
# 生成数字字符串作为常量名LLVM IR 要求常量输出数字值)
name_buf: t.CChar | t.CPtr = pool.alloc(32)
if name_buf is not None:
viperlib.snprintf(name_buf, 32, "%lld", member.Value)
return llvmlite.ConstInt(pool, base_ty, member.Value, name_buf)
return llvmlite.const_int32(pool, member.Value)
return None
# 模块属性访问 (读路径): sys._mbuddy → 加载全局变量 @_mbuddy
# 当 obj 是已导入模块名且不是普通变量时,查找/创建全局变量引用并加载
if at.value.kind() == ast.ASTKind.Name and trans is not None:
nm_ma: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value)
if nm_ma.id is not None:
# 先检查是否是普通变量 (优先级高于模块属性)
ma_is_var: llvmlite.Value | t.CPtr = HandlesVar.lookup_var(
trans.SymTab, nm_ma.id)
if ma_is_var is None:
# 使用 i8* 作为类型提示 (模块级变量通常存储指针)
i8_ptr_hint: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, llvmlite.Int8(pool))
mod_gv_ref: llvmlite.Value | t.CPtr = _resolve_module_attribute_global(
pool, mod, trans, at, i8_ptr_hint)
if mod_gv_ref is not None and mod_gv_ref.Ty is not None:
load_ty_mod: llvmlite.LLVMType | t.CPtr = mod_gv_ref.Ty.Pointee
if load_ty_mod is not None:
return llvmlite.build_load(builder, load_ty_mod, mod_gv_ref)
# 对于 Name 类型的 obj直接查找 alloca不 load 结构体)
obj_ptr: llvmlite.Value | t.CPtr = None
if at.value.kind() == ast.ASTKind.Name and trans is not None:
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value)
if nm.id is not None:
obj_ptr = HandlesVar.lookup_var(trans.SymTab, nm.id)
# 非 Name 路径:翻译对象值(会 load
if obj_ptr is None:
obj_ptr = translate_value(builder, pool, mod, at.value, None, 0, trans)
if obj_ptr is None or obj_ptr.Ty is None:
return None
# 如果 obj_ptr 是 Ptr(Ptr(Struct))X|t.CPtr 变量的 alloca
# load 解引用获取 Ptr(Struct)
obj_ptr = _deref_if_ptr_ptr(builder, obj_ptr)
# 查找结构体类型信息
# obj_ptr 类型应该是 Ptr(Struct(...))
match obj_ptr.Ty:
case llvmlite.LLVMType.Ptr(struct_ty):
# 查找字段索引和类型
field_info: HandlesStruct.FieldEntry | t.CPtr = HandlesStruct.lookup_field(
struct_ty, at.attr)
# 回退: 类型指针比较失败时,通过 AnnotClassName 按类名查找
# 传递 SHA1 以区分跨模块同名类
if field_info is None:
if at.value.kind() == ast.ASTKind.Name and trans is not None:
nm_fb: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value)
if nm_fb.id is not None:
var_entry: HandlesVar.VarEntry | t.CPtr = HandlesVar.lookup_var_entry(
trans.SymTab, nm_fb.id)
if var_entry is not None and var_entry.AnnotClassName is not None:
cur_sha1: str = trans.ModuleSha1
field_info = HandlesStruct.lookup_field_by_class(
var_entry.AnnotClassName, at.attr, cur_sha1)
if field_info is not None:
field_ty: llvmlite.LLVMType | t.CPtr = field_info.Ty
# 联合体bitcast obj_ptr 到 field_ty* 后 load
if HandlesStruct.is_union_by_type(struct_ty) == 1:
field_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, field_ty)
casted: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(
builder, obj_ptr, field_ptr_ty)
if casted is not None:
return llvmlite.build_load(builder, field_ty, casted)
return None
# 普通结构体GEP + load
field_idx: int = field_info.Index
field_ptr: llvmlite.Value | t.CPtr = llvmlite.build_gep_struct(
builder, struct_ty, field_ty, obj_ptr, field_idx)
if field_ptr is not None:
return llvmlite.build_load(builder, field_ty, field_ptr)
return None
case _:
return None
# ============================================================
# 获取下标表达式的元素指针(不加载值,用于赋值)
# ============================================================
def get_subscript_ptr(builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
node: ast.AST | t.CPtr,
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
"""获取下标表达式的元素指针(用于赋值 lhs"""
if node is None or builder is None:
return None
sub: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(node)
if sub is None:
return None
# 翻译索引
idx_val: llvmlite.Value | t.CPtr = translate_value(
builder, pool, mod, sub.slice, None, 0, trans)
if idx_val is None:
return None
# 如果 value 是 Name尝试从 alloca 类型推断
if sub.value is not None and sub.value.kind() == ast.ASTKind.Name:
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(sub.value)
if nm.id is not None and trans is not None:
alloca: llvmlite.Value | t.CPtr = HandlesVar.lookup_var(trans.SymTab, nm.id)
if alloca is not None and alloca.Ty is not None:
if is_ptr_type(alloca.Ty) != 0:
pointee: llvmlite.LLVMType | t.CPtr = alloca.Ty.Pointee
if pointee is not None:
# 单层 match避免嵌套 match 的编译器 bug
match pointee:
case llvmlite.LLVMType.Array(elem_ty, count):
# 数组遍历
return llvmlite.build_gep_array(
builder, pointee, elem_ty, alloca, idx_val)
case llvmlite.LLVMType.Ptr(inner_ty):
# 指针遍历
ptr_val: llvmlite.Value | t.CPtr = llvmlite.build_load(
builder, pointee, alloca)
if ptr_val is not None:
return llvmlite.build_gep(
builder, inner_ty, ptr_val, idx_val)
return None
case _:
pass
# 通用路径
ptr_val: llvmlite.Value | t.CPtr = translate_value(
builder, pool, mod, sub.value, None, 0, trans)
if ptr_val is None or ptr_val.Ty is None:
return None
if is_ptr_type(ptr_val.Ty) != 0:
elem_ty2: llvmlite.LLVMType | t.CPtr = ptr_val.Ty.Pointee
if elem_ty2 is not None:
return llvmlite.build_gep(builder, elem_ty2, ptr_val, idx_val)
return None
# ============================================================
# 获取属性访问的字段指针(不加载值,用于赋值)
# 对于 Name 类型的 obj直接使用 alloca 指针(不 load 结构体)
# ============================================================
def get_attribute_ptr(builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
node: ast.AST | t.CPtr,
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
"""获取属性访问的字段指针(用于赋值 lhs"""
if node is None or builder is None:
return None
at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(node)
if at is None or at.value is None or at.attr is None:
return None
# 对于 Name 类型的 obj直接查找 alloca不 load 结构体)
obj_ptr: llvmlite.Value | t.CPtr = None
if at.value.kind() == ast.ASTKind.Name and trans is not None:
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value)
if nm.id is not None:
obj_ptr = HandlesVar.lookup_var(trans.SymTab, nm.id)
# 模块属性访问 (写路径): sys._mbuddy = mb → store 到全局变量 @_mbuddy
# 当 obj 是已导入模块名且不是普通变量时,查找/创建全局变量引用
if obj_ptr is None and at.value.kind() == ast.ASTKind.Name and trans is not None:
nm_wma: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value)
if nm_wma.id is not None:
# 使用 i8* 作为类型提示 (模块级变量通常存储指针)
i8_ptr_w: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, llvmlite.Int8(pool))
wma_ref: llvmlite.Value | t.CPtr = _resolve_module_attribute_global(
pool, mod, trans, at, i8_ptr_w)
if wma_ref is not None:
return wma_ref
# 非 Name 路径:翻译对象值(会 load
if obj_ptr is None:
obj_ptr = translate_value(builder, pool, mod, at.value, None, 0, trans)
if obj_ptr is None or obj_ptr.Ty is None:
return None
# 如果 obj_ptr 是 Ptr(Ptr(Struct))X|t.CPtr 变量的 alloca
# load 解引用获取 Ptr(Struct)
obj_ptr = _deref_if_ptr_ptr(builder, obj_ptr)
match obj_ptr.Ty:
case llvmlite.LLVMType.Ptr(struct_ty):
field_info: HandlesStruct.FieldEntry | t.CPtr = HandlesStruct.lookup_field(
struct_ty, at.attr)
# 回退: 类型指针比较失败时,通过 AnnotClassName 按类名查找
# 传递 SHA1 以区分跨模块同名类
if field_info is None:
if at.value.kind() == ast.ASTKind.Name and trans is not None:
nm_fb: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value)
if nm_fb.id is not None:
var_entry: HandlesVar.VarEntry | t.CPtr = HandlesVar.lookup_var_entry(
trans.SymTab, nm_fb.id)
if var_entry is not None and var_entry.AnnotClassName is not None:
cur_sha1: str = None
if trans is not None:
cur_sha1 = trans.ModuleSha1
field_info = HandlesStruct.lookup_field_by_class(
var_entry.AnnotClassName, at.attr, cur_sha1)
if field_info is not None:
# 联合体bitcast obj_ptr 到 field_ty*(字段指针用于 store
if HandlesStruct.is_union_by_type(struct_ty) == 1:
field_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, field_info.Ty)
return llvmlite.build_bitcast(builder, obj_ptr, field_ptr_ty)
# 普通结构体GEP 获取字段指针
return llvmlite.build_gep_struct(
builder, struct_ty, field_info.Ty, obj_ptr, field_info.Index)
return None
case _:
return None