MLIR 24.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 = getLocation()->findInstanceOf<FileLineColLoc>()) {
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 // Remarks are often emitted at name, fused or call-site locations wrapping
141 // a file location; use the first file location found inside.
142 if (auto flc = getLocation()->findInstanceOf<FileLineColLoc>())
143 return {flc.getFilename(), flc.getLine(), flc.getColumn()};
144 return {"<unknown file>", 0, 0};
145 };
146
147 llvm::remarks::Remark r; // The result.
148 r.RemarkType = getRemarkType();
149 r.RemarkName = getRemarkName();
150 // MLIR does not use passes; instead, it has categories and sub-categories.
151 r.PassName = getCombinedCategoryName();
152 r.FunctionName = getFunction();
153 r.Loc = locLambda();
154 // Add all args (includes RemarkId and RelatedTo if they were added).
155 for (const Remark::Arg &arg : getArgs()) {
156 r.Args.emplace_back();
157 r.Args.back().Key = arg.key;
158 r.Args.back().Val = arg.val;
159 }
160 return r;
161}
162
163//===----------------------------------------------------------------------===//
164// InFlightRemark
165//===----------------------------------------------------------------------===//
166
168 if (remark && owner)
169 owner->report(std::move(*remark));
170 owner = nullptr;
171}
172
173//===----------------------------------------------------------------------===//
174// Remark Engine
175//===----------------------------------------------------------------------===//
176
177template <typename RemarkT>
178InFlightRemark RemarkEngine::makeRemark(Location loc, RemarkOpts opts) {
179 static_assert(std::is_base_of_v<Remark, RemarkT>,
180 "RemarkT must derive from Remark");
181 auto remark = std::make_unique<RemarkT>(loc, opts);
182 remark->setId(generateRemarkId());
183 return InFlightRemark(*this, std::move(remark));
184}
185
186template <typename RemarkT>
188RemarkEngine::emitIfEnabled(Location loc, RemarkOpts opts,
189 bool (RemarkEngine::*isEnabled)(StringRef) const) {
190 return (this->*isEnabled)(opts.categoryName) ? makeRemark<RemarkT>(loc, opts)
191 : InFlightRemark{};
192}
193
195 return missFilter && missFilter->match(categoryName);
196}
197
199 return passedFilter && passedFilter->match(categoryName);
200}
201
203 return analysisFilter && analysisFilter->match(categoryName);
204}
205
207 return failedFilter && failedFilter->match(categoryName);
208}
209
211 StringRef categoryName) const {
212 switch (kind) {
214 return false;
223 }
224 llvm_unreachable("Unknown remark kind");
225}
226
228 RemarkOpts opts) {
229 return emitIfEnabled<OptRemarkPass>(loc, opts,
231}
232
234 RemarkOpts opts) {
235 return emitIfEnabled<OptRemarkMissed>(
237}
238
240 RemarkOpts opts) {
241 return emitIfEnabled<OptRemarkFailure>(
243}
244
246 RemarkOpts opts) {
247 return emitIfEnabled<OptRemarkAnalysis>(
249}
250
251//===----------------------------------------------------------------------===//
252// RemarkEngine
253//===----------------------------------------------------------------------===//
254
255void RemarkEngine::reportImpl(const Remark &remark) {
256 // Stream the remark
257 if (remarkStreamer) {
258 remarkStreamer->streamOptimizationRemark(remark);
259 }
260
261 // Print using MLIR's diagnostic
262 if (printAsEmitRemarks)
263 emitRemark(remark.getLocation(), remark.getMsg());
264}
265
267 if (remarkEmittingPolicy)
268 remarkEmittingPolicy->reportRemark(remark);
269}
270
272 if (remarkEmittingPolicy)
273 remarkEmittingPolicy->finalize();
274
275 if (remarkStreamer)
276 remarkStreamer->finalize();
277}
278
279llvm::LogicalResult RemarkEngine::initialize(
280 std::unique_ptr<MLIRRemarkStreamerBase> streamer,
281 std::unique_ptr<RemarkEmittingPolicyBase> remarkEmittingPolicy,
282 std::string *errMsg) {
283 remarkStreamer = std::move(streamer);
284
285 auto reportFunc = llvm::bind_front<&RemarkEngine::reportImpl>(this);
286 remarkEmittingPolicy->initialize(ReportFn(std::move(reportFunc)));
287
288 this->remarkEmittingPolicy = std::move(remarkEmittingPolicy);
289 return success();
290}
291
292/// Returns true if filter is already anchored like ^...$
293static bool isAnchored(llvm::StringRef s) {
294 s = s.trim();
295 return s.starts_with("^") && s.ends_with("$"); // note: startswith/endswith
296}
297
298/// Anchor the entire pattern so it matches the whole string.
299static std::string anchorWhole(llvm::StringRef filter) {
300 if (isAnchored(filter))
301 return filter.str();
302 return (llvm::Twine("^(") + filter + ")$").str();
303}
304
305/// Build a combined filter from cats.all and a category-specific pattern.
306/// If neither is present, return std::nullopt. Otherwise "(all|specific)"
307/// and anchor once. Also validate before returning.
308static std::optional<llvm::Regex>
310 const std::optional<std::string> &specific) {
312 if (cats.all && !cats.all->empty())
313 parts.emplace_back(*cats.all);
314 if (specific && !specific->empty())
315 parts.emplace_back(*specific);
316
317 if (parts.empty())
318 return std::nullopt;
319
320 std::string joined = llvm::join(parts, "|");
321 std::string anchored = anchorWhole(joined);
322
323 llvm::Regex rx(anchored);
324 std::string err;
325 if (!rx.isValid(err))
326 return std::nullopt;
327
328 return std::make_optional<llvm::Regex>(std::move(rx));
329}
330
331RemarkEngine::RemarkEngine(bool printAsEmitRemarks,
332 const RemarkCategories &cats)
333 : printAsEmitRemarks(printAsEmitRemarks) {
334 if (cats.passed)
335 passedFilter = buildFilter(cats, cats.passed);
336 if (cats.missed)
337 missFilter = buildFilter(cats, cats.missed);
338 if (cats.analysis)
339 analysisFilter = buildFilter(cats, cats.analysis);
340 if (cats.failed)
341 failedFilter = buildFilter(cats, cats.failed);
342}
343
345 MLIRContext &ctx, std::unique_ptr<detail::MLIRRemarkStreamerBase> streamer,
346 std::unique_ptr<detail::RemarkEmittingPolicyBase> remarkEmittingPolicy,
347 const RemarkCategories &cats, bool printAsEmitRemarks) {
348 auto engine =
349 std::make_unique<detail::RemarkEngine>(printAsEmitRemarks, cats);
350
351 std::string errMsg;
352 if (failed(engine->initialize(std::move(streamer),
353 std::move(remarkEmittingPolicy), &errMsg))) {
354 llvm::report_fatal_error(
355 llvm::Twine("Failed to initialize remark engine. Error: ") + errMsg);
356 }
357 ctx.setRemarkEngine(std::move(engine));
358
359 return success();
360}
361
362//===----------------------------------------------------------------------===//
363// Remark emitting policies
364//===----------------------------------------------------------------------===//
365
366namespace mlir::remark {
369
371 assert(reportImpl && "reportImpl is not set");
372
373 // Take the pending remarks so that a second finalize(), e.g. from the engine
374 // destructor after an explicit call, does not emit them again. IDs are
375 // assigned in creation order; sorting by them keeps the output independent
376 // of the set's hash layout.
377 std::vector<detail::Remark> remarks(postponedRemarks.begin(),
378 postponedRemarks.end());
379 postponedRemarks.clear();
380 llvm::sort(remarks, [](const auto &lhs, const auto &rhs) {
381 return lhs.getId().getValue() < rhs.getId().getValue();
382 });
383
384 // Build ID -> Remark* lookup for resolving related remark references.
386 llvm::DenseSet<uint64_t> childIds; // IDs referenced as children
387
388 for (const auto &remark : remarks) {
389 if (remark.getId())
390 idMap[remark.getId().getValue()] = &remark;
391 for (auto relId : remark.getRelatedRemarkIds())
392 childIds.insert(relId.getValue());
393 }
394
395 // Emit remarks with related remarks grouped after their parents.
396 // Parent remarks are emitted first, followed by their related (child)
397 // remarks. Child-only remarks are skipped at the top level to avoid
398 // duplication.
399 for (const auto &remark : remarks) {
400 if (remark.getId() && childIds.contains(remark.getId().getValue()))
401 continue; // will be printed grouped under its parent
402
404
405 // Emit related remarks immediately after the parent.
406 for (auto relId : remark.getRelatedRemarkIds()) {
407 if (const auto *related = idMap.lookup(relId.getValue()))
408 reportImpl(*related);
409 }
410 }
411}
412
413} // namespace mlir::remark
return success()
if(failed(verifyVectorMemoryOp(getOperation(), memrefType, getVectorType()))) return failure()
static bool isAnchored(llvm::StringRef s)
Returns true if filter is already anchored like ^...$.
Definition Remarks.cpp:293
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:299
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:309
b
Return true if permutation is a valid permutation of the outer_dims_perm (case OuterOrInnerPerm::Oute...
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:64
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 and drains all stored remarks.
Definition Remarks.cpp:370
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:239
bool isPassedOptRemarkEnabled(StringRef categoryName) const
Return true if passed optimization remarks are enabled for the given category.
Definition Remarks.cpp:198
void report(const Remark &&remark)
Report a remark.
Definition Remarks.cpp:266
bool isMissedOptRemarkEnabled(StringRef categoryName) const
Return true if missed optimization remarks are enabled for the given category.
Definition Remarks.cpp:194
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:227
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:279
RemarkEngine()=delete
Default constructor is deleted, use the other constructor.
bool isAnalysisOptRemarkEnabled(StringRef categoryName) const
Return true if analysis remarks are enabled for the given category.
Definition Remarks.cpp:202
bool isRemarkEnabled(RemarkKind kind, StringRef categoryName) const
Return true if remarks of the given kind are enabled for the given category.
Definition Remarks.cpp:210
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:233
~RemarkEngine()
Destructor that will close the output file and reset the main remark streamer.
Definition Remarks.cpp:271
bool isFailedOptRemarkEnabled(StringRef categoryName) const
Return true if failed optimization remarks are enabled for the given category.
Definition Remarks.cpp:206
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:245
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:734
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.
RemarkKind
Categories describe the outcome of an transformation, not the mechanics of emitting/serializing remar...
Definition Remarks.h:70
@ 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.
Arg(llvm::StringRef m)
Definition Remarks.h:176
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