feat(ffilib): runtime CDLL 生成 thunk(绝对路径 LoadLibraryA + 间接调用)
_emit_runtime_ffi_thunk 生成 define <name>(%p0..): Load(path) -> Sym(h,name) -> bitcast 成 <ret>(<params>)* -> call_indirect -> ret;符号名与普通 extern 一致(裸名),调用点零改动。路径与符号名用 ExprGenConst.create_global_string 落成常量操作数(GEP 由该助手处理)。约束:仅标量参数/返回值,结构体按值与变参 fail-fast;declare-only 阶段仍走普通 declare,避免同模块 define/declare 同名并存。
This commit is contained in:
1 parent
5e038eae5e
commit
0acbefafd8
1 file changed
+149
-2
@@ -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 <name>(params...),函数体:
|
||||
# %h = call i8* @{_ffilib}.Load(i8* "<绝对路径>")
|
||||
# %p = call i8* @{_ffilib}.Sym(i8* %h, i8* "<符号名>")
|
||||
# %f = bitcast i8* %p to <ret>(<params>)*
|
||||
# %r = call <ret> %f(<params>) ; 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)
|
||||
# 函数指针类型 <ret>(<params>)*
|
||||
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.
|
||||
|
||||
Reference in new issue
Block a user