Implement DecomposeShift
authorMichelle McDaniel <adiaaida@gmail.com>
Tue, 30 Aug 2016 16:38:20 +0000 (09:38 -0700)
committerMichelle McDaniel <adiaaida@gmail.com>
Wed, 31 Aug 2016 02:24:24 +0000 (19:24 -0700)
This change adds support for GT_LSH, GT_RSH, and GT_RSZ for x86 longs.
These nodes are implemented via helpers, so in decompose, we must remove
the shift node from the tree and replace it with a call to the helper.
Additionally, these helpers require that the long operand is in EAX:EDX,
so rather than adding a GT_LONG to the call arg list like we would
normally, we split the high and low parts of the arg and mark them as
non-standard args in gtMorphArgs to force the low part to be in EAX and
the high part to be in EDX. The shift amount is already defaulted to ECX,
which is where the helper expects it to be.

src/jit/compiler.h
src/jit/decomposelongs.cpp
src/jit/decomposelongs.h
src/jit/gentree.cpp
src/jit/morph.cpp
src/jit/target.h

index 39b8121..2462c58 100644 (file)
@@ -1935,6 +1935,7 @@ public:
     GenTreeArgList* gtNewArgList(GenTreePtr op);
 
     GenTreeArgList* gtNewArgList(GenTreePtr op1, GenTreePtr op2);
+    GenTreeArgList* gtNewArgList(GenTreePtr op1, GenTreePtr op2, GenTreePtr op3);
 
     static fgArgTabEntryPtr gtArgEntryByArgNum(GenTreePtr call, unsigned argNum);
     static fgArgTabEntryPtr gtArgEntryByNode(GenTreePtr call, GenTreePtr node);
index 53d490d..d302e15 100644 (file)
@@ -223,7 +223,7 @@ GenTree* DecomposeLongs::DecomposeNode(LIR::Use& use)
         case GT_LSH:
         case GT_RSH:
         case GT_RSZ:
-            NYI("Arithmetic binary operators on TYP_LONG - SHIFT");
+            nextNode = DecomposeShift(use);
             break;
 
         case GT_ROL:
@@ -850,6 +850,86 @@ GenTree* DecomposeLongs::DecomposeArith(LIR::Use& use)
 }
 
 //------------------------------------------------------------------------
+// DecomposeShift: Decompose GT_LSH, GT_RSH, GT_RSZ. For shift nodes, we need to use
+// the shift helper functions, so we here convert the shift into a helper call by
+// pulling its arguments out of linear order and making them the args to a call, then
+// replacing the original node with the new call.
+//
+// Arguments:
+//    use - the LIR::Use object for the def that needs to be decomposed.
+//
+// Return Value:
+//    The next node to process.
+//
+GenTree* DecomposeLongs::DecomposeShift(LIR::Use& use)
+{
+    assert(use.IsInitialized());
+
+    GenTree* tree   = use.Def();
+    GenTree* gtLong = tree->gtGetOp1();
+    genTreeOps oper = tree->OperGet();
+
+    assert((oper == GT_LSH) || (oper == GT_RSH) || (oper == GT_RSZ));
+
+    unsigned blockWeight = m_block->getBBWeight(m_compiler);
+
+    LIR::Use loOp1Use(BlockRange(), &gtLong->gtOp.gtOp1, gtLong);
+    loOp1Use.ReplaceWithLclVar(m_compiler, blockWeight);
+
+    LIR::Use hiOp1Use(BlockRange(), &gtLong->gtOp.gtOp2, gtLong);
+    hiOp1Use.ReplaceWithLclVar(m_compiler, blockWeight);
+
+    LIR::Use shiftWidthUse(BlockRange(), &tree->gtOp.gtOp2, tree);
+    shiftWidthUse.ReplaceWithLclVar(m_compiler, blockWeight);
+
+    GenTree* loOp1 = gtLong->gtGetOp1();
+    GenTree* hiOp1 = gtLong->gtGetOp2();
+
+    GenTree* shiftWidthOp = tree->gtGetOp2();
+
+    BlockRange().Remove(gtLong);
+    BlockRange().Remove(loOp1);
+    BlockRange().Remove(hiOp1);
+
+    BlockRange().Remove(shiftWidthOp);
+
+    // TODO-X86-CQ: If the shift operand is a GT_CNS_INT, we should pipe the instructions through to codegen
+    // and generate the shift instructions ourselves there, rather than replacing it with a helper call.
+
+    unsigned helper;
+
+    switch (oper)
+    {
+        case GT_LSH:
+            helper = CORINFO_HELP_LLSH;
+            break;
+        case GT_RSH:
+            helper = CORINFO_HELP_LRSH;
+            break;
+        case GT_RSZ:
+            helper = CORINFO_HELP_LRSZ;
+            break;
+        default:
+            unreached();
+    }
+
+    GenTreeArgList* argList = m_compiler->gtNewArgList(loOp1, hiOp1, shiftWidthOp);
+
+    GenTree* call = m_compiler->gtNewHelperCallNode(helper, TYP_LONG, 0, argList);
+
+    GenTreeCall*    callNode    = call->AsCall();
+    ReturnTypeDesc* retTypeDesc = callNode->GetReturnTypeDesc();
+    retTypeDesc->InitializeLongReturnType(m_compiler);
+
+    call = m_compiler->fgMorphArgs(callNode);
+    BlockRange().InsertAfter(tree, LIR::SeqTree(m_compiler, call));
+    
+    BlockRange().Remove(tree);
+    use.ReplaceWith(m_compiler, call);
+    return call;
+}
+
+//------------------------------------------------------------------------
 // GetHiOper: Convert arithmetic operator to "high half" operator of decomposed node.
 //
 // Arguments:
index 523a06a..555bbc0 100644 (file)
@@ -47,6 +47,7 @@ private:
     GenTree* DecomposeNot(LIR::Use& use);
     GenTree* DecomposeNeg(LIR::Use& use);
     GenTree* DecomposeArith(LIR::Use& use);
+    GenTree* DecomposeShift(LIR::Use& use);
 
     // Helper functions
     GenTree* FinalizeDecomposition(LIR::Use& use, GenTree* loResult, GenTree* hiResult);
index 01c383a..57c9f78 100644 (file)
@@ -6388,6 +6388,16 @@ GenTreeArgList* Compiler::gtNewArgList(GenTreePtr arg1, GenTreePtr arg2)
 
 /*****************************************************************************
  *
+ *  Create a list out of the three values.
+ */
+
+GenTreeArgList* Compiler::gtNewArgList(GenTreePtr arg1, GenTreePtr arg2, GenTreePtr arg3)
+{
+    return new (this, GT_LIST) GenTreeArgList(arg1, gtNewArgList(arg2, arg3));
+}
+
+/*****************************************************************************
+ *
  *  Given a GT_CALL node, access the fgArgInfo and find the entry
  *  that has the matching argNum and return the fgArgTableEntryPtr
  */
index aea8431..e8c3463 100755 (executable)
@@ -2785,6 +2785,20 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* callNode)
             assert(arg1 != nullptr);
             nonStandardArgs.Add(arg1, REG_PINVOKE_FRAME);
         }
+        // The x86 shift helpers have custom calling conventions and expect the lo part of the long to be in EAX and the
+        // hi part to be in EDX. This sets the argument registers up correctly.
+        else if (call->IsHelperCall(this, CORINFO_HELP_LLSH) || call->IsHelperCall(this, CORINFO_HELP_LRSH) || call->IsHelperCall(this, CORINFO_HELP_LRSZ))
+        {
+            GenTreeArgList* args = call->gtCallArgs;
+            GenTree* arg1 = args->Current();
+            assert(arg1 != nullptr);
+            nonStandardArgs.Add(arg1, REG_LNGARG_LO);
+
+            args = args->Rest();
+            GenTree* arg2 = args->Current();
+            assert(arg2 != nullptr);
+            nonStandardArgs.Add(arg2, REG_LNGARG_HI);
+        }
 #endif // !defined(LEGACY_BACKEND) && defined(_TARGET_X86_)
 
 #if !defined(LEGACY_BACKEND) && !defined(_TARGET_X86_)
index 2cebb5d..54c6674 100644 (file)
@@ -549,6 +549,10 @@ typedef unsigned short regPairNoSmall; // arm: need 12 bits
   #define RBM_LNGARG_0            (RBM_EAX|RBM_EDX)
   #define PREDICT_PAIR_LNGARG_0    PREDICT_PAIR_EAXEDX
 
+  #define REG_LNGARG_LO             REG_EAX
+  #define RBM_LNGARG_LO             RBM_EAX
+  #define REG_LNGARG_HI             REG_EDX
+  #define RBM_LNGARG_HI             RBM_EDX
   // register to hold shift amount
   #define REG_SHIFT                REG_ECX
   #define RBM_SHIFT                RBM_ECX