[Mlir-commits] [mlir] b1624fc - [MLIR][Remark] Add remark linking and remarkID (#180953)
llvmlistbot at llvm.org
llvmlistbot at llvm.org
Thu Feb 12 11:52:52 PST 2026
Author: Guray Ozen
Date: 2026-02-12T20:52:47+01:00
New Revision: b1624fca5a4a70b0ae4a654359259370e6b4c4e5
URL: https://github.com/llvm/llvm-project/commit/b1624fca5a4a70b0ae4a654359259370e6b4c4e5
DIFF: https://github.com/llvm/llvm-project/commit/b1624fca5a4a70b0ae4a654359259370e6b4c4e5.diff
LOG: [MLIR][Remark] Add remark linking and remarkID (#180953)
Add RemarkId and relatedTo support to the MLIR Remarks infrastructure.
*Example*
```
remark1 = remark::analyzed(...)
remark::passed(loc, ... .relatedTo(remark1))
```
Added:
Modified:
mlir/include/mlir/IR/Remarks.h
mlir/lib/IR/Remarks.cpp
mlir/test/Pass/remarks.mlir
mlir/test/lib/Pass/TestRemarksPass.cpp
mlir/unittests/IR/RemarkTest.cpp
Removed:
################################################################################
diff --git a/mlir/include/mlir/IR/Remarks.h b/mlir/include/mlir/IR/Remarks.h
index eb0518e203d36..93f7993400e4f 100644
--- a/mlir/include/mlir/IR/Remarks.h
+++ b/mlir/include/mlir/IR/Remarks.h
@@ -23,8 +23,42 @@
#include "mlir/IR/MLIRContext.h"
#include "mlir/IR/Value.h"
+#include <atomic>
+
namespace mlir::remark {
+//===----------------------------------------------------------------------===//
+// RemarkId - Unique identifier for linking related remarks
+//===----------------------------------------------------------------------===//
+
+/// A unique identifier for a remark, used to link related remarks together.
+/// An invalid/empty ID has value 0.
+class RemarkId {
+public:
+ RemarkId() : id(0) {}
+ explicit RemarkId(uint64_t id) : id(id) {}
+
+ /// Check if this is a valid (non-zero) ID.
+ explicit operator bool() const { return id != 0; }
+
+ /// Get the raw ID value.
+ uint64_t getValue() const { return id; }
+
+ bool operator==(const RemarkId &other) const { return id == other.id; }
+ bool operator!=(const RemarkId &other) const { return id != other.id; }
+
+ /// Print the ID.
+ void print(llvm::raw_ostream &os) const { os << "remark-" << id; }
+
+private:
+ uint64_t id;
+};
+
+inline llvm::raw_ostream &operator<<(llvm::raw_ostream &os, RemarkId id) {
+ id.print(os);
+ return os;
+}
+
/// Define an the set of categories to accept. By default none are, the provided
/// regex matches against the category names for each kind of remark.
struct RemarkCategories {
@@ -53,6 +87,10 @@ enum class RemarkKind {
using namespace llvm;
+namespace detail {
+class InFlightRemark; // forward declaration
+} // namespace detail
+
/// Options to create a Remark
struct RemarkOpts {
StringRef remarkName; // Identifiable name
@@ -60,22 +98,45 @@ struct RemarkOpts {
StringRef subCategoryName; // Subcategory name
StringRef functionName; // Function name if available
+ /// Link to a related remark by explicit ID.
+ RemarkId relatedId;
+
// Construct RemarkOpts from a remark name.
- static constexpr RemarkOpts name(StringRef n) {
- return RemarkOpts{n, {}, {}, {}};
+ static RemarkOpts name(StringRef n) {
+ RemarkOpts o;
+ o.remarkName = n;
+ return o;
}
+
/// Return a copy with the category set.
- constexpr RemarkOpts category(StringRef v) const {
- return {remarkName, v, subCategoryName, functionName};
+ RemarkOpts category(StringRef v) const {
+ auto copy = *this;
+ copy.categoryName = v;
+ return copy;
}
/// Return a copy with the subcategory set.
- constexpr RemarkOpts subCategory(StringRef v) const {
- return {remarkName, categoryName, v, functionName};
+ RemarkOpts subCategory(StringRef v) const {
+ auto copy = *this;
+ copy.subCategoryName = v;
+ return copy;
}
/// Return a copy with the function name set.
- constexpr RemarkOpts function(StringRef v) const {
- return {remarkName, categoryName, subCategoryName, v};
+ RemarkOpts function(StringRef v) const {
+ auto copy = *this;
+ copy.functionName = v;
+ return copy;
}
+
+ /// Link this remark to a previously emitted remark by explicit ID.
+ RemarkOpts relatedTo(RemarkId id) const {
+ auto copy = *this;
+ copy.relatedId = id;
+ return copy;
+ }
+
+ /// Link this remark to a related remark that is still in flight.
+ /// Extracts the ID from the InFlightRemark.
+ inline RemarkOpts relatedTo(const detail::InFlightRemark &r) const;
};
} // namespace mlir::remark
@@ -97,6 +158,9 @@ class Remark {
(llvm::Twine(categoryName) + ":" + subCategoryName)
.toVector(fullCategoryName);
}
+ // Explicit ID linking from opts.
+ if (opts.relatedId)
+ addRelatedRemark(opts.relatedId);
}
// Remark argument that is a key-value pair that can be printed as machine
@@ -178,6 +242,38 @@ class Remark {
llvm::remarks::Type getRemarkType() const;
+ //===--------------------------------------------------------------------===//
+ // Remark Linking Support
+ //===--------------------------------------------------------------------===//
+
+ /// Get this remark's unique ID (0 if not assigned).
+ RemarkId getId() const { return id; }
+
+ /// Set this remark's unique ID. Also adds it as an Arg for serialization.
+ void setId(RemarkId newId) {
+ id = newId;
+ if (id)
+ args.emplace_back("RemarkId", llvm::utostr(id.getValue()));
+ }
+
+ /// Get the list of related remark IDs.
+ ArrayRef<RemarkId> getRelatedRemarkIds() const { return relatedRemarks; }
+
+ /// Add a reference to a related remark. Also adds it as an Arg for
+ /// serialization.
+ void addRelatedRemark(RemarkId relatedId) {
+ if (relatedId) {
+ relatedRemarks.push_back(relatedId);
+ args.emplace_back("RelatedTo", llvm::utostr(relatedId.getValue()));
+ }
+ }
+
+ /// Check if this remark has any related remarks.
+ bool hasRelatedRemarks() const { return !relatedRemarks.empty(); }
+
+ /// Get the remark kind.
+ RemarkKind getRemarkKind() const { return remarkKind; }
+
StringRef getRemarkTypeString() const;
protected:
@@ -207,6 +303,12 @@ class Remark {
/// Args collected via the streaming interface.
SmallVector<Arg, 4> args;
+ /// Unique ID for this remark (assigned by RemarkEngine).
+ RemarkId id;
+
+ /// IDs of related remarks (e.g., parent analysis that enabled this opt).
+ SmallVector<RemarkId> relatedRemarks;
+
private:
/// Convert the MLIR diagnostic severity to LLVM diagnostic severity.
static llvm::DiagnosticSeverity
@@ -289,6 +391,9 @@ struct LazyTextBuild {
std::function<std::string()> thunk;
};
+/// A wrapper for linking remarks by query - searches the engine's registry
+/// at stream time and links to all matching remarks.
+
/// InFlightRemark is a RAII class that holds a reference to a Remark
/// instance and allows to build the remark using the << operator. The remark
/// is emitted when the InFlightRemark instance is destroyed, which happens
@@ -321,6 +426,9 @@ class InFlightRemark {
explicit operator bool() const { return remark != nullptr; }
+ /// Get this remark's unique ID (for linking from other remarks).
+ RemarkId getId() const { return remark ? remark->getId() : RemarkId(); }
+
~InFlightRemark();
InFlightRemark(const InFlightRemark &) = delete;
@@ -372,6 +480,15 @@ class RemarkEmittingPolicyBase {
virtual void reportRemark(const Remark &remark) = 0;
virtual void finalize() = 0;
+
+ /// Find previously reported remarks matching the given criteria.
+ /// Default returns empty -- only policies that store remarks (like
+ /// PolicyFinal) override this to enable query-based linking.
+ virtual SmallVector<RemarkId>
+ findRemarks(const RemarkOpts &opts,
+ std::optional<RemarkKind> kind = std::nullopt) const {
+ return {};
+ }
};
//===----------------------------------------------------------------------===//
@@ -395,6 +512,8 @@ class RemarkEngine {
std::unique_ptr<RemarkEmittingPolicyBase> remarkEmittingPolicy;
/// When is enabled, engine also prints remarks as mlir::emitRemarks.
bool printAsEmitRemarks = false;
+ /// Atomic counter for generating unique remark IDs.
+ std::atomic<uint64_t> nextRemarkId{1};
/// Return true if missed optimization remarks are enabled, override
/// to provide
diff erent implementation.
@@ -423,8 +542,8 @@ class RemarkEngine {
/// Emit a remark using the given maker function, which should return
/// a Remark instance. The remark will be emitted using the main
/// remark streamer.
- template <typename RemarkT, typename... Args>
- InFlightRemark makeRemark(Args &&...args);
+ template <typename RemarkT>
+ InFlightRemark makeRemark(Location loc, RemarkOpts opts);
template <typename RemarkT>
InFlightRemark emitIfEnabled(Location loc, RemarkOpts opts,
@@ -457,6 +576,22 @@ class RemarkEngine {
return remarkEmittingPolicy.get();
}
+ /// Generate a unique ID for a new remark.
+ RemarkId generateRemarkId() {
+ return RemarkId(nextRemarkId.fetch_add(1, std::memory_order_relaxed));
+ }
+
+ //===--------------------------------------------------------------------===//
+ // Remark Linking - query previously emitted remarks
+ //===--------------------------------------------------------------------===//
+
+ /// Find all remarks matching the given criteria. Delegates to the
+ /// emitting policy. Only works with policies that store remarks
+ /// (e.g. RemarkEmittingPolicyFinal); returns empty for PolicyAll.
+ SmallVector<RemarkId>
+ findRemarks(const RemarkOpts &opts,
+ std::optional<RemarkKind> kind = std::nullopt) const;
+
/// Report a remark.
void report(const Remark &&remark);
@@ -491,6 +626,12 @@ inline InFlightRemark withEngine(Fn fn, Location loc, Args &&...args) {
} // namespace mlir::remark::detail
+// Deferred definition: needs InFlightRemark to be complete.
+inline mlir::remark::RemarkOpts
+mlir::remark::RemarkOpts::relatedTo(const detail::InFlightRemark &r) const {
+ return relatedTo(r.getId());
+}
+
namespace mlir::remark {
//===----------------------------------------------------------------------===//
@@ -509,7 +650,8 @@ class RemarkEmittingPolicyAll : public detail::RemarkEmittingPolicyBase {
void finalize() override {}
};
-/// Policy that emits final remarks.
+/// Policy that emits final remarks. Stores all remarks until finalize(),
+/// which enables query-based linking via findRemarks().
class RemarkEmittingPolicyFinal : public detail::RemarkEmittingPolicyBase {
private:
/// user can intercept them for custom processing via a registered callback,
@@ -524,13 +666,9 @@ class RemarkEmittingPolicyFinal : public detail::RemarkEmittingPolicyBase {
postponedRemarks.insert(remark);
}
- void finalize() override {
- assert(reportImpl && "reportImpl is not set");
- for (auto &remark : postponedRemarks) {
- if (reportImpl)
- reportImpl(remark);
- }
- }
+ /// Emits all stored remarks. Related remarks are printed as nested notes
+ /// under the remark that references them.
+ void finalize() override;
};
/// Create a Reason with llvm::formatv formatting.
@@ -559,6 +697,7 @@ inline detail::LazyTextBuild metric(StringRef key, V &&v) {
return detail::Remark::Arg(key, std::move(vv)).val;
}};
}
+
//===----------------------------------------------------------------------===//
// Emitters
//===----------------------------------------------------------------------===//
@@ -640,7 +779,8 @@ struct DenseMapInfo<mlir::remark::detail::Remark> {
return llvm::hash_combine(
remark.getLocation().getAsOpaquePointer(),
llvm::hash_value(remark.getRemarkName()),
- llvm::hash_value(remark.getCombinedCategoryName()));
+ llvm::hash_value(remark.getCombinedCategoryName()),
+ static_cast<unsigned>(remark.getRemarkKind()));
}
static bool isEqual(const mlir::remark::detail::Remark &lhs,
@@ -656,7 +796,8 @@ struct DenseMapInfo<mlir::remark::detail::Remark> {
// For regular remarks, compare key identifying fields
return lhs.getLocation() == rhs.getLocation() &&
lhs.getRemarkName() == rhs.getRemarkName() &&
- lhs.getCombinedCategoryName() == rhs.getCombinedCategoryName();
+ lhs.getCombinedCategoryName() == rhs.getCombinedCategoryName() &&
+ lhs.getRemarkKind() == rhs.getRemarkKind();
}
};
} // namespace llvm
diff --git a/mlir/lib/IR/Remarks.cpp b/mlir/lib/IR/Remarks.cpp
index 197cf421f9ba4..273918171746d 100644
--- a/mlir/lib/IR/Remarks.cpp
+++ b/mlir/lib/IR/Remarks.cpp
@@ -149,6 +149,7 @@ llvm::remarks::Remark Remark::generateRemark() const {
r.PassName = getCombinedCategoryName();
r.FunctionName = getFunction();
r.Loc = locLambda();
+ // Add all args (includes RemarkId and RelatedTo if they were added).
for (const Remark::Arg &arg : getArgs()) {
r.Args.emplace_back();
r.Args.back().Key = arg.key;
@@ -171,12 +172,13 @@ InFlightRemark::~InFlightRemark() {
// Remark Engine
//===----------------------------------------------------------------------===//
-template <typename RemarkT, typename... Args>
-InFlightRemark RemarkEngine::makeRemark(Args &&...args) {
+template <typename RemarkT>
+InFlightRemark RemarkEngine::makeRemark(Location loc, RemarkOpts opts) {
static_assert(std::is_base_of_v<Remark, RemarkT>,
"RemarkT must derive from Remark");
- return InFlightRemark(*this,
- std::make_unique<RemarkT>(std::forward<Args>(args)...));
+ auto remark = std::make_unique<RemarkT>(loc, opts);
+ remark->setId(generateRemarkId());
+ return InFlightRemark(*this, std::move(remark));
}
template <typename RemarkT>
@@ -345,4 +347,37 @@ llvm::LogicalResult mlir::remark::enableOptimizationRemarks(
namespace mlir::remark {
RemarkEmittingPolicyAll::RemarkEmittingPolicyAll() = default;
RemarkEmittingPolicyFinal::RemarkEmittingPolicyFinal() = default;
+
+void RemarkEmittingPolicyFinal::finalize() {
+ assert(reportImpl && "reportImpl is not set");
+
+ // Build ID -> Remark* lookup for resolving related remark references.
+ llvm::DenseMap<uint64_t, const detail::Remark *> idMap;
+ llvm::DenseSet<uint64_t> childIds; // IDs referenced as children
+
+ for (const auto &remark : postponedRemarks) {
+ if (remark.getId())
+ idMap[remark.getId().getValue()] = &remark;
+ for (auto relId : remark.getRelatedRemarkIds())
+ childIds.insert(relId.getValue());
+ }
+
+ // Emit remarks with related remarks grouped after their parents.
+ // Parent remarks are emitted first, followed by their related (child)
+ // remarks. Child-only remarks are skipped at the top level to avoid
+ // duplication.
+ for (const auto &remark : postponedRemarks) {
+ if (remark.getId() && childIds.count(remark.getId().getValue()))
+ continue; // will be printed grouped under its parent
+
+ reportImpl(remark);
+
+ // Emit related remarks immediately after the parent.
+ for (auto relId : remark.getRelatedRemarkIds()) {
+ if (const auto *related = idMap.lookup(relId.getValue()))
+ reportImpl(*related);
+ }
+ }
+}
+
} // namespace mlir::remark
diff --git a/mlir/test/Pass/remarks.mlir b/mlir/test/Pass/remarks.mlir
index 8aa04e3c98d80..f99a98e32ed1f 100644
--- a/mlir/test/Pass/remarks.mlir
+++ b/mlir/test/Pass/remarks.mlir
@@ -4,25 +4,35 @@
// RUN: mlir-opt %s --test-remark --remarks-filter-analyse="category-2-analysis" 2>&1 | FileCheck %s -check-prefix=CHECK-ANALYSIS
// RUN: mlir-opt %s --test-remark --remarks-filter="category.*" 2>&1 | FileCheck %s -check-prefix=CHECK-ALL
// RUN: mlir-opt %s --test-remark --remarks-filter="category-1.*" 2>&1 | FileCheck %s -check-prefix=CHECK-ALL1
+// RUN: mlir-opt %s --test-remark --remark-policy=final --remarks-filter-passed="category-link" --remarks-filter-analyse="category-link" 2>&1 | FileCheck %s -check-prefix=CHECK-LINK
module @foo {
"test.op"() : () -> ()
-
}
+// Each remark now includes a RemarkId=N arg and remark-N prefix, so use
+// {{.*}} to absorb them.
+// CHECK-PASSED: remark: [Passed]{{.*}}test-remark | Category:category-1-passed |{{.*}}Remark="This is a test passed remark"
+// CHECK-MISSED: remark: [Missed]{{.*}}test-remark | Category:a-category-1-missed |{{.*}}Remark="This is a test missed remark"
+// CHECK-FAILED: remark: [Failure]{{.*}}test-remark | Category:category-2-failed |{{.*}}Remark="This is a test failed remark"
+// CHECK-ANALYSIS: remark: [Analysis]{{.*}}test-remark | Category:category-2-analysis |{{.*}}Remark="This is a test analysis remark"
-// CHECK-PASSED: remarks.mlir:8:3: remark: [Passed] test-remark | Category:category-1-passed | Reason="because we are testing the remark pipeline", Remark="This is a test passed remark", Suggestion="try using the remark pipeline feature"
-// CHECK-MISSED:remarks.mlir:8:3: remark: [Missed] test-remark | Category:a-category-1-missed | Reason="because we are testing the remark pipeline", Remark="This is a test missed remark", Suggestion="try using the remark pipeline feature"
-// CHECK-FAILED: remarks.mlir:8:3: remark: [Failure] test-remark | Category:category-2-failed | Reason="because we are testing the remark pipeline", Remark="This is a test failed remark", Suggestion="try using the remark pipeline feature"
-// CHECK-ANALYSIS: remarks.mlir:8:3: remark: [Analysis] test-remark | Category:category-2-analysis | Remark="This is a test analysis remark"
-
-
-// CHECK-ALL: remarks.mlir:8:3: remark: [Passed] test-remark | Category:category-1-passed | Reason="because we are testing the remark pipeline", Remark="This is a test passed remark", Suggestion="try using the remark pipeline feature"
-// CHECK-ALL: remarks.mlir:8:3: remark: [Failure] test-remark | Category:category-2-failed | Reason="because we are testing the remark pipeline", Remark="This is a test failed remark", Suggestion="try using the remark pipeline feature"
-// CHECK-ALL: remarks.mlir:8:3: remark: [Analysis] test-remark | Category:category-2-analysis | Remark="This is a test analysis remark"
-
-// CHECK-ALL1: remarks.mlir:8:3: remark: [Passed] test-remark | Category:category-1-passed | Reason="because we are testing the remark pipeline", Remark="This is a test passed remark", Suggestion="try using the remark pipeline feature"
-// CHECK-ALL1-NOT: remarks.mlir:8:3: remark: [Missed]
-// CHECK-ALL1-NOT: remarks.mlir:8:3: remark: [Failure]
-// CHECK-ALL1-NOT: remarks.mlir:8:3: remark: [Analysis]
+// CHECK-ALL: remark: [Passed]{{.*}}test-remark | Category:category-1-passed |
+// CHECK-ALL: remark: [Passed]{{.*}}test-remark | Category:category-1-passed |
+// CHECK-ALL: remark: [Failure]{{.*}}test-remark | Category:category-2-failed |
+// CHECK-ALL: remark: [Analysis]{{.*}}test-remark | Category:category-2-analysis |
+// CHECK-ALL: remark: [Passed]{{.*}}test-remark | Category:category-link |
+// CHECK-ALL: remark: [Analysis]{{.*}}test-remark | Category:category-link |
+// CHECK-ALL1: remark: [Passed]{{.*}}test-remark | Category:category-1-passed |
+// CHECK-ALL1-NOT: remark: [Missed]
+// CHECK-ALL1-NOT: remark: [Failure]
+// CHECK-ALL1-NOT: remark: [Analysis]
+// Test remark linking via query-based API with PolicyFinal: the passed remark
+// finds the analysis remark by searching postponedRemarks. Related (child)
+// remarks are grouped after their parent.
+// CHECK-LINK: remark: [Passed]{{.*}}Category:category-link |{{.*}}RelatedTo=
+// CHECK-LINK-SAME: Remark="Optimization enabled by analysis"
+// CHECK-LINK-SAME: RemarkId=
+// CHECK-LINK: remark: [Analysis]{{.*}}Category:category-link |{{.*}}Remark="Analysis that enables optimization"
+// CHECK-LINK-SAME: RemarkId=
diff --git a/mlir/test/lib/Pass/TestRemarksPass.cpp b/mlir/test/lib/Pass/TestRemarksPass.cpp
index 5ca2d1a8550aa..b372d2cd6ad6b 100644
--- a/mlir/test/lib/Pass/TestRemarksPass.cpp
+++ b/mlir/test/lib/Pass/TestRemarksPass.cpp
@@ -66,6 +66,20 @@ class TestRemarkPass : public PassWrapper<TestRemarkPass, OperationPass<>> {
mlir::remark::analysis(loc, remark::RemarkOpts::name("test-remark")
.category("category-2-analysis"))
<< remark::add("This is a test analysis remark");
+
+ // Test remark linking: emit an analysis remark, then link a passed
+ // remark to it using relatedTo on RemarkOpts (no manual ID threading).
+ auto analysisRemark = mlir::remark::analysis(
+ loc,
+ remark::RemarkOpts::name("test-remark").category("category-link"));
+ analysisRemark << remark::add("Analysis that enables optimization");
+
+ // The passed remark finds the analysis via relatedTo on RemarkOpts:
+ mlir::remark::passed(loc, remark::RemarkOpts::name("test-remark")
+ .category("category-link")
+ .relatedTo(analysisRemark))
+ << remark::add("Optimization enabled by analysis");
+
return WalkResult::advance();
});
}
diff --git a/mlir/unittests/IR/RemarkTest.cpp b/mlir/unittests/IR/RemarkTest.cpp
index df8e8c8cc066b..81bfcd1446455 100644
--- a/mlir/unittests/IR/RemarkTest.cpp
+++ b/mlir/unittests/IR/RemarkTest.cpp
@@ -230,25 +230,43 @@ TEST(Remark, TestOutputOptimizationRemarkDiagnostic) {
int tripBad = 4;
int threshold = 256;
- remark::missed(loc, {"", categoryUnroll, "unroller2", ""})
+ remark::missed(loc, remark::RemarkOpts::name("")
+ .category(categoryUnroll)
+ .subCategory("unroller2"))
<< remark::reason("tripCount={0} < threshold={1}", tripBad, threshold);
- remark::missed(loc, {"", categoryUnroll, "", ""})
+ remark::missed(loc, remark::RemarkOpts::name("").category(categoryUnroll))
<< remark::reason("tripCount={0} < threshold={1}", tripBad, threshold)
<< remark::suggest("increase unroll to {0}", target);
// FAILURE: action attempted but failed
- remark::failed(loc, {"", categoryUnroll, "", ""})
+ remark::failed(loc, remark::RemarkOpts::name("").category(categoryUnroll))
<< remark::reason("failed due to unsupported pattern");
}
// clang-format off
+ // Remarks now include RemarkId=N, so use substring checks.
unsigned long expectedSize = 5;
ASSERT_EQ(seenMsg.size(), expectedSize);
- EXPECT_EQ(seenMsg[0], "[Passed] pass1 | Category:Vectorizer:myPass1 | Function=foo | Remark=\"vectorized loop\", tripCount=128");
- EXPECT_EQ(seenMsg[1], "[Analysis] Analysis1 | Category:Register | Function=foo | Remark=\"Kernel uses 168 registers\"");
- EXPECT_EQ(seenMsg[2], "[Missed] | Category:Unroll:unroller2 | Reason=\"tripCount=4 < threshold=256\"");
- EXPECT_EQ(seenMsg[3], "[Missed] | Category:Unroll | Reason=\"tripCount=4 < threshold=256\", Suggestion=\"increase unroll to 128\"");
- EXPECT_EQ(seenMsg[4], "[Failure] | Category:Unroll | Reason=\"failed due to unsupported pattern\"");
+ EXPECT_THAT(seenMsg[0], HasSubstr("[Passed]"));
+ EXPECT_THAT(seenMsg[0], HasSubstr("pass1 | Category:Vectorizer:myPass1 | Function=foo |"));
+ EXPECT_THAT(seenMsg[0], HasSubstr("Remark=\"vectorized loop\""));
+ EXPECT_THAT(seenMsg[0], HasSubstr("tripCount=128"));
+
+ EXPECT_THAT(seenMsg[1], HasSubstr("[Analysis]"));
+ EXPECT_THAT(seenMsg[1], HasSubstr("Analysis1 | Category:Register | Function=foo |"));
+ EXPECT_THAT(seenMsg[1], HasSubstr("Remark=\"Kernel uses 168 registers\""));
+
+ EXPECT_THAT(seenMsg[2], HasSubstr("[Missed]"));
+ EXPECT_THAT(seenMsg[2], HasSubstr("Category:Unroll:unroller2"));
+ EXPECT_THAT(seenMsg[2], HasSubstr("Reason=\"tripCount=4 < threshold=256\""));
+
+ EXPECT_THAT(seenMsg[3], HasSubstr("[Missed]"));
+ EXPECT_THAT(seenMsg[3], HasSubstr("Category:Unroll |"));
+ EXPECT_THAT(seenMsg[3], HasSubstr("Suggestion=\"increase unroll to 128\""));
+
+ EXPECT_THAT(seenMsg[4], HasSubstr("[Failure]"));
+ EXPECT_THAT(seenMsg[4], HasSubstr("Category:Unroll |"));
+ EXPECT_THAT(seenMsg[4], HasSubstr("Reason=\"failed due to unsupported pattern\""));
// clang-format on
}
@@ -294,12 +312,20 @@ TEST(Remark, TestCustomOptimizationRemarkDiagnostic) {
ASSERT_TRUE(succeeded(isEnabled)) << "Failed to enable remark engine";
// Remark 1: pass, category LoopUnroll
- remark::passed(loc, {"", categoryLoopunroll, myPassname1, ""}) << pass1Msg;
+ remark::passed(loc, remark::RemarkOpts::name("")
+ .category(categoryLoopunroll)
+ .subCategory(myPassname1))
+ << pass1Msg;
// Remark 2: failure, category LoopUnroll
- remark::failed(loc, {"", categoryLoopunroll, myPassname2, ""})
+ remark::failed(loc, remark::RemarkOpts::name("")
+ .category(categoryLoopunroll)
+ .subCategory(myPassname2))
<< remark::reason(pass2Msg);
// Remark 3: pass, category Inline (should not be printed)
- remark::passed(loc, {"", categoryInline, myPassname1, ""}) << pass3Msg;
+ remark::passed(loc, remark::RemarkOpts::name("")
+ .category(categoryInline)
+ .subCategory(myPassname1))
+ << pass3Msg;
}
llvm::errs().flush();
@@ -372,11 +398,14 @@ TEST(Remark, TestRemarkFinal) {
llvm::errs().flush();
std::string errOut = ::testing::internal::GetCapturedStderr();
- // Containment checks for messages.
- EXPECT_EQ(errOut.find(pass1Msg), std::string::npos); // dropped
- EXPECT_EQ(errOut.find(pass2Msg), std::string::npos); // dropped
- EXPECT_NE(errOut.find(pass3Msg), std::string::npos); // shown
- EXPECT_NE(errOut.find(pass4Msg), std::string::npos); // shown
+ // PolicyFinal deduplicates by (location, name, category, kind).
+ // Remarks 1 (failed), 2 (missed), 3 (passed) have
diff erent kinds, so all
+ // survive. Remark 4 (passed) has a
diff erent location, so it also survives.
+ EXPECT_NE(errOut.find(pass1Msg), std::string::npos); // shown (failed)
+ EXPECT_NE(errOut.find(pass2Msg), std::string::npos); // shown (missed)
+ EXPECT_NE(errOut.find(pass3Msg), std::string::npos); // shown (passed)
+ EXPECT_NE(errOut.find(pass4Msg),
+ std::string::npos); // shown (passed,
diff loc)
}
TEST(Remark, TestArgWithAttribute) {
@@ -488,4 +517,62 @@ TEST(Remark, TestRemarkOwnsStringData) {
EXPECT_NE(errOut.find(expectedMessage), std::string::npos)
<< "Expected message not found in output. Got: " << errOut;
}
+
+// Test that remarks can be linked together using RemarkId.
+TEST(Remark, TestRemarkLinking) {
+ testing::internal::CaptureStderr();
+
+ std::string categoryOpt("Optimizer");
+
+ {
+ MLIRContext context;
+ Location loc = FileLineColLoc::get(&context, "test.cpp", 10, 5);
+
+ // Setup the remark engine
+ mlir::remark::RemarkCategories cats{/*all=*/std::nullopt,
+ /*passed=*/categoryOpt,
+ /*missed=*/std::nullopt,
+ /*analysis=*/categoryOpt,
+ /*failed=*/std::nullopt};
+
+ std::unique_ptr<remark::RemarkEmittingPolicyAll> policy =
+ std::make_unique<remark::RemarkEmittingPolicyAll>();
+ LogicalResult isEnabled = remark::enableOptimizationRemarks(
+ context, std::make_unique<MyCustomStreamer>(), std::move(policy), cats,
+ /*printAsEmitRemarks=*/true);
+ ASSERT_TRUE(succeeded(isEnabled)) << "Failed to enable remark engine";
+
+ // Emit an analysis remark and capture its ID.
+ auto analysisRemark = remark::analysis(
+ loc, remark::RemarkOpts::name("LoopAnalysis").category(categoryOpt));
+ analysisRemark << "analyzed loop with trip count 128";
+ remark::RemarkId analysisId = analysisRemark.getId();
+
+ // Verify we got a valid ID.
+ EXPECT_TRUE(static_cast<bool>(analysisId));
+ EXPECT_GT(analysisId.getValue(), 0u);
+
+ // Emit a passed remark that links to the analysis via RemarkOpts.
+ remark::passed(loc, remark::RemarkOpts::name("LoopOptimized")
+ .category(categoryOpt)
+ .relatedTo(analysisId))
+ << "vectorized loop";
+ }
+
+ llvm::errs().flush();
+ std::string errOut = ::testing::internal::GetCapturedStderr();
+
+ // Verify the analysis remark has an ID.
+ EXPECT_THAT(errOut, HasSubstr("RemarkId="));
+
+ // Verify the passed remark links to the analysis remark.
+ EXPECT_THAT(errOut, HasSubstr("RelatedTo="));
+
+ // Verify both remarks are present.
+ EXPECT_THAT(errOut, HasSubstr("LoopAnalysis"));
+ EXPECT_THAT(errOut, HasSubstr("LoopOptimized"));
+ EXPECT_THAT(errOut, HasSubstr("analyzed loop"));
+ EXPECT_THAT(errOut, HasSubstr("vectorized loop"));
+}
+
} // namespace
More information about the Mlir-commits
mailing list