[llvm] [InstCombine] Simplify call to abs if it is known one side is positive (PR #157675)
via llvm-commits
llvm-commits at lists.llvm.org
Tue Sep 9 06:52:48 PDT 2025
https://github.com/AZero13 updated https://github.com/llvm/llvm-project/pull/157675
>From 8eaea9f1f6e0501f01b2c1d7c9635c17c0fc14ad Mon Sep 17 00:00:00 2001
From: AZero13 <gfunni234 at gmail.com>
Date: Tue, 9 Sep 2025 09:21:14 -0400
Subject: [PATCH 1/2] Pre-commit test (NFC)
---
.../InstCombine/select-abs-positive.ll | 173 ++++++++++++++++++
1 file changed, 173 insertions(+)
create mode 100644 llvm/test/Transforms/InstCombine/select-abs-positive.ll
diff --git a/llvm/test/Transforms/InstCombine/select-abs-positive.ll b/llvm/test/Transforms/InstCombine/select-abs-positive.ll
new file mode 100644
index 0000000000000..48dd45a4428de
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/select-abs-positive.ll
@@ -0,0 +1,173 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+declare i32 @llvm.abs.i32(i32, i1)
+declare i64 @llvm.abs.i64(i64, i1)
+
+; X == Positive ? X : ABS(X) -> ABS(X)
+define i32 @feq_1(i32 noundef %a, i8 noundef zeroext %b) {
+; CHECK-LABEL: @feq_1(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[CONV:%.*]] = zext i8 [[B:%.*]] to i32
+; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[A:%.*]], [[CONV]]
+; CHECK-NEXT: [[COND:%.*]] = tail call i32 @llvm.abs.i32(i32 [[A]], i1 true)
+; CHECK-NEXT: [[RETVAL_0:%.*]] = select i1 [[CMP]], i32 [[A]], i32 [[COND]]
+; CHECK-NEXT: ret i32 [[RETVAL_0]]
+;
+entry:
+ %conv = zext i8 %b to i32
+ %cmp = icmp eq i32 %a, %conv
+ %cond = tail call i32 @llvm.abs.i32(i32 %a, i1 true)
+ %retval.0 = select i1 %cmp, i32 %a, i32 %cond
+ ret i32 %retval.0
+}
+
+; X == Positive ? Positive : ABS(X) -> ABS(X)
+define i32 @feq_2(i32 noundef %a, i8 noundef zeroext %b) {
+; CHECK-LABEL: @feq_2(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[CONV:%.*]] = zext i8 [[B:%.*]] to i32
+; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[A:%.*]], [[CONV]]
+; CHECK-NEXT: [[COND:%.*]] = tail call i32 @llvm.abs.i32(i32 [[A]], i1 true)
+; CHECK-NEXT: [[RETVAL_0:%.*]] = select i1 [[CMP]], i32 [[CONV]], i32 [[COND]]
+; CHECK-NEXT: ret i32 [[RETVAL_0]]
+;
+entry:
+ %conv = zext i8 %b to i32
+ %cmp = icmp eq i32 %a, %conv
+ %cond = tail call i32 @llvm.abs.i32(i32 %a, i1 true)
+ %retval.0 = select i1 %cmp, i32 %conv, i32 %cond
+ ret i32 %retval.0
+}
+
+; X > Positive ? X : ABS(X) -> ABS(X)
+define i32 @fgt_1(i32 noundef %a, i8 noundef zeroext %b) {
+; CHECK-LABEL: @fgt_1(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[CONV:%.*]] = zext i8 [[B:%.*]] to i32
+; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i32 [[A:%.*]], [[CONV]]
+; CHECK-NEXT: [[COND:%.*]] = tail call i32 @llvm.abs.i32(i32 [[A]], i1 true)
+; CHECK-NEXT: [[RETVAL_0:%.*]] = select i1 [[CMP]], i32 [[A]], i32 [[COND]]
+; CHECK-NEXT: ret i32 [[RETVAL_0]]
+;
+entry:
+ %conv = zext i8 %b to i32
+ %cmp = icmp sgt i32 %a, %conv
+ %cond = tail call i32 @llvm.abs.i32(i32 %a, i1 true)
+ %retval.0 = select i1 %cmp, i32 %a, i32 %cond
+ ret i32 %retval.0
+}
+
+; X >= Positive ? X : ABS(X) -> ABS(X)
+define i32 @fge_1(i32 noundef %a, i8 noundef zeroext %b) {
+; CHECK-LABEL: @fge_1(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[CONV:%.*]] = zext i8 [[B:%.*]] to i32
+; CHECK-NEXT: [[CMP_NOT:%.*]] = icmp slt i32 [[A:%.*]], [[CONV]]
+; CHECK-NEXT: [[COND:%.*]] = tail call i32 @llvm.abs.i32(i32 [[A]], i1 true)
+; CHECK-NEXT: [[RETVAL_0:%.*]] = select i1 [[CMP_NOT]], i32 [[COND]], i32 [[A]]
+; CHECK-NEXT: ret i32 [[RETVAL_0]]
+;
+entry:
+ %conv = zext i8 %b to i32
+ %cmp = icmp sge i32 %a, %conv
+ %cond = tail call i32 @llvm.abs.i32(i32 %a, i1 true)
+ %retval.0 = select i1 %cmp, i32 %a, i32 %cond
+ ret i32 %retval.0
+}
+
+; Test with constant positive value
+define i32 @constant_positive(i32 noundef %a) {
+; CHECK-LABEL: @constant_positive(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[COND:%.*]] = tail call i32 @llvm.abs.i32(i32 [[A:%.*]], i1 true)
+; CHECK-NEXT: ret i32 [[COND]]
+;
+entry:
+ %cmp = icmp eq i32 %a, 42
+ %cond = tail call i32 @llvm.abs.i32(i32 %a, i1 true)
+ %retval.0 = select i1 %cmp, i32 %a, i32 %cond
+ ret i32 %retval.0
+}
+
+; Negative test: Should not optimize when comparing with negative value
+define i32 @negative_value(i32 noundef %a) {
+; CHECK-LABEL: @negative_value(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[A:%.*]], -42
+; CHECK-NEXT: [[COND:%.*]] = tail call i32 @llvm.abs.i32(i32 [[A]], i1 true)
+; CHECK-NEXT: [[RETVAL_0:%.*]] = select i1 [[CMP]], i32 -42, i32 [[COND]]
+; CHECK-NEXT: ret i32 [[RETVAL_0]]
+;
+entry:
+ %cmp = icmp eq i32 %a, -42
+ %cond = tail call i32 @llvm.abs.i32(i32 %a, i1 true)
+ %retval.0 = select i1 %cmp, i32 %a, i32 %cond
+ ret i32 %retval.0
+}
+
+; Negative test: Should not optimize when true value is not X or positive
+define i32 @wrong_true_value(i32 noundef %a, i32 noundef %c) {
+; CHECK-LABEL: @wrong_true_value(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[A:%.*]], 42
+; CHECK-NEXT: [[COND:%.*]] = tail call i32 @llvm.abs.i32(i32 [[A]], i1 true)
+; CHECK-NEXT: [[RETVAL_0:%.*]] = select i1 [[CMP]], i32 [[C:%.*]], i32 [[COND]]
+; CHECK-NEXT: ret i32 [[RETVAL_0]]
+;
+entry:
+ %cmp = icmp eq i32 %a, 42
+ %cond = tail call i32 @llvm.abs.i32(i32 %a, i1 true)
+ %retval.0 = select i1 %cmp, i32 %c, i32 %cond
+ ret i32 %retval.0
+}
+
+; Negative test: Should not optimize when false value is not abs(X)
+define i32 @wrong_false_value(i32 noundef %a, i32 noundef %c) {
+; CHECK-LABEL: @wrong_false_value(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[A:%.*]], 42
+; CHECK-NEXT: [[RETVAL_0:%.*]] = select i1 [[CMP]], i32 42, i32 [[C:%.*]]
+; CHECK-NEXT: ret i32 [[RETVAL_0]]
+;
+entry:
+ %cmp = icmp eq i32 %a, 42
+ %retval.0 = select i1 %cmp, i32 %a, i32 %c
+ ret i32 %retval.0
+}
+
+; Test with different types (i64)
+define i64 @i64_test(i64 noundef %a, i32 noundef zeroext %b) {
+; CHECK-LABEL: @i64_test(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[CONV:%.*]] = zext i32 [[B:%.*]] to i64
+; CHECK-NEXT: [[CMP:%.*]] = icmp eq i64 [[A:%.*]], [[CONV]]
+; CHECK-NEXT: [[COND:%.*]] = tail call i64 @llvm.abs.i64(i64 [[A]], i1 true)
+; CHECK-NEXT: [[RETVAL_0:%.*]] = select i1 [[CMP]], i64 [[A]], i64 [[COND]]
+; CHECK-NEXT: ret i64 [[RETVAL_0]]
+;
+entry:
+ %conv = zext i32 %b to i64
+ %cmp = icmp eq i64 %a, %conv
+ %cond = tail call i64 @llvm.abs.i64(i64 %a, i1 true)
+ %retval.0 = select i1 %cmp, i64 %a, i64 %cond
+ ret i64 %retval.0
+}
+
+; Test with swapped comparison operands
+define i32 @swapped_comparison(i32 noundef %a, i8 noundef zeroext %b) {
+; CHECK-LABEL: @swapped_comparison(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[CONV:%.*]] = zext i8 [[B:%.*]] to i32
+; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[A:%.*]], [[CONV]]
+; CHECK-NEXT: [[COND:%.*]] = tail call i32 @llvm.abs.i32(i32 [[A]], i1 true)
+; CHECK-NEXT: [[RETVAL_0:%.*]] = select i1 [[CMP]], i32 [[A]], i32 [[COND]]
+; CHECK-NEXT: ret i32 [[RETVAL_0]]
+;
+entry:
+ %conv = zext i8 %b to i32
+ %cmp = icmp eq i32 %conv, %a
+ %cond = tail call i32 @llvm.abs.i32(i32 %a, i1 true)
+ %retval.0 = select i1 %cmp, i32 %a, i32 %cond
+ ret i32 %retval.0
+}
>From 12c65a8845bb9bfe886c526f9f22d8fda73fb9b3 Mon Sep 17 00:00:00 2001
From: AZero13 <gfunni234 at gmail.com>
Date: Tue, 9 Sep 2025 09:49:06 -0400
Subject: [PATCH 2/2] [InstCombine] Simplify call to abs if it is known one
side is positive
---
.../InstCombine/InstCombineSelect.cpp | 60 +++++++++++++++++++
.../InstCombine/select-abs-positive.ll | 35 ++++-------
2 files changed, 70 insertions(+), 25 deletions(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
index eb4332fbc0959..c9362892d1bfd 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
@@ -1156,6 +1156,63 @@ static Value *foldAbsDiff(ICmpInst *Cmp, Value *TVal, Value *FVal,
return nullptr;
}
+/// Fold select patterns involving abs intrinsic:
+/// X == Positive ? X : ABS(X) -> ABS(X)
+/// X == Positive ? Positive : ABS(X) -> ABS(X)
+/// X > Positive ? X : ABS(X) -> ABS(X)
+/// X >= Positive ? X : ABS(X) -> ABS(X)
+static Value *foldSelectPositiveAbs(ICmpInst *Cmp, Value *TVal, Value *FVal,
+ InstCombiner::BuilderTy &Builder,
+ InstCombinerImpl &IC) {
+ // Check if false value is abs(X)
+ Value *X;
+ Constant *IntMinIsPoison;
+ if (!match(FVal, m_Intrinsic<Intrinsic::abs>(m_Value(X),
+ m_Constant(IntMinIsPoison))))
+ return nullptr;
+
+ ICmpInst::Predicate Pred = Cmp->getPredicate();
+ Value *CmpLHS = Cmp->getOperand(0);
+ Value *CmpRHS = Cmp->getOperand(1);
+
+ // Normalize so that X is on the LHS of comparison
+ if (CmpLHS != X) {
+ if (CmpRHS == X) {
+ std::swap(CmpLHS, CmpRHS);
+ Pred = ICmpInst::getSwappedPredicate(Pred);
+ } else {
+ return nullptr;
+ }
+ }
+
+ // Check if RHS is non-negative
+ if (!isKnownNonNegative(CmpRHS, IC.getSimplifyQuery()))
+ return nullptr;
+
+ // Handle different patterns
+ switch (Pred) {
+ case ICmpInst::ICMP_EQ:
+ // X == Positive ? X : ABS(X) -> ABS(X)
+ if (TVal == X)
+ return FVal;
+ // X == Positive ? Positive : ABS(X) -> ABS(X)
+ if (TVal == CmpRHS)
+ return FVal;
+ break;
+ case ICmpInst::ICMP_SGT:
+ case ICmpInst::ICMP_SGE:
+ // X > Positive ? X : ABS(X) -> ABS(X)
+ // X >= Positive ? X : ABS(X) -> ABS(X)
+ if (TVal == X)
+ return FVal;
+ break;
+ default:
+ break;
+ }
+
+ return nullptr;
+}
+
/// Fold the following code sequence:
/// \code
/// int a = ctlz(x & -x);
@@ -2068,6 +2125,9 @@ Instruction *InstCombinerImpl::foldSelectInstWithICmp(SelectInst &SI,
if (Value *V = foldAbsDiff(ICI, TrueVal, FalseVal, Builder))
return replaceInstUsesWith(SI, V);
+ if (Value *V = foldSelectPositiveAbs(ICI, TrueVal, FalseVal, Builder, *this))
+ return replaceInstUsesWith(SI, V);
+
if (Value *V = foldSelectWithConstOpToBinOp(ICI, TrueVal, FalseVal))
return replaceInstUsesWith(SI, V);
diff --git a/llvm/test/Transforms/InstCombine/select-abs-positive.ll b/llvm/test/Transforms/InstCombine/select-abs-positive.ll
index 48dd45a4428de..a123e6e3a3272 100644
--- a/llvm/test/Transforms/InstCombine/select-abs-positive.ll
+++ b/llvm/test/Transforms/InstCombine/select-abs-positive.ll
@@ -8,11 +8,8 @@ declare i64 @llvm.abs.i64(i64, i1)
define i32 @feq_1(i32 noundef %a, i8 noundef zeroext %b) {
; CHECK-LABEL: @feq_1(
; CHECK-NEXT: entry:
-; CHECK-NEXT: [[CONV:%.*]] = zext i8 [[B:%.*]] to i32
-; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[A:%.*]], [[CONV]]
-; CHECK-NEXT: [[COND:%.*]] = tail call i32 @llvm.abs.i32(i32 [[A]], i1 true)
-; CHECK-NEXT: [[RETVAL_0:%.*]] = select i1 [[CMP]], i32 [[A]], i32 [[COND]]
-; CHECK-NEXT: ret i32 [[RETVAL_0]]
+; CHECK-NEXT: [[COND:%.*]] = tail call i32 @llvm.abs.i32(i32 [[A:%.*]], i1 true)
+; CHECK-NEXT: ret i32 [[COND]]
;
entry:
%conv = zext i8 %b to i32
@@ -26,11 +23,8 @@ entry:
define i32 @feq_2(i32 noundef %a, i8 noundef zeroext %b) {
; CHECK-LABEL: @feq_2(
; CHECK-NEXT: entry:
-; CHECK-NEXT: [[CONV:%.*]] = zext i8 [[B:%.*]] to i32
-; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[A:%.*]], [[CONV]]
-; CHECK-NEXT: [[COND:%.*]] = tail call i32 @llvm.abs.i32(i32 [[A]], i1 true)
-; CHECK-NEXT: [[RETVAL_0:%.*]] = select i1 [[CMP]], i32 [[CONV]], i32 [[COND]]
-; CHECK-NEXT: ret i32 [[RETVAL_0]]
+; CHECK-NEXT: [[COND:%.*]] = tail call i32 @llvm.abs.i32(i32 [[A:%.*]], i1 true)
+; CHECK-NEXT: ret i32 [[COND]]
;
entry:
%conv = zext i8 %b to i32
@@ -44,11 +38,8 @@ entry:
define i32 @fgt_1(i32 noundef %a, i8 noundef zeroext %b) {
; CHECK-LABEL: @fgt_1(
; CHECK-NEXT: entry:
-; CHECK-NEXT: [[CONV:%.*]] = zext i8 [[B:%.*]] to i32
-; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i32 [[A:%.*]], [[CONV]]
-; CHECK-NEXT: [[COND:%.*]] = tail call i32 @llvm.abs.i32(i32 [[A]], i1 true)
-; CHECK-NEXT: [[RETVAL_0:%.*]] = select i1 [[CMP]], i32 [[A]], i32 [[COND]]
-; CHECK-NEXT: ret i32 [[RETVAL_0]]
+; CHECK-NEXT: [[COND:%.*]] = tail call i32 @llvm.abs.i32(i32 [[A:%.*]], i1 true)
+; CHECK-NEXT: ret i32 [[COND]]
;
entry:
%conv = zext i8 %b to i32
@@ -140,11 +131,8 @@ entry:
define i64 @i64_test(i64 noundef %a, i32 noundef zeroext %b) {
; CHECK-LABEL: @i64_test(
; CHECK-NEXT: entry:
-; CHECK-NEXT: [[CONV:%.*]] = zext i32 [[B:%.*]] to i64
-; CHECK-NEXT: [[CMP:%.*]] = icmp eq i64 [[A:%.*]], [[CONV]]
-; CHECK-NEXT: [[COND:%.*]] = tail call i64 @llvm.abs.i64(i64 [[A]], i1 true)
-; CHECK-NEXT: [[RETVAL_0:%.*]] = select i1 [[CMP]], i64 [[A]], i64 [[COND]]
-; CHECK-NEXT: ret i64 [[RETVAL_0]]
+; CHECK-NEXT: [[COND:%.*]] = tail call i64 @llvm.abs.i64(i64 [[A:%.*]], i1 true)
+; CHECK-NEXT: ret i64 [[COND]]
;
entry:
%conv = zext i32 %b to i64
@@ -158,11 +146,8 @@ entry:
define i32 @swapped_comparison(i32 noundef %a, i8 noundef zeroext %b) {
; CHECK-LABEL: @swapped_comparison(
; CHECK-NEXT: entry:
-; CHECK-NEXT: [[CONV:%.*]] = zext i8 [[B:%.*]] to i32
-; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[A:%.*]], [[CONV]]
-; CHECK-NEXT: [[COND:%.*]] = tail call i32 @llvm.abs.i32(i32 [[A]], i1 true)
-; CHECK-NEXT: [[RETVAL_0:%.*]] = select i1 [[CMP]], i32 [[A]], i32 [[COND]]
-; CHECK-NEXT: ret i32 [[RETVAL_0]]
+; CHECK-NEXT: [[COND:%.*]] = tail call i32 @llvm.abs.i32(i32 [[A:%.*]], i1 true)
+; CHECK-NEXT: ret i32 [[COND]]
;
entry:
%conv = zext i8 %b to i32
More information about the llvm-commits
mailing list