[llvm] [InstCombine] Missed optimization: Fold (sext(a) & c1) == c2 to (a & c3) == trunc(c2) (PR #112646)

Lee Wei via llvm-commits llvm-commits at lists.llvm.org
Sat Oct 26 13:12:40 PDT 2024


https://github.com/leewei05 updated https://github.com/llvm/llvm-project/pull/112646

>From ab5fac98fe2a5477f65c10697cd9ddc22f2f35c0 Mon Sep 17 00:00:00 2001
From: Lee <lee10202013 at gmail.com>
Date: Sat, 12 Oct 2024 15:13:47 -0600
Subject: [PATCH 1/2] Add pre-commit tests

---
 llvm/test/Transforms/InstCombine/sext-and.ll | 176 +++++++++++++++++++
 1 file changed, 176 insertions(+)
 create mode 100644 llvm/test/Transforms/InstCombine/sext-and.ll

diff --git a/llvm/test/Transforms/InstCombine/sext-and.ll b/llvm/test/Transforms/InstCombine/sext-and.ll
new file mode 100644
index 00000000000000..38a75cddb2c9d3
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/sext-and.ll
@@ -0,0 +1,176 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+declare void @use(i8)
+
+define i1 @fold_sext_to_and(i8 %x) {
+; CHECK-LABEL: define i1 @fold_sext_to_and(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:    [[TMP1:%.*]] = sext i8 [[X]] to i32
+; CHECK-NEXT:    [[TMP2:%.*]] = and i32 [[TMP1]], -2147483647
+; CHECK-NEXT:    [[TMP3:%.*]] = icmp eq i32 [[TMP2]], 1
+; CHECK-NEXT:    ret i1 [[TMP3]]
+;
+  %1 = sext i8 %x to i32
+  %2 = and i32 %1, -2147483647
+  %3 = icmp eq i32 %2, 1
+  ret i1 %3
+}
+
+define i1 @fold_sext_to_and1(i8 %x) {
+; CHECK-LABEL: define i1 @fold_sext_to_and1(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:    [[TMP1:%.*]] = sext i8 [[X]] to i32
+; CHECK-NEXT:    [[TMP2:%.*]] = and i32 [[TMP1]], -2147483647
+; CHECK-NEXT:    [[TMP3:%.*]] = icmp ne i32 [[TMP2]], 1
+; CHECK-NEXT:    ret i1 [[TMP3]]
+;
+  %1 = sext i8 %x to i32
+  %2 = and i32 %1, -2147483647
+  %3 = icmp ne i32 %2, 1
+  ret i1 %3
+}
+
+define i1 @fold_sext_to_and_multi_use(i8 %x) {
+; CHECK-LABEL: define i1 @fold_sext_to_and_multi_use(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:    [[TMP1:%.*]] = sext i8 [[X]] to i32
+; CHECK-NEXT:    call void @use(i32 [[TMP1]])
+; CHECK-NEXT:    [[TMP2:%.*]] = and i32 [[TMP1]], -2147483647
+; CHECK-NEXT:    [[TMP3:%.*]] = icmp eq i32 [[TMP2]], 1
+; CHECK-NEXT:    ret i1 [[TMP3]]
+;
+  %1 = sext i8 %x to i32
+  call void @use(i32 %1)
+  %2 = and i32 %1, -2147483647
+  %3 = icmp eq i32 %2, 1
+  ret i1 %3
+}
+
+define i1 @fold_sext_to_and_multi_use1(i8 %x) {
+; CHECK-LABEL: define i1 @fold_sext_to_and_multi_use1(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:    [[TMP1:%.*]] = sext i8 [[X]] to i32
+; CHECK-NEXT:    call void @use(i32 [[TMP1]])
+; CHECK-NEXT:    [[TMP2:%.*]] = and i32 [[TMP1]], -2147483647
+; CHECK-NEXT:    [[TMP3:%.*]] = icmp ne i32 [[TMP2]], 1
+; CHECK-NEXT:    ret i1 [[TMP3]]
+;
+  %1 = sext i8 %x to i32
+  call void @use(i32 %1)
+  %2 = and i32 %1, -2147483647
+  %3 = icmp ne i32 %2, 1
+  ret i1 %3
+}
+
+; Negative tests
+
+define i1 @fold_sext_to_and_wrong(i8 %x) {
+; CHECK-LABEL: define i1 @fold_sext_to_and_wrong(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:    ret i1 false
+;
+  %1 = sext i8 %x to i32
+  %2 = and i32 %1, -2147483647
+  %3 = icmp eq i32 %2, -1
+  ret i1 %3
+}
+
+define i1 @fold_sext_to_and_wrong2(i8 %x) {
+; CHECK-LABEL: define i1 @fold_sext_to_and_wrong2(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:    ret i1 false
+;
+  %1 = sext i8 %x to i32
+  %2 = and i32 %1, -2147483647
+  %3 = icmp eq i32 %2, 128
+  ret i1 %3
+}
+
+define i1 @fold_sext_to_and_wrong3(i8 %x) {
+; CHECK-LABEL: define i1 @fold_sext_to_and_wrong3(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:    ret i1 false
+;
+  %1 = sext i8 %x to i32
+  %2 = and i32 %1, 128
+  %3 = icmp eq i32 %2, -2147483648
+  ret i1 %3
+}
+
+define i1 @fold_sext_to_and_wrong4(i8 %x) {
+; CHECK-LABEL: define i1 @fold_sext_to_and_wrong4(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:    ret i1 false
+;
+  %1 = sext i8 %x to i32
+  %2 = and i32 %1, 128
+  %3 = icmp eq i32 %2, 1
+  ret i1 %3
+}
+
+define i1 @fold_sext_to_and_wrong5(i8 %x) {
+; CHECK-LABEL: define i1 @fold_sext_to_and_wrong5(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:    ret i1 false
+;
+  %1 = sext i8 %x to i32
+  %2 = and i32 %1, -256
+  %3 = icmp eq i32 %2, 1
+  ret i1 %3
+}
+
+define i1 @fold_sext_to_and_wrong6(i8 %x) {
+; CHECK-LABEL: define i1 @fold_sext_to_and_wrong6(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:    ret i1 true
+;
+  %1 = sext i8 %x to i32
+  %2 = and i32 %1, -2147483647
+  %3 = icmp ne i32 %2, -1
+  ret i1 %3
+}
+
+define i1 @fold_sext_to_and_wrong7(i8 %x) {
+; CHECK-LABEL: define i1 @fold_sext_to_and_wrong7(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:    ret i1 true
+;
+  %1 = sext i8 %x to i32
+  %2 = and i32 %1, -2147483647
+  %3 = icmp ne i32 %2, 128
+  ret i1 %3
+}
+
+define i1 @fold_sext_to_and_wrong8(i8 %x) {
+; CHECK-LABEL: define i1 @fold_sext_to_and_wrong8(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:    ret i1 true
+;
+  %1 = sext i8 %x to i32
+  %2 = and i32 %1, 128
+  %3 = icmp ne i32 %2, -2147483648
+  ret i1 %3
+}
+
+define i1 @fold_sext_to_and_wrong9(i8 %x) {
+; CHECK-LABEL: define i1 @fold_sext_to_and_wrong9(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:    ret i1 true
+;
+  %1 = sext i8 %x to i32
+  %2 = and i32 %1, 128
+  %3 = icmp ne i32 %2, 1
+  ret i1 %3
+}
+
+define i1 @fold_sext_to_and_wrong10(i8 %x) {
+; CHECK-LABEL: define i1 @fold_sext_to_and_wrong10(
+; CHECK-SAME: i8 [[X:%.*]]) {
+; CHECK-NEXT:    ret i1 true
+;
+  %1 = sext i8 %x to i32
+  %2 = and i32 %1, -256
+  %3 = icmp ne i32 %2, 1
+  ret i1 %3
+}

>From 8e912fcc8907993a6d55f7bb9d4af5a134770e10 Mon Sep 17 00:00:00 2001
From: Lee <lee10202013 at gmail.com>
Date: Thu, 24 Oct 2024 21:06:30 -0600
Subject: [PATCH 2/2] Apply transformation

---
 .../InstCombine/InstCombineCompares.cpp       | 27 +++++++++++++++++++
 llvm/test/Transforms/InstCombine/sext-and.ll  | 18 ++++++-------
 2 files changed, 35 insertions(+), 10 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index 18a6fdcec1728e..b73fc0d6827b9b 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -24,6 +24,7 @@
 #include "llvm/IR/ConstantRange.h"
 #include "llvm/IR/DataLayout.h"
 #include "llvm/IR/InstrTypes.h"
+#include "llvm/IR/Instructions.h"
 #include "llvm/IR/IntrinsicInst.h"
 #include "llvm/IR/PatternMatch.h"
 #include "llvm/Support/KnownBits.h"
@@ -7687,6 +7688,32 @@ Instruction *InstCombinerImpl::visitICmpInst(ICmpInst &I) {
   if (Instruction *Res = foldReductionIdiom(I, Builder, DL))
     return Res;
 
+  {
+    Value *A;
+    const APInt *C1, *C2;
+    ICmpInst::Predicate Pred = I.getPredicate();
+    if (ICmpInst::isEquality(Pred)) {
+      // sext(a) & c1 == c2 --> a & c3 == trunc(c2)
+      // sext(a) & c1 != c2 --> a & c3 != trunc(c2)
+      if (match(Op0, m_And(m_SExtLike(m_Value(A)), m_APInt(C1))) &&
+          match(Op1, m_APInt(C2))) {
+        Type *InputTy = A->getType();
+        unsigned InputBitWidth = InputTy->getScalarSizeInBits();
+        // c2 must be non-negative at the bitwidth of a.
+        if (C2->ult(APInt::getOneBitSet(C2->getBitWidth(), InputBitWidth))) {
+          APInt TruncC1 = C1->trunc(InputBitWidth);
+          // Check if there are 1s in C1 high bits of size InputBitWidth.
+          if (C1->uge(APInt::getOneBitSet(C1->getBitWidth(), InputBitWidth)))
+            TruncC1.setBit(InputBitWidth - 1);
+          Value *AndInst = Builder.CreateAnd(A, TruncC1);
+          return new ICmpInst(
+              Pred, AndInst,
+              ConstantInt::get(InputTy, C2->trunc(InputBitWidth)));
+        }
+      }
+    }
+  }
+
   return Changed ? &I : nullptr;
 }
 
diff --git a/llvm/test/Transforms/InstCombine/sext-and.ll b/llvm/test/Transforms/InstCombine/sext-and.ll
index 38a75cddb2c9d3..1c0c824709fd2f 100644
--- a/llvm/test/Transforms/InstCombine/sext-and.ll
+++ b/llvm/test/Transforms/InstCombine/sext-and.ll
@@ -6,9 +6,8 @@ declare void @use(i8)
 define i1 @fold_sext_to_and(i8 %x) {
 ; CHECK-LABEL: define i1 @fold_sext_to_and(
 ; CHECK-SAME: i8 [[X:%.*]]) {
-; CHECK-NEXT:    [[TMP1:%.*]] = sext i8 [[X]] to i32
-; CHECK-NEXT:    [[TMP2:%.*]] = and i32 [[TMP1]], -2147483647
-; CHECK-NEXT:    [[TMP3:%.*]] = icmp eq i32 [[TMP2]], 1
+; CHECK-NEXT:    [[TMP1:%.*]] = and i8 [[X]], -127
+; CHECK-NEXT:    [[TMP3:%.*]] = icmp eq i8 [[TMP1]], 1
 ; CHECK-NEXT:    ret i1 [[TMP3]]
 ;
   %1 = sext i8 %x to i32
@@ -20,9 +19,8 @@ define i1 @fold_sext_to_and(i8 %x) {
 define i1 @fold_sext_to_and1(i8 %x) {
 ; CHECK-LABEL: define i1 @fold_sext_to_and1(
 ; CHECK-SAME: i8 [[X:%.*]]) {
-; CHECK-NEXT:    [[TMP1:%.*]] = sext i8 [[X]] to i32
-; CHECK-NEXT:    [[TMP2:%.*]] = and i32 [[TMP1]], -2147483647
-; CHECK-NEXT:    [[TMP3:%.*]] = icmp ne i32 [[TMP2]], 1
+; CHECK-NEXT:    [[TMP1:%.*]] = and i8 [[X]], -127
+; CHECK-NEXT:    [[TMP3:%.*]] = icmp ne i8 [[TMP1]], 1
 ; CHECK-NEXT:    ret i1 [[TMP3]]
 ;
   %1 = sext i8 %x to i32
@@ -36,8 +34,8 @@ define i1 @fold_sext_to_and_multi_use(i8 %x) {
 ; CHECK-SAME: i8 [[X:%.*]]) {
 ; CHECK-NEXT:    [[TMP1:%.*]] = sext i8 [[X]] to i32
 ; CHECK-NEXT:    call void @use(i32 [[TMP1]])
-; CHECK-NEXT:    [[TMP2:%.*]] = and i32 [[TMP1]], -2147483647
-; CHECK-NEXT:    [[TMP3:%.*]] = icmp eq i32 [[TMP2]], 1
+; CHECK-NEXT:    [[TMP2:%.*]] = and i8 [[X]], -127
+; CHECK-NEXT:    [[TMP3:%.*]] = icmp eq i8 [[TMP2]], 1
 ; CHECK-NEXT:    ret i1 [[TMP3]]
 ;
   %1 = sext i8 %x to i32
@@ -52,8 +50,8 @@ define i1 @fold_sext_to_and_multi_use1(i8 %x) {
 ; CHECK-SAME: i8 [[X:%.*]]) {
 ; CHECK-NEXT:    [[TMP1:%.*]] = sext i8 [[X]] to i32
 ; CHECK-NEXT:    call void @use(i32 [[TMP1]])
-; CHECK-NEXT:    [[TMP2:%.*]] = and i32 [[TMP1]], -2147483647
-; CHECK-NEXT:    [[TMP3:%.*]] = icmp ne i32 [[TMP2]], 1
+; CHECK-NEXT:    [[TMP2:%.*]] = and i8 [[X]], -127
+; CHECK-NEXT:    [[TMP3:%.*]] = icmp ne i8 [[TMP2]], 1
 ; CHECK-NEXT:    ret i1 [[TMP3]]
 ;
   %1 = sext i8 %x to i32



More information about the llvm-commits mailing list