[Mlir-commits] [mlir] d8d1d44 - [MLIR] Rerun control for actions in execution context (#209197)
llvmlistbot at llvm.org
llvmlistbot at llvm.org
Tue Jul 21 02:53:23 PDT 2026
Author: Kigyosi Alexandru
Date: 2026-07-21T11:53:17+02:00
New Revision: d8d1d444a2a69293292f10f19691da05207630f7
URL: https://github.com/llvm/llvm-project/commit/d8d1d444a2a69293292f10f19691da05207630f7
DIFF: https://github.com/llvm/llvm-project/commit/d8d1d444a2a69293292f10f19691da05207630f7.diff
LOG: [MLIR] Rerun control for actions in execution context (#209197)
Adds a Rerun control value to ExecutionContext::Control that allows the
re-execution of the current action immediately after it completes,
without restarting the full compilation pipeline. This is analogous to
GDB's ability to restart execution from a breakpoint. When the callback
returns Rerun, the action is executed normally, then re-dispatched
through the full ExecutionContext::operator() pipeline, including
breakpoint matching, so the user gets a fresh opportunity to inspect or
control the re-execution. As a practical usage example, a breakpoint +
an observer can be added, to save and restore IR between runs to check
if each run produces the same IR or something different each time. A
depth-keyed structure is used, so rerun requests survive nested action
dispatch and are consumed by the correct stack frame.
Added:
Modified:
mlir/include/mlir/Debug/ExecutionContext.h
mlir/lib/Debug/ExecutionContext.cpp
mlir/unittests/Debug/ExecutionContextTest.cpp
Removed:
################################################################################
diff --git a/mlir/include/mlir/Debug/ExecutionContext.h b/mlir/include/mlir/Debug/ExecutionContext.h
index fbad04b9a2f1d..93c26e0ce5f82 100644
--- a/mlir/include/mlir/Debug/ExecutionContext.h
+++ b/mlir/include/mlir/Debug/ExecutionContext.h
@@ -65,7 +65,15 @@ class ExecutionContext {
/// - Finish: The action is executed and the execution is paused only when we
/// reach the parent/enclosing operation. If there are no enclosing
/// operation, the execution continues without stopping.
- enum Control { Apply = 1, Skip = 2, Step = 3, Next = 4, Finish = 5 };
+ /// - Rerun: The action gets executed again immediately after it finishes.
+ enum Control {
+ Apply = 1,
+ Skip = 2,
+ Step = 3,
+ Next = 4,
+ Finish = 5,
+ Rerun = 6
+ };
/// The type of the callback that is used to control the execution.
/// The callback is passed the current action.
@@ -134,6 +142,9 @@ class ExecutionContext {
/// The list of managers that are queried for breakpoints.
SmallVector<BreakpointManager *> breakpoints;
+
+ /// Stack of depths of actions that should be rerun.
+ SmallVector<int> rerunControlStack;
};
} // namespace tracing
diff --git a/mlir/lib/Debug/ExecutionContext.cpp b/mlir/lib/Debug/ExecutionContext.cpp
index 7fc5c165d391e..cc72bac549ab0 100644
--- a/mlir/lib/Debug/ExecutionContext.cpp
+++ b/mlir/lib/Debug/ExecutionContext.cpp
@@ -84,10 +84,25 @@ void ExecutionContext::operator()(llvm::function_ref<void()> transform,
case ExecutionContext::Finish:
depthToBreak = depth - 1;
return true;
+ case ExecutionContext::Rerun:
+ depthToBreak = std::nullopt;
+ rerunControlStack.push_back(depth);
+ return true;
}
llvm::report_fatal_error("Unknown control request");
};
+ auto rerunCurrentActionIfRequested = [&]() -> bool {
+ if (!rerunControlStack.empty() && rerunControlStack.back() == depth) {
+ // If the user requested to rerun this action, we do it here.
+ rerunControlStack.pop_back();
+ actionStack = info.getParent();
+ (*this)(transform, action);
+ return true;
+ }
+ return false;
+ };
+
// Try to find a breakpoint that would hit on this action.
// Right now there is no way to collect them all, we stop at the first one.
for (auto *breakpointManager : breakpoints) {
@@ -116,6 +131,11 @@ void ExecutionContext::operator()(llvm::function_ref<void()> transform,
observer->afterExecute(actionStack);
}
- if (depthToBreak && depth <= depthToBreak)
+ if (rerunCurrentActionIfRequested())
+ return;
+
+ if (depthToBreak && depth <= depthToBreak) {
handleUserInput();
+ rerunCurrentActionIfRequested();
+ }
}
diff --git a/mlir/unittests/Debug/ExecutionContextTest.cpp b/mlir/unittests/Debug/ExecutionContextTest.cpp
index 642adff51002a..1c442d4aa1acc 100644
--- a/mlir/unittests/Debug/ExecutionContextTest.cpp
+++ b/mlir/unittests/Debug/ExecutionContextTest.cpp
@@ -349,4 +349,71 @@ TEST(ExecutionContext, EnableDisableBreakpointOnCallback) {
executionCtx(original, DebuggerAction{});
EXPECT_EQ(counter, 4);
}
+
+TEST(ExecutionContext, RerunRequestedFromPostActionCallback) {
+ // If rerun is requested from the post-action callback path,
+ // the same action must be rerun immediately.
+ std::vector<ExecutionContext::Control> controlSequence = {
+ ExecutionContext::Next, ExecutionContext::Rerun, ExecutionContext::Apply};
+ int idx = 0;
+ int callbackCounter = 0;
+ int executionCounter = 0;
+
+ auto onBreakpoint = [&](const ActionActiveStack *backtrace) {
+ ++callbackCounter;
+ EXPECT_EQ(backtrace->getAction().getTag(), DebuggerAction::tag);
+ return controlSequence[idx++];
+ };
+
+ TagBreakpointManager simpleManager;
+ ExecutionContext executionCtx(onBreakpoint);
+ executionCtx.addBreakpointManager(&simpleManager);
+ simpleManager.addBreakpoint(DebuggerAction::tag);
+
+ auto callback = [&]() { ++executionCounter; };
+
+ executionCtx(callback, DebuggerAction{});
+ EXPECT_EQ(callbackCounter, 3);
+ EXPECT_EQ(executionCounter, 2);
+}
+
+TEST(ExecutionContext, RerunStackWithNestedActions) {
+ // Request rerun at an outer depth, then request rerun again at an inner
+ // depth before the outer rerun is consumed.
+ int debuggerHits = 0;
+ int otherHits = 0;
+ int debuggerExecutions = 0;
+ int otherExecutions = 0;
+
+ auto onBreakpoint = [&](const ActionActiveStack *backtrace) {
+ StringRef tag = backtrace->getAction().getTag();
+ if (tag == DebuggerAction::tag)
+ return ++debuggerHits == 1 ? ExecutionContext::Rerun
+ : ExecutionContext::Apply;
+ if (tag == OtherAction::tag)
+ return ++otherHits == 1 ? ExecutionContext::Rerun
+ : ExecutionContext::Apply;
+ ADD_FAILURE();
+ return ExecutionContext::Apply;
+ };
+
+ TagBreakpointManager simpleManager;
+ ExecutionContext executionCtx(onBreakpoint);
+ executionCtx.addBreakpointManager(&simpleManager);
+ simpleManager.addBreakpoint(DebuggerAction::tag);
+ simpleManager.addBreakpoint(OtherAction::tag);
+
+ auto nested = [&]() { ++otherExecutions; };
+ auto original = [&]() {
+ ++debuggerExecutions;
+ executionCtx(nested, OtherAction{});
+ };
+
+ executionCtx(original, DebuggerAction{});
+
+ EXPECT_EQ(debuggerHits, 2);
+ EXPECT_EQ(otherHits, 3);
+ EXPECT_EQ(debuggerExecutions, 2);
+ EXPECT_EQ(otherExecutions, 3);
+}
} // namespace
More information about the Mlir-commits
mailing list