1 ///===- MachineOptimizationRemarkEmitter.h - Opt Diagnostics -*- C++ -*----===//
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 /// \file
9 /// Optimization diagnostic interfaces for machine passes.  It's packaged as an
10 /// analysis pass so that by using this service passes become dependent on MBFI
11 /// as well.  MBFI is used to compute the "hotness" of the diagnostic message.
12 ///
13 ///===---------------------------------------------------------------------===//
14 
15 #ifndef LLVM_CODEGEN_MACHINEOPTIMIZATIONREMARKEMITTER_H
16 #define LLVM_CODEGEN_MACHINEOPTIMIZATIONREMARKEMITTER_H
17 
18 #include "llvm/CodeGen/MachineFunctionPass.h"
19 #include "llvm/IR/DiagnosticInfo.h"
20 #include "llvm/IR/Function.h"
21 #include <optional>
22 
23 namespace llvm {
24 class MachineBasicBlock;
25 class MachineBlockFrequencyInfo;
26 class MachineInstr;
27 
28 /// Common features for diagnostics dealing with optimization remarks
29 /// that are used by machine passes.
30 class DiagnosticInfoMIROptimization : public DiagnosticInfoOptimizationBase {
31 public:
32   DiagnosticInfoMIROptimization(enum DiagnosticKind Kind, const char *PassName,
33                                 StringRef RemarkName,
34                                 const DiagnosticLocation &Loc,
35                                 const MachineBasicBlock *MBB)
36       : DiagnosticInfoOptimizationBase(Kind, DS_Remark, PassName, RemarkName,
37                                        MBB->getParent()->getFunction(), Loc),
38         MBB(MBB) {}
39 
40   /// MI-specific kinds of diagnostic Arguments.
41   struct MachineArgument : public DiagnosticInfoOptimizationBase::Argument {
42     /// Print an entire MachineInstr.
43     MachineArgument(StringRef Key, const MachineInstr &MI);
44   };
45 
46   static bool classof(const DiagnosticInfo *DI) {
47     return DI->getKind() >= DK_FirstMachineRemark &&
48            DI->getKind() <= DK_LastMachineRemark;
49   }
50 
51   const MachineBasicBlock *getBlock() const { return MBB; }
52 
53 private:
54   const MachineBasicBlock *MBB;
55 };
56 
57 /// Diagnostic information for applied optimization remarks.
58 class MachineOptimizationRemark : public DiagnosticInfoMIROptimization {
59 public:
60   /// \p PassName is the name of the pass emitting this diagnostic. If this name
61   /// matches the regular expression given in -Rpass=, then the diagnostic will
62   /// be emitted.  \p RemarkName is a textual identifier for the remark.  \p
63   /// Loc is the debug location and \p MBB is the block that the optimization
64   /// operates in.
65   MachineOptimizationRemark(const char *PassName, StringRef RemarkName,
66                             const DiagnosticLocation &Loc,
67                             const MachineBasicBlock *MBB)
68       : DiagnosticInfoMIROptimization(DK_MachineOptimizationRemark, PassName,
69                                       RemarkName, Loc, MBB) {}
70 
71   static bool classof(const DiagnosticInfo *DI) {
72     return DI->getKind() == DK_MachineOptimizationRemark;
73   }
74 
75   /// \see DiagnosticInfoOptimizationBase::isEnabled.
76   bool isEnabled() const override {
77     const Function &Fn = getFunction();
78     LLVMContext &Ctx = Fn.getContext();
79     return Ctx.getDiagHandlerPtr()->isPassedOptRemarkEnabled(getPassName());
80   }
81 };
82 
83 /// Diagnostic information for missed-optimization remarks.
84 class MachineOptimizationRemarkMissed : public DiagnosticInfoMIROptimization {
85 public:
86   /// \p PassName is the name of the pass emitting this diagnostic. If this name
87   /// matches the regular expression given in -Rpass-missed=, then the
88   /// diagnostic will be emitted.  \p RemarkName is a textual identifier for the
89   /// remark.  \p Loc is the debug location and \p MBB is the block that the
90   /// optimization operates in.
91   MachineOptimizationRemarkMissed(const char *PassName, StringRef RemarkName,
92                                   const DiagnosticLocation &Loc,
93                                   const MachineBasicBlock *MBB)
94       : DiagnosticInfoMIROptimization(DK_MachineOptimizationRemarkMissed,
95                                       PassName, RemarkName, Loc, MBB) {}
96 
97   static bool classof(const DiagnosticInfo *DI) {
98     return DI->getKind() == DK_MachineOptimizationRemarkMissed;
99   }
100 
101   /// \see DiagnosticInfoOptimizationBase::isEnabled.
102   bool isEnabled() const override {
103     const Function &Fn = getFunction();
104     LLVMContext &Ctx = Fn.getContext();
105     return Ctx.getDiagHandlerPtr()->isMissedOptRemarkEnabled(getPassName());
106   }
107 };
108 
109 /// Diagnostic information for optimization analysis remarks.
110 class MachineOptimizationRemarkAnalysis : public DiagnosticInfoMIROptimization {
111 public:
112   /// \p PassName is the name of the pass emitting this diagnostic. If this name
113   /// matches the regular expression given in -Rpass-analysis=, then the
114   /// diagnostic will be emitted.  \p RemarkName is a textual identifier for the
115   /// remark.  \p Loc is the debug location and \p MBB is the block that the
116   /// optimization operates in.
117   MachineOptimizationRemarkAnalysis(const char *PassName, StringRef RemarkName,
118                                     const DiagnosticLocation &Loc,
119                                     const MachineBasicBlock *MBB)
120       : DiagnosticInfoMIROptimization(DK_MachineOptimizationRemarkAnalysis,
121                                       PassName, RemarkName, Loc, MBB) {}
122 
123   MachineOptimizationRemarkAnalysis(const char *PassName, StringRef RemarkName,
124                                     const MachineInstr *MI)
125       : DiagnosticInfoMIROptimization(DK_MachineOptimizationRemarkAnalysis,
126                                       PassName, RemarkName, MI->getDebugLoc(),
127                                       MI->getParent()) {}
128 
129   static bool classof(const DiagnosticInfo *DI) {
130     return DI->getKind() == DK_MachineOptimizationRemarkAnalysis;
131   }
132 
133   /// \see DiagnosticInfoOptimizationBase::isEnabled.
134   bool isEnabled() const override {
135     const Function &Fn = getFunction();
136     LLVMContext &Ctx = Fn.getContext();
137     return Ctx.getDiagHandlerPtr()->isAnalysisRemarkEnabled(getPassName());
138   }
139 };
140 
141 /// Extend llvm::ore:: with MI-specific helper names.
142 namespace ore {
143 using MNV = DiagnosticInfoMIROptimization::MachineArgument;
144 }
145 
146 /// The optimization diagnostic interface.
147 ///
148 /// It allows reporting when optimizations are performed and when they are not
149 /// along with the reasons for it.  Hotness information of the corresponding
150 /// code region can be included in the remark if DiagnosticsHotnessRequested is
151 /// enabled in the LLVM context.
152 class MachineOptimizationRemarkEmitter {
153 public:
154   MachineOptimizationRemarkEmitter(MachineFunction &MF,
155                                    MachineBlockFrequencyInfo *MBFI)
156       : MF(MF), MBFI(MBFI) {}
157 
158   /// Emit an optimization remark.
159   void emit(DiagnosticInfoOptimizationBase &OptDiag);
160 
161   /// Whether we allow for extra compile-time budget to perform more
162   /// analysis to be more informative.
163   ///
164   /// This is useful to enable additional missed optimizations to be reported
165   /// that are normally too noisy.  In this mode, we can use the extra analysis
166   /// (1) to filter trivial false positives or (2) to provide more context so
167   /// that non-trivial false positives can be quickly detected by the user.
168   bool allowExtraAnalysis(StringRef PassName) const {
169     return (
170         MF.getFunction().getContext().getLLVMRemarkStreamer() ||
171         MF.getFunction().getContext().getDiagHandlerPtr()->isAnyRemarkEnabled(
172             PassName));
173   }
174 
175   /// Take a lambda that returns a remark which will be emitted.  Second
176   /// argument is only used to restrict this to functions.
177   template <typename T>
178   void emit(T RemarkBuilder, decltype(RemarkBuilder()) * = nullptr) {
179     // Avoid building the remark unless we know there are at least *some*
180     // remarks enabled. We can't currently check whether remarks are requested
181     // for the calling pass since that requires actually building the remark.
182 
183     if (MF.getFunction().getContext().getLLVMRemarkStreamer() ||
184         MF.getFunction()
185             .getContext()
186             .getDiagHandlerPtr()
187             ->isAnyRemarkEnabled()) {
188       auto R = RemarkBuilder();
189       emit((DiagnosticInfoOptimizationBase &)R);
190     }
191   }
192 
193   MachineBlockFrequencyInfo *getBFI() {
194     return MBFI;
195   }
196 
197 private:
198   MachineFunction &MF;
199 
200   /// MBFI is only set if hotness is requested.
201   MachineBlockFrequencyInfo *MBFI;
202 
203   /// Compute hotness from IR value (currently assumed to be a block) if PGO is
204   /// available.
205   std::optional<uint64_t> computeHotness(const MachineBasicBlock &MBB);
206 
207   /// Similar but use value from \p OptDiag and update hotness there.
208   void computeHotness(DiagnosticInfoMIROptimization &Remark);
209 
210   /// Only allow verbose messages if we know we're filtering by hotness
211   /// (BFI is only set in this case).
212   bool shouldEmitVerbose() { return MBFI != nullptr; }
213 };
214 
215 /// The analysis pass
216 ///
217 /// Note that this pass shouldn't generally be marked as preserved by other
218 /// passes.  It's holding onto BFI, so if the pass does not preserve BFI, BFI
219 /// could be freed.
220 class MachineOptimizationRemarkEmitterPass : public MachineFunctionPass {
221   std::unique_ptr<MachineOptimizationRemarkEmitter> ORE;
222 
223 public:
224   MachineOptimizationRemarkEmitterPass();
225 
226   bool runOnMachineFunction(MachineFunction &MF) override;
227 
228   void getAnalysisUsage(AnalysisUsage &AU) const override;
229 
230   MachineOptimizationRemarkEmitter &getORE() {
231     assert(ORE && "pass not run yet");
232     return *ORE;
233   }
234 
235   static char ID;
236 };
237 }
238 
239 #endif
240