Implement DecomposeUMod
authorMichelle McDaniel <adiaaida@gmail.com>
Thu, 8 Sep 2016 15:52:37 +0000 (08:52 -0700)
committerMichelle McDaniel <adiaaida@gmail.com>
Tue, 13 Sep 2016 02:01:45 +0000 (19:01 -0700)
This change implements DecomposeUMod for x86 RyuJIT. The only GT_UMOD
nodes that make it to decompose are ones where op2 is a cast from a
constant int to long. Because op2 is an int, we can guarantee that the
result will be an int. Therefore, in decompose, we change the type of the
GT_UMOD to be TYP_INT, and replace op2 with its lo part. We set the high
part of the GT_LONG to be 0, since the divisor < 0x3fffffff. In lower, we need
to make sure that loOp1 is in RAX and hiOp1 is in RDX, which is where
idiv expects them to be. We also increase the number of sources since
there are now three sources. In codegen, we need to make sure that the hi
and lo parts of the dividend are in the correct registers for idiv, then
we can just use the normal logic for GT_UMOD.

src/jit/codegenxarch.cpp
src/jit/decomposelongs.cpp
src/jit/decomposelongs.h
src/jit/lower.cpp
src/jit/lowerxarch.cpp

index 17967cb..78f3bb6 100644 (file)
@@ -1281,7 +1281,11 @@ void CodeGen::genCodeForMulHi(GenTreeOp* treeNode)
     }
 }
 
-// generate code for a DIV or MOD operation
+//------------------------------------------------------------------------
+// genCodeForDivMod: Generate code for a DIV or MOD operation.
+//
+// Arguments:
+//    treeNode - the node to generate the code for
 //
 void CodeGen::genCodeForDivMod(GenTreeOp* treeNode)
 {
@@ -1293,8 +1297,27 @@ void CodeGen::genCodeForDivMod(GenTreeOp* treeNode)
     var_types  targetType = treeNode->TypeGet();
     emitter*   emit       = getEmitter();
 
-    // dividend is not contained.
-    assert(!dividend->isContained());
+#ifdef _TARGET_X86_
+    bool dividendIsLong = varTypeIsLong(dividend->TypeGet());
+    GenTree* dividendLo;
+    GenTree* dividendHi;
+
+    if (dividendIsLong)
+    {
+        // If dividend is a GT_LONG, the we need to make sure its lo and hi parts are not contained.
+        dividendLo = dividend->gtGetOp1();
+        dividendHi = dividend->gtGetOp2();
+
+        assert(!dividendLo->isContained());
+        assert(!dividendHi->isContained());
+        assert(divisor->IsCnsIntOrI());
+    }
+    else
+#endif
+    {
+        // dividend is not contained.
+        assert(!dividend->isContained());
+    }
 
     genConsumeOperands(treeNode->AsOp());
     if (varTypeIsFloating(targetType))
@@ -1329,22 +1352,44 @@ void CodeGen::genCodeForDivMod(GenTreeOp* treeNode)
     }
     else
     {
-        // dividend must be in RAX
+#ifdef _TARGET_X86_
+        if (dividendIsLong)
+        {
+            assert(dividendLo != nullptr && dividendHi != nullptr);
+
+            // dividendLo must be in RAX; dividendHi must be in RDX
+            if (dividendLo->gtRegNum != REG_EAX)
+            {
+                inst_RV_RV(INS_mov, REG_EAX, dividendLo->gtRegNum, targetType);
+            }
+            if (dividendHi->gtRegNum != REG_EDX)
+            {
+                inst_RV_RV(INS_mov, REG_EDX, dividendHi->gtRegNum, targetType);
+            }
+        }
+        else
+#endif
         if (dividend->gtRegNum != REG_RAX)
         {
+            // dividend must be in RAX
             inst_RV_RV(INS_mov, REG_RAX, dividend->gtRegNum, targetType);
         }
 
         // zero or sign extend rax to rdx
-        if (oper == GT_UMOD || oper == GT_UDIV)
-        {
-            instGen_Set_Reg_To_Zero(EA_PTRSIZE, REG_EDX);
-        }
-        else
+#ifdef _TARGET_X86_
+        if (!dividendIsLong)
+#endif
         {
-            emit->emitIns(INS_cdq, size);
-            // the cdq instruction writes RDX, So clear the gcInfo for RDX
-            gcInfo.gcMarkRegSetNpt(RBM_RDX);
+            if (oper == GT_UMOD || oper == GT_UDIV)
+            {
+                instGen_Set_Reg_To_Zero(EA_PTRSIZE, REG_EDX);
+            }
+            else
+            {
+                emit->emitIns(INS_cdq, size);
+                // the cdq instruction writes RDX, So clear the gcInfo for RDX
+                gcInfo.gcMarkRegSetNpt(RBM_RDX);
+            }
         }
 
         // Perform the 'targetType' (64-bit or 32-bit) divide instruction
index bf0c424..026d05a 100644 (file)
@@ -248,7 +248,7 @@ GenTree* DecomposeLongs::DecomposeNode(GenTree* tree)
             break;
 
         case GT_UMOD:
-            NYI("Arithmetic binary operators on TYP_LONG - GT_UMOD");
+            nextNode = DecomposeUMod(use);
             break;
 
         case GT_LSH:
@@ -1030,6 +1030,70 @@ GenTree* DecomposeLongs::DecomposeMul(LIR::Use& use)
 }
 
 //------------------------------------------------------------------------
+// DecomposeUMod: Decompose GT_UMOD. The only GT_UMODs that make it to decompose
+// are guaranteed to be an unsigned long mod with op2 which is a cast to long from
+// a constant int whose value is between 2 and 0x3fffffff. All other GT_UMODs are
+// morphed into helper calls. These GT_UMODs will actually return an int value in
+// RDX. In decompose, we make the lo operation a TYP_INT GT_UMOD, with op2 as the
+// original lo half and op1 as a GT_LONG. We make the hi part 0,  so we end up with:
+//
+// GT_UMOD[TYP_INT] ( GT_LONG [TYP_LONG] (loOp1, hiOp1), loOp2 [TYP_INT] )
+//
+// With the expectation that we will generate:
+//
+// EDX = hiOp1
+// EAX = loOp1
+// reg = loOp2
+// idiv reg
+// EDX is the remainder, and result of GT_UMOD
+// mov hiReg = 0
+//
+// Arguments:
+//    use - the LIR::Use object for the def that needs to be decomposed.
+//
+// Return Value:
+//    The next node to process.
+//
+GenTree* DecomposeLongs::DecomposeUMod(LIR::Use& use)
+{
+    assert(use.IsInitialized());
+
+    GenTree*   tree = use.Def();
+    genTreeOps oper = tree->OperGet();
+
+    assert(oper == GT_UMOD);
+
+    GenTree* op1 = tree->gtGetOp1();
+    GenTree* op2 = tree->gtGetOp2();
+    assert(op1->OperGet() == GT_LONG);
+    assert(op2->OperGet() == GT_LONG);
+
+    GenTree* loOp2 = op2->gtGetOp1();
+    GenTree* hiOp2 = op2->gtGetOp2();
+
+    assert(loOp2->OperGet() == GT_CNS_INT);
+    assert(hiOp2->OperGet() == GT_CNS_INT);
+    assert((loOp2->gtIntCon.gtIconVal >= 2) && (loOp2->gtIntCon.gtIconVal <= 0x3fffffff));
+    assert(hiOp2->gtIntCon.gtIconVal == 0);
+
+    // Get rid of op2's hi part. We don't need it.
+    Range().Remove(hiOp2);
+    Range().Remove(op2);
+
+    // Lo part is the GT_UMOD
+    GenTree* loResult = tree;
+    loResult->gtOp.gtOp2 = loOp2;
+    loResult->gtType = TYP_INT;
+
+    // Set the high part to 0
+    GenTree* hiResult = m_compiler->gtNewZeroConNode(TYP_INT);
+
+    Range().InsertAfter(loResult, hiResult);
+
+    return FinalizeDecomposition(use, loResult, hiResult);
+}
+
+//------------------------------------------------------------------------
 // StoreNodeToVar: Check if the user is a STORE_LCL_VAR, and if it isn't,
 // store the node to a var. Then decompose the new LclVar.
 //
index 2908d7c..9cb183f 100644 (file)
@@ -52,6 +52,7 @@ private:
     GenTree* DecomposeArith(LIR::Use& use);
     GenTree* DecomposeShift(LIR::Use& use);
     GenTree* DecomposeMul(LIR::Use& use);
+    GenTree* DecomposeUMod(LIR::Use& use);
 
     // Helper functions
     GenTree* FinalizeDecomposition(LIR::Use& use, GenTree* loResult, GenTree* hiResult);
index c095411..bca72b7 100644 (file)
@@ -3220,8 +3220,13 @@ void Lowering::LowerUnsignedDivOrMod(GenTree* node)
     assert((node->OperGet() == GT_UDIV) || (node->OperGet() == GT_UMOD));
 
     GenTree* divisor = node->gtGetOp2();
+    GenTree* dividend = node->gtGetOp1();
 
-    if (divisor->IsCnsIntOrI())
+    if (divisor->IsCnsIntOrI()
+#ifdef _TARGET_X86_
+            && (dividend->OperGet() != GT_LONG)
+#endif
+            )
     {
         size_t divisorValue = static_cast<size_t>(divisor->gtIntCon.IconValue());
 
index 785992e..72c1ec8 100644 (file)
@@ -2202,8 +2202,26 @@ void Lowering::TreeNodeInfoInitModDiv(GenTree* tree)
         info->setDstCandidates(l, RBM_RAX);
     }
 
-    // If possible would like to have op1 in RAX to avoid a register move
-    op1->gtLsraInfo.setSrcCandidates(l, RBM_RAX);
+#ifdef _TARGET_X86_
+    if (op1->OperGet() == GT_LONG)
+    {
+        // To avoid reg move would like to have op1's low part in RAX and high part in RDX.
+        GenTree* loVal = op1->gtGetOp1();
+        GenTree* hiVal = op1->gtGetOp2();
+        
+        // Src count is actually 3, so increment.
+        assert(op2->IsCnsIntOrI());
+        info->srcCount++;
+
+        loVal->gtLsraInfo.setSrcCandidates(l, RBM_EAX);
+        hiVal->gtLsraInfo.setSrcCandidates(l, RBM_EDX);
+    }
+    else
+#endif
+    {
+        // If possible would like to have op1 in RAX to avoid a register move
+        op1->gtLsraInfo.setSrcCandidates(l, RBM_RAX);
+    }
 
     // divisor can be an r/m, but the memory indirection must be of the same size as the divide
     if (op2->isMemoryOp() && (op2->TypeGet() == tree->TypeGet()))