xref: /llvm-project/llvm/lib/Target/X86/X86DiscriminateMemOps.cpp (revision 5c31c05fbde371e03c057080a05eeb18dda4c4ff)
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-drived 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 namespace {
38 
39 using Location = std::pair<StringRef, unsigned>;
40 
41 Location diToLocation(const DILocation *Loc) {
42   return std::make_pair(Loc->getFilename(), Loc->getLine());
43 }
44 
45 /// Ensure each instruction having a memory operand has a distinct <LineNumber,
46 /// Discriminator> pair.
47 void updateDebugInfo(MachineInstr *MI, const DILocation *Loc) {
48   DebugLoc DL(Loc);
49   MI->setDebugLoc(DL);
50 }
51 
52 class X86DiscriminateMemOps : public MachineFunctionPass {
53   bool runOnMachineFunction(MachineFunction &MF) override;
54   StringRef getPassName() const override {
55     return "X86 Discriminate Memory Operands";
56   }
57 
58 public:
59   static char ID;
60 
61   /// Default construct and initialize the pass.
62   X86DiscriminateMemOps();
63 };
64 
65 } // end anonymous namespace
66 
67 //===----------------------------------------------------------------------===//
68 //            Implementation
69 //===----------------------------------------------------------------------===//
70 
71 char X86DiscriminateMemOps::ID = 0;
72 
73 /// Default construct and initialize the pass.
74 X86DiscriminateMemOps::X86DiscriminateMemOps() : MachineFunctionPass(ID) {}
75 
76 bool X86DiscriminateMemOps::runOnMachineFunction(MachineFunction &MF) {
77   if (!EnableDiscriminateMemops)
78     return false;
79 
80   DISubprogram *FDI = MF.getFunction().getSubprogram();
81   if (!FDI || !FDI->getUnit()->getDebugInfoForProfiling())
82     return false;
83 
84   // Have a default DILocation, if we find instructions with memops that don't
85   // have any debug info.
86   const DILocation *ReferenceDI =
87       DILocation::get(FDI->getContext(), FDI->getLine(), 0, FDI);
88   assert(ReferenceDI && "ReferenceDI should not be nullptr");
89   DenseMap<Location, unsigned> MemOpDiscriminators;
90   MemOpDiscriminators[diToLocation(ReferenceDI)] = 0;
91 
92   // Figure out the largest discriminator issued for each Location. When we
93   // issue new discriminators, we can thus avoid issuing discriminators
94   // belonging to instructions that don't have memops. This isn't a requirement
95   // for the goals of this pass, however, it avoids unnecessary ambiguity.
96   for (auto &MBB : MF) {
97     for (auto &MI : MBB) {
98       const auto &DI = MI.getDebugLoc();
99       if (!DI)
100         continue;
101       Location Loc = diToLocation(DI);
102       MemOpDiscriminators[Loc] =
103           std::max(MemOpDiscriminators[Loc], DI->getBaseDiscriminator());
104     }
105   }
106 
107   // Keep track of the discriminators seen at each Location. If an instruction's
108   // DebugInfo has a Location and discriminator we've already seen, replace its
109   // discriminator with a new one, to guarantee uniqueness.
110   DenseMap<Location, DenseSet<unsigned>> Seen;
111 
112   bool Changed = false;
113   for (auto &MBB : MF) {
114     for (auto &MI : MBB) {
115       if (X86II::getMemoryOperandNo(MI.getDesc().TSFlags) < 0)
116         continue;
117       const DILocation *DI = MI.getDebugLoc();
118       bool HasDebug = DI;
119       if (!HasDebug) {
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 || !HasDebug) {
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         assert(DI && "DI should not be nullptr");
148         updateDebugInfo(&MI, DI);
149         Changed = true;
150         std::pair<DenseSet<unsigned>::iterator, bool> MustInsert =
151             Set.insert(DI->getBaseDiscriminator());
152         (void)MustInsert; // Silence warning in release build.
153         assert(MustInsert.second && "New discriminator shouldn't be present in set");
154       }
155 
156       // Bump the reference DI to avoid cramming discriminators on line 0.
157       // FIXME(mtrofin): pin ReferenceDI on blocks or first instruction with DI
158       // in a block. It's more consistent than just relying on the last memop
159       // instruction we happened to see.
160       ReferenceDI = DI;
161     }
162   }
163   return Changed;
164 }
165 
166 FunctionPass *llvm::createX86DiscriminateMemOpsPass() {
167   return new X86DiscriminateMemOps();
168 }
169