From 3b4bd1c6dcfd8cb501093ce750427549e0492d10 Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Wed, 7 Oct 2026 15:59:54 +0800 Subject: [PATCH] =?UTF-8?q?fix(gen):=20=E8=B7=A8=E6=A8=A1=E5=9D=97?= =?UTF-8?q?=E5=87=BD=E6=95=B0=E4=BD=9C=E5=80=BC=E6=97=B6=E4=B8=8D=E5=86=8D?= =?UTF-8?q?=E4=BC=AA=E9=80=A0=E5=90=8C=E5=90=8D=E5=A4=96=E9=83=A8=E5=85=A8?= =?UTF-8?q?=E5=B1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `serial.puts` 这类模块属性指向的是函数(elf.vp 的 _resolve_symbol 返回它作 函数指针),属性兜底路径却为它造了一个 llvm.mlir.global external @.puts, 与已有的 llvm.func @.puts 同名,mlir-opt 报 redefinition of symbol named。 改为在伪造之前先精确匹配函数符号(目标模块 sha1 前缀过的函数名与 Function.Name 逐字符相等,避免误命中他模块同名函数),命中则返回函数符号引用本身 (@,类型 Ptr(Func),与同模块 &f 分支同一形态),并让属性读路径跳过 对该值的 load(函数地址即值,load 会去读函数体)。 EOF --- App/lib/core/Gen/Expr/ExprGenAttr.vp | 29 ++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/App/lib/core/Gen/Expr/ExprGenAttr.vp b/App/lib/core/Gen/Expr/ExprGenAttr.vp index 121aa5f..6350d57 100644 --- a/App/lib/core/Gen/Expr/ExprGenAttr.vp +++ b/App/lib/core/Gen/Expr/ExprGenAttr.vp @@ -188,6 +188,26 @@ novirtual class ExprGenAttr(ExprGenBase.ExprGenBase): # build the reference from gv.Name (full name, matching the definition when SHA1-prefixed), # avoiding an undefined symbol for a bare @_mbuddy under per-module prefixed definitions return self.make_global_ref(pool, gv.Name, gv.Ty) + # A module function used as a value (`return serial.puts`, `cb = serial.puts`): the symbol + # is already declared in this module as `llvm.func @.puts`, so manufacturing an + # external global under the same name makes mlir-opt reject the module with "redefinition + # of symbol named '.puts'". Hand back the function symbol itself instead: a Value + # whose text is the reference `@` typed Ptr(Func), the same shape the same-module + # `&f` branch (ExprGenOps.translate_unaryop) produces -- the caller must not load it. + if gv_q is not None: + fn_ma: mlirlite.Function* = self.Gen.CallGenBase.find_func_in_module(mod, at.attr) + if fn_ma is not None: + fname_ma: str = mlirlite.function_get_name(fn_ma) + # exact match only: a bare suffix hit could be another module's same-name function + if fname_ma is not None and string.strcmp(fname_ma, gv_q) == 0: + fn_ty_ma: mlirlite.LLVMType* = self.Gen.CallGenBase._FuncTyFromFunction(pool, fn_ma) + if fn_ty_ma is not None: + fnlen_ma: sizet = string.strlen(fname_ma) + fbuf_ma: str = pool.alloc(fnlen_ma + 4) + if fbuf_ma is not None: + if self.Gen.ClassGenVTable._vtable_name_needs_quote(fname_ma) != 0: viperlib.snprintf(fbuf_ma, fnlen_ma + 4, "@\"%s\"", fname_ma) + else: viperlib.snprintf(fbuf_ma, fnlen_ma + 4, "@%s", fname_ma) + return mlirlite.SSAValue(pool, mlirlite.Ptr(pool, fn_ty_ma), fbuf_ma) # not found: create an external global declaration and add it to the module; otherwise only # an @_argc reference is emitted with no declaration (LLVM: "use of undefined value"). use_ty: mlirlite.LLVMType* = ty_hint @@ -469,10 +489,15 @@ novirtual class ExprGenAttr(ExprGenBase.ExprGenBase): if mod_gv_ref is not None and mod_gv_ref.Ty is not None: load_ty_mod: mlirlite.LLVMType* = mod_gv_ref.Ty.Pointee if load_ty_mod is not None: - # module-level global array (e.g. zdef.zdeflate_len_base: CArray[T, N]): - # decay to the base address per C semantics; loading [N x T] cannot be GEP base. + # A module function used as a value is already the address (Ptr(Func), + # the symbol reference itself): loading it would read the function body, + # so return the reference unchanged. match load_ty_mod: + case mlirlite.LLVMType.Func(_, _, _): + return mod_gv_ref case mlirlite.LLVMType.Array(_, _): + # module-level global array (e.g. zdef.zdeflate_len_base: CArray[T, N]): + # decay to the base address per C semantics; loading [N x T] cannot be GEP base. return mod_gv_ref case _: pass