MLIR 23.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
18using namespace mlir::remark::detail;
19using namespace mlir::remark;
20//------------------------------------------------------------------------------
21// Remark
22//------------------------------------------------------------------------------
23
24Remark::Arg::Arg(llvm::StringRef k, Value v) : key(k) {
25 llvm::raw_string_ostream os(val);
26 os << v;
27}
28
29Remark::Arg::Arg(llvm::StringRef k, Type t) : key(k) {
30 llvm::raw_string_ostream os(val);
31 os << t;
32}
33
34Remark::Arg::Arg(llvm::StringRef k, Attribute a) : key(k), attr(a) {
35 llvm::raw_string_ostream os(val);
36 os << a;
37}
38
39void Remark::insert(llvm::StringRef s) { args.emplace_back(s); }
40void Remark::insert(Arg a) { args.push_back(std::move(a)); }
41
42// Simple helper to print key=val list (sorted).
43static void printArgs(llvm::raw_ostream &os, llvm::ArrayRef<Remark::Arg> args) {
44 if (args.empty())
45 return;
46
47 llvm::SmallVector<Remark::Arg, 8> sorted(args.begin(), args.end());
48 llvm::sort(sorted, [](const Remark::Arg &a, const Remark::Arg &b) {
49 return a.key < b.key;
50 });
51
52 for (size_t i = 0; i < sorted.size(); ++i) {
53 const auto &a = sorted[i];
54 os << a.key << "=";
55
56 llvm::StringRef val(a.val);
57 bool needsQuote = val.contains(' ') || val.contains(',') ||
58 val.contains('{') || val.contains('}');
59 if (needsQuote)
60 os << '"' << val << '"';
61 else
62 os << val;
63
64 if (i + 1 < sorted.size())
65 os << ", ";
66 }
67}
68
69/// Print the remark to the given output stream.
70/// Example output:
71// clang-format off
72/// [Missed] Category: Loop | Pass:Unroller | Function=main | Reason="tripCount=4 < threshold=256"
73/// [Failure] LoopOptimizer | Reason="failed due to unsupported pattern"
74// clang-format on
75void Remark::print(llvm::raw_ostream &os, bool printLocation) const {
76 // Header: [Type] pass:remarkName
77 StringRef type = getRemarkTypeString();
79 StringRef name = remarkName;
80
81 os << '[' << type << "] ";
82 os << name << " | ";
83 if (!categoryName.empty())
84 os << "Category:" << categoryName << " | ";
85 if (!functionName.empty())
86 os << "Function=" << getFunction() << " | ";
87
88 if (printLocation) {
89 if (auto flc = mlir::dyn_cast<mlir::FileLineColLoc>(getLocation())) {
90 os << " @" << flc.getFilename() << ":" << flc.getLine() << ":"
91 << flc.getColumn();
92 }
93 }
94
95 printArgs(os, getArgs());
96}
97
98std::string Remark::getMsg() const {
99 std::string s;
100 llvm::raw_string_ostream os(s);
101 print(os);
102 os.flush();
103 return s;
104}
105
106llvm::StringRef Remark::getRemarkTypeString() const {
107 switch (remarkKind) {
109 return "Unknown";
111 return "Passed";
113 return "Missed";
115 return "Failure";
117 return "Analysis";
118 }
119 llvm_unreachable("Unknown remark kind");
120}
121
122llvm::remarks::Type Remark::getRemarkType() const {
123 switch (remarkKind) {
125 return llvm::remarks::Type::Unknown;
127 return llvm::remarks::Type::Passed;
129 return llvm::remarks::Type::Missed;
131 return llvm::remarks::Type::Failure;
133 return llvm::remarks::Type::Analysis;
134 }
135 llvm_unreachable("Unknown remark kind");
136}
137
138llvm::remarks::Remark Remark::generateRemark() const {
139 auto locLambda = [&]() -> llvm::remarks::RemarkLocation {
140 if (auto flc = dyn_cast<FileLineColLoc>(getLocation()))
141 return {flc.getFilename(), flc.getLine(), flc.getColumn()};
142 return {"<unknown file>", 0, 0};
143 };
144
145 llvm::remarks::Remark r; // The result.
146 r.RemarkType = getRemarkType();
147 r.RemarkName = getRemarkName();
148 // MLIR does not use passes; instead, it has categories and sub-categories.
149 r.PassName = getCombinedCategoryName();
150 r.FunctionName = getFunction();
151 r.Loc = locLambda();
152 // Add all args (includes RemarkId and RelatedTo if they were added).
153 for (const Remark::Arg &arg : getArgs()) {
154 r.Args.emplace_back();
155 r.Args.back().Key = arg.key;
156 r.Args.back().Val = arg.val;
157 }
158 return r;
159}
160
161//===----------------------------------------------------------------------===//
162// InFlightRemark
163//===----------------------------------------------------------------------===//
164
166 if (remark && owner)
167 owner->report(std::move(*remark));
168 owner = nullptr;
169}
170
171//===----------------------------------------------------------------------===//
172// Remark Engine
173//===----------------------------------------------------------------------===//
174
175template <typename RemarkT>
176InFlightRemark RemarkEngine::makeRemark(Location loc, RemarkOpts opts) {
177 static_assert(std::is_base_of_v<Remark, RemarkT>,
178 "RemarkT must derive from Remark");
179 auto remark = std::make_unique<RemarkT>(loc, opts);
180 remark->setId(generateRemarkId());
181 return InFlightRemark(*this, std::move(remark));
182}
183
184template <typename RemarkT>
186RemarkEngine::emitIfEnabled(Location loc, RemarkOpts opts,
187 bool (RemarkEngine::*isEnabled)(StringRef) const) {
188 return (this->*isEnabled)(opts.categoryName) ? makeRemark<RemarkT>(loc, opts)
189 : InFlightRemark{};
190}
191
192bool RemarkEngine::isMissedOptRemarkEnabled(StringRef categoryName) const {
193 return missFilter && missFilter->match(categoryName);
194}
195
196bool RemarkEngine::isPassedOptRemarkEnabled(StringRef categoryName) const {
197 return passedFilter && passedFilter->match(categoryName);
198}
199
200bool RemarkEngine::isAnalysisOptRemarkEnabled(StringRef categoryName) const {
201 return analysisFilter && analysisFilter->match(categoryName);
202}
203
204bool RemarkEngine::isFailedOptRemarkEnabled(StringRef categoryName) const {
205 return failedFilter && failedFilter->match(categoryName);
206}
207
209 RemarkOpts opts) {
210 return emitIfEnabled<OptRemarkPass>(loc, opts,
211 &RemarkEngine::isPassedOptRemarkEnabled);
212}
213
215 RemarkOpts opts) {
216 return emitIfEnabled<OptRemarkMissed>(
217 loc, opts, &RemarkEngine::isMissedOptRemarkEnabled);
218}
219
221 RemarkOpts opts) {
222 return emitIfEnabled<OptRemarkFailure>(
223 loc, opts, &RemarkEngine::isFailedOptRemarkEnabled);
224}
225
227 RemarkOpts opts) {
228 return emitIfEnabled<OptRemarkAnalysis>(
229 loc, opts, &RemarkEngine::isAnalysisOptRemarkEnabled);
230}
231
232//===----------------------------------------------------------------------===//
233// RemarkEngine
234//===----------------------------------------------------------------------===//
235
236void RemarkEngine::reportImpl(const Remark &remark) {
237 // Stream the remark
238 if (remarkStreamer) {
239 remarkStreamer->streamOptimizationRemark(remark);
240 }
241
242 // Print using MLIR's diagnostic
243 if (printAsEmitRemarks)
244 emitRemark(remark.getLocation(), remark.getMsg());
245}
246
248 if (remarkEmittingPolicy)
249 remarkEmittingPolicy->reportRemark(remark);
250}
251
253 if (remarkEmittingPolicy)
254 remarkEmittingPolicy->finalize();
255
256 if (remarkStreamer)
257 remarkStreamer->finalize();
258}
259
260llvm::LogicalResult RemarkEngine::initialize(
261 std::unique_ptr<MLIRRemarkStreamerBase> streamer,
262 std::unique_ptr<RemarkEmittingPolicyBase> remarkEmittingPolicy,
263 std::string *errMsg) {
264 remarkStreamer = std::move(streamer);
265
266 auto reportFunc = llvm::bind_front<&RemarkEngine::reportImpl>(this);
267 remarkEmittingPolicy->initialize(ReportFn(std::move(reportFunc)));
268
269 this->remarkEmittingPolicy = std::move(remarkEmittingPolicy);
270 return success();
271}
272
273/// Returns true if filter is already anchored like ^...$
274static bool isAnchored(llvm::StringRef s) {
275 s = s.trim();
276 return s.starts_with("^") && s.ends_with("$"); // note: startswith/endswith
277}
278
279/// Anchor the entire pattern so it matches the whole string.
280static std::string anchorWhole(llvm::StringRef filter) {
281 if (isAnchored(filter))
282 return filter.str();
283 return (llvm::Twine("^(") + filter + ")$").str();
284}
285
286/// Build a combined filter from cats.all and a category-specific pattern.
287/// If neither is present, return std::nullopt. Otherwise "(all|specific)"
288/// and anchor once. Also validate before returning.
289static std::optional<llvm::Regex>
291 const std::optional<std::string> &specific) {
293 if (cats.all && !cats.all->empty())
294 parts.emplace_back(*cats.all);
295 if (specific && !specific->empty())
296 parts.emplace_back(*specific);
297
298 if (parts.empty())
299 return std::nullopt;
300
301 std::string joined = llvm::join(parts, "|");
302 std::string anchored = anchorWhole(joined);
303
304 llvm::Regex rx(anchored);
305 std::string err;
306 if (!rx.isValid(err))
307 return std::nullopt;
308
309 return std::make_optional<llvm::Regex>(std::move(rx));
310}
311
312RemarkEngine::RemarkEngine(bool printAsEmitRemarks,
313 const RemarkCategories &cats)
314 : printAsEmitRemarks(printAsEmitRemarks) {
315 if (cats.passed)
316 passedFilter = buildFilter(cats, cats.passed);
317 if (cats.missed)
318 missFilter = buildFilter(cats, cats.missed);
319 if (cats.analysis)
320 analysisFilter = buildFilter(cats, cats.analysis);
321 if (cats.failed)
322 failedFilter = buildFilter(cats, cats.failed);
323}
324
326 MLIRContext &ctx, std::unique_ptr<detail::MLIRRemarkStreamerBase> streamer,
327 std::unique_ptr<detail::RemarkEmittingPolicyBase> remarkEmittingPolicy,
328 const RemarkCategories &cats, bool printAsEmitRemarks) {
329 auto engine =
330 std::make_unique<detail::RemarkEngine>(printAsEmitRemarks, cats);
331
332 std::string errMsg;
333 if (failed(engine->initialize(std::move(streamer),
334 std::move(remarkEmittingPolicy), &errMsg))) {
335 llvm::report_fatal_error(
336 llvm::Twine("Failed to initialize remark engine. Error: ") + errMsg);
337 }
338 ctx.setRemarkEngine(std::move(engine));
339
340 return success();
341}
342
343//===----------------------------------------------------------------------===//
344// Remark emitting policies
345//===----------------------------------------------------------------------===//
346
347namespace mlir::remark {
350
352 assert(reportImpl && "reportImpl is not set");
353
354 // Build ID -> Remark* lookup for resolving related remark references.
356 llvm::DenseSet<uint64_t> childIds; // IDs referenced as children
357
358 for (const auto &remark : postponedRemarks) {
359 if (remark.getId())
360 idMap[remark.getId().getValue()] = &remark;
361 for (auto relId : remark.getRelatedRemarkIds())
362 childIds.insert(relId.getValue());
363 }
364
365 // Emit remarks with related remarks grouped after their parents.
366 // Parent remarks are emitted first, followed by their related (child)
367 // remarks. Child-only remarks are skipped at the top level to avoid
368 // duplication.
369 for (const auto &remark : postponedRemarks) {
370 if (remark.getId() && childIds.count(remark.getId().getValue()))
371 continue; // will be printed grouped under its parent
372
374
375 // Emit related remarks immediately after the parent.
376 for (auto relId : remark.getRelatedRemarkIds()) {
377 if (const auto *related = idMap.lookup(relId.getValue()))
378 reportImpl(*related);
379 }
380 }
381}
382
383} // namespace mlir::remark
return success()
b
Return true if permutation is a valid permutation of the outer_dims_perm (case OuterOrInnerPerm::Oute...
if(!isCopyOut)
static bool isAnchored(llvm::StringRef s)
Returns true if filter is already anchored like ^...$.
Definition Remarks.cpp:274
static void printArgs(llvm::raw_ostream &os, llvm::ArrayRef< Remark::Arg > args)
Definition Remarks.cpp:43
static std::string anchorWhole(llvm::StringRef filter)
Anchor the entire pattern so it matches the whole string.
Definition Remarks.cpp:280
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:290
Attributes are known-constant values of operations.
Definition Attributes.h:25
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
void finalize() override
Emits all stored remarks.
Definition Remarks.cpp:351
A wrapper for linking remarks by query - searches the engine's registry at stream time and links to a...
Definition Remarks.h:402
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:220
void report(const Remark &&remark)
Report a remark.
Definition Remarks.cpp:247
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:208
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:260
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:214
~RemarkEngine()
Destructor that will close the output file and reset the main remark streamer.
Definition Remarks.cpp:252
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:226
std::string functionName
Name of the covering function like interface.
Definition Remarks.h:285
void print(llvm::raw_ostream &os, bool printLocation=false) const
Print the remark to the given output stream.
Definition Remarks.cpp:75
ArrayRef< Arg > getArgs() const
Definition Remarks.h:241
SmallVector< Arg, 4 > args
Args collected via the streaming interface.
Definition Remarks.h:304
StringRef getFunction() const
Definition Remarks.h:215
llvm::remarks::Type getRemarkType() const
Definition Remarks.cpp:122
StringRef getRemarkTypeString() const
Definition Remarks.cpp:106
std::string remarkName
Remark identifier.
Definition Remarks.h:301
Location getLocation() const
Definition Remarks.h:211
llvm::StringRef getCombinedCategoryName() const
Definition Remarks.h:223
RemarkKind remarkKind
Keeps the MLIR diagnostic kind, which is used to determine the diagnostic kind in the LLVM remark str...
Definition Remarks.h:282
StringRef getRemarkName() const
Definition Remarks.h:233
void insert(llvm::StringRef s)
Definition Remarks.cpp:39
llvm::remarks::Remark generateRemark() const
Diagnostic -> Remark.
Definition Remarks.cpp:138
std::string categoryName
Category name e.g., "Unroll" or "UnrollAndJam".
Definition Remarks.h:290
std::string getMsg() const
Definition Remarks.cpp:98
llvm::unique_function< void(const Remark &)> ReportFn
Definition Remarks.h:465
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition Remarks.h:717
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.
Definition Remarks.h:74
@ RemarkAnalysis
Informational context (e.g., analysis numbers) without a pass/fail outcome.
Definition Remarks.h:85
@ RemarkMissed
A profitable optimization opportunity was found but not applied.
Definition Remarks.h:77
@ RemarkFailure
The compiler attempted the optimization but failed (e.g., legality checks, or better opportunites).
Definition Remarks.h:81
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:64
std::optional< std::string > missed
Definition Remarks.h:65
std::optional< std::string > all
Definition Remarks.h:65
std::optional< std::string > passed
Definition Remarks.h:65
std::optional< std::string > analysis
Definition Remarks.h:65
std::optional< std::string > failed
Definition Remarks.h:65
Options to create a Remark.
Definition Remarks.h:95
std::optional< Attribute > attr
Optional attribute storage for Attribute-based args.
Definition Remarks.h:174