Skip to content

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

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

AZero13
Copy link
Contributor

@AZero13 AZero13 commented Jul 17, 2025

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.

AZero13 added 2 commits July 17, 2025 12:52
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.
@AZero13 AZero13 requested a review from nikic as a code owner July 17, 2025 17:13
@llvmbot llvmbot added llvm:instcombine Covers the InstCombine, InstSimplify and AggressiveInstCombine passes llvm:transforms labels Jul 17, 2025
@AZero13 AZero13 changed the title Propagate NSW in ~((-X) | Y) --> (X - 1) & (~Y) [InstCombine] Propagate NSW in ~((-X) | Y) --> (X - 1) & (~Y) Jul 17, 2025
@llvmbot
Copy link
Member

llvmbot commented Jul 17, 2025

@llvm/pr-subscribers-llvm-transforms

Author: AZero13 (AZero13)

Changes

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.


Full diff: https://github.com/llvm/llvm-project/pull/149359.diff

2 Files Affected:

  • (modified) llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp (+14)
  • (modified) llvm/test/Transforms/InstCombine/not.ll (+28)
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 d693b9d8f8557..9de46c290b363 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 nsw 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 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]]
+;
+  %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)  {

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
llvm:instcombine Covers the InstCombine, InstSimplify and AggressiveInstCombine passes llvm:transforms
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants