fix(gen): nonlocal 跨层共享与链堆预建修复(ClosureTest2 12/12 通过)
- 嵌套函数独立 nonlocal 缓冲,防共享缓冲 [0] 槽覆盖(Test 7 根因), translate_nested_function_def 三处退出点补齐缓冲恢复 - _prepare_chain_nonlocals 去掉模块全局 found_g 跳过:建堆+store 每次 照发,映射变量仅首次 add(去重),修复跨测试函数残留堆地址致 env 读错堆 - 新增 trans._nl_chain_done 每函数标志(translate_function_def 入口重置), 多嵌套函数定义共享同一堆,防重复 malloc + 二次重定向 VarEntry - nonlocal 属性/下标优先走 env 读堆值(对象即指针,Struct/Array 不 load), 新增 _nonlocal_subscript_ptr 经 env 读堆地址后 GEP - _type_byte_size 按类型字节数分配 nonlocal 共享堆(数组 16 字节防越界)
This commit is contained in:
1 parent
c472ea9798
commit
48d9fbab4c
3 files changed
+461
-36
No files matched your search
@@ -2585,6 +2585,52 @@ class ExprGenMixin:
|
||||
builder: llvmlite.IRBuilder | t.CPtr = trans._cur_builder
|
||||
return llvmlite.build_load(builder, pointee, alloca)
|
||||
|
||||
# ============================================================
|
||||
# _nonlocal_subscript_ptr - nonlocal 数组/指针的下标元素指针
|
||||
#
|
||||
# 嵌套函数(提升函数)体内访问 nonlocal 变量时,变量的实际存储是
|
||||
# 运行时共享堆(env[ni] 存的堆地址),不能 lookup_var 找外层函数
|
||||
# alloca(其 SSA 编号属于外层函数,当前函数引用错乱崩溃——
|
||||
# ClosureTest2 Test 4 fill() 内 arr[i] 崩溃根因)。
|
||||
# 正确路径:从 env 读堆地址(_load_raw_var_ptr)→ bitcast → GEP。
|
||||
# ============================================================
|
||||
def _nonlocal_subscript_ptr(self, builder: llvmlite.IRBuilder | t.CPtr,
|
||||
pool: memhub.MemBuddy | t.CPtr,
|
||||
trans: HT.Translator | t.CPtr,
|
||||
nm_id: str,
|
||||
idx_val: llvmlite.Value | t.CPtr) -> llvmlite.Value | t.CPtr:
|
||||
"""nonlocal 数组/指针下标:从 env 读堆地址 → bitcast → GEP,返回元素指针"""
|
||||
if trans is None or nm_id is None or idx_val is None:
|
||||
return None
|
||||
nl_ty: llvmlite.LLVMType | t.CPtr = HT.get_nonlocal_type(trans, nm_id)
|
||||
if nl_ty is None:
|
||||
return None
|
||||
raw_nl: llvmlite.Value | t.CPtr = HandlesNonlocal._load_raw_var_ptr(trans, nm_id)
|
||||
if raw_nl is None:
|
||||
return None
|
||||
match nl_ty:
|
||||
case llvmlite.LLVMType.Array(elem_ty_nl, _cnt_nl):
|
||||
arr_ptr_nl: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(
|
||||
builder, raw_nl, llvmlite.Ptr(pool, nl_ty))
|
||||
if arr_ptr_nl is None:
|
||||
return None
|
||||
return llvmlite.build_gep_array(
|
||||
builder, nl_ty, elem_ty_nl, arr_ptr_nl,
|
||||
self._zext_index_to_i32(builder, idx_val))
|
||||
case llvmlite.LLVMType.Ptr(inner_nl):
|
||||
arr_ptr_nl2: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(
|
||||
builder, raw_nl, llvmlite.Ptr(pool, nl_ty))
|
||||
if arr_ptr_nl2 is None:
|
||||
return None
|
||||
return llvmlite.build_gep(builder, inner_nl, arr_ptr_nl2, idx_val)
|
||||
case _:
|
||||
arr_ptr_nl3: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(
|
||||
builder, raw_nl, llvmlite.Ptr(pool, nl_ty))
|
||||
if arr_ptr_nl3 is None:
|
||||
return None
|
||||
return llvmlite.build_gep(builder, nl_ty, arr_ptr_nl3, idx_val)
|
||||
return None
|
||||
|
||||
# ============================================================
|
||||
# 翻译下标表达式 Subscript(value, slice, ctx) — ptr[i] / arr[i]
|
||||
#
|
||||
@@ -2614,6 +2660,17 @@ class ExprGenMixin:
|
||||
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:
|
||||
# nonlocal 数组/指针:从 env 读共享堆地址(提升函数内不能
|
||||
# lookup_var 外层 alloca——SSA 编号跨函数错乱崩溃)
|
||||
# 读路径:元素指针 load 出元素值
|
||||
if HT.is_nonlocal_name(trans, nm.id) != 0:
|
||||
nl_ptr_r: llvmlite.Value | t.CPtr = self._nonlocal_subscript_ptr(
|
||||
builder, pool, trans, nm.id, idx_val)
|
||||
if nl_ptr_r is not None and nl_ptr_r.Ty is not None:
|
||||
nl_elem_ty: llvmlite.LLVMType | t.CPtr = nl_ptr_r.Ty.Pointee
|
||||
if nl_elem_ty is not None:
|
||||
return llvmlite.build_load(builder, nl_elem_ty, nl_ptr_r)
|
||||
return 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 self.is_ptr_type(alloca.Ty) != 0:
|
||||
@@ -3334,12 +3391,39 @@ class ExprGenMixin:
|
||||
VLogger.error(err_buf_ma, "CD")
|
||||
return None
|
||||
|
||||
# 对于 Name 类型的 obj,直接查找 alloca(不 load 结构体)
|
||||
# 对于 Name 类型的 obj,直接查找 alloca(不 load 结构体);
|
||||
# nonlocal 变量(提升函数内)从 env 读共享堆值,避免 lookup_var
|
||||
# 跨函数引用外层 alloca(SSA 编号跨函数错乱崩溃)
|
||||
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)
|
||||
if HT.is_nonlocal_name(trans, nm.id) != 0:
|
||||
raw_nl_a: llvmlite.Value | t.CPtr = HandlesNonlocal._load_raw_var_ptr(
|
||||
trans, nm.id)
|
||||
if raw_nl_a is not None:
|
||||
nl_ty_a: llvmlite.LLVMType | t.CPtr = HT.get_nonlocal_type(
|
||||
trans, nm.id)
|
||||
if nl_ty_a is not None:
|
||||
# 对象即指针:Struct/Array 类型直接用堆地址(bitcast 后
|
||||
# 作为对象指针,不 load);指针类型(如 Ptr(Point))堆里
|
||||
# 存对象指针,需 load 出对象指针
|
||||
match nl_ty_a:
|
||||
case llvmlite.LLVMType.Struct(_, _, _):
|
||||
obj_ptr = llvmlite.build_bitcast(
|
||||
builder, raw_nl_a, llvmlite.Ptr(pool, nl_ty_a))
|
||||
case llvmlite.LLVMType.Array(_, _):
|
||||
obj_ptr = llvmlite.build_bitcast(
|
||||
builder, raw_nl_a, llvmlite.Ptr(pool, nl_ty_a))
|
||||
case _:
|
||||
nl_val_ptr_a: llvmlite.Value | t.CPtr = \
|
||||
llvmlite.build_bitcast(
|
||||
builder, raw_nl_a, llvmlite.Ptr(pool, nl_ty_a))
|
||||
if nl_val_ptr_a is not None:
|
||||
obj_ptr = llvmlite.build_load(
|
||||
builder, nl_ty_a, nl_val_ptr_a)
|
||||
else:
|
||||
obj_ptr = HandlesVar.lookup_var(trans.SymTab, nm.id)
|
||||
|
||||
# 非 Name 路径:翻译对象值(会 load)
|
||||
if obj_ptr is None:
|
||||
@@ -3543,6 +3627,11 @@ class ExprGenMixin:
|
||||
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:
|
||||
# nonlocal 数组/指针:从 env 读共享堆地址(提升函数内不能
|
||||
# lookup_var 外层 alloca——SSA 编号跨函数错乱崩溃)
|
||||
if HT.is_nonlocal_name(trans, nm.id) != 0:
|
||||
return self._nonlocal_subscript_ptr(
|
||||
builder, pool, trans, nm.id, idx_val)
|
||||
alloca: llvmlite.Value | t.CPtr = HandlesVar.lookup_var(trans.SymTab, nm.id)
|
||||
if alloca is not None and alloca.Ty is not None:
|
||||
if self.is_ptr_type(alloca.Ty) != 0:
|
||||
@@ -3735,12 +3824,39 @@ class ExprGenMixin:
|
||||
if at is None or at.value is None or at.attr is None:
|
||||
return None
|
||||
|
||||
# 对于 Name 类型的 obj,直接查找 alloca(不 load 结构体)
|
||||
# 对于 Name 类型的 obj,直接查找 alloca(不 load 结构体);
|
||||
# nonlocal 变量(提升函数内)从 env 读共享堆值,避免 lookup_var
|
||||
# 跨函数引用外层 alloca(SSA 编号跨函数错乱崩溃)
|
||||
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)
|
||||
if HT.is_nonlocal_name(trans, nm.id) != 0:
|
||||
raw_nl_a: llvmlite.Value | t.CPtr = HandlesNonlocal._load_raw_var_ptr(
|
||||
trans, nm.id)
|
||||
if raw_nl_a is not None:
|
||||
nl_ty_a: llvmlite.LLVMType | t.CPtr = HT.get_nonlocal_type(
|
||||
trans, nm.id)
|
||||
if nl_ty_a is not None:
|
||||
# 对象即指针:Struct/Array 类型直接用堆地址(bitcast 后
|
||||
# 作为对象指针,不 load);指针类型(如 Ptr(Point))堆里
|
||||
# 存对象指针,需 load 出对象指针
|
||||
match nl_ty_a:
|
||||
case llvmlite.LLVMType.Struct(_, _, _):
|
||||
obj_ptr = llvmlite.build_bitcast(
|
||||
builder, raw_nl_a, llvmlite.Ptr(pool, nl_ty_a))
|
||||
case llvmlite.LLVMType.Array(_, _):
|
||||
obj_ptr = llvmlite.build_bitcast(
|
||||
builder, raw_nl_a, llvmlite.Ptr(pool, nl_ty_a))
|
||||
case _:
|
||||
nl_val_ptr_a: llvmlite.Value | t.CPtr = \
|
||||
llvmlite.build_bitcast(
|
||||
builder, raw_nl_a, llvmlite.Ptr(pool, nl_ty_a))
|
||||
if nl_val_ptr_a is not None:
|
||||
obj_ptr = llvmlite.build_load(
|
||||
builder, nl_ty_a, nl_val_ptr_a)
|
||||
else:
|
||||
obj_ptr = HandlesVar.lookup_var(trans.SymTab, nm.id)
|
||||
|
||||
# 模块属性访问 (写路径): sys._mbuddy = mb → store 到全局变量 @_mbuddy
|
||||
# 当 obj 是已导入模块名且不是普通变量时,查找/创建全局变量引用
|
||||
|
||||
@@ -780,6 +780,9 @@ class FuncGenMixin:
|
||||
trans._cur_builder = func_builder
|
||||
# 清空 global/nonlocal 名称集合(新函数作用域)
|
||||
HT.clear_scope_names(trans)
|
||||
# 重置链内 nonlocal 堆预建标志(每函数只建一次;多嵌套函数定义
|
||||
# 共享同一堆,防重复 malloc + 二次重定向 VarEntry)
|
||||
trans._nl_chain_done = 0
|
||||
|
||||
# 预扫描函数体:
|
||||
# 1. 先预扫描 Global/Nonlocal 声明(Python 语义:global/nonlocal
|
||||
@@ -803,6 +806,12 @@ class FuncGenMixin:
|
||||
for bi2 in range(bn2):
|
||||
stmt2: ast.AST | t.CPtr = body.get(bi2)
|
||||
if stmt2 is not None:
|
||||
# 嵌套函数定义前:预扫描其链内 nonlocal 并建堆
|
||||
# (不同层捕获不同变量的场景,见 _prepare_chain_nonlocals)
|
||||
if stmt2.kind() == ast.ASTKind.FunctionDef:
|
||||
self._prepare_chain_nonlocals(
|
||||
trans, stmt2, pool, func_builder, mod,
|
||||
llvmlite.Int8(pool), llvmlite.Ptr(pool, llvmlite.Int8(pool)))
|
||||
HandlesBody.translate_stmt(trans, stmt2)
|
||||
|
||||
# 如果函数体最后一条语句不是 Return,添加隐式 ret
|
||||
@@ -999,12 +1008,24 @@ class FuncGenMixin:
|
||||
old_global_count: int = trans._global_name_count
|
||||
old_nonlocal_count: int = trans._nonlocal_name_count
|
||||
old_env_count: int = trans._closure_env_count
|
||||
# nonlocal 集合独立缓冲:嵌套函数注册 nonlocal 写入共享缓冲 [0] 会
|
||||
# 覆盖父函数集合(ClosureTest2 Test 7 不同变量场景:level2 的 b 覆盖
|
||||
# level1 的 a → level1 env[0] 存错)。嵌套函数用独立缓冲,翻译完恢复。
|
||||
saved_nl_names: t.CVoid | t.CPtr = trans._nonlocal_names
|
||||
saved_nl_types: t.CVoid | t.CPtr = trans._nonlocal_types
|
||||
|
||||
# 7. 切换到嵌套函数作用域 + 清空 scope names
|
||||
trans._cur_func = func
|
||||
trans._cur_builder = func_builder
|
||||
trans._cur_func_name = func_name
|
||||
HT.clear_scope_names(trans)
|
||||
# 嵌套函数独立 nonlocal 缓冲(避免覆盖父函数集合)
|
||||
trans._nonlocal_names = stdlib.malloc(HT.MAX_GLOBAL_NAMES * 8)
|
||||
trans._nonlocal_types = stdlib.malloc(HT.MAX_GLOBAL_NAMES * 8)
|
||||
if trans._nonlocal_names is not None:
|
||||
string.memset(trans._nonlocal_names, 0, HT.MAX_GLOBAL_NAMES * 8)
|
||||
if trans._nonlocal_types is not None:
|
||||
string.memset(trans._nonlocal_types, 0, HT.MAX_GLOBAL_NAMES * 8)
|
||||
|
||||
# 8. 预扫描函数体:
|
||||
# a. 先预扫描 Global/Nonlocal 声明(Python 语义:对整个函数作用域生效)
|
||||
@@ -1058,6 +1079,8 @@ class FuncGenMixin:
|
||||
trans._global_name_count = old_global_count
|
||||
trans._nonlocal_name_count = old_nonlocal_count
|
||||
trans._closure_env_count = old_env_count
|
||||
trans._nonlocal_names = saved_nl_names
|
||||
trans._nonlocal_types = saved_nl_types
|
||||
return 0
|
||||
|
||||
# 13a. 创建 env: malloc(8 * nested_nonlocal_count) — env 存储指针数组
|
||||
@@ -1091,38 +1114,70 @@ class FuncGenMixin:
|
||||
if nl_name is None:
|
||||
continue
|
||||
heap_store: llvmlite.Value | t.CPtr = None
|
||||
local_alloca: llvmlite.Value | t.CPtr = HandlesVar.lookup_current(
|
||||
trans.SymTab, nl_name)
|
||||
if local_alloca is not None and local_alloca.Ty is not None:
|
||||
# 父函数局部变量:新建堆存储 + 拷贝当前值 + 重定向
|
||||
nl_load_ty: llvmlite.LLVMType | t.CPtr = local_alloca.Ty.Pointee
|
||||
if nl_load_ty is not None:
|
||||
cur_val_nl: llvmlite.Value | t.CPtr = llvmlite.build_load(
|
||||
parent_builder, nl_load_ty, local_alloca)
|
||||
eight_c: llvmlite.Value | t.CPtr = llvmlite.const_int64(pool, 8)
|
||||
if eight_c is not None:
|
||||
eight_c.Next = None
|
||||
var_storage: llvmlite.Value | t.CPtr = llvmlite.build_call(
|
||||
parent_builder, "malloc", eight_c, 1, i8_ptr_ty, 0)
|
||||
if var_storage is not None:
|
||||
# 拷贝当前值到堆存储
|
||||
st_slot_nl: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(
|
||||
parent_builder, var_storage, llvmlite.Ptr(pool, nl_load_ty))
|
||||
if st_slot_nl is not None and cur_val_nl is not None:
|
||||
llvmlite.build_store(parent_builder, cur_val_nl, st_slot_nl)
|
||||
# 父函数 VarEntry.Alloca 重定向到堆(父函数读写走共享堆)
|
||||
ve_nl: HandlesVar.VarEntry | t.CPtr = HandlesVar.lookup_var_entry(
|
||||
trans.SymTab, nl_name)
|
||||
if ve_nl is not None:
|
||||
heap_typed: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(
|
||||
# 1. 优先查全局堆地址映射(_prepare_chain_nonlocals 已为
|
||||
# 链内 nonlocal 建堆并 store 到 @nlheap.*;不同层捕获
|
||||
# 不同变量时(ClosureTest2 Test 7)level2 的 b 经此共享)
|
||||
gname_ck: t.CChar | t.CPtr = self._nl_heap_global_name(
|
||||
pool, trans, nl_name)
|
||||
if gname_ck is not None \
|
||||
and self._nl_heap_global_exists(mod, gname_ck) != 0:
|
||||
gv_ref0: llvmlite.Value | t.CPtr = self._nl_heap_global_ref(
|
||||
pool, trans, nl_name)
|
||||
if gv_ref0 is not None:
|
||||
heap_store = llvmlite.build_load(
|
||||
parent_builder, i8_ptr_ty, gv_ref0)
|
||||
if heap_store is None:
|
||||
local_alloca: llvmlite.Value | t.CPtr = HandlesVar.lookup_current(
|
||||
trans.SymTab, nl_name)
|
||||
if local_alloca is not None and local_alloca.Ty is not None:
|
||||
# 父函数局部变量:新建堆存储 + 拷贝当前值 + 重定向
|
||||
nl_load_ty: llvmlite.LLVMType | t.CPtr = local_alloca.Ty.Pointee
|
||||
if nl_load_ty is not None:
|
||||
cur_val_nl: llvmlite.Value | t.CPtr = llvmlite.build_load(
|
||||
parent_builder, nl_load_ty, local_alloca)
|
||||
# 堆存储大小按类型字节数:数组(如 [4 x i32]=16 字节)
|
||||
# 不能固定 8 字节(越界写崩溃);指针/标量 ≥ 8 字节
|
||||
st_size: t.CInt = self._type_byte_size(pool, nl_load_ty)
|
||||
st_size_val: llvmlite.Value | t.CPtr = llvmlite.const_int64(
|
||||
pool, st_size)
|
||||
if st_size_val is not None:
|
||||
st_size_val.Next = None
|
||||
var_storage: llvmlite.Value | t.CPtr = llvmlite.build_call(
|
||||
parent_builder, "malloc", st_size_val, 1, i8_ptr_ty, 0)
|
||||
if var_storage is not None:
|
||||
# 拷贝当前值到堆存储
|
||||
st_slot_nl: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(
|
||||
parent_builder, var_storage, llvmlite.Ptr(pool, nl_load_ty))
|
||||
if heap_typed is not None:
|
||||
ve_nl.Alloca = heap_typed
|
||||
heap_store = var_storage
|
||||
else:
|
||||
# 父函数中该变量是 nonlocal(多层嵌套):从父函数 env
|
||||
# 读堆地址(运行时链式共享同一堆),不新建
|
||||
heap_store = HandlesNonlocal._load_raw_var_ptr(trans, nl_name)
|
||||
if st_slot_nl is not None and cur_val_nl is not None:
|
||||
llvmlite.build_store(parent_builder, cur_val_nl, st_slot_nl)
|
||||
# 父函数 VarEntry.Alloca 重定向到堆(父函数读写走共享堆)
|
||||
ve_nl: HandlesVar.VarEntry | t.CPtr = HandlesVar.lookup_var_entry(
|
||||
trans.SymTab, nl_name)
|
||||
if ve_nl is not None:
|
||||
heap_typed: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(
|
||||
parent_builder, var_storage, llvmlite.Ptr(pool, nl_load_ty))
|
||||
if heap_typed is not None:
|
||||
ve_nl.Alloca = heap_typed
|
||||
# 记录到全局映射(后续层/父函数可见)
|
||||
if gname_ck is not None \
|
||||
and self._nl_heap_global_exists(mod, gname_ck) == 0:
|
||||
gv_new: llvmlite.GlobalVariable | t.CPtr = \
|
||||
llvmlite.new_global_variable(pool, gname_ck, i8_ptr_ty)
|
||||
if gv_new is not None:
|
||||
# 清除 external linkage + 零初始化(防 undefined reference)
|
||||
gv_new.Linkage = None
|
||||
gv_new.Initializer = "null"
|
||||
llvmlite.module_add_global(mod, gv_new)
|
||||
gv_ref_new: llvmlite.Value | t.CPtr = \
|
||||
self._nl_heap_global_ref(pool, trans, nl_name)
|
||||
if gv_ref_new is not None:
|
||||
llvmlite.build_store(
|
||||
parent_builder, var_storage, gv_ref_new)
|
||||
heap_store = var_storage
|
||||
else:
|
||||
# 父函数中该变量是 nonlocal(多层嵌套):从父函数 env
|
||||
# 读堆地址(运行时链式共享同一堆),不新建
|
||||
heap_store = HandlesNonlocal._load_raw_var_ptr(trans, nl_name)
|
||||
if heap_store is None:
|
||||
continue
|
||||
# env[ni] = 堆存储地址(共享)
|
||||
@@ -1147,6 +1202,8 @@ class FuncGenMixin:
|
||||
trans._global_name_count = old_global_count
|
||||
trans._nonlocal_name_count = old_nonlocal_count
|
||||
trans._closure_env_count = old_env_count
|
||||
trans._nonlocal_names = saved_nl_names
|
||||
trans._nonlocal_types = saved_nl_types
|
||||
return 0
|
||||
|
||||
# 13c. 存储 fn_ptr 到 offset 0
|
||||
@@ -1197,14 +1254,252 @@ class FuncGenMixin:
|
||||
# 两者 alloca 类型同为 i8**,_is_func_ptr_alloca 无法区分)
|
||||
HandlesExprCall.register_closure_name(func_name)
|
||||
|
||||
# 14. 恢复 scope names
|
||||
# 14. 恢复 scope names(含 nonlocal 集合指针)
|
||||
trans._cur_func_name = old_func_name
|
||||
trans._global_name_count = old_global_count
|
||||
trans._nonlocal_name_count = old_nonlocal_count
|
||||
trans._closure_env_count = old_env_count
|
||||
trans._nonlocal_names = saved_nl_names
|
||||
trans._nonlocal_types = saved_nl_types
|
||||
|
||||
return 1
|
||||
|
||||
# ============================================================
|
||||
# _type_byte_size - 计算 LLVM 类型占用的字节大小(递归)
|
||||
#
|
||||
# 用于 nonlocal 共享堆存储分配:数组(如 [4 x i32]=16 字节)不能
|
||||
# 用固定 8 字节(越界写破坏 canary/邻块 → 运行时崩溃,
|
||||
# ClosureTest2 Test 4 非local 数组崩溃根因)。未知类型保守返回 8。
|
||||
# ============================================================
|
||||
def _type_byte_size(self, pool: memhub.MemBuddy | t.CPtr,
|
||||
ty: llvmlite.LLVMType | t.CPtr) -> t.CInt:
|
||||
"""计算 LLVM 类型占用的字节大小(递归),未知类型返回 8"""
|
||||
if ty is None:
|
||||
return 8
|
||||
match ty:
|
||||
case llvmlite.LLVMType.Int(bits):
|
||||
sz_i: t.CInt = (bits + 7) // 8
|
||||
if sz_i < 8:
|
||||
sz_i = 8
|
||||
return sz_i
|
||||
case llvmlite.LLVMType.Ptr(_pe):
|
||||
return 8
|
||||
case llvmlite.LLVMType.Float(fb):
|
||||
if fb == 64:
|
||||
return 8
|
||||
if fb >= 128:
|
||||
return 16
|
||||
return 4
|
||||
case llvmlite.LLVMType.Array(et, cnt):
|
||||
esz: t.CInt = self._type_byte_size(pool, et)
|
||||
return esz * cnt
|
||||
case _:
|
||||
return 8
|
||||
|
||||
# ============================================================
|
||||
# _nl_heap_global_name - nonlocal 堆地址全局映射变量名
|
||||
#
|
||||
# 变量名: nlheap_{sha1}.{name}(全局变量,跨函数可见;SSA 编号是函数
|
||||
# 局部的,跨函数传递堆地址只能经全局变量/模块级映射)。
|
||||
# ============================================================
|
||||
def _nl_heap_global_name(self, pool: memhub.MemBuddy | t.CPtr,
|
||||
trans: HT.Translator | t.CPtr,
|
||||
name: str) -> t.CChar | t.CPtr:
|
||||
"""构造 nonlocal 堆地址全局映射变量名 nlheap_{sha1}.{name}"""
|
||||
if pool is None or trans is None or name is None:
|
||||
return None
|
||||
mangled: str = self._mangle_name(trans, name)
|
||||
buf: t.CChar | t.CPtr = pool.alloc(160)
|
||||
if buf is not None:
|
||||
viperlib.snprintf(buf, 160, "nlheap_%s", mangled)
|
||||
return buf
|
||||
|
||||
# ============================================================
|
||||
# _nl_heap_global_ref - 全局映射变量引用 Value(i8**)
|
||||
# ============================================================
|
||||
def _nl_heap_global_ref(self, pool: memhub.MemBuddy | t.CPtr,
|
||||
trans: HT.Translator | t.CPtr,
|
||||
name: str) -> llvmlite.Value | t.CPtr:
|
||||
"""获取 nonlocal 堆地址全局映射变量的引用(@"..." 引号包裹)"""
|
||||
if pool is None or trans is None or name is None:
|
||||
return None
|
||||
gname: t.CChar | t.CPtr = self._nl_heap_global_name(pool, trans, name)
|
||||
if gname is None:
|
||||
return None
|
||||
i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
|
||||
i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty)
|
||||
ref_buf: t.CChar | t.CPtr = pool.alloc(176)
|
||||
if ref_buf is None:
|
||||
return None
|
||||
viperlib.snprintf(ref_buf, 176, "@\"%s\"", gname)
|
||||
return llvmlite.SSAValue(pool, llvmlite.Ptr(pool, i8_ptr_ty), ref_buf)
|
||||
|
||||
# ============================================================
|
||||
# _nl_heap_global_exists - 检查 nonlocal 堆地址全局映射变量是否存在
|
||||
# ============================================================
|
||||
def _nl_heap_global_exists(self, mod: llvmlite.LLVMModule | t.CPtr,
|
||||
gname: str) -> int:
|
||||
"""检查全局映射变量 gname 是否已在模块中,1=存在"""
|
||||
if mod is None or gname is None:
|
||||
return 0
|
||||
gcur: llvmlite.GlobalVariable | t.CPtr = mod.GlobalHead
|
||||
while gcur is not None:
|
||||
if gcur.Name is not None and string.strcmp(gcur.Name, gname) == 0:
|
||||
return 1
|
||||
gcur = gcur.Next
|
||||
return 0
|
||||
|
||||
# ============================================================
|
||||
# _collect_chain_nonlocals - 递归收集嵌套函数链内 nonlocal 声明
|
||||
#
|
||||
# 遍历 node(FunctionDef)的 children:遇嵌套 FunctionDef 递归,
|
||||
# 遇 Nonlocal 语句收集变量名(去重)。返回更新后的计数。
|
||||
# ============================================================
|
||||
def _collect_chain_nonlocals(self, trans: HT.Translator | t.CPtr,
|
||||
node: ast.AST | t.CPtr,
|
||||
names: t.CVoid | t.CPtr,
|
||||
count: int) -> int:
|
||||
"""递归收集 node(FunctionDef)链内所有 nonlocal 声明名(去重)"""
|
||||
if node is None or names is None:
|
||||
return count
|
||||
fd: ast.FunctionDef | t.CPtr = (ast.FunctionDef | t.CPtr)(node)
|
||||
if fd is None or fd.children is None:
|
||||
return count
|
||||
ch: list[ast.AST | t.CPtr] | t.CPtr = fd.children
|
||||
cn: t.CSizeT = ch.__len__()
|
||||
for ci in range(cn):
|
||||
st: ast.AST | t.CPtr = ch.get(ci)
|
||||
if st is None:
|
||||
continue
|
||||
k: int = st.kind()
|
||||
if k == ast.ASTKind.FunctionDef:
|
||||
count = self._collect_chain_nonlocals(trans, st, names, count)
|
||||
elif k == ast.ASTKind.Nonlocal:
|
||||
nl_node: ast.Nonlocal | t.CPtr = (ast.Nonlocal | t.CPtr)(st)
|
||||
if nl_node is not None and nl_node.names is not None:
|
||||
nl_list: list[ast.AST | t.CPtr] | t.CPtr = nl_node.names
|
||||
nn: t.CSizeT = nl_list.__len__()
|
||||
for ni in range(nn):
|
||||
nm_node: ast.AST | t.CPtr = nl_list.get(ni)
|
||||
if nm_node is None:
|
||||
continue
|
||||
nm_x: ast.Name | t.CPtr = (ast.Name | t.CPtr)(nm_node)
|
||||
if nm_x is None or nm_x.id is None:
|
||||
continue
|
||||
dup: int = 0
|
||||
for di in range(count):
|
||||
if string.strcmp((t.CChar | t.CPtr)(
|
||||
t.CUInt64T(names) + di * 8), nm_x.id) == 0:
|
||||
dup = 1
|
||||
break
|
||||
if dup == 0 and count < 64:
|
||||
nm_slot: str | t.CPtr = (t.CVoid | t.CPtr)(
|
||||
t.CUInt64T(names) + count * 8)
|
||||
nm_slot[0] = nm_x.id
|
||||
count += 1
|
||||
return count
|
||||
|
||||
# ============================================================
|
||||
# _prepare_chain_nonlocals - 预扫描嵌套函数链内 nonlocal 并建堆
|
||||
#
|
||||
# 在翻译嵌套函数定义(translate_nested_function_def)之前调用。
|
||||
# 场景(ClosureTest2 Test 7):level1→a、level2→b、level3→c_val 各自
|
||||
# nonlocal 不同变量,都定义在最外层函数——level1 env 无 b 槽,level2
|
||||
# 无法经 env 链获取 b 的堆。此处在最外层函数中为链内所有 nonlocal
|
||||
# 建堆 + 重定向 VarEntry + 把堆地址 store 到模块级映射
|
||||
# @nlheap.{sha1}.{name}(全局变量跨函数可见),内层闭包创建时从映射 load。
|
||||
# ============================================================
|
||||
def _prepare_chain_nonlocals(self, trans: HT.Translator | t.CPtr,
|
||||
node: ast.AST | t.CPtr,
|
||||
pool: memhub.MemBuddy | t.CPtr,
|
||||
builder: llvmlite.IRBuilder | t.CPtr,
|
||||
mod: llvmlite.LLVMModule | t.CPtr,
|
||||
i8_ty: llvmlite.LLVMType | t.CPtr,
|
||||
i8_ptr_ty: llvmlite.LLVMType | t.CPtr) -> int:
|
||||
"""预收集嵌套函数链内 nonlocal 并建堆,1 成功"""
|
||||
if node is None or trans is None or builder is None or mod is None:
|
||||
return 0
|
||||
# 每函数只预建一次:多个嵌套函数定义共享同一堆(防重复 malloc +
|
||||
# 二次重定向 VarEntry.Alloca 导致兄弟闭包各持一堆,计数不同步)
|
||||
if trans._nl_chain_done != 0:
|
||||
return 1
|
||||
if trans._nl_chain_names is None:
|
||||
trans._nl_chain_names = pool.alloc(8 * 64)
|
||||
if trans._nl_chain_names is not None:
|
||||
string.memset(trans._nl_chain_names, 0, 8 * 64)
|
||||
if trans._nl_chain_names is None:
|
||||
return 0
|
||||
trans._nl_chain_count = self._collect_chain_nonlocals(
|
||||
trans, node, trans._nl_chain_names, 0)
|
||||
trans._nl_chain_done = 1
|
||||
for ci in range(trans._nl_chain_count):
|
||||
nm_slot: str | t.CPtr = (t.CVoid | t.CPtr)(
|
||||
t.CUInt64T(trans._nl_chain_names) + ci * 8)
|
||||
nl_name: str = nm_slot[0]
|
||||
if nl_name is None:
|
||||
continue
|
||||
# 只在"定义它的函数"(当前顶层作用域有 alloca)中建堆
|
||||
local_alloca: llvmlite.Value | t.CPtr = HandlesVar.lookup_current(
|
||||
trans.SymTab, nl_name)
|
||||
if local_alloca is None or local_alloca.Ty is None:
|
||||
continue
|
||||
nl_ty: llvmlite.LLVMType | t.CPtr = local_alloca.Ty.Pointee
|
||||
if nl_ty is None:
|
||||
continue
|
||||
gname: t.CChar | t.CPtr = self._nl_heap_global_name(pool, trans, nl_name)
|
||||
if gname is None:
|
||||
continue
|
||||
# 建堆(按类型字节大小)并拷贝当前值——每次本函数执行都新建共享堆,
|
||||
# 并 store 到模块级映射(跨函数可见)。注意:不能因映射变量已存在
|
||||
# 就跳过(前一个测试函数已把 @nlheap.* 加入模块——若跳过,本函数
|
||||
# 运行时 @nlheap.* 保持上一函数的堆地址,env 读到错堆。
|
||||
# ClosureTest2 Test 7 根因)。映射变量仅首次 add(模块去重),
|
||||
# store 指令每次照发。
|
||||
st_size: t.CInt = self._type_byte_size(pool, nl_ty)
|
||||
st_size_val: llvmlite.Value | t.CPtr = llvmlite.const_int64(pool, st_size)
|
||||
if st_size_val is not None:
|
||||
st_size_val.Next = None
|
||||
var_storage: llvmlite.Value | t.CPtr = llvmlite.build_call(
|
||||
builder, "malloc", st_size_val, 1, i8_ptr_ty, 0)
|
||||
if var_storage is None:
|
||||
continue
|
||||
cur_val: llvmlite.Value | t.CPtr = llvmlite.build_load(
|
||||
builder, nl_ty, local_alloca)
|
||||
st_slot: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(
|
||||
builder, var_storage, llvmlite.Ptr(pool, nl_ty))
|
||||
if st_slot is not None and cur_val is not None:
|
||||
llvmlite.build_store(builder, cur_val, st_slot)
|
||||
# 重定向 VarEntry(父函数读写走共享堆)
|
||||
ve: HandlesVar.VarEntry | t.CPtr = HandlesVar.lookup_var_entry(
|
||||
trans.SymTab, nl_name)
|
||||
if ve is not None:
|
||||
heap_typed: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(
|
||||
builder, var_storage, llvmlite.Ptr(pool, nl_ty))
|
||||
if heap_typed is not None:
|
||||
ve.Alloca = heap_typed
|
||||
# 创建/复用全局映射变量并 store 堆地址(跨函数可见)
|
||||
gv_found: llvmlite.GlobalVariable | t.CPtr = None
|
||||
gcur: llvmlite.GlobalVariable | t.CPtr = mod.GlobalHead
|
||||
while gcur is not None:
|
||||
if gcur.Name is not None and string.strcmp(gcur.Name, gname) == 0:
|
||||
gv_found = gcur
|
||||
break
|
||||
gcur = gcur.Next
|
||||
if gv_found is None:
|
||||
gv_found = llvmlite.new_global_variable(pool, gname, i8_ptr_ty)
|
||||
if gv_found is not None:
|
||||
# 清除 external linkage 并给零初始化,否则打印为 external
|
||||
# 声明、store 引用无定义体 → 链接 undefined reference
|
||||
gv_found.Linkage = None
|
||||
gv_found.Initializer = "null"
|
||||
llvmlite.module_add_global(mod, gv_found)
|
||||
if gv_found is not None:
|
||||
gv_ref: llvmlite.Value | t.CPtr = self._nl_heap_global_ref(
|
||||
pool, trans, nl_name)
|
||||
if gv_ref is not None:
|
||||
llvmlite.build_store(builder, var_storage, gv_ref)
|
||||
return 1
|
||||
|
||||
# ============================================================
|
||||
# translate_lambda - 翻译 lambda 表达式
|
||||
#
|
||||
|
||||
@@ -126,6 +126,17 @@ class Translator:
|
||||
# 独立于 _closure_env_count:后者随函数进入被 clear_scope_names 重置,
|
||||
# lambda ID 需跨函数全局唯一(提升函数名避免冲突)
|
||||
_lambda_count: t.CInt
|
||||
# 函数链内 nonlocal 变量集合(预扫描收集,含嵌套函数声明的 nonlocal)。
|
||||
# 用于"不同层捕获不同变量"场景(如 level1→a, level2→b):b 定义在最外层
|
||||
# 但 level1 不捕获——level1 env 无 b 槽,level2 无法经 env 链获取 b 的堆。
|
||||
# 解决:在最外层函数中为链内所有 nonlocal 建堆,并把堆地址 store 到
|
||||
# 模块级映射变量 @nlheap.{sha1}.{name};内层闭包创建时从映射 load。
|
||||
_nl_chain_names: t.CVoid | t.CPtr
|
||||
_nl_chain_count: t.CInt
|
||||
# 链内 nonlocal 堆预建标志(每函数只建一次):translate_function_def 进入
|
||||
# 新函数时重置为 0;_prepare_chain_nonlocals 首次调用置 1。多个嵌套函数
|
||||
# 定义共享同一堆(防重复 malloc + 二次重定向 VarEntry.Alloca)。
|
||||
_nl_chain_done: t.CInt
|
||||
|
||||
# 泛型特化上下文:当前正在特化的类型参数名/实参名列表
|
||||
# 由 _specialize_generic_class 设置,方法体翻译时用于将 T 替换为具体类型
|
||||
@@ -182,6 +193,9 @@ class Translator:
|
||||
self._closure_env_offsets = 0
|
||||
self._closure_env_count = 0
|
||||
self._lambda_count = 0
|
||||
self._nl_chain_names = None
|
||||
self._nl_chain_count = 0
|
||||
self._nl_chain_done = 0
|
||||
self.GenericTypeParamNames = None
|
||||
self.GenericTypeArgs = None
|
||||
self._declare_only = 0
|
||||
|
||||
Reference in new issue
Block a user