xref: /freebsd-src/contrib/llvm-project/llvm/lib/CodeGen/MIRFSDiscriminator.cpp (revision 06c3fb2749bda94cb5201f81ffdb8fa6c3161b2e)
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