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,27 @@ } } + { + // 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 (!isa(Op1)) + break; + 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,19 +716,112 @@ } ; -; 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: [[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 %y, i8 %z) + %s = sub i8 %a, %m + ret i8 %s +} + +define i8 @sub_add_umin_commutation_umin(i8 %x, i8 %y, i8 %z) { +; CHECK-LABEL: @sub_add_umin_commutation_umin( +; 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_commutation_add(i8 %x, i8 %y, i8 %z) { +; CHECK-LABEL: @sub_add_umin_commutation_add( +; 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 +} + + +define i8 @sub_add_umin_commute_add_umin(i8 %x, i8 %y, i8 %z) { +; CHECK-LABEL: @sub_add_umin_commute_add_umin( +; 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 <2 x i8> @sub_add_umin_vec(<2 x i8> %x, <2 x i8> %y, <2 x i8> %z) { +; CHECK-LABEL: @sub_add_umin_vec( +; CHECK-NEXT: [[TMP1:%.*]] = call <2 x i8> @llvm.usub.sat.v2i8(<2 x i8> [[Y:%.*]], <2 x i8> [[Z:%.*]]) +; CHECK-NEXT: [[S:%.*]] = add <2 x i8> [[TMP1]], [[X:%.*]] +; CHECK-NEXT: ret <2 x i8> [[S]] +; + %a = add <2 x i8> %x, %y + %m = call <2 x i8> @llvm.umin.v2i8(<2 x i8> %y, <2 x i8> %z) + %s = sub <2 x i8> %a, %m + ret <2 x i8> %s +} + +; negative test + +define i8 @sub_add_umin_mismatch(i8 %x, i8 %y, i8 %z, i8 %t) { +; CHECK-LABEL: @sub_add_umin_mismatch( +; CHECK-NEXT: [[A:%.*]] = add i8 [[X:%.*]], [[Y:%.*]] +; CHECK-NEXT: [[M:%.*]] = call i8 @llvm.umin.i8(i8 [[T:%.*]], i8 [[Z:%.*]]) +; CHECK-NEXT: [[S:%.*]] = sub i8 [[A]], [[M]] +; CHECK-NEXT: ret i8 [[S]] +; + %a = add i8 %x, %y + %m = call i8 @llvm.umin.i8(i8 %t, i8 %z) + %s = sub i8 %a, %m + ret i8 %s +} + +define i8 @sub_add_umin_use_a(i8 %x, i8 %y, i8 %z) { +; CHECK-LABEL: @sub_add_umin_use_a( +; 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: call void @use8(i8 [[A]]) +; CHECK-NEXT: ret i8 [[S]] +; + %a = add i8 %x, %y + %m = call i8 @llvm.umin.i8(i8 %y, i8 %z) + %s = sub i8 %a, %m + call void @use8(i8 %a) + ret i8 %s +} + +define i8 @sub_add_umin_use_m(i8 %x, i8 %y, i8 %z) { +; CHECK-LABEL: @sub_add_umin_use_m( ; 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: call void @use8(i8 [[M]]) ; CHECK-NEXT: ret i8 [[S]] ; %a = add i8 %x, %y %m = call i8 @llvm.umin.i8(i8 %y, i8 %z) %s = sub i8 %a, %m + call void @use8(i8 %m) ret i8 %s }