[llvm] [mlir] [MLIR] Add debug log to the pass manager (NFC) (PR #156205)
Mehdi Amini via llvm-commits
llvm-commits at lists.llvm.org
Fri Sep 5 15:58:21 PDT 2025
https://github.com/joker-eph updated https://github.com/llvm/llvm-project/pull/156205
>From 7606fa3d0b689abbfbd6446dc9f1019e38567106 Mon Sep 17 00:00:00 2001
From: Mehdi Amini <joker.eph at gmail.com>
Date: Fri, 5 Sep 2025 15:55:55 -0700
Subject: [PATCH 1/2] Introduce LDBG_OS() macro as a variant of LDBG()
Also, improve LDBG() to accept debug type and level in any order,
and add unit-tests for LDBG() and LGDB_OS().
LDBG_OS() is a macro that behaves like LDBG() but instead of directly using
it to stream the output, it takes a callback function that will be called
with a raw_ostream.
---
llvm/include/llvm/Support/Debug.h | 5 -
llvm/include/llvm/Support/DebugLog.h | 245 +++++++++++++++++++-----
llvm/unittests/Support/DebugLogTest.cpp | 129 ++++++++++++-
3 files changed, 321 insertions(+), 58 deletions(-)
diff --git a/llvm/include/llvm/Support/Debug.h b/llvm/include/llvm/Support/Debug.h
index a7795d403721c..b73f2d7c8b852 100644
--- a/llvm/include/llvm/Support/Debug.h
+++ b/llvm/include/llvm/Support/Debug.h
@@ -44,11 +44,6 @@ class raw_ostream;
/// level, return false.
LLVM_ABI bool isCurrentDebugType(const char *Type, int Level = 0);
-/// Overload allowing to swap the order of the Type and Level arguments.
-LLVM_ABI inline bool isCurrentDebugType(int Level, const char *Type) {
- return isCurrentDebugType(Type, Level);
-}
-
/// setCurrentDebugType - Set the current debug type, as if the -debug-only=X
/// option were specified. Note that DebugFlag also needs to be set to true for
/// debug output to be produced.
diff --git a/llvm/include/llvm/Support/DebugLog.h b/llvm/include/llvm/Support/DebugLog.h
index dce706e196bde..7963c38761ac1 100644
--- a/llvm/include/llvm/Support/DebugLog.h
+++ b/llvm/include/llvm/Support/DebugLog.h
@@ -19,52 +19,81 @@
namespace llvm {
#ifndef NDEBUG
-// LDBG() is a macro that can be used as a raw_ostream for debugging.
-// It will stream the output to the dbgs() stream, with a prefix of the
-// debug type and the file and line number. A trailing newline is added to the
-// output automatically. If the streamed content contains a newline, the prefix
-// is added to each beginning of a new line. Nothing is printed if the debug
-// output is not enabled or the debug type does not match.
-//
-// E.g.,
-// LDBG() << "Bitset contains: " << Bitset;
-// is somehow equivalent to
-// LLVM_DEBUG(dbgs() << "[" << DEBUG_TYPE << "] " << __FILE__ << ":" <<
-// __LINE__ << " "
-// << "Bitset contains: " << Bitset << "\n");
-//
+/// LDBG() is a macro that can be used as a raw_ostream for debugging.
+/// It will stream the output to the dbgs() stream, with a prefix of the
+/// debug type and the file and line number. A trailing newline is added to the
+/// output automatically. If the streamed content contains a newline, the prefix
+/// is added to each beginning of a new line. Nothing is printed if the debug
+/// output is not enabled or the debug type does not match.
+///
+/// E.g.,
+/// LDBG() << "Bitset contains: " << Bitset;
+/// is somehow equivalent to
+/// LLVM_DEBUG(dbgs() << "[" << DEBUG_TYPE << "] " << __FILE__ << ":" <<
+/// __LINE__ << " "
+/// << "Bitset contains: " << Bitset << "\n");
+///
// An optional `level` argument can be provided to control the verbosity of the
-// output. The default level is 1, and is in increasing level of verbosity.
-//
-// The `level` argument can be a literal integer, or a macro that evaluates to
-// an integer.
-//
-// An optional `type` argument can be provided to control the debug type. The
-// default type is DEBUG_TYPE. The `type` argument can be a literal string, or a
-// macro that evaluates to a string.
+/// output. The default level is 1, and is in increasing level of verbosity.
+///
+/// The `level` argument can be a literal integer, or a macro that evaluates to
+/// an integer.
+///
+/// An optional `type` argument can be provided to control the debug type. The
+/// default type is DEBUG_TYPE. The `type` argument can be a literal string, or
+/// a macro that evaluates to a string.
+///
+/// E.g.,
+/// LDBG(2) << "Bitset contains: " << Bitset;
+/// LDBG("debug_type") << "Bitset contains: " << Bitset;
+/// LDBG(2, "debug_type") << "Bitset contains: " << Bitset;
+/// LDBG("debug_type", 2) << "Bitset contains: " << Bitset;
#define LDBG(...) _GET_LDBG_MACRO(__VA_ARGS__)(__VA_ARGS__)
-// Helper macros to choose the correct macro based on the number of arguments.
+/// LDBG_OS() is a macro that behaves like LDBG() but instead of directly using
+/// it to stream the output, it takes a callback function that will be called
+/// with a raw_ostream.
+/// This is useful when you need to pass a `raw_ostream` to a helper function to
+/// be able to print (when the `<<` operator is not available).
+///
+/// E.g.,
+/// LDBG_OS([&] (raw_ostream &Os) {
+/// Os << "Pass Manager contains: ";
+/// pm.printAsTextual(Os);
+/// });
+///
+/// Just like LDBG(), it optionally accepts a `level` and `type` arguments.
+/// E.g.,
+/// LDBG_OS(2, [&] (raw_ostream &Os) { ... });
+/// LDBG_OS("debug_type", [&] (raw_ostream &Os) { ... });
+/// LDBG_OS(2, "debug_type", [&] (raw_ostream &Os) { ... });
+/// LDBG_OS("debug_type", 2, [&] (raw_ostream &Os) { ... });
+///
+#define LDBG_OS(...) _GET_LDBG_OS_MACRO(__VA_ARGS__)(__VA_ARGS__)
+
+// Helper macros to choose the correct LDBG() macro based on the number of
+// arguments.
#define LDBG_FUNC_CHOOSER(_f1, _f2, _f3, ...) _f3
#define LDBG_FUNC_RECOMPOSER(argsWithParentheses) \
LDBG_FUNC_CHOOSER argsWithParentheses
#define LDBG_CHOOSE_FROM_ARG_COUNT(...) \
LDBG_FUNC_RECOMPOSER( \
- (__VA_ARGS__, LDBG_LOG_LEVEL_WITH_TYPE, LDBG_LOG_LEVEL, ))
-#define LDBG_NO_ARG_EXPANDER() , , LDBG_LOG_LEVEL_1
+ (__VA_ARGS__, LDBG_LOG_LEVEL_WITH_TYPE, LDBG_LOG_LEVEL_1_ARG, ))
+#define LDBG_NO_ARG_EXPANDER() , , LDBG_LOG_LEVEL_NO_ARG
#define _GET_LDBG_MACRO(...) \
LDBG_CHOOSE_FROM_ARG_COUNT(LDBG_NO_ARG_EXPANDER __VA_ARGS__())
-// Dispatch macros to support the `level` argument or none (default to 1)
-#define LDBG_LOG_LEVEL(LEVEL) \
- DEBUGLOG_WITH_STREAM_AND_TYPE(llvm::dbgs(), LEVEL, DEBUG_TYPE)
-#define LDBG_LOG_LEVEL_1() LDBG_LOG_LEVEL(1)
-// This macro is a helper when LDBG() is called with 2 arguments.
-// In this case we want to allow the order of the arguments to be swapped.
-// We rely on the fact that the `level` argument is an integer, and the `type`
-// is a string and dispatch to a C++ API that is overloaded.
-#define LDBG_LOG_LEVEL_WITH_TYPE(LEVEL_OR_TYPE, TYPE_OR_LEVEL) \
- DEBUGLOG_WITH_STREAM_AND_TYPE(llvm::dbgs(), (LEVEL_OR_TYPE), (TYPE_OR_LEVEL))
+// Helper macros to choose the correct LDBG_OS() macro based on the number of
+// arguments.
+#define LDBG_OS_FUNC_CHOOSER(_f1, _f2, _f3, _f4, ...) _f4
+#define LDBG_OS_FUNC_RECOMPOSER(argsWithParentheses) \
+ LDBG_OS_FUNC_CHOOSER argsWithParentheses
+#define LDBG_OS_CHOOSE_FROM_ARG_COUNT(...) \
+ LDBG_OS_FUNC_RECOMPOSER( \
+ (__VA_ARGS__, LDBG_OS_3_ARGS, LDBG_OS_2_ARGS, LDBG_OS_1_ARG, ))
+#define LDBG_OS_NO_ARG_EXPANDER() , , , LDBG_OS_1_ARG
+#define _GET_LDBG_OS_MACRO(...) \
+ LDBG_OS_CHOOSE_FROM_ARG_COUNT(LDBG_OS_NO_ARG_EXPANDER __VA_ARGS__())
// We want the filename without the full path. We are using the __FILE__ macro
// and a constexpr function to strip the path prefix. We can avoid the frontend
@@ -76,22 +105,129 @@ namespace llvm {
#define __LLVM_FILE_NAME__ ::llvm::impl::getShortFileName(__FILE__)
#endif
-#define DEBUGLOG_WITH_STREAM_TYPE_FILE_AND_LINE(STREAM, LEVEL, TYPE, FILE, \
- LINE) \
- for (bool _c = \
- (::llvm::DebugFlag && ::llvm::isCurrentDebugType(TYPE, LEVEL)); \
+/// These macros are detecting if the DEBUG_TYPE or LDBG_DEBUG_STREAM macros are
+/// defined. They are using a combination of preprocessor tricks and C++17
+// used-defined string literals to achieve this.
+// For example, if DEBUG_TYPE is defined to "foo", the preprocessor will expand
+// the macro and then stringify the result to
+// "foo"_LDBG_VARIABLE_CHECK
+// This dispatch to the user-defined string literal operator named
+// _LDBG_VARIABLE_CHECK which returns true. Otherwise it expands to
+// DEBUG_TYPE_LDBG_VARIABLE_CHECK which we define as a macro that returns false.
+#define LDBG_VARIABLE_CHECK_(VARIABLE, ...) VARIABLE##__VA_ARGS__
+#define LDBG_VARIABLE_CHECK(VARIABLE) \
+ LDBG_VARIABLE_CHECK_(VARIABLE, _LDBG_VARIABLE_CHECK)
+// User-defined string literal operator for the LDBG_VARIABLE_CHECK macro.
+constexpr bool operator""_LDBG_VARIABLE_CHECK(const char *, std::size_t) {
+ return true;
+}
+
+#define IS_DEBUG_TYPE_DEFINED() LDBG_VARIABLE_CHECK(DEBUG_TYPE)
+#define DEBUG_TYPE_LDBG_VARIABLE_CHECK 0
+
+#define IS_LDBG_DEBUG_STREAM_DEFINED() LDBG_VARIABLE_CHECK(LDBG_DEBUG_STREAM)
+#define LDBG_DEBUG_STREAM_LDBG_VARIABLE_CHECK 0
+
+/// Helpers to get DEBUG_TYPE as a string literal, even when DEBUG_TYPE is not
+/// defined (in which case it expand to "DEBUG_TYPE")
+#define LDBG_GET_DEBUG_TYPE_STR__(X) #X##_LDBG_DEBUG_STRING
+#define LDBG_GET_DEBUG_TYPE_STR_(X) LDBG_GET_DEBUG_TYPE_STR__(X)
+#define LDBG_GET_DEBUG_TYPE_STR() LDBG_GET_DEBUG_TYPE_STR_(DEBUG_TYPE)
+/// If DEBUG_TYPE is defined, we get the string with the quotes, we need to
+/// remove them here.
+constexpr ::llvm::StringRef operator""_LDBG_DEBUG_STRING(const char *Str,
+ std::size_t) {
+ ::llvm::StringRef S(Str);
+ if (S.front() == '"' && S.back() == '"')
+ return S.drop_front().drop_back();
+ return S;
+}
+
+/// Helper to provide the default level (=1) or type (=DEBUG_TYPE). This is used
+/// when a single argument is passed, if it is an integer we return DEBUG_TYPE
+/// and if it is a string we return 1. This fails with a static_assert if we
+/// pass an integer and DEBUG_TYPE is not defined.
+#define LDBG_GET_DEFAULT_TYPE_OR_LEVEL(LEVEL_OR_TYPE) \
+ [](auto LevelOrType) { \
+ if constexpr (std::is_integral_v<decltype(LevelOrType)>) { \
+ using ::llvm::operator""_LDBG_VARIABLE_CHECK; \
+ using ::llvm::operator""_LDBG_DEBUG_STRING; \
+ if constexpr (IS_DEBUG_TYPE_DEFINED()) \
+ return LDBG_GET_DEBUG_TYPE_STR(); \
+ else \
+ static_assert(false, "DEBUG_TYPE is not defined"); \
+ } else { \
+ return 1; \
+ } \
+ }(LEVEL_OR_TYPE)
+
+/// Use a macro to allow unit-testing to override.
+#define LDBG_STREAM ::llvm::dbgs()
+
+#define DEBUGLOG_WITH_STREAM_TYPE_FILE_AND_LINE(STREAM, LEVEL_OR_TYPE, \
+ TYPE_OR_LEVEL, FILE, LINE) \
+ for (bool _c = (::llvm::DebugFlag && ::llvm::impl::ldbgIsCurrentDebugType( \
+ TYPE_OR_LEVEL, LEVEL_OR_TYPE)); \
_c; _c = false) \
for (::llvm::impl::raw_ldbg_ostream LdbgOS{ \
- ::llvm::impl::computePrefix(TYPE, FILE, LINE, LEVEL), (STREAM)}; \
+ ::llvm::impl::computePrefix(TYPE_OR_LEVEL, FILE, LINE, \
+ LEVEL_OR_TYPE), \
+ (STREAM), /*ShouldPrefixNextString=*/true, \
+ /*ShouldEmitNewLineOnDestruction=*/true}; \
_c; _c = false) \
- ::llvm::impl::RAIINewLineStream{LdbgOS}.asLvalue()
+ LdbgOS
-#define DEBUGLOG_WITH_STREAM_TYPE_AND_FILE(STREAM, LEVEL, TYPE, FILE) \
- DEBUGLOG_WITH_STREAM_TYPE_FILE_AND_LINE(STREAM, LEVEL, TYPE, FILE, __LINE__)
-#define DEBUGLOG_WITH_STREAM_AND_TYPE(STREAM, LEVEL, TYPE) \
- DEBUGLOG_WITH_STREAM_TYPE_AND_FILE(STREAM, LEVEL, TYPE, __LLVM_FILE_NAME__)
+#define DEBUGLOG_WITH_STREAM_TYPE_AND_FILE(STREAM, LEVEL_OR_TYPE, \
+ TYPE_OR_LEVEL, FILE) \
+ DEBUGLOG_WITH_STREAM_TYPE_FILE_AND_LINE(STREAM, LEVEL_OR_TYPE, \
+ TYPE_OR_LEVEL, FILE, __LINE__)
+#define DEBUGLOG_WITH_STREAM_AND_TYPE(STREAM, LEVEL_OR_TYPE, TYPE_OR_LEVEL) \
+ DEBUGLOG_WITH_STREAM_TYPE_AND_FILE(STREAM, LEVEL_OR_TYPE, TYPE_OR_LEVEL, \
+ __LLVM_FILE_NAME__)
+
+// Dispatch macros when a signle argument is provided. This can be either a
+// level of the debug type.
+#define LDBG_LOG_LEVEL_1_ARG(LEVEL_OR_TYPE) \
+ DEBUGLOG_WITH_STREAM_AND_TYPE(LDBG_STREAM, LEVEL_OR_TYPE, \
+ LDBG_GET_DEFAULT_TYPE_OR_LEVEL(LEVEL_OR_TYPE))
+#define LDBG_LOG_LEVEL_NO_ARG() LDBG_LOG_LEVEL_1_ARG(1)
+// This macro is a helper when LDBG() is called with 2 arguments.
+// In this case we want to allow the order of the arguments to be swapped.
+// We rely on the fact that the `level` argument is an integer, and the `type`
+// is a string and dispatch to a C++ API that is overloaded.
+#define LDBG_LOG_LEVEL_WITH_TYPE(LEVEL_OR_TYPE, TYPE_OR_LEVEL) \
+ DEBUGLOG_WITH_STREAM_AND_TYPE(LDBG_STREAM, (LEVEL_OR_TYPE), (TYPE_OR_LEVEL))
+
+#define LDBG_OS_IMPL(TYPE_OR_LEVEL, LEVEL_OR_TYPE, CALLBACK, STREAM, FILE, \
+ LINE) \
+ if (::llvm::DebugFlag && \
+ ::llvm::impl::ldbgIsCurrentDebugType(TYPE_OR_LEVEL, LEVEL_OR_TYPE)) { \
+ ::llvm::impl::raw_ldbg_ostream LdbgOS{ \
+ ::llvm::impl::computePrefix(TYPE_OR_LEVEL, FILE, LINE, LEVEL_OR_TYPE), \
+ (STREAM), /*ShouldPrefixNextString=*/true, \
+ /*ShouldEmitNewLineOnDestruction=*/true}; \
+ CALLBACK(LdbgOS); \
+ }
+
+#define LDBG_OS_3_ARGS(TYPE_OR_LEVEL, LEVEL_OR_TYPE, CALLBACK) \
+ LDBG_OS_IMPL(TYPE_OR_LEVEL, LEVEL_OR_TYPE, CALLBACK, LDBG_STREAM, \
+ __LLVM_FILE_NAME__, __LINE__)
+
+#define LDBG_OS_2_ARGS(LEVEL_OR_TYPE, CALLBACK) \
+ LDBG_OS_3_ARGS(LDBG_GET_DEFAULT_TYPE_OR_LEVEL(LEVEL_OR_TYPE), LEVEL_OR_TYPE, \
+ CALLBACK)
+#define LDBG_OS_1_ARG(CALLBACK) LDBG_OS_2_ARGS(1, CALLBACK)
namespace impl {
+/// Helper to call isCurrentDebugType with a StringRef.
+static LLVM_ATTRIBUTE_UNUSED bool ldbgIsCurrentDebugType(StringRef Type,
+ int Level) {
+ return ::llvm::isCurrentDebugType(Type.str().c_str(), Level);
+}
+static LLVM_ATTRIBUTE_UNUSED bool ldbgIsCurrentDebugType(int Level,
+ StringRef Type) {
+ return ::llvm::isCurrentDebugType(Type.str().c_str(), Level);
+}
/// A raw_ostream that tracks `\n` and print the prefix after each
/// newline.
@@ -99,6 +235,7 @@ class LLVM_ABI raw_ldbg_ostream final : public raw_ostream {
std::string Prefix;
raw_ostream &Os;
bool ShouldPrefixNextString;
+ bool ShouldEmitNewLineOnDestruction;
/// Split the line on newlines and insert the prefix before each
/// newline. Forward everything to the underlying stream.
@@ -131,12 +268,17 @@ class LLVM_ABI raw_ldbg_ostream final : public raw_ostream {
public:
explicit raw_ldbg_ostream(std::string Prefix, raw_ostream &Os,
- bool ShouldPrefixNextString = true)
+ bool ShouldPrefixNextString = true,
+ bool ShouldEmitNewLineOnDestruction = false)
: Prefix(std::move(Prefix)), Os(Os),
- ShouldPrefixNextString(ShouldPrefixNextString) {
+ ShouldPrefixNextString(ShouldPrefixNextString),
+ ShouldEmitNewLineOnDestruction(ShouldEmitNewLineOnDestruction) {
SetUnbuffered();
}
- ~raw_ldbg_ostream() final {}
+ ~raw_ldbg_ostream() final {
+ if (ShouldEmitNewLineOnDestruction)
+ Os << '\n';
+ }
/// Forward the current_pos method to the underlying stream.
uint64_t current_pos() const final { return Os.tell(); }
@@ -173,17 +315,17 @@ getShortFileName(const char *path) {
/// "[DebugType] File:Line "
/// Where the File is the file name without the path prefix.
static LLVM_ATTRIBUTE_UNUSED std::string
-computePrefix(const char *DebugType, const char *File, int Line, int Level) {
+computePrefix(StringRef DebugType, const char *File, int Line, int Level) {
std::string Prefix;
raw_string_ostream OsPrefix(Prefix);
- if (DebugType)
+ if (!DebugType.empty())
OsPrefix << "[" << DebugType << ":" << Level << "] ";
OsPrefix << File << ":" << Line << " ";
return OsPrefix.str();
}
/// Overload allowing to swap the order of the DebugType and Level arguments.
static LLVM_ATTRIBUTE_UNUSED std::string
-computePrefix(int Level, const char *File, int Line, const char *DebugType) {
+computePrefix(int Level, const char *File, int Line, StringRef DebugType) {
return computePrefix(DebugType, File, Line, Level);
}
@@ -194,6 +336,7 @@ computePrefix(int Level, const char *File, int Line, const char *DebugType) {
#define LDBG(...) \
for (bool _c = false; _c; _c = false) \
::llvm::nulls()
+#define LDBG_OS(...)
#endif
} // end namespace llvm
diff --git a/llvm/unittests/Support/DebugLogTest.cpp b/llvm/unittests/Support/DebugLogTest.cpp
index e087705b72586..62b269dc5b3e4 100644
--- a/llvm/unittests/Support/DebugLogTest.cpp
+++ b/llvm/unittests/Support/DebugLogTest.cpp
@@ -27,7 +27,7 @@ TEST(DebugLogTest, Basic) {
{
std::string str;
raw_string_ostream os(str);
- DEBUGLOG_WITH_STREAM_AND_TYPE(os, 0, nullptr) << "NoType";
+ DEBUGLOG_WITH_STREAM_AND_TYPE(os, "", 0) << "NoType";
EXPECT_FALSE(StringRef(os.str()).starts_with('['));
EXPECT_TRUE(StringRef(os.str()).ends_with("NoType\n"));
}
@@ -37,7 +37,7 @@ TEST(DebugLogTest, Basic) {
std::string str;
raw_string_ostream os(str);
DEBUGLOG_WITH_STREAM_AND_TYPE(os, 0, "A") << "A";
- DEBUGLOG_WITH_STREAM_AND_TYPE(os, 0, "B") << "B";
+ DEBUGLOG_WITH_STREAM_AND_TYPE(os, "B", 0) << "B";
EXPECT_TRUE(StringRef(os.str()).starts_with('['));
EXPECT_THAT(os.str(), AllOf(HasSubstr("A\n"), HasSubstr("B\n")));
}
@@ -128,6 +128,131 @@ TEST(DebugLogTest, DestructorPrefix) {
// After destructors, nothing should have been printed.
EXPECT_EQ(os.str(), "");
}
+
+TEST(DebugLogTest, LDBG_MACROS) {
+ llvm::DebugFlag = true;
+ static const char *DT[] = {"A:3", "B:2"};
+ setCurrentDebugTypes(DT, sizeof(DT) / sizeof(DT[0]));
+ std::string Str;
+ raw_string_ostream DebugOs(Str);
+ std::string StrRef;
+ raw_string_ostream RefOs(StrRef);
+#undef LDBG_STREAM
+#define LDBG_STREAM DebugOs
+#define DEBUG_TYPE "A"
+ LDBG() << "Hello, world!";
+ RefOs << "[A:1] " << __LLVM_FILE_NAME__ << ":" << (__LINE__ - 1)
+ << " Hello, world!\n";
+ EXPECT_EQ(DebugOs.str(), RefOs.str());
+ Str.clear();
+ StrRef.clear();
+
+ // Test with a level, no type.
+ LDBG(2) << "Hello, world!";
+ RefOs << "[A:2] " << __LLVM_FILE_NAME__ << ":" << (__LINE__ - 1)
+ << " Hello, world!\n";
+ EXPECT_EQ(DebugOs.str(), RefOs.str());
+ Str.clear();
+ StrRef.clear();
+
+// Now the type will be explicit, check we don't use DEBUG_TYPE.
+#undef DEBUG_TYPE
+
+ // Test with a type
+ LDBG("B") << "Hello, world!";
+ RefOs << "[B:1] " << __LLVM_FILE_NAME__ << ":" << (__LINE__ - 1)
+ << " Hello, world!\n";
+ EXPECT_EQ(DebugOs.str(), RefOs.str());
+ Str.clear();
+ StrRef.clear();
+
+ // Test with a level and a type
+ LDBG(2, "B") << "Hello, world!";
+ RefOs << "[B:2] " << __LLVM_FILE_NAME__ << ":" << (__LINE__ - 1)
+ << " Hello, world!\n";
+ EXPECT_EQ(DebugOs.str(), RefOs.str());
+ Str.clear();
+ StrRef.clear();
+
+ // Test with a type and a level
+ LDBG("B", 2) << "Hello, world!";
+ RefOs << "[B:2] " << __LLVM_FILE_NAME__ << ":" << (__LINE__ - 1)
+ << " Hello, world!\n";
+ EXPECT_EQ(DebugOs.str(), RefOs.str());
+ Str.clear();
+ StrRef.clear();
+
+ // Test with a type not enabled.
+ LDBG("C", 1) << "Hello, world!";
+ EXPECT_EQ(DebugOs.str(), "");
+
+ // Test with a level not enabled.
+ LDBG("B", 3) << "Hello, world!";
+ EXPECT_EQ(DebugOs.str(), "");
+}
+
+TEST(DebugLogTest, LDBG_OS_MACROS) {
+ llvm::DebugFlag = true;
+ static const char *DT[] = {"A:3", "B:2"};
+ setCurrentDebugTypes(DT, sizeof(DT) / sizeof(DT[0]));
+ std::string Str;
+ raw_string_ostream DebugOs(Str);
+ std::string StrRef;
+ raw_string_ostream RefOs(StrRef);
+#undef LDBG_STREAM
+#define LDBG_STREAM DebugOs
+#define DEBUG_TYPE "A"
+ LDBG_OS([](raw_ostream &Os) { Os << "Hello, world!"; });
+ RefOs << "[A:1] " << __LLVM_FILE_NAME__ << ":" << (__LINE__ - 1)
+ << " Hello, world!\n";
+ EXPECT_EQ(DebugOs.str(), RefOs.str());
+ Str.clear();
+ StrRef.clear();
+
+ // Test with a level, no type.
+ LDBG_OS(2, [](raw_ostream &Os) { Os << "Hello, world!"; });
+ RefOs << "[A:2] " << __LLVM_FILE_NAME__ << ":" << (__LINE__ - 1)
+ << " Hello, world!\n";
+ EXPECT_EQ(DebugOs.str(), RefOs.str());
+ Str.clear();
+ StrRef.clear();
+
+// Now the type will be explicit, check we don't use DEBUG_TYPE.
+#undef DEBUG_TYPE
+
+ // Test with a type.
+ LDBG_OS("B", [](raw_ostream &Os) { Os << "Hello, world!"; });
+ RefOs << "[B:1] " << __LLVM_FILE_NAME__ << ":" << (__LINE__ - 1)
+ << " Hello, world!\n";
+ EXPECT_EQ(DebugOs.str(), RefOs.str());
+ Str.clear();
+ StrRef.clear();
+
+ // Test with a level and a type
+ LDBG(3, "A") << "Hello, world!";
+ RefOs << "[A:3] " << __LLVM_FILE_NAME__ << ":" << (__LINE__ - 1)
+ << " Hello, world!\n";
+ EXPECT_EQ(DebugOs.str(), RefOs.str());
+ Str.clear();
+ StrRef.clear();
+
+ // Test with a type and a level
+ LDBG("B", 2) << "Hello, world!";
+ RefOs << "[B:2] " << __LLVM_FILE_NAME__ << ":" << (__LINE__ - 1)
+ << " Hello, world!\n";
+ EXPECT_EQ(DebugOs.str(), RefOs.str());
+ Str.clear();
+ StrRef.clear();
+
+ // Test with a type not enabled.
+ LDBG_OS("C", 1, [](raw_ostream &Os) { Os << "Hello, world!"; });
+ EXPECT_EQ(DebugOs.str(), "");
+
+ // Test with a level not enabled.
+ LDBG_OS("B", 3, [](raw_ostream &Os) { Os << "Hello, world!"; });
+ EXPECT_EQ(DebugOs.str(), "");
+}
+
#else
TEST(DebugLogTest, Basic) {
// LDBG should be compiled out in NDEBUG, so just check it compiles and has
>From 1570e392f09ea4fb34678e0ac7d2d19b94ff05b0 Mon Sep 17 00:00:00 2001
From: Mehdi Amini <joker.eph at gmail.com>
Date: Fri, 5 Sep 2025 15:57:56 -0700
Subject: [PATCH 2/2] [MLIR] Add debug log to the pass manager (NFC)
---
mlir/lib/Pass/Pass.cpp | 244 ++++++++++++++++++++++++++++++++++++-----
1 file changed, 214 insertions(+), 30 deletions(-)
diff --git a/mlir/lib/Pass/Pass.cpp b/mlir/lib/Pass/Pass.cpp
index 7094c8e279f2d..521c7c6be17b6 100644
--- a/mlir/lib/Pass/Pass.cpp
+++ b/mlir/lib/Pass/Pass.cpp
@@ -21,11 +21,14 @@
#include "llvm/ADT/Hashing.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/ScopeExit.h"
+#include "llvm/Support/DebugLog.h"
#include "llvm/Support/Mutex.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/Threading.h"
#include <optional>
+#define DEBUG_TYPE "pass-manager"
+
using namespace mlir;
using namespace mlir::detail;
@@ -242,6 +245,7 @@ LogicalResult OpPassManagerImpl::finalizePassList(MLIRContext *ctx) {
};
// Walk the pass list and merge adjacent adaptors.
+ LDBG(3) << "Merging adjacent adaptors in pass list";
OpToOpPassAdaptor *lastAdaptor = nullptr;
for (auto &pass : passes) {
// Check to see if this pass is an adaptor.
@@ -249,18 +253,26 @@ LogicalResult OpPassManagerImpl::finalizePassList(MLIRContext *ctx) {
// If it is the first adaptor in a possible chain, remember it and
// continue.
if (!lastAdaptor) {
+ LDBG(3) << "Found first adaptor in chain";
lastAdaptor = currentAdaptor;
continue;
}
// Otherwise, try to merge into the existing adaptor and delete the
// current one. If merging fails, just remember this as the last adaptor.
- if (succeeded(currentAdaptor->tryMergeInto(ctx, *lastAdaptor)))
+ LDBG(3) << "Attempting to merge adaptor with "
+ << currentAdaptor->getPassManagers().size()
+ << " managers into previous adaptor";
+ if (succeeded(currentAdaptor->tryMergeInto(ctx, *lastAdaptor))) {
+ LDBG(3) << "Successfully merged adaptors, removing current one";
pass.reset();
- else
+ } else {
+ LDBG(3) << "Failed to merge adaptors, keeping current as last";
lastAdaptor = currentAdaptor;
+ }
} else if (lastAdaptor) {
// If this pass isn't an adaptor, finalize it and forget the last adaptor.
+ LDBG(3) << "Finalizing adaptor chain before non-adaptor pass";
if (failed(finalizeAdaptor(lastAdaptor)))
return failure();
lastAdaptor = nullptr;
@@ -273,15 +285,26 @@ LogicalResult OpPassManagerImpl::finalizePassList(MLIRContext *ctx) {
// Now that the adaptors have been merged, erase any empty slots corresponding
// to the merged adaptors that were nulled-out in the loop above.
+ size_t beforeErase = passes.size();
llvm::erase_if(passes, std::logical_not<std::unique_ptr<Pass>>());
+ if (beforeErase != passes.size()) {
+ LDBG(3) << "Removed " << (beforeErase - passes.size())
+ << " merged adaptor slots from pass list";
+ }
// If this is a op-agnostic pass manager, there is nothing left to do.
std::optional<OperationName> rawOpName = getOpName(*ctx);
- if (!rawOpName)
+ if (!rawOpName) {
+ LDBG(3)
+ << "Op-agnostic pass manager, skipping operation-specific verification";
return success();
+ }
// Otherwise, verify that all of the passes are valid for the current
// operation anchor.
+ LDBG(3) << "Verifying " << passes.size() << " passes for operation '"
+ << getOpAnchorName() << "'";
+
std::optional<RegisteredOperationName> opName =
rawOpName->getRegisteredInfo();
for (std::unique_ptr<Pass> &pass : passes) {
@@ -292,6 +315,8 @@ LogicalResult OpPassManagerImpl::finalizePassList(MLIRContext *ctx) {
<< "'!";
}
}
+
+ LDBG(3) << "Pass list finalization completed successfully";
return success();
}
@@ -456,23 +481,45 @@ OpPassManager::Nesting OpPassManager::getNesting() { return impl->nesting; }
LogicalResult OpPassManager::initialize(MLIRContext *context,
unsigned newInitGeneration) {
- if (impl->initializationGeneration == newInitGeneration)
+
+ if (impl->initializationGeneration == newInitGeneration) {
+ LDBG(2) << "Pass manager already initialized "
+ << "' (generation " << newInitGeneration << ") with " << size()
+ << " passes";
return success();
+ }
+
+ LDBG(2) << "Initializing pass manager '" << getOpAnchorName()
+ << "' (generation " << newInitGeneration << ") with " << size()
+ << " passes";
impl->initializationGeneration = newInitGeneration;
+
for (Pass &pass : getPasses()) {
// If this pass isn't an adaptor, directly initialize it.
auto *adaptor = dyn_cast<OpToOpPassAdaptor>(&pass);
if (!adaptor) {
- if (failed(pass.initialize(context)))
+ LDBG(2) << "Initializing pass '" << pass.getName() << "'";
+ if (failed(pass.initialize(context))) {
+ LDBG(2) << "Failed to initialize pass '" << pass.getName() << "'";
return failure();
+ }
continue;
}
// Otherwise, initialize each of the adaptors pass managers.
+ LDBG(3) << "Initializing adaptor pass with "
+ << adaptor->getPassManagers().size() << " nested managers";
for (OpPassManager &adaptorPM : adaptor->getPassManagers())
- if (failed(adaptorPM.initialize(context, newInitGeneration)))
+ if (failed(adaptorPM.initialize(context, newInitGeneration))) {
+ LDBG(2) << "Failed to initialize nested pass manager";
return failure();
+ }
}
+
+ LDBG_OS([&](raw_ostream &os) {
+ os << "Pass manager initialization completed successfully: ";
+ printAsTextualPipeline(os, /*pretty=*/false);
+ });
return success();
}
@@ -499,16 +546,23 @@ llvm::hash_code OpPassManager::hash() {
LogicalResult OpToOpPassAdaptor::run(Pass *pass, Operation *op,
AnalysisManager am, bool verifyPasses,
unsigned parentInitGeneration) {
+ LDBG() << "Running pass '" << pass->getName() << "' on operation '"
+ << OpWithFlags(op, OpPrintingFlags().skipRegions()) << "' at "
+ << op->getLoc();
+
std::optional<RegisteredOperationName> opInfo = op->getRegisteredInfo();
- if (!opInfo)
+ if (!opInfo) {
return op->emitOpError()
<< "trying to schedule a pass on an unregistered operation";
- if (!opInfo->hasTrait<OpTrait::IsIsolatedFromAbove>())
+ }
+ if (!opInfo->hasTrait<OpTrait::IsIsolatedFromAbove>()) {
return op->emitOpError() << "trying to schedule a pass on an operation not "
"marked as 'IsolatedFromAbove'";
- if (!pass->canScheduleOn(*op->getName().getRegisteredInfo()))
+ }
+ if (!pass->canScheduleOn(*op->getName().getRegisteredInfo())) {
return op->emitOpError()
<< "trying to schedule a pass on an unsupported operation";
+ }
// Initialize the pass state with a callback for the pass to dynamically
// execute a pipeline on the currently visited operation.
@@ -526,8 +580,10 @@ LogicalResult OpToOpPassAdaptor::run(Pass *pass, Operation *op,
pipeline.getImpl().canScheduleOn(*op->getContext(), root->getName()));
// Before running, finalize the passes held by the pipeline.
- if (failed(pipeline.getImpl().finalizePassList(root->getContext())))
+ if (failed(pipeline.getImpl().finalizePassList(root->getContext()))) {
+ LDBG() << "Failed to finalize pass list for pipeline";
return failure();
+ }
// Initialize the user provided pipeline and execute the pipeline.
if (failed(pipeline.initialize(root->getContext(), parentInitGeneration)))
@@ -599,6 +655,13 @@ LogicalResult OpToOpPassAdaptor::runPipeline(
OpPassManager &pm, Operation *op, AnalysisManager am, bool verifyPasses,
unsigned parentInitGeneration, PassInstrumentor *instrumentor,
const PassInstrumentation::PipelineParentInfo *parentInfo) {
+ LDBG_OS([&](raw_ostream &os) {
+ os << "Running pipeline on operation '"
+ << OpWithFlags(op, OpPrintingFlags().skipRegions()) << "' with "
+ << pm.size() << " passes, verifyPasses=" << verifyPasses
+ << " pipeline: ";
+ pm.printAsTextualPipeline(os, /*pretty=*/false);
+ });
assert((!instrumentor || parentInfo) &&
"expected parent info if instrumentor is provided");
auto scopeExit = llvm::make_scope_exit([&] {
@@ -615,9 +678,14 @@ LogicalResult OpToOpPassAdaptor::runPipeline(
*parentInfo);
}
- for (Pass &pass : pm.getPasses())
- if (failed(run(&pass, op, am, verifyPasses, parentInitGeneration)))
+ for (Pass &pass : pm.getPasses()) {
+ if (failed(run(&pass, op, am, verifyPasses, parentInitGeneration))) {
+ LDBG() << "Pipeline failed for pass '" << pass.getName()
+ << "' on operation '"
+ << OpWithFlags(op, OpPrintingFlags().skipRegions()) << "'";
return failure();
+ }
+ }
if (instrumentor) {
instrumentor->runAfterPipeline(pm.getOpName(*op->getContext()),
@@ -630,9 +698,19 @@ LogicalResult OpToOpPassAdaptor::runPipeline(
/// does not exist.
static OpPassManager *
findPassManagerWithAnchor(MutableArrayRef<OpPassManager> mgrs, StringRef name) {
+ LDBG(3) << "Looking for pass manager with anchor name '" << name << "' among "
+ << mgrs.size() << " managers";
+
auto *it = llvm::find_if(
mgrs, [&](OpPassManager &mgr) { return mgr.getOpAnchorName() == name; });
- return it == mgrs.end() ? nullptr : &*it;
+
+ if (it == mgrs.end()) {
+ LDBG(2) << "No pass manager found with anchor name '" << name << "'";
+ return nullptr;
+ }
+
+ LDBG(2) << "Found pass manager with anchor name '" << name << "'";
+ return &*it;
}
/// Find an operation pass manager that can operate on an operation of the given
@@ -640,10 +718,22 @@ findPassManagerWithAnchor(MutableArrayRef<OpPassManager> mgrs, StringRef name) {
static OpPassManager *findPassManagerFor(MutableArrayRef<OpPassManager> mgrs,
OperationName name,
MLIRContext &context) {
+ LDBG(4) << "Looking for pass manager that can handle operation '" << name
+ << "' among " << mgrs.size() << " managers";
+
auto *it = llvm::find_if(mgrs, [&](OpPassManager &mgr) {
return mgr.getImpl().canScheduleOn(context, name);
});
- return it == mgrs.end() ? nullptr : &*it;
+
+ if (it == mgrs.end()) {
+ LDBG(4) << "No pass manager found that can handle operation '" << name
+ << "'";
+ return nullptr;
+ }
+
+ LDBG(4) << "Found pass manager '" << it->getOpAnchorName()
+ << "' that can handle operation '" << name << "'";
+ return &*it;
}
OpToOpPassAdaptor::OpToOpPassAdaptor(OpPassManager &&mgr) {
@@ -657,6 +747,9 @@ void OpToOpPassAdaptor::getDependentDialects(DialectRegistry &dialects) const {
LogicalResult OpToOpPassAdaptor::tryMergeInto(MLIRContext *ctx,
OpToOpPassAdaptor &rhs) {
+ LDBG(3) << "Attempting to merge pass adaptor with " << mgrs.size()
+ << " managers into rhs with " << rhs.mgrs.size() << " managers";
+
// Functor used to check if a pass manager is generic, i.e. op-agnostic.
auto isGenericPM = [&](OpPassManager &pm) { return !pm.getOpName(); };
@@ -682,14 +775,24 @@ LogicalResult OpToOpPassAdaptor::tryMergeInto(MLIRContext *ctx,
//
// Check the current adaptor.
auto *lhsGenericPMIt = llvm::find_if(mgrs, isGenericPM);
- if (lhsGenericPMIt != mgrs.end() &&
- hasScheduleConflictWith(*lhsGenericPMIt, rhs.mgrs))
- return failure();
+ if (lhsGenericPMIt != mgrs.end()) {
+ LDBG(4) << "Found generic pass manager on LHS, checking for conflicts";
+ if (hasScheduleConflictWith(*lhsGenericPMIt, rhs.mgrs)) {
+ LDBG(4)
+ << "Merge failed: LHS generic pass manager has conflicts with RHS";
+ return failure();
+ }
+ }
// Check the rhs adaptor.
auto *rhsGenericPMIt = llvm::find_if(rhs.mgrs, isGenericPM);
- if (rhsGenericPMIt != rhs.mgrs.end() &&
- hasScheduleConflictWith(*rhsGenericPMIt, mgrs))
- return failure();
+ if (rhsGenericPMIt != rhs.mgrs.end()) {
+ LDBG(4) << "Found generic pass manager on RHS, checking for conflicts";
+ if (hasScheduleConflictWith(*rhsGenericPMIt, mgrs)) {
+ LDBG(4)
+ << "Merge failed: RHS generic pass manager has conflicts with LHS";
+ return failure();
+ }
+ }
for (auto &pm : mgrs) {
// If an existing pass manager exists, then merge the given pass manager
@@ -744,25 +847,51 @@ void OpToOpPassAdaptor::runOnOperation(bool verifyPasses) {
/// Run this pass adaptor synchronously.
void OpToOpPassAdaptor::runOnOperationImpl(bool verifyPasses) {
+ LDBG_OS([&](raw_ostream &os) {
+ os << "Running pass adaptor synchronously on operation '"
+ << OpWithFlags(getOperation(), OpPrintingFlags().skipRegions())
+ << "' with " << mgrs.size()
+ << " pass managers, verifyPasses=" << verifyPasses << " pipeline: ";
+ printAsTextualPipeline(os, /*pretty=*/false);
+ });
+
auto am = getAnalysisManager();
PassInstrumentation::PipelineParentInfo parentInfo = {llvm::get_threadid(),
this};
auto *instrumentor = am.getPassInstrumentor();
+
+ unsigned processedOps = 0;
for (auto ®ion : getOperation()->getRegions()) {
for (auto &block : region) {
for (auto &op : block) {
auto *mgr = findPassManagerFor(mgrs, op.getName(), *op.getContext());
- if (!mgr)
+ if (!mgr) {
+ LDBG(2) << "Skipping operation '"
+ << OpWithFlags(&op, OpPrintingFlags().skipRegions())
+ << "': no suitable pass manager found";
continue;
+ }
// Run the held pipeline over the current operation.
+ LDBG(2) << "Processing operation '"
+ << OpWithFlags(&op, OpPrintingFlags().skipRegions())
+ << "' with pass manager '" << mgr->getOpAnchorName() << "'";
+
unsigned initGeneration = mgr->impl->initializationGeneration;
if (failed(runPipeline(*mgr, &op, am.nest(&op), verifyPasses,
- initGeneration, instrumentor, &parentInfo)))
+ initGeneration, instrumentor, &parentInfo))) {
+ LDBG(2) << "Pipeline failed for operation '"
+ << OpWithFlags(&op, OpPrintingFlags().skipRegions()) << "'";
signalPassFailure();
+ } else {
+ processedOps++;
+ }
}
}
}
+
+ LDBG() << "Completed synchronous pass adaptor run, processed " << processedOps
+ << " operations";
}
/// Utility functor that checks if the two ranges of pass managers have a size
@@ -776,13 +905,24 @@ static bool hasSizeMismatch(ArrayRef<OpPassManager> lhs,
/// Run this pass adaptor synchronously.
void OpToOpPassAdaptor::runOnOperationAsyncImpl(bool verifyPasses) {
+ LDBG_OS([&](raw_ostream &os) {
+ os << "Running pass adaptor asynchronously on operation '"
+ << OpWithFlags(getOperation(), OpPrintingFlags().skipRegions())
+ << "' with " << mgrs.size()
+ << " pass managers, verifyPasses=" << verifyPasses << " pipeline: ";
+ printAsTextualPipeline(os, /*pretty=*/false);
+ });
+
AnalysisManager am = getAnalysisManager();
MLIRContext *context = &getContext();
// Create the async executors if they haven't been created, or if the main
// pipeline has changed.
- if (asyncExecutors.empty() || hasSizeMismatch(asyncExecutors.front(), mgrs))
+ if (asyncExecutors.empty() || hasSizeMismatch(asyncExecutors.front(), mgrs)) {
+ LDBG(2) << "Creating " << context->getThreadPool().getMaxConcurrency()
+ << " async executors";
asyncExecutors.assign(context->getThreadPool().getMaxConcurrency(), mgrs);
+ }
// This struct represents the information for a single operation to be
// scheduled on a pass manager.
@@ -803,21 +943,36 @@ void OpToOpPassAdaptor::runOnOperationAsyncImpl(bool verifyPasses) {
// operation, as well as providing a queue of operations to execute over.
std::vector<OpPMInfo> opInfos;
DenseMap<OperationName, std::optional<unsigned>> knownOpPMIdx;
+
+ LDBG(2) << "Collecting operations for async execution";
for (auto ®ion : getOperation()->getRegions()) {
for (Operation &op : region.getOps()) {
// Get the pass manager index for this operation type.
auto pmIdxIt = knownOpPMIdx.try_emplace(op.getName(), std::nullopt);
if (pmIdxIt.second) {
- if (auto *mgr = findPassManagerFor(mgrs, op.getName(), *context))
+ if (auto *mgr = findPassManagerFor(mgrs, op.getName(), *context)) {
pmIdxIt.first->second = std::distance(mgrs.begin(), mgr);
+ LDBG(2) << "Operation '"
+ << OpWithFlags(&op, OpPrintingFlags().skipRegions())
+ << "' will use pass manager '" << mgr->getOpAnchorName()
+ << "'";
+ }
}
// If this operation can be scheduled, add it to the list.
- if (pmIdxIt.first->second)
+ if (pmIdxIt.first->second) {
opInfos.emplace_back(*pmIdxIt.first->second, &op, am.nest(&op));
+ } else {
+ LDBG(2) << "Operation '"
+ << OpWithFlags(&op, OpPrintingFlags().skipRegions())
+ << "' skipped: no suitable pass manager";
+ }
}
}
+ LDBG(2) << "Collected " << opInfos.size()
+ << " operations for async execution";
+
// Get the current thread for this adaptor.
PassInstrumentation::PipelineParentInfo parentInfo = {llvm::get_threadid(),
this};
@@ -872,23 +1027,36 @@ void PassManager::enableVerifier(bool enabled) { verifyPasses = enabled; }
/// Run the passes within this manager on the provided operation.
LogicalResult PassManager::run(Operation *op) {
+ LDBG_OS([&](raw_ostream &os) {
+ os << "Starting PassManager run on operation '"
+ << OpWithFlags(op, OpPrintingFlags().skipRegions()) << "' with "
+ << size() << " passes, verifyPasses=" << verifyPasses << " pipeline: ";
+ printAsTextualPipeline(os, /*pretty=*/false);
+ });
+
MLIRContext *context = getContext();
std::optional<OperationName> anchorOp = getOpName(*context);
- if (anchorOp && anchorOp != op->getName())
+ if (anchorOp && anchorOp != op->getName()) {
return emitError(op->getLoc())
<< "can't run '" << getOpAnchorName() << "' pass manager on '"
<< op->getName() << "' op";
+ }
// Register all dialects for the current pipeline.
+ LDBG(2) << "Registering dependent dialects for pipeline";
DialectRegistry dependentDialects;
getDependentDialects(dependentDialects);
context->appendDialectRegistry(dependentDialects);
- for (StringRef name : dependentDialects.getDialectNames())
+ for (StringRef name : dependentDialects.getDialectNames()) {
+ LDBG(2) << "Loading dialect: " << name;
context->getOrLoadDialect(name);
+ }
// Before running, make sure to finalize the pipeline pass list.
- if (failed(getImpl().finalizePassList(context)))
+ if (failed(getImpl().finalizePassList(context))) {
+ LDBG(2) << "Pass list finalization failed";
return failure();
+ }
// Notify the context that we start running a pipeline for bookkeeping.
context->enterMultiThreadedExecution();
@@ -898,17 +1066,27 @@ LogicalResult PassManager::run(Operation *op) {
llvm::hash_code pipelineKey = hash();
if (newInitKey != initializationKey ||
pipelineKey != pipelineInitializationKey) {
- if (failed(initialize(context, impl->initializationGeneration + 1)))
+ LDBG(2) << "Initializing passes with new generation: "
+ << (impl->initializationGeneration + 1);
+ if (failed(initialize(context, impl->initializationGeneration + 1))) {
+ LDBG(2) << "Pass initialization failed";
return failure();
+ }
initializationKey = newInitKey;
pipelineInitializationKey = pipelineKey;
+ } else {
+ LDBG(2) << "Using existing pass initialization (generation: "
+ << impl->initializationGeneration << ")";
}
// Construct a top level analysis manager for the pipeline.
+ LDBG(2) << "Constructing analysis manager for pipeline execution";
ModuleAnalysisManager am(op, instrumentor.get());
// If reproducer generation is enabled, run the pass manager with crash
// handling enabled.
+ LDBG(2) << "Executing pipeline with "
+ << (crashReproGenerator ? "crash recovery" : "normal execution");
LogicalResult result =
crashReproGenerator ? runWithCrashRecovery(op, am) : runPasses(op, am);
@@ -916,8 +1094,13 @@ LogicalResult PassManager::run(Operation *op) {
context->exitMultiThreadedExecution();
// Dump all of the pass statistics if necessary.
- if (passStatisticsMode)
+ if (passStatisticsMode) {
+ LDBG(2) << "Dumping pass statistics";
dumpStatistics();
+ }
+
+ LDBG(2) << "PassManager run completed with result: "
+ << (succeeded(result) ? "success" : "failure");
return result;
}
@@ -930,6 +1113,7 @@ void PassManager::addInstrumentation(std::unique_ptr<PassInstrumentation> pi) {
}
LogicalResult PassManager::runPasses(Operation *op, AnalysisManager am) {
+ LDBG(2) << "Executing passes using OpToOpPassAdaptor pipeline";
return OpToOpPassAdaptor::runPipeline(*this, op, am, verifyPasses,
impl->initializationGeneration);
}
More information about the llvm-commits
mailing list