summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSergey Andreenko <seandree@microsoft.com>2018-03-07 17:26:27 -0800
committerGitHub <noreply@github.com>2018-03-07 17:26:27 -0800
commitc9eaaacdd8800cc813c2588d47a49f65a69d6aae (patch)
tree87718cfa33ad67b89c4761afb4bf4b94769999b5 /src
parent3f93e7da18231da6e7e2a1cb956851d85ae55ee6 (diff)
downloadcoreclr-c9eaaacdd8800cc813c2588d47a49f65a69d6aae.tar.gz
coreclr-c9eaaacdd8800cc813c2588d47a49f65a69d6aae.tar.bz2
coreclr-c9eaaacdd8800cc813c2588d47a49f65a69d6aae.zip
[RyuJIt] Clean and fix gen return (#16725)
* create genSimpleReturn for arm64 * create genFloatReturn for x86 * create genLongReturn for x86 and arm * merge genReturn
Diffstat (limited to 'src')
-rw-r--r--src/jit/codegenarm.cpp89
-rw-r--r--src/jit/codegenarm64.cpp87
-rw-r--r--src/jit/codegencommon.cpp199
-rw-r--r--src/jit/codegenlinear.h12
-rw-r--r--src/jit/codegenxarch.cpp193
5 files changed, 266 insertions, 314 deletions
diff --git a/src/jit/codegenarm.cpp b/src/jit/codegenarm.cpp
index b5f9d7c323..60f11fb791 100644
--- a/src/jit/codegenarm.cpp
+++ b/src/jit/codegenarm.cpp
@@ -240,95 +240,6 @@ void CodeGen::genCodeForBinary(GenTree* treeNode)
}
//------------------------------------------------------------------------
-// genReturn: Generates code for return statement.
-// In case of struct return, delegates to the genStructReturn method.
-//
-// Arguments:
-// treeNode - The GT_RETURN or GT_RETFILT tree node.
-//
-// Return Value:
-// None
-//
-void CodeGen::genReturn(GenTree* treeNode)
-{
- assert(treeNode->OperGet() == GT_RETURN || treeNode->OperGet() == GT_RETFILT);
- GenTree* op1 = treeNode->gtGetOp1();
- var_types targetType = treeNode->TypeGet();
-
- // A void GT_RETFILT is the end of a finally. For non-void filter returns we need to load the result in the return
- // register, if it's not already there. The processing is the same as GT_RETURN. For filters, the IL spec says the
- // result is type int32. Further, the only legal values are 0 or 1; the use of other values is "undefined".
- assert(!treeNode->OperIs(GT_RETFILT) || (targetType == TYP_VOID) || (targetType == TYP_INT));
-
-#ifdef DEBUG
- if (targetType == TYP_VOID)
- {
- assert(op1 == nullptr);
- }
-#endif
-
- if (treeNode->TypeGet() == TYP_LONG)
- {
- assert(op1 != nullptr);
- noway_assert(op1->OperGet() == GT_LONG);
- GenTree* loRetVal = op1->gtGetOp1();
- GenTree* hiRetVal = op1->gtGetOp2();
- noway_assert((loRetVal->gtRegNum != REG_NA) && (hiRetVal->gtRegNum != REG_NA));
-
- genConsumeReg(loRetVal);
- genConsumeReg(hiRetVal);
- if (loRetVal->gtRegNum != REG_LNGRET_LO)
- {
- inst_RV_RV(ins_Copy(targetType), REG_LNGRET_LO, loRetVal->gtRegNum, TYP_INT);
- }
- if (hiRetVal->gtRegNum != REG_LNGRET_HI)
- {
- inst_RV_RV(ins_Copy(targetType), REG_LNGRET_HI, hiRetVal->gtRegNum, TYP_INT);
- }
- }
- else
- {
- if (isStructReturn(treeNode))
- {
- genStructReturn(treeNode);
- }
- else if (targetType != TYP_VOID)
- {
- assert(op1 != nullptr);
- noway_assert(op1->gtRegNum != REG_NA);
-
- // !! NOTE !! genConsumeReg will clear op1 as GC ref after it has
- // consumed a reg for the operand. This is because the variable
- // is dead after return. But we are issuing more instructions
- // like "profiler leave callback" after this consumption. So
- // if you are issuing more instructions after this point,
- // remember to keep the variable live up until the new method
- // exit point where it is actually dead.
- genConsumeReg(op1);
-
- regNumber retReg = varTypeIsFloating(treeNode) ? REG_FLOATRET : REG_INTRET;
- if (varTypeIsFloating(treeNode) && (compiler->opts.compUseSoftFP || compiler->info.compIsVarArgs))
- {
- if (targetType == TYP_FLOAT)
- {
- getEmitter()->emitIns_R_R(INS_vmov_f2i, EA_4BYTE, REG_INTRET, op1->gtRegNum);
- }
- else
- {
- assert(targetType == TYP_DOUBLE);
- getEmitter()->emitIns_R_R_R(INS_vmov_d2i, EA_8BYTE, REG_INTRET, REG_NEXT(REG_INTRET),
- op1->gtRegNum);
- }
- }
- else if (op1->gtRegNum != retReg)
- {
- inst_RV_RV(ins_Move_Extend(targetType, true), retReg, op1->gtRegNum, targetType);
- }
- }
- }
-}
-
-//------------------------------------------------------------------------
// genLockedInstructions: Generate code for the locked operations.
//
// Notes:
diff --git a/src/jit/codegenarm64.cpp b/src/jit/codegenarm64.cpp
index 44a5661b3d..efefbf10ad 100644
--- a/src/jit/codegenarm64.cpp
+++ b/src/jit/codegenarm64.cpp
@@ -1775,91 +1775,56 @@ void CodeGen::genCodeForStoreLclVar(GenTreeLclVar* tree)
}
//------------------------------------------------------------------------
-// genReturn: Generates code for return statement.
-// In case of struct return, delegates to the genStructReturn method.
+// genSimpleReturn: Generates code for simple return statement for arm64.
+//
+// Note: treeNode's and op1's registers are already consumed.
//
// Arguments:
-// treeNode - The GT_RETURN or GT_RETFILT tree node.
+// treeNode - The GT_RETURN or GT_RETFILT tree node with non-struct and non-void type
//
// Return Value:
// None
//
-void CodeGen::genReturn(GenTree* treeNode)
+void CodeGen::genSimpleReturn(GenTree* treeNode)
{
assert(treeNode->OperGet() == GT_RETURN || treeNode->OperGet() == GT_RETFILT);
GenTree* op1 = treeNode->gtGetOp1();
var_types targetType = treeNode->TypeGet();
- // A void GT_RETFILT is the end of a finally. For non-void filter returns we need to load the result in the return
- // register, if it's not already there. The processing is the same as GT_RETURN. For filters, the IL spec says the
- // result is type int32. Further, the only legal values are 0 or 1; the use of other values is "undefined".
- assert(!treeNode->OperIs(GT_RETFILT) || (targetType == TYP_VOID) || (targetType == TYP_INT));
-
-#ifdef DEBUG
- if (targetType == TYP_VOID)
- {
- assert(op1 == nullptr);
- }
-#endif
-
- if (isStructReturn(treeNode))
- {
- genStructReturn(treeNode);
- }
- else if (targetType != TYP_VOID)
- {
- assert(op1 != nullptr);
- noway_assert(op1->gtRegNum != REG_NA);
-
- genConsumeReg(op1);
+ assert(!isStructReturn(treeNode));
+ assert(targetType != TYP_VOID);
- regNumber retReg = varTypeIsFloating(treeNode) ? REG_FLOATRET : REG_INTRET;
+ regNumber retReg = varTypeIsFloating(treeNode) ? REG_FLOATRET : REG_INTRET;
- bool movRequired = (op1->gtRegNum != retReg);
+ bool movRequired = (op1->gtRegNum != retReg);
- if (!movRequired)
+ if (!movRequired)
+ {
+ if (op1->OperGet() == GT_LCL_VAR)
{
- if (op1->OperGet() == GT_LCL_VAR)
+ GenTreeLclVarCommon* lcl = op1->AsLclVarCommon();
+ bool isRegCandidate = compiler->lvaTable[lcl->gtLclNum].lvIsRegCandidate();
+ if (isRegCandidate && ((op1->gtFlags & GTF_SPILLED) == 0))
{
- GenTreeLclVarCommon* lcl = op1->AsLclVarCommon();
- bool isRegCandidate = compiler->lvaTable[lcl->gtLclNum].lvIsRegCandidate();
- if (isRegCandidate && ((op1->gtFlags & GTF_SPILLED) == 0))
- {
- // We may need to generate a zero-extending mov instruction to load the value from this GT_LCL_VAR
+ // We may need to generate a zero-extending mov instruction to load the value from this GT_LCL_VAR
- unsigned lclNum = lcl->gtLclNum;
- LclVarDsc* varDsc = &(compiler->lvaTable[lclNum]);
- var_types op1Type = genActualType(op1->TypeGet());
- var_types lclType = genActualType(varDsc->TypeGet());
+ unsigned lclNum = lcl->gtLclNum;
+ LclVarDsc* varDsc = &(compiler->lvaTable[lclNum]);
+ var_types op1Type = genActualType(op1->TypeGet());
+ var_types lclType = genActualType(varDsc->TypeGet());
- if (genTypeSize(op1Type) < genTypeSize(lclType))
- {
- movRequired = true;
- }
+ if (genTypeSize(op1Type) < genTypeSize(lclType))
+ {
+ movRequired = true;
}
}
}
-
- if (movRequired)
- {
- emitAttr attr = emitActualTypeSize(targetType);
- getEmitter()->emitIns_R_R(INS_mov, attr, retReg, op1->gtRegNum);
- }
}
-
-#ifdef PROFILING_SUPPORTED
- // There will be a single return block while generating profiler ELT callbacks.
- //
- // Reason for not materializing Leave callback as a GT_PROF_HOOK node after GT_RETURN:
- // In flowgraph and other places assert that the last node of a block marked as
- // GT_RETURN is either a GT_RETURN or GT_JMP or a tail call. It would be nice to
- // maintain such an invariant irrespective of whether profiler hook needed or not.
- // Also, there is not much to be gained by materializing it as an explicit node.
- if (compiler->compCurBB == compiler->genReturnBB)
+ if (movRequired)
{
- genProfilingLeaveCallback();
+ emitAttr attr = emitActualTypeSize(targetType);
+ getEmitter()->emitIns_R_R(INS_mov, attr, retReg, op1->gtRegNum);
}
-#endif
}
/***********************************************************************************************
diff --git a/src/jit/codegencommon.cpp b/src/jit/codegencommon.cpp
index f57abb06d6..22491c8bda 100644
--- a/src/jit/codegencommon.cpp
+++ b/src/jit/codegencommon.cpp
@@ -12783,4 +12783,203 @@ GenTreeIntCon CodeGen::intForm(var_types type, ssize_t value)
return i;
}
+#if defined(_TARGET_X86_) || defined(_TARGET_ARM_)
+//------------------------------------------------------------------------
+// genLongReturn: Generates code for long return statement for x86 and arm.
+//
+// Note: treeNode's and op1's registers are already consumed.
+//
+// Arguments:
+// treeNode - The GT_RETURN or GT_RETFILT tree node with LONG return type.
+//
+// Return Value:
+// None
+//
+void CodeGen::genLongReturn(GenTree* treeNode)
+{
+ assert(treeNode->OperGet() == GT_RETURN || treeNode->OperGet() == GT_RETFILT);
+ assert(treeNode->TypeGet() == TYP_LONG);
+ GenTree* op1 = treeNode->gtGetOp1();
+ var_types targetType = treeNode->TypeGet();
+
+ assert(op1 != nullptr);
+ assert(op1->OperGet() == GT_LONG);
+ GenTree* loRetVal = op1->gtGetOp1();
+ GenTree* hiRetVal = op1->gtGetOp2();
+ assert((loRetVal->gtRegNum != REG_NA) && (hiRetVal->gtRegNum != REG_NA));
+
+ genConsumeReg(loRetVal);
+ genConsumeReg(hiRetVal);
+ if (loRetVal->gtRegNum != REG_LNGRET_LO)
+ {
+ inst_RV_RV(ins_Copy(targetType), REG_LNGRET_LO, loRetVal->gtRegNum, TYP_INT);
+ }
+ if (hiRetVal->gtRegNum != REG_LNGRET_HI)
+ {
+ inst_RV_RV(ins_Copy(targetType), REG_LNGRET_HI, hiRetVal->gtRegNum, TYP_INT);
+ }
+}
+#endif // _TARGET_X86_ || _TARGET_ARM_
+
+//------------------------------------------------------------------------
+// genReturn: Generates code for return statement.
+// In case of struct return, delegates to the genStructReturn method.
+//
+// Arguments:
+// treeNode - The GT_RETURN or GT_RETFILT tree node.
+//
+// Return Value:
+// None
+//
+void CodeGen::genReturn(GenTree* treeNode)
+{
+ assert(treeNode->OperGet() == GT_RETURN || treeNode->OperGet() == GT_RETFILT);
+ GenTree* op1 = treeNode->gtGetOp1();
+ var_types targetType = treeNode->TypeGet();
+
+ // A void GT_RETFILT is the end of a finally. For non-void filter returns we need to load the result in the return
+ // register, if it's not already there. The processing is the same as GT_RETURN. For filters, the IL spec says the
+ // result is type int32. Further, the only legal values are 0 or 1; the use of other values is "undefined".
+ assert(!treeNode->OperIs(GT_RETFILT) || (targetType == TYP_VOID) || (targetType == TYP_INT));
+
+#ifdef DEBUG
+ if (targetType == TYP_VOID)
+ {
+ assert(op1 == nullptr);
+ }
+#endif // DEBUG
+
+#if defined(_TARGET_X86_) || defined(_TARGET_ARM_)
+ if (targetType == TYP_LONG)
+ {
+ genLongReturn(treeNode);
+ }
+ else
+#endif // _TARGET_X86_ || _TARGET_ARM_
+ {
+ if (isStructReturn(treeNode))
+ {
+ genStructReturn(treeNode);
+ }
+ else if (targetType != TYP_VOID)
+ {
+ assert(op1 != nullptr);
+ noway_assert(op1->gtRegNum != REG_NA);
+
+ // !! NOTE !! genConsumeReg will clear op1 as GC ref after it has
+ // consumed a reg for the operand. This is because the variable
+ // is dead after return. But we are issuing more instructions
+ // like "profiler leave callback" after this consumption. So
+ // if you are issuing more instructions after this point,
+ // remember to keep the variable live up until the new method
+ // exit point where it is actually dead.
+ genConsumeReg(op1);
+
+#if defined(_TARGET_ARM64_)
+ genSimpleReturn(treeNode);
+#else // !_TARGET_ARM64_
+#if defined(_TARGET_X86_)
+ if (varTypeIsFloating(treeNode))
+ {
+ genFloatReturn(treeNode);
+ }
+ else
+#elif defined(_TARGET_ARM_)
+ if (varTypeIsFloating(treeNode) && (compiler->opts.compUseSoftFP || compiler->info.compIsVarArgs))
+ {
+ if (targetType == TYP_FLOAT)
+ {
+ getEmitter()->emitIns_R_R(INS_vmov_f2i, EA_4BYTE, REG_INTRET, op1->gtRegNum);
+ }
+ else
+ {
+ assert(targetType == TYP_DOUBLE);
+ getEmitter()->emitIns_R_R_R(INS_vmov_d2i, EA_8BYTE, REG_INTRET, REG_NEXT(REG_INTRET),
+ op1->gtRegNum);
+ }
+ }
+ else
+#endif // _TARGET_ARM_
+ {
+ regNumber retReg = varTypeIsFloating(treeNode) ? REG_FLOATRET : REG_INTRET;
+ if (op1->gtRegNum != retReg)
+ {
+ inst_RV_RV(ins_Move_Extend(targetType, true), retReg, op1->gtRegNum, targetType);
+ }
+ }
+#endif // !_TARGET_ARM64_
+ }
+ }
+
+#ifdef PROFILING_SUPPORTED
+ // !! Note !!
+ // TODO-AMD64-Unix: If the profiler hook is implemented on *nix, make sure for 2 register returned structs
+ // the RAX and RDX needs to be kept alive. Make the necessary changes in lowerxarch.cpp
+ // in the handling of the GT_RETURN statement.
+ // Such structs containing GC pointers need to be handled by calling gcInfo.gcMarkRegSetNpt
+ // for the return registers containing GC refs.
+
+ // There will be a single return block while generating profiler ELT callbacks.
+ //
+ // Reason for not materializing Leave callback as a GT_PROF_HOOK node after GT_RETURN:
+ // In flowgraph and other places assert that the last node of a block marked as
+ // BBJ_RETURN is either a GT_RETURN or GT_JMP or a tail call. It would be nice to
+ // maintain such an invariant irrespective of whether profiler hook needed or not.
+ // Also, there is not much to be gained by materializing it as an explicit node.
+ if (compiler->compCurBB == compiler->genReturnBB)
+ {
+ // !! NOTE !!
+ // Since we are invalidating the assumption that we would slip into the epilog
+ // right after the "return", we need to preserve the return reg's GC state
+ // across the call until actual method return.
+ ReturnTypeDesc retTypeDesc;
+ unsigned regCount = 0;
+ if (compiler->compMethodReturnsMultiRegRetType())
+ {
+ if (varTypeIsLong(compiler->info.compRetNativeType))
+ {
+ retTypeDesc.InitializeLongReturnType(compiler);
+ }
+ else // we must have a struct return type
+ {
+ retTypeDesc.InitializeStructReturnType(compiler, compiler->info.compMethodInfo->args.retTypeClass);
+ }
+ regCount = retTypeDesc.GetReturnRegCount();
+ }
+
+ if (varTypeIsGC(compiler->info.compRetType))
+ {
+ gcInfo.gcMarkRegPtrVal(REG_INTRET, compiler->info.compRetType);
+ }
+ else if (compiler->compMethodReturnsMultiRegRetType())
+ {
+ for (unsigned i = 0; i < regCount; ++i)
+ {
+ if (varTypeIsGC(retTypeDesc.GetReturnRegType(i)))
+ {
+ gcInfo.gcMarkRegPtrVal(retTypeDesc.GetABIReturnReg(i), retTypeDesc.GetReturnRegType(i));
+ }
+ }
+ }
+
+ genProfilingLeaveCallback();
+
+ if (varTypeIsGC(compiler->info.compRetType))
+ {
+ gcInfo.gcMarkRegSetNpt(genRegMask(REG_INTRET));
+ }
+ else if (compiler->compMethodReturnsMultiRegRetType())
+ {
+ for (unsigned i = 0; i < regCount; ++i)
+ {
+ if (varTypeIsGC(retTypeDesc.GetReturnRegType(i)))
+ {
+ gcInfo.gcMarkRegSetNpt(genRegMask(retTypeDesc.GetABIReturnReg(i)));
+ }
+ }
+ }
+ }
+#endif // PROFILING_SUPPORTED
+}
+
#endif // !LEGACY_BACKEND
diff --git a/src/jit/codegenlinear.h b/src/jit/codegenlinear.h
index f1ff36265b..c36e4230c8 100644
--- a/src/jit/codegenlinear.h
+++ b/src/jit/codegenlinear.h
@@ -327,6 +327,18 @@ void genMultiRegCallStoreToLocal(GenTree* treeNode);
bool isStructReturn(GenTree* treeNode);
void genStructReturn(GenTree* treeNode);
+#if defined(_TARGET_X86_) || defined(_TARGET_ARM_)
+void genLongReturn(GenTree* treeNode);
+#endif // _TARGET_X86_ || _TARGET_ARM_
+
+#if defined(_TARGET_X86_)
+void genFloatReturn(GenTree* treeNode);
+#endif // _TARGET_X86_
+
+#if defined(_TARGET_ARM64_)
+void genSimpleReturn(GenTree* treeNode);
+#endif // _TARGET_ARM64_
+
void genReturn(GenTree* treeNode);
void genLclHeap(GenTree* tree);
diff --git a/src/jit/codegenxarch.cpp b/src/jit/codegenxarch.cpp
index b5cb2c81ea..0c00e4a7e3 100644
--- a/src/jit/codegenxarch.cpp
+++ b/src/jit/codegenxarch.cpp
@@ -1252,189 +1252,54 @@ void CodeGen::genStructReturn(GenTree* treeNode)
#endif
}
+#if defined(_TARGET_X86_)
+
//------------------------------------------------------------------------
-// genReturn: Generates code for return statement.
-// In case of struct return, delegates to the genStructReturn method.
+// genFloatReturn: Generates code for float return statement for x86.
+//
+// Note: treeNode's and op1's registers are already consumed.
//
// Arguments:
-// treeNode - The GT_RETURN or GT_RETFILT tree node.
+// treeNode - The GT_RETURN or GT_RETFILT tree node with float type.
//
// Return Value:
// None
//
-void CodeGen::genReturn(GenTree* treeNode)
+void CodeGen::genFloatReturn(GenTree* treeNode)
{
assert(treeNode->OperGet() == GT_RETURN || treeNode->OperGet() == GT_RETFILT);
- GenTree* op1 = treeNode->gtGetOp1();
- var_types targetType = treeNode->TypeGet();
-
- // A void GT_RETFILT is the end of a finally. For non-void filter returns we need to load the result in the return
- // register, if it's not already there. The processing is the same as GT_RETURN. For filters, the IL spec says the
- // result is type int32. Further, the only legal values are 0 or 1; the use of other values is "undefined".
- assert(!treeNode->OperIs(GT_RETFILT) || (targetType == TYP_VOID) || (targetType == TYP_INT));
-
-#ifdef DEBUG
- if (targetType == TYP_VOID)
- {
- assert(op1 == nullptr);
- }
-#endif
+ assert(varTypeIsFloating(treeNode));
-#ifdef _TARGET_X86_
- if (treeNode->TypeGet() == TYP_LONG)
+ GenTree* op1 = treeNode->gtGetOp1();
+ // Spill the return value register from an XMM register to the stack, then load it on the x87 stack.
+ // If it already has a home location, use that. Otherwise, we need a temp.
+ if (genIsRegCandidateLocal(op1) && compiler->lvaTable[op1->gtLclVarCommon.gtLclNum].lvOnFrame)
{
- assert(op1 != nullptr);
- noway_assert(op1->OperGet() == GT_LONG);
- GenTree* loRetVal = op1->gtGetOp1();
- GenTree* hiRetVal = op1->gtGetOp2();
- noway_assert((loRetVal->gtRegNum != REG_NA) && (hiRetVal->gtRegNum != REG_NA));
-
- genConsumeReg(loRetVal);
- genConsumeReg(hiRetVal);
- if (loRetVal->gtRegNum != REG_LNGRET_LO)
- {
- inst_RV_RV(ins_Copy(targetType), REG_LNGRET_LO, loRetVal->gtRegNum, TYP_INT);
- }
- if (hiRetVal->gtRegNum != REG_LNGRET_HI)
+ if (compiler->lvaTable[op1->gtLclVarCommon.gtLclNum].lvRegNum != REG_STK)
{
- inst_RV_RV(ins_Copy(targetType), REG_LNGRET_HI, hiRetVal->gtRegNum, TYP_INT);
+ op1->gtFlags |= GTF_SPILL;
+ inst_TT_RV(ins_Store(op1->gtType, compiler->isSIMDTypeLocalAligned(op1->gtLclVarCommon.gtLclNum)), op1,
+ op1->gtRegNum);
}
+ // Now, load it to the fp stack.
+ getEmitter()->emitIns_S(INS_fld, emitTypeSize(op1), op1->AsLclVarCommon()->gtLclNum, 0);
}
else
-#endif // !defined(_TARGET_X86_)
{
- if (isStructReturn(treeNode))
- {
- genStructReturn(treeNode);
- }
- else if (targetType != TYP_VOID)
- {
- assert(op1 != nullptr);
- noway_assert(op1->gtRegNum != REG_NA);
+ // Spill the value, which should be in a register, then load it to the fp stack.
+ // TODO-X86-CQ: Deal with things that are already in memory (don't call genConsumeReg yet).
+ op1->gtFlags |= GTF_SPILL;
+ regSet.rsSpillTree(op1->gtRegNum, op1);
+ op1->gtFlags |= GTF_SPILLED;
+ op1->gtFlags &= ~GTF_SPILL;
- // !! NOTE !! genConsumeReg will clear op1 as GC ref after it has
- // consumed a reg for the operand. This is because the variable
- // is dead after return. But we are issuing more instructions
- // like "profiler leave callback" after this consumption. So
- // if you are issuing more instructions after this point,
- // remember to keep the variable live up until the new method
- // exit point where it is actually dead.
- genConsumeReg(op1);
-
- regNumber retReg = varTypeIsFloating(treeNode) ? REG_FLOATRET : REG_INTRET;
-#ifdef _TARGET_X86_
- if (varTypeIsFloating(treeNode))
- {
- // Spill the return value register from an XMM register to the stack, then load it on the x87 stack.
- // If it already has a home location, use that. Otherwise, we need a temp.
- if (genIsRegCandidateLocal(op1) && compiler->lvaTable[op1->gtLclVarCommon.gtLclNum].lvOnFrame)
- {
- if (compiler->lvaTable[op1->gtLclVarCommon.gtLclNum].lvRegNum != REG_STK)
- {
- op1->gtFlags |= GTF_SPILL;
- inst_TT_RV(ins_Store(op1->gtType,
- compiler->isSIMDTypeLocalAligned(op1->gtLclVarCommon.gtLclNum)),
- op1, op1->gtRegNum);
- }
- // Now, load it to the fp stack.
- getEmitter()->emitIns_S(INS_fld, emitTypeSize(op1), op1->AsLclVarCommon()->gtLclNum, 0);
- }
- else
- {
- // Spill the value, which should be in a register, then load it to the fp stack.
- // TODO-X86-CQ: Deal with things that are already in memory (don't call genConsumeReg yet).
- op1->gtFlags |= GTF_SPILL;
- regSet.rsSpillTree(op1->gtRegNum, op1);
- op1->gtFlags |= GTF_SPILLED;
- op1->gtFlags &= ~GTF_SPILL;
-
- TempDsc* t = regSet.rsUnspillInPlace(op1, op1->gtRegNum);
- inst_FS_ST(INS_fld, emitActualTypeSize(op1->gtType), t, 0);
- op1->gtFlags &= ~GTF_SPILLED;
- compiler->tmpRlsTemp(t);
- }
- }
- else
-#endif // _TARGET_X86_
- {
- if (op1->gtRegNum != retReg)
- {
- inst_RV_RV(ins_Copy(targetType), retReg, op1->gtRegNum, targetType);
- }
- }
- }
+ TempDsc* t = regSet.rsUnspillInPlace(op1, op1->gtRegNum);
+ inst_FS_ST(INS_fld, emitActualTypeSize(op1->gtType), t, 0);
+ op1->gtFlags &= ~GTF_SPILLED;
+ compiler->tmpRlsTemp(t);
}
-
-#ifdef PROFILING_SUPPORTED
- // !! Note !!
- // TODO-AMD64-Unix: If the profiler hook is implemented on *nix, make sure for 2 register returned structs
- // the RAX and RDX needs to be kept alive. Make the necessary changes in lowerxarch.cpp
- // in the handling of the GT_RETURN statement.
- // Such structs containing GC pointers need to be handled by calling gcInfo.gcMarkRegSetNpt
- // for the return registers containing GC refs.
-
- // There will be a single return block while generating profiler ELT callbacks.
- //
- // Reason for not materializing Leave callback as a GT_PROF_HOOK node after GT_RETURN:
- // In flowgraph and other places assert that the last node of a block marked as
- // BBJ_RETURN is either a GT_RETURN or GT_JMP or a tail call. It would be nice to
- // maintain such an invariant irrespective of whether profiler hook needed or not.
- // Also, there is not much to be gained by materializing it as an explicit node.
- if (compiler->compCurBB == compiler->genReturnBB)
- {
- // !! NOTE !!
- // Since we are invalidating the assumption that we would slip into the epilog
- // right after the "return", we need to preserve the return reg's GC state
- // across the call until actual method return.
- ReturnTypeDesc retTypeDesc;
- unsigned regCount = 0;
- if (compiler->compMethodReturnsMultiRegRetType())
- {
- if (varTypeIsLong(compiler->info.compRetNativeType))
- {
- retTypeDesc.InitializeLongReturnType(compiler);
- }
- else // we must have a struct return type
- {
- retTypeDesc.InitializeStructReturnType(compiler, compiler->info.compMethodInfo->args.retTypeClass);
- }
- regCount = retTypeDesc.GetReturnRegCount();
- }
-
- if (varTypeIsGC(compiler->info.compRetType))
- {
- gcInfo.gcMarkRegPtrVal(REG_INTRET, compiler->info.compRetType);
- }
- else if (compiler->compMethodReturnsMultiRegRetType())
- {
- for (unsigned i = 0; i < regCount; ++i)
- {
- if (varTypeIsGC(retTypeDesc.GetReturnRegType(i)))
- {
- gcInfo.gcMarkRegPtrVal(retTypeDesc.GetABIReturnReg(i), retTypeDesc.GetReturnRegType(i));
- }
- }
- }
-
- genProfilingLeaveCallback();
-
- if (varTypeIsGC(compiler->info.compRetType))
- {
- gcInfo.gcMarkRegSetNpt(genRegMask(REG_INTRET));
- }
- else if (compiler->compMethodReturnsMultiRegRetType())
- {
- for (unsigned i = 0; i < regCount; ++i)
- {
- if (varTypeIsGC(retTypeDesc.GetReturnRegType(i)))
- {
- gcInfo.gcMarkRegSetNpt(genRegMask(retTypeDesc.GetABIReturnReg(i)));
- }
- }
- }
- }
-#endif
}
+#endif // _TARGET_X86_
//------------------------------------------------------------------------
// genCodeForCompare: Produce code for a GT_EQ/GT_NE/GT_LT/GT_LE/GT_GE/GT_GT/GT_TEST_EQ/GT_TEST_NE/GT_CMP node.