[llvm] Propagate NSW in ~((-X) | Y) --> (X - 1) & (~Y) (PR #149359)

via llvm-commits llvm-commits at lists.llvm.org
Thu Jul 17 10:13:22 PDT 2025


https://github.com/AZero13 created https://github.com/llvm/llvm-project/pull/149359

https://alive2.llvm.org/ce/z/qojyPF

If -X is nsw, then X - 1 must be too.
This is because -X can only be nsw if X is any number that is not
INT_MIN. And if X is not INT_MIN, then X - 1 must be nsw.

>From 641ae741bc73642dfaf774c48aa8d4ec936be0d4 Mon Sep 17 00:00:00 2001
From: Rose <gfunni234 at gmail.com>
Date: Thu, 17 Jul 2025 12:52:53 -0400
Subject: [PATCH 1/2] Pre-commit test (NFC)

---
 llvm/test/Transforms/InstCombine/not.ll | 28 +++++++++++++++++++++++++
 1 file changed, 28 insertions(+)

diff --git a/llvm/test/Transforms/InstCombine/not.ll b/llvm/test/Transforms/InstCombine/not.ll
index d693b9d8f8557..82fb94b6928e9 100644
--- a/llvm/test/Transforms/InstCombine/not.ll
+++ b/llvm/test/Transforms/InstCombine/not.ll
@@ -436,6 +436,34 @@ define <3 x i5> @not_or_neg_commute_vec(<3 x i5> %x, <3 x i5> %p)  {
   ret <3 x i5> %not
 }
 
+define i8 @not_or_neg_nsw(i8 %x, i8 %y)  {
+; CHECK-LABEL: @not_or_neg_nsw(
+; CHECK-NEXT:    [[TMP1:%.*]] = add i8 [[Y:%.*]], -1
+; CHECK-NEXT:    [[TMP2:%.*]] = xor i8 [[X:%.*]], -1
+; CHECK-NEXT:    [[NOT:%.*]] = and i8 [[TMP1]], [[TMP2]]
+; CHECK-NEXT:    ret i8 [[NOT]]
+;
+  %s = sub nsw i8 0, %y
+  %o = or i8 %s, %x
+  %not = xor i8 %o, -1
+  ret i8 %not
+}
+
+define <3 x i5> @not_or_neg_commute_vec_nsw(<3 x i5> %x, <3 x i5> %p)  {
+; CHECK-LABEL: @not_or_neg_commute_vec_nsw(
+; CHECK-NEXT:    [[Y:%.*]] = mul <3 x i5> [[P:%.*]], <i5 1, i5 2, i5 3>
+; CHECK-NEXT:    [[TMP1:%.*]] = add <3 x i5> [[X:%.*]], splat (i5 -1)
+; CHECK-NEXT:    [[TMP2:%.*]] = xor <3 x i5> [[Y]], splat (i5 -1)
+; CHECK-NEXT:    [[NOT:%.*]] = and <3 x i5> [[TMP1]], [[TMP2]]
+; CHECK-NEXT:    ret <3 x i5> [[NOT]]
+;
+  %y = mul <3 x i5> %p, <i5 1, i5 2, i5 3> ; thwart complexity-based-canonicalization
+  %s = sub nsw <3 x i5> <i5 0, i5 0, i5 poison>, %x
+  %o = or <3 x i5> %y, %s
+  %not = xor <3 x i5> %o, <i5 -1, i5 poison, i5 -1>
+  ret <3 x i5> %not
+}
+
 ; negative test
 
 define i8 @not_or_neg_use1(i8 %x, i8 %y)  {

>From a8e39c230472d98387b54f16e4915b0caefc2f73 Mon Sep 17 00:00:00 2001
From: Rose <gfunni234 at gmail.com>
Date: Thu, 17 Jul 2025 12:51:23 -0400
Subject: [PATCH 2/2] Propagate NSW in ~((-X) | Y) --> (X - 1) & (~Y)

https://alive2.llvm.org/ce/z/qojyPF

If -X is nsw, then X - 1 must be too.
This is because -X can only be nsw if X is any number that is not
INT_MIN. And if X is not INT_MIN, then X - 1 must be nsw.
---
 .../Transforms/InstCombine/InstCombineAndOrXor.cpp | 14 ++++++++++++++
 llvm/test/Transforms/InstCombine/not.ll            |  4 ++--
 2 files changed, 16 insertions(+), 2 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
index 3beda6bc5ba38..18fa95dfa1497 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
@@ -4750,8 +4750,22 @@ Instruction *InstCombinerImpl::foldNot(BinaryOperator &I) {
   BinaryOperator *NotVal;
   if (match(NotOp, m_BinOp(NotVal))) {
     // ~((-X) | Y) --> (X - 1) & (~Y)
+    if (match(NotVal,
+              m_OneUse(m_c_Or(m_OneUse(m_NSWNeg(m_Value(X))), m_Value(Y))))) {
+      // If -X is nsw, then X - 1 must be too.
+      // This is because -X can only be nsw if X is any number that is not
+      // INT_MIN. And if X is not INT_MIN, then X - 1 must be nsw.
+      Value *DecX = Builder.CreateAdd(X, ConstantInt::getAllOnesValue(Ty), "",
+                                      /*HasNUW=*/false, /*HasNSW=*/true);
+      Value *NotY = Builder.CreateNot(Y);
+      return BinaryOperator::CreateAnd(DecX, NotY);
+    }
+
     if (match(NotVal,
               m_OneUse(m_c_Or(m_OneUse(m_Neg(m_Value(X))), m_Value(Y))))) {
+      // If -X is nsw, then X - 1 must be too.
+      // This is because -X can only be nsw if X is any number that is not
+      // INT_MIN. And if X is not INT_MIN, then X - 1 must be nsw.
       Value *DecX = Builder.CreateAdd(X, ConstantInt::getAllOnesValue(Ty));
       Value *NotY = Builder.CreateNot(Y);
       return BinaryOperator::CreateAnd(DecX, NotY);
diff --git a/llvm/test/Transforms/InstCombine/not.ll b/llvm/test/Transforms/InstCombine/not.ll
index 82fb94b6928e9..9de46c290b363 100644
--- a/llvm/test/Transforms/InstCombine/not.ll
+++ b/llvm/test/Transforms/InstCombine/not.ll
@@ -438,7 +438,7 @@ define <3 x i5> @not_or_neg_commute_vec(<3 x i5> %x, <3 x i5> %p)  {
 
 define i8 @not_or_neg_nsw(i8 %x, i8 %y)  {
 ; CHECK-LABEL: @not_or_neg_nsw(
-; CHECK-NEXT:    [[TMP1:%.*]] = add i8 [[Y:%.*]], -1
+; CHECK-NEXT:    [[TMP1:%.*]] = add nsw i8 [[Y:%.*]], -1
 ; CHECK-NEXT:    [[TMP2:%.*]] = xor i8 [[X:%.*]], -1
 ; CHECK-NEXT:    [[NOT:%.*]] = and i8 [[TMP1]], [[TMP2]]
 ; CHECK-NEXT:    ret i8 [[NOT]]
@@ -452,7 +452,7 @@ define i8 @not_or_neg_nsw(i8 %x, i8 %y)  {
 define <3 x i5> @not_or_neg_commute_vec_nsw(<3 x i5> %x, <3 x i5> %p)  {
 ; CHECK-LABEL: @not_or_neg_commute_vec_nsw(
 ; CHECK-NEXT:    [[Y:%.*]] = mul <3 x i5> [[P:%.*]], <i5 1, i5 2, i5 3>
-; CHECK-NEXT:    [[TMP1:%.*]] = add <3 x i5> [[X:%.*]], splat (i5 -1)
+; CHECK-NEXT:    [[TMP1:%.*]] = add nsw <3 x i5> [[X:%.*]], splat (i5 -1)
 ; CHECK-NEXT:    [[TMP2:%.*]] = xor <3 x i5> [[Y]], splat (i5 -1)
 ; CHECK-NEXT:    [[NOT:%.*]] = and <3 x i5> [[TMP1]], [[TMP2]]
 ; CHECK-NEXT:    ret <3 x i5> [[NOT]]



More information about the llvm-commits mailing list