1//===- DynamicDebugging.cpp - Dynamic Debugging utils --------------------===//
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#include "llvm/Transforms/Utils/DynamicDebugging.h"
10#include "llvm/IR/Module.h"
11#include "llvm/Transforms/Utils/Cloning.h"
12#include "llvm/Transforms/Utils/ModuleUtils.h"
13#include "llvm/Transforms/Utils/ValueMapper.h"
14
15using namespace llvm;
16
17std::unique_ptr<Module>
18llvm::prepareForDynamicDebugging(Module *M, StringRef PromotionSuffix) {
19 using namespace llvm;
20 assert(M->getNamedMetadata("llvm.dbg.cu") &&
21 "Expected module with debug info");
22
23 auto ShouldPromoteGlobal = [](const GlobalValue &GV) {
24 if (!GV.hasLocalLinkage())
25 return false;
26
27 // Local symbols in a comdat shouldn't be promoted either.
28 // This can happen with (at least) __cxx_global_var_init (which is local
29 // and may initialize an ODR-weak global variable).
30 if (GV.hasComdat())
31 return false;
32
33 return true;
34 };
35
36 // Clone functions definitions only - CloneModule will clone data definitions
37 // as declarations. We rename these and explicitly set their linkage later.
38 auto ShouldCloneDefinition = [](const GlobalValue *GV) {
39 return isa<Function>(Val: GV);
40 };
41 ValueToValueMapTy VMap;
42 std::unique_ptr<Module> UnoptM = CloneModule(M: *M, VMap, ShouldCloneDefinition);
43
44 // Insert declarations into Inner that point to Outer, apply attributes to
45 // Outer functions.
46 DenseMap<Function *, Function *> OuterDefToInnerDecl;
47 for (Function &OuterDef : M->functions()) {
48 if (OuterDef.isDeclaration())
49 continue;
50
51 // Find the Inner version of Outer's function.
52 Function *InnerDef = cast<Function>(Val&: VMap[&OuterDef]);
53
54 // Apply some attributes to both Inner and Outer defs.
55 {
56 // Unoptimized module wants no inlining at all.
57 InnerDef->addFnAttr(Kind: Attribute::NoInline);
58 InnerDef->removeFnAttr(Kind: Attribute::AlwaysInline);
59
60 // Apply optnone, remove clashing attributes.
61 InnerDef->addFnAttr(Kind: Attribute::OptimizeNone);
62 InnerDef->removeFnAttr(Kind: Attribute::OptimizeForSize);
63 InnerDef->removeFnAttr(Kind: Attribute::MinSize);
64
65 // Add attributes to the outer-object functions to ensure they're
66 // always patchable.
67 //
68 // The debugger patches outer (optimized) functions to redirect to inner
69 // (unoptimized) functions. Block interprocedural analysis to ensure
70 // the two function implementations share the same interface.
71 OuterDef.addFnAttr(Kind: Attribute::NoIPA);
72 // Outlining creates specialized functions in the outer (optimized)
73 // module without an inner (unoptimized) equivalent, meaning the debugger
74 // can't switch to an unoptimized version, so block outlining.
75 OuterDef.addFnAttr(Kind: Attribute::NoOutline);
76 // TODO: Add patch bytes size/value for other targets.
77 if (M->getTargetTriple().isX86_64()) {
78 OuterDef.addFnAttr(Kind: "tail-pad-to-size", Val: "5");
79 OuterDef.addFnAttr(Kind: "tail-pad-value", Val: "144"); // 0x90
80 }
81 }
82
83 // Apply COMDAT grouping to the clone if OuterDef is in one.
84 if (OuterDef.hasComdat()) {
85 std::string NewComdat =
86 Twine("__dyndbg." + OuterDef.getComdat()->getName()).str();
87 Comdat *C = M->getOrInsertComdat(Name: NewComdat);
88 C->setSelectionKind(OuterDef.getComdat()->getSelectionKind());
89 InnerDef->setComdat(C);
90 }
91
92 // Rename Inner's copy and set appropriate linkage depending on whether
93 // it'll get promoted in Outer or not.
94 if (ShouldPromoteGlobal(OuterDef)) {
95 InnerDef->setName("__dyndbg." + InnerDef->getName() + PromotionSuffix);
96 InnerDef->setLinkage(GlobalValue::ExternalLinkage);
97 InnerDef->setVisibility(GlobalValue::HiddenVisibility);
98 } else {
99 InnerDef->setName("__dyndbg." + InnerDef->getName());
100 InnerDef->setLinkage(OuterDef.getLinkage());
101 InnerDef->setVisibility(OuterDef.getVisibility());
102 }
103
104 // Create Inner's external reference to Outer's version.
105 Function *InnerDeclOfOuterDef = Function::Create(
106 Ty: cast<FunctionType>(Val: OuterDef.getValueType()), Linkage: OuterDef.getLinkage(),
107 AddrSpace: OuterDef.getAddressSpace(), N: OuterDef.getName(), M: UnoptM.get());
108 InnerDeclOfOuterDef->copyAttributesFrom(Src: &OuterDef);
109 // Re-set linkage and visibility after copyAttributesFrom.
110 InnerDeclOfOuterDef->setLinkage(GlobalValue::ExternalLinkage);
111 InnerDeclOfOuterDef->setPersonalityFn(nullptr);
112
113 // Replace Inner uses of function with that external reference.
114 InnerDef->replaceAllUsesWith(V: InnerDeclOfOuterDef);
115
116 VMap[&OuterDef] = InnerDeclOfOuterDef;
117 }
118
119 // Add Outer aliases for globals with internal linkage, adding
120 // ".dyndbg.<TU-unique-hash>" suffix. Update Inner's external references to
121 // these promoted functions to use their new names.
122 SmallVector<GlobalValue *> GlobalsToPreserve;
123 for (GlobalValue &GV : M->global_values()) {
124 // If the global is used but may be discarded after optimizations
125 // (e.g. inlining) then ensure it's marked as compiler-used to prevent
126 // that. It may be referenced from the inner module.
127 if (GV.isDiscardableIfUnused()) {
128 if (GV.getNumUses()) {
129 GlobalsToPreserve.push_back(Elt: &GV);
130 // Name unnamed globals. Compiler-used expects named globals only.
131 if (GV.getName().empty())
132 GV.setName("__unnamed");
133 } else {
134 // No uses, so the inner module doesn't need a reference nor do we need
135 // to produce an alias.
136 // Remove the inner module reference.
137 auto GVAndUnoptPair = VMap.find(Val: &GV);
138 assert(GVAndUnoptPair != VMap.end() && "Unmapped global?");
139 // Delete the external reference - VMap should only contain mappings to
140 // those declarations now.
141 assert(cast<GlobalValue>(GVAndUnoptPair->second)->isDeclaration() &&
142 "expected only declarations in VMap now");
143 cast<GlobalValue>(Val&: GVAndUnoptPair->second)->eraseFromParent();
144 GVAndUnoptPair->second = nullptr;
145 // Nothing else to do for this global.
146 continue;
147 }
148 }
149
150 if (!ShouldPromoteGlobal(GV))
151 continue;
152
153 // We need external aliases with a mangled name and hidden visibility.
154 auto *Alias = GlobalAlias::create(Linkage: GlobalValue::ExternalLinkage,
155 Name: GV.getName() + PromotionSuffix, Aliasee: &GV);
156 Alias->setVisibility(GlobalValue::HiddenVisibility);
157
158 // Update the Inner external reference that corresponds to the promoted
159 // Outer global (created just now as an alias in opt) to reference the new
160 // alias.
161 GlobalValue *UnoptGV = cast<GlobalValue>(Val&: VMap[&GV]);
162 UnoptGV->setName(Alias->getName());
163 UnoptGV->setVisibility(GlobalValue::HiddenVisibility);
164 assert(UnoptGV->getLinkage() == GlobalValue::ExternalLinkage &&
165 "Expected ExternalLinkage from CloneModule or inserted decl");
166 }
167
168 // Preserve functions that may be discarded after optimizing away call sites
169 // (e.g. ODR-weak). Another desirable effect of this is that it prevents
170 // GlobalOpt promoting the alias. If the function-preservation mechanism
171 // changes in the future GlobalOpt alias promotion must be handled another
172 // way.
173 appendToCompilerUsed(M&: *M, Values: GlobalsToPreserve);
174
175 return UnoptM;
176}