1 //===- X86DiscriminateMemOps.cpp - Unique IDs for Mem Ops -----------------===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 ///
10 /// This pass aids profile-driven cache prefetch insertion by ensuring all
11 /// instructions that have a memory operand are distinguishible from each other.
12 ///
13 //===----------------------------------------------------------------------===//
14
15 #include "X86.h"
16 #include "X86InstrBuilder.h"
17 #include "X86InstrInfo.h"
18 #include "X86MachineFunctionInfo.h"
19 #include "X86Subtarget.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-drived 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 namespace {
39
40 using Location = std::pair<StringRef, unsigned>;
41
diToLocation(const DILocation * Loc)42 Location diToLocation(const DILocation *Loc) {
43 return std::make_pair(Loc->getFilename(), Loc->getLine());
44 }
45
46 /// Ensure each instruction having a memory operand has a distinct <LineNumber,
47 /// Discriminator> pair.
updateDebugInfo(MachineInstr * MI,const DILocation * Loc)48 void updateDebugInfo(MachineInstr *MI, const DILocation *Loc) {
49 DebugLoc DL(Loc);
50 MI->setDebugLoc(DL);
51 }
52
53 class X86DiscriminateMemOps : public MachineFunctionPass {
54 bool runOnMachineFunction(MachineFunction &MF) override;
getPassName() const55 StringRef getPassName() const override {
56 return "X86 Discriminate Memory Operands";
57 }
58
59 public:
60 static char ID;
61
62 /// Default construct and initialize the pass.
63 X86DiscriminateMemOps();
64 };
65
66 } // end anonymous namespace
67
68 //===----------------------------------------------------------------------===//
69 // Implementation
70 //===----------------------------------------------------------------------===//
71
72 char X86DiscriminateMemOps::ID = 0;
73
74 /// Default construct and initialize the pass.
X86DiscriminateMemOps()75 X86DiscriminateMemOps::X86DiscriminateMemOps() : MachineFunctionPass(ID) {}
76
runOnMachineFunction(MachineFunction & MF)77 bool X86DiscriminateMemOps::runOnMachineFunction(MachineFunction &MF) {
78 if (!EnableDiscriminateMemops)
79 return false;
80
81 DISubprogram *FDI = MF.getFunction().getSubprogram();
82 if (!FDI || !FDI->getUnit()->getDebugInfoForProfiling())
83 return false;
84
85 // Have a default DILocation, if we find instructions with memops that don't
86 // have any debug info.
87 const DILocation *ReferenceDI =
88 DILocation::get(FDI->getContext(), FDI->getLine(), 0, FDI);
89
90 DenseMap<Location, unsigned> MemOpDiscriminators;
91 MemOpDiscriminators[diToLocation(ReferenceDI)] = 0;
92
93 // Figure out the largest discriminator issued for each Location. When we
94 // issue new discriminators, we can thus avoid issuing discriminators
95 // belonging to instructions that don't have memops. This isn't a requirement
96 // for the goals of this pass, however, it avoids unnecessary ambiguity.
97 for (auto &MBB : MF) {
98 for (auto &MI : MBB) {
99 const auto &DI = MI.getDebugLoc();
100 if (!DI)
101 continue;
102 Location Loc = diToLocation(DI);
103 MemOpDiscriminators[Loc] =
104 std::max(MemOpDiscriminators[Loc], DI->getBaseDiscriminator());
105 }
106 }
107
108 // Keep track of the discriminators seen at each Location. If an instruction's
109 // DebugInfo has a Location and discriminator we've already seen, replace its
110 // discriminator with a new one, to guarantee uniqueness.
111 DenseMap<Location, DenseSet<unsigned>> Seen;
112
113 bool Changed = false;
114 for (auto &MBB : MF) {
115 for (auto &MI : MBB) {
116 if (X86II::getMemoryOperandNo(MI.getDesc().TSFlags) < 0)
117 continue;
118 const DILocation *DI = MI.getDebugLoc();
119 if (!DI) {
120 DI = ReferenceDI;
121 }
122 Location L = diToLocation(DI);
123 DenseSet<unsigned> &Set = Seen[L];
124 const std::pair<DenseSet<unsigned>::iterator, bool> TryInsert =
125 Set.insert(DI->getBaseDiscriminator());
126 if (!TryInsert.second) {
127 unsigned BF, DF, CI = 0;
128 DILocation::decodeDiscriminator(DI->getDiscriminator(), BF, DF, CI);
129 Optional<unsigned> EncodedDiscriminator = DILocation::encodeDiscriminator(
130 MemOpDiscriminators[L] + 1, DF, CI);
131
132 if (!EncodedDiscriminator) {
133 // FIXME(mtrofin): The assumption is that this scenario is infrequent/OK
134 // not to support. If evidence points otherwise, we can explore synthesizeing
135 // unique DIs by adding fake line numbers, or by constructing 64 bit
136 // discriminators.
137 LLVM_DEBUG(dbgs() << "Unable to create a unique discriminator "
138 "for instruction with memory operand in: "
139 << DI->getFilename() << " Line: " << DI->getLine()
140 << " Column: " << DI->getColumn()
141 << ". This is likely due to a large macro expansion. \n");
142 continue;
143 }
144 // Since we were able to encode, bump the MemOpDiscriminators.
145 ++MemOpDiscriminators[L];
146 DI = DI->cloneWithDiscriminator(EncodedDiscriminator.getValue());
147 updateDebugInfo(&MI, DI);
148 Changed = true;
149 std::pair<DenseSet<unsigned>::iterator, bool> MustInsert =
150 Set.insert(DI->getBaseDiscriminator());
151 (void)MustInsert; // Silence warning in release build.
152 assert(MustInsert.second && "New discriminator shouldn't be present in set");
153 }
154
155 // Bump the reference DI to avoid cramming discriminators on line 0.
156 // FIXME(mtrofin): pin ReferenceDI on blocks or first instruction with DI
157 // in a block. It's more consistent than just relying on the last memop
158 // instruction we happened to see.
159 ReferenceDI = DI;
160 }
161 }
162 return Changed;
163 }
164
createX86DiscriminateMemOpsPass()165 FunctionPass *llvm::createX86DiscriminateMemOpsPass() {
166 return new X86DiscriminateMemOps();
167 }
168