Skip to content

Commit 389aa46

Browse files
authored
[mlir][DataFlow] Add visitBlockTransfer hook to dense analyses (#166263)
Add a customizable `visitBlockTransfer` method to dense forward and backward dataflow analyses, allowing subclasses to customize lattice propagation behavior along control flow edges between blocks. Default implementation preserves existing join/meet semantics. This change mirrors the exiting structure of both dense dataflow classes, where `RegionBranchOpInterface` and callables are allowed to be customized by subclasses. The use case motivating this change is dense liveness analysis. Currently, without the customization hook the block transfer function produces incorrect results. The issue is the current logic doesn't remove the successor block arguments from the live set, as it only meets the successor state with the predecessor state (ie. set union). With this change is now possible to compute the correct result by specifying the correct logic in `visitBlockTransfer`. Signed-off-by: Fabian Mora <[email protected]>
1 parent 12f392c commit 389aa46

File tree

2 files changed

+75
-3
lines changed

2 files changed

+75
-3
lines changed

mlir/include/mlir/Analysis/DataFlow/DenseAnalysis.h

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,18 @@ class AbstractDenseForwardDataFlowAnalysis : public DataFlowAnalysis {
127127
/// them into the same equivalent class.
128128
virtual void buildOperationEquivalentLatticeAnchor(Operation *op) {}
129129

130+
/// Visit a block and propagate the dense lattice forward along the control
131+
/// flow edge from predecessor to block. `point` corresponds to the program
132+
/// point before `block`. The default implementation merges in the state from
133+
/// the predecessor's terminator.
134+
virtual void visitBlockTransfer(Block *block, ProgramPoint *point,
135+
Block *predecessor,
136+
const AbstractDenseLattice &before,
137+
AbstractDenseLattice *after) {
138+
// Merge in the state from the predecessor's terminator.
139+
join(after, before);
140+
}
141+
130142
/// Propagate the dense lattice forward along the control flow edge from
131143
/// `regionFrom` to `regionTo` regions of the `branch` operation. `nullopt`
132144
/// values correspond to control flow branches originating at or targeting the
@@ -259,6 +271,22 @@ class DenseForwardDataFlowAnalysis
259271
branch, regionFrom, regionTo, before, after);
260272
}
261273

274+
/// Hook for customizing the behavior of lattice propagation along the control
275+
/// flow edges between blocks. The control flows from `predecessor` to
276+
/// `block`. The lattice is propagated forward along this edge. The lattices
277+
/// are as follows:
278+
/// - `before` is the lattice at the end of the predecessor block;
279+
/// - `after` is the lattice at the beginning of the block.
280+
/// By default, the `after` state is simply joined with the `before` state.
281+
/// Concrete analyses can override this behavior or delegate to the parent
282+
/// call for the default behavior.
283+
virtual void visitBlockTransfer(Block *block, ProgramPoint *point,
284+
Block *predecessor, const LatticeT &before,
285+
LatticeT *after) {
286+
AbstractDenseForwardDataFlowAnalysis::visitBlockTransfer(
287+
block, point, predecessor, before, after);
288+
}
289+
262290
protected:
263291
/// Get the dense lattice on this lattice anchor.
264292
LatticeT *getLattice(LatticeAnchor anchor) override {
@@ -306,6 +334,13 @@ class DenseForwardDataFlowAnalysis
306334
static_cast<const LatticeT &>(before),
307335
static_cast<LatticeT *>(after));
308336
}
337+
void visitBlockTransfer(Block *block, ProgramPoint *point, Block *predecessor,
338+
const AbstractDenseLattice &before,
339+
AbstractDenseLattice *after) final {
340+
visitBlockTransfer(block, point, predecessor,
341+
static_cast<const LatticeT &>(before),
342+
static_cast<LatticeT *>(after));
343+
}
309344
};
310345

311346
//===----------------------------------------------------------------------===//
@@ -388,6 +423,17 @@ class AbstractDenseBackwardDataFlowAnalysis : public DataFlowAnalysis {
388423
/// them into the same equivalent class.
389424
virtual void buildOperationEquivalentLatticeAnchor(Operation *op) {}
390425

426+
/// Visit a block and propagate the dense lattice backward along the control
427+
/// flow edge from successor to block. `point` corresponds to the program
428+
/// point after `block`. The default implementation merges in the state from
429+
/// the successor's first operation or the block itself when empty.
430+
virtual void visitBlockTransfer(Block *block, ProgramPoint *point,
431+
Block *successor,
432+
const AbstractDenseLattice &after,
433+
AbstractDenseLattice *before) {
434+
meet(before, after);
435+
}
436+
391437
/// Propagate the dense lattice backwards along the control flow edge from
392438
/// `regionFrom` to `regionTo` regions of the `branch` operation. `nullopt`
393439
/// values correspond to control flow branches originating at or targeting the
@@ -531,6 +577,22 @@ class DenseBackwardDataFlowAnalysis
531577
branch, regionFrom, regionTo, after, before);
532578
}
533579

580+
/// Hook for customizing the behavior of lattice propagation along the control
581+
/// flow edges between blocks. The control flows from `successor` to
582+
/// `block`. The lattice is propagated back along this edge. The lattices
583+
/// are as follows:
584+
/// - `after` is the lattice at the beginning of the successor block;
585+
/// - `before` is the lattice at the end of the block.
586+
/// By default, the `before` state is simply met with the `after` state.
587+
/// Concrete analyses can override this behavior or delegate to the parent
588+
/// call for the default behavior.
589+
virtual void visitBlockTransfer(Block *block, ProgramPoint *point,
590+
Block *successor, const LatticeT &after,
591+
LatticeT *before) {
592+
AbstractDenseBackwardDataFlowAnalysis::visitBlockTransfer(
593+
block, point, successor, after, before);
594+
}
595+
534596
protected:
535597
/// Get the dense lattice at the given lattice anchor.
536598
LatticeT *getLattice(LatticeAnchor anchor) override {
@@ -577,6 +639,13 @@ class DenseBackwardDataFlowAnalysis
577639
static_cast<const LatticeT &>(after),
578640
static_cast<LatticeT *>(before));
579641
}
642+
void visitBlockTransfer(Block *block, ProgramPoint *point, Block *successor,
643+
const AbstractDenseLattice &after,
644+
AbstractDenseLattice *before) final {
645+
visitBlockTransfer(block, point, successor,
646+
static_cast<const LatticeT &>(after),
647+
static_cast<LatticeT *>(before));
648+
}
580649
};
581650

582651
} // end namespace dataflow

mlir/lib/Analysis/DataFlow/DenseAnalysis.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -266,9 +266,10 @@ void AbstractDenseForwardDataFlowAnalysis::visitBlock(Block *block) {
266266
}
267267

268268
LDBG() << " Joining state from predecessor " << predecessor;
269+
const AbstractDenseLattice &before = *getLatticeFor(
270+
point, getProgramPointAfter(predecessor->getTerminator()));
269271
// Merge in the state from the predecessor's terminator.
270-
join(after, *getLatticeFor(
271-
point, getProgramPointAfter(predecessor->getTerminator())));
272+
visitBlockTransfer(block, point, predecessor, before, after);
272273
}
273274
}
274275

@@ -614,7 +615,9 @@ void AbstractDenseBackwardDataFlowAnalysis::visitBlock(Block *block) {
614615
LDBG() << " Meeting state from successor " << successor;
615616
// Merge in the state from the successor: either the first operation, or the
616617
// block itself when empty.
617-
meet(before, *getLatticeFor(point, getProgramPointBefore(successor)));
618+
visitBlockTransfer(block, point, successor,
619+
*getLatticeFor(point, getProgramPointBefore(successor)),
620+
before);
618621
}
619622
}
620623

0 commit comments

Comments
 (0)