-
Notifications
You must be signed in to change notification settings - Fork 15.4k
[MLIR] Add logging/tracing to DataFlow analysis and RemoveDeadValues (NFC) #144695
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
@llvm/pr-subscribers-mlir-core @llvm/pr-subscribers-mlir Author: Mehdi Amini (joker-eph) ChangesDebugging issue with this pass is quite difficult at the moment, this should help. Patch is 31.24 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/144695.diff 3 Files Affected:
diff --git a/mlir/lib/Analysis/DataFlow/DeadCodeAnalysis.cpp b/mlir/lib/Analysis/DataFlow/DeadCodeAnalysis.cpp
index e805e21d878bf..21e62a7d06a4f 100644
--- a/mlir/lib/Analysis/DataFlow/DeadCodeAnalysis.cpp
+++ b/mlir/lib/Analysis/DataFlow/DeadCodeAnalysis.cpp
@@ -22,9 +22,12 @@
#include "mlir/Interfaces/ControlFlowInterfaces.h"
#include "mlir/Support/LLVM.h"
#include "llvm/Support/Casting.h"
+#include "llvm/Support/Debug.h"
#include <cassert>
#include <optional>
+#define DEBUG_TYPE "dead-code-analysis"
+
using namespace mlir;
using namespace mlir::dataflow;
@@ -122,6 +125,9 @@ DeadCodeAnalysis::DeadCodeAnalysis(DataFlowSolver &solver)
}
LogicalResult DeadCodeAnalysis::initialize(Operation *top) {
+ LLVM_DEBUG(
+ llvm::dbgs() << "DCA: Initializing DeadCodeAnalysis for top-level op: "
+ << top->getName() << "\n");
// Mark the top-level blocks as executable.
for (Region ®ion : top->getRegions()) {
if (region.empty())
@@ -129,6 +135,8 @@ LogicalResult DeadCodeAnalysis::initialize(Operation *top) {
auto *state =
getOrCreate<Executable>(getProgramPointBefore(®ion.front()));
propagateIfChanged(state, state->setToLive());
+ LLVM_DEBUG(llvm::dbgs() << "DCA: Marked entry block live for region in op: "
+ << top->getName() << "\n");
}
// Mark as overdefined the predecessors of symbol callables with potentially
@@ -139,13 +147,21 @@ LogicalResult DeadCodeAnalysis::initialize(Operation *top) {
}
void DeadCodeAnalysis::initializeSymbolCallables(Operation *top) {
+ LLVM_DEBUG(
+ llvm::dbgs()
+ << "DCA: [init] Entering initializeSymbolCallables for top-level op: "
+ << top->getName() << "\n");
analysisScope = top;
auto walkFn = [&](Operation *symTable, bool allUsesVisible) {
+ LLVM_DEBUG(llvm::dbgs() << "DCA: [init] Processing symbol table op: "
+ << symTable->getName() << "\n");
Region &symbolTableRegion = symTable->getRegion(0);
Block *symbolTableBlock = &symbolTableRegion.front();
bool foundSymbolCallable = false;
for (auto callable : symbolTableBlock->getOps<CallableOpInterface>()) {
+ LLVM_DEBUG(llvm::dbgs() << "DCA: [init] Found CallableOpInterface: "
+ << callable.getOperation()->getName() << "\n");
Region *callableRegion = callable.getCallableRegion();
if (!callableRegion)
continue;
@@ -159,6 +175,10 @@ void DeadCodeAnalysis::initializeSymbolCallables(Operation *top) {
auto *state =
getOrCreate<PredecessorState>(getProgramPointAfter(callable));
propagateIfChanged(state, state->setHasUnknownPredecessors());
+ LLVM_DEBUG(
+ llvm::dbgs()
+ << "DCA: [init] Marked callable as having unknown predecessors: "
+ << callable.getOperation()->getName() << "\n");
}
foundSymbolCallable = true;
}
@@ -173,10 +193,17 @@ void DeadCodeAnalysis::initializeSymbolCallables(Operation *top) {
if (!uses) {
// If we couldn't gather the symbol uses, conservatively assume that
// we can't track information for any nested symbols.
+ LLVM_DEBUG(
+ llvm::dbgs()
+ << "DCA: [init] Could not gather symbol uses, conservatively marking "
+ "all nested callables as having unknown predecessors\n");
return top->walk([&](CallableOpInterface callable) {
auto *state =
getOrCreate<PredecessorState>(getProgramPointAfter(callable));
propagateIfChanged(state, state->setHasUnknownPredecessors());
+ LLVM_DEBUG(llvm::dbgs() << "DCA: [init] Marked nested callable as "
+ "having unknown predecessors: "
+ << callable.getOperation()->getName() << "\n");
});
}
@@ -190,10 +217,17 @@ void DeadCodeAnalysis::initializeSymbolCallables(Operation *top) {
continue;
auto *state = getOrCreate<PredecessorState>(getProgramPointAfter(symbol));
propagateIfChanged(state, state->setHasUnknownPredecessors());
+ LLVM_DEBUG(llvm::dbgs() << "DCA: [init] Found non-call use for symbol, "
+ "marked as having unknown predecessors: "
+ << symbol->getName() << "\n");
}
};
SymbolTable::walkSymbolTables(top, /*allSymUsesVisible=*/!top->getBlock(),
walkFn);
+ LLVM_DEBUG(
+ llvm::dbgs()
+ << "DCA: [init] Finished initializeSymbolCallables for top-level op: "
+ << top->getName() << "\n");
}
/// Returns true if the operation is a returning terminator in region
@@ -205,9 +239,15 @@ static bool isRegionOrCallableReturn(Operation *op) {
}
LogicalResult DeadCodeAnalysis::initializeRecursively(Operation *op) {
+ LLVM_DEBUG(
+ llvm::dbgs() << "DCA: [init] Entering initializeRecursively for op: "
+ << op->getName() << " at " << op << "\n");
// Initialize the analysis by visiting every op with control-flow semantics.
if (op->getNumRegions() || op->getNumSuccessors() ||
isRegionOrCallableReturn(op) || isa<CallOpInterface>(op)) {
+ LLVM_DEBUG(llvm::dbgs()
+ << "DCA: [init] Visiting op with control-flow semantics: " << *op
+ << "\n");
// When the liveness of the parent block changes, make sure to re-invoke the
// analysis on the op.
if (op->getBlock())
@@ -218,14 +258,26 @@ LogicalResult DeadCodeAnalysis::initializeRecursively(Operation *op) {
return failure();
}
// Recurse on nested operations.
- for (Region ®ion : op->getRegions())
- for (Operation &op : region.getOps())
- if (failed(initializeRecursively(&op)))
+ for (Region ®ion : op->getRegions()) {
+ LLVM_DEBUG(llvm::dbgs() << "DCA: [init] Recursing into region of op: "
+ << op->getName() << "\n");
+ for (Operation &nestedOp : region.getOps()) {
+ LLVM_DEBUG(llvm::dbgs()
+ << "DCA: [init] Recursing into nested op: "
+ << nestedOp.getName() << " at " << &nestedOp << "\n");
+ if (failed(initializeRecursively(&nestedOp)))
return failure();
+ }
+ }
+ LLVM_DEBUG(
+ llvm::dbgs() << "DCA: [init] Finished initializeRecursively for op: "
+ << op->getName() << " at " << op << "\n");
return success();
}
void DeadCodeAnalysis::markEdgeLive(Block *from, Block *to) {
+ LLVM_DEBUG(llvm::dbgs() << "DCA: Marking edge live from block " << from
+ << " to block " << to << "\n");
auto *state = getOrCreate<Executable>(getProgramPointBefore(to));
propagateIfChanged(state, state->setToLive());
auto *edgeState =
@@ -234,37 +286,54 @@ void DeadCodeAnalysis::markEdgeLive(Block *from, Block *to) {
}
void DeadCodeAnalysis::markEntryBlocksLive(Operation *op) {
+ LLVM_DEBUG(llvm::dbgs() << "DCA: Marking entry blocks live for op: "
+ << op->getName() << "\n");
for (Region ®ion : op->getRegions()) {
if (region.empty())
continue;
auto *state =
getOrCreate<Executable>(getProgramPointBefore(®ion.front()));
propagateIfChanged(state, state->setToLive());
+ LLVM_DEBUG(llvm::dbgs() << "DCA: Marked entry block live for region in op: "
+ << op->getName() << "\n");
}
}
LogicalResult DeadCodeAnalysis::visit(ProgramPoint *point) {
+ LLVM_DEBUG(llvm::dbgs() << "DCA: Visiting program point: " << point << " "
+ << *point << "\n");
if (point->isBlockStart())
return success();
Operation *op = point->getPrevOp();
+ LLVM_DEBUG(llvm::dbgs() << "DCA: Visiting operation: " << *op << "\n");
// If the parent block is not executable, there is nothing to do.
if (op->getBlock() != nullptr &&
- !getOrCreate<Executable>(getProgramPointBefore(op->getBlock()))->isLive())
+ !getOrCreate<Executable>(getProgramPointBefore(op->getBlock()))
+ ->isLive()) {
+ LLVM_DEBUG(llvm::dbgs()
+ << "DCA: Parent block not live, skipping op: " << *op << "\n");
return success();
+ }
// We have a live call op. Add this as a live predecessor of the callee.
- if (auto call = dyn_cast<CallOpInterface>(op))
+ if (auto call = dyn_cast<CallOpInterface>(op)) {
+ LLVM_DEBUG(llvm::dbgs() << "DCA: Visiting call operation: " << *op << "\n");
visitCallOperation(call);
+ }
// Visit the regions.
if (op->getNumRegions()) {
// Check if we can reason about the region control-flow.
if (auto branch = dyn_cast<RegionBranchOpInterface>(op)) {
+ LLVM_DEBUG(llvm::dbgs()
+ << "DCA: Visiting region branch operation: " << *op << "\n");
visitRegionBranchOperation(branch);
// Check if this is a callable operation.
} else if (auto callable = dyn_cast<CallableOpInterface>(op)) {
+ LLVM_DEBUG(llvm::dbgs()
+ << "DCA: Visiting callable operation: " << *op << "\n");
const auto *callsites = getOrCreateFor<PredecessorState>(
getProgramPointAfter(op), getProgramPointAfter(callable));
@@ -276,16 +345,22 @@ LogicalResult DeadCodeAnalysis::visit(ProgramPoint *point) {
// Otherwise, conservatively mark all entry blocks as executable.
} else {
+ LLVM_DEBUG(llvm::dbgs() << "DCA: Marking all entry blocks live for op: "
+ << *op << "\n");
markEntryBlocksLive(op);
}
}
if (isRegionOrCallableReturn(op)) {
if (auto branch = dyn_cast<RegionBranchOpInterface>(op->getParentOp())) {
+ LLVM_DEBUG(llvm::dbgs()
+ << "DCA: Visiting region terminator: " << *op << "\n");
// Visit the exiting terminator of a region.
visitRegionTerminator(op, branch);
} else if (auto callable =
dyn_cast<CallableOpInterface>(op->getParentOp())) {
+ LLVM_DEBUG(llvm::dbgs()
+ << "DCA: Visiting callable terminator: " << *op << "\n");
// Visit the exiting terminator of a callable.
visitCallableTerminator(op, callable);
}
@@ -294,10 +369,14 @@ LogicalResult DeadCodeAnalysis::visit(ProgramPoint *point) {
if (op->getNumSuccessors()) {
// Check if we can reason about the control-flow.
if (auto branch = dyn_cast<BranchOpInterface>(op)) {
+ LLVM_DEBUG(llvm::dbgs()
+ << "DCA: Visiting branch operation: " << *op << "\n");
visitBranchOperation(branch);
// Otherwise, conservatively mark all successors as exectuable.
} else {
+ LLVM_DEBUG(llvm::dbgs()
+ << "DCA: Marking all successors live for op: " << *op << "\n");
for (Block *successor : op->getSuccessors())
markEdgeLive(op->getBlock(), successor);
}
@@ -307,6 +386,8 @@ LogicalResult DeadCodeAnalysis::visit(ProgramPoint *point) {
}
void DeadCodeAnalysis::visitCallOperation(CallOpInterface call) {
+ LLVM_DEBUG(llvm::dbgs() << "DCA: visitCallOperation: "
+ << call.getOperation()->getName() << "\n");
Operation *callableOp = call.resolveCallableInTable(&symbolTable);
// A call to a externally-defined callable has unknown predecessors.
@@ -329,11 +410,17 @@ void DeadCodeAnalysis::visitCallOperation(CallOpInterface call) {
auto *callsites =
getOrCreate<PredecessorState>(getProgramPointAfter(callableOp));
propagateIfChanged(callsites, callsites->join(call));
+ LLVM_DEBUG(llvm::dbgs()
+ << "DCA: Added callsite as predecessor for callable: "
+ << callableOp->getName() << "\n");
} else {
// Mark this call op's predecessors as overdefined.
auto *predecessors =
getOrCreate<PredecessorState>(getProgramPointAfter(call));
propagateIfChanged(predecessors, predecessors->setHasUnknownPredecessors());
+ LLVM_DEBUG(llvm::dbgs()
+ << "DCA: Marked call op's predecessors as unknown for: "
+ << call.getOperation()->getName() << "\n");
}
}
@@ -365,6 +452,8 @@ DeadCodeAnalysis::getOperandValues(Operation *op) {
}
void DeadCodeAnalysis::visitBranchOperation(BranchOpInterface branch) {
+ LLVM_DEBUG(llvm::dbgs() << "DCA: visitBranchOperation: "
+ << branch.getOperation()->getName() << "\n");
// Try to deduce a single successor for the branch.
std::optional<SmallVector<Attribute>> operands = getOperandValues(branch);
if (!operands)
@@ -372,15 +461,21 @@ void DeadCodeAnalysis::visitBranchOperation(BranchOpInterface branch) {
if (Block *successor = branch.getSuccessorForOperands(*operands)) {
markEdgeLive(branch->getBlock(), successor);
+ LLVM_DEBUG(llvm::dbgs()
+ << "DCA: Branch has single successor: " << successor << "\n");
} else {
// Otherwise, mark all successors as executable and outgoing edges.
for (Block *successor : branch->getSuccessors())
markEdgeLive(branch->getBlock(), successor);
+ LLVM_DEBUG(llvm::dbgs()
+ << "DCA: Branch has multiple/all successors live\n");
}
}
void DeadCodeAnalysis::visitRegionBranchOperation(
RegionBranchOpInterface branch) {
+ LLVM_DEBUG(llvm::dbgs() << "DCA: visitRegionBranchOperation: "
+ << branch.getOperation()->getName() << "\n");
// Try to deduce which regions are executable.
std::optional<SmallVector<Attribute>> operands = getOperandValues(branch);
if (!operands)
@@ -397,16 +492,22 @@ void DeadCodeAnalysis::visitRegionBranchOperation(
// Mark the entry block as executable.
auto *state = getOrCreate<Executable>(point);
propagateIfChanged(state, state->setToLive());
+ LLVM_DEBUG(llvm::dbgs()
+ << "DCA: Marked region successor live: " << point << "\n");
// Add the parent op as a predecessor.
auto *predecessors = getOrCreate<PredecessorState>(point);
propagateIfChanged(
predecessors,
predecessors->join(branch, successor.getSuccessorInputs()));
+ LLVM_DEBUG(llvm::dbgs()
+ << "DCA: Added region branch as predecessor for successor: "
+ << point << "\n");
}
}
void DeadCodeAnalysis::visitRegionTerminator(Operation *op,
RegionBranchOpInterface branch) {
+ LLVM_DEBUG(llvm::dbgs() << "DCA: visitRegionTerminator: " << *op << "\n");
std::optional<SmallVector<Attribute>> operands = getOperandValues(op);
if (!operands)
return;
@@ -425,6 +526,9 @@ void DeadCodeAnalysis::visitRegionTerminator(Operation *op,
auto *state =
getOrCreate<Executable>(getProgramPointBefore(®ion->front()));
propagateIfChanged(state, state->setToLive());
+ LLVM_DEBUG(llvm::dbgs()
+ << "DCA: Marked region entry block live for region: " << region
+ << "\n");
predecessors = getOrCreate<PredecessorState>(
getProgramPointBefore(®ion->front()));
} else {
@@ -434,11 +538,16 @@ void DeadCodeAnalysis::visitRegionTerminator(Operation *op,
}
propagateIfChanged(predecessors,
predecessors->join(op, successor.getSuccessorInputs()));
+ LLVM_DEBUG(llvm::dbgs()
+ << "DCA: Added region terminator as predecessor for successor: "
+ << (successor.getSuccessor() ? "region entry" : "parent op")
+ << "\n");
}
}
void DeadCodeAnalysis::visitCallableTerminator(Operation *op,
CallableOpInterface callable) {
+ LLVM_DEBUG(llvm::dbgs() << "DCA: visitCallableTerminator: " << *op << "\n");
// Add as predecessors to all callsites this return op.
auto *callsites = getOrCreateFor<PredecessorState>(
getProgramPointAfter(op), getProgramPointAfter(callable));
@@ -449,11 +558,18 @@ void DeadCodeAnalysis::visitCallableTerminator(Operation *op,
getOrCreate<PredecessorState>(getProgramPointAfter(predecessor));
if (canResolve) {
propagateIfChanged(predecessors, predecessors->join(op));
+ LLVM_DEBUG(
+ llvm::dbgs()
+ << "DCA: Added callable terminator as predecessor for callsite: "
+ << predecessor->getName() << "\n");
} else {
// If the terminator is not a return-like, then conservatively assume we
// can't resolve the predecessor.
propagateIfChanged(predecessors,
predecessors->setHasUnknownPredecessors());
+ LLVM_DEBUG(llvm::dbgs()
+ << "DCA: Could not resolve callable terminator for callsite: "
+ << predecessor->getName() << "\n");
}
}
}
diff --git a/mlir/lib/Analysis/DataFlow/LivenessAnalysis.cpp b/mlir/lib/Analysis/DataFlow/LivenessAnalysis.cpp
index d61cdb143e7dd..8d8af7a92ac23 100644
--- a/mlir/lib/Analysis/DataFlow/LivenessAnalysis.cpp
+++ b/mlir/lib/Analysis/DataFlow/LivenessAnalysis.cpp
@@ -10,6 +10,7 @@
#include <cassert>
#include <mlir/Analysis/DataFlow/LivenessAnalysis.h>
+#include "llvm/Support/Debug.h"
#include <mlir/Analysis/DataFlow/ConstantPropagationAnalysis.h>
#include <mlir/Analysis/DataFlow/DeadCodeAnalysis.h>
#include <mlir/Analysis/DataFlow/SparseAnalysis.h>
@@ -20,6 +21,8 @@
#include <mlir/Interfaces/SideEffectInterfaces.h>
#include <mlir/Support/LLVM.h>
+#define DEBUG_TYPE "liveness-analysis"
+
using namespace mlir;
using namespace mlir::dataflow;
@@ -77,28 +80,46 @@ ChangeResult Liveness::meet(const AbstractSparseLattice &other) {
LogicalResult
LivenessAnalysis::visitOperation(Operation *op, ArrayRef<Liveness *> operands,
ArrayRef<const Liveness *> results) {
+ LLVM_DEBUG(llvm::dbgs() << "LNA: Visiting operation: " << *op << "\n");
// This marks values of type (1.a) and (4) liveness as "live".
if (!isMemoryEffectFree(op) || op->hasTrait<OpTrait::ReturnLike>()) {
- for (auto *operand : operands)
+ LLVM_DEBUG(llvm::dbgs()
+ << "LNA: [visitOperation] Operation has memory effects or is "
+ "return-like, marking operands live\n");
+ for (auto *operand : operands) {
+ LLVM_DEBUG(llvm::dbgs() << "LNA: [visitOperation] Marking operand live: "
+ << operand << " (" << operand->isLive << ")\n");
propagateIfChanged(operand, operand->markLive());
+ }
}
// This marks values of type (3) liveness as "live".
bool foundLiveResult = false;
for (const Liveness *r : results) {
if (r->isLive && !foundLiveResult) {
+ LLVM_DEBUG(llvm::dbgs() << "LNA: [visitOperation] Found live result, "
+ "meeting all operands with result: "
+ << r << "\n");
// It is assumed that each operand is used to compute each result of an
// op. Thus, if at least one result is live, each operand is live.
- for (Liveness *operand : operands)
+ for (Liveness *operand : operands) {
+ LLVM_DEBUG(llvm::dbgs() << "LNA: [visitOperation] Meeting operand: "
+ << operand << " with result: " << r << "\n");
meet(operand, *r);
+ }
foundLiveResult = true;
}
+ LLVM_DEBUG(llvm::dbgs()
+ << "LNA: [visitOperation] Adding dependency for result: " << r
+ << " after op: " << *op << "\n");
addDependency(const_cast<Liveness *>(r), getProgramPointAfter(op));
}
return success();
}
void LivenessAnalysis::visitBranchOperand(OpOperand &operand) {
+ LLVM_DEBUG(llvm::dbgs() << "LNA: Visiting branch operand: " << operand.get()
+ << " in op: " << *operand.getOwner() << "\n");
// We know (at the moment) and assume (for the future) that `operand` is a
// non-forwarded branch operand of a `RegionBranchOpInterface`,
// `BranchOpInterface`, `RegionBranchTerminatorOpInterface` or return-like op.
@@ -130,6 +151,10 @@ void LivenessAnalysis::visitBranchOperand(OpOperand &operand) {
for (Value result : op->getResults()) {
if (getLatticeElement(result)->isLive) {
mayLive = true;
+ LLVM_DEBUG(llvm::dbgs()
+ << "LNA: [visitBranchOperand] Non-forwarded branch "
+ "operand may be live due to live result: "
+ << result << "\n");
break;
}
}
@@ -149,6 +174,9 ...
[truncated]
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This pull request adds extensive LLVM_DEBUG logging to the DataFlow analysis, particularly for the RemoveDeadValues pass, LivenessAnalysis, and DeadCodeAnalysis, to ease debugging of these passes.
- Adds debug messages in RemoveDeadValues.cpp to trace liveness computations and dead value marking.
- Enhances LivenessAnalysis.cpp and DeadCodeAnalysis.cpp with detailed logging for operation visits, branch operand handling, and control-flow analysis.
Reviewed Changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| mlir/lib/Transforms/RemoveDeadValues.cpp | Introduces debug logging in liveness checks and value marking. |
| mlir/lib/Analysis/DataFlow/LivenessAnalysis.cpp | Adds LLVM_DEBUG messages to track operation processing and operand liveness. |
| mlir/lib/Analysis/DataFlow/DeadCodeAnalysis.cpp | Integrates detailed logging in control-flow analysis and state propagation. |
Comments suppressed due to low confidence (1)
mlir/lib/Analysis/DataFlow/DeadCodeAnalysis.cpp:263
- [nitpick] Including additional unique identifiers (such as region indices or operation IDs) in the debug logs could further improve traceability in deeply nested control flows.
<< op->getName() << "\n");
…(NFC) Debugging issue with this pass is quite difficult at the moment, this should help.
Debugging issues with this pass is quite difficult at the moment, this should help.