diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp index eb4332fbc0959..4467fabc86b01 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp @@ -1153,6 +1153,24 @@ static Value *foldAbsDiff(ICmpInst *Cmp, Value *TVal, Value *FVal, return Builder.CreateBinaryIntrinsic(Intrinsic::abs, TI, Builder.getTrue()); } + // Match: (A > B) ? (A - B) : (0 - (A - B)) --> abs(A - B) + // Also handles commuted cases like (B < A), (A >= B), (B <= A) after normalization + if (Pred == CmpInst::ICMP_SGT && + match(TI, m_NSWSub(m_Specific(A), 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) + // This handles cases like (A <= B) after normalization + if (Pred == CmpInst::ICMP_SLT && + match(TI, m_Neg(m_NSWSub(m_Specific(A), m_Specific(B)))) && + match(FI, m_NSWSub(m_Specific(A), m_Specific(B)))) { + return Builder.CreateBinaryIntrinsic(Intrinsic::abs, FI, + Builder.getFalse()); + } + return nullptr; } diff --git a/llvm/test/Transforms/InstCombine/abs-intrinsic.ll b/llvm/test/Transforms/InstCombine/abs-intrinsic.ll index 346111d892975..66ed9d8350db3 100644 --- a/llvm/test/Transforms/InstCombine/abs-intrinsic.ll +++ b/llvm/test/Transforms/InstCombine/abs-intrinsic.ll @@ -859,4 +859,128 @@ define i32 @abs_range_metadata(i32 %x) { %b = and i32 %a, 15 ret i32 %b } + !1 = !{i32 0, i32 16} + +define i32 @abs_diff(i32 %x, i32 %y) { +; CHECK-LABEL: @abs_diff( +; CHECK-NEXT: [[SUB:%.*]] = sub nsw i32 [[X:%.*]], [[Y:%.*]] +; CHECK-NEXT: [[COND:%.*]] = call i32 @llvm.abs.i32(i32 [[SUB]], i1 false) +; CHECK-NEXT: ret i32 [[COND]] +; + %sub = sub nsw i32 %x, %y + %cmp = icmp sgt i32 %x, %y + %sub1 = sub i32 0, %sub + %cond = select i1 %cmp, i32 %sub, i32 %sub1 + ret i32 %cond +} + +define i32 @abs_diff_neg_no_nsw_neg(i32 %x, i32 %y) { +; CHECK-LABEL: @abs_diff_neg_no_nsw_neg( +; CHECK-NEXT: [[SUB:%.*]] = sub i32 [[X:%.*]], [[Y:%.*]] +; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i32 [[X]], [[Y]] +; CHECK-NEXT: [[SUB1:%.*]] = sub i32 0, [[SUB]] +; CHECK-NEXT: [[COND:%.*]] = select i1 [[CMP]], i32 [[SUB]], i32 [[SUB1]] +; CHECK-NEXT: ret i32 [[COND]] +; + %sub = sub i32 %x, %y + %cmp = icmp sgt i32 %x, %y + %sub1 = sub i32 0, %sub + %cond = select i1 %cmp, i32 %sub, i32 %sub1 + ret i32 %cond +} + +define i32 @abs_diff_neg(i32 %x, i32 %y) { +; CHECK-LABEL: @abs_diff_neg( +; CHECK-NEXT: [[SUB:%.*]] = sub nsw i32 [[Y:%.*]], [[X:%.*]] +; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i32 [[X]], [[Y]] +; CHECK-NEXT: [[SUB1:%.*]] = sub i32 0, [[SUB]] +; CHECK-NEXT: [[COND:%.*]] = select i1 [[CMP]], i32 [[SUB]], i32 [[SUB1]] +; CHECK-NEXT: ret i32 [[COND]] +; + %sub = sub nsw i32 %y, %x + %cmp = icmp sgt i32 %x, %y + %sub1 = sub i32 0, %sub + %cond = select i1 %cmp, i32 %sub, i32 %sub1 + ret i32 %cond +} + +define i32 @abs_diff_neg_no_nsw(i32 %x, i32 %y) { +; CHECK-LABEL: @abs_diff_neg_no_nsw( +; CHECK-NEXT: [[SUB:%.*]] = sub i32 [[Y:%.*]], [[X:%.*]] +; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i32 [[X]], [[Y]] +; CHECK-NEXT: [[SUB1:%.*]] = sub i32 0, [[SUB]] +; CHECK-NEXT: [[COND:%.*]] = select i1 [[CMP]], i32 [[SUB]], i32 [[SUB1]] +; CHECK-NEXT: ret i32 [[COND]] +; + %sub = sub i32 %y, %x + %cmp = icmp sgt i32 %x, %y + %sub1 = sub i32 0, %sub + %cond = select i1 %cmp, i32 %sub, i32 %sub1 + ret i32 %cond +} + +define i32 @abs_diff_ge(i32 %x, i32 %y) { +; CHECK-LABEL: @abs_diff_ge( +; CHECK-NEXT: [[SUB:%.*]] = sub nsw i32 [[X:%.*]], [[Y:%.*]] +; CHECK-NEXT: [[COND:%.*]] = call i32 @llvm.abs.i32(i32 [[SUB]], i1 false) +; CHECK-NEXT: ret i32 [[COND]] +; + %sub = sub nsw i32 %x, %y + %cmp = icmp sge i32 %x, %y + %sub1 = sub i32 0, %sub + %cond = select i1 %cmp, i32 %sub, i32 %sub1 + ret i32 %cond +} + +define i32 @abs_diff_slt_commute(i32 %x, i32 %y) { +; CHECK-LABEL: @abs_diff_slt_commute( +; CHECK-NEXT: [[SUB:%.*]] = sub nsw i32 [[X:%.*]], [[Y:%.*]] +; CHECK-NEXT: [[COND:%.*]] = call i32 @llvm.abs.i32(i32 [[SUB]], i1 false) +; CHECK-NEXT: ret i32 [[COND]] +; + %sub = sub nsw i32 %x, %y + %cmp = icmp slt i32 %y, %x + %sub1 = sub i32 0, %sub + %cond = select i1 %cmp, i32 %sub, i32 %sub1 + ret i32 %cond +} + +define i32 @abs_diff_sge_same(i32 %x, i32 %y) { +; CHECK-LABEL: @abs_diff_sge_same( +; CHECK-NEXT: [[SUB:%.*]] = sub nsw i32 [[X:%.*]], [[Y:%.*]] +; CHECK-NEXT: [[COND:%.*]] = call i32 @llvm.abs.i32(i32 [[SUB]], i1 false) +; CHECK-NEXT: ret i32 [[COND]] +; + %sub = sub nsw i32 %x, %y + %cmp = icmp sge i32 %x, %y + %sub1 = sub i32 0, %sub + %cond = select i1 %cmp, i32 %sub, i32 %sub1 + ret i32 %cond +} + +define i32 @abs_diff_sle_inverted(i32 %x, i32 %y) { +; CHECK-LABEL: @abs_diff_sle_inverted( +; CHECK-NEXT: [[SUB:%.*]] = sub nsw i32 [[X:%.*]], [[Y:%.*]] +; CHECK-NEXT: [[COND:%.*]] = call i32 @llvm.abs.i32(i32 [[SUB]], i1 false) +; CHECK-NEXT: ret i32 [[COND]] +; + %sub = sub nsw i32 %x, %y + %cmp = icmp sle i32 %x, %y + %sub1 = sub i32 0, %sub + %cond = select i1 %cmp, i32 %sub1, i32 %sub + ret i32 %cond +} + +define i32 @abs_diff_sle_commute(i32 %x, i32 %y) { +; CHECK-LABEL: @abs_diff_sle_commute( +; CHECK-NEXT: [[SUB:%.*]] = sub nsw i32 [[X:%.*]], [[Y:%.*]] +; CHECK-NEXT: [[COND:%.*]] = call i32 @llvm.abs.i32(i32 [[SUB]], i1 false) +; CHECK-NEXT: ret i32 [[COND]] +; + %sub = sub nsw i32 %x, %y + %cmp = icmp sle i32 %y, %x + %sub1 = sub i32 0, %sub + %cond = select i1 %cmp, i32 %sub, i32 %sub1 + ret i32 %cond +}