[llvm] cf58063 - [InstCombine] Canonicalize math-y conditional negation into a `select`

Nikita Popov via llvm-commits llvm-commits at lists.llvm.org
Mon Jan 2 11:43:45 PST 2023


On Mon, Jan 2, 2023, at 19:27, Roman Lebedev via llvm-commits wrote:
> 
> Author: Roman Lebedev
> Date: 2023-01-02T21:26:37+03:00
> New Revision: cf58063a4069b9ff11a99fd49345c1d188813feb
> 
> URL: https://github.com/llvm/llvm-project/commit/cf58063a4069b9ff11a99fd49345c1d188813feb
> DIFF: https://github.com/llvm/llvm-project/commit/cf58063a4069b9ff11a99fd49345c1d188813feb.diff
> 
> LOG: [InstCombine] Canonicalize math-y conditional negation into a `select`
> 
> https://alive2.llvm.org/ce/z/vPs-gZ
> 
> This is a larger pattern than would seem necessary, with minimal being:
> * `and` https://alive2.llvm.org/ce/z/q9-MqK
> * `or`  https://alive2.llvm.org/ce/z/AUUEMZ
> * `xor` https://alive2.llvm.org/ce/z/dm3Ume
> .. so for all others, we canonicalize away from math to `select`,
> but there we canonicalize in the opposite direction.
> 
> Fixes https://github.com/llvm/llvm-project/issues/59791
> 
> Added: 
>     
> 
> Modified: 
>     llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
>     llvm/lib/Transforms/InstCombine/InstCombineInternal.h
>     llvm/test/Transforms/InstCombine/conditional-negation.ll
> 
> Removed: 
>     
> 
> 
> ################################################################################
> diff  --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
> index 47f8c143b754c..a4b14ad5eda1c 100644
> --- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
> +++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
> @@ -1356,6 +1356,27 @@ Instruction *InstCombinerImpl::foldLogicOfIsFPClass(BinaryOperator &BO,
>    return nullptr;
> }
>  
> +/// Look for the pattern that conditionally negates a value via math operations:
> +///   cond.splat = sext i1 cond
> +///   sub = add cond.splat, x
> +///   xor = xor sub, cond.splat
> +/// and rewrite it to do the same, but via logical operations:
> +///   value.neg = sub 0, value
> +///   cond = select i1 neg, value.neg, value
> +Instruction *InstCombinerImpl::canonicalizeConditionalNegationViaMathToSelect(
> +    BinaryOperator &I) {
> +  assert(I.getOpcode() == BinaryOperator::Xor && "Only for xor!");
> +  Value *Cond, *X;
> +  // As per complexity ordering, `xor` is not commutative here.
> +  if (!match(&I, m_c_BinOp(m_OneUse(m_Value()), m_Value())) ||
> +      !match(I.getOperand(1), m_SExt(m_Value(Cond))) ||
> +      !Cond->getType()->isIntOrIntVectorTy(1) ||
> +      !match(I.getOperand(0), m_c_Add(m_SExt(m_Deferred(Cond)), m_Value(X))))

Nit: This m_Deferred() can be m_Specific(), as you're not using a value captured in the same match.

Though more generally, this is written in an unusual way: Why does this match m_SExt(m_Deferred(Cond)) rather than m_Specific(I.getOperand(1))?

As written this does allow those two sexts to be different instructions, but I believe handling such cases is an explicit non-goal for InstCombine (because that's the job of CSE/GVN style optimizations).

Regards,
Nikita

> +    return nullptr;
> +  return SelectInst::Create(Cond, Builder.CreateNeg(X, X->getName() + ".neg"),
> +                            X);
> +}
> +
> /// This a limited reassociation for a special case (see above) where we are
> /// checking if two values are either both NAN (unordered) or not-NAN (ordered).
> /// This could be handled more generally in '-reassociation', but it seems like
> @@ -4237,5 +4258,8 @@ Instruction *InstCombinerImpl::visitXor(BinaryOperator &I) {
>    if (Instruction *Folded = foldLogicOfIsFPClass(I, Op0, Op1))
>      return Folded;
>  
> +  if (Instruction *Folded = canonicalizeConditionalNegationViaMathToSelect(I))
> +    return Folded;
> +
>    return nullptr;
> }
> 
> diff  --git a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
> index f700cdb84d573..bfbc31e10a80a 100644
> --- a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
> +++ b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
> @@ -365,6 +365,9 @@ class LLVM_LIBRARY_VISIBILITY InstCombinerImpl final
>    Instruction *foldLogicOfIsFPClass(BinaryOperator &Operator, Value *LHS,
>                                      Value *RHS);
>  
> +  Instruction *
> +  canonicalizeConditionalNegationViaMathToSelect(BinaryOperator &i);
> +
>    Value *foldAndOrOfICmpsOfAndWithPow2(ICmpInst *LHS, ICmpInst *RHS,
>                                         Instruction *CxtI, bool IsAnd,
>                                         bool IsLogical = false);
> 
> diff  --git a/llvm/test/Transforms/InstCombine/conditional-negation.ll b/llvm/test/Transforms/InstCombine/conditional-negation.ll
> index b1b704011ec82..18b2ac4c52ed7 100644
> --- a/llvm/test/Transforms/InstCombine/conditional-negation.ll
> +++ b/llvm/test/Transforms/InstCombine/conditional-negation.ll
> @@ -4,9 +4,8 @@
> ; Basic pattern
> define i8 @t0(i8 %x, i1 %cond) {
> ; CHECK-LABEL: @t0(
> -; CHECK-NEXT:    [[COND_SPLAT:%.*]] = sext i1 [[COND:%.*]] to i8
> -; CHECK-NEXT:    [[SUB:%.*]] = add i8 [[COND_SPLAT]], [[X:%.*]]
> -; CHECK-NEXT:    [[XOR:%.*]] = xor i8 [[SUB]], [[COND_SPLAT]]
> +; CHECK-NEXT:    [[X_NEG:%.*]] = sub i8 0, [[X:%.*]]
> +; CHECK-NEXT:    [[XOR:%.*]] = select i1 [[COND:%.*]], i8 [[X_NEG]], i8 [[X]]
> ; CHECK-NEXT:    ret i8 [[XOR]]
> ;
>    %cond.splat = sext i1 %cond to i8
> @@ -16,9 +15,8 @@ define i8 @t0(i8 %x, i1 %cond) {
> }
> define <2 x i8> @t0_vec(<2 x i8> %x, <2 x i1> %cond) {
> ; CHECK-LABEL: @t0_vec(
> -; CHECK-NEXT:    [[COND_SPLAT:%.*]] = sext <2 x i1> [[COND:%.*]] to <2 x i8>
> -; CHECK-NEXT:    [[SUB:%.*]] = add <2 x i8> [[COND_SPLAT]], [[X:%.*]]
> -; CHECK-NEXT:    [[XOR:%.*]] = xor <2 x i8> [[SUB]], [[COND_SPLAT]]
> +; CHECK-NEXT:    [[X_NEG:%.*]] = sub <2 x i8> zeroinitializer, [[X:%.*]]
> +; CHECK-NEXT:    [[XOR:%.*]] = select <2 x i1> [[COND:%.*]], <2 x i8> [[X_NEG]], <2 x i8> [[X]]
> ; CHECK-NEXT:    ret <2 x i8> [[XOR]]
> ;
>    %cond.splat = sext <2 x i1> %cond to <2 x i8>
> @@ -30,10 +28,8 @@ define <2 x i8> @t0_vec(<2 x i8> %x, <2 x i1> %cond) {
> ; Two 
> diff erent extensions are fine
> define i8 @t1(i8 %x, i1 %cond) {
> ; CHECK-LABEL: @t1(
> -; CHECK-NEXT:    [[COND_SPLAT0:%.*]] = sext i1 [[COND:%.*]] to i8
> -; CHECK-NEXT:    [[COND_SPLAT1:%.*]] = sext i1 [[COND]] to i8
> -; CHECK-NEXT:    [[SUB:%.*]] = add i8 [[COND_SPLAT0]], [[X:%.*]]
> -; CHECK-NEXT:    [[XOR:%.*]] = xor i8 [[SUB]], [[COND_SPLAT1]]
> +; CHECK-NEXT:    [[X_NEG:%.*]] = sub i8 0, [[X:%.*]]
> +; CHECK-NEXT:    [[XOR:%.*]] = select i1 [[COND:%.*]], i8 [[X_NEG]], i8 [[X]]
> ; CHECK-NEXT:    ret i8 [[XOR]]
> ;
>    %cond.splat0 = sext i1 %cond to i8
> @@ -89,10 +85,9 @@ define <2 x i8> @t3_vec(<2 x i8> %x, <2 x i2> %cond) {
> ; xor is not commutative here because of complexity ordering
> define i8 @xor.commuted(i1 %cond) {
> ; CHECK-LABEL: @xor.commuted(
> -; CHECK-NEXT:    [[COND_SPLAT:%.*]] = sext i1 [[COND:%.*]] to i8
> ; CHECK-NEXT:    [[X:%.*]] = call i8 @gen.i8()
> -; CHECK-NEXT:    [[SUB:%.*]] = add i8 [[X]], [[COND_SPLAT]]
> -; CHECK-NEXT:    [[XOR:%.*]] = xor i8 [[SUB]], [[COND_SPLAT]]
> +; CHECK-NEXT:    [[X_NEG:%.*]] = sub i8 0, [[X]]
> +; CHECK-NEXT:    [[XOR:%.*]] = select i1 [[COND:%.*]], i8 [[X_NEG]], i8 [[X]]
> ; CHECK-NEXT:    ret i8 [[XOR]]
> ;
>    %cond.splat = sext i1 %cond to i8
> @@ -107,8 +102,8 @@ define i8 @extrause01_v1(i8 %x, i1 %cond) {
> ; CHECK-LABEL: @extrause01_v1(
> ; CHECK-NEXT:    [[COND_SPLAT:%.*]] = sext i1 [[COND:%.*]] to i8
> ; CHECK-NEXT:    call void @use.i8(i8 [[COND_SPLAT]])
> -; CHECK-NEXT:    [[SUB:%.*]] = add i8 [[COND_SPLAT]], [[X:%.*]]
> -; CHECK-NEXT:    [[XOR:%.*]] = xor i8 [[SUB]], [[COND_SPLAT]]
> +; CHECK-NEXT:    [[X_NEG:%.*]] = sub i8 0, [[X:%.*]]
> +; CHECK-NEXT:    [[XOR:%.*]] = select i1 [[COND]], i8 [[X_NEG]], i8 [[X]]
> ; CHECK-NEXT:    ret i8 [[XOR]]
> ;
>    %cond.splat = sext i1 %cond to i8
> @@ -153,9 +148,8 @@ define i8 @extrause001_v2(i8 %x, i1 %cond) {
> ; CHECK-LABEL: @extrause001_v2(
> ; CHECK-NEXT:    [[COND_SPLAT0:%.*]] = sext i1 [[COND:%.*]] to i8
> ; CHECK-NEXT:    call void @use.i8(i8 [[COND_SPLAT0]])
> -; CHECK-NEXT:    [[COND_SPLAT1:%.*]] = sext i1 [[COND]] to i8
> -; CHECK-NEXT:    [[SUB:%.*]] = add i8 [[COND_SPLAT0]], [[X:%.*]]
> -; CHECK-NEXT:    [[XOR:%.*]] = xor i8 [[SUB]], [[COND_SPLAT1]]
> +; CHECK-NEXT:    [[X_NEG:%.*]] = sub i8 0, [[X:%.*]]
> +; CHECK-NEXT:    [[XOR:%.*]] = select i1 [[COND]], i8 [[X_NEG]], i8 [[X]]
> ; CHECK-NEXT:    ret i8 [[XOR]]
> ;
>    %cond.splat0 = sext i1 %cond to i8
> @@ -167,11 +161,10 @@ define i8 @extrause001_v2(i8 %x, i1 %cond) {
> }
> define i8 @extrause010_v2(i8 %x, i1 %cond) {
> ; CHECK-LABEL: @extrause010_v2(
> -; CHECK-NEXT:    [[COND_SPLAT0:%.*]] = sext i1 [[COND:%.*]] to i8
> -; CHECK-NEXT:    [[COND_SPLAT1:%.*]] = sext i1 [[COND]] to i8
> +; CHECK-NEXT:    [[COND_SPLAT1:%.*]] = sext i1 [[COND:%.*]] to i8
> ; CHECK-NEXT:    call void @use.i8(i8 [[COND_SPLAT1]])
> -; CHECK-NEXT:    [[SUB:%.*]] = add i8 [[COND_SPLAT0]], [[X:%.*]]
> -; CHECK-NEXT:    [[XOR:%.*]] = xor i8 [[SUB]], [[COND_SPLAT1]]
> +; CHECK-NEXT:    [[X_NEG:%.*]] = sub i8 0, [[X:%.*]]
> +; CHECK-NEXT:    [[XOR:%.*]] = select i1 [[COND]], i8 [[X_NEG]], i8 [[X]]
> ; CHECK-NEXT:    ret i8 [[XOR]]
> ;
>    %cond.splat0 = sext i1 %cond to i8
> @@ -187,8 +180,8 @@ define i8 @extrause011_v2(i8 %x, i1 %cond) {
> ; CHECK-NEXT:    call void @use.i8(i8 [[COND_SPLAT0]])
> ; CHECK-NEXT:    [[COND_SPLAT1:%.*]] = sext i1 [[COND]] to i8
> ; CHECK-NEXT:    call void @use.i8(i8 [[COND_SPLAT1]])
> -; CHECK-NEXT:    [[SUB:%.*]] = add i8 [[COND_SPLAT0]], [[X:%.*]]
> -; CHECK-NEXT:    [[XOR:%.*]] = xor i8 [[SUB]], [[COND_SPLAT1]]
> +; CHECK-NEXT:    [[X_NEG:%.*]] = sub i8 0, [[X:%.*]]
> +; CHECK-NEXT:    [[XOR:%.*]] = select i1 [[COND]], i8 [[X_NEG]], i8 [[X]]
> ; CHECK-NEXT:    ret i8 [[XOR]]
> ;
>    %cond.splat0 = sext i1 %cond to i8
> @@ -202,10 +195,10 @@ define i8 @extrause011_v2(i8 %x, i1 %cond) {
> define i8 @extrause100_v2(i8 %x, i1 %cond) {
> ; CHECK-LABEL: @extrause100_v2(
> ; CHECK-NEXT:    [[COND_SPLAT0:%.*]] = sext i1 [[COND:%.*]] to i8
> -; CHECK-NEXT:    [[COND_SPLAT1:%.*]] = sext i1 [[COND]] to i8
> ; CHECK-NEXT:    [[SUB:%.*]] = add i8 [[COND_SPLAT0]], [[X:%.*]]
> ; CHECK-NEXT:    call void @use.i8(i8 [[SUB]])
> -; CHECK-NEXT:    [[XOR:%.*]] = xor i8 [[SUB]], [[COND_SPLAT1]]
> +; CHECK-NEXT:    [[X_NEG:%.*]] = sub i8 0, [[X]]
> +; CHECK-NEXT:    [[XOR:%.*]] = select i1 [[COND]], i8 [[X_NEG]], i8 [[X]]
> ; CHECK-NEXT:    ret i8 [[XOR]]
> ;
>    %cond.splat0 = sext i1 %cond to i8
> @@ -219,10 +212,10 @@ define i8 @extrause101_v2(i8 %x, i1 %cond) {
> ; CHECK-LABEL: @extrause101_v2(
> ; CHECK-NEXT:    [[COND_SPLAT0:%.*]] = sext i1 [[COND:%.*]] to i8
> ; CHECK-NEXT:    call void @use.i8(i8 [[COND_SPLAT0]])
> -; CHECK-NEXT:    [[COND_SPLAT1:%.*]] = sext i1 [[COND]] to i8
> ; CHECK-NEXT:    [[SUB:%.*]] = add i8 [[COND_SPLAT0]], [[X:%.*]]
> ; CHECK-NEXT:    call void @use.i8(i8 [[SUB]])
> -; CHECK-NEXT:    [[XOR:%.*]] = xor i8 [[SUB]], [[COND_SPLAT1]]
> +; CHECK-NEXT:    [[X_NEG:%.*]] = sub i8 0, [[X]]
> +; CHECK-NEXT:    [[XOR:%.*]] = select i1 [[COND]], i8 [[X_NEG]], i8 [[X]]
> ; CHECK-NEXT:    ret i8 [[XOR]]
> ;
>    %cond.splat0 = sext i1 %cond to i8
> 
> 
>         
> _______________________________________________
> 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/20230102/8d172137/attachment.html>


More information about the llvm-commits mailing list