MLIR  22.0.0git
Remarks.cpp
Go to the documentation of this file.
1 //===- Remarks.cpp - MLIR Remarks -----------------------------------------===//
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 #include "mlir/IR/Remarks.h"
10 
12 #include "mlir/IR/Diagnostics.h"
13 #include "mlir/IR/Value.h"
14 
15 #include "llvm/ADT/StringExtras.h"
16 #include "llvm/ADT/StringRef.h"
17 
18 using namespace mlir::remark::detail;
19 using namespace mlir::remark;
20 //------------------------------------------------------------------------------
21 // Remark
22 //------------------------------------------------------------------------------
23 
24 Remark::Arg::Arg(llvm::StringRef k, Value v) : key(k) {
25  llvm::raw_string_ostream os(val);
26  os << v;
27 }
28 
29 Remark::Arg::Arg(llvm::StringRef k, Type t) : key(k) {
30  llvm::raw_string_ostream os(val);
31  os << t;
32 }
33 
34 void Remark::insert(llvm::StringRef s) { args.emplace_back(s); }
35 void Remark::insert(Arg a) { args.push_back(std::move(a)); }
36 
37 // Simple helper to print key=val list (sorted).
38 static void printArgs(llvm::raw_ostream &os, llvm::ArrayRef<Remark::Arg> args) {
39  if (args.empty())
40  return;
41 
42  llvm::SmallVector<Remark::Arg, 8> sorted(args.begin(), args.end());
43  llvm::sort(sorted, [](const Remark::Arg &a, const Remark::Arg &b) {
44  return a.key < b.key;
45  });
46 
47  for (size_t i = 0; i < sorted.size(); ++i) {
48  const auto &a = sorted[i];
49  os << a.key << "=";
50 
51  llvm::StringRef val(a.val);
52  bool needsQuote = val.contains(' ') || val.contains(',') ||
53  val.contains('{') || val.contains('}');
54  if (needsQuote)
55  os << '"' << val << '"';
56  else
57  os << val;
58 
59  if (i + 1 < sorted.size())
60  os << ", ";
61  }
62 }
63 
64 /// Print the remark to the given output stream.
65 /// Example output:
66 // clang-format off
67 /// [Missed] Category: Loop | Pass:Unroller | Function=main | Reason="tripCount=4 < threshold=256"
68 /// [Failure] LoopOptimizer | Reason="failed due to unsupported pattern"
69 // clang-format on
70 void Remark::print(llvm::raw_ostream &os, bool printLocation) const {
71  // Header: [Type] pass:remarkName
72  StringRef type = getRemarkTypeString();
74  StringRef name = remarkName;
75 
76  os << '[' << type << "] ";
77  os << name << " | ";
78  if (!categoryName.empty())
79  os << "Category:" << categoryName << " | ";
80  if (!functionName.empty())
81  os << "Function=" << getFunction() << " | ";
82 
83  if (printLocation) {
84  if (auto flc = mlir::dyn_cast<mlir::FileLineColLoc>(getLocation())) {
85  os << " @" << flc.getFilename() << ":" << flc.getLine() << ":"
86  << flc.getColumn();
87  }
88  }
89 
90  printArgs(os, getArgs());
91 }
92 
93 std::string Remark::getMsg() const {
94  std::string s;
95  llvm::raw_string_ostream os(s);
96  print(os);
97  os.flush();
98  return s;
99 }
100 
101 llvm::StringRef Remark::getRemarkTypeString() const {
102  switch (remarkKind) {
104  return "Unknown";
106  return "Passed";
108  return "Missed";
110  return "Failure";
112  return "Analysis";
113  }
114  llvm_unreachable("Unknown remark kind");
115 }
116 
117 llvm::remarks::Type Remark::getRemarkType() const {
118  switch (remarkKind) {
120  return llvm::remarks::Type::Unknown;
122  return llvm::remarks::Type::Passed;
124  return llvm::remarks::Type::Missed;
128  return llvm::remarks::Type::Analysis;
129  }
130  llvm_unreachable("Unknown remark kind");
131 }
132 
133 llvm::remarks::Remark Remark::generateRemark() const {
134  auto locLambda = [&]() -> llvm::remarks::RemarkLocation {
135  if (auto flc = dyn_cast<FileLineColLoc>(getLocation()))
136  return {flc.getFilename(), flc.getLine(), flc.getColumn()};
137  return {"<unknown file>", 0, 0};
138  };
139 
140  llvm::remarks::Remark r; // The result.
141  r.RemarkType = getRemarkType();
142  r.RemarkName = getRemarkName();
143  // MLIR does not use passes; instead, it has categories and sub-categories.
144  r.PassName = getCombinedCategoryName();
145  r.FunctionName = getFunction();
146  r.Loc = locLambda();
147  for (const Remark::Arg &arg : getArgs()) {
148  r.Args.emplace_back();
149  r.Args.back().Key = arg.key;
150  r.Args.back().Val = arg.val;
151  }
152  return r;
153 }
154 
155 //===----------------------------------------------------------------------===//
156 // InFlightRemark
157 //===----------------------------------------------------------------------===//
158 
160  if (remark && owner)
161  owner->report(std::move(*remark));
162  owner = nullptr;
163 }
164 
165 //===----------------------------------------------------------------------===//
166 // Remark Engine
167 //===----------------------------------------------------------------------===//
168 
169 template <typename RemarkT, typename... Args>
170 InFlightRemark RemarkEngine::makeRemark(Args &&...args) {
171  static_assert(std::is_base_of_v<Remark, RemarkT>,
172  "RemarkT must derive from Remark");
173  return InFlightRemark(*this,
174  std::make_unique<RemarkT>(std::forward<Args>(args)...));
175 }
176 
177 template <typename RemarkT>
179 RemarkEngine::emitIfEnabled(Location loc, RemarkOpts opts,
180  bool (RemarkEngine::*isEnabled)(StringRef) const) {
181  return (this->*isEnabled)(opts.categoryName) ? makeRemark<RemarkT>(loc, opts)
182  : InFlightRemark{};
183 }
184 
185 bool RemarkEngine::isMissedOptRemarkEnabled(StringRef categoryName) const {
186  return missFilter && missFilter->match(categoryName);
187 }
188 
189 bool RemarkEngine::isPassedOptRemarkEnabled(StringRef categoryName) const {
190  return passedFilter && passedFilter->match(categoryName);
191 }
192 
193 bool RemarkEngine::isAnalysisOptRemarkEnabled(StringRef categoryName) const {
194  return analysisFilter && analysisFilter->match(categoryName);
195 }
196 
197 bool RemarkEngine::isFailedOptRemarkEnabled(StringRef categoryName) const {
198  return failedFilter && failedFilter->match(categoryName);
199 }
200 
202  RemarkOpts opts) {
203  return emitIfEnabled<OptRemarkPass>(loc, opts,
204  &RemarkEngine::isPassedOptRemarkEnabled);
205 }
206 
208  RemarkOpts opts) {
209  return emitIfEnabled<OptRemarkMissed>(
210  loc, opts, &RemarkEngine::isMissedOptRemarkEnabled);
211 }
212 
214  RemarkOpts opts) {
215  return emitIfEnabled<OptRemarkFailure>(
216  loc, opts, &RemarkEngine::isFailedOptRemarkEnabled);
217 }
218 
220  RemarkOpts opts) {
221  return emitIfEnabled<OptRemarkAnalysis>(
222  loc, opts, &RemarkEngine::isAnalysisOptRemarkEnabled);
223 }
224 
225 //===----------------------------------------------------------------------===//
226 // RemarkEngine
227 //===----------------------------------------------------------------------===//
228 
229 void RemarkEngine::reportImpl(const Remark &remark) {
230  // Stream the remark
231  if (remarkStreamer) {
232  remarkStreamer->streamOptimizationRemark(remark);
233  }
234 
235  // Print using MLIR's diagnostic
236  if (printAsEmitRemarks)
237  emitRemark(remark.getLocation(), remark.getMsg());
238 }
239 
240 void RemarkEngine::report(const Remark &&remark) {
241  if (remarkEmittingPolicy)
242  remarkEmittingPolicy->reportRemark(remark);
243 }
244 
246  if (remarkEmittingPolicy)
247  remarkEmittingPolicy->finalize();
248 
249  if (remarkStreamer)
250  remarkStreamer->finalize();
251 }
252 
253 llvm::LogicalResult RemarkEngine::initialize(
254  std::unique_ptr<MLIRRemarkStreamerBase> streamer,
255  std::unique_ptr<RemarkEmittingPolicyBase> remarkEmittingPolicy,
256  std::string *errMsg) {
257 
258  remarkStreamer = std::move(streamer);
259 
260  auto reportFunc =
261  std::bind(&RemarkEngine::reportImpl, this, std::placeholders::_1);
262  remarkEmittingPolicy->initialize(ReportFn(std::move(reportFunc)));
263 
264  this->remarkEmittingPolicy = std::move(remarkEmittingPolicy);
265  return success();
266 }
267 
268 /// Returns true if filter is already anchored like ^...$
269 static bool isAnchored(llvm::StringRef s) {
270  s = s.trim();
271  return s.starts_with("^") && s.ends_with("$"); // note: startswith/endswith
272 }
273 
274 /// Anchor the entire pattern so it matches the whole string.
275 static std::string anchorWhole(llvm::StringRef filter) {
276  if (isAnchored(filter))
277  return filter.str();
278  return (llvm::Twine("^(") + filter + ")$").str();
279 }
280 
281 /// Build a combined filter from cats.all and a category-specific pattern.
282 /// If neither is present, return std::nullopt. Otherwise "(all|specific)"
283 /// and anchor once. Also validate before returning.
284 static std::optional<llvm::Regex>
286  const std::optional<std::string> &specific) {
288  if (cats.all && !cats.all->empty())
289  parts.emplace_back(*cats.all);
290  if (specific && !specific->empty())
291  parts.emplace_back(*specific);
292 
293  if (parts.empty())
294  return std::nullopt;
295 
296  std::string joined = llvm::join(parts, "|");
297  std::string anchored = anchorWhole(joined);
298 
299  llvm::Regex rx(anchored);
300  std::string err;
301  if (!rx.isValid(err))
302  return std::nullopt;
303 
304  return std::make_optional<llvm::Regex>(std::move(rx));
305 }
306 
307 RemarkEngine::RemarkEngine(bool printAsEmitRemarks,
308  const RemarkCategories &cats)
309  : printAsEmitRemarks(printAsEmitRemarks) {
310  if (cats.passed)
311  passedFilter = buildFilter(cats, cats.passed);
312  if (cats.missed)
313  missFilter = buildFilter(cats, cats.missed);
314  if (cats.analysis)
315  analysisFilter = buildFilter(cats, cats.analysis);
316  if (cats.failed)
317  failedFilter = buildFilter(cats, cats.failed);
318 }
319 
320 llvm::LogicalResult mlir::remark::enableOptimizationRemarks(
321  MLIRContext &ctx, std::unique_ptr<detail::MLIRRemarkStreamerBase> streamer,
322  std::unique_ptr<detail::RemarkEmittingPolicyBase> remarkEmittingPolicy,
323  const RemarkCategories &cats, bool printAsEmitRemarks) {
324  auto engine =
325  std::make_unique<detail::RemarkEngine>(printAsEmitRemarks, cats);
326 
327  std::string errMsg;
328  if (failed(engine->initialize(std::move(streamer),
329  std::move(remarkEmittingPolicy), &errMsg))) {
330  llvm::report_fatal_error(
331  llvm::Twine("Failed to initialize remark engine. Error: ") + errMsg);
332  }
333  ctx.setRemarkEngine(std::move(engine));
334 
335  return success();
336 }
337 
338 //===----------------------------------------------------------------------===//
339 // Remark emitting policies
340 //===----------------------------------------------------------------------===//
341 
342 namespace mlir::remark {
345 } // namespace mlir::remark
static bool isAnchored(llvm::StringRef s)
Returns true if filter is already anchored like ^...$.
Definition: Remarks.cpp:269
static std::optional< llvm::Regex > buildFilter(const mlir::remark::RemarkCategories &cats, const std::optional< std::string > &specific)
Build a combined filter from cats.all and a category-specific pattern.
Definition: Remarks.cpp:285
static void printArgs(llvm::raw_ostream &os, llvm::ArrayRef< Remark::Arg > args)
Definition: Remarks.cpp:38
static std::string anchorWhole(llvm::StringRef filter)
Anchor the entire pattern so it matches the whole string.
Definition: Remarks.cpp:275
@ Failure
Op failed to vectorize.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition: Location.h:76
MLIRContext is the top-level object for a collection of MLIR operations.
Definition: MLIRContext.h:63
void setRemarkEngine(std::unique_ptr< remark::detail::RemarkEngine > engine)
Set the remark engine for this context.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition: Types.h:74
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition: Value.h:96
InFlightRemark is a RAII class that holds a reference to a Remark instance and allows to build the re...
Definition: Remarks.h:280
InFlightRemark emitOptimizationRemarkFailure(Location loc, RemarkOpts opts)
Report a failed optimization remark, this will create an InFlightRemark that can be used to build the...
Definition: Remarks.cpp:213
void report(const Remark &&remark)
Report a remark.
Definition: Remarks.cpp:240
InFlightRemark emitOptimizationRemark(Location loc, RemarkOpts opts)
Report a successful remark, this will create an InFlightRemark that can be used to build the remark u...
Definition: Remarks.cpp:201
LogicalResult initialize(std::unique_ptr< MLIRRemarkStreamerBase > streamer, std::unique_ptr< RemarkEmittingPolicyBase > remarkEmittingPolicy, std::string *errMsg)
Setup the remark engine with the given output path and format.
Definition: Remarks.cpp:253
RemarkEngine()=delete
Default constructor is deleted, use the other constructor.
InFlightRemark emitOptimizationRemarkMiss(Location loc, RemarkOpts opts)
Report a missed optimization remark that can be used to build the remark using the << operator.
Definition: Remarks.cpp:207
~RemarkEngine()
Destructor that will close the output file and reset the main remark streamer.
Definition: Remarks.cpp:245
InFlightRemark emitOptimizationRemarkAnalysis(Location loc, RemarkOpts opts)
Report an analysis remark, this will create an InFlightRemark that can be used to build the remark us...
Definition: Remarks.cpp:219
void print(llvm::raw_ostream &os, bool printLocation=false) const
Print the remark to the given output stream.
Definition: Remarks.cpp:70
StringRef categoryName
Sub category passname e.g., "Unroll" or "UnrollAndJam".
Definition: Remarks.h:179
SmallVector< Arg, 4 > args
Args collected via the streaming interface.
Definition: Remarks.h:191
StringRef getFunction() const
Definition: Remarks.h:138
llvm::remarks::Type getRemarkType() const
Definition: Remarks.cpp:117
StringRef getRemarkTypeString() const
Definition: Remarks.cpp:101
Location getLocation() const
Definition: Remarks.h:134
StringRef remarkName
Remark identifier.
Definition: Remarks.h:188
ArrayRef< Arg > getArgs() const
Definition: Remarks.h:164
llvm::StringRef getCombinedCategoryName() const
Definition: Remarks.h:146
RemarkKind remarkKind
Keeps the MLIR diagnostic kind, which is used to determine the diagnostic kind in the LLVM remark str...
Definition: Remarks.h:173
StringRef getRemarkName() const
Definition: Remarks.h:156
void insert(llvm::StringRef s)
Definition: Remarks.cpp:34
StringRef functionName
Name of the convering function like interface.
Definition: Remarks.h:175
llvm::remarks::Remark generateRemark() const
Diagnostic -> Remark.
Definition: Remarks.cpp:133
std::string getMsg() const
Definition: Remarks.cpp:93
llvm::unique_function< void(const Remark &)> ReportFn
Definition: Remarks.h:340
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition: Remarks.h:561
LogicalResult enableOptimizationRemarks(MLIRContext &ctx, std::unique_ptr< remark::detail::MLIRRemarkStreamerBase > streamer, std::unique_ptr< remark::detail::RemarkEmittingPolicyBase > remarkEmittingPolicy, const remark::RemarkCategories &cats, bool printAsEmitRemarks=false)
Setup remarks for the context.
@ RemarkPassed
An optimization was applied.
@ RemarkAnalysis
Informational context (e.g., analysis numbers) without a pass/fail outcome.
@ RemarkMissed
A profitable optimization opportunity was found but not applied.
@ RemarkFailure
The compiler attempted the optimization but failed (e.g., legality checks, or better opportunites).
InFlightDiagnostic emitRemark(Location loc)
Utility method to emit a remark message using this location.
Define an the set of categories to accept.
Definition: Remarks.h:30
std::optional< std::string > missed
Definition: Remarks.h:31
std::optional< std::string > all
Definition: Remarks.h:31
std::optional< std::string > passed
Definition: Remarks.h:31
std::optional< std::string > analysis
Definition: Remarks.h:31
std::optional< std::string > failed
Definition: Remarks.h:31
Options to create a Remark.
Definition: Remarks.h:57
StringRef categoryName
Definition: Remarks.h:59
Arg(llvm::StringRef m)
Definition: Remarks.h:106