<!DOCTYPE html><html><head><title></title><style type="text/css">p.MsoNormal,p.MsoNoSpacing{margin:0}</style></head><body><div>On Mon, Jan 2, 2023, at 19:27, Roman Lebedev via llvm-commits wrote:<br></div><blockquote type="cite" id="qt" style=""><div><br></div><div>Author: Roman Lebedev<br></div><div>Date: 2023-01-02T21:26:37+03:00<br></div><div>New Revision: cf58063a4069b9ff11a99fd49345c1d188813feb<br></div><div><br></div><div>URL: <a href="https://github.com/llvm/llvm-project/commit/cf58063a4069b9ff11a99fd49345c1d188813feb">https://github.com/llvm/llvm-project/commit/cf58063a4069b9ff11a99fd49345c1d188813feb</a><br></div><div>DIFF: <a href="https://github.com/llvm/llvm-project/commit/cf58063a4069b9ff11a99fd49345c1d188813feb.diff">https://github.com/llvm/llvm-project/commit/cf58063a4069b9ff11a99fd49345c1d188813feb.diff</a><br></div><div><br></div><div>LOG: [InstCombine] Canonicalize math-y conditional negation into a `select`<br></div><div><br></div><div><a href="https://alive2.llvm.org/ce/z/vPs-gZ">https://alive2.llvm.org/ce/z/vPs-gZ</a><br></div><div><br></div><div>This is a larger pattern than would seem necessary, with minimal being:<br></div><div>* `and` <a href="https://alive2.llvm.org/ce/z/q9-MqK">https://alive2.llvm.org/ce/z/q9-MqK</a><br></div><div>* `or`  <a href="https://alive2.llvm.org/ce/z/AUUEMZ">https://alive2.llvm.org/ce/z/AUUEMZ</a><br></div><div>* `xor` <a href="https://alive2.llvm.org/ce/z/dm3Ume">https://alive2.llvm.org/ce/z/dm3Ume</a><br></div><div>.. so for all others, we canonicalize away from math to `select`,<br></div><div>but there we canonicalize in the opposite direction.<br></div><div><br></div><div>Fixes <a href="https://github.com/llvm/llvm-project/issues/59791">https://github.com/llvm/llvm-project/issues/59791</a><br></div><div><br></div><div>Added: <br></div><div>    <br></div><div><br></div><div>Modified: <br></div><div>    llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp<br></div><div>    llvm/lib/Transforms/InstCombine/InstCombineInternal.h<br></div><div>    llvm/test/Transforms/InstCombine/conditional-negation.ll<br></div><div><br></div><div>Removed: <br></div><div>    <br></div><div><br></div><div><br></div><div>################################################################################<br></div><div>diff  --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp<br></div><div>index 47f8c143b754c..a4b14ad5eda1c 100644<br></div><div>--- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp<br></div><div>+++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp<br></div><div>@@ -1356,6 +1356,27 @@ Instruction *InstCombinerImpl::foldLogicOfIsFPClass(BinaryOperator &BO,<br></div><div>   return nullptr;<br></div><div>}<br></div><div> <br></div><div>+/// Look for the pattern that conditionally negates a value via math operations:<br></div><div>+///   cond.splat = sext i1 cond<br></div><div>+///   sub = add cond.splat, x<br></div><div>+///   xor = xor sub, cond.splat<br></div><div>+/// and rewrite it to do the same, but via logical operations:<br></div><div>+///   value.neg = sub 0, value<br></div><div>+///   cond = select i1 neg, value.neg, value<br></div><div>+Instruction *InstCombinerImpl::canonicalizeConditionalNegationViaMathToSelect(<br></div><div>+    BinaryOperator &I) {<br></div><div>+  assert(I.getOpcode() == BinaryOperator::Xor && "Only for xor!");<br></div><div>+  Value *Cond, *X;<br></div><div>+  // As per complexity ordering, `xor` is not commutative here.<br></div><div>+  if (!match(&I, m_c_BinOp(m_OneUse(m_Value()), m_Value())) ||<br></div><div>+      !match(I.getOperand(1), m_SExt(m_Value(Cond))) ||<br></div><div>+      !Cond->getType()->isIntOrIntVectorTy(1) ||<br></div><div>+      !match(I.getOperand(0), m_c_Add(m_SExt(m_Deferred(Cond)), m_Value(X))))<br></div></blockquote><div><br></div><div>Nit: This m_Deferred() can be m_Specific(), as you're not using a value captured in the same match.<br></div><div><br></div><div>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))?<br></div><div><br></div><div>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).<br></div><div><br></div><div>Regards,<br></div><div>Nikita<br></div><div><br></div><blockquote type="cite" id="qt" style=""><div>+    return nullptr;<br></div><div>+  return SelectInst::Create(Cond, Builder.CreateNeg(X, X->getName() + ".neg"),<br></div><div>+                            X);<br></div><div>+}<br></div><div>+<br></div><div>/// This a limited reassociation for a special case (see above) where we are<br></div><div>/// checking if two values are either both NAN (unordered) or not-NAN (ordered).<br></div><div>/// This could be handled more generally in '-reassociation', but it seems like<br></div><div>@@ -4237,5 +4258,8 @@ Instruction *InstCombinerImpl::visitXor(BinaryOperator &I) {<br></div><div>   if (Instruction *Folded = foldLogicOfIsFPClass(I, Op0, Op1))<br></div><div>     return Folded;<br></div><div> <br></div><div>+  if (Instruction *Folded = canonicalizeConditionalNegationViaMathToSelect(I))<br></div><div>+    return Folded;<br></div><div>+<br></div><div>   return nullptr;<br></div><div>}<br></div><div><br></div><div>diff  --git a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h<br></div><div>index f700cdb84d573..bfbc31e10a80a 100644<br></div><div>--- a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h<br></div><div>+++ b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h<br></div><div>@@ -365,6 +365,9 @@ class LLVM_LIBRARY_VISIBILITY InstCombinerImpl final<br></div><div>   Instruction *foldLogicOfIsFPClass(BinaryOperator &Operator, Value *LHS,<br></div><div>                                     Value *RHS);<br></div><div> <br></div><div>+  Instruction *<br></div><div>+  canonicalizeConditionalNegationViaMathToSelect(BinaryOperator &i);<br></div><div>+<br></div><div>   Value *foldAndOrOfICmpsOfAndWithPow2(ICmpInst *LHS, ICmpInst *RHS,<br></div><div>                                        Instruction *CxtI, bool IsAnd,<br></div><div>                                        bool IsLogical = false);<br></div><div><br></div><div>diff  --git a/llvm/test/Transforms/InstCombine/conditional-negation.ll b/llvm/test/Transforms/InstCombine/conditional-negation.ll<br></div><div>index b1b704011ec82..18b2ac4c52ed7 100644<br></div><div>--- a/llvm/test/Transforms/InstCombine/conditional-negation.ll<br></div><div>+++ b/llvm/test/Transforms/InstCombine/conditional-negation.ll<br></div><div>@@ -4,9 +4,8 @@<br></div><div>; Basic pattern<br></div><div>define i8 @t0(i8 %x, i1 %cond) {<br></div><div>; CHECK-LABEL: @t0(<br></div><div>-; CHECK-NEXT:    [[COND_SPLAT:%.*]] = sext i1 [[COND:%.*]] to i8<br></div><div>-; CHECK-NEXT:    [[SUB:%.*]] = add i8 [[COND_SPLAT]], [[X:%.*]]<br></div><div>-; CHECK-NEXT:    [[XOR:%.*]] = xor i8 [[SUB]], [[COND_SPLAT]]<br></div><div>+; CHECK-NEXT:    [[X_NEG:%.*]] = sub i8 0, [[X:%.*]]<br></div><div>+; CHECK-NEXT:    [[XOR:%.*]] = select i1 [[COND:%.*]], i8 [[X_NEG]], i8 [[X]]<br></div><div>; CHECK-NEXT:    ret i8 [[XOR]]<br></div><div>;<br></div><div>   %cond.splat = sext i1 %cond to i8<br></div><div>@@ -16,9 +15,8 @@ define i8 @t0(i8 %x, i1 %cond) {<br></div><div>}<br></div><div>define <2 x i8> @t0_vec(<2 x i8> %x, <2 x i1> %cond) {<br></div><div>; CHECK-LABEL: @t0_vec(<br></div><div>-; CHECK-NEXT:    [[COND_SPLAT:%.*]] = sext <2 x i1> [[COND:%.*]] to <2 x i8><br></div><div>-; CHECK-NEXT:    [[SUB:%.*]] = add <2 x i8> [[COND_SPLAT]], [[X:%.*]]<br></div><div>-; CHECK-NEXT:    [[XOR:%.*]] = xor <2 x i8> [[SUB]], [[COND_SPLAT]]<br></div><div>+; CHECK-NEXT:    [[X_NEG:%.*]] = sub <2 x i8> zeroinitializer, [[X:%.*]]<br></div><div>+; CHECK-NEXT:    [[XOR:%.*]] = select <2 x i1> [[COND:%.*]], <2 x i8> [[X_NEG]], <2 x i8> [[X]]<br></div><div>; CHECK-NEXT:    ret <2 x i8> [[XOR]]<br></div><div>;<br></div><div>   %cond.splat = sext <2 x i1> %cond to <2 x i8><br></div><div>@@ -30,10 +28,8 @@ define <2 x i8> @t0_vec(<2 x i8> %x, <2 x i1> %cond) {<br></div><div>; Two <br></div><div>diff erent extensions are fine<br></div><div>define i8 @t1(i8 %x, i1 %cond) {<br></div><div>; CHECK-LABEL: @t1(<br></div><div>-; CHECK-NEXT:    [[COND_SPLAT0:%.*]] = sext i1 [[COND:%.*]] to i8<br></div><div>-; CHECK-NEXT:    [[COND_SPLAT1:%.*]] = sext i1 [[COND]] to i8<br></div><div>-; CHECK-NEXT:    [[SUB:%.*]] = add i8 [[COND_SPLAT0]], [[X:%.*]]<br></div><div>-; CHECK-NEXT:    [[XOR:%.*]] = xor i8 [[SUB]], [[COND_SPLAT1]]<br></div><div>+; CHECK-NEXT:    [[X_NEG:%.*]] = sub i8 0, [[X:%.*]]<br></div><div>+; CHECK-NEXT:    [[XOR:%.*]] = select i1 [[COND:%.*]], i8 [[X_NEG]], i8 [[X]]<br></div><div>; CHECK-NEXT:    ret i8 [[XOR]]<br></div><div>;<br></div><div>   %cond.splat0 = sext i1 %cond to i8<br></div><div>@@ -89,10 +85,9 @@ define <2 x i8> @t3_vec(<2 x i8> %x, <2 x i2> %cond) {<br></div><div>; xor is not commutative here because of complexity ordering<br></div><div>define i8 @xor.commuted(i1 %cond) {<br></div><div>; CHECK-LABEL: @xor.commuted(<br></div><div>-; CHECK-NEXT:    [[COND_SPLAT:%.*]] = sext i1 [[COND:%.*]] to i8<br></div><div>; CHECK-NEXT:    [[X:%.*]] = call i8 @gen.i8()<br></div><div>-; CHECK-NEXT:    [[SUB:%.*]] = add i8 [[X]], [[COND_SPLAT]]<br></div><div>-; CHECK-NEXT:    [[XOR:%.*]] = xor i8 [[SUB]], [[COND_SPLAT]]<br></div><div>+; CHECK-NEXT:    [[X_NEG:%.*]] = sub i8 0, [[X]]<br></div><div>+; CHECK-NEXT:    [[XOR:%.*]] = select i1 [[COND:%.*]], i8 [[X_NEG]], i8 [[X]]<br></div><div>; CHECK-NEXT:    ret i8 [[XOR]]<br></div><div>;<br></div><div>   %cond.splat = sext i1 %cond to i8<br></div><div>@@ -107,8 +102,8 @@ define i8 @extrause01_v1(i8 %x, i1 %cond) {<br></div><div>; CHECK-LABEL: @extrause01_v1(<br></div><div>; CHECK-NEXT:    [[COND_SPLAT:%.*]] = sext i1 [[COND:%.*]] to i8<br></div><div>; CHECK-NEXT:    call void @use.i8(i8 [[COND_SPLAT]])<br></div><div>-; CHECK-NEXT:    [[SUB:%.*]] = add i8 [[COND_SPLAT]], [[X:%.*]]<br></div><div>-; CHECK-NEXT:    [[XOR:%.*]] = xor i8 [[SUB]], [[COND_SPLAT]]<br></div><div>+; CHECK-NEXT:    [[X_NEG:%.*]] = sub i8 0, [[X:%.*]]<br></div><div>+; CHECK-NEXT:    [[XOR:%.*]] = select i1 [[COND]], i8 [[X_NEG]], i8 [[X]]<br></div><div>; CHECK-NEXT:    ret i8 [[XOR]]<br></div><div>;<br></div><div>   %cond.splat = sext i1 %cond to i8<br></div><div>@@ -153,9 +148,8 @@ define i8 @extrause001_v2(i8 %x, i1 %cond) {<br></div><div>; CHECK-LABEL: @extrause001_v2(<br></div><div>; CHECK-NEXT:    [[COND_SPLAT0:%.*]] = sext i1 [[COND:%.*]] to i8<br></div><div>; CHECK-NEXT:    call void @use.i8(i8 [[COND_SPLAT0]])<br></div><div>-; CHECK-NEXT:    [[COND_SPLAT1:%.*]] = sext i1 [[COND]] to i8<br></div><div>-; CHECK-NEXT:    [[SUB:%.*]] = add i8 [[COND_SPLAT0]], [[X:%.*]]<br></div><div>-; CHECK-NEXT:    [[XOR:%.*]] = xor i8 [[SUB]], [[COND_SPLAT1]]<br></div><div>+; CHECK-NEXT:    [[X_NEG:%.*]] = sub i8 0, [[X:%.*]]<br></div><div>+; CHECK-NEXT:    [[XOR:%.*]] = select i1 [[COND]], i8 [[X_NEG]], i8 [[X]]<br></div><div>; CHECK-NEXT:    ret i8 [[XOR]]<br></div><div>;<br></div><div>   %cond.splat0 = sext i1 %cond to i8<br></div><div>@@ -167,11 +161,10 @@ define i8 @extrause001_v2(i8 %x, i1 %cond) {<br></div><div>}<br></div><div>define i8 @extrause010_v2(i8 %x, i1 %cond) {<br></div><div>; CHECK-LABEL: @extrause010_v2(<br></div><div>-; CHECK-NEXT:    [[COND_SPLAT0:%.*]] = sext i1 [[COND:%.*]] to i8<br></div><div>-; CHECK-NEXT:    [[COND_SPLAT1:%.*]] = sext i1 [[COND]] to i8<br></div><div>+; CHECK-NEXT:    [[COND_SPLAT1:%.*]] = sext i1 [[COND:%.*]] to i8<br></div><div>; CHECK-NEXT:    call void @use.i8(i8 [[COND_SPLAT1]])<br></div><div>-; CHECK-NEXT:    [[SUB:%.*]] = add i8 [[COND_SPLAT0]], [[X:%.*]]<br></div><div>-; CHECK-NEXT:    [[XOR:%.*]] = xor i8 [[SUB]], [[COND_SPLAT1]]<br></div><div>+; CHECK-NEXT:    [[X_NEG:%.*]] = sub i8 0, [[X:%.*]]<br></div><div>+; CHECK-NEXT:    [[XOR:%.*]] = select i1 [[COND]], i8 [[X_NEG]], i8 [[X]]<br></div><div>; CHECK-NEXT:    ret i8 [[XOR]]<br></div><div>;<br></div><div>   %cond.splat0 = sext i1 %cond to i8<br></div><div>@@ -187,8 +180,8 @@ define i8 @extrause011_v2(i8 %x, i1 %cond) {<br></div><div>; CHECK-NEXT:    call void @use.i8(i8 [[COND_SPLAT0]])<br></div><div>; CHECK-NEXT:    [[COND_SPLAT1:%.*]] = sext i1 [[COND]] to i8<br></div><div>; CHECK-NEXT:    call void @use.i8(i8 [[COND_SPLAT1]])<br></div><div>-; CHECK-NEXT:    [[SUB:%.*]] = add i8 [[COND_SPLAT0]], [[X:%.*]]<br></div><div>-; CHECK-NEXT:    [[XOR:%.*]] = xor i8 [[SUB]], [[COND_SPLAT1]]<br></div><div>+; CHECK-NEXT:    [[X_NEG:%.*]] = sub i8 0, [[X:%.*]]<br></div><div>+; CHECK-NEXT:    [[XOR:%.*]] = select i1 [[COND]], i8 [[X_NEG]], i8 [[X]]<br></div><div>; CHECK-NEXT:    ret i8 [[XOR]]<br></div><div>;<br></div><div>   %cond.splat0 = sext i1 %cond to i8<br></div><div>@@ -202,10 +195,10 @@ define i8 @extrause011_v2(i8 %x, i1 %cond) {<br></div><div>define i8 @extrause100_v2(i8 %x, i1 %cond) {<br></div><div>; CHECK-LABEL: @extrause100_v2(<br></div><div>; CHECK-NEXT:    [[COND_SPLAT0:%.*]] = sext i1 [[COND:%.*]] to i8<br></div><div>-; CHECK-NEXT:    [[COND_SPLAT1:%.*]] = sext i1 [[COND]] to i8<br></div><div>; CHECK-NEXT:    [[SUB:%.*]] = add i8 [[COND_SPLAT0]], [[X:%.*]]<br></div><div>; CHECK-NEXT:    call void @use.i8(i8 [[SUB]])<br></div><div>-; CHECK-NEXT:    [[XOR:%.*]] = xor i8 [[SUB]], [[COND_SPLAT1]]<br></div><div>+; CHECK-NEXT:    [[X_NEG:%.*]] = sub i8 0, [[X]]<br></div><div>+; CHECK-NEXT:    [[XOR:%.*]] = select i1 [[COND]], i8 [[X_NEG]], i8 [[X]]<br></div><div>; CHECK-NEXT:    ret i8 [[XOR]]<br></div><div>;<br></div><div>   %cond.splat0 = sext i1 %cond to i8<br></div><div>@@ -219,10 +212,10 @@ define i8 @extrause101_v2(i8 %x, i1 %cond) {<br></div><div>; CHECK-LABEL: @extrause101_v2(<br></div><div>; CHECK-NEXT:    [[COND_SPLAT0:%.*]] = sext i1 [[COND:%.*]] to i8<br></div><div>; CHECK-NEXT:    call void @use.i8(i8 [[COND_SPLAT0]])<br></div><div>-; CHECK-NEXT:    [[COND_SPLAT1:%.*]] = sext i1 [[COND]] to i8<br></div><div>; CHECK-NEXT:    [[SUB:%.*]] = add i8 [[COND_SPLAT0]], [[X:%.*]]<br></div><div>; CHECK-NEXT:    call void @use.i8(i8 [[SUB]])<br></div><div>-; CHECK-NEXT:    [[XOR:%.*]] = xor i8 [[SUB]], [[COND_SPLAT1]]<br></div><div>+; CHECK-NEXT:    [[X_NEG:%.*]] = sub i8 0, [[X]]<br></div><div>+; CHECK-NEXT:    [[XOR:%.*]] = select i1 [[COND]], i8 [[X_NEG]], i8 [[X]]<br></div><div>; CHECK-NEXT:    ret i8 [[XOR]]<br></div><div>;<br></div><div>   %cond.splat0 = sext i1 %cond to i8<br></div><div><br></div><div><br></div><div>        <br></div><div>_______________________________________________<br></div><div>llvm-commits mailing list<br></div><div><a href="mailto:llvm-commits@lists.llvm.org">llvm-commits@lists.llvm.org</a><br></div><div><a href="https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits">https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits</a><br></div><div><br></div></blockquote><div><br></div></body></html>