[llvm] [BDCE] Handle multi-use `and`/`or` on demanded bits (PR #79688)
Antonio Frighetto via llvm-commits
llvm-commits at lists.llvm.org
Sat Jan 27 10:58:54 PST 2024
https://github.com/antoniofrighetto updated https://github.com/llvm/llvm-project/pull/79688
>From 0f7599247e1d02be12ad1b61ffa51e4961594a50 Mon Sep 17 00:00:00 2001
From: Antonio Frighetto <me at antoniofrighetto.com>
Date: Sat, 27 Jan 2024 19:19:51 +0100
Subject: [PATCH 1/2] [BDCE] Introduce test for PR79688 (NFC)
---
llvm/test/Transforms/BDCE/and-or-multiuse.ll | 214 +++++++++++++++++++
1 file changed, 214 insertions(+)
create mode 100644 llvm/test/Transforms/BDCE/and-or-multiuse.ll
diff --git a/llvm/test/Transforms/BDCE/and-or-multiuse.ll b/llvm/test/Transforms/BDCE/and-or-multiuse.ll
new file mode 100644
index 000000000000000..703ecbd3abcbb47
--- /dev/null
+++ b/llvm/test/Transforms/BDCE/and-or-multiuse.ll
@@ -0,0 +1,214 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 4
+; RUN: opt -S -passes=bdce < %s | FileCheck %s
+
+define void @or(i64 %a) {
+; CHECK-LABEL: define void @or(
+; CHECK-SAME: i64 [[A:%.*]]) {
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[OR:%.*]] = or i64 [[A]], 3
+; CHECK-NEXT: [[RET1:%.*]] = and i64 [[OR]], 8
+; CHECK-NEXT: [[RET2:%.*]] = and i64 [[OR]], 16
+; CHECK-NEXT: call void @use(i64 [[RET1]])
+; CHECK-NEXT: call void @use(i64 [[RET2]])
+; CHECK-NEXT: ret void
+;
+entry:
+ %or = or i64 %a, 3 ; Mask: 0000 0011
+ %ret1 = and i64 %or, 8 ; Demanded bits: 0000 1000
+ %ret2 = and i64 %or, 16 ; Demanded bits: 0001 0000
+ call void @use(i64 %ret1)
+ call void @use(i64 %ret2)
+ ret void
+}
+
+define void @and(i64 %a) {
+; CHECK-LABEL: define void @and(
+; CHECK-SAME: i64 [[A:%.*]]) {
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[AND:%.*]] = and i64 [[A]], 24
+; CHECK-NEXT: [[RET1:%.*]] = and i64 [[AND]], 8
+; CHECK-NEXT: [[RET2:%.*]] = and i64 [[AND]], 16
+; CHECK-NEXT: call void @use(i64 [[RET1]])
+; CHECK-NEXT: call void @use(i64 [[RET2]])
+; CHECK-NEXT: ret void
+;
+entry:
+ %and = and i64 %a, 24 ; Mask: 0001 1000
+ %ret1 = and i64 %and, 8 ; Demanded bits: 0000 1000
+ %ret2 = and i64 %and, 16 ; Demanded bits: 0001 0000
+ call void @use(i64 %ret1)
+ call void @use(i64 %ret2)
+ ret void
+}
+
+define void @or_of_and(i64 %a, i64 %b) {
+; CHECK-LABEL: define void @or_of_and(
+; CHECK-SAME: i64 [[A:%.*]], i64 [[B:%.*]]) {
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[AND1:%.*]] = and i64 [[A]], 24
+; CHECK-NEXT: [[AND2:%.*]] = and i64 [[B]], 25
+; CHECK-NEXT: [[OR:%.*]] = or i64 [[AND1]], [[AND2]]
+; CHECK-NEXT: [[RET1:%.*]] = and i64 [[OR]], 8
+; CHECK-NEXT: [[RET2:%.*]] = and i64 [[OR]], 16
+; CHECK-NEXT: call void @use(i64 [[RET1]])
+; CHECK-NEXT: call void @use(i64 [[RET2]])
+; CHECK-NEXT: ret void
+;
+entry:
+ %and1 = and i64 %a, 24 ; Mask: 0001 1000
+ %and2 = and i64 %b, 25 ; Mask: 0001 1001
+ %or = or i64 %and1, %and2
+ %ret1 = and i64 %or, 8 ; Demanded bits: 0000 1000
+ %ret2 = and i64 %or, 16 ; Demanded bits: 0001 0000
+ call void @use(i64 %ret1)
+ call void @use(i64 %ret2)
+ ret void
+}
+
+define void @select_of_and(i1 %c, i64 %a, i64 %b) {
+; CHECK-LABEL: define void @select_of_and(
+; CHECK-SAME: i1 [[C:%.*]], i64 [[A:%.*]], i64 [[B:%.*]]) {
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[AND1:%.*]] = and i64 [[A]], 24
+; CHECK-NEXT: [[AND2:%.*]] = and i64 [[B]], 25
+; CHECK-NEXT: [[S:%.*]] = select i1 [[C]], i64 [[AND1]], i64 [[AND2]]
+; CHECK-NEXT: [[RET1:%.*]] = and i64 [[S]], 8
+; CHECK-NEXT: [[RET2:%.*]] = and i64 [[S]], 16
+; CHECK-NEXT: call void @use(i64 [[RET1]])
+; CHECK-NEXT: call void @use(i64 [[RET2]])
+; CHECK-NEXT: ret void
+;
+entry:
+ %and1 = and i64 %a, 24 ; Mask: 0001 1000
+ %and2 = and i64 %b, 25 ; Mask: 0001 1001
+ %s = select i1 %c, i64 %and1, i64 %and2
+ %ret1 = and i64 %s, 8 ; Demanded bits: 0000 1000
+ %ret2 = and i64 %s, 16 ; Demanded bits: 0001 0000
+ call void @use(i64 %ret1)
+ call void @use(i64 %ret2)
+ ret void
+}
+
+define void @select_of_and_2(i1 %c, i64 %a, i64 %b) {
+; CHECK-LABEL: define void @select_of_and_2(
+; CHECK-SAME: i1 [[C:%.*]], i64 [[A:%.*]], i64 [[B:%.*]]) {
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[AND1:%.*]] = and i64 [[A]], 25
+; CHECK-NEXT: [[AND2:%.*]] = and i64 [[B]], 23
+; CHECK-NEXT: [[S:%.*]] = select i1 [[C]], i64 [[AND1]], i64 [[AND2]]
+; CHECK-NEXT: [[RET1:%.*]] = and i64 [[S]], 8
+; CHECK-NEXT: [[RET2:%.*]] = and i64 [[S]], 16
+; CHECK-NEXT: call void @use(i64 [[RET1]])
+; CHECK-NEXT: call void @use(i64 [[RET2]])
+; CHECK-NEXT: ret void
+;
+entry:
+ %and1 = and i64 %a, 25 ; Mask: 0001 1001
+ %and2 = and i64 %b, 23 ; Mask: 0001 0111
+ %s = select i1 %c, i64 %and1, i64 %and2
+ %ret1 = and i64 %s, 8 ; Demanded bits: 0000 1000
+ %ret2 = and i64 %s, 16 ; Demanded bits: 0001 0000
+ call void @use(i64 %ret1)
+ call void @use(i64 %ret2)
+ ret void
+}
+
+define void @select_of_and_multiuse(i1 %c, i64 %a, i64 %b) {
+; CHECK-LABEL: define void @select_of_and_multiuse(
+; CHECK-SAME: i1 [[C:%.*]], i64 [[A:%.*]], i64 [[B:%.*]]) {
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[AND1:%.*]] = and i64 [[A]], 24
+; CHECK-NEXT: [[AND2:%.*]] = and i64 [[B]], 25
+; CHECK-NEXT: [[S:%.*]] = select i1 [[C]], i64 [[AND1]], i64 [[AND2]]
+; CHECK-NEXT: [[RET1:%.*]] = and i64 [[S]], 8
+; CHECK-NEXT: [[RET2:%.*]] = and i64 [[S]], 16
+; CHECK-NEXT: call void @use(i64 [[RET1]])
+; CHECK-NEXT: call void @use2(i64 [[RET2]], i64 [[AND2]])
+; CHECK-NEXT: ret void
+;
+entry:
+ %and1 = and i64 %a, 24 ; Mask: 0001 1000
+ %and2 = and i64 %b, 25 ; Mask: 0001 1001
+ %s = select i1 %c, i64 %and1, i64 %and2
+ %ret1 = and i64 %s, 8 ; Demanded bits: 0000 1000
+ %ret2 = and i64 %s, 16 ; Demanded bits: 0001 0000
+ call void @use(i64 %ret1)
+ call void @use2(i64 %ret2, i64 %and2)
+ ret void
+}
+
+define void @select_of_and_different_demanded(i1 %c, i64 %a, i64 %b) {
+; CHECK-LABEL: define void @select_of_and_different_demanded(
+; CHECK-SAME: i1 [[C:%.*]], i64 [[A:%.*]], i64 [[B:%.*]]) {
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[AND1:%.*]] = and i64 0, 24
+; CHECK-NEXT: [[AND2:%.*]] = and i64 [[B]], 25
+; CHECK-NEXT: [[S:%.*]] = select i1 [[C]], i64 [[AND1]], i64 [[AND2]]
+; CHECK-NEXT: [[RET1:%.*]] = and i64 [[S]], 3
+; CHECK-NEXT: [[RET2:%.*]] = and i64 [[S]], 7
+; CHECK-NEXT: call void @use(i64 [[RET1]])
+; CHECK-NEXT: call void @use(i64 [[RET2]])
+; CHECK-NEXT: ret void
+;
+entry:
+ %and1 = and i64 %a, 24 ; Mask: 0001 1000
+ %and2 = and i64 %b, 25 ; Mask: 0001 1001
+ %s = select i1 %c, i64 %and1, i64 %and2
+ %ret1 = and i64 %s, 3 ; Demanded bits: 0000 0011
+ %ret2 = and i64 %s, 7 ; Demanded bits: 0000 0111
+ call void @use(i64 %ret1)
+ call void @use(i64 %ret2)
+ ret void
+}
+
+define void @select_of_or(i1 %c, i64 %a, i64 %b) {
+; CHECK-LABEL: define void @select_of_or(
+; CHECK-SAME: i1 [[C:%.*]], i64 [[A:%.*]], i64 [[B:%.*]]) {
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[OR1:%.*]] = or i64 [[A]], 3
+; CHECK-NEXT: [[OR2:%.*]] = or i64 [[B]], 192
+; CHECK-NEXT: [[S:%.*]] = select i1 [[C]], i64 [[OR1]], i64 [[OR2]]
+; CHECK-NEXT: [[RET1:%.*]] = and i64 [[S]], 8
+; CHECK-NEXT: [[RET2:%.*]] = and i64 [[S]], 16
+; CHECK-NEXT: call void @use(i64 [[RET1]])
+; CHECK-NEXT: call void @use(i64 [[RET2]])
+; CHECK-NEXT: ret void
+;
+entry:
+ %or1 = or i64 %a, 3 ; Mask: 0000 0011
+ %or2 = or i64 %b, 192 ; Mask: 1100 0000
+ %s = select i1 %c, i64 %or1, i64 %or2
+ %ret1 = and i64 %s, 8 ; Demanded bits: 0000 1000
+ %ret2 = and i64 %s, 16 ; Demanded bits: 0001 0000
+ call void @use(i64 %ret1)
+ call void @use(i64 %ret2)
+ ret void
+}
+
+define void @select_vectorized(i1 %c, <2 x i8> %a, <2 x i8> %b) {
+; CHECK-LABEL: define void @select_vectorized(
+; CHECK-SAME: i1 [[C:%.*]], <2 x i8> [[A:%.*]], <2 x i8> [[B:%.*]]) {
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[AND1:%.*]] = and <2 x i8> [[A]], <i8 28, i8 28>
+; CHECK-NEXT: [[AND2:%.*]] = and <2 x i8> [[B]], <i8 29, i8 29>
+; CHECK-NEXT: [[S:%.*]] = select i1 [[C]], <2 x i8> [[AND1]], <2 x i8> [[AND2]]
+; CHECK-NEXT: [[RET1:%.*]] = and <2 x i8> [[S]], <i8 4, i8 4>
+; CHECK-NEXT: [[RET2:%.*]] = and <2 x i8> [[S]], <i8 12, i8 12>
+; CHECK-NEXT: call void @use3(<2 x i8> [[RET1]])
+; CHECK-NEXT: call void @use3(<2 x i8> [[RET2]])
+; CHECK-NEXT: ret void
+;
+entry:
+ %and1 = and <2 x i8> %a, <i8 28, i8 28>
+ %and2 = and <2 x i8> %b, <i8 29, i8 29>
+ %s = select i1 %c, <2 x i8> %and1, <2 x i8> %and2
+ %ret1 = and <2 x i8> %s, <i8 4, i8 4>
+ %ret2 = and <2 x i8> %s, <i8 12, i8 12>
+ call void @use3(<2 x i8> %ret1)
+ call void @use3(<2 x i8> %ret2)
+ ret void
+}
+
+declare void @use(i64)
+declare void @use2(i64, i64)
+declare void @use3(<2 x i8>)
>From 524bf498257ed1dadad90e5aa08f90b9a6f73d69 Mon Sep 17 00:00:00 2001
From: Antonio Frighetto <me at antoniofrighetto.com>
Date: Sat, 27 Jan 2024 19:24:55 +0100
Subject: [PATCH 2/2] [BDCE] Handle multi-use `and`/`or` on demanded bits
Simplify multi-use `and` or `or`s when these last
do not affect the demanded bits being considered.
Fixes: https://github.com/llvm/llvm-project/issues/78596.
Proofs: https://alive2.llvm.org/ce/z/kmVqE-.
---
llvm/lib/Transforms/Scalar/BDCE.cpp | 22 +++++++++++++++
llvm/test/Transforms/BDCE/and-or-multiuse.ll | 28 +++++++-------------
llvm/test/Transforms/BDCE/dead-uses.ll | 3 +--
3 files changed, 32 insertions(+), 21 deletions(-)
diff --git a/llvm/lib/Transforms/Scalar/BDCE.cpp b/llvm/lib/Transforms/Scalar/BDCE.cpp
index 1fa2c75b0f42ac1..0e2c724d6771101 100644
--- a/llvm/lib/Transforms/Scalar/BDCE.cpp
+++ b/llvm/lib/Transforms/Scalar/BDCE.cpp
@@ -125,6 +125,28 @@ static bool bitTrackingDCE(Function &F, DemandedBits &DB) {
}
}
+ // Simplify `and` or `or` when their mask does not affect the demanded bits.
+ if (auto *BO = dyn_cast<BinaryOperator>(&I)) {
+ unsigned Opc = BO->getOpcode();
+ if (Opc == Instruction::And || Opc == Instruction::Or) {
+ APInt Demanded = DB.getDemandedBits(BO);
+ if (!Demanded.isAllOnes()) {
+ if (auto *CV = dyn_cast<ConstantInt>(BO->getOperand(1))) {
+ APInt Mask = CV->getValue();
+ if ((Opc == Instruction::And && Demanded.isSubsetOf(Mask)) ||
+ (Opc == Instruction::Or && !Demanded.intersects(Mask))) {
+ clearAssumptionsOfUsers(BO, DB);
+ BO->replaceAllUsesWith(BO->getOperand(0));
+ Worklist.push_back(BO);
+ ++NumSimplified;
+ Changed = true;
+ continue;
+ }
+ }
+ }
+ }
+ }
+
for (Use &U : I.operands()) {
// DemandedBits only detects dead integer uses.
if (!U->getType()->isIntOrIntVectorTy())
diff --git a/llvm/test/Transforms/BDCE/and-or-multiuse.ll b/llvm/test/Transforms/BDCE/and-or-multiuse.ll
index 703ecbd3abcbb47..9c8cb7b9e9a947a 100644
--- a/llvm/test/Transforms/BDCE/and-or-multiuse.ll
+++ b/llvm/test/Transforms/BDCE/and-or-multiuse.ll
@@ -5,9 +5,8 @@ define void @or(i64 %a) {
; CHECK-LABEL: define void @or(
; CHECK-SAME: i64 [[A:%.*]]) {
; CHECK-NEXT: entry:
-; CHECK-NEXT: [[OR:%.*]] = or i64 [[A]], 3
-; CHECK-NEXT: [[RET1:%.*]] = and i64 [[OR]], 8
-; CHECK-NEXT: [[RET2:%.*]] = and i64 [[OR]], 16
+; CHECK-NEXT: [[RET1:%.*]] = and i64 [[A]], 8
+; CHECK-NEXT: [[RET2:%.*]] = and i64 [[A]], 16
; CHECK-NEXT: call void @use(i64 [[RET1]])
; CHECK-NEXT: call void @use(i64 [[RET2]])
; CHECK-NEXT: ret void
@@ -25,9 +24,8 @@ define void @and(i64 %a) {
; CHECK-LABEL: define void @and(
; CHECK-SAME: i64 [[A:%.*]]) {
; CHECK-NEXT: entry:
-; CHECK-NEXT: [[AND:%.*]] = and i64 [[A]], 24
-; CHECK-NEXT: [[RET1:%.*]] = and i64 [[AND]], 8
-; CHECK-NEXT: [[RET2:%.*]] = and i64 [[AND]], 16
+; CHECK-NEXT: [[RET1:%.*]] = and i64 [[A]], 8
+; CHECK-NEXT: [[RET2:%.*]] = and i64 [[A]], 16
; CHECK-NEXT: call void @use(i64 [[RET1]])
; CHECK-NEXT: call void @use(i64 [[RET2]])
; CHECK-NEXT: ret void
@@ -45,9 +43,7 @@ define void @or_of_and(i64 %a, i64 %b) {
; CHECK-LABEL: define void @or_of_and(
; CHECK-SAME: i64 [[A:%.*]], i64 [[B:%.*]]) {
; CHECK-NEXT: entry:
-; CHECK-NEXT: [[AND1:%.*]] = and i64 [[A]], 24
-; CHECK-NEXT: [[AND2:%.*]] = and i64 [[B]], 25
-; CHECK-NEXT: [[OR:%.*]] = or i64 [[AND1]], [[AND2]]
+; CHECK-NEXT: [[OR:%.*]] = or i64 [[A]], [[B]]
; CHECK-NEXT: [[RET1:%.*]] = and i64 [[OR]], 8
; CHECK-NEXT: [[RET2:%.*]] = and i64 [[OR]], 16
; CHECK-NEXT: call void @use(i64 [[RET1]])
@@ -69,9 +65,7 @@ define void @select_of_and(i1 %c, i64 %a, i64 %b) {
; CHECK-LABEL: define void @select_of_and(
; CHECK-SAME: i1 [[C:%.*]], i64 [[A:%.*]], i64 [[B:%.*]]) {
; CHECK-NEXT: entry:
-; CHECK-NEXT: [[AND1:%.*]] = and i64 [[A]], 24
-; CHECK-NEXT: [[AND2:%.*]] = and i64 [[B]], 25
-; CHECK-NEXT: [[S:%.*]] = select i1 [[C]], i64 [[AND1]], i64 [[AND2]]
+; CHECK-NEXT: [[S:%.*]] = select i1 [[C]], i64 [[A]], i64 [[B]]
; CHECK-NEXT: [[RET1:%.*]] = and i64 [[S]], 8
; CHECK-NEXT: [[RET2:%.*]] = and i64 [[S]], 16
; CHECK-NEXT: call void @use(i64 [[RET1]])
@@ -93,9 +87,8 @@ define void @select_of_and_2(i1 %c, i64 %a, i64 %b) {
; CHECK-LABEL: define void @select_of_and_2(
; CHECK-SAME: i1 [[C:%.*]], i64 [[A:%.*]], i64 [[B:%.*]]) {
; CHECK-NEXT: entry:
-; CHECK-NEXT: [[AND1:%.*]] = and i64 [[A]], 25
; CHECK-NEXT: [[AND2:%.*]] = and i64 [[B]], 23
-; CHECK-NEXT: [[S:%.*]] = select i1 [[C]], i64 [[AND1]], i64 [[AND2]]
+; CHECK-NEXT: [[S:%.*]] = select i1 [[C]], i64 [[A]], i64 [[AND2]]
; CHECK-NEXT: [[RET1:%.*]] = and i64 [[S]], 8
; CHECK-NEXT: [[RET2:%.*]] = and i64 [[S]], 16
; CHECK-NEXT: call void @use(i64 [[RET1]])
@@ -117,9 +110,8 @@ define void @select_of_and_multiuse(i1 %c, i64 %a, i64 %b) {
; CHECK-LABEL: define void @select_of_and_multiuse(
; CHECK-SAME: i1 [[C:%.*]], i64 [[A:%.*]], i64 [[B:%.*]]) {
; CHECK-NEXT: entry:
-; CHECK-NEXT: [[AND1:%.*]] = and i64 [[A]], 24
; CHECK-NEXT: [[AND2:%.*]] = and i64 [[B]], 25
-; CHECK-NEXT: [[S:%.*]] = select i1 [[C]], i64 [[AND1]], i64 [[AND2]]
+; CHECK-NEXT: [[S:%.*]] = select i1 [[C]], i64 [[A]], i64 [[AND2]]
; CHECK-NEXT: [[RET1:%.*]] = and i64 [[S]], 8
; CHECK-NEXT: [[RET2:%.*]] = and i64 [[S]], 16
; CHECK-NEXT: call void @use(i64 [[RET1]])
@@ -165,9 +157,7 @@ define void @select_of_or(i1 %c, i64 %a, i64 %b) {
; CHECK-LABEL: define void @select_of_or(
; CHECK-SAME: i1 [[C:%.*]], i64 [[A:%.*]], i64 [[B:%.*]]) {
; CHECK-NEXT: entry:
-; CHECK-NEXT: [[OR1:%.*]] = or i64 [[A]], 3
-; CHECK-NEXT: [[OR2:%.*]] = or i64 [[B]], 192
-; CHECK-NEXT: [[S:%.*]] = select i1 [[C]], i64 [[OR1]], i64 [[OR2]]
+; CHECK-NEXT: [[S:%.*]] = select i1 [[C]], i64 [[A]], i64 [[B]]
; CHECK-NEXT: [[RET1:%.*]] = and i64 [[S]], 8
; CHECK-NEXT: [[RET2:%.*]] = and i64 [[S]], 16
; CHECK-NEXT: call void @use(i64 [[RET1]])
diff --git a/llvm/test/Transforms/BDCE/dead-uses.ll b/llvm/test/Transforms/BDCE/dead-uses.ll
index 1a19ff6fb11d5e6..1cf25e409c7ddde 100644
--- a/llvm/test/Transforms/BDCE/dead-uses.ll
+++ b/llvm/test/Transforms/BDCE/dead-uses.ll
@@ -9,8 +9,7 @@ declare <2 x i32> @llvm.fshr.v2i32(<2 x i32>, <2 x i32>, <2 x i32>)
; First fshr operand is dead.
define i32 @pr39771_fshr_multi_use_instr(i32 %a) {
; CHECK-LABEL: @pr39771_fshr_multi_use_instr(
-; CHECK-NEXT: [[X:%.*]] = or i32 [[A:%.*]], 0
-; CHECK-NEXT: [[B:%.*]] = tail call i32 @llvm.fshr.i32(i32 0, i32 [[X]], i32 1)
+; CHECK-NEXT: [[B:%.*]] = tail call i32 @llvm.fshr.i32(i32 0, i32 [[A:%.*]], i32 1)
; CHECK-NEXT: [[C:%.*]] = lshr i32 [[B]], 23
; CHECK-NEXT: [[D:%.*]] = xor i32 [[C]], [[B]]
; CHECK-NEXT: [[E:%.*]] = and i32 [[D]], 31
More information about the llvm-commits
mailing list