[InstSimplify] fold nested min/max intrinsics with constant operands

This is based on the existing code for the non-intrinsic idioms
in InstCombine.

The vector constant constraint is non-obvious: undefs should be
ok in the outer call, but they can't propagate safely from the
inner call in all cases. Example:

https://alive2.llvm.org/ce/z/-2bVbM
  define <2 x i8> @src(<2 x i8> %x) {
  %0:
    %m = umin <2 x i8> %x, { 7, undef }
    %m2 = umin <2 x i8> { 9, 9 }, %m
    ret <2 x i8> %m2
  }
  =>
  define <2 x i8> @tgt(<2 x i8> %x) {
  %0:
    %m = umin <2 x i8> %x, { 7, undef }
    ret <2 x i8> %m
  }
  Transformation doesn't verify!
  ERROR: Value mismatch

  Example:
  <2 x i8> %x = < undef, undef >

  Source:
  <2 x i8> %m = < #x00 (0)	[based on undef value], #x00 (0) >
  <2 x i8> %m2 = < #x00 (0), #x00 (0) >

  Target:
  <2 x i8> %m = < #x07 (7), #x10 (16) >
  Source value: < #x00 (0), #x00 (0) >
  Target value: < #x07 (7), #x10 (16) >
This commit is contained in:
Sanjay Patel 2020-08-04 08:28:44 -04:00
parent cb9f9df5f8
commit 04e45ae1c6
2 changed files with 35 additions and 32 deletions

View File

@ -5285,6 +5285,21 @@ static Value *simplifyBinaryIntrinsic(Function *F, Value *Op0, Value *Op1,
// umin(i8 %x, i8 255) --> %x // umin(i8 %x, i8 255) --> %x
if (*C == getMaxMinLimit(getMaxMinOpposite(IID), BitWidth)) if (*C == getMaxMinLimit(getMaxMinOpposite(IID), BitWidth))
return Op0; return Op0;
// Remove nested call if constant operands allow it. Example:
// max (max X, 7), 5 -> max X, 7
auto *MinMax0 = dyn_cast<IntrinsicInst>(Op0);
if (MinMax0 && MinMax0->getIntrinsicID() == IID) {
// TODO: loosen undef/splat restrictions for vector constants.
Value *M00 = MinMax0->getOperand(0), *M01 = MinMax0->getOperand(1);
const APInt *InnerC;
if ((match(M00, m_APInt(InnerC)) || match(M01, m_APInt(InnerC))) &&
((IID == Intrinsic::smax && InnerC->sge(*C)) ||
(IID == Intrinsic::smin && InnerC->sle(*C)) ||
(IID == Intrinsic::umax && InnerC->uge(*C)) ||
(IID == Intrinsic::umin && InnerC->ule(*C))))
return Op0;
}
} }
break; break;

View File

@ -627,8 +627,7 @@ define i8 @umin_smin(i8 %x, i8 %y) {
define i8 @umax_umax_constants(i8 %x) { define i8 @umax_umax_constants(i8 %x) {
; CHECK-LABEL: @umax_umax_constants( ; CHECK-LABEL: @umax_umax_constants(
; CHECK-NEXT: [[M:%.*]] = call i8 @llvm.umax.i8(i8 [[X:%.*]], i8 9) ; CHECK-NEXT: [[M:%.*]] = call i8 @llvm.umax.i8(i8 [[X:%.*]], i8 9)
; CHECK-NEXT: [[M2:%.*]] = call i8 @llvm.umax.i8(i8 7, i8 [[M]]) ; CHECK-NEXT: ret i8 [[M]]
; CHECK-NEXT: ret i8 [[M2]]
; ;
%m = call i8 @llvm.umax.i8(i8 %x, i8 9) %m = call i8 @llvm.umax.i8(i8 %x, i8 9)
%m2 = call i8 @llvm.umax.i8(i8 7, i8 %m) %m2 = call i8 @llvm.umax.i8(i8 7, i8 %m)
@ -638,8 +637,7 @@ define i8 @umax_umax_constants(i8 %x) {
define i8 @umax_umax_constants_commute1(i8 %x) { define i8 @umax_umax_constants_commute1(i8 %x) {
; CHECK-LABEL: @umax_umax_constants_commute1( ; CHECK-LABEL: @umax_umax_constants_commute1(
; CHECK-NEXT: [[M:%.*]] = call i8 @llvm.umax.i8(i8 -128, i8 [[X:%.*]]) ; CHECK-NEXT: [[M:%.*]] = call i8 @llvm.umax.i8(i8 -128, i8 [[X:%.*]])
; CHECK-NEXT: [[M2:%.*]] = call i8 @llvm.umax.i8(i8 7, i8 [[M]]) ; CHECK-NEXT: ret i8 [[M]]
; CHECK-NEXT: ret i8 [[M2]]
; ;
%m = call i8 @llvm.umax.i8(i8 128, i8 %x) %m = call i8 @llvm.umax.i8(i8 128, i8 %x)
%m2 = call i8 @llvm.umax.i8(i8 7, i8 %m) %m2 = call i8 @llvm.umax.i8(i8 7, i8 %m)
@ -649,8 +647,7 @@ define i8 @umax_umax_constants_commute1(i8 %x) {
define i8 @umax_umax_constants_commute2(i8 %x) { define i8 @umax_umax_constants_commute2(i8 %x) {
; CHECK-LABEL: @umax_umax_constants_commute2( ; CHECK-LABEL: @umax_umax_constants_commute2(
; CHECK-NEXT: [[M:%.*]] = call i8 @llvm.umax.i8(i8 [[X:%.*]], i8 -56) ; CHECK-NEXT: [[M:%.*]] = call i8 @llvm.umax.i8(i8 [[X:%.*]], i8 -56)
; CHECK-NEXT: [[M2:%.*]] = call i8 @llvm.umax.i8(i8 [[M]], i8 127) ; CHECK-NEXT: ret i8 [[M]]
; CHECK-NEXT: ret i8 [[M2]]
; ;
%m = call i8 @llvm.umax.i8(i8 %x, i8 200) %m = call i8 @llvm.umax.i8(i8 %x, i8 200)
%m2 = call i8 @llvm.umax.i8(i8 %m, i8 127) %m2 = call i8 @llvm.umax.i8(i8 %m, i8 127)
@ -660,8 +657,7 @@ define i8 @umax_umax_constants_commute2(i8 %x) {
define <2 x i8> @umax_umax_constants_commute3(<2 x i8> %x) { define <2 x i8> @umax_umax_constants_commute3(<2 x i8> %x) {
; CHECK-LABEL: @umax_umax_constants_commute3( ; CHECK-LABEL: @umax_umax_constants_commute3(
; CHECK-NEXT: [[M:%.*]] = call <2 x i8> @llvm.umax.v2i8(<2 x i8> <i8 -2, i8 -2>, <2 x i8> [[X:%.*]]) ; CHECK-NEXT: [[M:%.*]] = call <2 x i8> @llvm.umax.v2i8(<2 x i8> <i8 -2, i8 -2>, <2 x i8> [[X:%.*]])
; CHECK-NEXT: [[M2:%.*]] = call <2 x i8> @llvm.umax.v2i8(<2 x i8> [[M]], <2 x i8> <i8 -128, i8 -128>) ; CHECK-NEXT: ret <2 x i8> [[M]]
; CHECK-NEXT: ret <2 x i8> [[M2]]
; ;
%m = call <2 x i8> @llvm.umax.v2i8(<2 x i8> <i8 254, i8 254>, <2 x i8> %x) %m = call <2 x i8> @llvm.umax.v2i8(<2 x i8> <i8 254, i8 254>, <2 x i8> %x)
%m2 = call <2 x i8> @llvm.umax.v2i8(<2 x i8> %m, <2 x i8> <i8 128, i8 128>) %m2 = call <2 x i8> @llvm.umax.v2i8(<2 x i8> %m, <2 x i8> <i8 128, i8 128>)
@ -671,8 +667,7 @@ define <2 x i8> @umax_umax_constants_commute3(<2 x i8> %x) {
define i8 @umin_umin_constants(i8 %x) { define i8 @umin_umin_constants(i8 %x) {
; CHECK-LABEL: @umin_umin_constants( ; CHECK-LABEL: @umin_umin_constants(
; CHECK-NEXT: [[M:%.*]] = call i8 @llvm.umin.i8(i8 [[X:%.*]], i8 7) ; CHECK-NEXT: [[M:%.*]] = call i8 @llvm.umin.i8(i8 [[X:%.*]], i8 7)
; CHECK-NEXT: [[M2:%.*]] = call i8 @llvm.umin.i8(i8 9, i8 [[M]]) ; CHECK-NEXT: ret i8 [[M]]
; CHECK-NEXT: ret i8 [[M2]]
; ;
%m = call i8 @llvm.umin.i8(i8 %x, i8 7) %m = call i8 @llvm.umin.i8(i8 %x, i8 7)
%m2 = call i8 @llvm.umin.i8(i8 9, i8 %m) %m2 = call i8 @llvm.umin.i8(i8 9, i8 %m)
@ -682,8 +677,7 @@ define i8 @umin_umin_constants(i8 %x) {
define i8 @umin_umin_constants_commute1(i8 %x) { define i8 @umin_umin_constants_commute1(i8 %x) {
; CHECK-LABEL: @umin_umin_constants_commute1( ; CHECK-LABEL: @umin_umin_constants_commute1(
; CHECK-NEXT: [[M:%.*]] = call i8 @llvm.umin.i8(i8 7, i8 [[X:%.*]]) ; CHECK-NEXT: [[M:%.*]] = call i8 @llvm.umin.i8(i8 7, i8 [[X:%.*]])
; CHECK-NEXT: [[M2:%.*]] = call i8 @llvm.umin.i8(i8 -128, i8 [[M]]) ; CHECK-NEXT: ret i8 [[M]]
; CHECK-NEXT: ret i8 [[M2]]
; ;
%m = call i8 @llvm.umin.i8(i8 7, i8 %x) %m = call i8 @llvm.umin.i8(i8 7, i8 %x)
%m2 = call i8 @llvm.umin.i8(i8 128, i8 %m) %m2 = call i8 @llvm.umin.i8(i8 128, i8 %m)
@ -693,8 +687,7 @@ define i8 @umin_umin_constants_commute1(i8 %x) {
define <2 x i8> @umin_umin_constants_commute2(<2 x i8> %x) { define <2 x i8> @umin_umin_constants_commute2(<2 x i8> %x) {
; CHECK-LABEL: @umin_umin_constants_commute2( ; CHECK-LABEL: @umin_umin_constants_commute2(
; CHECK-NEXT: [[M:%.*]] = call <2 x i8> @llvm.umin.v2i8(<2 x i8> [[X:%.*]], <2 x i8> <i8 127, i8 127>) ; CHECK-NEXT: [[M:%.*]] = call <2 x i8> @llvm.umin.v2i8(<2 x i8> [[X:%.*]], <2 x i8> <i8 127, i8 127>)
; CHECK-NEXT: [[M2:%.*]] = call <2 x i8> @llvm.umin.v2i8(<2 x i8> [[M]], <2 x i8> <i8 -56, i8 undef>) ; CHECK-NEXT: ret <2 x i8> [[M]]
; CHECK-NEXT: ret <2 x i8> [[M2]]
; ;
%m = call <2 x i8> @llvm.umin.v2i8(<2 x i8> %x, <2 x i8> <i8 127, i8 127>) %m = call <2 x i8> @llvm.umin.v2i8(<2 x i8> %x, <2 x i8> <i8 127, i8 127>)
%m2 = call <2 x i8> @llvm.umin.v2i8(<2 x i8> %m, <2 x i8> <i8 200, i8 undef>) %m2 = call <2 x i8> @llvm.umin.v2i8(<2 x i8> %m, <2 x i8> <i8 200, i8 undef>)
@ -704,8 +697,7 @@ define <2 x i8> @umin_umin_constants_commute2(<2 x i8> %x) {
define i8 @umin_umin_constants_commute3(i8 %x) { define i8 @umin_umin_constants_commute3(i8 %x) {
; CHECK-LABEL: @umin_umin_constants_commute3( ; CHECK-LABEL: @umin_umin_constants_commute3(
; CHECK-NEXT: [[M:%.*]] = call i8 @llvm.umin.i8(i8 -128, i8 [[X:%.*]]) ; CHECK-NEXT: [[M:%.*]] = call i8 @llvm.umin.i8(i8 -128, i8 [[X:%.*]])
; CHECK-NEXT: [[M2:%.*]] = call i8 @llvm.umin.i8(i8 [[M]], i8 -2) ; CHECK-NEXT: ret i8 [[M]]
; CHECK-NEXT: ret i8 [[M2]]
; ;
%m = call i8 @llvm.umin.i8(i8 128, i8 %x) %m = call i8 @llvm.umin.i8(i8 128, i8 %x)
%m2 = call i8 @llvm.umin.i8(i8 %m, i8 254) %m2 = call i8 @llvm.umin.i8(i8 %m, i8 254)
@ -715,8 +707,7 @@ define i8 @umin_umin_constants_commute3(i8 %x) {
define i8 @smax_smax_constants(i8 %x) { define i8 @smax_smax_constants(i8 %x) {
; CHECK-LABEL: @smax_smax_constants( ; CHECK-LABEL: @smax_smax_constants(
; CHECK-NEXT: [[M:%.*]] = call i8 @llvm.smax.i8(i8 [[X:%.*]], i8 9) ; CHECK-NEXT: [[M:%.*]] = call i8 @llvm.smax.i8(i8 [[X:%.*]], i8 9)
; CHECK-NEXT: [[M2:%.*]] = call i8 @llvm.smax.i8(i8 7, i8 [[M]]) ; CHECK-NEXT: ret i8 [[M]]
; CHECK-NEXT: ret i8 [[M2]]
; ;
%m = call i8 @llvm.smax.i8(i8 %x, i8 9) %m = call i8 @llvm.smax.i8(i8 %x, i8 9)
%m2 = call i8 @llvm.smax.i8(i8 7, i8 %m) %m2 = call i8 @llvm.smax.i8(i8 7, i8 %m)
@ -726,8 +717,7 @@ define i8 @smax_smax_constants(i8 %x) {
define <2 x i8> @smax_smax_constants_commute1(<2 x i8> %x) { define <2 x i8> @smax_smax_constants_commute1(<2 x i8> %x) {
; CHECK-LABEL: @smax_smax_constants_commute1( ; CHECK-LABEL: @smax_smax_constants_commute1(
; CHECK-NEXT: [[M:%.*]] = call <2 x i8> @llvm.smax.v2i8(<2 x i8> <i8 7, i8 7>, <2 x i8> [[X:%.*]]) ; CHECK-NEXT: [[M:%.*]] = call <2 x i8> @llvm.smax.v2i8(<2 x i8> <i8 7, i8 7>, <2 x i8> [[X:%.*]])
; CHECK-NEXT: [[M2:%.*]] = call <2 x i8> @llvm.smax.v2i8(<2 x i8> <i8 -127, i8 -127>, <2 x i8> [[M]]) ; CHECK-NEXT: ret <2 x i8> [[M]]
; CHECK-NEXT: ret <2 x i8> [[M2]]
; ;
%m = call <2 x i8> @llvm.smax.v2i8(<2 x i8> <i8 7, i8 7>, <2 x i8> %x) %m = call <2 x i8> @llvm.smax.v2i8(<2 x i8> <i8 7, i8 7>, <2 x i8> %x)
%m2 = call <2 x i8> @llvm.smax.v2i8(<2 x i8> <i8 -127, i8 -127>, <2 x i8> %m) %m2 = call <2 x i8> @llvm.smax.v2i8(<2 x i8> <i8 -127, i8 -127>, <2 x i8> %m)
@ -737,8 +727,7 @@ define <2 x i8> @smax_smax_constants_commute1(<2 x i8> %x) {
define i8 @smax_smax_constants_commute2(i8 %x) { define i8 @smax_smax_constants_commute2(i8 %x) {
; CHECK-LABEL: @smax_smax_constants_commute2( ; CHECK-LABEL: @smax_smax_constants_commute2(
; CHECK-NEXT: [[M:%.*]] = call i8 @llvm.smax.i8(i8 [[X:%.*]], i8 0) ; CHECK-NEXT: [[M:%.*]] = call i8 @llvm.smax.i8(i8 [[X:%.*]], i8 0)
; CHECK-NEXT: [[M2:%.*]] = call i8 @llvm.smax.i8(i8 [[M]], i8 -1) ; CHECK-NEXT: ret i8 [[M]]
; CHECK-NEXT: ret i8 [[M2]]
; ;
%m = call i8 @llvm.smax.i8(i8 %x, i8 0) %m = call i8 @llvm.smax.i8(i8 %x, i8 0)
%m2 = call i8 @llvm.smax.i8(i8 %m, i8 -1) %m2 = call i8 @llvm.smax.i8(i8 %m, i8 -1)
@ -748,8 +737,7 @@ define i8 @smax_smax_constants_commute2(i8 %x) {
define i8 @smax_smax_constants_commute3(i8 %x) { define i8 @smax_smax_constants_commute3(i8 %x) {
; CHECK-LABEL: @smax_smax_constants_commute3( ; CHECK-LABEL: @smax_smax_constants_commute3(
; CHECK-NEXT: [[M:%.*]] = call i8 @llvm.smax.i8(i8 -1, i8 [[X:%.*]]) ; CHECK-NEXT: [[M:%.*]] = call i8 @llvm.smax.i8(i8 -1, i8 [[X:%.*]])
; CHECK-NEXT: [[M2:%.*]] = call i8 @llvm.smax.i8(i8 [[M]], i8 -127) ; CHECK-NEXT: ret i8 [[M]]
; CHECK-NEXT: ret i8 [[M2]]
; ;
%m = call i8 @llvm.smax.i8(i8 -1, i8 %x) %m = call i8 @llvm.smax.i8(i8 -1, i8 %x)
%m2 = call i8 @llvm.smax.i8(i8 %m, i8 -127) %m2 = call i8 @llvm.smax.i8(i8 %m, i8 -127)
@ -759,8 +747,7 @@ define i8 @smax_smax_constants_commute3(i8 %x) {
define <2 x i8> @smin_smin_constants(<2 x i8> %x) { define <2 x i8> @smin_smin_constants(<2 x i8> %x) {
; CHECK-LABEL: @smin_smin_constants( ; CHECK-LABEL: @smin_smin_constants(
; CHECK-NEXT: [[M:%.*]] = call <2 x i8> @llvm.smin.v2i8(<2 x i8> [[X:%.*]], <2 x i8> <i8 7, i8 7>) ; CHECK-NEXT: [[M:%.*]] = call <2 x i8> @llvm.smin.v2i8(<2 x i8> [[X:%.*]], <2 x i8> <i8 7, i8 7>)
; CHECK-NEXT: [[M2:%.*]] = call <2 x i8> @llvm.smin.v2i8(<2 x i8> <i8 undef, i8 9>, <2 x i8> [[M]]) ; CHECK-NEXT: ret <2 x i8> [[M]]
; CHECK-NEXT: ret <2 x i8> [[M2]]
; ;
%m = call <2 x i8> @llvm.smin.v2i8(<2 x i8> %x, <2 x i8> <i8 7, i8 7>) %m = call <2 x i8> @llvm.smin.v2i8(<2 x i8> %x, <2 x i8> <i8 7, i8 7>)
%m2 = call <2 x i8> @llvm.smin.v2i8(<2 x i8> <i8 undef, i8 9>, <2 x i8> %m) %m2 = call <2 x i8> @llvm.smin.v2i8(<2 x i8> <i8 undef, i8 9>, <2 x i8> %m)
@ -770,8 +757,7 @@ define <2 x i8> @smin_smin_constants(<2 x i8> %x) {
define i8 @smin_smin_constants_commute1(i8 %x) { define i8 @smin_smin_constants_commute1(i8 %x) {
; CHECK-LABEL: @smin_smin_constants_commute1( ; CHECK-LABEL: @smin_smin_constants_commute1(
; CHECK-NEXT: [[M:%.*]] = call i8 @llvm.smin.i8(i8 -127, i8 [[X:%.*]]) ; CHECK-NEXT: [[M:%.*]] = call i8 @llvm.smin.i8(i8 -127, i8 [[X:%.*]])
; CHECK-NEXT: [[M2:%.*]] = call i8 @llvm.smin.i8(i8 7, i8 [[M]]) ; CHECK-NEXT: ret i8 [[M]]
; CHECK-NEXT: ret i8 [[M2]]
; ;
%m = call i8 @llvm.smin.i8(i8 -127, i8 %x) %m = call i8 @llvm.smin.i8(i8 -127, i8 %x)
%m2 = call i8 @llvm.smin.i8(i8 7, i8 %m) %m2 = call i8 @llvm.smin.i8(i8 7, i8 %m)
@ -781,8 +767,7 @@ define i8 @smin_smin_constants_commute1(i8 %x) {
define i8 @smin_smin_constants_commute2(i8 %x) { define i8 @smin_smin_constants_commute2(i8 %x) {
; CHECK-LABEL: @smin_smin_constants_commute2( ; CHECK-LABEL: @smin_smin_constants_commute2(
; CHECK-NEXT: [[M:%.*]] = call i8 @llvm.smin.i8(i8 [[X:%.*]], i8 -1) ; CHECK-NEXT: [[M:%.*]] = call i8 @llvm.smin.i8(i8 [[X:%.*]], i8 -1)
; CHECK-NEXT: [[M2:%.*]] = call i8 @llvm.smin.i8(i8 [[M]], i8 0) ; CHECK-NEXT: ret i8 [[M]]
; CHECK-NEXT: ret i8 [[M2]]
; ;
%m = call i8 @llvm.smin.i8(i8 %x, i8 -1) %m = call i8 @llvm.smin.i8(i8 %x, i8 -1)
%m2 = call i8 @llvm.smin.i8(i8 %m, i8 0) %m2 = call i8 @llvm.smin.i8(i8 %m, i8 0)
@ -792,14 +777,15 @@ define i8 @smin_smin_constants_commute2(i8 %x) {
define i8 @smin_smin_constants_commute3(i8 %x) { define i8 @smin_smin_constants_commute3(i8 %x) {
; CHECK-LABEL: @smin_smin_constants_commute3( ; CHECK-LABEL: @smin_smin_constants_commute3(
; CHECK-NEXT: [[M:%.*]] = call i8 @llvm.smin.i8(i8 -127, i8 [[X:%.*]]) ; CHECK-NEXT: [[M:%.*]] = call i8 @llvm.smin.i8(i8 -127, i8 [[X:%.*]])
; CHECK-NEXT: [[M2:%.*]] = call i8 @llvm.smin.i8(i8 [[M]], i8 -1) ; CHECK-NEXT: ret i8 [[M]]
; CHECK-NEXT: ret i8 [[M2]]
; ;
%m = call i8 @llvm.smin.i8(i8 -127, i8 %x) %m = call i8 @llvm.smin.i8(i8 -127, i8 %x)
%m2 = call i8 @llvm.smin.i8(i8 %m, i8 -1) %m2 = call i8 @llvm.smin.i8(i8 %m, i8 -1)
ret i8 %m2 ret i8 %m2
} }
; Negative test - undef in inner constant must not propagate.
define <2 x i8> @umin_umin_constants_partial_undef(<2 x i8> %x) { define <2 x i8> @umin_umin_constants_partial_undef(<2 x i8> %x) {
; CHECK-LABEL: @umin_umin_constants_partial_undef( ; CHECK-LABEL: @umin_umin_constants_partial_undef(
; CHECK-NEXT: [[M:%.*]] = call <2 x i8> @llvm.umin.v2i8(<2 x i8> [[X:%.*]], <2 x i8> <i8 7, i8 undef>) ; CHECK-NEXT: [[M:%.*]] = call <2 x i8> @llvm.umin.v2i8(<2 x i8> [[X:%.*]], <2 x i8> <i8 7, i8 undef>)
@ -811,6 +797,8 @@ define <2 x i8> @umin_umin_constants_partial_undef(<2 x i8> %x) {
ret <2 x i8> %m2 ret <2 x i8> %m2
} }
; Negative test - undef in inner constant must not propagate.
define <2 x i8> @smax_smax_constants_partial_undef(<2 x i8> %x) { define <2 x i8> @smax_smax_constants_partial_undef(<2 x i8> %x) {
; CHECK-LABEL: @smax_smax_constants_partial_undef( ; CHECK-LABEL: @smax_smax_constants_partial_undef(
; CHECK-NEXT: [[M:%.*]] = call <2 x i8> @llvm.smax.v2i8(<2 x i8> [[X:%.*]], <2 x i8> <i8 undef, i8 10>) ; CHECK-NEXT: [[M:%.*]] = call <2 x i8> @llvm.smax.v2i8(<2 x i8> [[X:%.*]], <2 x i8> <i8 undef, i8 10>)