Skip to content

Commit 1e1cd1b

Browse files
committed
Rewrite globals for __ref_* access by loading pointers at function start
1 parent 1f61f39 commit 1e1cd1b

File tree

2 files changed

+91
-51
lines changed

2 files changed

+91
-51
lines changed
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// This verifies different patterns of accesses to global variables within functions that are hotpatched
2+
//
3+
// RUN: %clang_cl -c --target=x86_64-windows-msvc -O2 -fms-hotpatch-functions-list=this_gets_hotpatched /Fo%t.obj /clang:-S /clang:-o- %s 2>& 1 | FileCheck %s
4+
5+
extern int g_foo;
6+
extern int g_bar;
7+
8+
int* this_gets_hotpatched(int k, void g()) {
9+
g_foo = 10;
10+
11+
int* ret;
12+
if (k) {
13+
g();
14+
ret = &g_foo;
15+
} else {
16+
ret = &g_bar;
17+
}
18+
return ret;
19+
}

llvm/lib/CodeGen/WindowsHotPatch.cpp

Lines changed: 72 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,6 @@ class WindowsHotPatch : public ModulePass {
6767
bool
6868
runOnFunction(Function &F,
6969
SmallDenseMap<GlobalVariable *, GlobalVariable *> &RefMapping);
70-
void replaceGlobalVariableUses(
71-
Function &F, SmallVectorImpl<GlobalVariableUse> &GVUses,
72-
SmallDenseMap<GlobalVariable *, GlobalVariable *> &RefMapping,
73-
DIBuilder &DebugInfo);
7470
};
7571

7672
} // end anonymous namespace
@@ -203,6 +199,8 @@ bool WindowsHotPatch::runOnModule(Module &M) {
203199
bool WindowsHotPatch::runOnFunction(
204200
Function &F,
205201
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.
206204
SmallVector<GlobalVariableUse, 32> GVUses;
207205
for (auto &I : instructions(F)) {
208206
for (auto &U : I.operands()) {
@@ -216,59 +214,82 @@ bool WindowsHotPatch::runOnFunction(
216214
}
217215
}
218216

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()) {
230219
return false;
231220
}
232-
}
233221

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+
238250
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);
266285
}
267286

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);
272287
GVUse.User->setOperand(GVUse.Op, LoadedRefGV);
273288
}
289+
290+
if (Subprogram != nullptr) {
291+
DebugInfo.finalize();
292+
}
293+
294+
return true;
274295
}

0 commit comments

Comments
 (0)