[llvm] [BDCE] Handle multi-use `select` of `and`s on demanded bits (PR #79688)

Antonio Frighetto via llvm-commits llvm-commits at lists.llvm.org
Sat Jan 27 02:45:19 PST 2024


https://github.com/antoniofrighetto created https://github.com/llvm/llvm-project/pull/79688

Simplify multi-use `select` of `and`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/BBdrCz.

>From e773278712d39674d5403a4bbf46b5cafdd125ca Mon Sep 17 00:00:00 2001
From: Antonio Frighetto <me at antoniofrighetto.com>
Date: Sat, 27 Jan 2024 11:40:40 +0100
Subject: [PATCH 1/2] [BDCE] Introduce test for PR79688 (NFC)

---
 llvm/test/Transforms/BDCE/select-multiuse.ll | 126 +++++++++++++++++++
 1 file changed, 126 insertions(+)
 create mode 100644 llvm/test/Transforms/BDCE/select-multiuse.ll

diff --git a/llvm/test/Transforms/BDCE/select-multiuse.ll b/llvm/test/Transforms/BDCE/select-multiuse.ll
new file mode 100644
index 000000000000000..02ca70e71535b39
--- /dev/null
+++ b/llvm/test/Transforms/BDCE/select-multiuse.ll
@@ -0,0 +1,126 @@
+; 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 @select(i1 %c, i64 %a, i64 %b) {
+; CHECK-LABEL: define void @select(
+; 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_2(i1 %c, i64 %a, i64 %b) {
+; CHECK-LABEL: define void @select_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_different_demanded(i1 %c, i64 %a, i64 %b) {
+; CHECK-LABEL: define void @select_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 @and_multiuse(i1 %c, i64 %a, i64 %b) {
+; CHECK-LABEL: define void @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
+  %and2 = and i64 %b, 25
+  %s = select i1 %c, i64 %and1, i64 %and2
+  %ret1 = and i64 %s, 8
+  %ret2 = and i64 %s, 16
+  call void @use(i64 %ret1)
+  call void @use2(i64 %ret2, i64 %and2)
+  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 ac76f66f897a2fb51b23d2632455f6bc2a1b17ed Mon Sep 17 00:00:00 2001
From: Antonio Frighetto <me at antoniofrighetto.com>
Date: Sat, 27 Jan 2024 11:41:32 +0100
Subject: [PATCH 2/2] [BDCE] Handle multi-use `select` of `and`s on demanded
 bits

Simplify multi-use `select` of `and`s  when these last
do not affect the demanded bits being considered.

Proofs: https://alive2.llvm.org/ce/z/BBdrCz.
---
 llvm/lib/Transforms/Scalar/BDCE.cpp          | 27 ++++++++++++++++++++
 llvm/test/Transforms/BDCE/select-multiuse.ll | 10 +++-----
 2 files changed, 30 insertions(+), 7 deletions(-)

diff --git a/llvm/lib/Transforms/Scalar/BDCE.cpp b/llvm/lib/Transforms/Scalar/BDCE.cpp
index 1fa2c75b0f42ac1..178b5a0da469cdd 100644
--- a/llvm/lib/Transforms/Scalar/BDCE.cpp
+++ b/llvm/lib/Transforms/Scalar/BDCE.cpp
@@ -125,6 +125,33 @@ static bool bitTrackingDCE(Function &F, DemandedBits &DB) {
       }
     }
 
+    // Simplify select of ands when the mask does not affect the demanded bits.
+    if (SelectInst *SI = dyn_cast<SelectInst>(&I)) {
+      APInt Demanded = DB.getDemandedBits(SI);
+      if (!Demanded.isAllOnes()) {
+        for (Use &U : I.operands()) {
+          auto *And = dyn_cast<BinaryOperator>(&U);
+          if (!And || !And->hasOneUse() || And->getOpcode() != Instruction::And)
+            continue;
+
+          auto *CV = dyn_cast<ConstantInt>(And->getOperand(1));
+          if (!CV)
+            continue;
+
+          APInt Mask = CV->getValue();
+          if ((Mask | ~Demanded).isAllOnes()) {
+            clearAssumptionsOfUsers(And, DB);
+            U.set(And->getOperand(0));
+            Worklist.push_back(And);
+            Changed = true;
+          }
+        }
+      }
+
+      if (Changed)
+        continue;
+    }
+
     for (Use &U : I.operands()) {
       // DemandedBits only detects dead integer uses.
       if (!U->getType()->isIntOrIntVectorTy())
diff --git a/llvm/test/Transforms/BDCE/select-multiuse.ll b/llvm/test/Transforms/BDCE/select-multiuse.ll
index 02ca70e71535b39..8ad91cc7458d114 100644
--- a/llvm/test/Transforms/BDCE/select-multiuse.ll
+++ b/llvm/test/Transforms/BDCE/select-multiuse.ll
@@ -5,9 +5,7 @@ define void @select(i1 %c, i64 %a, i64 %b) {
 ; CHECK-LABEL: define void @select(
 ; 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]])
@@ -29,9 +27,8 @@ define void @select_2(i1 %c, i64 %a, i64 %b) {
 ; CHECK-LABEL: define void @select_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]])
@@ -77,9 +74,8 @@ define void @and_multiuse(i1 %c, i64 %a, i64 %b) {
 ; CHECK-LABEL: define void @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]])



More information about the llvm-commits mailing list