feat(gen): volatile 构造写入落 LLVM volatile(逐字段 store + 构造三方法屏障)

值位置 volatile 前缀(a: int = volatile f() / a: S = volatile S(...))此前只在调用点无落点。LLVM 的 call 指令没有 volatile 限定符,故语义只能落在内存存取上:

- AssignGenMixin:赋值目标的结果槽按 volatile 存取(本次 store 立即带 volatile,并登记 VarEntry.IsVolatile 使后续 load/store 亦带);对已存在的 global/nonlocal/mod_glob 条目在分支前统一登记。

- CallGenCtor:_translate_struct_ctor / _translate_struct_ctor_kw / _translate_renum_construct / _apply_struct_defaults 增加 is_volatile 参数,清零、tag、默认值、逐字段 store 全部带 volatile;_translate_generic_ctor_call 直接从 cl.IsVolatile 取。

- CallGenCall._call_method_on_ptr 增加 is_volatile 参数:__new__/__before_init__/__init__ 的写入发生在被调函数内部,调用点无法逐条附加 volatile,故在调用后对该 self 指针追加一次 i8 volatile load 作为内存屏障,使构造写入不被优化器消除或跨越重排。

- CallGenMixin / CallGenDispatch:结构体、关键字、泛型、REnum 构造调用点统一透传 cl.IsVolatile。
This commit is contained in:
TermiNexus committed 2026-09-27 12:01:22 +08:00
1 parent 7d28c9649e
commit cf815b17de
5 files changed
+104 -39

No files matched your search

+21
View File
@@ -2167,6 +2167,18 @@ novirtual class AssignGenMixin:
if self.Gen.CallGenLeafKit.register_func_ptr_sig(pool, nm.id, callable_ret, aa) != 0:
return 0
# 值位置 volatile 调用前缀:`a: int = volatile f()` / `a: S = volatile S(...)`
# ⇒ 结果槽按 volatile 存取(下述三条"条目已先存在"的分支——global /
# nonlocal / mod_glob——在此统一登记,使各分支读取 var_is_volatile 时已置位)。
# 函数内**新声明**的局部变量此刻条目尚未建立,由主分支(alloca 就绪后)登记。
# LLVM 的 call 无 volatile 限定符,故语义落在内存存取上:本次 store 带
# volatile,且此后该变量的 load/store 亦带,避免这次调用/构造的往返访问
# 被优化器消除或提升。
if aa.value is not None and aa.value.kind() == ast.ASTKind.Call \
and (ast.Call*(aa.value)).IsVolatile != 0 and nm.id is not None:
if self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm.id) is not None:
self.Gen.VarGen.set_var_volatile(trans.SymTab, nm.id)
# global 变量:写入模块作用域中的全局变量
if GenApi.is_global_name(trans, nm.id) != 0:
mod_alloca: llvmlite.Value* = self.Gen.VarGen.lookup_module_var(trans.SymTab, nm.id)
@@ -2565,6 +2577,15 @@ novirtual class AssignGenMixin:
# PointeeVolatile;本 store 写的是指针变量自身,不影响。
if self.Gen.TypeGenErr.has_pointee_volatile_marker(aa.annotation) != 0:
self.Gen.VarGen.set_var_pointee_volatile(trans.SymTab, nm.id)
# 值位置 volatile 调用前缀:`a: int = volatile f()` /
# `a: S = volatile S(...)`——LLVM 的 call 指令没有 volatile
# 限定符,故语义落在内存存取上:结果槽按 volatile 存取
# (本次 store 立即带 volatile,且此后该变量的 load/store 也带),
# 使"这次调用/构造的往返内存访问"不会被优化器消除或提升。
if aa.value is not None and aa.value.kind() == ast.ASTKind.Call \
and (ast.Call*(aa.value)).IsVolatile != 0:
self.Gen.VarGen.set_var_volatile(trans.SymTab, nm.id)
vol_laa = 1
llvmlite.build_store(builder, rhs_val, alloca, vol_laa)
# FakeDuck: 初始化影子结构体 __data__ 字段 = 初始字符串指针
# (对齐 TPC HandlesAnnAssign.py: 仅当有初值时写入 __data__)