diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp --- a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp @@ -2406,6 +2406,20 @@ return replaceInstUsesWith(I, Mul); } + // max(X,Y) nsw/nuw - min(X,Y) --> abs(X nsw - Y) + if (match(Op0, + m_OneUse(m_Intrinsic(m_Value(X), m_Value(Y)))) && + match(Op1, m_OneUse(m_Intrinsic(m_Specific(X), + m_Specific(Y))))) { + if (I.hasNoUnsignedWrap() || I.hasNoSignedWrap()) { + Value *Sub = + Builder.CreateSub(X, Y, "sub", /*HasNUW=*/ false, /*HasNSW=*/ true); + Value *Call = + Builder.CreateBinaryIntrinsic(Intrinsic::abs, Sub, Builder.getTrue()); + return replaceInstUsesWith(I, Call); + } + } + return TryToNarrowDeduceFlags(); } diff --git a/llvm/test/Transforms/InstCombine/sub-minmax.ll b/llvm/test/Transforms/InstCombine/sub-minmax.ll --- a/llvm/test/Transforms/InstCombine/sub-minmax.ll +++ b/llvm/test/Transforms/InstCombine/sub-minmax.ll @@ -1001,9 +1001,8 @@ define i8 @sub_max_min_nsw(i8 %a, i8 %b) { ; CHECK-LABEL: define {{[^@]+}}@sub_max_min_nsw ; CHECK-SAME: (i8 [[A:%.*]], i8 [[B:%.*]]) { -; CHECK-NEXT: [[MIN:%.*]] = call i8 @llvm.smin.i8(i8 [[A]], i8 [[B]]) -; CHECK-NEXT: [[MAX:%.*]] = call i8 @llvm.smax.i8(i8 [[A]], i8 [[B]]) -; CHECK-NEXT: [[AB:%.*]] = sub nsw i8 [[MAX]], [[MIN]] +; CHECK-NEXT: [[SUB:%.*]] = sub nsw i8 [[A]], [[B]] +; CHECK-NEXT: [[AB:%.*]] = call i8 @llvm.abs.i8(i8 [[SUB]], i1 true) ; CHECK-NEXT: ret i8 [[AB]] ; %min = call i8 @llvm.smin.i8(i8 %a, i8 %b) @@ -1015,9 +1014,8 @@ define i8 @sub_max_min_nuw(i8 %a, i8 %b) { ; CHECK-LABEL: define {{[^@]+}}@sub_max_min_nuw ; CHECK-SAME: (i8 [[A:%.*]], i8 [[B:%.*]]) { -; CHECK-NEXT: [[MIN:%.*]] = call i8 @llvm.smin.i8(i8 [[A]], i8 [[B]]) -; CHECK-NEXT: [[MAX:%.*]] = call i8 @llvm.smax.i8(i8 [[A]], i8 [[B]]) -; CHECK-NEXT: [[AB:%.*]] = sub nuw i8 [[MAX]], [[MIN]] +; CHECK-NEXT: [[SUB:%.*]] = sub nsw i8 [[A]], [[B]] +; CHECK-NEXT: [[AB:%.*]] = call i8 @llvm.abs.i8(i8 [[SUB]], i1 true) ; CHECK-NEXT: ret i8 [[AB]] ; %min = call i8 @llvm.smin.i8(i8 %a, i8 %b) @@ -1029,9 +1027,8 @@ define <2 x i8> @sub_max_min_vec_nsw(<2 x i8> %a, <2 x i8> %b) { ; CHECK-LABEL: define {{[^@]+}}@sub_max_min_vec_nsw ; CHECK-SAME: (<2 x i8> [[A:%.*]], <2 x i8> [[B:%.*]]) { -; CHECK-NEXT: [[MIN:%.*]] = call <2 x i8> @llvm.smin.v2i8(<2 x i8> [[A]], <2 x i8> [[B]]) -; CHECK-NEXT: [[MAX:%.*]] = call <2 x i8> @llvm.smax.v2i8(<2 x i8> [[A]], <2 x i8> [[B]]) -; CHECK-NEXT: [[AB:%.*]] = sub nsw <2 x i8> [[MAX]], [[MIN]] +; CHECK-NEXT: [[SUB:%.*]] = sub nsw <2 x i8> [[A]], [[B]] +; CHECK-NEXT: [[AB:%.*]] = call <2 x i8> @llvm.abs.v2i8(<2 x i8> [[SUB]], i1 true) ; CHECK-NEXT: ret <2 x i8> [[AB]] ; %min = call <2 x i8> @llvm.smin.v2i8(<2 x i8> %a, <2 x i8> %b) @@ -1043,9 +1040,8 @@ define <2 x i8> @sub_max_min_vec_nuw(<2 x i8> %a, <2 x i8> %b) { ; CHECK-LABEL: define {{[^@]+}}@sub_max_min_vec_nuw ; CHECK-SAME: (<2 x i8> [[A:%.*]], <2 x i8> [[B:%.*]]) { -; CHECK-NEXT: [[MIN:%.*]] = call <2 x i8> @llvm.smin.v2i8(<2 x i8> [[A]], <2 x i8> [[B]]) -; CHECK-NEXT: [[MAX:%.*]] = call <2 x i8> @llvm.smax.v2i8(<2 x i8> [[A]], <2 x i8> [[B]]) -; CHECK-NEXT: [[AB:%.*]] = sub nuw <2 x i8> [[MAX]], [[MIN]] +; CHECK-NEXT: [[SUB:%.*]] = sub nsw <2 x i8> [[A]], [[B]] +; CHECK-NEXT: [[AB:%.*]] = call <2 x i8> @llvm.abs.v2i8(<2 x i8> [[SUB]], i1 true) ; CHECK-NEXT: ret <2 x i8> [[AB]] ; %min = call <2 x i8> @llvm.smin.v2i8(<2 x i8> %a, <2 x i8> %b)