[llvm] [InstCombine] Generalize sub(C, or(X, C)) --> and(X, ~C) fold (PR #202274)
via llvm-commits
llvm-commits at lists.llvm.org
Fri Jul 17 07:44:58 PDT 2026
https://github.com/mygitljf updated https://github.com/llvm/llvm-project/pull/202274
>From 1c57ba15a9705b3c5c3286a7cee4a761675df78c Mon Sep 17 00:00:00 2001
From: mygitljf <2410316423 at qq.com>
Date: Mon, 8 Jun 2026 07:09:16 +0000
Subject: [PATCH 1/4] [InstCombine] Fold signed-max sub-or pattern
---
.../InstCombine/InstCombineAddSub.cpp | 11 ++++
llvm/test/Transforms/InstCombine/sub.ll | 52 +++++++++++++++++++
2 files changed, 63 insertions(+)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
index 8c0dcc8029a1e..3942574723f16 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
@@ -2427,6 +2427,17 @@ Instruction *InstCombinerImpl::visitSub(BinaryOperator &I) {
}
}
+ {
+ const APInt *C, *OrC;
+ Value *X;
+ if (!I.hasNoSignedWrap() && !I.hasNoUnsignedWrap() &&
+ match(Op0, m_APInt(C)) && C->isMaxSignedValue() &&
+ match(Op1, m_OneUse(m_Or(m_Value(X), m_APInt(OrC)))) && *OrC == *C)
+ return BinaryOperator::CreateAnd(
+ X, Constant::getIntegerValue(I.getType(),
+ APInt::getSignMask(C->getBitWidth())));
+ }
+
auto TryToNarrowDeduceFlags = [this, &I, &Op0, &Op1]() -> Instruction * {
if (Instruction *Ext = narrowMathIfNoOverflow(I))
return Ext;
diff --git a/llvm/test/Transforms/InstCombine/sub.ll b/llvm/test/Transforms/InstCombine/sub.ll
index 1e7fa477c362c..8b72f01722bc9 100644
--- a/llvm/test/Transforms/InstCombine/sub.ll
+++ b/llvm/test/Transforms/InstCombine/sub.ll
@@ -50,6 +50,58 @@ define <4 x i32> @sub_constant_expression_vec(<4 x i32> %x) {
ret <4 x i32> %r
}
+define i32 @sub_smax_or_i32(i32 %x) {
+; CHECK-LABEL: @sub_smax_or_i32(
+; CHECK-NEXT: [[R:%.*]] = and i32 [[X:%.*]], -2147483648
+; CHECK-NEXT: ret i32 [[R]]
+;
+ %or = or i32 %x, 2147483647
+ %r = sub i32 2147483647, %or
+ ret i32 %r
+}
+
+define i8 @sub_smax_or_i8(i8 %x) {
+; CHECK-LABEL: @sub_smax_or_i8(
+; CHECK-NEXT: [[R:%.*]] = and i8 [[X:%.*]], -128
+; CHECK-NEXT: ret i8 [[R]]
+;
+ %or = or i8 %x, 127
+ %r = sub i8 127, %or
+ ret i8 %r
+}
+
+define <2 x i32> @sub_smax_or_vec(<2 x i32> %x) {
+; CHECK-LABEL: @sub_smax_or_vec(
+; CHECK-NEXT: [[R:%.*]] = and <2 x i32> [[X:%.*]], splat (i32 -2147483648)
+; CHECK-NEXT: ret <2 x i32> [[R]]
+;
+ %or = or <2 x i32> %x, <i32 2147483647, i32 2147483647>
+ %r = sub <2 x i32> <i32 2147483647, i32 2147483647>, %or
+ ret <2 x i32> %r
+}
+
+define i32 @sub_smax_or_nsw(i32 %x) {
+; CHECK-LABEL: @sub_smax_or_nsw(
+; CHECK-NEXT: [[OR:%.*]] = or i32 [[X:%.*]], 2147483647
+; CHECK-NEXT: [[R:%.*]] = sub nsw i32 2147483647, [[OR]]
+; CHECK-NEXT: ret i32 [[R]]
+;
+ %or = or i32 %x, 2147483647
+ %r = sub nsw i32 2147483647, %or
+ ret i32 %r
+}
+
+define i32 @sub_smax_or_nuw(i32 %x) {
+; CHECK-LABEL: @sub_smax_or_nuw(
+; CHECK-NEXT: [[OR:%.*]] = or i32 [[X:%.*]], 2147483647
+; CHECK-NEXT: [[R:%.*]] = sub nuw i32 2147483647, [[OR]]
+; CHECK-NEXT: ret i32 [[R]]
+;
+ %or = or i32 %x, 2147483647
+ %r = sub nuw i32 2147483647, %or
+ ret i32 %r
+}
+
define i32 @neg_sub(i32 %x, i32 %y) {
; CHECK-LABEL: @neg_sub(
; CHECK-NEXT: [[R:%.*]] = add i32 [[Y:%.*]], [[X:%.*]]
>From f5306d2263b84f58f59bb0548c76acf70f0a3b71 Mon Sep 17 00:00:00 2001
From: mygitljf <2410316423 at qq.com>
Date: Tue, 9 Jun 2026 12:14:10 +0000
Subject: [PATCH 2/4] [InstCombine] Generalize sub-or fold, drop wrap check
---
.../InstCombine/InstCombineAddSub.cpp | 13 ++--
llvm/test/Transforms/InstCombine/sub.ll | 60 +++++++++++++++++--
2 files changed, 63 insertions(+), 10 deletions(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
index 3942574723f16..7d86cff799e3e 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
@@ -2430,12 +2430,13 @@ Instruction *InstCombinerImpl::visitSub(BinaryOperator &I) {
{
const APInt *C, *OrC;
Value *X;
- if (!I.hasNoSignedWrap() && !I.hasNoUnsignedWrap() &&
- match(Op0, m_APInt(C)) && C->isMaxSignedValue() &&
- match(Op1, m_OneUse(m_Or(m_Value(X), m_APInt(OrC)))) && *OrC == *C)
- return BinaryOperator::CreateAnd(
- X, Constant::getIntegerValue(I.getType(),
- APInt::getSignMask(C->getBitWidth())));
+ if (match(Op0, m_APInt(C)) &&
+ match(Op1, m_OneUse(m_Or(m_Value(X), m_APInt(OrC)))) && *OrC == *C) {
+ APInt NotC = ~*C;
+ if ((NotC << 1).isZero())
+ return BinaryOperator::CreateAnd(X,
+ ConstantInt::get(I.getType(), NotC));
+ }
}
auto TryToNarrowDeduceFlags = [this, &I, &Op0, &Op1]() -> Instruction * {
diff --git a/llvm/test/Transforms/InstCombine/sub.ll b/llvm/test/Transforms/InstCombine/sub.ll
index 8b72f01722bc9..f957a54beb262 100644
--- a/llvm/test/Transforms/InstCombine/sub.ll
+++ b/llvm/test/Transforms/InstCombine/sub.ll
@@ -82,8 +82,7 @@ define <2 x i32> @sub_smax_or_vec(<2 x i32> %x) {
define i32 @sub_smax_or_nsw(i32 %x) {
; CHECK-LABEL: @sub_smax_or_nsw(
-; CHECK-NEXT: [[OR:%.*]] = or i32 [[X:%.*]], 2147483647
-; CHECK-NEXT: [[R:%.*]] = sub nsw i32 2147483647, [[OR]]
+; CHECK-NEXT: [[R:%.*]] = and i32 [[X:%.*]], -2147483648
; CHECK-NEXT: ret i32 [[R]]
;
%or = or i32 %x, 2147483647
@@ -93,8 +92,7 @@ define i32 @sub_smax_or_nsw(i32 %x) {
define i32 @sub_smax_or_nuw(i32 %x) {
; CHECK-LABEL: @sub_smax_or_nuw(
-; CHECK-NEXT: [[OR:%.*]] = or i32 [[X:%.*]], 2147483647
-; CHECK-NEXT: [[R:%.*]] = sub nuw i32 2147483647, [[OR]]
+; CHECK-NEXT: [[R:%.*]] = and i32 [[X:%.*]], -2147483648
; CHECK-NEXT: ret i32 [[R]]
;
%or = or i32 %x, 2147483647
@@ -102,6 +100,60 @@ define i32 @sub_smax_or_nuw(i32 %x) {
ret i32 %r
}
+define i32 @sub_allones_or_i32(i32 %x) {
+; CHECK-LABEL: @sub_allones_or_i32(
+; CHECK-NEXT: ret i32 0
+;
+ %or = or i32 %x, -1
+ %r = sub i32 -1, %or
+ ret i32 %r
+}
+
+define i32 @sub_or_commuted(i32 %x) {
+; CHECK-LABEL: @sub_or_commuted(
+; CHECK-NEXT: [[R:%.*]] = and i32 [[X:%.*]], -2147483648
+; CHECK-NEXT: ret i32 [[R]]
+;
+ %or = or i32 2147483647, %x
+ %r = sub i32 2147483647, %or
+ ret i32 %r
+}
+
+define i32 @sub_or_no_fold_arbitrary_c(i32 %x) {
+; CHECK-LABEL: @sub_or_no_fold_arbitrary_c(
+; CHECK-NEXT: [[OR:%.*]] = or i32 [[X:%.*]], 1073741823
+; CHECK-NEXT: [[R:%.*]] = sub i32 1073741823, [[OR]]
+; CHECK-NEXT: ret i32 [[R]]
+;
+ %or = or i32 %x, 1073741823
+ %r = sub i32 1073741823, %or
+ ret i32 %r
+}
+
+define i32 @sub_or_no_fold_diff_c(i32 %x) {
+; CHECK-LABEL: @sub_or_no_fold_diff_c(
+; CHECK-NEXT: [[OR:%.*]] = or i32 [[X:%.*]], 2147483646
+; CHECK-NEXT: [[R:%.*]] = sub i32 2147483647, [[OR]]
+; CHECK-NEXT: ret i32 [[R]]
+;
+ %or = or i32 %x, 2147483646
+ %r = sub i32 2147483647, %or
+ ret i32 %r
+}
+
+define i8 @sub_smax_or_multiuse(i8 %x) {
+; CHECK-LABEL: @sub_smax_or_multiuse(
+; CHECK-NEXT: [[OR:%.*]] = or i8 [[X:%.*]], 127
+; CHECK-NEXT: call void @use8(i8 [[OR]])
+; CHECK-NEXT: [[R:%.*]] = sub i8 127, [[OR]]
+; CHECK-NEXT: ret i8 [[R]]
+;
+ %or = or i8 %x, 127
+ call void @use8(i8 %or)
+ %r = sub i8 127, %or
+ ret i8 %r
+}
+
define i32 @neg_sub(i32 %x, i32 %y) {
; CHECK-LABEL: @neg_sub(
; CHECK-NEXT: [[R:%.*]] = add i32 [[Y:%.*]], [[X:%.*]]
>From 6849b170e5e76a495841c799b0620101d4e2ee54 Mon Sep 17 00:00:00 2001
From: mygitljf <2410316423 at qq.com>
Date: Thu, 16 Jul 2026 20:47:22 +0800
Subject: [PATCH 3/4] [InstCombine] Add near-miss regression test
---
llvm/test/Transforms/InstCombine/sub.ll | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/llvm/test/Transforms/InstCombine/sub.ll b/llvm/test/Transforms/InstCombine/sub.ll
index f957a54beb262..23508e0803072 100644
--- a/llvm/test/Transforms/InstCombine/sub.ll
+++ b/llvm/test/Transforms/InstCombine/sub.ll
@@ -130,6 +130,17 @@ define i32 @sub_or_no_fold_arbitrary_c(i32 %x) {
ret i32 %r
}
+define i32 @sub_or_no_fold_near_miss(i32 %x) {
+; CHECK-LABEL: @sub_or_no_fold_near_miss(
+; CHECK-NEXT: [[OR:%.*]] = or i32 [[X:%.*]], -2
+; CHECK-NEXT: [[R:%.*]] = sub nsw i32 -2, [[OR]]
+; CHECK-NEXT: ret i32 [[R]]
+;
+ %or = or i32 %x, -2
+ %r = sub i32 -2, %or
+ ret i32 %r
+}
+
define i32 @sub_or_no_fold_diff_c(i32 %x) {
; CHECK-LABEL: @sub_or_no_fold_diff_c(
; CHECK-NEXT: [[OR:%.*]] = or i32 [[X:%.*]], 2147483646
>From 19b7465c455ebb2322526b33e8a127207ad8f007 Mon Sep 17 00:00:00 2001
From: mygitljf <2410316423 at qq.com>
Date: Fri, 17 Jul 2026 22:39:51 +0800
Subject: [PATCH 4/4] [InstCombine] Use APInt predicates for sub-or fold
---
llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
index 14c3b54184550..f1dde2c189c13 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp
@@ -2443,10 +2443,8 @@ Instruction *InstCombinerImpl::visitSub(BinaryOperator &I) {
Value *X;
if (match(Op0, m_APInt(C)) &&
match(Op1, m_OneUse(m_Or(m_Value(X), m_APInt(OrC)))) && *OrC == *C) {
- APInt NotC = ~*C;
- if ((NotC << 1).isZero())
- return BinaryOperator::CreateAnd(X,
- ConstantInt::get(I.getType(), NotC));
+ if (C->isAllOnes() || C->isMaxSignedValue())
+ return BinaryOperator::CreateAnd(X, ConstantInt::get(I.getType(), ~*C));
}
}
More information about the llvm-commits
mailing list