[llvm] 04e45ae - [InstSimplify] fold nested min/max intrinsics with constant operands

Sanjay Patel via llvm-commits llvm-commits at lists.llvm.org
Thu Aug 6 05:33:00 PDT 2020


Yes - undef makes higher-level reasoning difficult. We had an icmp-based
simplification with:
https://reviews.llvm.org/D84655

But we're making progress on stubbing out the danger with patches like:
https://reviews.llvm.org/D84948
https://reviews.llvm.org/D84250


On Wed, Aug 5, 2020 at 3:01 PM Nikita Popov <nikita.ppv at gmail.com> wrote:

> On Wed, Aug 5, 2020 at 7:27 PM Philip Reames via llvm-commits <
> llvm-commits at lists.llvm.org> wrote:
>
>> A thought on a possible generalization.
>>
>> Instead of restricting this to constants, could we use generic reasoning
>> about Value relations?  If we implement a isKnownPredicate variant, we
>> could handle cases such as the following:
>>
>> %b = add nsw %a, 1
>> %c = smin(%a, %b) ;; %c == %b
>>
>> Weirdly, I can't find analogous code for selects.  I'd have assumed we
>> handled cases like this...
>>
>> Philip
>>
>
> More general folds in this area are currently blocked on
> https://reviews.llvm.org/D84792.
>
> Nikita
>
>
>> On 8/4/20 5:44 AM, Sanjay Patel via llvm-commits wrote:
>> > Author: Sanjay Patel
>> > Date: 2020-08-04T08:44:48-04:00
>> > New Revision: 04e45ae1c6d2fdbf3fd4242df69d1511df757d48
>> >
>> > URL:
>> https://github.com/llvm/llvm-project/commit/04e45ae1c6d2fdbf3fd4242df69d1511df757d48
>> > DIFF:
>> https://github.com/llvm/llvm-project/commit/04e45ae1c6d2fdbf3fd4242df69d1511df757d48.diff
>> >
>> > LOG: [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) >
>> >
>> > Added:
>> >
>> >
>> > Modified:
>> >      llvm/lib/Analysis/InstructionSimplify.cpp
>> >      llvm/test/Transforms/InstSimplify/maxmin_intrinsics.ll
>> >
>> > Removed:
>> >
>> >
>> >
>> >
>> ################################################################################
>> > diff  --git a/llvm/lib/Analysis/InstructionSimplify.cpp
>> b/llvm/lib/Analysis/InstructionSimplify.cpp
>> > index 2119ddcc7649..6b8f8e3acc17 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<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;
>> >
>> > diff  --git a/llvm/test/Transforms/InstSimplify/maxmin_intrinsics.ll
>> b/llvm/test/Transforms/InstSimplify/maxmin_intrinsics.ll
>> > index 3aa19e91e0e3..6b10853dd78f 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>
>> <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> [[M2]]
>> > +; CHECK-NEXT:    ret <2 x i8> [[M]]
>> >   ;
>> >     %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>)
>> > @@ -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> <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> [[M2]]
>> > +; CHECK-NEXT:    ret <2 x i8> [[M]]
>> >   ;
>> >     %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>)
>> > @@ -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>
>> <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> [[M2]]
>> > +; CHECK-NEXT:    ret <2 x i8> [[M]]
>> >   ;
>> >     %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)
>> > @@ -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> <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> [[M2]]
>> > +; CHECK-NEXT:    ret <2 x i8> [[M]]
>> >   ;
>> >     %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)
>> > @@ -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> <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
>> >   }
>> >
>> > +; 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> <i8 undef, i8 10>)
>> >
>> >
>> >
>> > _______________________________________________
>> > llvm-commits mailing list
>> > llvm-commits at lists.llvm.org
>> > https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits
>> _______________________________________________
>> llvm-commits mailing list
>> llvm-commits at lists.llvm.org
>> https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-commits/attachments/20200806/84f4fabd/attachment.html>


More information about the llvm-commits mailing list