JIT: Support converting OR(condition, condition) -> CCMP (#83089)
authorJakob Botsch Nielsen <Jakob.botsch.nielsen@gmail.com>
Wed, 8 Mar 2023 09:15:21 +0000 (10:15 +0100)
committerGitHub <noreply@github.com>
Wed, 8 Mar 2023 09:15:21 +0000 (10:15 +0100)
Also support generating ccmp for GC pointers, which fixes #82703.

Also makes the handling of operands symmetrical, so that the ccmp can be
generated for either op1 or op2 of the AND/OR.

src/coreclr/jit/lower.h
src/coreclr/jit/lowerarmarch.cpp

index 35f3423..2a7eb25 100644 (file)
@@ -85,7 +85,7 @@ private:
     void ContainCheckLclHeap(GenTreeOp* node);
     void ContainCheckRet(GenTreeUnOp* ret);
 #ifdef TARGET_ARM64
-    GenTree* TryLowerAndToCCMP(GenTreeOp* tree);
+    GenTree* TryLowerAndOrToCCMP(GenTreeOp* tree);
     insCflags TruthifyingFlags(GenCondition cond);
     void ContainCheckConditionalCompare(GenTreeCCMP* ccmp);
     void ContainCheckNeg(GenTreeOp* neg);
index 4de18ea..51a1a50 100644 (file)
@@ -457,32 +457,36 @@ GenTree* Lowering::LowerMul(GenTreeOp* mul)
 //
 GenTree* Lowering::LowerBinaryArithmetic(GenTreeOp* binOp)
 {
-    if (comp->opts.OptimizationEnabled() && binOp->OperIs(GT_AND))
+    if (comp->opts.OptimizationEnabled())
     {
-        GenTree* opNode  = nullptr;
-        GenTree* notNode = nullptr;
-        if (binOp->gtGetOp1()->OperIs(GT_NOT))
-        {
-            notNode = binOp->gtGetOp1();
-            opNode  = binOp->gtGetOp2();
-        }
-        else if (binOp->gtGetOp2()->OperIs(GT_NOT))
+        if (binOp->OperIs(GT_AND))
         {
-            notNode = binOp->gtGetOp2();
-            opNode  = binOp->gtGetOp1();
-        }
+            GenTree* opNode  = nullptr;
+            GenTree* notNode = nullptr;
+            if (binOp->gtGetOp1()->OperIs(GT_NOT))
+            {
+                notNode = binOp->gtGetOp1();
+                opNode  = binOp->gtGetOp2();
+            }
+            else if (binOp->gtGetOp2()->OperIs(GT_NOT))
+            {
+                notNode = binOp->gtGetOp2();
+                opNode  = binOp->gtGetOp1();
+            }
 
-        if (notNode != nullptr)
-        {
-            binOp->gtOp1 = opNode;
-            binOp->gtOp2 = notNode->AsUnOp()->gtGetOp1();
-            binOp->ChangeOper(GT_AND_NOT);
-            BlockRange().Remove(notNode);
+            if (notNode != nullptr)
+            {
+                binOp->gtOp1 = opNode;
+                binOp->gtOp2 = notNode->AsUnOp()->gtGetOp1();
+                binOp->ChangeOper(GT_AND_NOT);
+                BlockRange().Remove(notNode);
+            }
         }
+
 #ifdef TARGET_ARM64
-        else
+        if (binOp->OperIs(GT_AND, GT_OR))
         {
-            GenTree* next = TryLowerAndToCCMP(binOp);
+            GenTree* next = TryLowerAndOrToCCMP(binOp);
             if (next != nullptr)
             {
                 return next;
@@ -2268,14 +2272,14 @@ void Lowering::ContainCheckCompare(GenTreeOp* cmp)
 
 #ifdef TARGET_ARM64
 //------------------------------------------------------------------------
-// TryLowerAndToCCMP : Lower an and of two conditions into test + CCMP + SETCC nodes.
+// TryLowerAndOrToCCMP : Lower AND/OR of two conditions into test + CCMP + SETCC nodes.
 //
 // Arguments:
 //    tree - pointer to the node
 //
-GenTree* Lowering::TryLowerAndToCCMP(GenTreeOp* tree)
+GenTree* Lowering::TryLowerAndOrToCCMP(GenTreeOp* tree)
 {
-    assert(tree->OperIs(GT_AND));
+    assert(tree->OperIs(GT_AND, GT_OR));
 
     if (!comp->opts.OptimizationEnabled())
     {
@@ -2285,42 +2289,39 @@ GenTree* Lowering::TryLowerAndToCCMP(GenTreeOp* tree)
     GenTree* op1 = tree->gtGetOp1();
     GenTree* op2 = tree->gtGetOp2();
 
-    // Find out whether op2 is eligible to be converted to a conditional
+    if ((op1->OperIsCmpCompare() && varTypeIsIntegralOrI(op1->gtGetOp1())) ||
+        (op2->OperIsCmpCompare() && varTypeIsIntegralOrI(op2->gtGetOp1())))
+    {
+        JITDUMP("[%06u] is a potential candidate for CCMP:\n", Compiler::dspTreeID(tree));
+        DISPTREERANGE(BlockRange(), tree);
+        JITDUMP("\n");
+    }
+
+    // Find out whether an operand is eligible to be converted to a conditional
     // compare. It must be a normal integral relop; for example, we cannot
     // conditionally perform a floating point comparison and there is no "ctst"
     // instruction that would allow us to conditionally implement
     // TEST_EQ/TEST_NE.
     //
-    if (!op2->OperIsCmpCompare() || !varTypeIsIntegral(op2->gtGetOp1()))
-    {
-        return nullptr;
-    }
-
-    // For op1 we can allow more arbitrary operations that set the condition
-    // flags; the final transformation into the flags def is done by
-    // TryLowerConditionToFlagsNode below, but we have a quick early out here
-    // too.
+    // For the other operand we can allow more arbitrary operations that set
+    // the condition flags; the final transformation into the flags def is done
+    // by TryLowerConditionToFlagsNode.
     //
-    if (!op1->OperIsCompare() && !op1->OperIs(GT_SETCC))
+    GenCondition cond1;
+    if (op2->OperIsCmpCompare() && varTypeIsIntegralOrI(op2->gtGetOp1()) && IsInvariantInRange(op2, tree) &&
+        TryLowerConditionToFlagsNode(tree, op1, &cond1))
     {
-        return nullptr;
+        // Fall through, converting op2 to the CCMP
     }
-
-    JITDUMP("[%06u] is a candidate for CCMP:\n", Compiler::dspTreeID(tree));
-    DISPTREERANGE(BlockRange(), tree);
-    JITDUMP("\n");
-
-    // We leave checking invariance of op1 to tree to TryLowerConditionToFlagsNode.
-    if (!IsInvariantInRange(op2, tree))
+    else if (op1->OperIsCmpCompare() && varTypeIsIntegralOrI(op1->gtGetOp1()) && IsInvariantInRange(op1, tree) &&
+             TryLowerConditionToFlagsNode(tree, op2, &cond1))
     {
-        JITDUMP("  ..cannot move [%06u], bailing\n", Compiler::dspTreeID(op2));
-        return nullptr;
+        std::swap(op1, op2);
     }
-
-    GenCondition cond1;
-    if (!TryLowerConditionToFlagsNode(tree, op1, &cond1))
+    else
     {
-        JITDUMP("  ..could not turn [%06u] into a def of flags, bailing\n", Compiler::dspTreeID(op1));
+        JITDUMP("  ..could not turn [%06u] or [%06u] into a def of flags, bailing\n", Compiler::dspTreeID(op1),
+                Compiler::dspTreeID(op2));
         return nullptr;
     }
 
@@ -2336,10 +2337,24 @@ GenTree* Lowering::TryLowerAndToCCMP(GenTreeOp* tree)
     op2->gtGetOp2()->ClearContained();
 
     GenTreeCCMP* ccmp = op2->AsCCMP();
-    ccmp->gtCondition = cond1;
-    // If the first comparison fails, set the condition flags to something that
-    // makes the second one fail as well so that the overall AND failed.
-    ccmp->gtFlagsVal = TruthifyingFlags(GenCondition::Reverse(cond2));
+
+    if (tree->OperIs(GT_AND))
+    {
+        // If the first comparison succeeds then do the second comparison.
+        ccmp->gtCondition = cond1;
+        // Otherwise set the condition flags to something that makes the second
+        // one fail.
+        ccmp->gtFlagsVal = TruthifyingFlags(GenCondition::Reverse(cond2));
+    }
+    else
+    {
+        // If the first comparison fails then do the second comparison.
+        ccmp->gtCondition = GenCondition::Reverse(cond1);
+        // Otherwise set the condition flags to something that makes the second
+        // one succeed.
+        ccmp->gtFlagsVal = TruthifyingFlags(cond2);
+    }
+
     ContainCheckConditionalCompare(ccmp);
 
     tree->SetOper(GT_SETCC);