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