Skip to content

Commit 2073782

Browse files
[BDCE] Handle multi-use binary ops upon demanded bits
Simplify multi-use `and`/`or`/`xor` when these last do not affect the demanded bits being considered. Fixes: #78596. Proofs: https://alive2.llvm.org/ce/z/EjuWHa.
1 parent 0a15ead commit 2073782

File tree

3 files changed

+53
-35
lines changed

3 files changed

+53
-35
lines changed

llvm/lib/Transforms/Scalar/BDCE.cpp

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,13 @@
2323
#include "llvm/IR/IRBuilder.h"
2424
#include "llvm/IR/InstIterator.h"
2525
#include "llvm/IR/Instructions.h"
26+
#include "llvm/IR/PatternMatch.h"
2627
#include "llvm/Support/Debug.h"
2728
#include "llvm/Support/raw_ostream.h"
2829
#include "llvm/Transforms/Utils/Local.h"
30+
2931
using namespace llvm;
32+
using namespace PatternMatch;
3033

3134
#define DEBUG_TYPE "bdce"
3235

@@ -125,6 +128,38 @@ static bool bitTrackingDCE(Function &F, DemandedBits &DB) {
125128
}
126129
}
127130

131+
// Simplify and, or, xor when their mask does not affect the demanded bits.
132+
if (auto *BO = dyn_cast<BinaryOperator>(&I)) {
133+
APInt Demanded = DB.getDemandedBits(BO);
134+
if (!Demanded.isAllOnes()) {
135+
const APInt *Mask;
136+
if (match(BO->getOperand(1), m_APInt(Mask))) {
137+
bool CanBeSimplified = false;
138+
switch (BO->getOpcode()) {
139+
case Instruction::Or:
140+
case Instruction::Xor:
141+
CanBeSimplified = !Demanded.intersects(*Mask);
142+
break;
143+
case Instruction::And:
144+
CanBeSimplified = Demanded.isSubsetOf(*Mask);
145+
break;
146+
default:
147+
// TODO: Handle more cases here.
148+
break;
149+
}
150+
151+
if (CanBeSimplified) {
152+
clearAssumptionsOfUsers(BO, DB);
153+
BO->replaceAllUsesWith(BO->getOperand(0));
154+
Worklist.push_back(BO);
155+
++NumSimplified;
156+
Changed = true;
157+
continue;
158+
}
159+
}
160+
}
161+
}
162+
128163
for (Use &U : I.operands()) {
129164
// DemandedBits only detects dead integer uses.
130165
if (!U->getType()->isIntOrIntVectorTy())

llvm/test/Transforms/BDCE/binops-multiuse.ll

Lines changed: 14 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,8 @@ define void @or(i64 %a) {
55
; CHECK-LABEL: define void @or(
66
; CHECK-SAME: i64 [[A:%.*]]) {
77
; CHECK-NEXT: entry:
8-
; CHECK-NEXT: [[OR:%.*]] = or i64 [[A]], 3
9-
; CHECK-NEXT: [[RET1:%.*]] = and i64 [[OR]], 8
10-
; CHECK-NEXT: [[RET2:%.*]] = and i64 [[OR]], 16
8+
; CHECK-NEXT: [[RET1:%.*]] = and i64 [[A]], 8
9+
; CHECK-NEXT: [[RET2:%.*]] = and i64 [[A]], 16
1110
; CHECK-NEXT: call void @use(i64 [[RET1]])
1211
; CHECK-NEXT: call void @use(i64 [[RET2]])
1312
; CHECK-NEXT: ret void
@@ -25,9 +24,8 @@ define void @xor(i64 %a) {
2524
; CHECK-LABEL: define void @xor(
2625
; CHECK-SAME: i64 [[A:%.*]]) {
2726
; CHECK-NEXT: entry:
28-
; CHECK-NEXT: [[XOR:%.*]] = xor i64 [[A]], 3
29-
; CHECK-NEXT: [[RET1:%.*]] = and i64 [[XOR]], 8
30-
; CHECK-NEXT: [[RET2:%.*]] = and i64 [[XOR]], 16
27+
; CHECK-NEXT: [[RET1:%.*]] = and i64 [[A]], 8
28+
; CHECK-NEXT: [[RET2:%.*]] = and i64 [[A]], 16
3129
; CHECK-NEXT: call void @use(i64 [[RET1]])
3230
; CHECK-NEXT: call void @use(i64 [[RET2]])
3331
; CHECK-NEXT: ret void
@@ -45,9 +43,8 @@ define void @and(i64 %a) {
4543
; CHECK-LABEL: define void @and(
4644
; CHECK-SAME: i64 [[A:%.*]]) {
4745
; CHECK-NEXT: entry:
48-
; CHECK-NEXT: [[AND:%.*]] = and i64 [[A]], 24
49-
; CHECK-NEXT: [[RET1:%.*]] = and i64 [[AND]], 8
50-
; CHECK-NEXT: [[RET2:%.*]] = and i64 [[AND]], 16
46+
; CHECK-NEXT: [[RET1:%.*]] = and i64 [[A]], 8
47+
; CHECK-NEXT: [[RET2:%.*]] = and i64 [[A]], 16
5148
; CHECK-NEXT: call void @use(i64 [[RET1]])
5249
; CHECK-NEXT: call void @use(i64 [[RET2]])
5350
; CHECK-NEXT: ret void
@@ -65,9 +62,7 @@ define void @or_of_and(i64 %a, i64 %b) {
6562
; CHECK-LABEL: define void @or_of_and(
6663
; CHECK-SAME: i64 [[A:%.*]], i64 [[B:%.*]]) {
6764
; CHECK-NEXT: entry:
68-
; CHECK-NEXT: [[AND1:%.*]] = and i64 [[A]], 24
69-
; CHECK-NEXT: [[AND2:%.*]] = and i64 [[B]], 25
70-
; CHECK-NEXT: [[OR:%.*]] = or i64 [[AND1]], [[AND2]]
65+
; CHECK-NEXT: [[OR:%.*]] = or i64 [[A]], [[B]]
7166
; CHECK-NEXT: [[RET1:%.*]] = and i64 [[OR]], 8
7267
; CHECK-NEXT: [[RET2:%.*]] = and i64 [[OR]], 16
7368
; CHECK-NEXT: call void @use(i64 [[RET1]])
@@ -89,9 +84,7 @@ define void @or_disjoint_of_and(i64 %a, i64 %b) {
8984
; CHECK-LABEL: define void @or_disjoint_of_and(
9085
; CHECK-SAME: i64 [[A:%.*]], i64 [[B:%.*]]) {
9186
; CHECK-NEXT: entry:
92-
; CHECK-NEXT: [[AND1:%.*]] = and i64 [[A]], 56
93-
; CHECK-NEXT: [[AND2:%.*]] = and i64 [[B]], 25
94-
; CHECK-NEXT: [[OR:%.*]] = or disjoint i64 [[AND1]], [[AND2]]
87+
; CHECK-NEXT: [[OR:%.*]] = or i64 [[A]], [[B]]
9588
; CHECK-NEXT: [[RET1:%.*]] = and i64 [[OR]], 8
9689
; CHECK-NEXT: [[RET2:%.*]] = and i64 [[OR]], 16
9790
; CHECK-NEXT: call void @use(i64 [[RET1]])
@@ -113,9 +106,7 @@ define void @select_of_and(i1 %c, i64 %a, i64 %b) {
113106
; CHECK-LABEL: define void @select_of_and(
114107
; CHECK-SAME: i1 [[C:%.*]], i64 [[A:%.*]], i64 [[B:%.*]]) {
115108
; CHECK-NEXT: entry:
116-
; CHECK-NEXT: [[AND1:%.*]] = and i64 [[A]], 24
117-
; CHECK-NEXT: [[AND2:%.*]] = and i64 [[B]], 25
118-
; CHECK-NEXT: [[S:%.*]] = select i1 [[C]], i64 [[AND1]], i64 [[AND2]]
109+
; CHECK-NEXT: [[S:%.*]] = select i1 [[C]], i64 [[A]], i64 [[B]]
119110
; CHECK-NEXT: [[RET1:%.*]] = and i64 [[S]], 8
120111
; CHECK-NEXT: [[RET2:%.*]] = and i64 [[S]], 16
121112
; CHECK-NEXT: call void @use(i64 [[RET1]])
@@ -137,9 +128,8 @@ define void @select_of_and_2(i1 %c, i64 %a, i64 %b) {
137128
; CHECK-LABEL: define void @select_of_and_2(
138129
; CHECK-SAME: i1 [[C:%.*]], i64 [[A:%.*]], i64 [[B:%.*]]) {
139130
; CHECK-NEXT: entry:
140-
; CHECK-NEXT: [[AND1:%.*]] = and i64 [[A]], 25
141131
; CHECK-NEXT: [[AND2:%.*]] = and i64 [[B]], 23
142-
; CHECK-NEXT: [[S:%.*]] = select i1 [[C]], i64 [[AND1]], i64 [[AND2]]
132+
; CHECK-NEXT: [[S:%.*]] = select i1 [[C]], i64 [[A]], i64 [[AND2]]
143133
; CHECK-NEXT: [[RET1:%.*]] = and i64 [[S]], 8
144134
; CHECK-NEXT: [[RET2:%.*]] = and i64 [[S]], 16
145135
; CHECK-NEXT: call void @use(i64 [[RET1]])
@@ -161,9 +151,8 @@ define void @select_of_and_multiuse(i1 %c, i64 %a, i64 %b) {
161151
; CHECK-LABEL: define void @select_of_and_multiuse(
162152
; CHECK-SAME: i1 [[C:%.*]], i64 [[A:%.*]], i64 [[B:%.*]]) {
163153
; CHECK-NEXT: entry:
164-
; CHECK-NEXT: [[AND1:%.*]] = and i64 [[A]], 24
165154
; CHECK-NEXT: [[AND2:%.*]] = and i64 [[B]], 25
166-
; CHECK-NEXT: [[S:%.*]] = select i1 [[C]], i64 [[AND1]], i64 [[AND2]]
155+
; CHECK-NEXT: [[S:%.*]] = select i1 [[C]], i64 [[A]], i64 [[AND2]]
167156
; CHECK-NEXT: [[RET1:%.*]] = and i64 [[S]], 8
168157
; CHECK-NEXT: [[RET2:%.*]] = and i64 [[S]], 16
169158
; CHECK-NEXT: call void @use(i64 [[RET1]])
@@ -209,9 +198,7 @@ define void @select_of_or(i1 %c, i64 %a, i64 %b) {
209198
; CHECK-LABEL: define void @select_of_or(
210199
; CHECK-SAME: i1 [[C:%.*]], i64 [[A:%.*]], i64 [[B:%.*]]) {
211200
; CHECK-NEXT: entry:
212-
; CHECK-NEXT: [[OR1:%.*]] = or i64 [[A]], 3
213-
; CHECK-NEXT: [[OR2:%.*]] = or i64 [[B]], 192
214-
; CHECK-NEXT: [[S:%.*]] = select i1 [[C]], i64 [[OR1]], i64 [[OR2]]
201+
; CHECK-NEXT: [[S:%.*]] = select i1 [[C]], i64 [[A]], i64 [[B]]
215202
; CHECK-NEXT: [[RET1:%.*]] = and i64 [[S]], 8
216203
; CHECK-NEXT: [[RET2:%.*]] = and i64 [[S]], 16
217204
; CHECK-NEXT: call void @use(i64 [[RET1]])
@@ -233,9 +220,7 @@ define void @select_of_xor(i1 %c, i64 %a, i64 %b) {
233220
; CHECK-LABEL: define void @select_of_xor(
234221
; CHECK-SAME: i1 [[C:%.*]], i64 [[A:%.*]], i64 [[B:%.*]]) {
235222
; CHECK-NEXT: entry:
236-
; CHECK-NEXT: [[XOR1:%.*]] = xor i64 [[A]], 128
237-
; CHECK-NEXT: [[XOR2:%.*]] = xor i64 [[B]], 36
238-
; CHECK-NEXT: [[S:%.*]] = select i1 [[C]], i64 [[XOR1]], i64 [[XOR2]]
223+
; CHECK-NEXT: [[S:%.*]] = select i1 [[C]], i64 [[A]], i64 [[B]]
239224
; CHECK-NEXT: [[RET1:%.*]] = and i64 [[S]], 8
240225
; CHECK-NEXT: [[RET2:%.*]] = and i64 [[S]], 16
241226
; CHECK-NEXT: call void @use(i64 [[RET1]])
@@ -257,9 +242,7 @@ define void @select_vectorized(i1 %c, <2 x i8> %a, <2 x i8> %b) {
257242
; CHECK-LABEL: define void @select_vectorized(
258243
; CHECK-SAME: i1 [[C:%.*]], <2 x i8> [[A:%.*]], <2 x i8> [[B:%.*]]) {
259244
; CHECK-NEXT: entry:
260-
; CHECK-NEXT: [[AND1:%.*]] = and <2 x i8> [[A]], <i8 28, i8 28>
261-
; CHECK-NEXT: [[AND2:%.*]] = and <2 x i8> [[B]], <i8 29, i8 29>
262-
; CHECK-NEXT: [[S:%.*]] = select i1 [[C]], <2 x i8> [[AND1]], <2 x i8> [[AND2]]
245+
; CHECK-NEXT: [[S:%.*]] = select i1 [[C]], <2 x i8> [[A]], <2 x i8> [[B]]
263246
; CHECK-NEXT: [[RET1:%.*]] = and <2 x i8> [[S]], <i8 4, i8 4>
264247
; CHECK-NEXT: [[RET2:%.*]] = and <2 x i8> [[S]], <i8 12, i8 12>
265248
; CHECK-NEXT: call void @use3(<2 x i8> [[RET1]])

llvm/test/Transforms/BDCE/dead-uses.ll

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,14 @@ declare <2 x i32> @llvm.fshr.v2i32(<2 x i32>, <2 x i32>, <2 x i32>)
99
; First fshr operand is dead.
1010
define i32 @pr39771_fshr_multi_use_instr(i32 %a) {
1111
; CHECK-LABEL: @pr39771_fshr_multi_use_instr(
12-
; CHECK-NEXT: [[X:%.*]] = or i32 [[A:%.*]], 0
12+
; CHECK-NEXT: [[X:%.*]] = or i32 [[A:%.*]], 2
1313
; CHECK-NEXT: [[B:%.*]] = tail call i32 @llvm.fshr.i32(i32 0, i32 [[X]], i32 1)
1414
; CHECK-NEXT: [[C:%.*]] = lshr i32 [[B]], 23
1515
; CHECK-NEXT: [[D:%.*]] = xor i32 [[C]], [[B]]
1616
; CHECK-NEXT: [[E:%.*]] = and i32 [[D]], 31
1717
; CHECK-NEXT: ret i32 [[E]]
1818
;
19-
%x = or i32 %a, 0
19+
%x = or i32 %a, 2
2020
%b = tail call i32 @llvm.fshr.i32(i32 %x, i32 %x, i32 1)
2121
%c = lshr i32 %b, 23
2222
%d = xor i32 %c, %b
@@ -27,14 +27,14 @@ define i32 @pr39771_fshr_multi_use_instr(i32 %a) {
2727
; First fshr operand is dead (vector variant).
2828
define <2 x i32> @pr39771_fshr_multi_use_instr_vec(<2 x i32> %a) {
2929
; CHECK-LABEL: @pr39771_fshr_multi_use_instr_vec(
30-
; CHECK-NEXT: [[X:%.*]] = or <2 x i32> [[A:%.*]], zeroinitializer
30+
; CHECK-NEXT: [[X:%.*]] = or <2 x i32> [[A:%.*]], <i32 2, i32 2>
3131
; CHECK-NEXT: [[B:%.*]] = tail call <2 x i32> @llvm.fshr.v2i32(<2 x i32> zeroinitializer, <2 x i32> [[X]], <2 x i32> <i32 1, i32 1>)
3232
; CHECK-NEXT: [[C:%.*]] = lshr <2 x i32> [[B]], <i32 23, i32 23>
3333
; CHECK-NEXT: [[D:%.*]] = xor <2 x i32> [[C]], [[B]]
3434
; CHECK-NEXT: [[E:%.*]] = and <2 x i32> [[D]], <i32 31, i32 31>
3535
; CHECK-NEXT: ret <2 x i32> [[E]]
3636
;
37-
%x = or <2 x i32> %a, zeroinitializer
37+
%x = or <2 x i32> %a, <i32 2, i32 2>
3838
%b = tail call <2 x i32> @llvm.fshr.v2i32(<2 x i32> %x, <2 x i32> %x, <2 x i32> <i32 1, i32 1>)
3939
%c = lshr <2 x i32> %b, <i32 23, i32 23>
4040
%d = xor <2 x i32> %c, %b

0 commit comments

Comments
 (0)