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 @@ -2018,7 +2018,6 @@ { // sub(add(X,Y), s/umin(X,Y)) --> s/umax(X,Y) // sub(add(X,Y), s/umax(X,Y)) --> s/umin(X,Y) - // TODO: generalize to sub(add(Z,Y),umin(X,Y)) --> add(Z,usub.sat(Y,X))? if (auto *II = dyn_cast(Op1)) { Value *X = II->getLHS(); Value *Y = II->getRHS(); @@ -2031,6 +2030,24 @@ } } + { + // sub(add(X,Y),umin(Y,Z)) --> add(X,usub.sat(Y,Z)) + Value *X, *Y, *Z; + Type *Ty = I.getType(); + for (bool Swap : {false, true}) + if (match(Op0, m_c_Add(m_Value(X), m_Value(Y))) && Op0->hasOneUse() && + Op1->hasOneUse()) { + if (Swap) + std::swap(X, Y); + if (match(Op1, m_UMin(m_Specific(Y), m_Value(Z))) || + match(Op1, m_UMin(m_Value(Z), m_Specific(Y)))) { + Value *USub = + Builder.CreateIntrinsic(Intrinsic::usub_sat, {Ty}, {Y, Z}); + return BinaryOperator::CreateAdd(X, USub); + } + } + } + { // If we have a subtraction between some value and a select between // said value and something else, sink subtraction into select hands, i.e.: 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 @@ -716,14 +716,13 @@ } ; -; TODO: sub(add(X,Y),umin(Y,Z)) --> add(X,usubsat(Y,Z)) +; sub(add(X,Y),umin(Y,Z)) --> add(X,usubsat(Y,Z)) ; define i8 @sub_add_umin(i8 %x, i8 %y, i8 %z) { ; CHECK-LABEL: @sub_add_umin( -; CHECK-NEXT: [[A:%.*]] = add i8 [[X:%.*]], [[Y:%.*]] -; CHECK-NEXT: [[M:%.*]] = call i8 @llvm.umin.i8(i8 [[Y]], i8 [[Z:%.*]]) -; CHECK-NEXT: [[S:%.*]] = sub i8 [[A]], [[M]] +; CHECK-NEXT: [[TMP1:%.*]] = call i8 @llvm.usub.sat.i8(i8 [[Y:%.*]], i8 [[Z:%.*]]) +; CHECK-NEXT: [[S:%.*]] = add i8 [[TMP1]], [[X:%.*]] ; CHECK-NEXT: ret i8 [[S]] ; %a = add i8 %x, %y @@ -732,5 +731,41 @@ ret i8 %s } +define i8 @sub_add_umin_1(i8 %x, i8 %y, i8 %z) { +; CHECK-LABEL: @sub_add_umin_1( +; CHECK-NEXT: [[TMP1:%.*]] = call i8 @llvm.usub.sat.i8(i8 [[Y:%.*]], i8 [[Z:%.*]]) +; CHECK-NEXT: [[S:%.*]] = add i8 [[TMP1]], [[X:%.*]] +; CHECK-NEXT: ret i8 [[S]] +; + %a = add i8 %x, %y + %m = call i8 @llvm.umin.i8(i8 %z, i8 %y) + %s = sub i8 %a, %m + ret i8 %s +} + +define i8 @sub_add_umin_2(i8 %x, i8 %y, i8 %z) { +; CHECK-LABEL: @sub_add_umin_2( +; CHECK-NEXT: [[TMP1:%.*]] = call i8 @llvm.usub.sat.i8(i8 [[Y:%.*]], i8 [[Z:%.*]]) +; CHECK-NEXT: [[S:%.*]] = add i8 [[TMP1]], [[X:%.*]] +; CHECK-NEXT: ret i8 [[S]] +; + %a = add i8 %y, %x + %m = call i8 @llvm.umin.i8(i8 %z, i8 %y) + %s = sub i8 %a, %m + ret i8 %s +} + +define i8 @sub_add_umin_3(i8 %x, i8 %y, i8 %z) { +; CHECK-LABEL: @sub_add_umin_3( +; CHECK-NEXT: [[TMP1:%.*]] = call i8 @llvm.usub.sat.i8(i8 [[Y:%.*]], i8 [[Z:%.*]]) +; CHECK-NEXT: [[S:%.*]] = add i8 [[TMP1]], [[X:%.*]] +; CHECK-NEXT: ret i8 [[S]] +; + %a = add i8 %y, %x + %m = call i8 @llvm.umin.i8(i8 %y, i8 %z) + %s = sub i8 %a, %m + ret i8 %s +} + declare void @use8(i8) declare void @use32(i32 %u)