@@ -141,11 +141,12 @@ void jl_link_global(GlobalVariable *GV, void *addr) JL_NOTSAFEPOINT
141
141
++LinkedGlobals;
142
142
Constant *P = literal_static_pointer_val (addr, GV->getValueType ());
143
143
GV->setInitializer (P);
144
+ GV->setDSOLocal (true );
144
145
if (jl_options.image_codegen ) {
145
146
// If we are forcing imaging mode codegen for debugging,
146
147
// emit external non-const symbol to avoid LLVM optimizing the code
147
148
// similar to non-imaging mode.
148
- GV->setLinkage (GlobalValue::ExternalLinkage );
149
+ assert ( GV->hasExternalLinkage () );
149
150
}
150
151
else {
151
152
GV->setConstant (true );
@@ -162,6 +163,23 @@ void jl_jit_globals(std::map<void *, GlobalVariable*> &globals) JL_NOTSAFEPOINT
162
163
}
163
164
}
164
165
166
+ // used for image_codegen, where we keep all the gvs external
167
+ // so we can't jit them directly into each module
168
+ static orc::ThreadSafeModule jl_get_globals_module (orc::ThreadSafeContext &ctx, bool imaging_mode, const DataLayout &DL, const Triple &T, std::map<void *, GlobalVariable*> &globals) JL_NOTSAFEPOINT
169
+ {
170
+ auto lock = ctx.getLock ();
171
+ auto GTSM = jl_create_ts_module (" globals" , ctx, imaging_mode, DL, T);
172
+ auto GM = GTSM.getModuleUnlocked ();
173
+ for (auto &global : globals) {
174
+ auto GV = global.second ;
175
+ auto GV2 = new GlobalVariable (*GM, GV->getValueType (), GV->isConstant (), GlobalValue::ExternalLinkage, literal_static_pointer_val (global.first , GV->getValueType ()), GV->getName (), nullptr , GV->getThreadLocalMode (), GV->getAddressSpace (), false );
176
+ GV2->copyAttributesFrom (GV);
177
+ GV2->setDSOLocal (true );
178
+ GV2->setAlignment (GV->getAlign ());
179
+ }
180
+ return GTSM;
181
+ }
182
+
165
183
// this generates llvm code for the lambda info
166
184
// and adds the result to the jitlayers
167
185
// (and the shadow module),
@@ -211,46 +229,53 @@ static jl_callptr_t _jl_compile_codeinst(
211
229
212
230
if (params._shared_module )
213
231
jl_ExecutionEngine->addModule (orc::ThreadSafeModule (std::move (params._shared_module ), params.tsctx ));
214
- if (!params.imaging ) {
215
- StringMap<orc::ThreadSafeModule*> NewExports;
232
+
233
+ // In imaging mode, we can't inline global variable initializers in order to preserve
234
+ // the fiction that we don't know what loads from the global will return. Thus, we
235
+ // need to emit a separate module for the globals before any functions are compiled,
236
+ // to ensure that the globals are defined when they are compiled.
237
+ if (params.imaging ) {
238
+ jl_ExecutionEngine->addModule (jl_get_globals_module (params.tsctx , params.imaging , params.DL , params.TargetTriple , params.global_targets ));
239
+ } else {
216
240
StringMap<void *> NewGlobals;
217
241
for (auto &global : params.global_targets ) {
218
242
NewGlobals[global.second ->getName ()] = global.first ;
219
243
}
220
244
for (auto &def : emitted) {
221
- orc::ThreadSafeModule &TSM = std::get<0 >(def.second );
222
- // The underlying context object is still locked because params is not destroyed yet
223
- auto M = TSM.getModuleUnlocked ();
224
- for (auto &F : M->global_objects ()) {
225
- if (!F.isDeclaration () && F.getLinkage () == GlobalValue::ExternalLinkage) {
226
- NewExports[F.getName ()] = &TSM;
227
- }
228
- }
229
- // Let's link all globals here also (for now)
245
+ auto M = std::get<0 >(def.second ).getModuleUnlocked ();
230
246
for (auto &GV : M->globals ()) {
231
247
auto InitValue = NewGlobals.find (GV.getName ());
232
248
if (InitValue != NewGlobals.end ()) {
233
249
jl_link_global (&GV, InitValue->second );
234
250
}
235
251
}
236
252
}
237
- DenseMap<orc::ThreadSafeModule*, int > Queued;
238
- std::vector<orc::ThreadSafeModule*> Stack;
239
- for (auto &def : emitted) {
240
- // Add the results to the execution engine now
241
- orc::ThreadSafeModule &M = std::get<0 >(def.second );
242
- jl_add_to_ee (M, NewExports, Queued, Stack);
243
- assert (Queued.empty () && Stack.empty () && !M);
244
- }
245
- } else {
246
- jl_jit_globals (params.global_targets );
247
- auto main = std::move (emitted[codeinst].first );
248
- for (auto &def : emitted) {
249
- if (def.first != codeinst) {
250
- jl_merge_module (main, std::move (def.second .first ));
253
+ }
254
+
255
+ // Collect the exported functions from the emitted modules,
256
+ // which form dependencies on which functions need to be
257
+ // compiled first. Cycles of functions are compiled together.
258
+ // (essentially we compile a DAG of SCCs in reverse topological order,
259
+ // if we treat declarations of external functions as edges from declaration
260
+ // to definition)
261
+ StringMap<orc::ThreadSafeModule*> NewExports;
262
+ for (auto &def : emitted) {
263
+ orc::ThreadSafeModule &TSM = std::get<0 >(def.second );
264
+ // The underlying context object is still locked because params is not destroyed yet
265
+ auto M = TSM.getModuleUnlocked ();
266
+ for (auto &F : M->global_objects ()) {
267
+ if (!F.isDeclaration () && F.getLinkage () == GlobalValue::ExternalLinkage) {
268
+ NewExports[F.getName ()] = &TSM;
251
269
}
252
270
}
253
- jl_ExecutionEngine->addModule (std::move (main));
271
+ }
272
+ DenseMap<orc::ThreadSafeModule*, int > Queued;
273
+ std::vector<orc::ThreadSafeModule*> Stack;
274
+ for (auto &def : emitted) {
275
+ // Add the results to the execution engine now
276
+ orc::ThreadSafeModule &M = std::get<0 >(def.second );
277
+ jl_add_to_ee (M, NewExports, Queued, Stack);
278
+ assert (Queued.empty () && Stack.empty () && !M);
254
279
}
255
280
++CompiledCodeinsts;
256
281
MaxWorkqueueSize.updateMax (emitted.size ());
0 commit comments