|
| 1 | +options gen2 |
| 2 | +options relaxed_pointer_const // fixed_array<LLVMTypeRef> with const PrimitiveTypes fields needs it |
| 3 | + |
| 4 | +module llvm_aot shared private |
| 5 | + |
| 6 | +require llvm/daslib/llvm_boost |
| 7 | +require llvm/daslib/llvm_jit |
| 8 | +require llvm/daslib/llvm_jit_common |
| 9 | +require llvm/daslib/llvm_exe |
| 10 | +require llvm/daslib/llvm_dll_utils |
| 11 | +require daslib/ast_boost |
| 12 | +require daslib/rtti |
| 13 | + |
| 14 | +//! LLVM-AOT registration pass. After the JIT visitor has generated + optimized the module (as for a |
| 15 | +//! normal exe), this adds the offline-AOT-object registration layer so a statically-linked .o binds |
| 16 | +//! through Program::linkCppAot: a self-registering das_aot_register ctor plus the builtin/Func-value |
| 17 | +//! glob re-resolution ctor (built by llvm_exe's extern resolver). All the AOT-object-only "register |
| 18 | +//! it into the host" logic lives here; run_jit calls build_llvm_aot_ctors once, in emit_aot_object mode. |
| 19 | + |
| 20 | +// The das_aot_register ctor: for each emitted function, emit das_aot_register(aotHash, publ) so |
| 21 | +// the linked .o self-registers into the AOT library at load; also wire the glob re-resolution callback. |
| 22 | +def private emit_aot_register_ctor_dtor(types : PrimitiveTypes?; funcs : array<FunctionPtr>; var uids : UidNodes?; _prog : Program?; glob_ctor : LLVMOpaqueValue?) : tuple<LLVMOpaqueValue?, LLVMOpaqueValue?> { |
| 23 | + let regType = LLVMFunctionType(types.t_void, |
| 24 | + fixed_array<LLVMTypeRef>(types.t_int64, types.LLVMVoidPtrType())) |
| 25 | + var regFn = LLVMGetNamedFunction(g_mod, "das_aot_register") |
| 26 | + if (regFn == null) { |
| 27 | + regFn = LLVMAddFunctionWithType(g_mod, "das_aot_register", regType) // linker binds it to daScript core |
| 28 | + } |
| 29 | + let functionType = LLVMFunctionType(types.t_void, null, 0u, 0) |
| 30 | + // Private: each object defines it, so public linkage would clash when many link together. |
| 31 | + let reg_ctor = LLVMAddFunctionWithType(g_mod, "das_aot_register_constructor", functionType) |
| 32 | + let reg_dtor = LLVMAddFunctionWithType(g_mod, "das_aot_register_destructor", functionType) |
| 33 | + set_private_linkage(reg_ctor) |
| 34 | + set_private_linkage(reg_dtor) |
| 35 | + var ctor_builder = LLVMCreateBuilderInContext(g_ctx) |
| 36 | + var dtor_builder = LLVMCreateBuilderInContext(g_ctx) |
| 37 | + LLVMPositionBuilderAtEnd(ctor_builder, LLVMAppendBasicBlockInContext(g_ctx, reg_ctor, "entry")) |
| 38 | + LLVMPositionBuilderAtEnd(dtor_builder, LLVMAppendBasicBlockInContext(g_ctx, reg_dtor, "entry")) |
| 39 | + for (fun in funcs) { |
| 40 | + var publ = LLVMGetNamedFunction(g_mod, uids->get_dll_fn_name(fun).publ()) |
| 41 | + if (publ == null) { |
| 42 | + continue |
| 43 | + } |
| 44 | + let aot_hash = get_function_aot_hash(fun) |
| 45 | + var publ_ptr = LLVMBuildPointerCast(ctor_builder, publ, types.LLVMVoidPtrType(), "") |
| 46 | + var reg_args = array<LLVMValueRef>(types.ConstI64(aot_hash), publ_ptr) |
| 47 | + LLVMBuildCall2(ctor_builder, regType, regFn, reg_args, "") |
| 48 | + } |
| 49 | + // Record the glob re-resolution callback; the runtime runs it lazily at the first AotLibrary drain (not static-init). |
| 50 | + if (glob_ctor != null) { |
| 51 | + let giType = LLVMFunctionType(types.t_void, fixed_array<LLVMTypeRef>(types.LLVMVoidPtrType())) |
| 52 | + var giFn = LLVMGetNamedFunction(g_mod, "das_aot_register_globinit") |
| 53 | + if (giFn == null) { |
| 54 | + giFn = LLVMAddFunctionWithType(g_mod, "das_aot_register_globinit", giType) |
| 55 | + } |
| 56 | + var gi_ptr = LLVMBuildPointerCast(ctor_builder, glob_ctor, types.LLVMVoidPtrType(), "") |
| 57 | + var gi_args = array<LLVMValueRef>(gi_ptr) |
| 58 | + LLVMBuildCall2(ctor_builder, giType, giFn, gi_args, "") |
| 59 | + } |
| 60 | + LLVMBuildRetVoid(ctor_builder) |
| 61 | + LLVMBuildRetVoid(dtor_builder) |
| 62 | + LLVMDisposeBuilder(ctor_builder) |
| 63 | + LLVMDisposeBuilder(dtor_builder) |
| 64 | + return (reg_ctor, reg_dtor) |
| 65 | +} |
| 66 | + |
| 67 | + |
| 68 | +// Build a load ctor that re-resolves this AOT object's builtin-accessor globals to the HOST's real |
| 69 | +// helpers (offline objects leave them null+internal since codegen-process addresses are meaningless |
| 70 | +// there) and points ExprAddr globals at their publ symbols. No module/function registration. |
| 71 | +def public emit_aot_object_globinit(ctx : LLVMContextRef; mod : LLVMOpaqueModule?; |
| 72 | + var types : PrimitiveTypes?; var funcs : array<FunctionPtr>; |
| 73 | + var uids : UidNodes?) : LLVMOpaqueValue? { |
| 74 | + // void das_aot_object_globinit_constructor(Context* ctx): ctx is used to |
| 75 | + // resolve Func-value globs to the host's SimFunction* (das_aot_get_func_by_mnh). |
| 76 | + let fnType = LLVMFunctionType(types.t_void, fixed_array<LLVMTypeRef>(types.LLVMVoidPtrType())) |
| 77 | + let ctor = LLVMAddFunctionWithType(g_mod, "das_aot_object_globinit_constructor", fnType) |
| 78 | + LLVMSetLinkage(ctor, LLVMLinkage.LLVMPrivateLinkage) |
| 79 | + var b = LLVMCreateBuilderInContext(ctx) |
| 80 | + LLVMPositionBuilderAtEnd(b, LLVMAppendBasicBlockInContext(ctx, ctor, "entry")) |
| 81 | + // Aliases the caller's uids; do NOT delete it (deletion cascades into that shared uid, which the |
| 82 | + // caller still uses). It's a gc_node, reclaimed by the surrounding ast_gc_guard / process teardown. |
| 83 | + var extern_resolver = new CollectExternVisitor(null, ctx, b, mod, types, uids, false, LlvmJitMode.LLVM_AOT) |
| 84 | + extern_resolver.aot_ctx_arg = LLVMGetParam(ctor, 0u) |
| 85 | + make_visitor(*extern_resolver) $(adapter_resolve) { |
| 86 | + ast_gc_guard() { |
| 87 | + for (fn in funcs) { |
| 88 | + visit(fn, adapter_resolve) |
| 89 | + } |
| 90 | + } |
| 91 | + } |
| 92 | + // Fill the cross-module call/ctor target globs recorded during codegen with the |
| 93 | + // host Context's SimFunction* for each callee (by mangled-name hash). |
| 94 | + if (!(g_aot_func_globs |> empty())) { |
| 95 | + let ctx_arg = LLVMGetParam(ctor, 0u) |
| 96 | + for (fg in g_aot_func_globs) { |
| 97 | + if (fg.glob == null) { |
| 98 | + continue |
| 99 | + } |
| 100 | + var simfn = build_aot_get_func_by_mnh(b, types, fg.mnh, ctx_arg) |
| 101 | + // The glob is a pointer-to-fn-type; the SimFunction* (void*) bitcasts in. |
| 102 | + let elemT = LLVMGlobalGetValueType(fg.glob) |
| 103 | + var casted = LLVMBuildPointerCast(b, simfn, elemT, "") |
| 104 | + LLVMBuildStore(b, casted, fg.glob) |
| 105 | + } |
| 106 | + } |
| 107 | + LLVMBuildRetVoid(b) |
| 108 | + LLVMDisposeBuilder(b) |
| 109 | + return ctor |
| 110 | +} |
| 111 | + |
| 112 | + |
| 113 | +//! Build both offline-AOT-object ctors (the extern-resolver globinit + the das_aot_register ctor) and |
| 114 | +//! return the (ctor, dtor) pair to append to llvm.global_ctors/dtors. Called by run_jit in emit_aot_object mode. |
| 115 | +def public build_llvm_aot_ctors(ctx : LLVMContextRef; mod : LLVMOpaqueModule?; var types : PrimitiveTypes?; var funcs : array<FunctionPtr>; var uids : UidNodes?; prog : Program?) : tuple<LLVMOpaqueValue?, LLVMOpaqueValue?> { |
| 116 | + let glob_ctor = emit_aot_object_globinit(ctx, mod, types, funcs, uids) |
| 117 | + return emit_aot_register_ctor_dtor(types, funcs, uids, prog, glob_ctor) |
| 118 | +} |
0 commit comments