@@ -84,40 +84,29 @@ class CollectExternVisitor : AstVisitor {
8484
8585 standalone_ctx : LLVMOpaqueValue?
8686
87-
88- def CollectExternVisitor (standalone_context : LLVMOpaqueValue ?; _ctx : LLVMContextRef ;
89- _builder : LLVMBuilderRef ,
90- _mod : LLVMOpaqueModule ?; var _types : PrimitiveTypes ?,
91- uids : UidNodes ?; register_all : bool ) {
92- any_unimplemented = false
93- needs_whole_lib = false
94- uid := uids
95- ctx = _ctx
96- standalone_ctx = standalone_context
97- mod = _mod
98- types = _types
99- builder = _builder
100- register_mod_type = LLVMFunctionType (types .LLVMVoidPtrType (), array < LLVMTypeRef > ())
101-
102- // Create initialize_modules() function — filled incrementally as we discover needed modules
103- let init_fn_type = LLVMFunctionType (types .t_void , array < LLVMTypeRef > ())
87+ // Offline AOT object linked into a host that already has the runtime: only
88+ // re-resolve builtin-accessor globals (into `builder`); register NO modules
89+ // or functions (the host owns those). See emit_aot_object_globinit.
90+ aot_object : bool
91+ // The globinit ctor's Context* parameter (set by emit_aot_object_globinit),
92+ // used to resolve Func-value globs to the host's SimFunction* at load.
93+ aot_ctx_arg : LLVMOpaqueValue?
94+
95+
96+ // Build initialize_modules(): das_ensure_environment, then register builtins — ALL native
97+ // modules (register_all, force-linked via jit_register_all_builtin_modules) or just $ + strings.
98+ // Marks registered_modules so ensure_module doesn't double-register ("already created").
99+ def private build_initialize_modules (register_all : bool ) {
104100 init_fn = LLVMGetNamedFunction (g_mod , "initialize_modules" )
105101 let init_entry = LLVMAppendBasicBlockInContext (ctx , init_fn , "entry" )
106102 init_builder = LLVMCreateBuilder ()
107103 LLVMPositionBuilderAtEnd (init_builder , init_entry )
108- // Always call das_ensure_environment first
109104 let env_type = LLVMFunctionType (types .t_void , array < LLVMTypeRef > ())
110105 var env_fn = LLVMGetNamedFunction (g_mod , "das_ensure_environment" )
111106 if (env_fn == null ) {
112107 env_fn = LLVMAddFunctionWithType (g_mod , "das_ensure_environment" , env_type )
113108 }
114109 LLVMBuildCall2 (init_builder , env_type , env_fn , array < LLVMValueRef > (), "" )
115- // Optionally register ALL builtin native modules (math, fio, dasbind, ...)
116- // so a self-contained compiler-driver exe can recompile arbitrary daslang
117- // at runtime. Referencing this symbol also force-links those modules in.
118- // register_builtin_modules() covers exactly the set below, so mark them as
119- // already-registered to suppress the per-module emission (otherwise each
120- // would be registered twice -> "Module '...' already created").
121110 if (register_all ) {
122111 var reg_all = LLVMGetNamedFunction (g_mod , "jit_register_all_builtin_modules" )
123112 if (reg_all == null ) {
@@ -130,9 +119,7 @@ class CollectExternVisitor : AstVisitor {
130119 registered_modules [m ] = true
131120 }
132121 } else {
133- // Always register Module_BuiltIn ($) and Module_Strings (strings) — unless
134- // inject_main's static sweep already emitted the call (the thunks are NOT
135- // idempotent: a second call dies with "Module '$' already created").
122+ // $ and strings are non-idempotent thunks; skip if inject_main's static sweep already emitted them.
136123 if (! (g_exe_emitted_reg | > key_exists ("jit_register_Module_BuiltIn" ))) {
137124 g_exe_emitted_reg ["jit_register_Module_BuiltIn "] = true
138125 var reg_builtin = LLVMAddFunctionWithType (g_mod , "jit_register_Module_BuiltIn" , register_mod_type )
@@ -146,6 +133,30 @@ class CollectExternVisitor : AstVisitor {
146133 }
147134 registered_modules ["strings "] = true
148135 }
136+ }
137+
138+ def CollectExternVisitor (standalone_context : LLVMOpaqueValue ?; _ctx : LLVMContextRef ;
139+ _builder : LLVMBuilderRef ,
140+ _mod : LLVMOpaqueModule ?; var _types : PrimitiveTypes ?,
141+ uids : UidNodes ?; register_all : bool ; aot_obj : bool = false ) {
142+ any_unimplemented = false
143+ needs_whole_lib = false
144+ aot_object = aot_obj
145+ uid := uids
146+ ctx = _ctx
147+ standalone_ctx = standalone_context
148+ mod = _mod
149+ types = _types
150+ builder = _builder
151+ register_mod_type = LLVMFunctionType (types .LLVMVoidPtrType (), array < LLVMTypeRef > ())
152+
153+ if (aot_object ) {
154+ // Glob re-resolution goes into the caller-provided ctor (builder);
155+ // no initialize_modules(), no env, no module registration.
156+ init_builder = _builder
157+ } else {
158+ build_initialize_modules (register_all )
159+ }
149160
150161 // void * get_jit_table_{at,find,erase} ( int32_t baseType, Context * context, LineInfoArg * at )
151162 reg_extern_tab_type = LLVMFunctionType (types .LLVMVoidPtrType (),
@@ -225,6 +236,21 @@ class CollectExternVisitor : AstVisitor {
225236 }
226237
227238 def register_function (fn : Function ?) : LLVMOpaqueValue ? {
239+ // AOT-into-host: a Func VALUE must be the host Context's SimFunction* (Func
240+ // dispatch goes through SimFunction, not native code). The function is
241+ // already bound in the host by linkCppAot, so look it up by mangled-name
242+ // hash against the ctor's Context* arg -- works for builtins too (which have
243+ // no publ symbol in this object). No standalone-context registration.
244+ if (aot_object ) {
245+ let gfType = LLVMFunctionType (types .LLVMVoidPtrType (),
246+ fixed_array < LLVMTypeRef > (types .t_int64 , types .LLVMVoidPtrType ()))
247+ var gfFn = LLVMGetNamedFunction (g_mod , "das_aot_get_func_by_mnh" )
248+ if (gfFn == null ) {
249+ gfFn = LLVMAddFunctionWithType (g_mod , "das_aot_get_func_by_mnh" , gfType )
250+ }
251+ var gf_args = array < LLVMValueRef > (types .ConstI64 (fn .getMangledNameHash ), aot_ctx_arg )
252+ return LLVMBuildCall2 (builder , gfType , gfFn , gf_args , "" )
253+ }
228254 let fnmna = uid .get_dll_fn_name_ptr (fn )
229255 let fn_publ = LLVMGetNamedFunction (g_mod , fnmna .publ ())
230256 if (fn_publ == null ) return null
@@ -266,6 +292,7 @@ class CollectExternVisitor : AstVisitor {
266292 }
267293
268294 def ensure_module (m : Module ?) {
295+ if (aot_object ) return // host already has every module registered
269296 if (m == null ) return
270297 let mod_name = string (m .name )
271298 // Dynamic (.shared_module) modules are normally registered via the
@@ -576,6 +603,64 @@ class CollectExternVisitor : AstVisitor {
576603 }
577604}
578605
606+ // Build a load ctor/dtor pair that re-resolves this AOT object's builtin-accessor
607+ // globals to the HOST's real helpers. Offline objects leave those globals null +
608+ // internal (save_address_global / get_or_create_table_fn under emit_aot_object),
609+ // because the codegen-process addresses are meaningless in the host; this ctor
610+ // fills them at load via linker-resolved accessors (get_jit_table_at, das_get_jit_new,
611+ // jit_initialize_extern_function, ...) and points ExprAddr globals at their publ
612+ // symbols. No module/function registration -- the host owns those; functions bind
613+ // through Program::linkCppAot. Returns the (ctor, dtor) for llvm.global_ctors/dtors.
614+ def public emit_aot_object_globinit (ctx : LLVMContextRef ; mod : LLVMOpaqueModule ?;
615+ var types : PrimitiveTypes ?; var funcs : array < FunctionPtr > ;
616+ var uids : UidNodes ?) : LLVMOpaqueValue ? {
617+ // void das_aot_object_globinit_constructor(Context* ctx): ctx is used to
618+ // resolve Func-value globs to the host's SimFunction* (das_aot_get_func_by_mnh).
619+ let fnType = LLVMFunctionType (types .t_void , fixed_array < LLVMTypeRef > (types .LLVMVoidPtrType ()))
620+ let ctor = LLVMAddFunctionWithType (g_mod , "das_aot_object_globinit_constructor" , fnType )
621+ LLVMSetLinkage (ctor , LLVMLinkage .LLVMPrivateLinkage )
622+ var b = LLVMCreateBuilderInContext (ctx )
623+ LLVMPositionBuilderAtEnd (b , LLVMAppendBasicBlockInContext (ctx , ctor , "entry" ))
624+ // The visitor aliases the caller's uids (its `uid` field). We deliberately do
625+ // NOT delete it: deleting cascades into that shared uid, and the caller keeps
626+ // using uids afterward (the das_aot_register loop). It is a gc_node, reclaimed
627+ // by the surrounding ast_gc_guard / process teardown.
628+ var extern_resolver = new CollectExternVisitor (null , ctx , b , mod , types , uids , false , true )
629+ extern_resolver .aot_ctx_arg = LLVMGetParam (ctor , 0u )
630+ make_visitor (* extern_resolver ) $ (adapter_resolve) {
631+ ast_gc_guard () {
632+ for (fn in funcs ) {
633+ visit (fn , adapter_resolve )
634+ }
635+ }
636+ }
637+ // Fill the cross-module call/ctor target globs recorded during codegen with the
638+ // host Context's SimFunction* for each callee (by mangled-name hash).
639+ if (! (g_aot_func_globs | > empty ())) {
640+ let gfType = LLVMFunctionType (types .LLVMVoidPtrType (),
641+ fixed_array < LLVMTypeRef > (types .t_int64 , types .LLVMVoidPtrType ()))
642+ var gfFn = LLVMGetNamedFunction (g_mod , "das_aot_get_func_by_mnh" )
643+ if (gfFn == null ) {
644+ gfFn = LLVMAddFunctionWithType (g_mod , "das_aot_get_func_by_mnh" , gfType )
645+ }
646+ let ctx_arg = LLVMGetParam (ctor , 0u )
647+ for (fg in g_aot_func_globs ) {
648+ if (fg .glob == null ) {
649+ continue
650+ }
651+ var gf_args = array < LLVMValueRef > (types .ConstI64 (fg .mnh ), ctx_arg )
652+ var simfn = LLVMBuildCall2 (b , gfType , gfFn , gf_args , "" )
653+ // The glob is a pointer-to-fn-type; the SimFunction* (void*) bitcasts in.
654+ let elemT = LLVMGlobalGetValueType (fg .glob )
655+ var casted = LLVMBuildPointerCast (b , simfn , elemT , "" )
656+ LLVMBuildStore (b , casted , fg .glob )
657+ }
658+ }
659+ LLVMBuildRetVoid (b )
660+ LLVMDisposeBuilder (b )
661+ return ctor
662+ }
663+
579664def collect_external_functions (standalone_context : LLVMOpaqueValue ?; ctx : LLVMContextRef ; builder : LLVMBuilderRef ,
580665 mod : LLVMOpaqueModule ?; var types : PrimitiveTypes ?;
581666 var funcs : array < FunctionPtr > ; var uids : UidNodes ?;
0 commit comments