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