[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