@@ -67,10 +67,6 @@ class WindowsHotPatch : public ModulePass {
67
67
bool
68
68
runOnFunction (Function &F,
69
69
SmallDenseMap<GlobalVariable *, GlobalVariable *> &RefMapping);
70
- void replaceGlobalVariableUses (
71
- Function &F, SmallVectorImpl<GlobalVariableUse> &GVUses,
72
- SmallDenseMap<GlobalVariable *, GlobalVariable *> &RefMapping,
73
- DIBuilder &DebugInfo);
74
70
};
75
71
76
72
} // end anonymous namespace
@@ -203,6 +199,8 @@ bool WindowsHotPatch::runOnModule(Module &M) {
203
199
bool WindowsHotPatch::runOnFunction (
204
200
Function &F,
205
201
SmallDenseMap<GlobalVariable *, GlobalVariable *> &RefMapping) {
202
+ // First pass: Find any instructions that reference global variables. We do not modify the IR
203
+ // in this pass because we don't want to modify a collection while iterating the same collection.
206
204
SmallVector<GlobalVariableUse, 32 > GVUses;
207
205
for (auto &I : instructions (F)) {
208
206
for (auto &U : I.operands ()) {
@@ -216,59 +214,82 @@ bool WindowsHotPatch::runOnFunction(
216
214
}
217
215
}
218
216
219
- if (!GVUses.empty ()) {
220
- const llvm::DISubprogram *Subprogram = F.getSubprogram ();
221
- DIBuilder DebugInfo{*F.getParent (), true ,
222
- Subprogram != nullptr ? Subprogram->getUnit ()
223
- : nullptr };
224
- replaceGlobalVariableUses (F, GVUses, RefMapping, DebugInfo);
225
- if (Subprogram != nullptr ) {
226
- DebugInfo.finalize ();
227
- }
228
- return true ;
229
- } else {
217
+ // Most functions do not touch global variables.
218
+ if (GVUses.empty ()) {
230
219
return false ;
231
220
}
232
- }
233
221
234
- void WindowsHotPatch::replaceGlobalVariableUses (
235
- Function &F, SmallVectorImpl<GlobalVariableUse> &GVUses,
236
- SmallDenseMap<GlobalVariable *, GlobalVariable *> &RefMapping,
237
- DIBuilder &DebugInfo) {
222
+ // For each unique global variable, we create a __ref_FOO global variable that points to it.
223
+ // The RefMapping collection maps from a global variable to its __ref_* global variable.
224
+ //
225
+ // A given function may reference the same global variable more than once, either at different
226
+ // instructions or in a loop (or both). During the lifetime of a hotpatched image, these
227
+ // __ref_FOO pointers never change. We generate a single load for each unique global variable
228
+ // that is used within a given function, and place that load at the start of the function.
229
+ //
230
+ // This avoids the cost of repeatedly loading each __ref_FOO within a function, such as
231
+ // within a hot loop. However, it comes with a minor cost: in each hot-patched function, the
232
+ // function will read all of the __ref_FOO pointers that it might need, even if control flow
233
+ // never reaches a place that uses a given pointer. This is a minor cost, compared to avoiding
234
+ // the cost of repeated fetches in a loop.
235
+
236
+ const llvm::DISubprogram *Subprogram = F.getSubprogram ();
237
+ DIBuilder DebugInfo{*F.getParent (), true ,
238
+ Subprogram != nullptr ? Subprogram->getUnit ()
239
+ : nullptr };
240
+
241
+ auto & EntryBlock = F.getEntryBlock ();
242
+
243
+ // Insert loads for __ref_* variables at the start of the entry block.
244
+ // Entry blocks can never have PHI nodes, so there is no conflict with PHI nodes at the start.
245
+ IRBuilder<> Builder (&EntryBlock, EntryBlock.begin (), nullptr , {});
246
+
247
+ // Maps GlobalVariable (the old one, not the pointer) to the result of a load instruction for that global
248
+ SmallDenseMap<GlobalVariable *, LoadInst *> GVLoadInsts;
249
+
238
250
for (auto &GVUse : GVUses) {
239
- IRBuilder<> Builder (GVUse.User );
240
-
241
- // Get or create a new global variable that points to the old one and who's
242
- // name begins with `__ref_`.
243
- GlobalVariable *&ReplaceWithRefGV =
244
- RefMapping.try_emplace (GVUse.GV ).first ->second ;
245
- if (ReplaceWithRefGV == nullptr ) {
246
- Constant *AddrOfOldGV = ConstantExpr::getGetElementPtr (
247
- Builder.getPtrTy (), GVUse.GV , ArrayRef<Value *>{});
248
- ReplaceWithRefGV =
249
- new GlobalVariable (*F.getParent (), Builder.getPtrTy (), true ,
250
- GlobalValue::InternalLinkage, AddrOfOldGV,
251
- Twine (" __ref_" ).concat (GVUse.GV ->getName ()),
252
- nullptr , GlobalVariable::NotThreadLocal);
253
-
254
- // Create debug info for the replacement global variable.
255
- DISubprogram *SP = F.getSubprogram ();
256
- DataLayout Layout = F.getParent ()->getDataLayout ();
257
- DIType *DebugType = DebugInfo.createPointerType (
258
- nullptr , Layout.getTypeSizeInBits (GVUse.GV ->getValueType ()));
259
- DIGlobalVariableExpression *GVE =
260
- DebugInfo.createGlobalVariableExpression (
261
- SP != nullptr ? SP->getUnit () : nullptr ,
262
- ReplaceWithRefGV->getName (), StringRef{},
263
- SP != nullptr ? SP->getFile () : nullptr , /* LineNo*/ 0 , DebugType,
264
- /* IsLocalToUnit*/ false );
265
- ReplaceWithRefGV->addDebugInfo (GVE);
251
+ LoadInst *&LoadedRefGV = GVLoadInsts.try_emplace (GVUse.GV ).first ->second ;
252
+
253
+ if (LoadedRefGV == nullptr ) {
254
+ // Get or create a new global variable that points to the old one and who's
255
+ // name begins with `__ref_`.
256
+ GlobalVariable *&ReplaceWithRefGV =
257
+ RefMapping.try_emplace (GVUse.GV ).first ->second ;
258
+ if (ReplaceWithRefGV == nullptr ) {
259
+ Constant *AddrOfOldGV = ConstantExpr::getGetElementPtr (
260
+ Builder.getPtrTy (), GVUse.GV , ArrayRef<Value *>{});
261
+
262
+ ReplaceWithRefGV =
263
+ new GlobalVariable (*F.getParent (), Builder.getPtrTy (), true ,
264
+ GlobalValue::InternalLinkage, AddrOfOldGV,
265
+ Twine (" __ref_" ).concat (GVUse.GV ->getName ()),
266
+ nullptr , GlobalVariable::NotThreadLocal);
267
+
268
+ // Create debug info for the replacement global variable.
269
+ DISubprogram *SP = F.getSubprogram ();
270
+ DataLayout Layout = F.getParent ()->getDataLayout ();
271
+ DIType *DebugType = DebugInfo.createPointerType (
272
+ nullptr , Layout.getTypeSizeInBits (GVUse.GV ->getValueType ()));
273
+ DIGlobalVariableExpression *GVE =
274
+ DebugInfo.createGlobalVariableExpression (
275
+ SP != nullptr ? SP->getUnit () : nullptr ,
276
+ ReplaceWithRefGV->getName (), StringRef{},
277
+ SP != nullptr ? SP->getFile () : nullptr , /* LineNo*/ 0 , DebugType,
278
+ /* IsLocalToUnit*/ false );
279
+ ReplaceWithRefGV->addDebugInfo (GVE);
280
+ }
281
+
282
+ // Now replace the use of that global variable with the new one (via a load
283
+ // since it is a pointer to the old global variable).
284
+ LoadedRefGV = Builder.CreateLoad (ReplaceWithRefGV->getValueType (), ReplaceWithRefGV);
266
285
}
267
286
268
- // Now replace the use of that global variable with the new one (via a load
269
- // since it is a pointer to the old global variable).
270
- LoadInst *LoadedRefGV =
271
- Builder.CreateLoad (ReplaceWithRefGV->getValueType (), ReplaceWithRefGV);
272
287
GVUse.User ->setOperand (GVUse.Op , LoadedRefGV);
273
288
}
289
+
290
+ if (Subprogram != nullptr ) {
291
+ DebugInfo.finalize ();
292
+ }
293
+
294
+ return true ;
274
295
}
0 commit comments