[llvm] [mlir] Introduce LDBG_OS() macro as a variant of LDBG() (PR #157194)
Mehdi Amini via llvm-commits
llvm-commits at lists.llvm.org
Tue Sep 9 14:53:54 PDT 2025
https://github.com/joker-eph updated https://github.com/llvm/llvm-project/pull/157194
>From 73e1581aee2c658680a17ab42a8dc74b4607f2cd 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] Introduce LDBG_OS() macro as a variant of LDBG()
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
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.
Co-authored-by: Andrzej WarzyĆski <andrzej.warzynski at gmail.com>
---
llvm/include/llvm/Support/Debug.h | 5 -
llvm/include/llvm/Support/DebugLog.h | 301 ++++++++++++++----
llvm/unittests/Support/DebugLogTest.cpp | 137 +++++++-
.../lib/Dialect/Transform/IR/TransformOps.cpp | 12 +-
4 files changed, 370 insertions(+), 85 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..75c2b66f3839b 100644
--- a/llvm/include/llvm/Support/DebugLog.h
+++ b/llvm/include/llvm/Support/DebugLog.h
@@ -19,52 +19,57 @@
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 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.
-#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
-#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))
+/// 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__)
// 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,29 +81,191 @@ 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)); \
- _c; _c = false) \
- for (::llvm::impl::raw_ldbg_ostream LdbgOS{ \
- ::llvm::impl::computePrefix(TYPE, FILE, LINE, LEVEL), (STREAM)}; \
- _c; _c = false) \
- ::llvm::impl::RAIINewLineStream{LdbgOS}.asLvalue()
-
-#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__)
-
+// Everything below are implementation details of the macros above.
namespace impl {
+/// This macro expands to the stream to use for output, we use a macro to allow
+/// unit-testing to override.
+#define LDBG_STREAM ::llvm::dbgs()
+
+// ----------------------------------------------------------------------------
+// LDBG() implementation
+// ----------------------------------------------------------------------------
+
+// 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_TYPE_AND_LEVEL, LDBG_LEVEL_OR_TYPE, ))
+#define LDBG_NO_ARG_EXPANDER() , , LDBG_NO_ARG
+#define _GET_LDBG_MACRO(...) \
+ LDBG_CHOOSE_FROM_ARG_COUNT(LDBG_NO_ARG_EXPANDER __VA_ARGS__())
+
+/// This macro is the core of the LDBG() implementation. It is used to print the
+/// debug output with the given stream, level, type, file, and line number.
+#define LDBG_STREAM_LEVEL_TYPE_FILE_AND_LINE(STREAM, LEVEL_OR_TYPE, \
+ TYPE_OR_LEVEL, FILE, LINE) \
+ for (bool _c = true; \
+ _c && ::llvm::DebugFlag && \
+ ::llvm::impl::ldbgIsCurrentDebugType(TYPE_OR_LEVEL, LEVEL_OR_TYPE); \
+ _c = false) \
+ ::llvm::impl::raw_ldbg_ostream{ \
+ ::llvm::impl::computePrefix(TYPE_OR_LEVEL, FILE, LINE, LEVEL_OR_TYPE), \
+ (STREAM), /*ShouldPrefixNextString=*/true, \
+ /*ShouldEmitNewLineOnDestruction=*/true} \
+ .asLvalue()
+
+/// These macros are helpers to implement LDBG() with an increasing amount of
+/// optional arguments made explicit.
+#define LDBG_STREAM_LEVEL_TYPE_AND_FILE(STREAM, LEVEL_OR_TYPE, TYPE_OR_LEVEL, \
+ FILE) \
+ LDBG_STREAM_LEVEL_TYPE_FILE_AND_LINE(STREAM, LEVEL_OR_TYPE, TYPE_OR_LEVEL, \
+ FILE, __LINE__)
+#define LDGB_STREAM_LEVEL_AND_TYPE(STREAM, LEVEL_OR_TYPE, TYPE_OR_LEVEL) \
+ LDBG_STREAM_LEVEL_TYPE_AND_FILE(STREAM, LEVEL_OR_TYPE, TYPE_OR_LEVEL, \
+ __LLVM_FILE_NAME__)
+/// This macro is a helper when LDBG() is called with 2 arguments.
+/// In this case we want to force the first argument to be the type for
+/// consistency in the codebase.
+/// We trick this by casting the first argument to a (const char *) which
+/// won't compile with an int.
+#define LDBG_TYPE_AND_LEVEL(TYPE, LEVEL) \
+ LDGB_STREAM_LEVEL_AND_TYPE(LDBG_STREAM, (const char *)(TYPE), (LEVEL))
+
+/// When a single argument is provided. This can be either a level or the debug
+/// type. If a level is provided, we default the debug type to DEBUG_TYPE, if a
+/// string is provided, we default the level to 1.
+#define LDBG_LEVEL_OR_TYPE(LEVEL_OR_TYPE) \
+ LDGB_STREAM_LEVEL_AND_TYPE(LDBG_STREAM, (LEVEL_OR_TYPE), \
+ LDBG_GET_DEFAULT_TYPE_OR_LEVEL(LEVEL_OR_TYPE))
+#define LDBG_NO_ARG() LDBG_LEVEL_OR_TYPE(1)
+
+// ----------------------------------------------------------------------------
+// LDBG_OS() implementation
+// ----------------------------------------------------------------------------
+
+// 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_LEVEL_AND_TYPE_AND_CALLBACK, \
+ LDBG_OS_LEVEL_AND_TYPE, LDBG_OS_LEVEL_OR_TYPE, ))
+#define LDBG_OS_NO_ARG_EXPANDER() , , , LDBG_OS_LEVEL_OR_TYPE
+#define _GET_LDBG_OS_MACRO(...) \
+ LDBG_OS_CHOOSE_FROM_ARG_COUNT(LDBG_OS_NO_ARG_EXPANDER __VA_ARGS__())
+
+/// This macro is the core of the LDBG_OS() macros. It is used to print the
+/// debug output with the given stream, level, type, file, and line number.
+#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_LEVEL_AND_TYPE_AND_CALLBACK(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_LEVEL_AND_TYPE(LEVEL_OR_TYPE, CALLBACK) \
+ LDBG_OS_LEVEL_AND_TYPE_AND_CALLBACK( \
+ LDBG_GET_DEFAULT_TYPE_OR_LEVEL(LEVEL_OR_TYPE), LEVEL_OR_TYPE, CALLBACK)
+#define LDBG_OS_LEVEL_OR_TYPE(CALLBACK) LDBG_OS_LEVEL_AND_TYPE(1, CALLBACK)
+
+// ----------------------------------------------------------------------------
+// General Helpers for the implementation above
+// ----------------------------------------------------------------------------
+
+/// These macros detect if the DEBUG_TYPE macro is defined. It is using a
+/// combination of preprocessor tricks and C++11 user-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 dispatches 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)
+
+#define IS_DEBUG_TYPE_DEFINED() LDBG_VARIABLE_CHECK(DEBUG_TYPE)
+
+/// User-defined string literal operator for the LDBG_VARIABLE_CHECK macro.
+constexpr bool operator""_LDBG_VARIABLE_CHECK(const char *, std::size_t) {
+ return true;
+}
+/// Fallback when DEBUG_TYPE is undefined
+#define DEBUG_TYPE_LDBG_VARIABLE_CHECK 0
+
+/// Helper to provide the default level (=1) or type (=DEBUG_TYPE). This is used
+/// when a single argument is passed to LDBG() (or LDBG_OS()), 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.
+/// Note: the two level macro is a workaround for a MSVC bug, see:
+/// https://developercommunity.visualstudio.com/t/C-parsing-failure:-resolving-user-defi/10962937
+#define LDBG_GET_DEFAULT_TYPE_OR_LEVEL(LEVEL_OR_TYPE) \
+ [](auto LevelOrType) { \
+ return [](auto LevelOrType) { \
+ if constexpr (std::is_integral_v<decltype(LevelOrType)>) { \
+ using ::llvm::impl::operator""_LDBG_VARIABLE_CHECK; \
+ using ::llvm::impl::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; \
+ } \
+ }(LevelOrType); \
+ }(LEVEL_OR_TYPE)
+
+/// Helpers to get DEBUG_TYPE as a StringRef, even when DEBUG_TYPE is not
+/// defined (in which case it expands 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)
+
+/// Return the stringified macro as a StringRef.
+/// Also, strip out potential surrounding quotes: this comes from an artifact of
+/// the macro stringification, if DEBUG_TYPE is undefined we get the string
+/// "DEBUG_TYPE", however if it is defined we get the string with the quotes.
+/// For example if DEBUG_TYPE is "foo", we get "\"foo\"" but we want to return
+/// "foo" here.
+constexpr ::llvm::StringRef operator""_LDBG_DEBUG_STRING(const char *Str,
+ std::size_t) {
+ ::llvm::StringRef S(Str);
+ if (Str[0] == '"' && Str[S.size() - 1] == '"')
+ return StringRef(Str + 1, S.size() - 2);
+ return S;
+}
+
+/// 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.
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 +298,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 +345,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 +366,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..5ed5d4a5708eb 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";
+ LDGB_STREAM_LEVEL_AND_TYPE(os, "", 0) << "NoType";
EXPECT_FALSE(StringRef(os.str()).starts_with('['));
EXPECT_TRUE(StringRef(os.str()).ends_with("NoType\n"));
}
@@ -36,8 +36,8 @@ 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";
+ LDGB_STREAM_LEVEL_AND_TYPE(os, 0, "A") << "A";
+ LDGB_STREAM_LEVEL_AND_TYPE(os, "B", 0) << "B";
EXPECT_TRUE(StringRef(os.str()).starts_with('['));
EXPECT_THAT(os.str(), AllOf(HasSubstr("A\n"), HasSubstr("B\n")));
}
@@ -48,18 +48,18 @@ TEST(DebugLogTest, Basic) {
raw_string_ostream os(str);
// Just check that the macro doesn't result in dangling else.
if (true)
- DEBUGLOG_WITH_STREAM_AND_TYPE(os, 0, "A") << "A";
+ LDGB_STREAM_LEVEL_AND_TYPE(os, 0, "A") << "A";
else
- DEBUGLOG_WITH_STREAM_AND_TYPE(os, 0, "A") << "B";
- DEBUGLOG_WITH_STREAM_AND_TYPE(os, 0, "B") << "B";
+ LDGB_STREAM_LEVEL_AND_TYPE(os, 0, "A") << "B";
+ LDGB_STREAM_LEVEL_AND_TYPE(os, 0, "B") << "B";
EXPECT_THAT(os.str(), AllOf(HasSubstr("A\n"), Not(HasSubstr("B\n"))));
int count = 0;
auto inc = [&]() { return ++count; };
EXPECT_THAT(count, Eq(0));
- DEBUGLOG_WITH_STREAM_AND_TYPE(os, 0, "A") << inc();
+ LDGB_STREAM_LEVEL_AND_TYPE(os, 0, "A") << inc();
EXPECT_THAT(count, Eq(1));
- DEBUGLOG_WITH_STREAM_AND_TYPE(os, 0, "B") << inc();
+ LDGB_STREAM_LEVEL_AND_TYPE(os, 0, "B") << inc();
EXPECT_THAT(count, Eq(1));
}
}
@@ -75,7 +75,7 @@ TEST(DebugLogTest, BasicWithLevel) {
raw_string_ostream os(str);
for (auto type : {"A", "B", "C", "D"})
for (int level : llvm::seq<int>(0, 4))
- DEBUGLOG_WITH_STREAM_TYPE_FILE_AND_LINE(os, level, type, type, level)
+ LDBG_STREAM_LEVEL_TYPE_FILE_AND_LINE(os, level, type, type, level)
<< level;
EXPECT_EQ(os.str(), "[A:0] A:0 0\n[A:1] A:1 1\n[A:2] A:2 2\n[A:3] A:3 "
"3\n[B:0] B:0 0\n[B:1] B:1 1\n[C:0] C:0 0\n");
@@ -92,7 +92,7 @@ TEST(DebugLogTest, NegativeLevel) {
raw_string_ostream os(str);
for (auto type : {"A", "B"})
for (int level : llvm::seq<int>(0, 2))
- DEBUGLOG_WITH_STREAM_TYPE_FILE_AND_LINE(os, level, type, type, level)
+ LDBG_STREAM_LEVEL_TYPE_FILE_AND_LINE(os, level, type, type, level)
<< level;
EXPECT_EQ(os.str(), "[A:0] A:0 0\n[B:0] B:0 0\n[B:1] B:1 1\n");
}
@@ -128,6 +128,123 @@ 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 StrExpected;
+ raw_string_ostream ExpectedOs(StrExpected);
+#undef LDBG_STREAM
+#define LDBG_STREAM DebugOs
+#define DEBUG_TYPE "A"
+ LDBG() << "Hello, world!";
+ ExpectedOs << "[A:1] " << __LLVM_FILE_NAME__ << ":" << (__LINE__ - 1)
+ << " Hello, world!\n";
+ EXPECT_EQ(DebugOs.str(), ExpectedOs.str());
+ Str.clear();
+ StrExpected.clear();
+
+ // Test with a level, no type.
+ LDBG(2) << "Hello, world!";
+ ExpectedOs << "[A:2] " << __LLVM_FILE_NAME__ << ":" << (__LINE__ - 1)
+ << " Hello, world!\n";
+ EXPECT_EQ(DebugOs.str(), ExpectedOs.str());
+ Str.clear();
+ StrExpected.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!";
+ ExpectedOs << "[B:1] " << __LLVM_FILE_NAME__ << ":" << (__LINE__ - 1)
+ << " Hello, world!\n";
+ EXPECT_EQ(DebugOs.str(), ExpectedOs.str());
+ Str.clear();
+ StrExpected.clear();
+
+ // Test with a type and a level
+ LDBG("B", 2) << "Hello, world!";
+ ExpectedOs << "[B:2] " << __LLVM_FILE_NAME__ << ":" << (__LINE__ - 1)
+ << " Hello, world!\n";
+ EXPECT_EQ(DebugOs.str(), ExpectedOs.str());
+ Str.clear();
+ StrExpected.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 StrExpected;
+ raw_string_ostream ExpectedOs(StrExpected);
+#undef LDBG_STREAM
+#define LDBG_STREAM DebugOs
+#define DEBUG_TYPE "A"
+ LDBG_OS([](raw_ostream &Os) { Os << "Hello, world!"; });
+ ExpectedOs << "[A:1] " << __LLVM_FILE_NAME__ << ":" << (__LINE__ - 1)
+ << " Hello, world!\n";
+ EXPECT_EQ(DebugOs.str(), ExpectedOs.str());
+ Str.clear();
+ StrExpected.clear();
+
+ // Test with a level, no type.
+ LDBG_OS(2, [](raw_ostream &Os) { Os << "Hello, world!"; });
+ ExpectedOs << "[A:2] " << __LLVM_FILE_NAME__ << ":" << (__LINE__ - 1)
+ << " Hello, world!\n";
+ EXPECT_EQ(DebugOs.str(), ExpectedOs.str());
+ Str.clear();
+ StrExpected.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!"; });
+ ExpectedOs << "[B:1] " << __LLVM_FILE_NAME__ << ":" << (__LINE__ - 1)
+ << " Hello, world!\n";
+ EXPECT_EQ(DebugOs.str(), ExpectedOs.str());
+ Str.clear();
+ StrExpected.clear();
+
+ // Test with a level and a type
+ LDBG_OS(3, "A", [](raw_ostream &Os) { Os << "Hello, world!"; });
+ ExpectedOs << "[A:3] " << __LLVM_FILE_NAME__ << ":" << (__LINE__ - 1)
+ << " Hello, world!\n";
+ EXPECT_EQ(DebugOs.str(), ExpectedOs.str());
+ Str.clear();
+ StrExpected.clear();
+
+ // Test with a type and a level
+ LDBG_OS("B", 2, [](raw_ostream &Os) { Os << "Hello, world!"; });
+ ExpectedOs << "[B:2] " << __LLVM_FILE_NAME__ << ":" << (__LINE__ - 1)
+ << " Hello, world!\n";
+ EXPECT_EQ(DebugOs.str(), ExpectedOs.str());
+ Str.clear();
+ StrExpected.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
diff --git a/mlir/lib/Dialect/Transform/IR/TransformOps.cpp b/mlir/lib/Dialect/Transform/IR/TransformOps.cpp
index aba6178a2ea6c..132ed815c354e 100644
--- a/mlir/lib/Dialect/Transform/IR/TransformOps.cpp
+++ b/mlir/lib/Dialect/Transform/IR/TransformOps.cpp
@@ -1151,7 +1151,7 @@ transform::CollectMatchingOp::apply(transform::TransformRewriter &rewriter,
std::optional<DiagnosedSilenceableFailure> maybeFailure;
for (Operation *root : state.getPayloadOps(getRoot())) {
WalkResult walkResult = root->walk([&](Operation *op) {
- LDBG(1, DEBUG_TYPE_MATCHER)
+ LDBG(DEBUG_TYPE_MATCHER, 1)
<< "matching "
<< OpWithFlags(op, OpPrintingFlags().assumeVerified().skipRegions())
<< " @" << op;
@@ -1166,7 +1166,7 @@ transform::CollectMatchingOp::apply(transform::TransformRewriter &rewriter,
if (diag.isDefiniteFailure())
return WalkResult::interrupt();
if (diag.isSilenceableFailure()) {
- LDBG(1, DEBUG_TYPE_MATCHER) << "matcher " << matcher.getName()
+ LDBG(DEBUG_TYPE_MATCHER, 1) << "matcher " << matcher.getName()
<< " failed: " << diag.getMessage();
return WalkResult::advance();
}
@@ -1298,7 +1298,7 @@ transform::ForeachMatchOp::apply(transform::TransformRewriter &rewriter,
if (!getRestrictRoot() && op == root)
return WalkResult::advance();
- LDBG(1, DEBUG_TYPE_MATCHER)
+ LDBG(DEBUG_TYPE_MATCHER, 1)
<< "matching "
<< OpWithFlags(op, OpPrintingFlags().assumeVerified().skipRegions())
<< " @" << op;
@@ -1314,7 +1314,7 @@ transform::ForeachMatchOp::apply(transform::TransformRewriter &rewriter,
if (diag.isDefiniteFailure())
return WalkResult::interrupt();
if (diag.isSilenceableFailure()) {
- LDBG(1, DEBUG_TYPE_MATCHER) << "matcher " << matcher.getName()
+ LDBG(DEBUG_TYPE_MATCHER, 1) << "matcher " << matcher.getName()
<< " failed: " << diag.getMessage();
continue;
}
@@ -2165,10 +2165,10 @@ DiagnosedSilenceableFailure transform::MatchOperationEmptyOp::matchOperation(
::std::optional<::mlir::Operation *> maybeCurrent,
transform::TransformResults &results, transform::TransformState &state) {
if (!maybeCurrent.has_value()) {
- LDBG(1, DEBUG_TYPE_MATCHER) << "MatchOperationEmptyOp success";
+ LDBG(DEBUG_TYPE_MATCHER, 1) << "MatchOperationEmptyOp success";
return DiagnosedSilenceableFailure::success();
}
- LDBG(1, DEBUG_TYPE_MATCHER) << "MatchOperationEmptyOp failure";
+ LDBG(DEBUG_TYPE_MATCHER, 1) << "MatchOperationEmptyOp failure";
return emitSilenceableError() << "operation is not empty";
}
More information about the llvm-commits
mailing list