fix(ffilib): 模块级 ffilib.CDLL(...) 赋值不再生成全局变量
现场 main.vp 的 text.ll 里出现 @"<sha1>.add_c2"() : i32 全局,说明纯编译期配置的 CDLL 声明被当成普通模块级变量翻译了。ViperBuild 增加 IsCdlLDeclCall(复用 _IsCDLLCall,认别名与全名);TranslatorGenMixin 的模块级 Assign 分支先判它,命中则整条跳过 Pass 0 已抽取的声明。
This commit is contained in:
1 parent
522075097e
commit
c765cfa9d4
2 files changed
+46
-45
No files matched your search
@@ -1,4 +1,5 @@
|
||||
import lib.core.Gen.Generator as Generator
|
||||
import lib.core.builtin.ViperBuild as ViperBuild
|
||||
from stdint import *
|
||||
import ast
|
||||
import mlirlite
|
||||
@@ -20,20 +21,20 @@ import lib.core.Gen.GenApi as GenApi
|
||||
novirtual class TranslatorGenMixin:
|
||||
"""Translation driver Mixin."""
|
||||
|
||||
# _register_cexport_from_funcdef - Phase 1a: pre-register every CExport/State function
|
||||
# _register_cexport_from_funcdef - Phase 1a: pre-register every ExportT/State function
|
||||
# name in the global table so Phase 1b's alphabetical order cannot miss bare-name calls.
|
||||
Gen: Generator.Generator*
|
||||
def _register_cexport_from_funcdef(self, trans: HT.Translator*, node: ast.AST*) -> int:
|
||||
"""Pre-register CExport/State function names into the global table (no IR)."""
|
||||
"""Pre-register ExportT/State function names into the global table (no IR)."""
|
||||
fd: ast.FunctionDef* = ast.FunctionDef*(node)
|
||||
if fd is None or fd.name is None: return 0
|
||||
if trans is None or trans.ModuleSha1 is None: return 0
|
||||
|
||||
# Check whether the return type carries a CExport or State marker
|
||||
# Check whether the return type carries a ExportT or State marker
|
||||
has_export: int = 0
|
||||
has_state: int = 0
|
||||
if fd.returns is not None:
|
||||
has_export = self.Gen.TypeGenErr.has_decorator_marker(fd.returns, "CExport", 0)
|
||||
has_export = self.Gen.TypeGenErr.has_decorator_marker(fd.returns, "ExportT", 0)
|
||||
has_state = self.Gen.TypeGenErr.has_decorator_marker(fd.returns, "State", 0)
|
||||
|
||||
if has_export != 0 or has_state != 0: self.Gen.CallGenBase.register_cexport_func(trans.ModuleSha1, fd.name)
|
||||
@@ -64,7 +65,7 @@ novirtual class TranslatorGenMixin:
|
||||
# template is registered by Phase1Scan/Phase2) and monomorphize at call sites.
|
||||
fd_tc: ast.FunctionDef* = ast.FunctionDef*(child)
|
||||
if fd_tc is not None and fd_tc.type_params is not None: continue
|
||||
# Phase 1a declare mode registers only CExport/State names; Phase 1b fully translates bodies.
|
||||
# Phase 1a declare mode registers only ExportT/State names; Phase 1b fully translates bodies.
|
||||
if trans._declare_only == 0:
|
||||
# 函数体翻译 = 编译器下潜进一个源码上下文:推一帧(def 位置),
|
||||
# 让函数体内部的错误在 traceback 里回溯到"正在翻译哪个函数"(调用链)。
|
||||
@@ -91,14 +92,16 @@ novirtual class TranslatorGenMixin:
|
||||
added = self.Gen.StmtGen.translate_stmt(trans, child)
|
||||
added_total += added
|
||||
elif kd == ast.ASTKind.AnnAssign and trans._declare_only != 2:
|
||||
# Module-level AnnAssign: skip in import-scan mode, handle only CDefine in
|
||||
# Module-level AnnAssign: skip in import-scan mode, handle only DefineT in
|
||||
# struct-registration mode, and handle all in full-translation mode.
|
||||
added = self.handle_module_level_var(trans, child)
|
||||
added_total += added
|
||||
elif trans._declare_only == 0 and kd == ast.ASTKind.Assign:
|
||||
# Module-level Assign without a builder -> create a global (full-translation mode only)
|
||||
added = self.handle_module_level_var(trans, child)
|
||||
added_total += added
|
||||
# ffilib.CDLL(...) 是纯编译期配置(Pass 0 已抽取),不能生成模块级全局
|
||||
asn_ml: ast.Assign* = ast.Assign*(child)
|
||||
if asn_ml is None or ViperBuild.IsCdlLDeclCall(asn_ml.value) == 0:
|
||||
added = self.handle_module_level_var(trans, child)
|
||||
added_total += added
|
||||
|
||||
return added_total
|
||||
|
||||
@@ -127,13 +130,13 @@ novirtual class TranslatorGenMixin:
|
||||
if ag is not None and ag.annotation is not None: self.Gen.TypeGenResolve.resolve_annotation_type(pool, ag.annotation, imp, fi, trans, 0)
|
||||
|
||||
# _build_array_initializer_text - build an LLVM IR array initializer from an AST List,
|
||||
# formatted as [N x elem_ty] [elem_ty val0, ...], for module-level t.CArray literals.
|
||||
# formatted as [N x elem_ty] [elem_ty val0, ...], for module-level t.ArrayT literals.
|
||||
def _build_array_initializer_text(self, pool: memhub.MemBuddy*, var_ty: mlirlite.LLVMType*, list_node: ast.List*) -> str:
|
||||
"""Build LLVM IR array initializer text from an AST List node; None on failure."""
|
||||
if var_ty is None or list_node is None: return None
|
||||
|
||||
# Re-cast so the VPC compiler recognizes ast.List, avoiding type inference degrading
|
||||
# to t.CPtr when init_list_node's initial value is None.
|
||||
# to rawptr when init_list_node's initial value is None.
|
||||
ln: ast.List* = ast.List*(list_node)
|
||||
if ln is None: return None
|
||||
|
||||
@@ -274,7 +277,7 @@ novirtual class TranslatorGenMixin:
|
||||
return buf
|
||||
|
||||
# _build_string_array_initializer_text - initialize a char array global from a string literal:
|
||||
# t.CArray[char, N] = "..." -> [i8 b0, ..., i8 0, ...] zero-padded; empty -> zeroinitializer.
|
||||
# t.ArrayT[char, N] = "..." -> [i8 b0, ..., i8 0, ...] zero-padded; empty -> zeroinitializer.
|
||||
def _build_string_array_initializer_text(self, pool: memhub.MemBuddy*, var_ty: mlirlite.LLVMType*, str_val: str) -> str:
|
||||
"""Build initializer text for an array global from a string literal; None on failure."""
|
||||
if var_ty is None or str_val is None: return None
|
||||
@@ -461,11 +464,11 @@ novirtual class TranslatorGenMixin:
|
||||
return 0
|
||||
|
||||
# handle_module_level_var - module-level var declaration -> LLVM global (int/list/string init).
|
||||
# _RegisterStaticModuleVar - register (name, def module SHA1) of t.CStatic vars for cross-module access checks.
|
||||
# _RegisterStaticModuleVar - register (name, def module SHA1) of t.StaticT vars for cross-module access checks.
|
||||
def _RegisterStaticModuleVar(self, trans: HT.Translator*, annot_node: ast.AST*, var_name: str) -> int:
|
||||
if trans is None or annot_node is None or var_name is None: return 0
|
||||
if trans.ModuleSha1 is None: return 0
|
||||
if self.Gen.TypeGenErr.has_decorator_marker(annot_node, "CStatic", 0) == 0: return 0
|
||||
if self.Gen.TypeGenErr.has_decorator_marker(annot_node, "StaticT", 0) == 0: return 0
|
||||
self.Gen.CallGenBase.RegisterStaticSymbol(trans.ModuleSha1, var_name)
|
||||
return 0
|
||||
|
||||
@@ -475,8 +478,8 @@ novirtual class TranslatorGenMixin:
|
||||
pool: memhub.MemBuddy* = trans.Pool
|
||||
mod: mlirlite.LLVMModule* = trans.Module
|
||||
|
||||
# CDefine annotation: a compile-time constant, no global is created; register it in the
|
||||
# global CDefine table so t.CArray[elem_ty, NAME] can resolve it.
|
||||
# DefineT annotation: a compile-time constant, no global is created; register it in the
|
||||
# global DefineT table so t.ArrayT[elem_ty, NAME] can resolve it.
|
||||
k: int = node.kind()
|
||||
if k == ast.ASTKind.AnnAssign:
|
||||
aa_cd: ast.AnnAssign* = ast.AnnAssign*(node)
|
||||
@@ -485,18 +488,18 @@ novirtual class TranslatorGenMixin:
|
||||
nm_cd: ast.Name* = ast.Name*(aa_cd.target)
|
||||
if nm_cd.id is not None:
|
||||
if self.Gen.AssignGen.is_cdefine_annotation(aa_cd.annotation) != 0:
|
||||
# Auto-detect and register float/int CDefine values (e.g. vipermath U_M_PI)
|
||||
# Auto-detect and register float/int DefineT values (e.g. vipermath U_M_PI)
|
||||
self.Gen.AssignGen._register_cdefine_from_value(pool, nm_cd.id, aa_cd.value, aa_cd.annotation)
|
||||
return 0
|
||||
# CTypedef annotation: no global is created (would duplicate the symbol);
|
||||
# register it in the CTypedef table instead, or annotation lookups fail.
|
||||
# TypedefT annotation: no global is created (would duplicate the symbol);
|
||||
# register it in the TypedefT table instead, or annotation lookups fail.
|
||||
if self.Gen.TypeGenCType._is_ctypedef_annotation(aa_cd.annotation) != 0:
|
||||
ctd_str: str = self.Gen.TypeGenResolve._get_type_name_from_annotation(pool, aa_cd.value)
|
||||
if ctd_str is not None: self.Gen.TypeGenCType.register_ctypedef(pool, nm_cd.id, ctd_str)
|
||||
return 0
|
||||
|
||||
# In _declare_only=1 (struct-registration) mode only CDefine is handled; reaching here
|
||||
# means it is not a CDefine, so skip.
|
||||
# In _declare_only=1 (struct-registration) mode only DefineT is handled; reaching here
|
||||
# means it is not a DefineT, so skip.
|
||||
if trans._declare_only == 1: return 0
|
||||
|
||||
var_name: str = None
|
||||
@@ -512,7 +515,7 @@ novirtual class TranslatorGenMixin:
|
||||
# Explicit `= None` init keeps lazy-initialization pointer semantics: the global must stay
|
||||
# NULL, so no BSS storage is created (storage would break the `!= None` guard).
|
||||
is_none_init: int = 0
|
||||
annot_node: ast.AST* = None # Annotation node (used to detect t.CExport markers)
|
||||
annot_node: ast.AST* = None # Annotation node (used to detect t.ExportT markers)
|
||||
|
||||
if k == ast.ASTKind.AnnAssign:
|
||||
aa: ast.AnnAssign* = ast.AnnAssign*(node)
|
||||
@@ -524,7 +527,7 @@ novirtual class TranslatorGenMixin:
|
||||
# f-string escape: a module-level initial value is global, but the f-string result
|
||||
# points into the current function's stack buffer -> fail-fast (use escape_fstring_copy).
|
||||
GenApi.fail_if_fstring_escape(aa.value, "global variable initialization")
|
||||
# t.CStatic vars (no cross-module use) are registered as (name, def module SHA1);
|
||||
# t.StaticT vars (no cross-module use) are registered as (name, def module SHA1);
|
||||
# the logic stays inside _RegisterStaticModuleVar to avoid disturbing type inference.
|
||||
self._RegisterStaticModuleVar(trans, annot_node, var_name)
|
||||
# Module-level t.Callable function pointers register (name -> return type) here so
|
||||
@@ -541,18 +544,10 @@ novirtual class TranslatorGenMixin:
|
||||
self.Gen.TypeGenResolve.reject_ref_marker_position(aa.annotation, trans._imported_modules, "a module-level variable declaration")
|
||||
resolved: mlirlite.LLVMType* = self.Gen.TypeGenResolve.resolve_annotation_type(pool, aa.annotation, trans._imported_modules, trans._from_imports, trans, 0)
|
||||
if resolved is not None:
|
||||
# Object-class module vars (@t.Object/@t.CVTable: object-as-pointer) wrap the
|
||||
# resolved struct value in Ptr; pure t.CStruct keeps a value instance.
|
||||
# Object-class module vars (@t.Object/@t.VTableT: object-as-pointer) wrap the
|
||||
# resolved struct value in Ptr; pure t.StructBase keeps a value instance.
|
||||
if resolved.kind() == mlirlite.LLVMType.Struct:
|
||||
gv_entry: GenTypes.StructEntry* = self.Gen.StructGen.find_struct_by_type(resolved)
|
||||
# [TEMP-DIAG] 模块级 struct 全局的类型决策(定位完删除):
|
||||
# hasnew/hasvtable/isoop 任一为真 ⇒ 全局被包成 Ptr(指针槽),
|
||||
# 与"局部变量=值本体"不一致 ⇒ `CAM = V3(...)` 被当成把栈地址交给全局。
|
||||
gv_hn: int = -1; gv_hv: int = -1; gv_oo: int = -1
|
||||
if gv_entry is not None:
|
||||
gv_hn = gv_entry.HasNew; gv_hv = gv_entry.HasVTable; gv_oo = gv_entry.IsOOP
|
||||
stdio.printf("[GVAR-DIAG] name=%s hasnew=%d hasvtable=%d isoop=%d\n",
|
||||
var_name, gv_hn, gv_hv, gv_oo)
|
||||
if gv_entry is not None and gv_entry.HasNew != 0:
|
||||
# 只有定义了 `__new__`(自定义分配器,对象存储可能不在本帧)的类才**必须**
|
||||
# 以指针表示。仅"有方法 / 带 vtable"(IsOOP / HasVTable)的类,值本体内联
|
||||
@@ -609,7 +604,7 @@ novirtual class TranslatorGenMixin:
|
||||
else:
|
||||
# 负浮点字面量 `-100.0` = UnaryOp(USub, Constant(float)):按**浮点初始化**
|
||||
# 处理(init_kind=4,值稍后从源码重读,故负号会被保留);
|
||||
# 否则会被送进整型常量提取器 ⇒ TypeError: CDefine value extraction failed。
|
||||
# 否则会被送进整型常量提取器 ⇒ TypeError: DefineT value extraction failed。
|
||||
is_neg_f: int = 0
|
||||
if uop_g.op == ast.OpKind.USub and uop_g.operand is not None and uop_g.operand.kind() == ast.ASTKind.Constant:
|
||||
opf_d: ast.Constant* = ast.Constant*(uop_g.operand)
|
||||
@@ -705,14 +700,14 @@ novirtual class TranslatorGenMixin:
|
||||
# struct type in var_ty, or llc reports "use of undefined type named 'sha1.ClassName'".
|
||||
mlirlite.module_ensure_opaque_for_type(mod, pool, var_ty)
|
||||
# Module-level globals are SHA1-prefixed (like function mangling) to avoid duplicate
|
||||
# symbols; SymTab keys stay bare, and t.CExport globals keep bare names for asm references.
|
||||
# symbols; SymTab keys stay bare, and t.ExportT globals keep bare names for asm references.
|
||||
is_global_cexport: int = 0
|
||||
if annot_node is not None: is_global_cexport = self.Gen.TypeGenErr.has_decorator_marker(annot_node, "CExport", 0)
|
||||
# extern globals (annotation has t.State/t.CExtern) are declared only: `@bare = external
|
||||
if annot_node is not None: is_global_cexport = self.Gen.TypeGenErr.has_decorator_marker(annot_node, "ExportT", 0)
|
||||
# extern globals (annotation has t.State/t.ExternT) are declared only: `@bare = external
|
||||
# global T` with external linkage, no initializer and no storage; export instead defines them.
|
||||
is_global_extern: int = 0
|
||||
if annot_node is not None:
|
||||
is_global_extern = self.Gen.TypeGenErr.has_decorator_marker(annot_node, "CExtern", 0)
|
||||
is_global_extern = self.Gen.TypeGenErr.has_decorator_marker(annot_node, "ExternT", 0)
|
||||
if is_global_extern == 0: is_global_extern = self.Gen.TypeGenErr.has_decorator_marker(annot_node, "State", 0)
|
||||
# Global-side t.attr.* attributes (`T | t.attr.xxx` union): section->gv.Section,
|
||||
# aligned->gv.Align, weak->gv.Linkage; only present for AnnAssign (annot_node not None).
|
||||
@@ -776,7 +771,7 @@ novirtual class TranslatorGenMixin:
|
||||
# Initializer build failed: error out (fail-fast) instead of falling back
|
||||
self.Gen.TypeGenErr.fatal_error(node, f"failed to build the initializer of module-level variable '{var_name}'", "TypeError")
|
||||
elif init_kind == 3:
|
||||
# String literal initializer: for array types (t.CArray[char, N]) use a byte-array
|
||||
# String literal initializer: for array types (t.ArrayT[char, N]) use a byte-array
|
||||
# initializer (content + zero padding); a string-pointer GEP initializer would mismatch.
|
||||
is_arr_g: int = 0
|
||||
match var_ty:
|
||||
@@ -789,7 +784,7 @@ novirtual class TranslatorGenMixin:
|
||||
if arr_str_init is not None: gv.Initializer = arr_str_init
|
||||
else: self.Gen.TypeGenErr.fatal_error(node, f"failed to build the string initializer of module-level array variable '{var_name}'", "TypeError")
|
||||
else:
|
||||
# Pointer type (e.g. b64_tab: t.CArray[char, None] = "ABC..."): create an internal
|
||||
# Pointer type (e.g. b64_tab: t.ArrayT[char, None] = "ABC..."): create an internal
|
||||
# string constant global and reference it via a GEP initializer.
|
||||
str_init: str = self._build_string_ptr_initializer(pool, mod, var_name, init_str_val)
|
||||
if str_init is not None: gv.Initializer = str_init
|
||||
@@ -842,7 +837,7 @@ novirtual class TranslatorGenMixin:
|
||||
# Value-type storage needs a full struct definition, since cross-module classes are
|
||||
# opaque by default and a zeroinitializer global would report "use of undefined type".
|
||||
self.Gen.StructGen.ensure_struct_def_in_module(pool, mod, resolved_stg_ty)
|
||||
# @t.CVTable object classes initialize storage field 0 (vtable pointer) to the vtable
|
||||
# @t.VTableT object classes initialize storage field 0 (vtable pointer) to the vtable
|
||||
# address; otherwise the entry virtual call jumps to 0x0. @t.Object stays zeroinitializer.
|
||||
stg_init_text: str = "zeroinitializer"
|
||||
if resolved_stg_entry is not None and resolved_stg_entry.Name is not None and resolved_stg_entry.HasVTable != 0:
|
||||
@@ -939,14 +934,14 @@ novirtual class TranslatorGenMixin:
|
||||
|
||||
# Register in the module scope
|
||||
if self.Gen.VarGen.define_module_var(trans.SymTab, var_name, gv_ref) == 0:
|
||||
# t.CVolatile storage marker (union annotation): mark the global volatile so all
|
||||
# t.VolatileT storage marker (union annotation): mark the global volatile so all
|
||||
# loads/stores carry LLVM volatile and busy-wait loops are not optimized away.
|
||||
if annot_node is not None:
|
||||
# const object global: a non-pointer annotation with t.CConst registers IsConst,
|
||||
# const object global: a non-pointer annotation with t.ConstT registers IsConst,
|
||||
# making later assignment/aug-assign/element-assign to the name fail-fast.
|
||||
if self.Gen.TypeGenErr.is_const_object_marker(annot_node, var_ty) != 0: self.Gen.VarGen.set_var_const(trans.SymTab, var_name)
|
||||
if self.Gen.TypeGenErr.has_decorator_marker(annot_node, "CVolatile", 0) != 0: self.Gen.VarGen.set_var_volatile(trans.SymTab, var_name)
|
||||
# rawptr[t.CVolatile | T] (volatile pointee): loads/stores through p[i]/p.field/
|
||||
if self.Gen.TypeGenErr.has_decorator_marker(annot_node, "VolatileT", 0) != 0: self.Gen.VarGen.set_var_volatile(trans.SymTab, var_name)
|
||||
# rawptr[t.VolatileT | T] (volatile pointee): loads/stores through p[i]/p.field/
|
||||
# c.Deref(p) carry volatile; distinct from the variable's own volatile qualifier.
|
||||
if self.Gen.TypeGenErr.has_pointee_volatile_marker(annot_node) != 0: self.Gen.VarGen.set_var_pointee_volatile(trans.SymTab, var_name)
|
||||
# Set AnnotClassName so attribute access can fall back to the class name when a union
|
||||
|
||||
@@ -1065,6 +1065,12 @@ def _HandleCDLL(node: ast.AST*, handle: str) -> int:
|
||||
return rc_s
|
||||
|
||||
|
||||
# IsCdlLDeclCall - 供 Gen 层判断"这条模块级赋值是 ffilib.CDLL 声明"(纯编译期配置,不能生成全局)
|
||||
def IsCdlLDeclCall(node: ast.AST*) -> int:
|
||||
"""node 是 ffilib.CDLL(...) 调用时返回 1"""
|
||||
return _IsCDLLCall(node)
|
||||
|
||||
|
||||
# FfiHandlePath - 查询 extern <lib> name(...) 的库路径(runtime=True 的 thunk 用它 LoadLibraryA)
|
||||
def FfiHandlePath(name: str) -> str:
|
||||
"""库路径;未登记返回 None"""
|
||||
|
||||
Reference in new issue
Block a user