| 1 | //===-------- MIRFSDiscriminator.cpp: Flow Sensitive Discriminator --------===// |
| 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 file provides the implementation of a machine pass that adds the flow |
| 10 | // sensitive discriminator to the instruction debug information. |
| 11 | // |
| 12 | //===----------------------------------------------------------------------===// |
| 13 | |
| 14 | #include "llvm/CodeGen/MIRFSDiscriminator.h" |
| 15 | #include "llvm/ADT/DenseMap.h" |
| 16 | #include "llvm/ADT/DenseSet.h" |
| 17 | #include "llvm/Analysis/BlockFrequencyInfoImpl.h" |
| 18 | #include "llvm/CodeGen/Passes.h" |
| 19 | #include "llvm/IR/DebugInfoMetadata.h" |
| 20 | #include "llvm/IR/Function.h" |
| 21 | #include "llvm/IR/Module.h" |
| 22 | #include "llvm/IR/PseudoProbe.h" |
| 23 | #include "llvm/InitializePasses.h" |
| 24 | #include "llvm/Support/CommandLine.h" |
| 25 | #include "llvm/Support/Debug.h" |
| 26 | #include "llvm/Support/raw_ostream.h" |
| 27 | #include "llvm/Support/xxhash.h" |
| 28 | #include "llvm/Transforms/Utils/SampleProfileLoaderBaseUtil.h" |
| 29 | |
| 30 | using namespace llvm; |
| 31 | using namespace sampleprof; |
| 32 | using namespace sampleprofutil; |
| 33 | |
| 34 | #define DEBUG_TYPE "mirfs-discriminators" |
| 35 | |
| 36 | // TODO(xur): Remove this option and related code once we make true as the |
| 37 | // default. |
| 38 | namespace llvm { |
| 39 | cl::opt<bool> ImprovedFSDiscriminator( |
| 40 | "improved-fs-discriminator" , cl::Hidden, cl::init(Val: false), |
| 41 | cl::desc("New FS discriminators encoding (incompatible with the original " |
| 42 | "encoding)" )); |
| 43 | } |
| 44 | |
| 45 | char MIRAddFSDiscriminators::ID = 0; |
| 46 | |
| 47 | INITIALIZE_PASS(MIRAddFSDiscriminators, DEBUG_TYPE, |
| 48 | "Add MIR Flow Sensitive Discriminators" , |
| 49 | /* cfg = */ false, /* is_analysis = */ false) |
| 50 | |
| 51 | char &llvm::MIRAddFSDiscriminatorsID = MIRAddFSDiscriminators::ID; |
| 52 | |
| 53 | FunctionPass *llvm::createMIRAddFSDiscriminatorsPass(FSDiscriminatorPass P) { |
| 54 | return new MIRAddFSDiscriminators(P); |
| 55 | } |
| 56 | |
| 57 | // TODO(xur): Remove this once we switch to ImprovedFSDiscriminator. |
| 58 | // Compute a hash value using debug line number, and the line numbers from the |
| 59 | // inline stack. |
| 60 | static uint64_t getCallStackHashV0(const MachineBasicBlock &BB, |
| 61 | const MachineInstr &MI, |
| 62 | const DILocation *DIL) { |
| 63 | auto updateHash = [](const StringRef &Str) -> uint64_t { |
| 64 | if (Str.empty()) |
| 65 | return 0; |
| 66 | return MD5Hash(Str); |
| 67 | }; |
| 68 | uint64_t Ret = updateHash(std::to_string(val: DIL->getLine())); |
| 69 | Ret ^= updateHash(BB.getName()); |
| 70 | Ret ^= updateHash(DIL->getScope()->getSubprogram()->getLinkageName()); |
| 71 | for (DIL = DIL->getInlinedAt(); DIL; DIL = DIL->getInlinedAt()) { |
| 72 | Ret ^= updateHash(std::to_string(val: DIL->getLine())); |
| 73 | Ret ^= updateHash(DIL->getScope()->getSubprogram()->getLinkageName()); |
| 74 | } |
| 75 | return Ret; |
| 76 | } |
| 77 | |
| 78 | static uint64_t getCallStackHash(const DILocation *DIL) { |
| 79 | auto hashCombine = [](const uint64_t Seed, const uint64_t Val) { |
| 80 | std::hash<uint64_t> Hasher; |
| 81 | return Seed ^ (Hasher(Val) + 0x9e3779b9 + (Seed << 6) + (Seed >> 2)); |
| 82 | }; |
| 83 | uint64_t Ret = 0; |
| 84 | for (DIL = DIL->getInlinedAt(); DIL; DIL = DIL->getInlinedAt()) { |
| 85 | Ret = hashCombine(Ret, xxh3_64bits(data: ArrayRef<uint8_t>(DIL->getLine()))); |
| 86 | Ret = hashCombine(Ret, xxh3_64bits(data: DIL->getSubprogramLinkageName())); |
| 87 | } |
| 88 | return Ret; |
| 89 | } |
| 90 | |
| 91 | // Traverse the CFG and assign FD discriminators. If two instructions |
| 92 | // have the same lineno and discriminator, but residing in different BBs, |
| 93 | // the latter instruction will get a new discriminator value. The new |
| 94 | // discriminator keeps the existing discriminator value but sets new bits |
| 95 | // b/w LowBit and HighBit. |
| 96 | bool MIRAddFSDiscriminators::runOnMachineFunction(MachineFunction &MF) { |
| 97 | if (!EnableFSDiscriminator) |
| 98 | return false; |
| 99 | |
| 100 | bool HasPseudoProbe = MF.getFunction().getParent()->getNamedMetadata( |
| 101 | Name: PseudoProbeDescMetadataName); |
| 102 | |
| 103 | if (!HasPseudoProbe && !MF.getFunction().shouldEmitDebugInfoForProfiling()) |
| 104 | return false; |
| 105 | |
| 106 | bool Changed = false; |
| 107 | using LocationDiscriminator = |
| 108 | std::tuple<StringRef, unsigned, unsigned, uint64_t>; |
| 109 | using BBSet = DenseSet<const MachineBasicBlock *>; |
| 110 | using LocationDiscriminatorBBMap = DenseMap<LocationDiscriminator, BBSet>; |
| 111 | using LocationDiscriminatorCurrPassMap = |
| 112 | DenseMap<LocationDiscriminator, unsigned>; |
| 113 | |
| 114 | LocationDiscriminatorBBMap LDBM; |
| 115 | LocationDiscriminatorCurrPassMap LDCM; |
| 116 | |
| 117 | // Mask of discriminators before this pass. |
| 118 | // TODO(xur): simplify this once we switch to ImprovedFSDiscriminator. |
| 119 | unsigned LowBitTemp = LowBit; |
| 120 | assert(LowBit > 0 && "LowBit in FSDiscriminator cannot be 0" ); |
| 121 | if (ImprovedFSDiscriminator) |
| 122 | LowBitTemp -= 1; |
| 123 | unsigned BitMaskBefore = getN1Bits(N: LowBitTemp); |
| 124 | // Mask of discriminators including this pass. |
| 125 | unsigned BitMaskNow = getN1Bits(N: HighBit); |
| 126 | // Mask of discriminators for bits specific to this pass. |
| 127 | unsigned BitMaskThisPass = BitMaskNow ^ BitMaskBefore; |
| 128 | unsigned NumNewD = 0; |
| 129 | |
| 130 | LLVM_DEBUG(dbgs() << "MIRAddFSDiscriminators working on Func: " |
| 131 | << MF.getFunction().getName() << " Highbit=" << HighBit |
| 132 | << "\n" ); |
| 133 | |
| 134 | for (MachineBasicBlock &BB : MF) { |
| 135 | for (MachineInstr &I : BB) { |
| 136 | if (HasPseudoProbe) { |
| 137 | // Only assign discriminators to pseudo probe instructions. Call |
| 138 | // instructions are excluded since their dwarf discriminators are used |
| 139 | // for other purposes, i.e, storing probe ids. |
| 140 | if (!I.isPseudoProbe()) |
| 141 | continue; |
| 142 | } else if (ImprovedFSDiscriminator && I.isMetaInstruction()) { |
| 143 | continue; |
| 144 | } |
| 145 | const DILocation *DIL = I.getDebugLoc().get(); |
| 146 | if (!DIL) |
| 147 | continue; |
| 148 | |
| 149 | // Use the id of pseudo probe to compute the discriminator. |
| 150 | unsigned LineNo = |
| 151 | I.isPseudoProbe() ? I.getOperand(i: 1).getImm() : DIL->getLine(); |
| 152 | if (LineNo == 0) |
| 153 | continue; |
| 154 | unsigned Discriminator = DIL->getDiscriminator(); |
| 155 | // Clean up discriminators for pseudo probes at the first FS discriminator |
| 156 | // pass as their discriminators should not ever be used. |
| 157 | if ((Pass == FSDiscriminatorPass::Pass1) && I.isPseudoProbe()) { |
| 158 | Discriminator = 0; |
| 159 | I.setDebugLoc(DIL->cloneWithDiscriminator(Discriminator: 0)); |
| 160 | } |
| 161 | uint64_t CallStackHashVal = 0; |
| 162 | if (ImprovedFSDiscriminator) |
| 163 | CallStackHashVal = getCallStackHash(DIL); |
| 164 | |
| 165 | LocationDiscriminator LD{DIL->getFilename(), LineNo, Discriminator, |
| 166 | CallStackHashVal}; |
| 167 | auto &BBMap = LDBM[LD]; |
| 168 | auto R = BBMap.insert(V: &BB); |
| 169 | if (BBMap.size() == 1) |
| 170 | continue; |
| 171 | |
| 172 | unsigned DiscriminatorCurrPass; |
| 173 | DiscriminatorCurrPass = R.second ? ++LDCM[LD] : LDCM[LD]; |
| 174 | DiscriminatorCurrPass = DiscriminatorCurrPass << LowBit; |
| 175 | if (!ImprovedFSDiscriminator) |
| 176 | DiscriminatorCurrPass += getCallStackHashV0(BB, MI: I, DIL); |
| 177 | DiscriminatorCurrPass &= BitMaskThisPass; |
| 178 | unsigned NewD = Discriminator | DiscriminatorCurrPass; |
| 179 | const auto *const NewDIL = DIL->cloneWithDiscriminator(Discriminator: NewD); |
| 180 | if (!NewDIL) { |
| 181 | LLVM_DEBUG(dbgs() << "Could not encode discriminator: " |
| 182 | << DIL->getFilename() << ":" << DIL->getLine() << ":" |
| 183 | << DIL->getColumn() << ":" << Discriminator << " " |
| 184 | << I << "\n" ); |
| 185 | continue; |
| 186 | } |
| 187 | |
| 188 | I.setDebugLoc(NewDIL); |
| 189 | NumNewD++; |
| 190 | LLVM_DEBUG(dbgs() << DIL->getFilename() << ":" << DIL->getLine() << ":" |
| 191 | << DIL->getColumn() << ": add FS discriminator, from " |
| 192 | << Discriminator << " -> " << NewD << "\n" ); |
| 193 | Changed = true; |
| 194 | } |
| 195 | } |
| 196 | |
| 197 | if (Changed) { |
| 198 | createFSDiscriminatorVariable(M: MF.getFunction().getParent()); |
| 199 | LLVM_DEBUG(dbgs() << "Num of FS Discriminators: " << NumNewD << "\n" ); |
| 200 | (void) NumNewD; |
| 201 | } |
| 202 | |
| 203 | return Changed; |
| 204 | } |
| 205 | |