From 0acbefafd8ba4a403f1bcbe83556246d90424456 Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Sat, 24 Oct 2026 22:20:55 +0800 Subject: [PATCH] =?UTF-8?q?feat(ffilib):=20runtime=20CDLL=20=E7=94=9F?= =?UTF-8?q?=E6=88=90=20thunk=EF=BC=88=E7=BB=9D=E5=AF=B9=E8=B7=AF=E5=BE=84?= =?UTF-8?q?=20LoadLibraryA=20+=20=E9=97=B4=E6=8E=A5=E8=B0=83=E7=94=A8?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit _emit_runtime_ffi_thunk 生成 define (%p0..): Load(path) -> Sym(h,name) -> bitcast 成 ()* -> call_indirect -> ret;符号名与普通 extern 一致(裸名),调用点零改动。路径与符号名用 ExprGenConst.create_global_string 落成常量操作数(GEP 由该助手处理)。约束:仅标量参数/返回值,结构体按值与变参 fail-fast;declare-only 阶段仍走普通 declare,避免同模块 define/declare 同名并存。 --- App/lib/core/Gen/Func/FuncGenDef.vp | 151 +++++++++++++++++++++++++++- 1 file changed, 149 insertions(+), 2 deletions(-) diff --git a/App/lib/core/Gen/Func/FuncGenDef.vp b/App/lib/core/Gen/Func/FuncGenDef.vp index 2b2cf3d..f5c8687 100644 --- a/App/lib/core/Gen/Func/FuncGenDef.vp +++ b/App/lib/core/Gen/Func/FuncGenDef.vp @@ -235,6 +235,138 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase): viperlib.snprintf(out, sha1_len + base_len + 20, "%s.%s_v%s", sha1, base_name, h) return out + # _emit_runtime_ffi_thunk - runtime CDLL 的本地 thunk。 + # 生成 define (params...),函数体: + # %h = call i8* @{_ffilib}.Load(i8* "<绝对路径>") + # %p = call i8* @{_ffilib}.Sym(i8* %h, i8* "<符号名>") + # %f = bitcast i8* %p to ()* + # %r = call %f() ; ret + # 符号名与普通 extern 一致(裸名),所以调用点零改动;差异只在运行期按绝对路径加载。 + # v1 约束:只支持标量参数/返回值;结构体按值与变参直接 fail-fast。 + def _emit_runtime_ffi_thunk(self, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, trans: HT.Translator*, fd: ast.FunctionDef*, lib_path: str, ret_ty: mlirlite.LLVMType*) -> mlirlite.Function*: + """生成 runtime CDLL thunk;失败已报错并返回 None""" + if pool is None or mod is None or fd is None or fd.name is None or lib_path is None or ret_ty is None: return None + ffilib_sha1: str = self.Gen.StructGen._resolve_module_sha1("_ffilib", None) + if ffilib_sha1 is None: + self.Gen.TypeGenErr.fatal_error(fd, f"runtime CDLL extern '{fd.name}' requires the _ffilib runtime library, but module '_ffilib' was not found in the includes scan", "NameError") + return None + # 返回值约束 + match ret_ty: + case mlirlite.LLVMType.Struct(_, _, _): + self.Gen.TypeGenErr.fatal_error(fd, f"runtime CDLL extern '{fd.name}': returning a struct by value is not supported yet", "TypeError") + return None + i8_ty: mlirlite.LLVMType* = mlirlite.Int8(pool) + i8_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, i8_ty) + # 形参类型(按注解解析;缺注解按 i32,与 declare 缺省一致) + args_node: ast.Arguments* = fd.args + ptypes: sizet* = None + pnames: sizet* = None + pcount: int = 0 + if args_node is not None: + ags: ast.Arguments* = ast.Arguments*(args_node) + if ags.vararg is not None: + self.Gen.TypeGenErr.fatal_error(fd, f"runtime CDLL extern '{fd.name}' cannot be variadic", "TypeError") + return None + if ags.args is not None: + alist: list[ast.AST*]* = ags.args + an: sizet = len(alist) + if an > 0: + ptypes = stdlib.malloc(8 * an) + pnames = stdlib.malloc(8 * an) + if ptypes is None or pnames is None: return None + ai: sizet = 0 + while ai < an: + arg: ast.Arg* = ast.Arg*(alist.get(ai)) + ai += 1 + if arg is None or arg.arg is None: continue + pty: mlirlite.LLVMType* = None + if arg.annotation is not None: + if self.Gen.TypeGenResolve._is_builtin_marker(arg.annotation, "CVoid", trans._imported_modules) != 0: continue + pty = self.Gen.TypeGenResolve.resolve_annotation_type(pool, arg.annotation, trans._imported_modules, trans._from_imports, trans, 0) + if pty is None: + self.Gen.TypeGenErr.fatal_error(arg, f"runtime CDLL extern '{fd.name}' parameter '{arg.arg}' type annotation failed to resolve", "NameError") + return None + else: pty = mlirlite.Int32(pool) + match pty: + case mlirlite.LLVMType.Struct(_, _, _): + self.Gen.TypeGenErr.fatal_error(arg, f"runtime CDLL extern '{fd.name}' parameter '{arg.arg}': struct by value is not supported yet (use a pointer)", "TypeError") + return None + case mlirlite.LLVMType.Array(_, _): + pty = mlirlite.Ptr(pool, i8_ty) + ty_slot: sizet* = (sizet*)(uint64(ptypes) + uint64(pcount * 8)) + ty_slot[0] = sizet(pty) + # 形参名 %p0/%p1...(AST 形参名可能缺失,且与 C 名无关) + nm_buf: str = pool.alloc(16) + if nm_buf is None: return None + viperlib.snprintf(nm_buf, 16, "%%p%d", pcount) + nm_slot: sizet* = (sizet*)(uint64(pnames) + uint64(pcount * 8)) + nm_slot[0] = sizet(nm_buf) + pcount += 1 + # thunk define + 形参 + thunk: mlirlite.Function* = mlirlite.create_function(pool, mod, fd.name, ret_ty) + if thunk is None: return None + for i in range(pcount): + pty_a: mlirlite.LLVMType* = (mlirlite.LLVMType*)((sizet*)(uint64(ptypes) + uint64(i * 8))[0]) + pnm_a: str = rawptr((sizet*)(uint64(pnames) + uint64(i * 8))[0]) + mlirlite.add_param(pool, thunk, pty_a, pnm_a) + # 函数指针类型 ()* + pnode_head: mlirlite.ParamNode* = None + pnode_tail: mlirlite.ParamNode* = None + for i in range(pcount): + pty_b: mlirlite.LLVMType* = (mlirlite.LLVMType*)((sizet*)(uint64(ptypes) + uint64(i * 8))[0]) + nd: mlirlite.ParamNode* = mlirlite.new_param_node(pool, pty_b) + if nd is None: return None + if pnode_head is None: pnode_head = nd + else: pnode_tail.Next = nd + pnode_tail = nd + fn_ty: mlirlite.LLVMType* = mlirlite.Func(pool, ret_ty, pnode_head, pcount) + fn_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, fn_ty) + # 函数体 + b: mlirlite.IRBuilder* = mlirlite.new_builder(pool, thunk) + if b is None: return None + v_path: mlirlite.Value* = self.Gen.ExprGenConst.create_global_string(b, pool, mod, lib_path, trans) + v_name: mlirlite.Value* = self.Gen.ExprGenConst.create_global_string(b, pool, mod, fd.name, trans) + if v_path is None or v_name is None: return None + # h = Load(path) + load_sym: str = stdlib.malloc(64) + if load_sym is None: return None + viperlib.snprintf(load_sym, 64, "%s.Load", ffilib_sha1) + v_h: mlirlite.Value* = mlirlite.build_call(b, load_sym, v_path, 1, i8_ptr_ty, 0) + if v_h is None: return None + # p = Sym(h, name) + sym_sym: str = stdlib.malloc(64) + if sym_sym is None: return None + viperlib.snprintf(sym_sym, 64, "%s.Sym", ffilib_sha1) + mlirlite.value_set_next(v_h, v_name) + v_p: mlirlite.Value* = mlirlite.build_call(b, sym_sym, v_h, 2, i8_ptr_ty, 0) + if v_p is None: return None + # f = bitcast p to fn_ptr_ty + v_f: mlirlite.Value* = mlirlite.build_bitcast(b, v_p, fn_ptr_ty) + if v_f is None: return None + # 实参列表 = thunk 自己的形参 + arg_head: mlirlite.Value* = None + arg_prev: mlirlite.Value* = None + for i in range(pcount): + pty_c: mlirlite.LLVMType* = (mlirlite.LLVMType*)((sizet*)(uint64(ptypes) + uint64(i * 8))[0]) + pnm_c: str = rawptr((sizet*)(uint64(pnames) + uint64(i * 8))[0]) + av: mlirlite.Value* = mlirlite.SSAValue(pool, pty_c, pnm_c) + if av is None: return None + if arg_head is None: arg_head = av + else: mlirlite.value_set_next(arg_prev, av) + arg_prev = av + v_r: mlirlite.Value* = mlirlite.build_call_indirect(b, v_f, arg_head, pcount, ret_ty) + is_void: int = 0 + match ret_ty: + case mlirlite.LLVMType.Void(): + is_void = 1 + case _: + is_void = 0 + if is_void != 0: mlirlite.build_ret_void(b) + else: + if v_r is None: return None + mlirlite.build_ret(b, v_r) + return thunk + # forward_declare_functions - pre-scan all top-level FunctionDefs and emit forward declares. # Solves same-module forward references; translate_function_def later reuses the declare via find_func_in_module. def forward_declare_functions(self, trans: HT.Translator*, tree: ast.AST*) -> int: @@ -344,8 +476,23 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase): self.Gen.TypeGenErr.fatal_error(fd, f"extern library handle '{fd.ffi_lib}' is not a ffilib.CDLL binding (declare it with ffilib.CDLL(...) first)", "NameError") return 0 if ffi_rt != 0: - self.Gen.TypeGenErr.fatal_error(fd, f"extern '{fd.name}' is bound to runtime CDLL '{fd.ffi_lib}' (runtime=True): runtime symbol binding is not implemented yet (requires _ffilib)", "NotImplementedError") - return 0 + # runtime=True:生成 thunk(绝对路径 LoadLibraryA + 间接调用)。 + # 只在实际翻译阶段生成(declare-only 阶段仍走普通 declare,避免同一模块里 + # define 与 declare 同名并存)。 + if trans._declare_only == 0: + if self.Gen.CallGenBase.find_func_in_module(mod, fd.name) is None: + ffi_path: str = ViperBuild.FfiHandlePath(fd.ffi_lib) + if ffi_path is None: + self.Gen.TypeGenErr.fatal_error(fd, f"extern '{fd.name}' is bound to '{fd.ffi_lib}' but its library path was not recorded by Pass 0", "InternalCompilerError") + return 0 + thunk_f: mlirlite.Function* = self._emit_runtime_ffi_thunk(pool, mod, trans, fd, ffi_path, ret_ty) + if thunk_f is None: return 0 + thunk_max: int = 1024 + thunk_cnt: int = trans._func_count + if self.Gen.CallGenLeafKit.add_func_to_table(funcs_ptr, thunk_cnt, fd.name, thunk_f, thunk_max, None, 0, 0, pool) == 0: + trans._func_count = thunk_cnt + 1 + self.Gen.CallGenLeafKit.update_global_func_spec(fd.name, thunk_f) + continue # Compute the mangled name. Overload groups (same name >=2 in this module body) get the # signature suffix "{sha1}.{name}_v{参数签名hash}" so both defines can coexist in IR.