1 | //===------ WindowsHotPatch.cpp - Support for Windows hotpatching ---------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | // |
9 | // Provides support for the Windows "Secure Hot-Patching" feature. |
10 | // |
11 | // Windows contains technology, called "Secure Hot-Patching" (SHP), for securely |
12 | // applying hot-patches to a running system. Hot-patches may be applied to the |
13 | // kernel, kernel-mode components, device drivers, user-mode system services, |
14 | // etc. |
15 | // |
16 | // SHP relies on integration between many tools, including compiler, linker, |
17 | // hot-patch generation tools, and the Windows kernel. This file implements that |
18 | // part of the workflow needed in compilers / code generators. |
19 | // |
20 | // SHP is not intended for productivity scenarios such as Edit-and-Continue or |
21 | // interactive development. SHP is intended to minimize downtime during |
22 | // installation of Windows OS patches. |
23 | // |
24 | // In order to work with SHP, LLVM must do all of the following: |
25 | // |
26 | // * On some architectures (X86, AMD64), the function prolog must begin with |
27 | // hot-patchable instructions. This is handled by the MSVC `/hotpatch` option |
28 | // and the equivalent `-fms-hotpatch` function. This is necessary because we |
29 | // generally cannot anticipate which functions will need to be patched in the |
30 | // future. This option ensures that a function can be hot-patched in the |
31 | // future, but does not actually generate any hot-patch for it. |
32 | // |
33 | // * For a selected set of functions that are being hot-patched (which are |
34 | // identified using command-line options), LLVM must generate the |
35 | // `S_HOTPATCHFUNC` CodeView record (symbol). This record indicates that a |
36 | // function was compiled with hot-patching enabled. |
37 | // |
38 | // This implementation uses the `MarkedForWindowsHotPatching` attribute to |
39 | // annotate those functions that were marked for hot-patching by command-line |
40 | // parameters. The attribute may be specified by a language front-end by |
41 | // setting an attribute when a function is created in LLVM IR, or it may be |
42 | // set by passing LLVM arguments. |
43 | // |
44 | // * For those functions that are hot-patched, LLVM must rewrite references to |
45 | // global variables so that they are indirected through a `__ref_*` pointer |
46 | // variable. For each global variable, that is accessed by a hot-patched |
47 | // function, e.g. `FOO`, a `__ref_FOO` global pointer variable is created and |
48 | // all references to the original `FOO` are rewritten as dereferences of the |
49 | // `__ref_FOO` pointer. |
50 | // |
51 | // Some globals do not need `__ref_*` indirection. The pointer indirection |
52 | // behavior can be disabled for these globals by marking them with the |
53 | // `AllowDirectAccessInHotPatchFunction`. |
54 | // |
55 | // Rewriting references to global variables has some complexity. |
56 | // |
57 | // For ordinary instructions that reference GlobalVariables, we rewrite the |
58 | // operand of the instruction to a Load of the __ref_* variable. |
59 | // |
60 | // For constant expressions, we have to convert the constant expression (and |
61 | // transitively all constant expressions in its parent chain) to non-constant |
62 | // expressions, i.e. to a sequence of instructions. |
63 | // |
64 | // Pass 1: |
65 | // * Enumerate all instructions in all basic blocks. |
66 | // |
67 | // * If an instruction references a GlobalVariable (and it is not marked |
68 | // as being ignored), then we create (if necessary) the __ref_* variable |
69 | // for the GlobalVariable reference. However, we do not yet modify the |
70 | // Instruction. |
71 | // |
72 | // * If an instruction has an operand that is a ConstantExpr and the |
73 | // ConstantExpression tree contains a reference to a GlobalVariable, then |
74 | // we similarly create __ref_*. Similarly, we do not yet modify the |
75 | // Instruction or the ConstantExpr tree. |
76 | // |
77 | // After Pass 1 completes, we will know whether we found any references to |
78 | // globals in this pass. If the function does not use any globals (and most |
79 | // functions do not use any globals), then we return immediately. |
80 | // |
81 | // If a function does reference globals, then we iterate the list of globals |
82 | // used by this function and we generate Load instructions for each (unique) |
83 | // global. |
84 | // |
85 | // Next, we do another pass over all instructions: |
86 | // |
87 | // Pass 2: |
88 | // * Re-visit the instructions that were found in Pass 1. |
89 | // |
90 | // * If an instruction operand is a GlobalVariable, then look up the |
91 | // replacement |
92 | // __ref_* global variable and the Value that came from the Load instruction |
93 | // for it. Replace the operand of the GlobalVariable with the Load Value. |
94 | // |
95 | // * If an instruction operand is a ConstantExpr, then recursively examine the |
96 | // operands of all instructions in the ConstantExpr tree. If an operand is |
97 | // a GlobalVariable, then replace the operand with the result of the load |
98 | // *and* convert the ConstantExpr to a non-constant instruction. This |
99 | // instruction will need to be inserted into the BB of the instruction whose |
100 | // operand is being modified, ideally immediately before the instruction |
101 | // being modified. |
102 | // |
103 | // Limitations |
104 | // |
105 | // This feature is not intended to work in every situation. There are many |
106 | // legitimate code changes (patches) for which it is not possible to generate |
107 | // a hot-patch. Developers who are writing hot-patches are expected to |
108 | // understand the limitations. |
109 | // |
110 | // Tools which generate hot-patch metadata may also check that certain |
111 | // variables are upheld, and some of these invariants may be global (may require |
112 | // whole-program knowledge, not available in any single compiland). However, |
113 | // such tools are not required to be perfect; they are also best-effort. |
114 | // |
115 | // For these reasons, the hot-patching support implemented in this file is |
116 | // "best effort". It does not recognize every possible code pattern that could |
117 | // be patched, nor does it generate diagnostics for certain code patterns that |
118 | // could result in a binary that does not work with hot-patching. For example, |
119 | // const GlobalVariables that point to other non-const GlobalVariables are not |
120 | // compatible with hot-patching because they cannot use __ref_*-based |
121 | // redirection. |
122 | // |
123 | // References |
124 | // |
125 | // * "Hotpatching on Windows": |
126 | // https://techcommunity.microsoft.com/blog/windowsosplatform/hotpatching-on-windows/2959541 |
127 | // |
128 | // * "Hotpatch for Windows client now available": |
129 | // https://techcommunity.microsoft.com/blog/windows-itpro-blog/hotpatch-for-windows-client-now-available/4399808 |
130 | // |
131 | // * "Get hotpatching for Windows Server": |
132 | // https://www.microsoft.com/en-us/windows-server/blog/2025/04/24/tired-of-all-the-restarts-get-hotpatching-for-windows-server/ |
133 | // |
134 | //===----------------------------------------------------------------------===// |
135 | |
136 | #include "llvm/ADT/SmallSet.h" |
137 | #include "llvm/CodeGen/Passes.h" |
138 | #include "llvm/IR/Attributes.h" |
139 | #include "llvm/IR/DIBuilder.h" |
140 | #include "llvm/IR/DiagnosticInfo.h" |
141 | #include "llvm/IR/Function.h" |
142 | #include "llvm/IR/IRBuilder.h" |
143 | #include "llvm/IR/InstIterator.h" |
144 | #include "llvm/IR/Module.h" |
145 | #include "llvm/InitializePasses.h" |
146 | #include "llvm/Pass.h" |
147 | #include "llvm/Support/CommandLine.h" |
148 | #include "llvm/Support/LineIterator.h" |
149 | #include "llvm/Support/MemoryBuffer.h" |
150 | |
151 | using namespace llvm; |
152 | |
153 | #define DEBUG_TYPE "windows-secure-hot-patch" |
154 | |
155 | // A file containing list of mangled function names to mark for hot patching. |
156 | static cl::opt<std::string> LLVMMSSecureHotPatchFunctionsFile( |
157 | "ms-secure-hotpatch-functions-file" , cl::value_desc("filename" ), |
158 | cl::desc("A file containing list of mangled function names to mark for " |
159 | "Windows Secure Hot-Patching" )); |
160 | |
161 | // A list of mangled function names to mark for hot patching. |
162 | static cl::list<std::string> LLVMMSSecureHotPatchFunctionsList( |
163 | "ms-secure-hotpatch-functions-list" , cl::value_desc("list" ), |
164 | cl::desc("A list of mangled function names to mark for Windows Secure " |
165 | "Hot-Patching" ), |
166 | cl::CommaSeparated); |
167 | |
168 | namespace { |
169 | |
170 | struct GlobalVariableUse { |
171 | // GlobalVariable *GV; |
172 | Instruction *User; |
173 | unsigned Op; |
174 | }; |
175 | |
176 | class WindowsSecureHotPatching : public ModulePass { |
177 | public: |
178 | static char ID; |
179 | |
180 | WindowsSecureHotPatching() : ModulePass(ID) { |
181 | initializeWindowsSecureHotPatchingPass(*PassRegistry::getPassRegistry()); |
182 | } |
183 | |
184 | void getAnalysisUsage(AnalysisUsage &AU) const override { |
185 | AU.setPreservesCFG(); |
186 | } |
187 | |
188 | bool doInitialization(Module &) override; |
189 | bool runOnModule(Module &M) override { return false; } |
190 | |
191 | private: |
192 | bool |
193 | runOnFunction(Function &F, |
194 | SmallDenseMap<GlobalVariable *, GlobalVariable *> &RefMapping); |
195 | }; |
196 | |
197 | } // end anonymous namespace |
198 | |
199 | char WindowsSecureHotPatching::ID = 0; |
200 | |
201 | INITIALIZE_PASS(WindowsSecureHotPatching, "windows-secure-hot-patch" , |
202 | "Mark functions for Windows hot patch support" , false, false) |
203 | ModulePass *llvm::createWindowsSecureHotPatchingPass() { |
204 | return new WindowsSecureHotPatching(); |
205 | } |
206 | |
207 | // Find functions marked with Attribute::MarkedForWindowsHotPatching and modify |
208 | // their code (if necessary) to account for accesses to global variables. |
209 | // |
210 | // This runs during doInitialization() instead of runOnModule() because it needs |
211 | // to run before CodeViewDebug::collectGlobalVariableInfo(). |
212 | bool WindowsSecureHotPatching::doInitialization(Module &M) { |
213 | // The front end may have already marked functions for hot-patching. However, |
214 | // we also allow marking functions by passing -ms-hotpatch-functions-file or |
215 | // -ms-hotpatch-functions-list directly to LLVM. This allows hot-patching to |
216 | // work with languages that have not yet updated their front-ends. |
217 | if (!LLVMMSSecureHotPatchFunctionsFile.empty() || |
218 | !LLVMMSSecureHotPatchFunctionsList.empty()) { |
219 | std::vector<std::string> HotPatchFunctionsList; |
220 | |
221 | if (!LLVMMSSecureHotPatchFunctionsFile.empty()) { |
222 | auto BufOrErr = MemoryBuffer::getFile(Filename: LLVMMSSecureHotPatchFunctionsFile); |
223 | if (BufOrErr) { |
224 | const MemoryBuffer &FileBuffer = **BufOrErr; |
225 | for (line_iterator I(FileBuffer.getMemBufferRef(), true), E; I != E; |
226 | ++I) |
227 | HotPatchFunctionsList.push_back(x: std::string{*I}); |
228 | } else { |
229 | M.getContext().diagnose(DI: DiagnosticInfoGeneric{ |
230 | Twine("failed to open hotpatch functions file " |
231 | "(--ms-hotpatch-functions-file): " ) + |
232 | LLVMMSSecureHotPatchFunctionsFile + Twine(" : " ) + |
233 | BufOrErr.getError().message()}); |
234 | } |
235 | } |
236 | |
237 | if (!LLVMMSSecureHotPatchFunctionsList.empty()) |
238 | for (const auto &FuncName : LLVMMSSecureHotPatchFunctionsList) |
239 | HotPatchFunctionsList.push_back(x: FuncName); |
240 | |
241 | // Build a set for quick lookups. This points into HotPatchFunctionsList, so |
242 | // HotPatchFunctionsList must live longer than HotPatchFunctionsSet. |
243 | SmallSet<StringRef, 16> HotPatchFunctionsSet; |
244 | for (const auto &FuncName : HotPatchFunctionsList) |
245 | HotPatchFunctionsSet.insert(V: StringRef{FuncName}); |
246 | |
247 | // Iterate through all of the functions and check whether they need to be |
248 | // marked for hotpatching using the list provided directly to LLVM. |
249 | for (auto &F : M.functions()) { |
250 | // Ignore declarations that are not definitions. |
251 | if (F.isDeclarationForLinker()) |
252 | continue; |
253 | |
254 | if (HotPatchFunctionsSet.contains(V: F.getName())) |
255 | F.addFnAttr(Kind: "marked_for_windows_hot_patching" ); |
256 | } |
257 | } |
258 | |
259 | SmallDenseMap<GlobalVariable *, GlobalVariable *> RefMapping; |
260 | bool MadeChanges = false; |
261 | for (auto &F : M.functions()) { |
262 | if (F.hasFnAttribute(Kind: "marked_for_windows_hot_patching" )) { |
263 | if (runOnFunction(F, RefMapping)) |
264 | MadeChanges = true; |
265 | } |
266 | } |
267 | return MadeChanges; |
268 | } |
269 | |
270 | static bool TypeContainsPointers(Type *ty) { |
271 | switch (ty->getTypeID()) { |
272 | case Type::PointerTyID: |
273 | return true; |
274 | |
275 | case Type::ArrayTyID: |
276 | return TypeContainsPointers(ty: ty->getArrayElementType()); |
277 | |
278 | case Type::StructTyID: { |
279 | unsigned NumElements = ty->getStructNumElements(); |
280 | for (unsigned I = 0; I < NumElements; ++I) { |
281 | if (TypeContainsPointers(ty: ty->getStructElementType(N: I))) { |
282 | return true; |
283 | } |
284 | } |
285 | return false; |
286 | } |
287 | |
288 | default: |
289 | return false; |
290 | } |
291 | } |
292 | |
293 | // Returns true if GV needs redirection through a __ref_* variable. |
294 | static bool globalVariableNeedsRedirect(GlobalVariable *GV) { |
295 | // If a global variable is explictly marked as allowing access in hot-patched |
296 | // functions, then do not redirect it. |
297 | if (GV->hasAttribute(Kind: "allow_direct_access_in_hot_patch_function" )) |
298 | return false; |
299 | |
300 | // If the global variable is not a constant, then we want to redirect it. |
301 | if (!GV->isConstant()) { |
302 | if (GV->getName().starts_with(Prefix: "??_R" )) { |
303 | // This is the name mangling prefix that MSVC uses for RTTI data. |
304 | // Clang is currently generating RTTI data that is marked non-constant. |
305 | // We override that and treat it like it is constant. |
306 | return false; |
307 | } |
308 | |
309 | // In general, if a global variable is not a constant, then redirect it. |
310 | return true; |
311 | } |
312 | |
313 | // If the type of GV cannot contain pointers, then it cannot point to |
314 | // other global variables. In this case, there is no need for redirects. |
315 | // For example, string literals do not contain pointers. |
316 | return TypeContainsPointers(ty: GV->getValueType()); |
317 | } |
318 | |
319 | // Get or create a new global variable that points to the old one and whose |
320 | // name begins with `__ref_`. |
321 | // |
322 | // In hot-patched images, the __ref_* variables point to global variables in |
323 | // the original (unpatched) image. Hot-patched functions in the hot-patch |
324 | // image use these __ref_* variables to access global variables. This ensures |
325 | // that all code (both unpatched and patched) is using the same instances of |
326 | // global variables. |
327 | // |
328 | // The Windows hot-patch infrastructure handles modifying these __ref_* |
329 | // variables. By default, they are initialized with pointers to the equivalent |
330 | // global variables, so when a hot-patch module is loaded *as* a base image |
331 | // (such as after a system reboot), hot-patch functions will access the |
332 | // instances of global variables that are compiled into the hot-patch image. |
333 | // This is the desired outcome, since in this situation (normal boot) the |
334 | // hot-patch image *is* the base image. |
335 | // |
336 | // When we create the GlobalVariable for the __ref_* variable, we must create |
337 | // it as a *non-constant* global variable. The __ref_* pointers will not change |
338 | // during the runtime of the program, so it is tempting to think that they |
339 | // should be constant. However, they still need to be updateable by the |
340 | // hot-patching infrastructure. Also, if the GlobalVariable is created as a |
341 | // constant, then the LLVM optimizer will assume that it can dereference the |
342 | // definition of the __ref_* variable at compile time, which defeats the |
343 | // purpose of the indirection (pointer). |
344 | // |
345 | // The RefMapping table spans the entire module, not just a single function. |
346 | static GlobalVariable *getOrCreateRefVariable( |
347 | Function &F, SmallDenseMap<GlobalVariable *, GlobalVariable *> &RefMapping, |
348 | GlobalVariable *GV) { |
349 | GlobalVariable *&ReplaceWithRefGV = RefMapping.try_emplace(Key: GV).first->second; |
350 | if (ReplaceWithRefGV != nullptr) { |
351 | // We have already created a __ref_* pointer for this GlobalVariable. |
352 | return ReplaceWithRefGV; |
353 | } |
354 | |
355 | Module *M = F.getParent(); |
356 | |
357 | const DISubprogram *Subprogram = F.getSubprogram(); |
358 | DICompileUnit *Unit = Subprogram != nullptr ? Subprogram->getUnit() : nullptr; |
359 | DIFile *File = Subprogram != nullptr ? Subprogram->getFile() : nullptr; |
360 | DIBuilder DebugInfo{*F.getParent(), true, Unit}; |
361 | |
362 | auto PtrTy = PointerType::get(C&: M->getContext(), AddressSpace: 0); |
363 | |
364 | Constant *AddrOfOldGV = |
365 | ConstantExpr::getGetElementPtr(Ty: PtrTy, C: GV, IdxList: ArrayRef<Value *>{}); |
366 | |
367 | GlobalVariable *RefGV = |
368 | new GlobalVariable(*M, PtrTy, false, GlobalValue::LinkOnceAnyLinkage, |
369 | AddrOfOldGV, Twine("__ref_" ).concat(Suffix: GV->getName()), |
370 | nullptr, GlobalVariable::NotThreadLocal); |
371 | |
372 | // Create debug info for the replacement global variable. |
373 | DataLayout Layout = M->getDataLayout(); |
374 | DIType *DebugType = DebugInfo.createPointerType( |
375 | PointeeTy: nullptr, SizeInBits: Layout.getTypeSizeInBits(Ty: GV->getValueType())); |
376 | DIGlobalVariableExpression *GVE = DebugInfo.createGlobalVariableExpression( |
377 | Context: Unit, Name: RefGV->getName(), LinkageName: StringRef{}, File, |
378 | /*LineNo*/ 0, Ty: DebugType, |
379 | /*IsLocalToUnit*/ false); |
380 | RefGV->addDebugInfo(GV: GVE); |
381 | |
382 | // Store the __ref_* in RefMapping so that future calls use the same RefGV. |
383 | ReplaceWithRefGV = RefGV; |
384 | |
385 | return RefGV; |
386 | } |
387 | |
388 | // Given a ConstantExpr, this searches for GlobalVariable references within |
389 | // the expression tree. If found, it will generate instructions and will |
390 | // return a non-null Value* that points to the new root instruction. |
391 | // |
392 | // If C does not contain any GlobalVariable references, this returns nullptr. |
393 | // |
394 | // If this function creates new instructions, then it will insert them |
395 | // before InsertionPoint. |
396 | static Value *rewriteGlobalVariablesInConstant( |
397 | Constant *C, SmallDenseMap<GlobalVariable *, Value *> &GVLoadMap, |
398 | IRBuilder<> &IRBuilderAtEntry) { |
399 | if (C->getValueID() == Value::GlobalVariableVal) { |
400 | GlobalVariable *GV = cast<GlobalVariable>(Val: C); |
401 | if (globalVariableNeedsRedirect(GV)) { |
402 | return GVLoadMap.at(Val: GV); |
403 | } else { |
404 | return nullptr; |
405 | } |
406 | } |
407 | |
408 | // Scan the operands of this expression. |
409 | |
410 | SmallVector<Value *, 8> ReplacedValues; |
411 | bool ReplacedAnyOperands = false; |
412 | |
413 | unsigned NumOperands = C->getNumOperands(); |
414 | for (unsigned OpIndex = 0; OpIndex < NumOperands; ++OpIndex) { |
415 | Value *OldValue = C->getOperand(i: OpIndex); |
416 | Value *ReplacedValue = nullptr; |
417 | if (Constant *OldConstant = dyn_cast<Constant>(Val: OldValue)) { |
418 | ReplacedValue = rewriteGlobalVariablesInConstant(C: OldConstant, GVLoadMap, |
419 | IRBuilderAtEntry); |
420 | } |
421 | // Do not use short-circuiting, here. We need to traverse the whole tree. |
422 | ReplacedAnyOperands |= ReplacedValue != nullptr; |
423 | ReplacedValues.push_back(Elt: ReplacedValue); |
424 | } |
425 | |
426 | // If none of our operands were replaced, then don't rewrite this expression. |
427 | if (!ReplacedAnyOperands) { |
428 | return nullptr; |
429 | } |
430 | |
431 | // We need to rewrite this expression. Convert this constant expression |
432 | // to an instruction, then replace any operands as needed. |
433 | Instruction *NewInst = cast<ConstantExpr>(Val: C)->getAsInstruction(); |
434 | for (unsigned OpIndex = 0; OpIndex < NumOperands; ++OpIndex) { |
435 | Value *ReplacedValue = ReplacedValues[OpIndex]; |
436 | if (ReplacedValue != nullptr) { |
437 | NewInst->setOperand(i: OpIndex, Val: ReplacedValue); |
438 | } |
439 | } |
440 | |
441 | // Insert the new instruction before the reference instruction. |
442 | IRBuilderAtEntry.Insert(I: NewInst); |
443 | |
444 | return NewInst; |
445 | } |
446 | |
447 | static bool searchConstantExprForGlobalVariables( |
448 | Value *V, SmallDenseMap<GlobalVariable *, Value *> &GVLoadMap, |
449 | SmallVector<GlobalVariableUse> &GVUses) { |
450 | |
451 | SmallVector<Value *, 8> ReplacedOperands; |
452 | |
453 | if (GlobalVariable *GV = dyn_cast<GlobalVariable>(Val: V)) { |
454 | if (globalVariableNeedsRedirect(GV)) { |
455 | GVLoadMap[GV] = nullptr; |
456 | return true; |
457 | } else { |
458 | return false; |
459 | } |
460 | } |
461 | |
462 | if (User *U = dyn_cast<User>(Val: V)) { |
463 | unsigned NumOperands = U->getNumOperands(); |
464 | bool FoundAny = false; |
465 | for (unsigned OpIndex = 0; OpIndex < NumOperands; ++OpIndex) { |
466 | Value *Op = U->getOperand(i: OpIndex); |
467 | // Do not use short-circuiting, here. We need to traverse the whole tree. |
468 | FoundAny |= searchConstantExprForGlobalVariables(V: Op, GVLoadMap, GVUses); |
469 | } |
470 | return FoundAny; |
471 | } else { |
472 | return false; |
473 | } |
474 | } |
475 | |
476 | // Processes a function that is marked for hot-patching. |
477 | // |
478 | // If a function is marked for hot-patching, we generate an S_HOTPATCHFUNC |
479 | // CodeView debug symbol. Tools that generate hot-patches look for |
480 | // S_HOTPATCHFUNC in final PDBs so that they can find functions that have been |
481 | // hot-patched and so that they can distinguish hot-patched functions from |
482 | // non-hot-patched functions. |
483 | // |
484 | // Also, in functions that are hot-patched, we must indirect all access to |
485 | // (mutable) global variables through a pointer. This pointer may point into the |
486 | // unpatched ("base") binary or may point into the patched image, depending on |
487 | // whether a hot-patch was loaded as a patch or as a base image. These |
488 | // indirections go through a new global variable, named `__ref_<Foo>` where |
489 | // `<Foo>` is the original symbol name of the global variable. |
490 | // |
491 | // This function handles rewriting accesses to global variables, but the |
492 | // generation of S_HOTPATCHFUNC occurs in |
493 | // CodeViewDebug::emitHotPatchInformation(). |
494 | // |
495 | // Returns true if any global variable references were found and rewritten. |
496 | bool WindowsSecureHotPatching::runOnFunction( |
497 | Function &F, |
498 | SmallDenseMap<GlobalVariable *, GlobalVariable *> &RefMapping) { |
499 | // Scan the function for references to global variables. If we find such a |
500 | // reference, create (if necessary) the __ref_* variable, then add an entry |
501 | // to the GVUses table. |
502 | // |
503 | // We ignore references to global variables if the variable is marked with |
504 | // AllowDirectAccessInHotPatchFunction. |
505 | |
506 | SmallDenseMap<GlobalVariable *, Value *> GVLoadMap; |
507 | SmallVector<GlobalVariableUse> GVUses; |
508 | |
509 | for (auto &I : instructions(F)) { |
510 | unsigned NumOperands = I.getNumOperands(); |
511 | for (unsigned OpIndex = 0; OpIndex < NumOperands; ++OpIndex) { |
512 | Value *V = I.getOperand(i: OpIndex); |
513 | |
514 | bool FoundAnyGVUses = false; |
515 | |
516 | switch (V->getValueID()) { |
517 | case Value::GlobalVariableVal: { |
518 | // Discover all uses of GlobalVariable, these will need to be replaced. |
519 | GlobalVariable *GV = cast<GlobalVariable>(Val: V); |
520 | if (globalVariableNeedsRedirect(GV)) { |
521 | GVLoadMap.insert(KV: std::make_pair(x&: GV, y: nullptr)); |
522 | FoundAnyGVUses = true; |
523 | } |
524 | break; |
525 | } |
526 | |
527 | case Value::ConstantExprVal: { |
528 | ConstantExpr *CE = cast<ConstantExpr>(Val: V); |
529 | if (searchConstantExprForGlobalVariables(V: CE, GVLoadMap, GVUses)) { |
530 | FoundAnyGVUses = true; |
531 | } |
532 | break; |
533 | } |
534 | |
535 | default: |
536 | break; |
537 | } |
538 | |
539 | if (FoundAnyGVUses) { |
540 | GVUses.push_back(Elt: GlobalVariableUse{.User: &I, .Op: OpIndex}); |
541 | } |
542 | } |
543 | } |
544 | |
545 | // If this function did not reference any global variables then we have no |
546 | // work to do. Most functions do not access global variables. |
547 | if (GVUses.empty()) { |
548 | return false; |
549 | } |
550 | |
551 | // We know that there is at least one instruction that needs to be rewritten. |
552 | // Generate a Load instruction for each unique GlobalVariable used by this |
553 | // function. The Load instructions are inserted at the beginning of the |
554 | // entry block. Since entry blocks cannot contain PHI instructions, there is |
555 | // no need to skip PHI instructions. |
556 | |
557 | // We use a single IRBuilder for inserting Load instructions as well as the |
558 | // constants that we convert to instructions. Because constants do not |
559 | // depend on any dynamic values (they're constant, after all!), it is safe |
560 | // to move them to the start of entry BB. |
561 | |
562 | auto &EntryBlock = F.getEntryBlock(); |
563 | IRBuilder<> IRBuilderAtEntry(&EntryBlock, EntryBlock.begin()); |
564 | |
565 | for (auto &[GV, LoadValue] : GVLoadMap) { |
566 | assert(LoadValue == nullptr); |
567 | GlobalVariable *RefGV = getOrCreateRefVariable(F, RefMapping, GV); |
568 | LoadValue = IRBuilderAtEntry.CreateLoad(Ty: RefGV->getValueType(), Ptr: RefGV); |
569 | } |
570 | |
571 | const DISubprogram *Subprogram = F.getSubprogram(); |
572 | DICompileUnit *Unit = Subprogram != nullptr ? Subprogram->getUnit() : nullptr; |
573 | DIBuilder DebugInfo{*F.getParent(), true, Unit}; |
574 | |
575 | // Go back to the instructions and rewrite their uses of GlobalVariable. |
576 | // Because a ConstantExpr can be a tree, it may reference more than one |
577 | // GlobalVariable. |
578 | |
579 | for (auto &GVUse : GVUses) { |
580 | Value *OldOperandValue = GVUse.User->getOperand(i: GVUse.Op); |
581 | Value *NewOperandValue; |
582 | |
583 | switch (OldOperandValue->getValueID()) { |
584 | case Value::GlobalVariableVal: { |
585 | // This is easy. Look up the replacement value and store the operand. |
586 | Value *OperandValue = GVUse.User->getOperand(i: GVUse.Op); |
587 | GlobalVariable *GV = cast<GlobalVariable>(Val: OperandValue); |
588 | NewOperandValue = GVLoadMap.at(Val: GV); |
589 | break; |
590 | } |
591 | |
592 | case Value::ConstantExprVal: { |
593 | // Walk the recursive tree of the ConstantExpr. If we find a |
594 | // GlobalVariable then replace it with the loaded value and rewrite |
595 | // the ConstantExpr to an Instruction and insert it before the |
596 | // current instruction. |
597 | Value *OperandValue = GVUse.User->getOperand(i: GVUse.Op); |
598 | ConstantExpr *CE = cast<ConstantExpr>(Val: OperandValue); |
599 | NewOperandValue = |
600 | rewriteGlobalVariablesInConstant(C: CE, GVLoadMap, IRBuilderAtEntry); |
601 | assert(NewOperandValue != nullptr); |
602 | break; |
603 | } |
604 | |
605 | default: |
606 | // We should only ever get here because a GVUse was created in the first |
607 | // pass, and this only happens for GlobalVariableVal and ConstantExprVal. |
608 | llvm_unreachable_internal( |
609 | msg: "unexpected Value in second pass of hot-patching" ); |
610 | break; |
611 | } |
612 | |
613 | GVUse.User->setOperand(i: GVUse.Op, Val: NewOperandValue); |
614 | } |
615 | |
616 | return true; |
617 | } |
618 | |