Index: llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp =================================================================== --- llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp +++ llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp @@ -1593,6 +1593,15 @@ I, Builder.CreateIntrinsic(Intrinsic::ctpop, {I.getType()}, {Builder.CreateOr(A, B)})); + // min(A, B) + max(A, B) => A + B. + if (match(&I, match_combine_or( + m_c_Add(m_SMax(m_Value(A), m_Value(B)), + m_c_SMin(m_Deferred(A), m_Deferred(B))), + m_c_Add(m_UMax(m_Value(A), m_Value(B)), + m_c_UMin(m_Deferred(A), m_Deferred(B)))))) { + return BinaryOperator::CreateAdd(A, B); + } + return Changed ? &I : nullptr; } Index: llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp =================================================================== --- llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp +++ llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp @@ -482,6 +482,15 @@ I.setHasNoUnsignedWrap(true); } + // min(X, Y) * max(X, Y) => X * Y. + if (match(&I, match_combine_or( + m_c_Mul(m_SMax(m_Value(X), m_Value(Y)), + m_c_SMin(m_Deferred(X), m_Deferred(Y))), + m_c_Mul(m_UMax(m_Value(X), m_Value(Y)), + m_c_UMin(m_Deferred(X), m_Deferred(Y)))))) { + return BinaryOperator::CreateMul(X, Y); + } + return Changed ? &I : nullptr; } Index: llvm/test/Transforms/InstCombine/add-min-max.ll =================================================================== --- /dev/null +++ llvm/test/Transforms/InstCombine/add-min-max.ll @@ -0,0 +1,59 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -passes=instcombine -S | FileCheck %s + +declare i32 @llvm.smax.i32(i32 %a, i32 %b) +declare i32 @llvm.smin.i32(i32 %a, i32 %b) +declare i32 @llvm.umax.i32(i32 %a, i32 %b) +declare i32 @llvm.umin.i32(i32 %a, i32 %b) + +define i32 @uadd_min_max(i32 %a, i32 %b) { +; CHECK-LABEL: @uadd_min_max( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[RES:%.*]] = add i32 [[A:%.*]], [[B:%.*]] +; CHECK-NEXT: ret i32 [[RES]] +; +entry: + %max = call i32 @llvm.umax.i32(i32 %a, i32 %b) + %min = call i32 @llvm.umin.i32(i32 %a, i32 %b) + %res = add i32 %min, %max + ret i32 %res +} + +define i32 @uadd_min_max_comm(i32 %a, i32 %b) { +; CHECK-LABEL: @uadd_min_max_comm( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[RES:%.*]] = add i32 [[B:%.*]], [[A:%.*]] +; CHECK-NEXT: ret i32 [[RES]] +; +entry: + %max = call i32 @llvm.umax.i32(i32 %b, i32 %a) + %min = call i32 @llvm.umin.i32(i32 %a, i32 %b) + %res = add i32 %min, %max + ret i32 %res +} + +define i32 @sadd_min_max(i32 %a, i32 %b) { +; CHECK-LABEL: @sadd_min_max( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[RES:%.*]] = add i32 [[A:%.*]], [[B:%.*]] +; CHECK-NEXT: ret i32 [[RES]] +; +entry: + %max = call i32 @llvm.smax.i32(i32 %a, i32 %b) + %min = call i32 @llvm.smin.i32(i32 %a, i32 %b) + %res = add i32 %min, %max + ret i32 %res +} + +define i32 @sadd_min_max_comm(i32 %a, i32 %b) { +; CHECK-LABEL: @sadd_min_max_comm( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[RES:%.*]] = add i32 [[B:%.*]], [[A:%.*]] +; CHECK-NEXT: ret i32 [[RES]] +; +entry: + %max = call i32 @llvm.smax.i32(i32 %b, i32 %a) + %min = call i32 @llvm.smin.i32(i32 %a, i32 %b) + %res = add i32 %min, %max + ret i32 %res +} Index: llvm/test/Transforms/InstCombine/mul-min-max.ll =================================================================== --- /dev/null +++ llvm/test/Transforms/InstCombine/mul-min-max.ll @@ -0,0 +1,59 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -passes=instcombine -S | FileCheck %s + +declare i32 @llvm.smax.i32(i32 %a, i32 %b) +declare i32 @llvm.smin.i32(i32 %a, i32 %b) +declare i32 @llvm.umax.i32(i32 %a, i32 %b) +declare i32 @llvm.umin.i32(i32 %a, i32 %b) + +define i32 @uadd_min_max(i32 %a, i32 %b) { +; CHECK-LABEL: @uadd_min_max( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[RES:%.*]] = mul i32 [[A:%.*]], [[B:%.*]] +; CHECK-NEXT: ret i32 [[RES]] +; +entry: + %max = call i32 @llvm.umax.i32(i32 %a, i32 %b) + %min = call i32 @llvm.umin.i32(i32 %a, i32 %b) + %res = mul i32 %min, %max + ret i32 %res +} + +define i32 @uadd_min_max_comm(i32 %a, i32 %b) { +; CHECK-LABEL: @uadd_min_max_comm( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[RES:%.*]] = mul i32 [[B:%.*]], [[A:%.*]] +; CHECK-NEXT: ret i32 [[RES]] +; +entry: + %max = call i32 @llvm.umax.i32(i32 %b, i32 %a) + %min = call i32 @llvm.umin.i32(i32 %a, i32 %b) + %res = mul i32 %min, %max + ret i32 %res +} + +define i32 @sadd_min_max(i32 %a, i32 %b) { +; CHECK-LABEL: @sadd_min_max( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[RES:%.*]] = mul i32 [[A:%.*]], [[B:%.*]] +; CHECK-NEXT: ret i32 [[RES]] +; +entry: + %max = call i32 @llvm.smax.i32(i32 %a, i32 %b) + %min = call i32 @llvm.smin.i32(i32 %a, i32 %b) + %res = mul i32 %min, %max + ret i32 %res +} + +define i32 @sadd_min_max_comm(i32 %a, i32 %b) { +; CHECK-LABEL: @sadd_min_max_comm( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[RES:%.*]] = mul i32 [[B:%.*]], [[A:%.*]] +; CHECK-NEXT: ret i32 [[RES]] +; +entry: + %max = call i32 @llvm.smax.i32(i32 %b, i32 %a) + %min = call i32 @llvm.smin.i32(i32 %a, i32 %b) + %res = mul i32 %min, %max + ret i32 %res +}