1 //===- X86DiscriminateMemOps.cpp - Unique IDs for Mem Ops -----------------===//
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 /// This pass aids profile-driven cache prefetch insertion by ensuring all
10 /// instructions that have a memory operand are distinguishible from each other.
11 ///
12 //===----------------------------------------------------------------------===//
13
14 #include "X86.h"
15 #include "X86InstrBuilder.h"
16 #include "X86InstrInfo.h"
17 #include "X86MachineFunctionInfo.h"
18 #include "X86Subtarget.h"
19 #include "llvm/CodeGen/MachineFunctionPass.h"
20 #include "llvm/CodeGen/MachineModuleInfo.h"
21 #include "llvm/IR/DebugInfoMetadata.h"
22 #include "llvm/ProfileData/SampleProf.h"
23 #include "llvm/ProfileData/SampleProfReader.h"
24 #include "llvm/Support/Debug.h"
25 #include "llvm/Transforms/IPO/SampleProfile.h"
26 using namespace llvm;
27
28 #define DEBUG_TYPE "x86-discriminate-memops"
29
30 static cl::opt<bool> EnableDiscriminateMemops(
31 DEBUG_TYPE, cl::init(false),
32 cl::desc("Generate unique debug info for each instruction with a memory "
33 "operand. Should be enabled for profile-driven cache prefetching, "
34 "both in the build of the binary being profiled, as well as in "
35 "the build of the binary consuming the profile."),
36 cl::Hidden);
37
38 static cl::opt<bool> BypassPrefetchInstructions(
39 "x86-bypass-prefetch-instructions", cl::init(true),
40 cl::desc("When discriminating instructions with memory operands, ignore "
41 "prefetch instructions. This ensures the other memory operand "
42 "instructions have the same identifiers after inserting "
43 "prefetches, allowing for successive insertions."),
44 cl::Hidden);
45
46 namespace {
47
48 using Location = std::pair<StringRef, unsigned>;
49
diToLocation(const DILocation * Loc)50 Location diToLocation(const DILocation *Loc) {
51 return std::make_pair(Loc->getFilename(), Loc->getLine());
52 }
53
54 /// Ensure each instruction having a memory operand has a distinct <LineNumber,
55 /// Discriminator> pair.
updateDebugInfo(MachineInstr * MI,const DILocation * Loc)56 void updateDebugInfo(MachineInstr *MI, const DILocation *Loc) {
57 DebugLoc DL(Loc);
58 MI->setDebugLoc(DL);
59 }
60
61 class X86DiscriminateMemOps : public MachineFunctionPass {
62 bool runOnMachineFunction(MachineFunction &MF) override;
getPassName() const63 StringRef getPassName() const override {
64 return "X86 Discriminate Memory Operands";
65 }
66
67 public:
68 static char ID;
69
70 /// Default construct and initialize the pass.
71 X86DiscriminateMemOps();
72 };
73
IsPrefetchOpcode(unsigned Opcode)74 bool IsPrefetchOpcode(unsigned Opcode) {
75 return Opcode == X86::PREFETCHNTA || Opcode == X86::PREFETCHT0 ||
76 Opcode == X86::PREFETCHT1 || Opcode == X86::PREFETCHT2;
77 }
78 } // end anonymous namespace
79
80 //===----------------------------------------------------------------------===//
81 // Implementation
82 //===----------------------------------------------------------------------===//
83
84 char X86DiscriminateMemOps::ID = 0;
85
86 /// Default construct and initialize the pass.
X86DiscriminateMemOps()87 X86DiscriminateMemOps::X86DiscriminateMemOps() : MachineFunctionPass(ID) {}
88
runOnMachineFunction(MachineFunction & MF)89 bool X86DiscriminateMemOps::runOnMachineFunction(MachineFunction &MF) {
90 if (!EnableDiscriminateMemops)
91 return false;
92
93 DISubprogram *FDI = MF.getFunction().getSubprogram();
94 if (!FDI || !FDI->getUnit()->getDebugInfoForProfiling())
95 return false;
96
97 // Have a default DILocation, if we find instructions with memops that don't
98 // have any debug info.
99 const DILocation *ReferenceDI =
100 DILocation::get(FDI->getContext(), FDI->getLine(), 0, FDI);
101 assert(ReferenceDI && "ReferenceDI should not be nullptr");
102 DenseMap<Location, unsigned> MemOpDiscriminators;
103 MemOpDiscriminators[diToLocation(ReferenceDI)] = 0;
104
105 // Figure out the largest discriminator issued for each Location. When we
106 // issue new discriminators, we can thus avoid issuing discriminators
107 // belonging to instructions that don't have memops. This isn't a requirement
108 // for the goals of this pass, however, it avoids unnecessary ambiguity.
109 for (auto &MBB : MF) {
110 for (auto &MI : MBB) {
111 const auto &DI = MI.getDebugLoc();
112 if (!DI)
113 continue;
114 if (BypassPrefetchInstructions && IsPrefetchOpcode(MI.getDesc().Opcode))
115 continue;
116 Location Loc = diToLocation(DI);
117 MemOpDiscriminators[Loc] =
118 std::max(MemOpDiscriminators[Loc], DI->getBaseDiscriminator());
119 }
120 }
121
122 // Keep track of the discriminators seen at each Location. If an instruction's
123 // DebugInfo has a Location and discriminator we've already seen, replace its
124 // discriminator with a new one, to guarantee uniqueness.
125 DenseMap<Location, DenseSet<unsigned>> Seen;
126
127 bool Changed = false;
128 for (auto &MBB : MF) {
129 for (auto &MI : MBB) {
130 if (X86II::getMemoryOperandNo(MI.getDesc().TSFlags) < 0)
131 continue;
132 if (BypassPrefetchInstructions && IsPrefetchOpcode(MI.getDesc().Opcode))
133 continue;
134 const DILocation *DI = MI.getDebugLoc();
135 bool HasDebug = DI;
136 if (!HasDebug) {
137 DI = ReferenceDI;
138 }
139 Location L = diToLocation(DI);
140 DenseSet<unsigned> &Set = Seen[L];
141 const std::pair<DenseSet<unsigned>::iterator, bool> TryInsert =
142 Set.insert(DI->getBaseDiscriminator());
143 if (!TryInsert.second || !HasDebug) {
144 unsigned BF, DF, CI = 0;
145 DILocation::decodeDiscriminator(DI->getDiscriminator(), BF, DF, CI);
146 Optional<unsigned> EncodedDiscriminator = DILocation::encodeDiscriminator(
147 MemOpDiscriminators[L] + 1, DF, CI);
148
149 if (!EncodedDiscriminator) {
150 // FIXME(mtrofin): The assumption is that this scenario is infrequent/OK
151 // not to support. If evidence points otherwise, we can explore synthesizeing
152 // unique DIs by adding fake line numbers, or by constructing 64 bit
153 // discriminators.
154 LLVM_DEBUG(dbgs() << "Unable to create a unique discriminator "
155 "for instruction with memory operand in: "
156 << DI->getFilename() << " Line: " << DI->getLine()
157 << " Column: " << DI->getColumn()
158 << ". This is likely due to a large macro expansion. \n");
159 continue;
160 }
161 // Since we were able to encode, bump the MemOpDiscriminators.
162 ++MemOpDiscriminators[L];
163 DI = DI->cloneWithDiscriminator(*EncodedDiscriminator);
164 assert(DI && "DI should not be nullptr");
165 updateDebugInfo(&MI, DI);
166 Changed = true;
167 std::pair<DenseSet<unsigned>::iterator, bool> MustInsert =
168 Set.insert(DI->getBaseDiscriminator());
169 (void)MustInsert; // Silence warning in release build.
170 assert(MustInsert.second && "New discriminator shouldn't be present in set");
171 }
172
173 // Bump the reference DI to avoid cramming discriminators on line 0.
174 // FIXME(mtrofin): pin ReferenceDI on blocks or first instruction with DI
175 // in a block. It's more consistent than just relying on the last memop
176 // instruction we happened to see.
177 ReferenceDI = DI;
178 }
179 }
180 return Changed;
181 }
182
createX86DiscriminateMemOpsPass()183 FunctionPass *llvm::createX86DiscriminateMemOpsPass() {
184 return new X86DiscriminateMemOps();
185 }
186