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/DenseMap.h"
16#include "llvm/ADT/DenseSet.h"
17#include "llvm/ADT/STLExtras.h"
18#include "llvm/ADT/StringExtras.h"
19#include "llvm/ADT/StringRef.h"
20
21#include <mutex>
22
23using namespace mlir::remark::detail;
24using namespace mlir::remark;
25//------------------------------------------------------------------------------
26// Remark
27//------------------------------------------------------------------------------
28
29Remark::Arg::Arg(llvm::StringRef k, Value v) : key(k) {
30 llvm::raw_string_ostream os(val);
31 os << v;
32}
33
34Remark::Arg::Arg(llvm::StringRef k, Type t) : key(k) {
35 llvm::raw_string_ostream os(val);
36 os << t;
37}
38
39Remark::Arg::Arg(llvm::StringRef k, Attribute a) : key(k), attr(a) {
40 llvm::raw_string_ostream os(val);
41 os << a;
42}
43
44void Remark::insert(llvm::StringRef s) { args.emplace_back(s); }
45void Remark::insert(Arg a) { args.push_back(std::move(a)); }
46
49 llvm::erase_if(args, [](const Arg &arg) { return arg.isLink; });
50 relatedRemarks.clear();
51 for (RemarkId id : copy)
53}
54
55// Simple helper to print key=val list (sorted).
56static void printArgs(llvm::raw_ostream &os, llvm::ArrayRef<Remark::Arg> args) {
57 if (args.empty())
58 return;
59
60 // Preserve the relative order of repeated keys, including related IDs.
61 llvm::SmallVector<Remark::Arg, 8> sorted(args.begin(), args.end());
62 llvm::stable_sort(sorted, [](const Remark::Arg &a, const Remark::Arg &b) {
63 return a.key < b.key;
64 });
65
66 for (size_t i = 0; i < sorted.size(); ++i) {
67 const auto &a = sorted[i];
68 os << a.key << "=";
69
70 llvm::StringRef val(a.val);
71 bool needsQuote = val.contains(' ') || val.contains(',') ||
72 val.contains('{') || val.contains('}');
73 if (needsQuote)
74 os << '"' << val << '"';
75 else
76 os << val;
77
78 if (i + 1 < sorted.size())
79 os << ", ";
80 }
81}
82
83/// Print the remark to the given output stream.
84/// Example output:
85// clang-format off
86/// [Missed] Category: Loop | Pass:Unroller | Function=main | Reason="tripCount=4 < threshold=256"
87/// [Failure] LoopOptimizer | Reason="failed due to unsupported pattern"
88// clang-format on
89void Remark::print(llvm::raw_ostream &os, bool printLocation) const {
90 // Header: [Type] pass:remarkName
91 StringRef type = getRemarkTypeString();
93 StringRef name = remarkName;
94
95 os << '[' << type << "] ";
96 os << name << " | ";
97 if (!categoryName.empty())
98 os << "Category:" << categoryName << " | ";
99 if (!functionName.empty())
100 os << "Function=" << getFunction() << " | ";
101
102 if (printLocation) {
103 if (auto flc = getLocation()->findInstanceOf<FileLineColLoc>()) {
104 os << " @" << flc.getFilename() << ":" << flc.getLine() << ":"
105 << flc.getColumn();
106 }
107 }
108
109 printArgs(os, getArgs());
110}
111
112std::string Remark::getMsg() const {
113 std::string s;
114 llvm::raw_string_ostream os(s);
115 print(os);
116 os.flush();
117 return s;
118}
119
120llvm::StringRef Remark::getRemarkTypeString() const {
121 switch (remarkKind) {
123 return "Unknown";
125 return "Passed";
127 return "Missed";
129 return "Failure";
131 return "Analysis";
132 }
133 llvm_unreachable("Unknown remark kind");
134}
135
136llvm::remarks::Type Remark::getRemarkType() const {
137 switch (remarkKind) {
139 return llvm::remarks::Type::Unknown;
141 return llvm::remarks::Type::Passed;
143 return llvm::remarks::Type::Missed;
145 return llvm::remarks::Type::Failure;
147 return llvm::remarks::Type::Analysis;
148 }
149 llvm_unreachable("Unknown remark kind");
150}
151
152llvm::remarks::Remark Remark::generateRemark() const {
153 auto locLambda = [&]() -> llvm::remarks::RemarkLocation {
154 // Remarks are often emitted at name, fused or call-site locations wrapping
155 // a file location; use the first file location found inside.
156 if (auto flc = getLocation()->findInstanceOf<FileLineColLoc>())
157 return {flc.getFilename(), flc.getLine(), flc.getColumn()};
158 return {"<unknown file>", 0, 0};
159 };
160
161 llvm::remarks::Remark r; // The result.
162 r.RemarkType = getRemarkType();
163 r.RemarkName = getRemarkName();
164 // MLIR does not use passes; instead, it has categories and sub-categories.
165 r.PassName = getCombinedCategoryName();
166 r.FunctionName = getFunction();
167 r.Loc = locLambda();
168 // Keep serialized strings owned by the source remark.
169 for (const Remark::Arg &arg : getArgs()) {
170 r.Args.emplace_back();
171 r.Args.back().Key = arg.key;
172 r.Args.back().Val = arg.val;
173 }
174 return r;
175}
176
177//===----------------------------------------------------------------------===//
178// InFlightRemark
179//===----------------------------------------------------------------------===//
180
182 if (remark && owner)
183 owner->report(std::move(*remark));
184 owner = nullptr;
185}
186
187//===----------------------------------------------------------------------===//
188// Remark Engine
189//===----------------------------------------------------------------------===//
190
191template <typename RemarkT>
192InFlightRemark RemarkEngine::makeRemark(Location loc, RemarkOpts opts) {
193 static_assert(std::is_base_of_v<Remark, RemarkT>,
194 "RemarkT must derive from Remark");
195 auto remark = std::make_unique<RemarkT>(loc, opts);
196 remark->setId(generateRemarkId());
197 return InFlightRemark(*this, std::move(remark));
198}
199
200template <typename RemarkT>
202RemarkEngine::emitIfEnabled(Location loc, RemarkOpts opts,
203 bool (RemarkEngine::*isEnabled)(StringRef) const) {
204 return (this->*isEnabled)(opts.categoryName) ? makeRemark<RemarkT>(loc, opts)
205 : InFlightRemark{};
206}
207
209 return missFilter && missFilter->match(categoryName);
210}
211
213 return passedFilter && passedFilter->match(categoryName);
214}
215
217 return analysisFilter && analysisFilter->match(categoryName);
218}
219
221 return failedFilter && failedFilter->match(categoryName);
222}
223
225 StringRef categoryName) const {
226 switch (kind) {
228 return false;
237 }
238 llvm_unreachable("Unknown remark kind");
239}
240
242 RemarkOpts opts) {
243 return emitIfEnabled<OptRemarkPass>(loc, opts,
245}
246
248 RemarkOpts opts) {
249 return emitIfEnabled<OptRemarkMissed>(
251}
252
254 RemarkOpts opts) {
255 return emitIfEnabled<OptRemarkFailure>(
257}
258
260 RemarkOpts opts) {
261 return emitIfEnabled<OptRemarkAnalysis>(
263}
264
265//===----------------------------------------------------------------------===//
266// RemarkEngine
267//===----------------------------------------------------------------------===//
268
269void RemarkEngine::reportImpl(const Remark &remark) {
270 // Stream the remark
271 if (remarkStreamer) {
272 remarkStreamer->streamOptimizationRemark(remark);
273 }
274
275 // Print using MLIR's diagnostic
276 if (printAsEmitRemarks)
277 emitRemark(remark.getLocation(), remark.getMsg());
278}
279
281 if (remarkEmittingPolicy)
282 remarkEmittingPolicy->reportRemark(remark);
283}
284
286 if (remarkEmittingPolicy)
287 remarkEmittingPolicy->finalize();
288
289 if (remarkStreamer)
290 remarkStreamer->finalize();
291}
292
293llvm::LogicalResult RemarkEngine::initialize(
294 std::unique_ptr<MLIRRemarkStreamerBase> streamer,
295 std::unique_ptr<RemarkEmittingPolicyBase> remarkEmittingPolicy,
296 std::string *errMsg) {
297 remarkStreamer = std::move(streamer);
298
299 auto reportFunc = llvm::bind_front<&RemarkEngine::reportImpl>(this);
300 remarkEmittingPolicy->initialize(ReportFn(std::move(reportFunc)));
301
302 this->remarkEmittingPolicy = std::move(remarkEmittingPolicy);
303 return success();
304}
305
306/// Returns true if filter is already anchored like ^...$
307static bool isAnchored(llvm::StringRef s) {
308 s = s.trim();
309 return s.starts_with("^") && s.ends_with("$"); // note: startswith/endswith
310}
311
312/// Anchor the entire pattern so it matches the whole string.
313static std::string anchorWhole(llvm::StringRef filter) {
314 if (isAnchored(filter))
315 return filter.str();
316 return (llvm::Twine("^(") + filter + ")$").str();
317}
318
319/// Build a combined filter from cats.all and a category-specific pattern.
320/// If neither is present, return std::nullopt. Otherwise "(all|specific)"
321/// and anchor once. Also validate before returning.
322static std::optional<llvm::Regex>
324 const std::optional<std::string> &specific) {
326 if (cats.all && !cats.all->empty())
327 parts.emplace_back(*cats.all);
328 if (specific && !specific->empty())
329 parts.emplace_back(*specific);
330
331 if (parts.empty())
332 return std::nullopt;
333
334 std::string joined = llvm::join(parts, "|");
335 std::string anchored = anchorWhole(joined);
336
337 llvm::Regex rx(anchored);
338 std::string err;
339 if (!rx.isValid(err))
340 return std::nullopt;
341
342 return std::make_optional<llvm::Regex>(std::move(rx));
343}
344
345RemarkEngine::RemarkEngine(bool printAsEmitRemarks,
346 const RemarkCategories &cats)
347 : printAsEmitRemarks(printAsEmitRemarks) {
348 if (cats.passed)
349 passedFilter = buildFilter(cats, cats.passed);
350 if (cats.missed)
351 missFilter = buildFilter(cats, cats.missed);
352 if (cats.analysis)
353 analysisFilter = buildFilter(cats, cats.analysis);
354 if (cats.failed)
355 failedFilter = buildFilter(cats, cats.failed);
356}
357
359 MLIRContext &ctx, std::unique_ptr<detail::MLIRRemarkStreamerBase> streamer,
360 std::unique_ptr<detail::RemarkEmittingPolicyBase> remarkEmittingPolicy,
361 const RemarkCategories &cats, bool printAsEmitRemarks) {
362 auto engine =
363 std::make_unique<detail::RemarkEngine>(printAsEmitRemarks, cats);
364
365 std::string errMsg;
366 if (failed(engine->initialize(std::move(streamer),
367 std::move(remarkEmittingPolicy), &errMsg))) {
368 llvm::report_fatal_error(
369 llvm::Twine("Failed to initialize remark engine. Error: ") + errMsg);
370 }
371 ctx.setRemarkEngine(std::move(engine));
372
373 return success();
374}
375
376//===----------------------------------------------------------------------===//
377// Remark emitting policies
378//===----------------------------------------------------------------------===//
379
380namespace mlir::remark {
382
384
386 const llvm::DenseSet<uint64_t> &discardedIds) {
387 if (discardedIds.empty())
388 return;
389 auto ids = llvm::to_vector(remark.getRelatedRemarkIds());
390 llvm::erase_if(
391 ids, [&](RemarkId id) { return discardedIds.contains(id.getValue()); });
392 if (ids.size() != remark.getRelatedRemarkIds().size())
393 remark.setRelatedRemarkIds(ids);
394}
395
398 std::scoped_lock<std::mutex> lock(mutex);
399 auto previous = pendingRemarks.find(copy);
400 if (previous != pendingRemarks.end()) {
401 if (previous->getId() && previous->getId() != copy.getId())
402 discardedIds.insert(previous->getId().getValue());
403 pendingRemarks.erase(previous);
404 }
405 pendingRemarks.insert(std::move(copy));
406}
407
409 assert(reportImpl && "reportImpl is not set");
410
412 llvm::DenseSet<uint64_t> discarded;
413 {
414 std::scoped_lock<std::mutex> lock(mutex);
415 remarks = llvm::to_vector<0>(pendingRemarks);
416 pendingRemarks.clear();
417 discarded.swap(discardedIds);
418 }
419
420 // A re-reported ID that survives is still a valid target.
421 for (const auto &remark : remarks)
422 if (remark.getId())
423 discarded.erase(remark.getId().getValue());
424 for (auto &remark : remarks)
425 dropDiscardedLinks(remark, discarded);
426
427 llvm::sort(remarks, [](const auto &lhs, const auto &rhs) {
428 if (!lhs.getId())
429 return false;
430 if (!rhs.getId())
431 return true;
432 return lhs.getId().getValue() < rhs.getId().getValue();
433 });
434
435 // Build ID -> Remark* lookup for resolving related remark references.
437 llvm::DenseSet<uint64_t> childIds; // IDs referenced as children
438
439 for (const auto &remark : remarks) {
440 if (remark.getId())
441 idMap[remark.getId().getValue()] = &remark;
442 for (auto relId : remark.getRelatedRemarkIds())
443 childIds.insert(relId.getValue());
444 }
445
446 // Visit by address so ID-less remarks remain distinct.
449 auto emit = [&](const detail::Remark &root) {
450 worklist.push_back(&root);
451 while (!worklist.empty()) {
452 const auto *remark = worklist.pop_back_val();
453 if (!emitted.insert(remark).second)
454 continue;
456 for (RemarkId id : llvm::reverse(remark->getRelatedRemarkIds()))
457 if (const auto *related = idMap.lookup(id.getValue()))
458 worklist.push_back(related);
459 }
460 };
461
462 for (const auto &remark : remarks) {
463 if (remark.getId() && childIds.contains(remark.getId().getValue()))
464 continue;
465 emit(remark);
466 }
467 // Cycles have no root.
468 for (const auto &remark : remarks)
469 if (!emitted.contains(&remark))
470 emit(remark);
471}
472
473} // namespace mlir::remark
return success()
if(failed(verifyVectorMemoryOp(getOperation(), memrefType, getVectorType()))) return failure()
static void copy(Location loc, Value dst, Value src, Value size, OpBuilder &builder)
Copies the given number of bytes from src to dst pointers.
static bool isAnchored(llvm::StringRef s)
Returns true if filter is already anchored like ^...$.
Definition Remarks.cpp:307
static void printArgs(llvm::raw_ostream &os, llvm::ArrayRef< Remark::Arg > args)
Definition Remarks.cpp:56
static std::string anchorWhole(llvm::StringRef filter)
Anchor the entire pattern so it matches the whole string.
Definition Remarks.cpp:313
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:323
b
Return true if permutation is a valid permutation of the outer_dims_perm (case OuterOrInnerPerm::Oute...
static LogicalResult emit(SolverOp solver, const SMTEmissionOptions &options, mlir::raw_indented_ostream &stream)
Emit the SMT operations in the given 'solver' to the 'stream'.
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:71
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
Emit each survivor once, grouping related remarks after their parents.
Definition Remarks.cpp:408
void reportRemark(const detail::Remark &remark) override
Definition Remarks.cpp:396
A unique identifier for a remark, used to link related remarks together.
Definition Remarks.h:39
A wrapper for linking remarks by query - searches the engine's registry at stream time and links to a...
Definition Remarks.h:412
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:253
bool isPassedOptRemarkEnabled(StringRef categoryName) const
Return true if passed optimization remarks are enabled for the given category.
Definition Remarks.cpp:212
void report(const Remark &&remark)
Report a remark.
Definition Remarks.cpp:280
bool isMissedOptRemarkEnabled(StringRef categoryName) const
Return true if missed optimization remarks are enabled for the given category.
Definition Remarks.cpp:208
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:241
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:293
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:216
bool isRemarkEnabled(RemarkKind kind, StringRef categoryName) const
Return true if remarks of the given kind are enabled for the given category.
Definition Remarks.cpp:224
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:247
~RemarkEngine()
Destructor that will close the output file and reset the main remark streamer.
Definition Remarks.cpp:285
bool isFailedOptRemarkEnabled(StringRef categoryName) const
Return true if failed optimization remarks are enabled for the given category.
Definition Remarks.cpp:220
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:259
void setRelatedRemarkIds(ArrayRef< RemarkId > ids)
Replace the list of related remark IDs.
Definition Remarks.cpp:47
std::string functionName
Name of the covering function like interface.
Definition Remarks.h:295
void print(llvm::raw_ostream &os, bool printLocation=false) const
Print the remark to the given output stream.
Definition Remarks.cpp:89
ArrayRef< Arg > getArgs() const
Return arguments, owned until mutation or destruction.
Definition Remarks.h:247
SmallVector< Arg, 4 > args
Args collected via the streaming interface.
Definition Remarks.h:314
StringRef getFunction() const
Definition Remarks.h:220
llvm::remarks::Type getRemarkType() const
Definition Remarks.cpp:136
StringRef getRemarkTypeString() const
Definition Remarks.cpp:120
std::string remarkName
Remark identifier.
Definition Remarks.h:311
Location getLocation() const
Definition Remarks.h:216
llvm::StringRef getCombinedCategoryName() const
Definition Remarks.h:228
void addRelatedRemark(RemarkId relatedId)
Add a related ID and its generated argument.
Definition Remarks.h:272
RemarkKind remarkKind
Keeps the MLIR diagnostic kind, which is used to determine the diagnostic kind in the LLVM remark str...
Definition Remarks.h:292
StringRef getRemarkName() const
Definition Remarks.h:238
SmallVector< RemarkId > relatedRemarks
IDs of related remarks (e.g., parent analysis that enabled this opt).
Definition Remarks.h:320
void insert(llvm::StringRef s)
Definition Remarks.cpp:44
llvm::remarks::Remark generateRemark() const
Diagnostic -> Remark.
Definition Remarks.cpp:152
std::string categoryName
Category name e.g., "Unroll" or "UnrollAndJam".
Definition Remarks.h:300
std::string getMsg() const
Definition Remarks.cpp:112
llvm::unique_function< void(const Remark &)> ReportFn
Definition Remarks.h:475
static void dropDiscardedLinks(detail::Remark &remark, const llvm::DenseSet< uint64_t > &discardedIds)
Definition Remarks.cpp:385
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition Remarks.h:744
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:73
@ RemarkPassed
An optimization was applied.
Definition Remarks.h:77
@ RemarkAnalysis
Informational context (e.g., analysis numbers) without a pass/fail outcome.
Definition Remarks.h:88
@ RemarkMissed
A profitable optimization opportunity was found but not applied.
Definition Remarks.h:80
@ RemarkFailure
The compiler attempted the optimization but failed (e.g., legality checks, or better opportunites).
Definition Remarks.h:84
InFlightDiagnostic emitRemark(Location loc)
Utility method to emit a remark message using this location.
Arg(llvm::StringRef m)
Definition Remarks.h:181
Define an the set of categories to accept.
Definition Remarks.h:67
std::optional< std::string > missed
Definition Remarks.h:68
std::optional< std::string > all
Definition Remarks.h:68
std::optional< std::string > passed
Definition Remarks.h:68
std::optional< std::string > analysis
Definition Remarks.h:68
std::optional< std::string > failed
Definition Remarks.h:68
Options to create a Remark.
Definition Remarks.h:98
bool isLink
Generated RelatedTo argument, distinct from a user-supplied argument.
Definition Remarks.h:179
std::optional< Attribute > attr
Optional attribute storage for Attribute-based args.
Definition Remarks.h:177