Skip to content

Commit 437db73

Browse files
committed
Removing dead values for branches
1 parent 3cecf17 commit 437db73

File tree

2 files changed

+60
-18
lines changed

2 files changed

+60
-18
lines changed

mlir/lib/Transforms/RemoveDeadValues.cpp

Lines changed: 40 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -563,6 +563,44 @@ static void cleanRegionBranchOp(RegionBranchOpInterface regionBranchOp,
563563
dropUsesAndEraseResults(regionBranchOp.getOperation(), resultsToKeep.flip());
564564
}
565565

566+
// 1. Iterate over each successor block of the given BranchOpInterface
567+
// operation.
568+
// 2. For each successor block:
569+
// a. Retrieve the operands passed to the successor.
570+
// b. Use the provided liveness analysis (`RunLivenessAnalysis`) to determine
571+
// which
572+
// operands are live in the successor block.
573+
// c. Mark each operand as live or dead based on the analysis.
574+
// 3. Remove dead operands from the branch operation and arguments accordingly
575+
576+
static void cleanBranchOp(BranchOpInterface branchOp, RunLivenessAnalysis &la) {
577+
unsigned numSuccessors = branchOp->getNumSuccessors();
578+
579+
// Do (1)
580+
for (unsigned succIdx = 0; succIdx < numSuccessors; ++succIdx) {
581+
Block *successorBlock = branchOp->getSuccessor(succIdx);
582+
583+
// Do (2)
584+
SuccessorOperands successorOperands =
585+
branchOp.getSuccessorOperands(succIdx);
586+
SmallVector<Value> operandValues;
587+
for (unsigned operandIdx = 0; operandIdx < successorOperands.size();
588+
++operandIdx) {
589+
operandValues.push_back(successorOperands[operandIdx]);
590+
}
591+
592+
BitVector successorLiveOperands = markLives(operandValues, la);
593+
594+
// Do (3)
595+
for (int argIdx = successorLiveOperands.size() - 1; argIdx >= 0; --argIdx) {
596+
if (!successorLiveOperands[argIdx]) {
597+
successorOperands.erase(argIdx);
598+
successorBlock->eraseArgument(argIdx);
599+
}
600+
}
601+
}
602+
}
603+
566604
struct RemoveDeadValues : public impl::RemoveDeadValuesBase<RemoveDeadValues> {
567605
void runOnOperation() override;
568606
};
@@ -572,26 +610,13 @@ void RemoveDeadValues::runOnOperation() {
572610
auto &la = getAnalysis<RunLivenessAnalysis>();
573611
Operation *module = getOperation();
574612

575-
// The removal of non-live values is performed iff there are no branch ops,
576-
// and all symbol user ops present in the IR are call-like.
577-
WalkResult acceptableIR = module->walk([&](Operation *op) {
578-
if (op == module)
579-
return WalkResult::advance();
580-
if (isa<BranchOpInterface>(op)) {
581-
op->emitError() << "cannot optimize an IR with branch ops\n";
582-
return WalkResult::interrupt();
583-
}
584-
return WalkResult::advance();
585-
});
586-
587-
if (acceptableIR.wasInterrupted())
588-
return signalPassFailure();
589-
590613
module->walk([&](Operation *op) {
591614
if (auto funcOp = dyn_cast<FunctionOpInterface>(op)) {
592615
cleanFuncOp(funcOp, module, la);
593616
} else if (auto regionBranchOp = dyn_cast<RegionBranchOpInterface>(op)) {
594617
cleanRegionBranchOp(regionBranchOp, la);
618+
} else if (auto branchOp = dyn_cast<BranchOpInterface>(op)) {
619+
cleanBranchOp(branchOp, la);
595620
} else if (op->hasTrait<::mlir::OpTrait::IsTerminator>()) {
596621
// Nothing to do here because this is a terminator op and it should be
597622
// honored with respect to its parent

mlir/test/Transforms/remove-dead-values.mlir

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,32 @@ module @named_module_acceptable {
2828

2929
// -----
3030

31-
// The IR remains untouched because of the presence of a branch op `cf.cond_br`.
31+
// The IR is optimized regardless of the presence of a branch op `cf.cond_br`.
3232
//
33-
func.func @dont_touch_unacceptable_ir_has_cleanable_simple_op_with_branch_op(%arg0: i1) {
33+
func.func @acceptable_ir_has_cleanable_simple_op_with_branch_op(%arg0: i1) {
3434
%non_live = arith.constant 0 : i32
35-
// expected-error @+1 {{cannot optimize an IR with branch ops}}
35+
// CHECK-NOT: non_live
3636
cf.cond_br %arg0, ^bb1(%non_live : i32), ^bb2(%non_live : i32)
3737
^bb1(%non_live_0 : i32):
38+
// CHECK-NOT: non_live_0
3839
cf.br ^bb3
3940
^bb2(%non_live_1 : i32):
41+
// CHECK-NOT: non_live_1
42+
cf.br ^bb3
43+
^bb3:
44+
return
45+
}
46+
47+
// -----
48+
49+
// Arguments of unconditional branch op `cf.br` are properly removed.
50+
//
51+
func.func @acceptable_ir_has_cleanable_simple_op_with_unconditional_branch_op(%arg0: i1) {
52+
%non_live = arith.constant 0 : i32
53+
// CHECK-NOT: non_live
54+
cf.br ^bb1(%non_live : i32)
55+
^bb1(%non_live_1 : i32):
56+
// CHECK-NOT: non_live_1
4057
cf.br ^bb3
4158
^bb3:
4259
return

0 commit comments

Comments
 (0)