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