From 04e45ae1c6d2fdbf3fd4242df69d1511df757d48 Mon Sep 17 00:00:00 2001 From: Sanjay Patel Date: Tue, 4 Aug 2020 08:28:44 -0400 Subject: [PATCH] [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) > --- llvm/lib/Analysis/InstructionSimplify.cpp | 15 +++++++ .../Transforms/InstSimplify/maxmin_intrinsics.ll | 52 +++++++++------------- 2 files changed, 35 insertions(+), 32 deletions(-) diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp index 2119ddc..6b8f8e3 100644 --- a/llvm/lib/Analysis/InstructionSimplify.cpp +++ b/llvm/lib/Analysis/InstructionSimplify.cpp @@ -5285,6 +5285,21 @@ static Value *simplifyBinaryIntrinsic(Function *F, Value *Op0, Value *Op1, // umin(i8 %x, i8 255) --> %x if (*C == getMaxMinLimit(getMaxMinOpposite(IID), BitWidth)) return Op0; + + // Remove nested call if constant operands allow it. Example: + // max (max X, 7), 5 -> max X, 7 + auto *MinMax0 = dyn_cast(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; diff --git a/llvm/test/Transforms/InstSimplify/maxmin_intrinsics.ll b/llvm/test/Transforms/InstSimplify/maxmin_intrinsics.ll index 3aa19e91..6b10853 100644 --- a/llvm/test/Transforms/InstSimplify/maxmin_intrinsics.ll +++ b/llvm/test/Transforms/InstSimplify/maxmin_intrinsics.ll @@ -627,8 +627,7 @@ define i8 @umin_smin(i8 %x, i8 %y) { define i8 @umax_umax_constants(i8 %x) { ; CHECK-LABEL: @umax_umax_constants( ; 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 [[M2]] +; CHECK-NEXT: ret i8 [[M]] ; %m = call i8 @llvm.umax.i8(i8 %x, i8 9) %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) { ; CHECK-LABEL: @umax_umax_constants_commute1( ; 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 [[M2]] +; CHECK-NEXT: ret i8 [[M]] ; %m = call i8 @llvm.umax.i8(i8 128, i8 %x) %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) { ; CHECK-LABEL: @umax_umax_constants_commute2( ; 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 [[M2]] +; CHECK-NEXT: ret i8 [[M]] ; %m = call i8 @llvm.umax.i8(i8 %x, i8 200) %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) { ; CHECK-LABEL: @umax_umax_constants_commute3( ; CHECK-NEXT: [[M:%.*]] = call <2 x i8> @llvm.umax.v2i8(<2 x i8> , <2 x i8> [[X:%.*]]) -; CHECK-NEXT: [[M2:%.*]] = call <2 x i8> @llvm.umax.v2i8(<2 x i8> [[M]], <2 x i8> ) -; CHECK-NEXT: ret <2 x i8> [[M2]] +; CHECK-NEXT: ret <2 x i8> [[M]] ; %m = call <2 x i8> @llvm.umax.v2i8(<2 x i8> , <2 x i8> %x) %m2 = call <2 x i8> @llvm.umax.v2i8(<2 x i8> %m, <2 x i8> ) @@ -671,8 +667,7 @@ define <2 x i8> @umax_umax_constants_commute3(<2 x i8> %x) { define i8 @umin_umin_constants(i8 %x) { ; CHECK-LABEL: @umin_umin_constants( ; 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 [[M2]] +; CHECK-NEXT: ret i8 [[M]] ; %m = call i8 @llvm.umin.i8(i8 %x, i8 7) %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) { ; CHECK-LABEL: @umin_umin_constants_commute1( ; 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 [[M2]] +; CHECK-NEXT: ret i8 [[M]] ; %m = call i8 @llvm.umin.i8(i8 7, i8 %x) %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) { ; CHECK-LABEL: @umin_umin_constants_commute2( ; CHECK-NEXT: [[M:%.*]] = call <2 x i8> @llvm.umin.v2i8(<2 x i8> [[X:%.*]], <2 x i8> ) -; CHECK-NEXT: [[M2:%.*]] = call <2 x i8> @llvm.umin.v2i8(<2 x i8> [[M]], <2 x i8> ) -; CHECK-NEXT: ret <2 x i8> [[M2]] +; CHECK-NEXT: ret <2 x i8> [[M]] ; %m = call <2 x i8> @llvm.umin.v2i8(<2 x i8> %x, <2 x i8> ) %m2 = call <2 x i8> @llvm.umin.v2i8(<2 x i8> %m, <2 x i8> ) @@ -704,8 +697,7 @@ define <2 x i8> @umin_umin_constants_commute2(<2 x i8> %x) { define i8 @umin_umin_constants_commute3(i8 %x) { ; CHECK-LABEL: @umin_umin_constants_commute3( ; 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 [[M2]] +; CHECK-NEXT: ret i8 [[M]] ; %m = call i8 @llvm.umin.i8(i8 128, i8 %x) %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) { ; CHECK-LABEL: @smax_smax_constants( ; 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 [[M2]] +; CHECK-NEXT: ret i8 [[M]] ; %m = call i8 @llvm.smax.i8(i8 %x, i8 9) %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) { ; CHECK-LABEL: @smax_smax_constants_commute1( ; CHECK-NEXT: [[M:%.*]] = call <2 x i8> @llvm.smax.v2i8(<2 x i8> , <2 x i8> [[X:%.*]]) -; CHECK-NEXT: [[M2:%.*]] = call <2 x i8> @llvm.smax.v2i8(<2 x i8> , <2 x i8> [[M]]) -; CHECK-NEXT: ret <2 x i8> [[M2]] +; CHECK-NEXT: ret <2 x i8> [[M]] ; %m = call <2 x i8> @llvm.smax.v2i8(<2 x i8> , <2 x i8> %x) %m2 = call <2 x i8> @llvm.smax.v2i8(<2 x i8> , <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) { ; CHECK-LABEL: @smax_smax_constants_commute2( ; 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 [[M2]] +; CHECK-NEXT: ret i8 [[M]] ; %m = call i8 @llvm.smax.i8(i8 %x, i8 0) %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) { ; CHECK-LABEL: @smax_smax_constants_commute3( ; 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 [[M2]] +; CHECK-NEXT: ret i8 [[M]] ; %m = call i8 @llvm.smax.i8(i8 -1, i8 %x) %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) { ; CHECK-LABEL: @smin_smin_constants( ; CHECK-NEXT: [[M:%.*]] = call <2 x i8> @llvm.smin.v2i8(<2 x i8> [[X:%.*]], <2 x i8> ) -; CHECK-NEXT: [[M2:%.*]] = call <2 x i8> @llvm.smin.v2i8(<2 x i8> , <2 x i8> [[M]]) -; CHECK-NEXT: ret <2 x i8> [[M2]] +; CHECK-NEXT: ret <2 x i8> [[M]] ; %m = call <2 x i8> @llvm.smin.v2i8(<2 x i8> %x, <2 x i8> ) %m2 = call <2 x i8> @llvm.smin.v2i8(<2 x i8> , <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) { ; CHECK-LABEL: @smin_smin_constants_commute1( ; 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 [[M2]] +; CHECK-NEXT: ret i8 [[M]] ; %m = call i8 @llvm.smin.i8(i8 -127, i8 %x) %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) { ; CHECK-LABEL: @smin_smin_constants_commute2( ; 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 [[M2]] +; CHECK-NEXT: ret i8 [[M]] ; %m = call i8 @llvm.smin.i8(i8 %x, i8 -1) %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) { ; CHECK-LABEL: @smin_smin_constants_commute3( ; 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 [[M2]] +; CHECK-NEXT: ret i8 [[M]] ; %m = call i8 @llvm.smin.i8(i8 -127, i8 %x) %m2 = call i8 @llvm.smin.i8(i8 %m, i8 -1) 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) { ; CHECK-LABEL: @umin_umin_constants_partial_undef( ; CHECK-NEXT: [[M:%.*]] = call <2 x i8> @llvm.umin.v2i8(<2 x i8> [[X:%.*]], <2 x i8> ) @@ -811,6 +797,8 @@ define <2 x i8> @umin_umin_constants_partial_undef(<2 x i8> %x) { 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) { ; CHECK-LABEL: @smax_smax_constants_partial_undef( ; CHECK-NEXT: [[M:%.*]] = call <2 x i8> @llvm.smax.v2i8(<2 x i8> [[X:%.*]], <2 x i8> ) -- 2.7.4