diff options
author | Nicolas Vasilache <nico.vasilache@amd.com> | 2025-07-04 10:52:53 +0200 |
---|---|---|
committer | Nicolas Vasilache <nico.vasilache@amd.com> | 2025-07-04 11:59:57 +0200 |
commit | 3a632bd7deadf2150d2fb64e732cf9c52ce6c83e (patch) | |
tree | 318aaccff0978deedab523d46c462b161e78c836 | |
parent | 2b8f82b2bad6b2ada988fb2b874d676aa748a35b (diff) | |
download | llvm-users/nico/python-2.zip llvm-users/nico/python-2.tar.gz llvm-users/nico/python-2.tar.bz2 |
[mlir][python] Add debug helpersusers/nico/python-2
This PR lets users provide --debug-only flags with python decorators.
This greatly simplifies the debugging experience in python.
Co-authored-by: Tres <tpopp@users.noreply.github.com>
-rw-r--r-- | llvm/include/llvm/Support/Debug.h | 35 | ||||
-rw-r--r-- | llvm/lib/Support/Debug.cpp | 31 | ||||
-rw-r--r-- | llvm/unittests/Support/DebugTest.cpp | 25 | ||||
-rw-r--r-- | mlir/include/mlir-c/Debug.h | 18 | ||||
-rw-r--r-- | mlir/lib/Bindings/Python/IRCore.cpp | 21 | ||||
-rw-r--r-- | mlir/lib/CAPI/Debug/Debug.cpp | 15 | ||||
-rw-r--r-- | mlir/python/mlir/_mlir_libs/_mlir/ir.pyi | 2 | ||||
-rw-r--r-- | mlir/python/mlir/_mlir_libs/_mlir/passmanager.pyi | 3 | ||||
-rw-r--r-- | mlir/python/mlir/utils.py | 76 | ||||
-rw-r--r-- | mlir/test/python/utils.py | 27 |
10 files changed, 247 insertions, 6 deletions
diff --git a/llvm/include/llvm/Support/Debug.h b/llvm/include/llvm/Support/Debug.h index 924d7b2..6720ef5c 100644 --- a/llvm/include/llvm/Support/Debug.h +++ b/llvm/include/llvm/Support/Debug.h @@ -54,6 +54,28 @@ void setCurrentDebugType(const char *Type); /// void setCurrentDebugTypes(const char **Types, unsigned Count); +/// appendDebugType - Set the current debug type, as if the +/// -debug-only=CurX,CurY,NewZ option were specified when called with NewZ. +/// The previous state is tracked, so popAppendedDebugTypes can be called to +/// restore the previous state. Note that DebugFlag also needs to be set to true +/// for debug output to be produced. +/// +void appendDebugType(const char *Type); + +/// appendDebugTypes - Set the current debug type, as if the +/// -debug-only=CurX,CurY,NewX,NewY option were specified when called with +/// [NewX, NewY]. The previous state is tracked, so popAppendedDebugTypes can be +/// called to restore the previous state. Note that DebugFlag also needs to be +/// set to true debug output to be produced. +/// +void appendDebugTypes(const char **Types, unsigned Count); + +/// popAppendedDebugTypes - Restores CurDebugType to the state before the last +/// call to appendDebugType(s). Asserts and returns if the previous state was +/// empty or was reset by setCurrentDebugType(s). +/// +void popAppendedDebugTypes(); + /// DEBUG_WITH_TYPE macro - This macro should be used by passes to emit debug /// information. If the '-debug' option is specified on the commandline, and if /// this is a debug build, then the code specified as the option to the macro @@ -77,6 +99,19 @@ void setCurrentDebugTypes(const char **Types, unsigned Count); #define DEBUG_WITH_TYPE(TYPE, ...) \ do { \ } while (false) +#define appendDebugType(X) \ + do { \ + (void)(X); \ + } while (false) +#define appendDebugTypes(X, N) \ + do { \ + (void)(X); \ + (void)(N); \ + } while (false) +#define popAppendedDebugTypes() \ + do { \ + ; \ + } while (false) #endif /// This boolean is set to true if the '-debug' command line option diff --git a/llvm/lib/Support/Debug.cpp b/llvm/lib/Support/Debug.cpp index 5bb04d0..2582be7 100644 --- a/llvm/lib/Support/Debug.cpp +++ b/llvm/lib/Support/Debug.cpp @@ -35,6 +35,9 @@ #undef isCurrentDebugType #undef setCurrentDebugType #undef setCurrentDebugTypes +#undef appendDebugType +#undef appendDebugTypes +#undef popAppendedDebugTypes using namespace llvm; @@ -45,6 +48,32 @@ namespace llvm { bool DebugFlag = false; static ManagedStatic<std::vector<std::string>> CurrentDebugType; +static ManagedStatic<std::vector<int>> AppendedDebugTypeSizes; + +/// Appends to the CurrentDebugState by keeping its old state and adding the +/// new state. +void appendDebugTypes(const char **Types, unsigned Count) { + AppendedDebugTypeSizes->push_back(CurrentDebugType->size()); + for (size_t T = 0; T < Count; ++T) + CurrentDebugType->push_back(Types[T]); +} + +/// Appends to the CurrentDebugState by keeping its old state and adding the +/// new state. +void appendDebugType(const char *Type) { appendDebugTypes(&Type, 1); } + +/// Restore to the state before the latest call to appendDebugTypes. This can be +/// done multiple times. +void popAppendedDebugTypes() { + assert(AppendedDebugTypeSizes->size() > 0 && + "Popping from DebugTypes without any previous appending."); + + if (!AppendedDebugTypeSizes->size()) + return; + + CurrentDebugType->resize(AppendedDebugTypeSizes->back()); + AppendedDebugTypeSizes->pop_back(); +} /// Return true if the specified string is the debug type /// specified on the command line, or if none was specified on the command line @@ -72,6 +101,8 @@ void setCurrentDebugType(const char *Type) { } void setCurrentDebugTypes(const char **Types, unsigned Count) { + assert(AppendedDebugTypeSizes->size() == 0 && + "Resetting CurrentDebugType when it was previously appended to"); CurrentDebugType->clear(); llvm::append_range(*CurrentDebugType, ArrayRef(Types, Count)); } diff --git a/llvm/unittests/Support/DebugTest.cpp b/llvm/unittests/Support/DebugTest.cpp index e8b7548..3874142 100644 --- a/llvm/unittests/Support/DebugTest.cpp +++ b/llvm/unittests/Support/DebugTest.cpp @@ -19,8 +19,8 @@ using namespace llvm; TEST(DebugTest, Basic) { std::string s1, s2; raw_string_ostream os1(s1), os2(s2); - static const char *DT[] = {"A", "B"}; - + static const char *DT[] = {"A", "B"}; + llvm::DebugFlag = true; setCurrentDebugTypes(DT, 2); DEBUG_WITH_TYPE("A", os1 << "A"); @@ -50,4 +50,25 @@ TEST(DebugTest, CommaInDebugBlock) { }); EXPECT_EQ("ZYX", os1.str()); } + +TEST(DebugTest, AppendAndPop) { + std::string s1, s2, s3; + raw_string_ostream os1(s1), os2(s2), os3(s3); + + llvm::DebugFlag = true; + appendDebugType("A"); + DEBUG_WITH_TYPE("A", os1 << "A"); + DEBUG_WITH_TYPE("B", os1 << "B"); + EXPECT_EQ("A", os1.str()); + + appendDebugType("B"); + DEBUG_WITH_TYPE("A", os2 << "A"); + DEBUG_WITH_TYPE("B", os2 << "B"); + EXPECT_EQ("AB", os2.str()); + + popAppendedDebugTypes(); + DEBUG_WITH_TYPE("A", os3 << "A"); + DEBUG_WITH_TYPE("B", os3 << "B"); + EXPECT_EQ("A", os3.str()); +} #endif diff --git a/mlir/include/mlir-c/Debug.h b/mlir/include/mlir-c/Debug.h index 7dad735..8f206e4 100644 --- a/mlir/include/mlir-c/Debug.h +++ b/mlir/include/mlir-c/Debug.h @@ -31,6 +31,24 @@ MLIR_CAPI_EXPORTED void mlirSetGlobalDebugType(const char *type); /// output to be produced. MLIR_CAPI_EXPORTED void mlirSetGlobalDebugTypes(const char **types, intptr_t n); +/// Adds to the current debug type state, similarly to +/// `-debug-only=prev_type,new_type` in the command-line tools. Note that global +/// debug should be enabled for any output to be produced. A single append call +/// can be reverted with mlirPopAppendedGlobalDebugTypes. +MLIR_CAPI_EXPORTED void mlirAppendGlobalDebugType(const char *type); + +/// Adds to the current debug type state, similarly to +/// `-debug-only=prev_type,new_type1,new_type2` in the command-line tools. Note +/// that global debug should be enabled for any output to be produced. A single +/// append call can be reverted with mlirPopAppendedGlobalDebugTypes. +MLIR_CAPI_EXPORTED void mlirAppendGlobalDebugTypes(const char **types, + intptr_t n); + +/// Restores the current debug type state to its state before the last append +/// call. An appended state of `-debug-only=prev_type,new_type1,new_type2` would +/// be `-debug-only=prev_type` after this call. +MLIR_CAPI_EXPORTED void mlirPopAppendedGlobalDebugTypes(); + /// Checks if `type` is set as the current debug type. MLIR_CAPI_EXPORTED bool mlirIsCurrentDebugType(const char *type); diff --git a/mlir/lib/Bindings/Python/IRCore.cpp b/mlir/lib/Bindings/Python/IRCore.cpp index 002923b..6ce27d1 100644 --- a/mlir/lib/Bindings/Python/IRCore.cpp +++ b/mlir/lib/Bindings/Python/IRCore.cpp @@ -282,7 +282,26 @@ struct PyGlobalDebugFlag { pointers.push_back(str.c_str()); nb::ft_lock_guard lock(mutex); mlirSetGlobalDebugTypes(pointers.data(), pointers.size()); - }); + }) + .def_static( + "push_debug_only_flags", + [](const std::string &type) { + mlirAppendGlobalDebugType(type.c_str()); + }, + "flags"_a, + "Appends specific debug only flags which can be popped later.") + .def_static("push_debug_only_flags", + [](const std::vector<std::string> &types) { + std::vector<const char *> pointers; + pointers.reserve(types.size()); + for (const std::string &str : types) + pointers.push_back(str.c_str()); + mlirAppendGlobalDebugTypes(pointers.data(), + pointers.size()); + }) + .def_static( + "pop_debug_only_flags", []() { mlirPopAppendedGlobalDebugTypes(); }, + "Removes the latest non-popped addition from push_debug_only_flags."); } private: diff --git a/mlir/lib/CAPI/Debug/Debug.cpp b/mlir/lib/CAPI/Debug/Debug.cpp index 320ece4..c76a109 100644 --- a/mlir/lib/CAPI/Debug/Debug.cpp +++ b/mlir/lib/CAPI/Debug/Debug.cpp @@ -34,3 +34,18 @@ bool mlirIsCurrentDebugType(const char *type) { using namespace llvm; return isCurrentDebugType(type); } + +void mlirAppendGlobalDebugType(const char *type) { + using namespace llvm; + appendDebugType(type); +} + +void mlirAppendGlobalDebugTypes(const char **types, intptr_t n) { + using namespace llvm; + appendDebugTypes(types, n); +} + +void mlirPopAppendedGlobalDebugTypes() { + using namespace llvm; + popAppendedDebugTypes(); +} diff --git a/mlir/python/mlir/_mlir_libs/_mlir/ir.pyi b/mlir/python/mlir/_mlir_libs/_mlir/ir.pyi index 56b9f17..ffba1e3 100644 --- a/mlir/python/mlir/_mlir_libs/_mlir/ir.pyi +++ b/mlir/python/mlir/_mlir_libs/_mlir/ir.pyi @@ -2818,3 +2818,5 @@ class VectorType(ShapedType): class _GlobalDebug: flag: ClassVar[bool] = False + def push_debug_only_flags(self, types: list[str]) -> None: ... + def pop_debug_only_flags(self) -> None: ... diff --git a/mlir/python/mlir/_mlir_libs/_mlir/passmanager.pyi b/mlir/python/mlir/_mlir_libs/_mlir/passmanager.pyi index 0d2eaff..d23da98 100644 --- a/mlir/python/mlir/_mlir_libs/_mlir/passmanager.pyi +++ b/mlir/python/mlir/_mlir_libs/_mlir/passmanager.pyi @@ -12,9 +12,10 @@ __all__ = [ ] class PassManager: - def __init__(self, context: _ir.Context | None = None) -> None: ... + def __init__(self, anchor_op: str | None = None) -> None: ... def _CAPICreate(self) -> object: ... def _testing_release(self) -> None: ... + def add(self, pipeline: str) -> None: ... def enable_ir_printing( self, print_before_all: bool = False, diff --git a/mlir/python/mlir/utils.py b/mlir/python/mlir/utils.py index c6e9b57..cf8ad220 100644 --- a/mlir/python/mlir/utils.py +++ b/mlir/python/mlir/utils.py @@ -209,3 +209,79 @@ def call_with_toplevel_context_create_module( decorated = with_toplevel_context_create_module(f) decorated() return decorated + + +def _debug_flags_impl(flags: Sequence[str]) -> Iterator[None]: + from mlir.ir import _GlobalDebug + + # Save the original debug state. The debug flags will be popped rather than + # manually copied and saved for later. + original_flag = _GlobalDebug.flag + _GlobalDebug.flag = True + _GlobalDebug.push_debug_only_flags(flags) + + try: + yield + finally: + # Reset the global debug flag and remove the most recent flags that were + # appended. This assumes that nothing else popped when it should not have. + _GlobalDebug.flag = original_flag + _GlobalDebug.pop_debug_only_flags() + + +@contextmanager +def debug_flags_context(flags: Sequence[str]): + """Temporarily create a context that enables debugging with specified filters. + + These would be the same as running with -debug-only=*flags. Where multiple contexts + will be joined together to create the full list if they are nested. + + This requires that the core MLIR units were compiled without NDEBUG. + """ + return _debug_flags_impl(flags) + + +@contextmanager +def debug_conversion(flags: Sequence[str] = []) -> Iterator[None]: + """Temporarily create a context that enables full conversion debugging, + potentially with additional specified filters. + + These would be the same as running with -debug-only=*flags. Where multiple contexts + will be joined together to create the full list if they are nested. + + This requires that the core MLIR units were compiled without NDEBUG. + """ + return _debug_flags_impl(list(flags) + ["dialect-conversion"]) + + +@contextmanager +def debug_greedy_rewriter(flags: Sequence[str] = []) -> Iterator[None]: + """Temporarily create a context that enables full conversion debugging, + potentially with additional specified filters. + + These would be the same as running with -debug-only=*flags. Where multiple contexts + will be joined together to create the full list if they are nested. + + This requires that the core MLIR units were compiled without NDEBUG. + """ + return _debug_flags_impl(list(flags) + ["greedy_rewriter"]) + + +@contextmanager +def debug_td(flags: Sequence[str] = [], *, full_debug: bool = False) -> Iterator[None]: + """Temporarily create a context that enables full transform dialect debugging, + potentially with additional specified filters. + + These would be the same as running with -debug-only=*flags. Where multiple contexts + will be joined together to create the full list if they are nested. + + This requires that the core MLIR units were compiled without NDEBUG. + """ + return _debug_flags_impl( + list(flags) + + [ + "transform-dialect", + "transform-dialect-print-top-level-after-all", + ] + + (["transform-dialect-full"] if full_debug else []) + ) diff --git a/mlir/test/python/utils.py b/mlir/test/python/utils.py index 8435fdd..c700808 100644 --- a/mlir/test/python/utils.py +++ b/mlir/test/python/utils.py @@ -1,13 +1,15 @@ # RUN: %python %s | FileCheck %s +# RUN: %python %s 2>&1 | FileCheck %s --check-prefix=DEBUG_ONLY import unittest from mlir import ir -from mlir.dialects import arith, builtin -from mlir.extras import types as T +from mlir.passmanager import PassManager +from mlir.dialects import arith from mlir.utils import ( call_with_toplevel_context_create_module, caller_mlir_context, + debug_conversion, using_mlir_context, ) @@ -31,6 +33,7 @@ class TestRequiredContext(unittest.TestCase): c = arith.ConstantOp(value=42.42, result=ir.F32Type.get()).result multiple_adds(c, c) + # CHECK-LABEL: module { # CHECK: constant # CHECK-NEXT: arith.addf # CHECK-NEXT: arith.addf @@ -54,5 +57,25 @@ class TestRequiredContext(unittest.TestCase): pass +class TestDebugOnlyFlags(unittest.TestCase): + def test_debug_types(self): + """Test checks --debug-only=xxx functionality is available in MLIR.""" + + @debug_conversion() + def lower(module) -> None: + pm = PassManager("builtin.module") + pm.add("convert-arith-to-llvm") + pm.run(module.operation) + + @call_with_toplevel_context_create_module + def _(module) -> None: + c = arith.ConstantOp(value=42.42, result=ir.F32Type.get()).result + arith.AddFOp(c, c, fastmath=arith.FastMathFlags.nnan | arith.FastMathFlags.ninf) + + # DEBUG_ONLY-LABEL: Legalizing operation : 'builtin.module' + # DEBUG_ONLY: Legalizing operation : 'arith.addf' + lower(module) + + if __name__ == "__main__": unittest.main() |