[llvm] [InstCombine] Fold sub+select abs-diff patterns with sext/zext operands (PR #209383)
Kunal Chadha via llvm-commits
llvm-commits at lists.llvm.org
Wed Jul 15 00:10:19 PDT 2026
https://github.com/chadhakunal updated https://github.com/llvm/llvm-project/pull/209383
>From 2de6e3a1d8a242f541acc537c326f00d4d671e6f Mon Sep 17 00:00:00 2001
From: Kunal Chadha <kunachad at qti.qualcomm.com>
Date: Tue, 14 Jul 2026 23:55:00 -0700
Subject: [PATCH 1/2] [InstCombine] test cases for sub+select abs-diff patterns
with sext/zext operands
---
llvm/test/Transforms/InstCombine/abs-1.ll | 276 ++++++++++++++++++++++
1 file changed, 276 insertions(+)
diff --git a/llvm/test/Transforms/InstCombine/abs-1.ll b/llvm/test/Transforms/InstCombine/abs-1.ll
index fd67fc3421498..3a7d32b6fbd0d 100644
--- a/llvm/test/Transforms/InstCombine/abs-1.ll
+++ b/llvm/test/Transforms/InstCombine/abs-1.ll
@@ -989,3 +989,279 @@ define <2 x i32> @abs_unary_shuffle_ops(<2 x i32> %x) {
%r = call <2 x i32> @llvm.abs(<2 x i32> %a, i1 false)
ret <2 x i32> %r
}
+
+; sext + sgt: (A > B) ? (sext(A) - sext(B)) : (sext(B) - sext(A)) --> abs
+define i32 @abs_diff_sext_sgt(i8 %a, i8 %b) {
+; CHECK-LABEL: @abs_diff_sext_sgt(
+; CHECK-NEXT: [[SEXT_A:%.*]] = sext i8 [[A:%.*]] to i32
+; CHECK-NEXT: [[SEXT_B:%.*]] = sext i8 [[B:%.*]] to i32
+; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i8 [[A]], [[B]]
+; CHECK-NEXT: [[SUB_AB:%.*]] = sub nsw i32 [[SEXT_A]], [[SEXT_B]]
+; CHECK-NEXT: [[SUB_BA:%.*]] = sub nsw i32 [[SEXT_B]], [[SEXT_A]]
+; CHECK-NEXT: [[COND:%.*]] = select i1 [[CMP]], i32 [[SUB_AB]], i32 [[SUB_BA]]
+; CHECK-NEXT: ret i32 [[COND]]
+;
+ %sext_a = sext i8 %a to i32
+ %sext_b = sext i8 %b to i32
+ %cmp = icmp sgt i8 %a, %b
+ %sub_ab = sub i32 %sext_a, %sext_b
+ %sub_ba = sub i32 %sext_b, %sext_a
+ %cond = select i1 %cmp, i32 %sub_ab, i32 %sub_ba
+ ret i32 %cond
+}
+
+; zext + ugt: (A >u B) ? (zext(A) - zext(B)) : (zext(B) - zext(A)) --> abs
+define i32 @abs_diff_zext_ugt(i8 %a, i8 %b) {
+; CHECK-LABEL: @abs_diff_zext_ugt(
+; CHECK-NEXT: [[ZEXT_A:%.*]] = zext i8 [[A:%.*]] to i32
+; CHECK-NEXT: [[ZEXT_B:%.*]] = zext i8 [[B:%.*]] to i32
+; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i8 [[A]], [[B]]
+; CHECK-NEXT: [[SUB_AB:%.*]] = sub nsw i32 [[ZEXT_A]], [[ZEXT_B]]
+; CHECK-NEXT: [[SUB_BA:%.*]] = sub nsw i32 [[ZEXT_B]], [[ZEXT_A]]
+; CHECK-NEXT: [[COND:%.*]] = select i1 [[CMP]], i32 [[SUB_AB]], i32 [[SUB_BA]]
+; CHECK-NEXT: ret i32 [[COND]]
+;
+ %zext_a = zext i8 %a to i32
+ %zext_b = zext i8 %b to i32
+ %cmp = icmp ugt i8 %a, %b
+ %sub_ab = sub i32 %zext_a, %zext_b
+ %sub_ba = sub i32 %zext_b, %zext_a
+ %cond = select i1 %cmp, i32 %sub_ab, i32 %sub_ba
+ ret i32 %cond
+}
+
+; vector zext + ugt: (A >u B) ? (zext(A) - zext(B)) : (zext(B) - zext(A)) --> abs
+define <8 x i16> @abs_diff_zext_ugt_vec(<8 x i8> %a, <8 x i8> %b) {
+; CHECK-LABEL: @abs_diff_zext_ugt_vec(
+; CHECK-NEXT: [[ZEXT_A:%.*]] = zext <8 x i8> [[A:%.*]] to <8 x i16>
+; CHECK-NEXT: [[ZEXT_B:%.*]] = zext <8 x i8> [[B:%.*]] to <8 x i16>
+; CHECK-NEXT: [[CMP:%.*]] = icmp ugt <8 x i8> [[A]], [[B]]
+; CHECK-NEXT: [[SUB_AB:%.*]] = sub nsw <8 x i16> [[ZEXT_A]], [[ZEXT_B]]
+; CHECK-NEXT: [[SUB_BA:%.*]] = sub nsw <8 x i16> [[ZEXT_B]], [[ZEXT_A]]
+; CHECK-NEXT: [[COND:%.*]] = select <8 x i1> [[CMP]], <8 x i16> [[SUB_AB]], <8 x i16> [[SUB_BA]]
+; CHECK-NEXT: ret <8 x i16> [[COND]]
+;
+ %zext_a = zext <8 x i8> %a to <8 x i16>
+ %zext_b = zext <8 x i8> %b to <8 x i16>
+ %cmp = icmp ugt <8 x i8> %a, %b
+ %sub_ab = sub <8 x i16> %zext_a, %zext_b
+ %sub_ba = sub <8 x i16> %zext_b, %zext_a
+ %cond = select <8 x i1> %cmp, <8 x i16> %sub_ab, <8 x i16> %sub_ba
+ ret <8 x i16> %cond
+}
+
+; sext + sgt: (A > B) ? (sext(A) - sext(B)) : (0 - (sext(A) - sext(B))) --> abs
+define i32 @abs_diff_sext_sgt_neg(i8 %a, i8 %b) {
+; CHECK-LABEL: @abs_diff_sext_sgt_neg(
+; CHECK-NEXT: [[SEXT_A:%.*]] = sext i8 [[A:%.*]] to i32
+; CHECK-NEXT: [[SEXT_B:%.*]] = sext i8 [[B:%.*]] to i32
+; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i8 [[A]], [[B]]
+; CHECK-NEXT: [[SUB_AB:%.*]] = sub nsw i32 [[SEXT_A]], [[SEXT_B]]
+; CHECK-NEXT: [[NEG:%.*]] = sub nsw i32 0, [[SUB_AB]]
+; CHECK-NEXT: [[COND:%.*]] = select i1 [[CMP]], i32 [[SUB_AB]], i32 [[NEG]]
+; CHECK-NEXT: ret i32 [[COND]]
+;
+ %sext_a = sext i8 %a to i32
+ %sext_b = sext i8 %b to i32
+ %cmp = icmp sgt i8 %a, %b
+ %sub_ab = sub nsw i32 %sext_a, %sext_b
+ %neg = sub i32 0, %sub_ab
+ %cond = select i1 %cmp, i32 %sub_ab, i32 %neg
+ ret i32 %cond
+}
+
+; zext + ugt: (A >u B) ? (zext(A) - zext(B)) : (0 - (zext(A) - zext(B))) --> abs
+define i32 @abs_diff_zext_ugt_neg(i8 %a, i8 %b) {
+; CHECK-LABEL: @abs_diff_zext_ugt_neg(
+; CHECK-NEXT: [[ZEXT_A:%.*]] = zext i8 [[A:%.*]] to i32
+; CHECK-NEXT: [[ZEXT_B:%.*]] = zext i8 [[B:%.*]] to i32
+; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i8 [[A]], [[B]]
+; CHECK-NEXT: [[SUB_AB:%.*]] = sub nsw i32 [[ZEXT_A]], [[ZEXT_B]]
+; CHECK-NEXT: [[NEG:%.*]] = sub nsw i32 0, [[SUB_AB]]
+; CHECK-NEXT: [[COND:%.*]] = select i1 [[CMP]], i32 [[SUB_AB]], i32 [[NEG]]
+; CHECK-NEXT: ret i32 [[COND]]
+;
+ %zext_a = zext i8 %a to i32
+ %zext_b = zext i8 %b to i32
+ %cmp = icmp ugt i8 %a, %b
+ %sub_ab = sub nsw i32 %zext_a, %zext_b
+ %neg = sub i32 0, %sub_ab
+ %cond = select i1 %cmp, i32 %sub_ab, i32 %neg
+ ret i32 %cond
+}
+
+; sext + slt: (A < B) ? (0 - (sext(A) - sext(B))) : (sext(A) - sext(B)) --> abs
+define i32 @abs_diff_sext_slt_neg(i8 %a, i8 %b) {
+; CHECK-LABEL: @abs_diff_sext_slt_neg(
+; CHECK-NEXT: [[SEXT_A:%.*]] = sext i8 [[A:%.*]] to i32
+; CHECK-NEXT: [[SEXT_B:%.*]] = sext i8 [[B:%.*]] to i32
+; CHECK-NEXT: [[CMP:%.*]] = icmp slt i8 [[A]], [[B]]
+; CHECK-NEXT: [[SUB_AB:%.*]] = sub nsw i32 [[SEXT_A]], [[SEXT_B]]
+; CHECK-NEXT: [[NEG:%.*]] = sub nsw i32 0, [[SUB_AB]]
+; CHECK-NEXT: [[COND:%.*]] = select i1 [[CMP]], i32 [[NEG]], i32 [[SUB_AB]]
+; CHECK-NEXT: ret i32 [[COND]]
+;
+ %sext_a = sext i8 %a to i32
+ %sext_b = sext i8 %b to i32
+ %cmp = icmp slt i8 %a, %b
+ %sub_ab = sub nsw i32 %sext_a, %sext_b
+ %neg = sub i32 0, %sub_ab
+ %cond = select i1 %cmp, i32 %neg, i32 %sub_ab
+ ret i32 %cond
+}
+
+; zext + ult: (A <u B) ? (0 - (zext(A) - zext(B))) : (zext(A) - zext(B)) --> abs
+define i32 @abs_diff_zext_ult_neg(i8 %a, i8 %b) {
+; CHECK-LABEL: @abs_diff_zext_ult_neg(
+; CHECK-NEXT: [[ZEXT_A:%.*]] = zext i8 [[A:%.*]] to i32
+; CHECK-NEXT: [[ZEXT_B:%.*]] = zext i8 [[B:%.*]] to i32
+; CHECK-NEXT: [[CMP:%.*]] = icmp ult i8 [[A]], [[B]]
+; CHECK-NEXT: [[SUB_AB:%.*]] = sub nsw i32 [[ZEXT_A]], [[ZEXT_B]]
+; CHECK-NEXT: [[NEG:%.*]] = sub nsw i32 0, [[SUB_AB]]
+; CHECK-NEXT: [[COND:%.*]] = select i1 [[CMP]], i32 [[NEG]], i32 [[SUB_AB]]
+; CHECK-NEXT: ret i32 [[COND]]
+;
+ %zext_a = zext i8 %a to i32
+ %zext_b = zext i8 %b to i32
+ %cmp = icmp ult i8 %a, %b
+ %sub_ab = sub nsw i32 %zext_a, %zext_b
+ %neg = sub i32 0, %sub_ab
+ %cond = select i1 %cmp, i32 %neg, i32 %sub_ab
+ ret i32 %cond
+}
+
+; sext + sgt: (A > B) ? (0 - (sext(B) - sext(A))) : (sext(B) - sext(A)) --> abs
+define i32 @abs_diff_sext_sgt_neg_ba(i8 %a, i8 %b) {
+; CHECK-LABEL: @abs_diff_sext_sgt_neg_ba(
+; CHECK-NEXT: [[SEXT_A:%.*]] = sext i8 [[A:%.*]] to i32
+; CHECK-NEXT: [[SEXT_B:%.*]] = sext i8 [[B:%.*]] to i32
+; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i8 [[A]], [[B]]
+; CHECK-NEXT: [[SUB_BA:%.*]] = sub nsw i32 [[SEXT_B]], [[SEXT_A]]
+; CHECK-NEXT: [[NEG:%.*]] = sub nsw i32 0, [[SUB_BA]]
+; CHECK-NEXT: [[COND:%.*]] = select i1 [[CMP]], i32 [[NEG]], i32 [[SUB_BA]]
+; CHECK-NEXT: ret i32 [[COND]]
+;
+ %sext_a = sext i8 %a to i32
+ %sext_b = sext i8 %b to i32
+ %cmp = icmp sgt i8 %a, %b
+ %sub_ba = sub nsw i32 %sext_b, %sext_a
+ %neg = sub i32 0, %sub_ba
+ %cond = select i1 %cmp, i32 %neg, i32 %sub_ba
+ ret i32 %cond
+}
+
+; zext + ugt: (A >u B) ? (0 - (zext(B) - zext(A))) : (zext(B) - zext(A)) --> abs
+define i32 @abs_diff_zext_ugt_neg_ba(i8 %a, i8 %b) {
+; CHECK-LABEL: @abs_diff_zext_ugt_neg_ba(
+; CHECK-NEXT: [[ZEXT_A:%.*]] = zext i8 [[A:%.*]] to i32
+; CHECK-NEXT: [[ZEXT_B:%.*]] = zext i8 [[B:%.*]] to i32
+; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i8 [[A]], [[B]]
+; CHECK-NEXT: [[SUB_BA:%.*]] = sub nsw i32 [[ZEXT_B]], [[ZEXT_A]]
+; CHECK-NEXT: [[NEG:%.*]] = sub nsw i32 0, [[SUB_BA]]
+; CHECK-NEXT: [[COND:%.*]] = select i1 [[CMP]], i32 [[NEG]], i32 [[SUB_BA]]
+; CHECK-NEXT: ret i32 [[COND]]
+;
+ %zext_a = zext i8 %a to i32
+ %zext_b = zext i8 %b to i32
+ %cmp = icmp ugt i8 %a, %b
+ %sub_ba = sub nsw i32 %zext_b, %zext_a
+ %neg = sub i32 0, %sub_ba
+ %cond = select i1 %cmp, i32 %neg, i32 %sub_ba
+ ret i32 %cond
+}
+
+; sext + slt: (A < B) ? (sext(B) - sext(A)) : (0 - (sext(B) - sext(A))) --> abs
+define i32 @abs_diff_sext_slt_ba(i8 %a, i8 %b) {
+; CHECK-LABEL: @abs_diff_sext_slt_ba(
+; CHECK-NEXT: [[SEXT_A:%.*]] = sext i8 [[A:%.*]] to i32
+; CHECK-NEXT: [[SEXT_B:%.*]] = sext i8 [[B:%.*]] to i32
+; CHECK-NEXT: [[CMP:%.*]] = icmp slt i8 [[A]], [[B]]
+; CHECK-NEXT: [[SUB_BA:%.*]] = sub nsw i32 [[SEXT_B]], [[SEXT_A]]
+; CHECK-NEXT: [[NEG:%.*]] = sub nsw i32 0, [[SUB_BA]]
+; CHECK-NEXT: [[COND:%.*]] = select i1 [[CMP]], i32 [[SUB_BA]], i32 [[NEG]]
+; CHECK-NEXT: ret i32 [[COND]]
+;
+ %sext_a = sext i8 %a to i32
+ %sext_b = sext i8 %b to i32
+ %cmp = icmp slt i8 %a, %b
+ %sub_ba = sub nsw i32 %sext_b, %sext_a
+ %neg = sub i32 0, %sub_ba
+ %cond = select i1 %cmp, i32 %sub_ba, i32 %neg
+ ret i32 %cond
+}
+
+; zext + ult: (A <u B) ? (zext(B) - zext(A)) : (0 - (zext(B) - zext(A))) --> abs
+define i32 @abs_diff_zext_ult_ba(i8 %a, i8 %b) {
+; CHECK-LABEL: @abs_diff_zext_ult_ba(
+; CHECK-NEXT: [[ZEXT_A:%.*]] = zext i8 [[A:%.*]] to i32
+; CHECK-NEXT: [[ZEXT_B:%.*]] = zext i8 [[B:%.*]] to i32
+; CHECK-NEXT: [[CMP:%.*]] = icmp ult i8 [[A]], [[B]]
+; CHECK-NEXT: [[SUB_BA:%.*]] = sub nsw i32 [[ZEXT_B]], [[ZEXT_A]]
+; CHECK-NEXT: [[NEG:%.*]] = sub nsw i32 0, [[SUB_BA]]
+; CHECK-NEXT: [[COND:%.*]] = select i1 [[CMP]], i32 [[SUB_BA]], i32 [[NEG]]
+; CHECK-NEXT: ret i32 [[COND]]
+;
+ %zext_a = zext i8 %a to i32
+ %zext_b = zext i8 %b to i32
+ %cmp = icmp ult i8 %a, %b
+ %sub_ba = sub nsw i32 %zext_b, %zext_a
+ %neg = sub i32 0, %sub_ba
+ %cond = select i1 %cmp, i32 %sub_ba, i32 %neg
+ ret i32 %cond
+}
+
+; negative test - ugt with bare operands (no zext) - should not fold to abs
+define i32 @abs_diff_ugt_no_zext(i32 %a, i32 %b) {
+; CHECK-LABEL: @abs_diff_ugt_no_zext(
+; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i32 [[A:%.*]], [[B:%.*]]
+; CHECK-NEXT: [[SUB_AB:%.*]] = sub nsw i32 [[A]], [[B]]
+; CHECK-NEXT: [[SUB_BA:%.*]] = sub nsw i32 [[B]], [[A]]
+; CHECK-NEXT: [[COND:%.*]] = select i1 [[CMP]], i32 [[SUB_AB]], i32 [[SUB_BA]]
+; CHECK-NEXT: ret i32 [[COND]]
+;
+ %cmp = icmp ugt i32 %a, %b
+ %sub_ab = sub nsw i32 %a, %b
+ %sub_ba = sub nsw i32 %b, %a
+ %cond = select i1 %cmp, i32 %sub_ab, i32 %sub_ba
+ ret i32 %cond
+}
+
+; negative test - sext + ugt (mismatched pairing) - should not fold to abs
+define i32 @abs_diff_sext_ugt_mismatch(i8 %a, i8 %b) {
+; CHECK-LABEL: @abs_diff_sext_ugt_mismatch(
+; CHECK-NEXT: [[SEXT_A:%.*]] = sext i8 [[A:%.*]] to i32
+; CHECK-NEXT: [[SEXT_B:%.*]] = sext i8 [[B:%.*]] to i32
+; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i8 [[A]], [[B]]
+; CHECK-NEXT: [[SUB_AB:%.*]] = sub nsw i32 [[SEXT_A]], [[SEXT_B]]
+; CHECK-NEXT: [[SUB_BA:%.*]] = sub nsw i32 [[SEXT_B]], [[SEXT_A]]
+; CHECK-NEXT: [[COND:%.*]] = select i1 [[CMP]], i32 [[SUB_AB]], i32 [[SUB_BA]]
+; CHECK-NEXT: ret i32 [[COND]]
+;
+ %sext_a = sext i8 %a to i32
+ %sext_b = sext i8 %b to i32
+ %cmp = icmp ugt i8 %a, %b
+ %sub_ab = sub nsw i32 %sext_a, %sext_b
+ %sub_ba = sub nsw i32 %sext_b, %sext_a
+ %cond = select i1 %cmp, i32 %sub_ab, i32 %sub_ba
+ ret i32 %cond
+}
+
+; negative test - zext + sgt (mismatched pairing) - should not fold to abs
+define i32 @abs_diff_zext_sgt_mismatch(i8 %a, i8 %b) {
+; CHECK-LABEL: @abs_diff_zext_sgt_mismatch(
+; CHECK-NEXT: [[ZEXT_A:%.*]] = zext i8 [[A:%.*]] to i32
+; CHECK-NEXT: [[ZEXT_B:%.*]] = zext i8 [[B:%.*]] to i32
+; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i8 [[A]], [[B]]
+; CHECK-NEXT: [[SUB_AB:%.*]] = sub nsw i32 [[ZEXT_A]], [[ZEXT_B]]
+; CHECK-NEXT: [[SUB_BA:%.*]] = sub nsw i32 [[ZEXT_B]], [[ZEXT_A]]
+; CHECK-NEXT: [[COND:%.*]] = select i1 [[CMP]], i32 [[SUB_AB]], i32 [[SUB_BA]]
+; CHECK-NEXT: ret i32 [[COND]]
+;
+ %zext_a = zext i8 %a to i32
+ %zext_b = zext i8 %b to i32
+ %cmp = icmp sgt i8 %a, %b
+ %sub_ab = sub nsw i32 %zext_a, %zext_b
+ %sub_ba = sub nsw i32 %zext_b, %zext_a
+ %cond = select i1 %cmp, i32 %sub_ab, i32 %sub_ba
+ ret i32 %cond
+}
>From c89a4b3ec84b928d106b57be3db95c57d1d9f679 Mon Sep 17 00:00:00 2001
From: Kunal Chadha <kunachad at qti.qualcomm.com>
Date: Wed, 15 Jul 2026 00:08:13 -0700
Subject: [PATCH 2/2] [InstCombine] Fold sub+select abs-diff patterns with
sext/zext operands to sub+abs
---
.../InstCombine/InstCombineSelect.cpp | 95 ++++++++++++++++++-
llvm/test/Transforms/InstCombine/abs-1.ll | 44 +++------
2 files changed, 101 insertions(+), 38 deletions(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
index f107c15304d9f..b692dd9d71fea 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
@@ -1423,7 +1423,9 @@ static Value *foldAbsDiff(ICmpInst *Cmp, Value *TVal, Value *FVal,
Value *B = Cmp->getOperand(1);
// Normalize "A - B" as the true value of the select.
- if (match(FI, m_Sub(m_Specific(A), m_Specific(B)))) {
+ if (match(FI, m_Sub(m_Specific(A), m_Specific(B))) ||
+ match(FI, m_Sub(m_SExt(m_Specific(A)), m_SExt(m_Specific(B)))) ||
+ match(FI, m_Sub(m_ZExt(m_Specific(A)), m_ZExt(m_Specific(B))))) {
std::swap(FI, TI);
Pred = ICmpInst::getSwappedPredicate(Pred);
}
@@ -1447,33 +1449,116 @@ static Value *foldAbsDiff(ICmpInst *Cmp, Value *TVal, Value *FVal,
return Builder.CreateBinaryIntrinsic(Intrinsic::abs, TI, Builder.getTrue());
}
+ // With any pair of sext'd subtracts from a common narrower type:
+ // (A > B) ? (sext(A) - sext(B)) : (sext(B) - sext(A))
+ // --> abs(sext(A) - sext(B))
+ // sext bounds the difference to [-2^n, 2^n-1] in the wider type,
+ // so nsw always holds structurally - no flag check needed.
+ if (Pred == CmpInst::ICMP_SGT &&
+ match(TI, m_Sub(m_SExt(m_Specific(A)), m_SExt(m_Specific(B)))) &&
+ match(FI, m_Sub(m_SExt(m_Specific(B)), m_SExt(m_Specific(A))))) {
+ TI->setHasNoUnsignedWrap(false);
+ TI->setHasNoSignedWrap(true);
+ return Builder.CreateBinaryIntrinsic(Intrinsic::abs, TI, Builder.getTrue());
+ }
+
+ // With any pair of zext'd subtracts from a common narrower type,
+ // paired with an unsigned compare:
+ // (A >u B) ? (zext(A) - zext(B)) : (zext(B) - zext(A))
+ // --> abs(zext(A) - zext(B))
+ // zext bounds the difference to [-(2^n-1), 2^n-1] in the wider type,
+ // so nsw always holds structurally - no flag check needed.
+ if (Pred == CmpInst::ICMP_UGT &&
+ match(TI, m_Sub(m_ZExt(m_Specific(A)), m_ZExt(m_Specific(B)))) &&
+ match(FI, m_Sub(m_ZExt(m_Specific(B)), m_ZExt(m_Specific(A))))) {
+ TI->setHasNoUnsignedWrap(false);
+ TI->setHasNoSignedWrap(true);
+ return Builder.CreateBinaryIntrinsic(Intrinsic::abs, TI, Builder.getTrue());
+ }
+
// Match: (A > B) ? (A - B) : (0 - (A - B)) --> abs(A - B)
+ // Also handles sext'd operands:
+ // (A > B) ? (sext(A) - sext(B)) : (0 - (sext(A) - sext(B)))
+ // --> abs(sext(A) - sext(B))
if (Pred == CmpInst::ICMP_SGT &&
- match(TI, m_NSWSub(m_Specific(A), m_Specific(B))) &&
+ (match(TI, m_NSWSub(m_Specific(A), m_Specific(B))) ||
+ match(TI, m_Sub(m_SExt(m_Specific(A)), m_SExt(m_Specific(B))))) &&
+ match(FI, m_Neg(m_Specific(TI)))) {
+ return Builder.CreateBinaryIntrinsic(Intrinsic::abs, TI,
+ Builder.getFalse());
+ }
+
+ // Match: (A > B) ? (zext(A) - zext(B)) : (0 - (zext(A) - zext(B)))
+ // --> abs(zext(A) - zext(B))
+ // (A, B zero-extended from a common narrower type, unsigned compare)
+ if (Pred == CmpInst::ICMP_UGT &&
+ match(TI, m_Sub(m_ZExt(m_Specific(A)), m_ZExt(m_Specific(B)))) &&
match(FI, m_Neg(m_Specific(TI)))) {
return Builder.CreateBinaryIntrinsic(Intrinsic::abs, TI,
Builder.getFalse());
}
// Match: (A < B) ? (0 - (A - B)) : (A - B) --> abs(A - B)
+ // Also handles sext'd operands:
+ // (A < B) ? (0 - (sext(A) - sext(B))) : (sext(A) - sext(B))
+ // --> abs(sext(A) - sext(B))
if (Pred == CmpInst::ICMP_SLT &&
- match(FI, m_NSWSub(m_Specific(A), m_Specific(B))) &&
+ (match(FI, m_NSWSub(m_Specific(A), m_Specific(B))) ||
+ match(FI, m_Sub(m_SExt(m_Specific(A)), m_SExt(m_Specific(B))))) &&
+ match(TI, m_Neg(m_Specific(FI)))) {
+ return Builder.CreateBinaryIntrinsic(Intrinsic::abs, FI,
+ Builder.getFalse());
+ }
+
+ // Match: (A < B) ? (0 - (zext(A) - zext(B))) : (zext(A) - zext(B))
+ // --> abs(zext(A) - zext(B))
+ // (A, B zero-extended from a common narrower type, unsigned compare)
+ if (Pred == CmpInst::ICMP_ULT &&
+ match(FI, m_Sub(m_ZExt(m_Specific(A)), m_ZExt(m_Specific(B)))) &&
match(TI, m_Neg(m_Specific(FI)))) {
return Builder.CreateBinaryIntrinsic(Intrinsic::abs, FI,
Builder.getFalse());
}
// Match: (A > B) ? (0 - (B - A)) : (B - A) --> abs(B - A)
+ // Also handles sext'd operands:
+ // (A > B) ? (0 - (sext(B) - sext(A))) : (sext(B) - sext(A))
+ // --> abs(sext(B) - sext(A))
if (Pred == CmpInst::ICMP_SGT &&
- match(FI, m_NSWSub(m_Specific(B), m_Specific(A))) &&
+ (match(FI, m_NSWSub(m_Specific(B), m_Specific(A))) ||
+ match(FI, m_Sub(m_SExt(m_Specific(B)), m_SExt(m_Specific(A))))) &&
+ match(TI, m_Neg(m_Specific(FI)))) {
+ return Builder.CreateBinaryIntrinsic(Intrinsic::abs, FI,
+ Builder.getFalse());
+ }
+
+ // Match: (A > B) ? (0 - (zext(B) - zext(A))) : (zext(B) - zext(A))
+ // --> abs(zext(B) - zext(A))
+ // (A, B zero-extended from a common narrower type, unsigned compare)
+ if (Pred == CmpInst::ICMP_UGT &&
+ match(FI, m_Sub(m_ZExt(m_Specific(B)), m_ZExt(m_Specific(A)))) &&
match(TI, m_Neg(m_Specific(FI)))) {
return Builder.CreateBinaryIntrinsic(Intrinsic::abs, FI,
Builder.getFalse());
}
// Match: (A < B) ? (B - A) : (0 - (B - A)) --> abs(B - A)
+ // Also handles sext'd operands:
+ // (A < B) ? (sext(B) - sext(A)) : (0 - (sext(B) - sext(A)))
+ // --> abs(sext(B) - sext(A))
if (Pred == CmpInst::ICMP_SLT &&
- match(TI, m_NSWSub(m_Specific(B), m_Specific(A))) &&
+ (match(TI, m_NSWSub(m_Specific(B), m_Specific(A))) ||
+ match(TI, m_Sub(m_SExt(m_Specific(B)), m_SExt(m_Specific(A))))) &&
+ match(FI, m_Neg(m_Specific(TI)))) {
+ return Builder.CreateBinaryIntrinsic(Intrinsic::abs, TI,
+ Builder.getFalse());
+ }
+
+ // Match: (A < B) ? (zext(B) - zext(A)) : (0 - (zext(B) - zext(A)))
+ // --> abs(zext(B) - zext(A))
+ // (A, B zero-extended from a common narrower type, unsigned compare)
+ if (Pred == CmpInst::ICMP_ULT &&
+ match(TI, m_Sub(m_ZExt(m_Specific(B)), m_ZExt(m_Specific(A)))) &&
match(FI, m_Neg(m_Specific(TI)))) {
return Builder.CreateBinaryIntrinsic(Intrinsic::abs, TI,
Builder.getFalse());
diff --git a/llvm/test/Transforms/InstCombine/abs-1.ll b/llvm/test/Transforms/InstCombine/abs-1.ll
index 3a7d32b6fbd0d..f75fea14a6659 100644
--- a/llvm/test/Transforms/InstCombine/abs-1.ll
+++ b/llvm/test/Transforms/InstCombine/abs-1.ll
@@ -995,10 +995,8 @@ define i32 @abs_diff_sext_sgt(i8 %a, i8 %b) {
; CHECK-LABEL: @abs_diff_sext_sgt(
; CHECK-NEXT: [[SEXT_A:%.*]] = sext i8 [[A:%.*]] to i32
; CHECK-NEXT: [[SEXT_B:%.*]] = sext i8 [[B:%.*]] to i32
-; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i8 [[A]], [[B]]
; CHECK-NEXT: [[SUB_AB:%.*]] = sub nsw i32 [[SEXT_A]], [[SEXT_B]]
-; CHECK-NEXT: [[SUB_BA:%.*]] = sub nsw i32 [[SEXT_B]], [[SEXT_A]]
-; CHECK-NEXT: [[COND:%.*]] = select i1 [[CMP]], i32 [[SUB_AB]], i32 [[SUB_BA]]
+; CHECK-NEXT: [[COND:%.*]] = call i32 @llvm.abs.i32(i32 [[SUB_AB]], i1 true)
; CHECK-NEXT: ret i32 [[COND]]
;
%sext_a = sext i8 %a to i32
@@ -1015,10 +1013,8 @@ define i32 @abs_diff_zext_ugt(i8 %a, i8 %b) {
; CHECK-LABEL: @abs_diff_zext_ugt(
; CHECK-NEXT: [[ZEXT_A:%.*]] = zext i8 [[A:%.*]] to i32
; CHECK-NEXT: [[ZEXT_B:%.*]] = zext i8 [[B:%.*]] to i32
-; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i8 [[A]], [[B]]
; CHECK-NEXT: [[SUB_AB:%.*]] = sub nsw i32 [[ZEXT_A]], [[ZEXT_B]]
-; CHECK-NEXT: [[SUB_BA:%.*]] = sub nsw i32 [[ZEXT_B]], [[ZEXT_A]]
-; CHECK-NEXT: [[COND:%.*]] = select i1 [[CMP]], i32 [[SUB_AB]], i32 [[SUB_BA]]
+; CHECK-NEXT: [[COND:%.*]] = call i32 @llvm.abs.i32(i32 [[SUB_AB]], i1 true)
; CHECK-NEXT: ret i32 [[COND]]
;
%zext_a = zext i8 %a to i32
@@ -1035,10 +1031,8 @@ define <8 x i16> @abs_diff_zext_ugt_vec(<8 x i8> %a, <8 x i8> %b) {
; CHECK-LABEL: @abs_diff_zext_ugt_vec(
; CHECK-NEXT: [[ZEXT_A:%.*]] = zext <8 x i8> [[A:%.*]] to <8 x i16>
; CHECK-NEXT: [[ZEXT_B:%.*]] = zext <8 x i8> [[B:%.*]] to <8 x i16>
-; CHECK-NEXT: [[CMP:%.*]] = icmp ugt <8 x i8> [[A]], [[B]]
; CHECK-NEXT: [[SUB_AB:%.*]] = sub nsw <8 x i16> [[ZEXT_A]], [[ZEXT_B]]
-; CHECK-NEXT: [[SUB_BA:%.*]] = sub nsw <8 x i16> [[ZEXT_B]], [[ZEXT_A]]
-; CHECK-NEXT: [[COND:%.*]] = select <8 x i1> [[CMP]], <8 x i16> [[SUB_AB]], <8 x i16> [[SUB_BA]]
+; CHECK-NEXT: [[COND:%.*]] = call <8 x i16> @llvm.abs.v8i16(<8 x i16> [[SUB_AB]], i1 true)
; CHECK-NEXT: ret <8 x i16> [[COND]]
;
%zext_a = zext <8 x i8> %a to <8 x i16>
@@ -1055,10 +1049,8 @@ define i32 @abs_diff_sext_sgt_neg(i8 %a, i8 %b) {
; CHECK-LABEL: @abs_diff_sext_sgt_neg(
; CHECK-NEXT: [[SEXT_A:%.*]] = sext i8 [[A:%.*]] to i32
; CHECK-NEXT: [[SEXT_B:%.*]] = sext i8 [[B:%.*]] to i32
-; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i8 [[A]], [[B]]
; CHECK-NEXT: [[SUB_AB:%.*]] = sub nsw i32 [[SEXT_A]], [[SEXT_B]]
-; CHECK-NEXT: [[NEG:%.*]] = sub nsw i32 0, [[SUB_AB]]
-; CHECK-NEXT: [[COND:%.*]] = select i1 [[CMP]], i32 [[SUB_AB]], i32 [[NEG]]
+; CHECK-NEXT: [[COND:%.*]] = call i32 @llvm.abs.i32(i32 [[SUB_AB]], i1 false)
; CHECK-NEXT: ret i32 [[COND]]
;
%sext_a = sext i8 %a to i32
@@ -1075,10 +1067,8 @@ define i32 @abs_diff_zext_ugt_neg(i8 %a, i8 %b) {
; CHECK-LABEL: @abs_diff_zext_ugt_neg(
; CHECK-NEXT: [[ZEXT_A:%.*]] = zext i8 [[A:%.*]] to i32
; CHECK-NEXT: [[ZEXT_B:%.*]] = zext i8 [[B:%.*]] to i32
-; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i8 [[A]], [[B]]
; CHECK-NEXT: [[SUB_AB:%.*]] = sub nsw i32 [[ZEXT_A]], [[ZEXT_B]]
-; CHECK-NEXT: [[NEG:%.*]] = sub nsw i32 0, [[SUB_AB]]
-; CHECK-NEXT: [[COND:%.*]] = select i1 [[CMP]], i32 [[SUB_AB]], i32 [[NEG]]
+; CHECK-NEXT: [[COND:%.*]] = call i32 @llvm.abs.i32(i32 [[SUB_AB]], i1 false)
; CHECK-NEXT: ret i32 [[COND]]
;
%zext_a = zext i8 %a to i32
@@ -1095,10 +1085,8 @@ define i32 @abs_diff_sext_slt_neg(i8 %a, i8 %b) {
; CHECK-LABEL: @abs_diff_sext_slt_neg(
; CHECK-NEXT: [[SEXT_A:%.*]] = sext i8 [[A:%.*]] to i32
; CHECK-NEXT: [[SEXT_B:%.*]] = sext i8 [[B:%.*]] to i32
-; CHECK-NEXT: [[CMP:%.*]] = icmp slt i8 [[A]], [[B]]
; CHECK-NEXT: [[SUB_AB:%.*]] = sub nsw i32 [[SEXT_A]], [[SEXT_B]]
-; CHECK-NEXT: [[NEG:%.*]] = sub nsw i32 0, [[SUB_AB]]
-; CHECK-NEXT: [[COND:%.*]] = select i1 [[CMP]], i32 [[NEG]], i32 [[SUB_AB]]
+; CHECK-NEXT: [[COND:%.*]] = call i32 @llvm.abs.i32(i32 [[SUB_AB]], i1 false)
; CHECK-NEXT: ret i32 [[COND]]
;
%sext_a = sext i8 %a to i32
@@ -1115,10 +1103,8 @@ define i32 @abs_diff_zext_ult_neg(i8 %a, i8 %b) {
; CHECK-LABEL: @abs_diff_zext_ult_neg(
; CHECK-NEXT: [[ZEXT_A:%.*]] = zext i8 [[A:%.*]] to i32
; CHECK-NEXT: [[ZEXT_B:%.*]] = zext i8 [[B:%.*]] to i32
-; CHECK-NEXT: [[CMP:%.*]] = icmp ult i8 [[A]], [[B]]
; CHECK-NEXT: [[SUB_AB:%.*]] = sub nsw i32 [[ZEXT_A]], [[ZEXT_B]]
-; CHECK-NEXT: [[NEG:%.*]] = sub nsw i32 0, [[SUB_AB]]
-; CHECK-NEXT: [[COND:%.*]] = select i1 [[CMP]], i32 [[NEG]], i32 [[SUB_AB]]
+; CHECK-NEXT: [[COND:%.*]] = call i32 @llvm.abs.i32(i32 [[SUB_AB]], i1 false)
; CHECK-NEXT: ret i32 [[COND]]
;
%zext_a = zext i8 %a to i32
@@ -1135,10 +1121,8 @@ define i32 @abs_diff_sext_sgt_neg_ba(i8 %a, i8 %b) {
; CHECK-LABEL: @abs_diff_sext_sgt_neg_ba(
; CHECK-NEXT: [[SEXT_A:%.*]] = sext i8 [[A:%.*]] to i32
; CHECK-NEXT: [[SEXT_B:%.*]] = sext i8 [[B:%.*]] to i32
-; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i8 [[A]], [[B]]
; CHECK-NEXT: [[SUB_BA:%.*]] = sub nsw i32 [[SEXT_B]], [[SEXT_A]]
-; CHECK-NEXT: [[NEG:%.*]] = sub nsw i32 0, [[SUB_BA]]
-; CHECK-NEXT: [[COND:%.*]] = select i1 [[CMP]], i32 [[NEG]], i32 [[SUB_BA]]
+; CHECK-NEXT: [[COND:%.*]] = call i32 @llvm.abs.i32(i32 [[SUB_BA]], i1 false)
; CHECK-NEXT: ret i32 [[COND]]
;
%sext_a = sext i8 %a to i32
@@ -1155,10 +1139,8 @@ define i32 @abs_diff_zext_ugt_neg_ba(i8 %a, i8 %b) {
; CHECK-LABEL: @abs_diff_zext_ugt_neg_ba(
; CHECK-NEXT: [[ZEXT_A:%.*]] = zext i8 [[A:%.*]] to i32
; CHECK-NEXT: [[ZEXT_B:%.*]] = zext i8 [[B:%.*]] to i32
-; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i8 [[A]], [[B]]
; CHECK-NEXT: [[SUB_BA:%.*]] = sub nsw i32 [[ZEXT_B]], [[ZEXT_A]]
-; CHECK-NEXT: [[NEG:%.*]] = sub nsw i32 0, [[SUB_BA]]
-; CHECK-NEXT: [[COND:%.*]] = select i1 [[CMP]], i32 [[NEG]], i32 [[SUB_BA]]
+; CHECK-NEXT: [[COND:%.*]] = call i32 @llvm.abs.i32(i32 [[SUB_BA]], i1 false)
; CHECK-NEXT: ret i32 [[COND]]
;
%zext_a = zext i8 %a to i32
@@ -1175,10 +1157,8 @@ define i32 @abs_diff_sext_slt_ba(i8 %a, i8 %b) {
; CHECK-LABEL: @abs_diff_sext_slt_ba(
; CHECK-NEXT: [[SEXT_A:%.*]] = sext i8 [[A:%.*]] to i32
; CHECK-NEXT: [[SEXT_B:%.*]] = sext i8 [[B:%.*]] to i32
-; CHECK-NEXT: [[CMP:%.*]] = icmp slt i8 [[A]], [[B]]
; CHECK-NEXT: [[SUB_BA:%.*]] = sub nsw i32 [[SEXT_B]], [[SEXT_A]]
-; CHECK-NEXT: [[NEG:%.*]] = sub nsw i32 0, [[SUB_BA]]
-; CHECK-NEXT: [[COND:%.*]] = select i1 [[CMP]], i32 [[SUB_BA]], i32 [[NEG]]
+; CHECK-NEXT: [[COND:%.*]] = call i32 @llvm.abs.i32(i32 [[SUB_BA]], i1 false)
; CHECK-NEXT: ret i32 [[COND]]
;
%sext_a = sext i8 %a to i32
@@ -1195,10 +1175,8 @@ define i32 @abs_diff_zext_ult_ba(i8 %a, i8 %b) {
; CHECK-LABEL: @abs_diff_zext_ult_ba(
; CHECK-NEXT: [[ZEXT_A:%.*]] = zext i8 [[A:%.*]] to i32
; CHECK-NEXT: [[ZEXT_B:%.*]] = zext i8 [[B:%.*]] to i32
-; CHECK-NEXT: [[CMP:%.*]] = icmp ult i8 [[A]], [[B]]
; CHECK-NEXT: [[SUB_BA:%.*]] = sub nsw i32 [[ZEXT_B]], [[ZEXT_A]]
-; CHECK-NEXT: [[NEG:%.*]] = sub nsw i32 0, [[SUB_BA]]
-; CHECK-NEXT: [[COND:%.*]] = select i1 [[CMP]], i32 [[SUB_BA]], i32 [[NEG]]
+; CHECK-NEXT: [[COND:%.*]] = call i32 @llvm.abs.i32(i32 [[SUB_BA]], i1 false)
; CHECK-NEXT: ret i32 [[COND]]
;
%zext_a = zext i8 %a to i32
More information about the llvm-commits
mailing list