From ad2a6674ba367b29db7d5cacc31d9050a5849ef6 Mon Sep 17 00:00:00 2001 From: Michelle McDaniel Date: Tue, 30 Aug 2016 09:38:20 -0700 Subject: [PATCH] Implement DecomposeShift 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. Commit migrated from https://github.com/dotnet/coreclr/commit/c06348f8f08356a081ab3713a6d749b7da2f79a9 --- src/coreclr/src/jit/compiler.h | 1 + src/coreclr/src/jit/decomposelongs.cpp | 82 +++++++++++++++++++++++++++++++++- src/coreclr/src/jit/decomposelongs.h | 1 + src/coreclr/src/jit/gentree.cpp | 10 +++++ src/coreclr/src/jit/morph.cpp | 14 ++++++ src/coreclr/src/jit/target.h | 4 ++ 6 files changed, 111 insertions(+), 1 deletion(-) diff --git a/src/coreclr/src/jit/compiler.h b/src/coreclr/src/jit/compiler.h index 39b8121..2462c58 100644 --- a/src/coreclr/src/jit/compiler.h +++ b/src/coreclr/src/jit/compiler.h @@ -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); diff --git a/src/coreclr/src/jit/decomposelongs.cpp b/src/coreclr/src/jit/decomposelongs.cpp index 53d490d..d302e15 100644 --- a/src/coreclr/src/jit/decomposelongs.cpp +++ b/src/coreclr/src/jit/decomposelongs.cpp @@ -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(), >Long->gtOp.gtOp1, gtLong); + loOp1Use.ReplaceWithLclVar(m_compiler, blockWeight); + + LIR::Use hiOp1Use(BlockRange(), >Long->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: diff --git a/src/coreclr/src/jit/decomposelongs.h b/src/coreclr/src/jit/decomposelongs.h index 523a06a..555bbc0 100644 --- a/src/coreclr/src/jit/decomposelongs.h +++ b/src/coreclr/src/jit/decomposelongs.h @@ -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); diff --git a/src/coreclr/src/jit/gentree.cpp b/src/coreclr/src/jit/gentree.cpp index 01c383a..57c9f78 100644 --- a/src/coreclr/src/jit/gentree.cpp +++ b/src/coreclr/src/jit/gentree.cpp @@ -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 */ diff --git a/src/coreclr/src/jit/morph.cpp b/src/coreclr/src/jit/morph.cpp index aea8431..e8c3463 100755 --- a/src/coreclr/src/jit/morph.cpp +++ b/src/coreclr/src/jit/morph.cpp @@ -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_) diff --git a/src/coreclr/src/jit/target.h b/src/coreclr/src/jit/target.h index 2cebb5d..54c6674 100644 --- a/src/coreclr/src/jit/target.h +++ b/src/coreclr/src/jit/target.h @@ -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 -- 2.7.4