summaryrefslogtreecommitdiff
path: root/src/jit
diff options
context:
space:
mode:
authorMichelle McDaniel <adiaaida@gmail.com>2016-05-31 08:40:16 -0700
committerMichelle McDaniel <adiaaida@gmail.com>2016-06-22 14:05:58 -0700
commit9237cec8009c478e76c6193a5ea628fc8aaa3bf1 (patch)
tree45e631924ec019cb49b1dad9102c22b3d3a2a7ef /src/jit
parent702b73a71b67992bad105c482f5641dfb84893e1 (diff)
downloadcoreclr-9237cec8009c478e76c6193a5ea628fc8aaa3bf1.tar.gz
coreclr-9237cec8009c478e76c6193a5ea628fc8aaa3bf1.tar.bz2
coreclr-9237cec8009c478e76c6193a5ea628fc8aaa3bf1.zip
Enable GT_CALL with long ret types for x86 RyuJIT
1) Enables genMultiRegCallStoreToLocal for x86 RyuJIT. Forces long return types to be stored to the stack after returning from a call. 2) Update lvaPromoteLongVars to not promote a long if it is a multi reg arg or ret. 3) Adds NYI for call arguments that are longs and contain adds or subtracts. We are currently producing the wrong order of operations so that we can push the argument immediately after performing the arithmetic. We will do the hi adc/sbb before the carry bit has been set by doing the lo operation. 4) Adds an NYI for morphing a node into a call node if the call will have a long return type. 5) Moves the logic for forcing var = call() for calls with long return types to lower::DecomposeNode().
Diffstat (limited to 'src/jit')
-rwxr-xr-xsrc/jit/codegenxarch.cpp49
-rw-r--r--src/jit/compiler.h20
-rw-r--r--src/jit/gentree.cpp2
-rw-r--r--src/jit/importer.cpp24
-rw-r--r--src/jit/lclvars.cpp2
-rwxr-xr-xsrc/jit/lower.cpp50
-rw-r--r--src/jit/morph.cpp13
7 files changed, 129 insertions, 31 deletions
diff --git a/src/jit/codegenxarch.cpp b/src/jit/codegenxarch.cpp
index 84803f07ba..621d606d6e 100755
--- a/src/jit/codegenxarch.cpp
+++ b/src/jit/codegenxarch.cpp
@@ -2871,9 +2871,54 @@ CodeGen::genMultiRegCallStoreToLocal(GenTreePtr treeNode)
varDsc->lvRegNum = REG_STK;
}
-#else // !FEATURE_UNIX_AMD64_STRUCT_PASSING
+#elif defined(_TARGET_X86_)
+ // Longs are returned in two return registers on x86.
+ assert(varTypeIsLong(treeNode));
+
+ // Assumption: current x86 implementation requires that a multi-reg long
+ // var in 'var = call' is flagged as lvIsMultiRegArgOrRet to prevent it from
+ // being promoted.
+ unsigned lclNum = treeNode->AsLclVarCommon()->gtLclNum;
+ LclVarDsc* varDsc = &(compiler->lvaTable[lclNum]);
+ noway_assert(varDsc->lvIsMultiRegArgOrRet);
+
+ GenTree* op1 = treeNode->gtGetOp1();
+ GenTree* actualOp1 = op1->gtSkipReloadOrCopy();
+ GenTreeCall* call = actualOp1->AsCall();
+ assert(call->HasMultiRegRetVal());
+
+ genConsumeRegs(op1);
+
+ ReturnTypeDesc* retTypeDesc = call->GetReturnTypeDesc();
+ unsigned regCount = retTypeDesc->GetReturnRegCount();
+ assert(regCount == MAX_RET_REG_COUNT);
+
+ // Stack store
+ int offset = 0;
+ for (unsigned i = 0; i < regCount; ++i)
+ {
+ var_types type = retTypeDesc->GetReturnRegType(i);
+ regNumber reg = call->GetRegNumByIdx(i);
+ if (op1->IsCopyOrReload())
+ {
+ // GT_COPY/GT_RELOAD will have valid reg for those positions
+ // that need to be copied or reloaded.
+ regNumber reloadReg = op1->AsCopyOrReload()->GetRegNumByIdx(i);
+ if (reloadReg != REG_NA)
+ {
+ reg = reloadReg;
+ }
+ }
+
+ assert(reg != REG_NA);
+ getEmitter()->emitIns_S_R(ins_Store(type), emitTypeSize(type), reg, lclNum, offset);
+ offset += genTypeSize(type);
+ }
+
+ varDsc->lvRegNum = REG_STK;
+#else // !FEATURE_UNIX_AMD64_STRUCT_PASSING && !_TARGET_X86_
assert(!"Unreached");
-#endif // !FEATURE_UNIX_AMD64_STRUCT_PASSING
+#endif // !FEATURE_UNIX_AMD64_STRUCT_PASSING && !_TARGET_X86_
}
diff --git a/src/jit/compiler.h b/src/jit/compiler.h
index 85e8db15b3..6927da1acc 100644
--- a/src/jit/compiler.h
+++ b/src/jit/compiler.h
@@ -2728,8 +2728,8 @@ protected :
GenTreePtr impFixupCallStructReturn(GenTreePtr call,
CORINFO_CLASS_HANDLE retClsHnd);
- GenTreePtr impFixupCallLongReturn(GenTreePtr call,
- CORINFO_CLASS_HANDLE retClsHnd);
+ GenTreePtr impInitCallReturnTypeDesc(GenTreePtr call,
+ CORINFO_CLASS_HANDLE retClsHnd);
GenTreePtr impFixupStructReturnType(GenTreePtr op,
CORINFO_CLASS_HANDLE retClsHnd);
@@ -4703,6 +4703,7 @@ private:
GenTreePtr fgAssignStructInlineeToVar(GenTreePtr child, CORINFO_CLASS_HANDLE retClsHnd);
void fgAttachStructInlineeToAsg(GenTreePtr tree, GenTreePtr child, CORINFO_CLASS_HANDLE retClsHnd);
#endif // defined(FEATURE_HFA) || defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
+
static fgWalkPreFn fgUpdateInlineReturnExpressionPlaceHolder;
#ifdef DEBUG
@@ -7988,13 +7989,22 @@ public :
// TODO-ARM64: Does this apply for ARM64 too?
bool compMethodReturnsMultiRegRetType()
{
-#if FEATURE_MULTIREG_RET && (defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) || defined(_TARGET_ARM_))
+#if FEATURE_MULTIREG_RET
+
+#if (defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) || defined(_TARGET_ARM_))
// Methods returning a struct in two registers is considered having a return value of TYP_STRUCT.
// Such method's compRetNativeType is TYP_STRUCT without a hidden RetBufArg
return varTypeIsStruct(info.compRetNativeType) && (info.compRetBuffArg == BAD_VAR_NUM);
-#else
+#elif defined(_TARGET_X86_)
+ // Longs are returned in two registers on x86
+ return varTypeIsLong(info.compRetNativeType);
+#else
+ unreached();
+#endif
+
+#else
return false;
-#endif // FEATURE_MULTIREG_RET && (defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) || defined(_TARGET_ARM_))
+#endif // FEATURE_MULTIREG_RET
}
diff --git a/src/jit/gentree.cpp b/src/jit/gentree.cpp
index dec3497466..ba4dabddfd 100644
--- a/src/jit/gentree.cpp
+++ b/src/jit/gentree.cpp
@@ -6826,7 +6826,7 @@ GenTreePtr Compiler::gtCloneExpr(GenTree * tree,
}
copy->gtCall.gtRetClsHnd = tree->gtCall.gtRetClsHnd;
-#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING
+#if FEATURE_MULTIREG_RET
copy->gtCall.gtReturnTypeDesc = tree->gtCall.gtReturnTypeDesc;
#endif
diff --git a/src/jit/importer.cpp b/src/jit/importer.cpp
index f2f8388df2..7410147ba8 100644
--- a/src/jit/importer.cpp
+++ b/src/jit/importer.cpp
@@ -7185,7 +7185,7 @@ DONE_CALL:
}
else if (varTypeIsLong(callRetTyp))
{
- call = impFixupCallLongReturn(call, sig->retTypeClass);
+ call = impInitCallReturnTypeDesc(call, sig->retTypeClass);
}
if ((call->gtFlags & GTF_CALL_INLINE_CANDIDATE) != 0)
@@ -7471,18 +7471,17 @@ GenTreePtr Compiler::impFixupCallStructReturn(GenTreePtr call
//-----------------------------------------------------------------------------------
-// impFixupCallLongReturn: For a call node that returns a long type, force the call
-// to always be in the IR form tmp = call
+// impInitCallReturnTypeDesc: Initialize the ReturnTypDesc for a call node
//
// Arguments:
// call - GT_CALL GenTree node
// retClsHnd - Class handle of return type of the call
//
// Return Value:
-// Returns new GenTree node after fixing long return of call node
+// Returns new GenTree node after initializing the ReturnTypeDesc of call node
//
-GenTreePtr Compiler::impFixupCallLongReturn(GenTreePtr call,
- CORINFO_CLASS_HANDLE retClsHnd)
+GenTreePtr Compiler::impInitCallReturnTypeDesc(GenTreePtr call,
+ CORINFO_CLASS_HANDLE retClsHnd)
{
#if defined(_TARGET_X86_) && !defined(LEGACY_BACKEND)
// LEGACY_BACKEND does not use multi reg returns for calls with long return types
@@ -7507,17 +7506,6 @@ GenTreePtr Compiler::impFixupCallLongReturn(GenTreePtr call,
unsigned retRegCount = retTypeDesc->GetReturnRegCount();
// must be a long returned in two registers
assert(retRegCount == 2);
-
- if ((!callNode->CanTailCall()) && (!callNode->IsInlineCandidate()))
- {
- // Force a call returning multi-reg long to be always of the IR form
- // tmp = call
- //
- // No need to assign a multi-reg long to a local var if:
- // - It is a tail call or
- // - The call is marked for in-lining later
- return impAssignMultiRegTypeToVar(call, retClsHnd);
- }
#endif // _TARGET_X86_ && !LEGACY_BACKEND
return call;
@@ -14003,8 +13991,6 @@ GenTreePtr Compiler::impAssignMultiRegTypeToVar(GenTreePtr op, CORINFO_CLASS_HAN
// Mark the var so that fields are not promoted and stay together.
lvaTable[tmpNum].lvIsMultiRegArgOrRet = true;
-#elif defined(_TARGET_X86_) && !defined(LEGACY_BACKEND)
- lvaTable[tmpNum].lvIsMultiRegArgOrRet = true;
#endif // defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
return ret;
diff --git a/src/jit/lclvars.cpp b/src/jit/lclvars.cpp
index eae804a429..2d21856041 100644
--- a/src/jit/lclvars.cpp
+++ b/src/jit/lclvars.cpp
@@ -1747,7 +1747,7 @@ void Compiler::lvaPromoteLongVars()
lclNum++)
{
LclVarDsc * varDsc = &lvaTable[lclNum];
- if(!varTypeIsLong(varDsc) || varDsc->lvDoNotEnregister || (varDsc->lvRefCnt == 0))
+ if(!varTypeIsLong(varDsc) || varDsc->lvDoNotEnregister || varDsc->lvIsMultiRegArgOrRet || (varDsc->lvRefCnt == 0))
{
continue;
}
diff --git a/src/jit/lower.cpp b/src/jit/lower.cpp
index 6fd97cb2e3..4ce6ac9b80 100755
--- a/src/jit/lower.cpp
+++ b/src/jit/lower.cpp
@@ -265,8 +265,10 @@ void Lowering::DecomposeNode(GenTreePtr* pTree, Compiler::fgWalkData* data)
{
GenTree* nextTree = tree->gtNext;
GenTree* rhs = tree->gtGetOp1();
- if (rhs->OperGet() == GT_PHI)
+ if (rhs->OperGet() == GT_PHI || rhs->OperGet() == GT_CALL)
{
+ // GT_CALLs are not decomposed, so will not be converted to GT_LONG
+ // GT_STORE_LCL_VAR = GT_CALL are handled in genMultiRegCallStoreToLocal
break;
}
noway_assert(rhs->OperGet() == GT_LONG);
@@ -381,8 +383,48 @@ void Lowering::DecomposeNode(GenTreePtr* pTree, Compiler::fgWalkData* data)
}
break;
case GT_CALL:
- NYI("Call with TYP_LONG return value");
- break;
+ {
+ GenTree* parent = data->parent;
+
+ // We only need to force var = call() if the call is not a top-level node.
+ if (parent != nullptr)
+ {
+ if (parent->gtOper == GT_STORE_LCL_VAR)
+ {
+ // If parent is already a STORE_LCL_VAR, we can skip it if
+ // it is already marked as lvIsMultiRegArgOrRet
+ unsigned varNum = parent->AsLclVarCommon()->gtLclNum;
+ if (comp->lvaTable[varNum].lvIsMultiRegArgOrRet)
+ {
+ break;
+ }
+ else if (!comp->lvaTable[varNum].lvPromoted)
+ {
+ // If var wasn't promoted, we can just set lvIsMultiRegArgOrRet
+ comp->lvaTable[varNum].lvIsMultiRegArgOrRet = true;
+ break;
+ }
+ }
+
+ // Otherwise, we need to force var = call()
+ GenTreePtr* treePtr = nullptr;
+ parent = tree->gtGetParent(&treePtr);
+
+ assert(treePtr != nullptr);
+
+ GenTreeStmt* asgStmt = comp->fgInsertEmbeddedFormTemp(treePtr);
+ GenTreePtr stLclVar = asgStmt->gtStmtExpr;
+ assert(stLclVar->OperIsLocalStore());
+
+ unsigned varNum = stLclVar->AsLclVarCommon()->gtLclNum;
+ comp->lvaTable[varNum].lvIsMultiRegArgOrRet = true;
+ comp->fgFixupIfCallArg(data->parentStack, tree, *treePtr);
+
+ // Decompose new node
+ DecomposeNode(treePtr, data);
+ }
+ break;
+ }
case GT_RETURN:
assert(tree->gtOp.gtOp1->OperGet() == GT_LONG);
break;
@@ -1856,6 +1898,8 @@ void Lowering::LowerArg(GenTreeCall* call, GenTreePtr* ppArg)
GenTreePtr argLo = arg->gtGetOp1();
GenTreePtr argHi = arg->gtGetOp2();
+ NYI_IF(argHi->OperGet() == GT_ADD_HI || argHi->OperGet() == GT_SUB_HI, "Hi and Lo cannot be reordered");
+
GenTreePtr putArgLo = NewPutArg(call, argLo, info, type);
GenTreePtr putArgHi = NewPutArg(call, argHi, info, type);
diff --git a/src/jit/morph.cpp b/src/jit/morph.cpp
index 9c231cb35f..0223c037af 100644
--- a/src/jit/morph.cpp
+++ b/src/jit/morph.cpp
@@ -83,6 +83,19 @@ GenTreePtr Compiler::fgMorphIntoHelperCall(GenTreePtr tree,
tree->gtCall.gtEntryPoint.addr = nullptr;
#endif
+#if defined(_TARGET_X86_) && !defined(LEGACY_BACKEND)
+ if (varTypeIsLong(tree))
+ {
+ GenTreeCall* callNode = tree->AsCall();
+ ReturnTypeDesc* retTypeDesc = callNode->GetReturnTypeDesc();
+ retTypeDesc->Reset();
+ retTypeDesc->Initialize(this, callNode->gtRetClsHnd);
+ callNode->ClearOtherRegs();
+
+ NYI("Helper with TYP_LONG return type");
+ }
+#endif
+
/* Perform the morphing */
tree = fgMorphArgs(tree->AsCall());