[llvm] Introduce LDBG_OS() macro as a variant of LDBG() (PR #157194)

Jacques Pienaar via llvm-commits llvm-commits at lists.llvm.org
Tue Sep 9 08:16:29 PDT 2025


================
@@ -76,29 +81,190 @@ 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));     \
+// 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_LEVEL_AND_TYPE, 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 = (::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__)
+/// 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 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_LEVEL_AND_TYPE(LEVEL_OR_TYPE, TYPE_OR_LEVEL)                      \
+  LDGB_STREAM_LEVEL_AND_TYPE(LDBG_STREAM, (LEVEL_OR_TYPE), (TYPE_OR_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)                                      \
+  LDBG_LEVEL_AND_TYPE((LEVEL_OR_TYPE),                                         \
+                      LDBG_GET_DEFAULT_TYPE_OR_LEVEL(LEVEL_OR_TYPE))
+#define LDBG_NO_ARG() LDBG_LEVEL_OR_TYPE(1)
 
-namespace impl {
+// ----------------------------------------------------------------------------
+// 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__())
+
+/// 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.
+#define LDBG_GET_DEFAULT_TYPE_OR_LEVEL(LEVEL_OR_TYPE)                          \
+  [](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;                                                                \
+    }                                                                          \
+  }(LEVEL_OR_TYPE)
+
+/// 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 or LDBG_DEBUG_STREAM macros are
+/// defined. They use 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)
+#define IS_LDBG_DEBUG_STREAM_DEFINED() LDBG_VARIABLE_CHECK(LDBG_DEBUG_STREAM)
+
+/// 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 LDBG_DEBUG_STREAM is undefined
+#define LDBG_DEBUG_STREAM_LDBG_VARIABLE_CHECK 0
+/// Fallback when DEBUG_TYPE is undefined
+#define DEBUG_TYPE_LDBG_VARIABLE_CHECK 0
+
+/// 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) {
----------------
jpienaar wrote:

Where is 2nd variable used?

https://github.com/llvm/llvm-project/pull/157194


More information about the llvm-commits mailing list