Files
ViperC/App/lib/core/Gen/VarGenMixin.vp
T

531 lines
27 KiB
Plaintext
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 lib.core.Gen.GenTypes as GenTypes
import lib.core.Gen.Generator as Generator
import lib.core.Gen.Type.TypeSpec as TSpec
import t, c
from stdint import *
import llvmlite
import memhub
import string
# ============================================================
# VarGenMixin - 变量/作用域符号表 Mixin(P6 对象化,wiki/14-oo-refactor.md)
#
# 从 HandlesVar.vp 迁移:模块级函数(init_vars/find_var/find_var_entry/
# lookup_var_entry/set_var_annot_class_name/set_var_ptr_element/add_var/
# _alloca_at_entry/get_or_create_var/_create_scope/init_symbol_table/
# enter_scope/exit_scope/define_var/define_module_var/lookup_var/
# lookup_current/lookup_module_var/is_module_scope/get_or_create_sym)
# → 本类方法。
#
# @t.NoVTable:静态绑定(方法沿 MRO 直接调用 + this 调整,无 vtable 开销)。
# ============================================================
# ============================================================
# 常量(与 HandlesVar 保持一致)
# ============================================================
# 作用域类型常量
SCOPE_MODULE: t.CDefine = 0
SCOPE_FUNCTION: t.CDefine = 1
SCOPE_BLOCK: t.CDefine = 2
# match case 作用域(对齐 MatchGenMixin.SCOPE_MATCH;本地定义避免循环依赖)。
# 该作用域仅用于"变体字段绑定隔离",普通赋值语义上属于外层函数作用域,
# get_or_create_sym/_lookup_through_match_scope 会"看穿"它写回外层变量。
SCOPE_MATCH: t.CDefine = 6
# 每个作用域最大变量数
# 256 太小:NewAstTest main() 有 289 个局部变量(几十个测试用例),
# add_var 超限静默失败导致变量未注册,后续使用报"未定义名称"(NameError)。
MAX_VARS: t.CDefine = 1024
# ============================================================
# VarGenMixin - 变量/作用域符号表 Mixin
# ============================================================
@t.NoVTable
class VarGenMixin:
"""变量/作用域 Mixin(P6 方法化)"""
# 迁移自 HandlesVar 的模块级函数
# ============================================================
# init_vars — 分配并清零 VarEntry 数组
# ============================================================
Gen: Generator.Generator | t.CPtr
def init_vars(self, pool: rowptr[memhub.MemBuddy]) -> GenTypes.VarEntry | t.CPtr:
"""分配并清零变量表数组"""
size: sizet = MAX_VARS * GenTypes.VarEntry.__sizeof__()
vars_ptr: GenTypes.VarEntry | t.CPtr = pool.alloc(size)
if vars_ptr is not None:
string.memset(vars_ptr, 0, size)
return vars_ptr
# ============================================================
# find_var — 在变量表中按名称查找
# ============================================================
def find_var(self, vars_ptr: GenTypes.VarEntry | t.CPtr,
var_count: int,
name: str) -> llvmlite.Value | t.CPtr:
"""在变量表中按名称查找"""
if name is None or vars_ptr is None:
return None
entry_size: sizet = GenTypes.VarEntry.__sizeof__()
for i in range(var_count):
entry_addr: uint64 = uint64(vars_ptr) + i * entry_size
entry: GenTypes.VarEntry | t.CPtr = (GenTypes.VarEntry | t.CPtr)(t.CVoid(entry_addr, t.CPtr))
if entry.Name is not None and entry.Used != 0:
if string.strcmp(entry.Name, name) == 0:
return entry.Alloca
return None
# ============================================================
# find_var_entry — 在变量表中按名称查找,返回 VarEntry(含 AnnotTy)
# ============================================================
def find_var_entry(self, vars_ptr: GenTypes.VarEntry | t.CPtr,
var_count: int,
name: str) -> GenTypes.VarEntry | t.CPtr:
"""在变量表中按名称查找,返回 VarEntry 或 None"""
if name is None or vars_ptr is None:
return None
entry_size: sizet = GenTypes.VarEntry.__sizeof__()
for i in range(var_count):
entry_addr: uint64 = uint64(vars_ptr) + i * entry_size
entry: GenTypes.VarEntry | t.CPtr = (GenTypes.VarEntry | t.CPtr)(t.CVoid(entry_addr, t.CPtr))
if entry.Name is not None and entry.Used != 0:
if string.strcmp(entry.Name, name) == 0:
return entry
return None
# ============================================================
# lookup_var_entry — 从当前作用域逐级向上查找变量,返回 VarEntry
# ============================================================
def lookup_var_entry(self, symtab: GenTypes.SymbolTable | t.CPtr,
name: str) -> GenTypes.VarEntry | t.CPtr:
"""从当前作用域逐级向上查找变量,返回 VarEntry 或 None"""
if symtab is None or name is None:
return None
scope: GenTypes.Scope | t.CPtr = symtab.Current
while scope is not None:
result: GenTypes.VarEntry | t.CPtr = self.find_var_entry(scope.Vars, scope.VarCount, name)
if result is not None:
return result
scope = scope.Parent
return None
# ============================================================
# set_var_annot_class_name — 设置变量的原始类型注解类名
#
# 在函数参数定义后调用,存储原始类型注解的类名(Spec.SourceName)。
# 方法调用检测时,当 alloca 类型是 Ptr(i8)(联合类型简化),
# 通过 Spec.SourceName 查找实际结构体类型。
# ============================================================
def set_var_annot_class_name(self, symtab: GenTypes.SymbolTable | t.CPtr,
name: str,
class_name: str) -> int:
"""设置变量的原始类型注解类名(写入 Spec.SourceName),返回 0=成功 / 1=失败
[S4] 原 VarEntry.AnnotClassName 字段已删除,注解名统一承载在 Spec.SourceName:
- 整数符号性:SpecFromLLVMType 反推恒 TS_INT(LLVM iN 不分符号),
注解名携带真实无符号性(uint32/CUInt32T 等)→ 切 TS_INT→TS_UINT;
- 来源名:`X | t.CPtr` 简化为 Ptr(i8) 后 Spec 里没有类名,附上供
属性访问/方法分派恢复(消费点经 var_annot_class_name 读取)。
类型信息缺失(SpecFromLLVMType 不适用致 Spec 为 None)时建 TS_UNKNOWN
承载注解名 —— TS_UNKNOWN 不表态任何具体类型,等价于原来的"Spec 缺失"。
"""
if symtab is None or name is None:
return 1
entry: GenTypes.VarEntry | t.CPtr = self.lookup_var_entry(symtab, name)
if entry is None:
return 1
if class_name is None:
# 无注解名:不写入。原实现无条件 `entry.AnnotClassName = None` 会清空,
# 但 VarEntry 槽位不复用(add_var 只递增),首次设置时原值恒为 None,
# 故二者等价;重复调用且第二次为 None 的差异不存在于现有调用点。
return 0
arena_sn: TSpec.TypeSpecArena | t.CPtr = self.Gen.TypeGenBase.ensure_spec_arena()
if arena_sn is None:
return 1
if entry.Spec is None:
entry.Spec = TSpec.NewTypeSpec(arena_sn, TSpec.TS_UNKNOWN, 0,
TSpec.TS_SIGN_UNKNOWN)
if entry.Spec is None:
return 1
sign: int = self.Gen.TypeGenCType.spec_sign_of_annot(class_name)
if sign != TSpec.TS_SIGN_UNKNOWN:
entry.Spec = TSpec.SpecWithSign(arena_sn, entry.Spec, sign)
entry.Spec = TSpec.SpecWithSource(entry.Spec, class_name)
return 0
# ============================================================
# var_annot_class_name - 变量条目注解类名的统一读取入口(S4 切读·类名块)
#
# 注解侧由 set_var_annot_class_name 经 SpecWithSource 写入 Spec.SourceName。
# 变量侧类名恢复读取点统一走此入口。
# ============================================================
def var_annot_class_name(self, entry: GenTypes.VarEntry | t.CPtr) -> str:
"""变量条目注解类名(Spec.SourceName);未设置返回 None"""
if entry is None:
return None
return TSpec.SpecSourceName(entry.Spec)
# ============================================================
# set_var_ptr_element — 标记变量为「指针元素(ptr-element)」语义
#
# 注解为 bytes|t.CPtr / str|t.CPtr / T|t.CPtr / 64位标量|t.CPtr 的变量,
# 其 LLVM 类型退化为 i8*(Ptr(Int8)),但下标访问应按 **8 字节槽步长**
# (i8** 语义),而非 1 字节步长(i8* 语义)。
#
# [S4] 原 VarEntry.IsPtrElement 布尔字段已删除,改由 Spec.ElemBits 承载:
# 上述四类在 LLVM 层都是 Ptr(Int8)(ElemBits=8,无法区分),故注解侧把
# 元素槽宽覆盖为 8 字节;消费点经 var_is_ptr_element 读取。
# 由 AnnAssign 按注解形式(_is_ptr_element_annotation)调用。
# ============================================================
def set_var_ptr_element(self, symtab: GenTypes.SymbolTable | t.CPtr,
name: str) -> int:
"""标记变量为 ptr-element(Spec.ElemBits=64),返回 0=成功 / 1=失败"""
if symtab is None or name is None:
return 1
entry: GenTypes.VarEntry | t.CPtr = self.lookup_var_entry(symtab, name)
if entry is None:
return 1
# ptr-element 变量的 alloca 是 Ptr(Ptr(Int8)),其值类型 Ptr(Int8) ⇒ Spec 必为
# PtrLevel>=1。不满足即上游异常(类型缺失/退化),fail-fast 暴露 ——
# 若静默放过,读点 var_is_ptr_element 返回 0 → 下标按 1 字节误算(数据损坏)。
if entry.Spec is None or entry.Spec.PtrLevel < 1:
self.Gen.TypeGenErr.fatal_error(
None, f"变量 '{name}' 标记为 ptr-element,但其 Spec 不可承载指针语义",
"InternalCompilerError")
return 1
entry.Spec = TSpec.SpecWithElemBits(entry.Spec, 64)
return 0
# ============================================================
# var_is_ptr_element - 变量是否「指针元素」语义(原 VarEntry.IsPtrElement)
#
# 判据(三元组 ElemBits 承载):PtrLevel>=1 且元素位宽为 8(LLVM 层退化后的
# i8*)且 ElemBits==64(注解侧覆盖的 8 字节槽)。
# 后两个条件缺一不可:真实 i64* 变量 ElemBits 也为 64,但其 Elem.Bits=64,
# 不会被误判;纯 str/bytes/char|t.CPtr 变量 ElemBits=8,同样不命中。
# ============================================================
def var_is_ptr_element(self, entry: GenTypes.VarEntry | t.CPtr) -> int:
"""变量是否为 ptr-element 语义(1=是 / 0=否)"""
if entry is None or entry.Spec is None:
return 0
sp: TSpec.TypeSpec | t.CPtr = entry.Spec
if sp.PtrLevel < 1 or sp.Elem is None:
return 0
if sp.Elem.Bits != 8:
return 0
if sp.ElemBits == 64:
return 1
return 0
# ============================================================
# add_var — 添加变量到变量表
# ============================================================
def add_var(self, vars_ptr: GenTypes.VarEntry | t.CPtr,
var_count: int,
name: str,
alloca: llvmlite.Value | t.CPtr) -> int:
"""添加变量到变量表"""
if name is None or alloca is None or vars_ptr is None:
return 1
if var_count >= MAX_VARS:
return 1
entry_size: sizet = GenTypes.VarEntry.__sizeof__()
entry_addr: uint64 = uint64(vars_ptr) + var_count * entry_size
entry: GenTypes.VarEntry | t.CPtr = (GenTypes.VarEntry | t.CPtr)(t.CVoid(entry_addr, t.CPtr))
entry.Name = name
entry.Alloca = alloca
entry.Used = 1
# [S3 条目双写] 值类型 Spec(alloca.Ty 是 Ptr(值类型) ⇒ 取其 pointee)
entry.Spec = self._spec_of_var_alloca(alloca)
return 0
# ============================================================
# _spec_of_var_alloca - 由 alloca 的值类型构造 TypeSpec(S3 条目双写)
#
# alloca.Ty 为 Ptr(var_ty)(见 _alloca_at_entry / 全局变量 SSAValue 路径),
# 故取 pointee 才是变量的**值类型**。失败或类型不适用(Label/Metadata 等
# SpecFromLLVMType 不支持者)返回 None —— 后者是设计内的「不适用」,非兜底。
# 分配一律 PermPool 驻留 ⇒ 跨文件/跨 1b reset 安全。
# ============================================================
def _spec_of_var_alloca(self, alloca: llvmlite.Value | t.CPtr) -> TSpec.TypeSpec | t.CPtr:
"""由 alloca 的值类型构造 TypeSpec,不适用/失败返回 None"""
if alloca is None or alloca.Ty is None:
return None
ty: llvmlite.LLVMType | t.CPtr = alloca.Ty
if self.Gen.ExprGenBase.is_ptr_type(ty) != 0:
ty = ty.Pointee
arena: TSpec.TypeSpecArena | t.CPtr = self.Gen.TypeGenBase.ensure_spec_arena()
if arena is None:
return None
return TSpec.SpecFromLLVMType(arena, ty)
# ============================================================
# _alloca_at_entry - 在函数入口块生成 alloca(确保支配性)
#
# LLVM IR 要求指令支配所有使用点。如果 alloca 在条件分支内生成,
# 但在其他分支使用,会违反支配性(Instruction does not dominate all uses)。
# 标准做法:所有 alloca 在函数入口块生成。
#
# 临时切换 builder.CurBlock 到入口块,生成 alloca,然后恢复。
# ============================================================
def _alloca_at_entry(self, builder: llvmlite.IRBuilder | t.CPtr,
ty: llvmlite.LLVMType | t.CPtr) -> llvmlite.Value | t.CPtr:
"""在函数入口块生成 alloca,返回 alloca 值
使用 llvmlite.build_alloca_at_entry 在入口块终止指令之前插入 alloca,
确保所有 alloca 在入口块,避免支配性违规。
"""
if builder is None or ty is None:
return None
return llvmlite.build_alloca_at_entry(builder, ty)
# ============================================================
# get_or_create_var — 查找或创建变量 alloca(旧版兼容)
# ============================================================
def get_or_create_var(self, pool: rowptr[memhub.MemBuddy],
builder: llvmlite.IRBuilder | t.CPtr,
vars_ptr: GenTypes.VarEntry | t.CPtr,
var_count: int,
name: str,
ty: llvmlite.LLVMType | t.CPtr) -> llvmlite.Value | t.CPtr:
"""查找或创建变量 alloca"""
existing: llvmlite.Value | t.CPtr = self.find_var(vars_ptr, var_count, name)
if existing is not None:
return existing
alloca: llvmlite.Value | t.CPtr = self._alloca_at_entry(builder, ty)
if alloca is None:
return None
if self.add_var(vars_ptr, var_count, name, alloca) != 0:
return None
return alloca
# ============================================================
# _create_scope — 创建新作用域节点(内部辅助函数)
# ============================================================
def _create_scope(self, pool: rowptr[memhub.MemBuddy],
parent: GenTypes.Scope | t.CPtr,
kind: int) -> GenTypes.Scope | t.CPtr:
"""创建并初始化作用域节点"""
scope: GenTypes.Scope | t.CPtr = pool.alloc(GenTypes.Scope.__sizeof__())
if scope is None:
return None
string.memset(scope, 0, GenTypes.Scope.__sizeof__())
scope.Parent = parent
scope.Vars = self.init_vars(pool)
scope.VarCount = 0
scope.Kind = kind
if scope.Vars is None:
return None
return scope
# ============================================================
# init_symbol_table — 创建符号表(含模块级根作用域)
# ============================================================
def init_symbol_table(self, pool: rowptr[memhub.MemBuddy]) -> GenTypes.SymbolTable | t.CPtr:
"""创建并初始化符号表,包含模块级根作用域"""
if pool is None:
return None
symtab: GenTypes.SymbolTable | t.CPtr = pool.alloc(GenTypes.SymbolTable.__sizeof__())
if symtab is None:
return None
string.memset(symtab, 0, GenTypes.SymbolTable.__sizeof__())
symtab.Pool = pool
# 创建根作用域(模块级)
root: GenTypes.Scope | t.CPtr = self._create_scope(pool, None, SCOPE_MODULE)
if root is None:
return None
symtab.Root = root
symtab.Current = root
return symtab
# ============================================================
# enter_scope — 进入新作用域
# ============================================================
def enter_scope(self, symtab: GenTypes.SymbolTable | t.CPtr,
kind: int) -> GenTypes.Scope | t.CPtr:
"""进入新作用域,返回新创建的作用域"""
if symtab is None:
return None
pool: rowptr[memhub.MemBuddy] = symtab.Pool
scope: GenTypes.Scope | t.CPtr = self._create_scope(pool, symtab.Current, kind)
if scope is None:
return None
symtab.Current = scope
return scope
# ============================================================
# exit_scope — 退出当前作用域
# ============================================================
def exit_scope(self, symtab: GenTypes.SymbolTable | t.CPtr):
"""退出当前作用域,恢复到父作用域"""
if symtab is not None and symtab.Current is not None:
symtab.Current = symtab.Current.Parent
# ============================================================
# define_var — 在当前作用域定义变量
# ============================================================
def define_var(self, symtab: GenTypes.SymbolTable | t.CPtr,
name: str,
alloca: llvmlite.Value | t.CPtr) -> int:
"""在当前作用域定义变量,返回 0 成功"""
if symtab is None or name is None or alloca is None:
return 1
scope: GenTypes.Scope | t.CPtr = symtab.Current
if scope is None:
return 1
ret: int = self.add_var(scope.Vars, scope.VarCount, name, alloca)
if ret == 0:
scope.VarCount = scope.VarCount + 1
return ret
# ============================================================
# define_module_var — 在模块作用域定义变量
# ============================================================
def define_module_var(self, symtab: GenTypes.SymbolTable | t.CPtr,
name: str,
alloca: llvmlite.Value | t.CPtr) -> int:
"""在模块作用域定义变量,返回 0 成功"""
if symtab is None or name is None or alloca is None:
return 1
scope: GenTypes.Scope | t.CPtr = symtab.Root
if scope is None:
return 1
ret: int = self.add_var(scope.Vars, scope.VarCount, name, alloca)
if ret == 0:
scope.VarCount = scope.VarCount + 1
return ret
# ============================================================
# lookup_var — 从当前作用域逐级向上查找变量
# ============================================================
def lookup_var(self, symtab: GenTypes.SymbolTable | t.CPtr,
name: str) -> llvmlite.Value | t.CPtr:
"""从当前作用域逐级向上查找变量,返回 alloca 或 None"""
if symtab is None or name is None:
return None
scope: GenTypes.Scope | t.CPtr = symtab.Current
while scope is not None:
result: llvmlite.Value | t.CPtr = self.find_var(
scope.Vars, scope.VarCount, name)
if result is not None:
return result
scope = scope.Parent
return None
# ============================================================
# lookup_current — 仅在当前作用域查找变量
# ============================================================
def lookup_current(self, symtab: GenTypes.SymbolTable | t.CPtr,
name: str) -> llvmlite.Value | t.CPtr:
"""仅在当前作用域查找变量"""
if symtab is None or name is None:
return None
scope: GenTypes.Scope | t.CPtr = symtab.Current
if scope is None:
return None
return self.find_var(scope.Vars, scope.VarCount, name)
# ============================================================
# lookup_module_var — 在模块作用域查找变量
# ============================================================
def lookup_module_var(self, symtab: GenTypes.SymbolTable | t.CPtr,
name: str) -> llvmlite.Value | t.CPtr:
"""在模块作用域查找变量"""
if symtab is None or name is None:
return None
scope: GenTypes.Scope | t.CPtr = symtab.Root
if scope is None:
return None
return self.find_var(scope.Vars, scope.VarCount, name)
# ============================================================
# is_module_scope - 判断当前是否在模块级作用域
#
# 用于区分模块级代码和函数内代码:
# - 模块级代码:AnnAssign 应修改模块级变量(Python 语义)
# - 函数内代码:无 global 声明时 AnnAssign 应创建局部变量(Python 语义)
# ============================================================
def is_module_scope(self, symtab: GenTypes.SymbolTable | t.CPtr) -> int:
"""判断当前作用域是否为模块级作用域(Current == Root)
Returns: 1=模块级作用域, 0=函数内作用域
"""
if symtab is None:
return 0
if symtab.Root is None or symtab.Current is None:
return 0
# 比较指针:Current 与 Root 相同则为模块级
if uint64(symtab.Current) == uint64(symtab.Root):
return 1
return 0
# ============================================================
# _lookup_through_match_scope — 穿越 match case 作用域查找变量
#
# match case 作用域(SCOPE_MATCH)仅用于"变体字段绑定隔离"(case 绑定的
# 模式变量互不泄漏)。case 体内对"外层普通变量"的赋值(如 FuncGenDef
# 参数 match 的 param_ty2 = Ptr(...),或 translate_subscript 内层 case 的
# list_struct_name = lsn)语义上应写回外层 alloca,而非新建 case 局部槽
# ——否则外层变量恒为初始值。自举证据:gen1 编译出的 gen2 其 FuncGenDef
# 参数 match 把赋值写进 case 局部槽、build_alloca 读初始 i32_ty,导致
# 指针参数 alloca 退化为 i32 → FuncTest main.vp:32 `p[0] = p[0] + val`
# 的 translate_subscript 读 i32 非指针返回 None("赋值右侧为 None
# [kind=11]" 根因)。
# 遍历规则:逐级向上查找;SCOPE_MATCH 边界可穿越(可命中外层 case 声明的
# 变量),遇到第一个非 match 作用域(函数/模块)后停止——不跨函数边界
# (闭包 nonlocal 语义由 is_nonlocal_name 单独处理)。
# ============================================================
def _lookup_through_match_scope(self, symtab: GenTypes.SymbolTable | t.CPtr,
name: str) -> llvmlite.Value | t.CPtr:
"""从当前作用域向上,仅穿越 SCOPE_MATCH 作用域查找变量 alloca"""
if symtab is None or name is None:
return None
scope: GenTypes.Scope | t.CPtr = symtab.Current
while scope is not None:
result: llvmlite.Value | t.CPtr = self.find_var(
scope.Vars, scope.VarCount, name)
if result is not None:
return result
if scope.Kind == SCOPE_MATCH:
scope = scope.Parent
continue
return None
return None
# ============================================================
# get_or_create_sym — 查找或创建变量(在当前作用域)
# ============================================================
def get_or_create_sym(self, symtab: GenTypes.SymbolTable | t.CPtr,
pool: rowptr[memhub.MemBuddy],
builder: llvmlite.IRBuilder | t.CPtr,
name: str,
ty: llvmlite.LLVMType | t.CPtr) -> llvmlite.Value | t.CPtr:
"""查找或创建变量 alloca(在当前作用域)"""
existing: llvmlite.Value | t.CPtr = self.lookup_current(symtab, name)
if existing is not None:
return existing
# SCOPE_MATCH 隔离作用域对"赋值"必须透明(详见 _lookup_through_match_scope):
# case 体内对外层变量的赋值要写回外层 alloca。仅穿越 match 边界,
# 不跨函数/模块作用域(global/nonlocal 在 AssignGenMixin 提前拦截)。
existing = self._lookup_through_match_scope(symtab, name)
if existing is not None:
return existing
alloca: llvmlite.Value | t.CPtr = self._alloca_at_entry(builder, ty)
if alloca is None:
return None
if self.define_var(symtab, name, alloca) != 0:
# fail-fast: 变量注册失败(如超过 MAX_VARS)必须立即终止。
# 此前静默忽略返回值:alloca 已创建但未入符号表,后续读取该变量
# 报"未定义名称"(NewAstTest main() 289 个局部变量 > MAX_VARS=256
# 的 NameError 根因),误导排查。变量表超限暴露上游规模问题。
self.Gen.TypeGenErr.fatal_error(
None,
f"变量 '{name}' 注册失败(作用域变量数超过上限 MAX_VARS)",
"InternalCompilerError")
return alloca