Skip to content

Commit ddc9892

Browse files
author
mlevesquedion
authored
Add operands to worklist when only used by deleted op (#86990)
I believe the existing check to determine if an operand should be added is incorrect: `operand.use_empty() || operand.hasOneUse()`. This is because these checks do not take into account the fact that the op is being deleted. It hasn't been deleted yet, so `operand.use_empty()` cannot be true, and `operand.hasOneUse()` may be true if the op being deleted is the only user of the operand and it only uses it once, but it will fail if the operand is used more than once (e.g. something like `add %0, %0`). Instead, check if the op being deleted is the only _user_ of the operand. If so, add the operand to the worklist. Fixes #86765
1 parent cc8c6b0 commit ddc9892

File tree

4 files changed

+92
-18
lines changed

4 files changed

+92
-18
lines changed

flang/test/Transforms/stack-arrays.fir

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -127,9 +127,7 @@ func.func @placement1() {
127127
return
128128
}
129129
// CHECK: func.func @placement1() {
130-
// CHECK-NEXT: %[[ONE:.*]] = arith.constant 1 : index
131-
// CHECK-NEXT: %[[TWO:.*]] = arith.constant 2 : index
132-
// CHECK-NEXT: %[[ARG:.*]] = arith.addi %[[ONE]], %[[TWO]] : index
130+
// CHECK-NEXT: %[[ARG:.*]] = arith.constant 3 : index
133131
// CHECK-NEXT: %[[MEM:.*]] = fir.alloca !fir.array<?xi32>, %[[ARG]]
134132
// CHECK-NEXT: return
135133
// CHECK-NEXT: }
@@ -204,13 +202,12 @@ func.func @placement4(%arg0 : i1) {
204202
// CHECK: func.func @placement4(%arg0: i1) {
205203
// CHECK-NEXT: %[[C1:.*]] = arith.constant 1 : index
206204
// CHECK-NEXT: %[[C1_I32:.*]] = fir.convert %[[C1]] : (index) -> i32
207-
// CHECK-NEXT: %[[C2:.*]] = arith.constant 2 : index
208205
// CHECK-NEXT: %[[C10:.*]] = arith.constant 10 : index
209206
// CHECK-NEXT: cf.br ^bb1
210207
// CHECK-NEXT: ^bb1:
211-
// CHECK-NEXT: %[[SUM:.*]] = arith.addi %[[C1]], %[[C2]] : index
208+
// CHECK-NEXT: %[[C3:.*]] = arith.constant 3 : index
212209
// CHECK-NEXT: %[[SP:.*]] = fir.call @llvm.stacksave.p0() : () -> !fir.ref<i8>
213-
// CHECK-NEXT: %[[MEM:.*]] = fir.alloca !fir.array<?xi32>, %[[SUM]]
210+
// CHECK-NEXT: %[[MEM:.*]] = fir.alloca !fir.array<?xi32>, %[[C3]]
214211
// CHECK-NEXT: fir.call @llvm.stackrestore.p0(%[[SP]]) : (!fir.ref<i8>) -> ()
215212
// CHECK-NEXT: cf.cond_br %arg0, ^bb1, ^bb2
216213
// CHECK-NEXT: ^bb2:

mlir/lib/Transforms/Utils/GreedyPatternRewriteDriver.cpp

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -377,7 +377,7 @@ class GreedyPatternRewriteDriver : public PatternRewriter,
377377
/// be re-added to the worklist. This function should be called when an
378378
/// operation is modified or removed, as it may trigger further
379379
/// simplifications.
380-
void addOperandsToWorklist(ValueRange operands);
380+
void addOperandsToWorklist(Operation *op);
381381

382382
/// Notify the driver that the given block was inserted.
383383
void notifyBlockInserted(Block *block, Region *previous,
@@ -688,17 +688,36 @@ void GreedyPatternRewriteDriver::notifyOperationModified(Operation *op) {
688688
addToWorklist(op);
689689
}
690690

691-
void GreedyPatternRewriteDriver::addOperandsToWorklist(ValueRange operands) {
692-
for (Value operand : operands) {
693-
// If the use count of this operand is now < 2, we re-add the defining
694-
// operation to the worklist.
695-
// TODO: This is based on the fact that zero use operations
696-
// may be deleted, and that single use values often have more
697-
// canonicalization opportunities.
698-
if (!operand || (!operand.use_empty() && !operand.hasOneUse()))
691+
void GreedyPatternRewriteDriver::addOperandsToWorklist(Operation *op) {
692+
for (Value operand : op->getOperands()) {
693+
// If this operand currently has at most 2 users, add its defining op to the
694+
// worklist. Indeed, after the op is deleted, then the operand will have at
695+
// most 1 user left. If it has 0 users left, it can be deleted too,
696+
// and if it has 1 user left, there may be further canonicalization
697+
// opportunities.
698+
if (!operand)
699699
continue;
700-
if (auto *defOp = operand.getDefiningOp())
701-
addToWorklist(defOp);
700+
701+
auto *defOp = operand.getDefiningOp();
702+
if (!defOp)
703+
continue;
704+
705+
Operation *otherUser = nullptr;
706+
bool hasMoreThanTwoUses = false;
707+
for (auto user : operand.getUsers()) {
708+
if (user == op || user == otherUser)
709+
continue;
710+
if (!otherUser) {
711+
otherUser = user;
712+
continue;
713+
}
714+
hasMoreThanTwoUses = true;
715+
break;
716+
}
717+
if (hasMoreThanTwoUses)
718+
continue;
719+
720+
addToWorklist(defOp);
702721
}
703722
}
704723

@@ -722,7 +741,7 @@ void GreedyPatternRewriteDriver::notifyOperationErased(Operation *op) {
722741
if (config.listener)
723742
config.listener->notifyOperationErased(op);
724743

725-
addOperandsToWorklist(op->getOperands());
744+
addOperandsToWorklist(op);
726745
worklist.remove(op);
727746

728747
if (config.strictMode != GreedyRewriteStrictness::AnyOp)
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// RUN: mlir-opt %s -test-patterns="max-iterations=1 top-down=true" \
2+
// RUN: --split-input-file | FileCheck %s
3+
4+
// Tests for https://github.com/llvm/llvm-project/issues/86765. Ensure
5+
// that operands of a dead op are added to the worklist even if the same value
6+
// appears multiple times as an operand.
7+
8+
// 2 uses of the same operand
9+
10+
// CHECK: func.func @f(%arg0: i1) {
11+
// CHECK-NEXT: return
12+
// CHECK-NEXT: }
13+
func.func @f(%arg0: i1) {
14+
%0 = arith.constant 0 : i32
15+
%if = scf.if %arg0 -> (i32) {
16+
scf.yield %0 : i32
17+
} else {
18+
scf.yield %0 : i32
19+
}
20+
%dead_leaf = arith.addi %if, %if : i32
21+
return
22+
}
23+
24+
// -----
25+
26+
// 3 uses of the same operand
27+
28+
// CHECK: func.func @f() {
29+
// CHECK-NEXT: return
30+
// CHECK-NEXT: }
31+
func.func @f() {
32+
%0 = arith.constant 0 : i1
33+
%if = scf.if %0 -> (i1) {
34+
scf.yield %0 : i1
35+
} else {
36+
scf.yield %0 : i1
37+
}
38+
%dead_leaf = arith.select %if, %if, %if : i1
39+
return
40+
}
41+
42+
// -----
43+
44+
// 2 uses of the same operand, op has 3 operands
45+
46+
// CHECK: func.func @f() {
47+
// CHECK-NEXT: return
48+
// CHECK-NEXT: }
49+
func.func @f() {
50+
%0 = arith.constant 0 : i1
51+
%if = scf.if %0 -> (i1) {
52+
scf.yield %0 : i1
53+
} else {
54+
scf.yield %0 : i1
55+
}
56+
%dead_leaf = arith.select %0, %if, %if : i1
57+
return
58+
}

0 commit comments

Comments
 (0)