//===- ExecutionContext.cpp - Debug Execution Context Support -------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "mlir/Debug/ExecutionContext.h" #include "llvm/ADT/ScopeExit.h" #include "llvm/Support/FormatVariadic.h" #include using namespace mlir; using namespace mlir::tracing; //===----------------------------------------------------------------------===// // ActionActiveStack //===----------------------------------------------------------------------===// void ActionActiveStack::print(raw_ostream &os, bool withContext) const { os << "ActionActiveStack depth " << getDepth() << "\n"; const ActionActiveStack *current = this; int count = 0; while (current) { llvm::errs() << llvm::formatv("#{0,3}: ", count++); current->action.print(llvm::errs()); llvm::errs() << "\n"; ArrayRef context = current->action.getContextIRUnits(); if (withContext && !context.empty()) { llvm::errs() << "Context:\n"; llvm::interleave( current->action.getContextIRUnits(), [&](const IRUnit &unit) { llvm::errs() << " - "; unit.print(llvm::errs()); }, [&]() { llvm::errs() << "\n"; }); llvm::errs() << "\n"; } current = current->parent; } } //===----------------------------------------------------------------------===// // ExecutionContext //===----------------------------------------------------------------------===// static const LLVM_THREAD_LOCAL ActionActiveStack *actionStack = nullptr; void ExecutionContext::registerObserver(Observer *observer) { observers.push_back(observer); } void ExecutionContext::operator()(llvm::function_ref transform, const Action &action) { // Update the top of the stack with the current action. int depth = 0; if (actionStack) depth = actionStack->getDepth() + 1; ActionActiveStack info{actionStack, action, depth}; actionStack = &info; auto raii = llvm::make_scope_exit([&]() { actionStack = info.getParent(); }); Breakpoint *breakpoint = nullptr; // Invoke the callback here and handles control requests here. auto handleUserInput = [&]() -> bool { if (!onBreakpointControlExecutionCallback) return true; auto todoNext = onBreakpointControlExecutionCallback(actionStack); switch (todoNext) { case ExecutionContext::Apply: depthToBreak = std::nullopt; return true; case ExecutionContext::Skip: depthToBreak = std::nullopt; return false; case ExecutionContext::Step: depthToBreak = depth + 1; return true; case ExecutionContext::Next: depthToBreak = depth; return true; case ExecutionContext::Finish: depthToBreak = depth - 1; return true; } llvm::report_fatal_error("Unknown control request"); }; // 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) { breakpoint = breakpointManager->match(action); if (breakpoint) break; } info.setBreakpoint(breakpoint); bool shouldExecuteAction = true; // If we have a breakpoint, or if `depthToBreak` was previously set and the // current depth matches, we invoke the user-provided callback. if (breakpoint || (depthToBreak && depth <= depthToBreak)) shouldExecuteAction = handleUserInput(); // Notify the observers about the current action. for (auto *observer : observers) observer->beforeExecute(actionStack, breakpoint, shouldExecuteAction); if (shouldExecuteAction) { // Execute the action here. transform(); // Notify the observers about completion of the action. for (auto *observer : observers) observer->afterExecute(actionStack); } if (depthToBreak && depth <= depthToBreak) handleUserInput(); }