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

Roman Lebedev via llvm-commits llvm-commits at lists.llvm.org
Mon Jan 2 10:27:10 PST 2023


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))))
+    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


        


More information about the llvm-commits mailing list