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