//===-------- MIRFSDiscriminator.cpp: Flow Sensitive Discriminator --------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file provides the implementation of a machine pass that adds the flow // sensitive discriminator to the instruction debug information. // //===----------------------------------------------------------------------===// #include "llvm/CodeGen/MIRFSDiscriminator.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseSet.h" #include "llvm/Analysis/BlockFrequencyInfoImpl.h" #include "llvm/CodeGen/Passes.h" #include "llvm/IR/DebugInfoMetadata.h" #include "llvm/IR/Function.h" #include "llvm/IR/Module.h" #include "llvm/IR/PseudoProbe.h" #include "llvm/InitializePasses.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Support/xxhash.h" #include "llvm/Transforms/Utils/SampleProfileLoaderBaseUtil.h" using namespace llvm; using namespace sampleprof; using namespace sampleprofutil; #define DEBUG_TYPE "mirfs-discriminators" // TODO(xur): Remove this option and related code once we make true as the // default. namespace llvm { cl::opt ImprovedFSDiscriminator( "improved-fs-discriminator", cl::Hidden, cl::init(false), cl::desc("New FS discriminators encoding (incompatible with the original " "encoding)")); } char MIRAddFSDiscriminators::ID = 0; INITIALIZE_PASS(MIRAddFSDiscriminators, DEBUG_TYPE, "Add MIR Flow Sensitive Discriminators", /* cfg = */ false, /* is_analysis = */ false) char &llvm::MIRAddFSDiscriminatorsID = MIRAddFSDiscriminators::ID; FunctionPass *llvm::createMIRAddFSDiscriminatorsPass(FSDiscriminatorPass P) { return new MIRAddFSDiscriminators(P); } // TODO(xur): Remove this once we switch to ImprovedFSDiscriminator. // Compute a hash value using debug line number, and the line numbers from the // inline stack. static uint64_t getCallStackHashV0(const MachineBasicBlock &BB, const MachineInstr &MI, const DILocation *DIL) { auto updateHash = [](const StringRef &Str) -> uint64_t { if (Str.empty()) return 0; return MD5Hash(Str); }; uint64_t Ret = updateHash(std::to_string(DIL->getLine())); Ret ^= updateHash(BB.getName()); Ret ^= updateHash(DIL->getScope()->getSubprogram()->getLinkageName()); for (DIL = DIL->getInlinedAt(); DIL; DIL = DIL->getInlinedAt()) { Ret ^= updateHash(std::to_string(DIL->getLine())); Ret ^= updateHash(DIL->getScope()->getSubprogram()->getLinkageName()); } return Ret; } static uint64_t getCallStackHash(const DILocation *DIL) { auto hashCombine = [](const uint64_t Seed, const uint64_t Val) { std::hash Hasher; return Seed ^ (Hasher(Val) + 0x9e3779b9 + (Seed << 6) + (Seed >> 2)); }; uint64_t Ret = 0; for (DIL = DIL->getInlinedAt(); DIL; DIL = DIL->getInlinedAt()) { Ret = hashCombine(Ret, xxh3_64bits(ArrayRef(DIL->getLine()))); Ret = hashCombine(Ret, xxh3_64bits(DIL->getSubprogramLinkageName())); } return Ret; } // Traverse the CFG and assign FD discriminators. If two instructions // have the same lineno and discriminator, but residing in different BBs, // the latter instruction will get a new discriminator value. The new // discriminator keeps the existing discriminator value but sets new bits // b/w LowBit and HighBit. bool MIRAddFSDiscriminators::runOnMachineFunction(MachineFunction &MF) { if (!EnableFSDiscriminator) return false; bool HasPseudoProbe = MF.getFunction().getParent()->getNamedMetadata( PseudoProbeDescMetadataName); if (!HasPseudoProbe && !MF.getFunction().shouldEmitDebugInfoForProfiling()) return false; bool Changed = false; using LocationDiscriminator = std::tuple; using BBSet = DenseSet; using LocationDiscriminatorBBMap = DenseMap; using LocationDiscriminatorCurrPassMap = DenseMap; LocationDiscriminatorBBMap LDBM; LocationDiscriminatorCurrPassMap LDCM; // Mask of discriminators before this pass. // TODO(xur): simplify this once we switch to ImprovedFSDiscriminator. unsigned LowBitTemp = LowBit; assert(LowBit > 0 && "LowBit in FSDiscriminator cannot be 0"); if (ImprovedFSDiscriminator) LowBitTemp -= 1; unsigned BitMaskBefore = getN1Bits(LowBitTemp); // Mask of discriminators including this pass. unsigned BitMaskNow = getN1Bits(HighBit); // Mask of discriminators for bits specific to this pass. unsigned BitMaskThisPass = BitMaskNow ^ BitMaskBefore; unsigned NumNewD = 0; LLVM_DEBUG(dbgs() << "MIRAddFSDiscriminators working on Func: " << MF.getFunction().getName() << " Highbit=" << HighBit << "\n"); for (MachineBasicBlock &BB : MF) { for (MachineInstr &I : BB) { if (HasPseudoProbe) { // Only assign discriminators to pseudo probe instructions. Call // instructions are excluded since their dwarf discriminators are used // for other purposes, i.e, storing probe ids. if (!I.isPseudoProbe()) continue; } else if (ImprovedFSDiscriminator && I.isMetaInstruction()) { continue; } const DILocation *DIL = I.getDebugLoc().get(); if (!DIL) continue; // Use the id of pseudo probe to compute the discriminator. unsigned LineNo = I.isPseudoProbe() ? I.getOperand(1).getImm() : DIL->getLine(); if (LineNo == 0) continue; unsigned Discriminator = DIL->getDiscriminator(); // Clean up discriminators for pseudo probes at the first FS discriminator // pass as their discriminators should not ever be used. if ((Pass == FSDiscriminatorPass::Pass1) && I.isPseudoProbe()) { Discriminator = 0; I.setDebugLoc(DIL->cloneWithDiscriminator(0)); } uint64_t CallStackHashVal = 0; if (ImprovedFSDiscriminator) CallStackHashVal = getCallStackHash(DIL); LocationDiscriminator LD{DIL->getFilename(), LineNo, Discriminator, CallStackHashVal}; auto &BBMap = LDBM[LD]; auto R = BBMap.insert(&BB); if (BBMap.size() == 1) continue; unsigned DiscriminatorCurrPass; DiscriminatorCurrPass = R.second ? ++LDCM[LD] : LDCM[LD]; DiscriminatorCurrPass = DiscriminatorCurrPass << LowBit; if (!ImprovedFSDiscriminator) DiscriminatorCurrPass += getCallStackHashV0(BB, I, DIL); DiscriminatorCurrPass &= BitMaskThisPass; unsigned NewD = Discriminator | DiscriminatorCurrPass; const auto *const NewDIL = DIL->cloneWithDiscriminator(NewD); if (!NewDIL) { LLVM_DEBUG(dbgs() << "Could not encode discriminator: " << DIL->getFilename() << ":" << DIL->getLine() << ":" << DIL->getColumn() << ":" << Discriminator << " " << I << "\n"); continue; } I.setDebugLoc(NewDIL); NumNewD++; LLVM_DEBUG(dbgs() << DIL->getFilename() << ":" << DIL->getLine() << ":" << DIL->getColumn() << ": add FS discriminator, from " << Discriminator << " -> " << NewD << "\n"); Changed = true; } } if (Changed) { createFSDiscriminatorVariable(MF.getFunction().getParent()); LLVM_DEBUG(dbgs() << "Num of FS Discriminators: " << NumNewD << "\n"); (void) NumNewD; } return Changed; }