#endif
#if FEATURE_MULTIREG_RET
- GenTreePtr impAssignStructClassToVar(GenTreePtr op, CORINFO_CLASS_HANDLE hClass);
+ GenTreePtr impAssignMultiRegTypeToVar(GenTreePtr op, CORINFO_CLASS_HANDLE hClass);
#endif // FEATURE_MULTIREG_RET
#ifdef _TARGET_ARM_
GenTreePtr impFixupCallStructReturn(GenTreePtr call,
CORINFO_CLASS_HANDLE retClsHnd);
+ GenTreePtr impFixupCallLongReturn(GenTreePtr call,
+ CORINFO_CLASS_HANDLE retClsHnd);
+
GenTreePtr impFixupStructReturnType(GenTreePtr op,
CORINFO_CLASS_HANDLE retClsHnd);
{
regMaskTP resultMask = RBM_NONE;
-#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING
+#if FEATURE_MULTIREG_RET
for (unsigned i = 0; i < MAX_RET_REG_COUNT - 1; ++i)
{
if (gtOtherRegs[i] != REG_NA)
void ReturnTypeDesc::Initialize(Compiler* comp, CORINFO_CLASS_HANDLE retClsHnd)
{
assert(!m_inited);
- assert(retClsHnd != NO_CLASS_HANDLE);
#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING
+ assert(retClsHnd != NO_CLASS_HANDLE);
+
SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR structDesc;
comp->eeGetSystemVAmd64PassStructInRegisterDescriptor(retClsHnd, &structDesc);
}
}
+#elif defined(_TARGET_X86_)
+ // TODO-X86: Assumes we are only using ReturnTypeDesc for longs on x86. Will
+ // need to be updated in the future to handle other return types
+ m_regType0 = TYP_INT;
+ m_regType1 = TYP_INT;
+#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING
+
#ifdef DEBUG
m_inited = true;
#endif
-
-#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING
}
//-------------------------------------------------------------------
// targets (Arm64/Arm32/x86).
//
// TODO-ARM: Implement this routine to support HFA returns.
-// TODO-X86: Implement this routine to support long returns.
regNumber ReturnTypeDesc::GetABIReturnReg(unsigned idx)
{
unsigned count = GetReturnRegCount();
}
}
}
+
+#elif defined(_TARGET_X86_)
+ if (idx == 0)
+ {
+ resultReg = REG_LNGRET_LO;
+ }
+ else if (idx == 1)
+ {
+ resultReg = REG_LNGRET_HI;
+ }
#endif //FEATURE_UNIX_AMD64_STRUCT_PASSING
assert(resultReg != REG_NA);
// of return registers and wants to know the set of return registers.
//
// TODO-ARM: Implement this routine to support HFA returns.
-// TODO-X86: Implement this routine to support long returns.
//
//static
regMaskTP ReturnTypeDesc::GetABIReturnRegs()
// - count of return registers in which the value is returned
//
// TODO-ARM: Update this to meet the needs of Arm64 and Arm32
-// TODO-X86: Update this to meet the needs of x86
//
// TODO-AllArch: Right now it is used for describing multi-reg returned types.
// Eventually we would want to use it for describing even single-reg
public:
ReturnTypeDesc()
{
+ Reset();
+ }
+
+ // Initialize type descriptor given its type handle
+ void Initialize(Compiler* comp, CORINFO_CLASS_HANDLE retClsHnd);
+
+ // Reset type descriptor to defaults
+ void Reset()
+ {
m_regType0 = TYP_UNKNOWN;
m_regType1 = TYP_UNKNOWN;
#endif
}
- // Initialize type descriptor given its type handle
- void Initialize(Compiler* comp, CORINFO_CLASS_HANDLE retClsHnd);
-
//--------------------------------------------------------------------------------------------
// GetReturnRegCount: Get the count of return registers in which the return value is returned.
//
// If regType0 is TYP_UNKNOWN then regType1 must also be TYP_UNKNOWN.
assert(m_regType1 == TYP_UNKNOWN);
}
-
return regCount;
}
// State required to support multi-reg returning call nodes.
// For now it is enabled only for x64 unix.
//
- // TODO-ARM: enable this for HFA returns on Arm64 and Arm32
- // TODO-X86: enable this for long returns on x86
// TODO-AllArch: enable for all call nodes to unify single-reg and multi-reg returns.
-#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING
+#if FEATURE_MULTIREG_RET
ReturnTypeDesc gtReturnTypeDesc;
// gtRegNum would always be the first return reg.
// Right now implemented only for x64 unix and yet to be
// implemented for other multi-reg target arch (Arm64/Arm32/x86).
//
- // TODO-ARM: Implement this routine for Arm64 and Arm32
- // TODO-X86: Implement this routine for x86
// TODO-AllArch: enable for all call nodes to unify single-reg and multi-reg returns.
ReturnTypeDesc* GetReturnTypeDesc()
{
-#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING
+#if FEATURE_MULTIREG_RET
return >ReturnTypeDesc;
#else
return nullptr;
// Return regNumber of ith return register of call node.
// Returns REG_NA if there is no valid return register for the given index.
//
- // TODO-ARM: Implement this routine for Arm64 and Arm32
- // TODO-X86: Implement this routine for x86
regNumber GetRegNumByIdx(unsigned idx) const
{
assert(idx < MAX_RET_REG_COUNT);
return gtRegNum;
}
-#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING
+#if FEATURE_MULTIREG_RET
return gtOtherRegs[idx-1];
#else
return REG_NA;
// Return Value:
// None
//
- // TODO-ARM: Implement this routine for Arm64 and Arm32
- // TODO-X86: Implement this routine for x86
void SetRegNumByIdx(regNumber reg, unsigned idx)
{
assert(idx < MAX_RET_REG_COUNT);
{
gtRegNum = reg;
}
-#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING
+#if FEATURE_MULTIREG_RET
else
{
gtOtherRegs[idx - 1] = reg;
//
void ClearOtherRegs()
{
-#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING
+#if FEATURE_MULTIREG_RET
for (unsigned i = 0; i < MAX_RET_REG_COUNT - 1; ++i)
{
gtOtherRegs[i] = REG_NA;
//
void CopyOtherRegs(GenTreeCall* fromCall)
{
-#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING
+#if FEATURE_MULTIREG_RET
for (unsigned i = 0; i < MAX_RET_REG_COUNT - 1; ++i)
{
this->gtOtherRegs[i] = fromCall->gtOtherRegs[i];
{
assert(idx < MAX_RET_REG_COUNT);
-#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING
+#if FEATURE_MULTIREG_RET
return gtSpillFlags[idx];
#else
assert(!"unreached");
{
assert(idx < MAX_RET_REG_COUNT);
-#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING
+#if FEATURE_MULTIREG_RET
gtSpillFlags[idx] = flags;
#else
unreached();
// None
void ClearOtherRegFlags()
{
-#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING
+#if FEATURE_MULTIREG_RET
for (unsigned i = 0; i < MAX_RET_REG_COUNT; ++i)
{
gtSpillFlags[i] = 0;
//
void CopyOtherRegFlags(GenTreeCall* fromCall)
{
-#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING
+#if FEATURE_MULTIREG_RET
for (unsigned i = 0; i < MAX_RET_REG_COUNT; ++i)
{
this->gtSpillFlags[i] = fromCall->gtSpillFlags[i];
// other multi-reg return target arch (arm64/arm32/x86).
//
// TODO-ARM: Implement this routine for Arm64 and Arm32
- // TODO-X86: Implement this routine for x86
bool HasMultiRegRetVal() const
{
#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING
return varTypeIsStruct(gtType) && !HasRetBufArg();
+#elif defined(_TARGET_X86_) && !defined(LEGACY_BACKEND)
+ // LEGACY_BACKEND does not use multi reg returns for calls with long return types
+ return varTypeIsLong(gtType);
#else
return false;
#endif
{
// State required to support copy/reload of a multi-reg call node.
// First register is is always given by gtRegNum.
- // Currently enabled for x64 unix.
//
- // TODO-ARM: Enable this when multi-reg call node support is added.
- // TODO-X86: Enable this when multi-reg call node support is added.
-#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING
+#if FEATURE_MULTIREG_RET
regNumber gtOtherRegs[MAX_RET_REG_COUNT - 1];
#endif
// Return Value:
// None
//
- // TODO-ARM: Implement this routine for Arm64 and Arm32
- // TODO-X86: Implement this routine for x86
void ClearOtherRegs()
{
-#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING
+#if FEATURE_MULTIREG_RET
for (unsigned i = 0; i < MAX_RET_REG_COUNT - 1; ++i)
{
gtOtherRegs[i] = REG_NA;
// Return Value:
// Returns regNumber assigned to ith position.
//
- // TODO-ARM: Implement this routine for Arm64 and Arm32
- // TODO-X86: Implement this routine for x86
regNumber GetRegNumByIdx(unsigned idx) const
{
assert(idx < MAX_RET_REG_COUNT);
return gtRegNum;
}
-#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING
+#if FEATURE_MULTIREG_RET
return gtOtherRegs[idx - 1];
#else
return REG_NA;
}
}
- // Sometimes "call" is not a GT_CALL (if we imported an intrinsic that didn't turn into a call)
- if (varTypeIsStruct(callRetTyp) && (call->gtOper == GT_CALL))
+ if (call->gtOper == GT_CALL)
{
- call = impFixupCallStructReturn(call, sig->retTypeClass);
- }
+ // Sometimes "call" is not a GT_CALL (if we imported an intrinsic that didn't turn into a call)
+ if (varTypeIsStruct(callRetTyp))
+ {
+ call = impFixupCallStructReturn(call, sig->retTypeClass);
+ }
+ else if (varTypeIsLong(callRetTyp))
+ {
+ call = impFixupCallLongReturn(call, sig->retTypeClass);
+ }
- if ((call->gtOper == GT_CALL) && ((call->gtFlags & GTF_CALL_INLINE_CANDIDATE) != 0))
- {
- assert(opts.OptEnabled(CLFLG_INLINING));
+ if ((call->gtFlags & GTF_CALL_INLINE_CANDIDATE) != 0)
+ {
+ assert(opts.OptEnabled(CLFLG_INLINING));
- // Make the call its own tree (spill the stack if needed).
- impAppendTree(call, (unsigned)CHECK_SPILL_ALL, impCurStmtOffs);
+ // Make the call its own tree (spill the stack if needed).
+ impAppendTree(call, (unsigned)CHECK_SPILL_ALL, impCurStmtOffs);
- // TODO: Still using the widened type.
- call = gtNewInlineCandidateReturnExpr(call, genActualType(callRetTyp));
+ // TODO: Still using the widened type.
+ call = gtNewInlineCandidateReturnExpr(call, genActualType(callRetTyp));
+ }
}
if (!bIntrinsicImported)
return call;
}
- return impAssignStructClassToVar(call, retClsHnd);
+ return impAssignMultiRegTypeToVar(call, retClsHnd);
}
#elif defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
// No need to assign a multi-reg struct to a local var if:
// - It is a tail call or
// - The call is marked for in-lining later
- return impAssignStructClassToVar(call, retClsHnd);
+ return impAssignMultiRegTypeToVar(call, retClsHnd);
}
}
}
return call;
}
+
+//-----------------------------------------------------------------------------------
+// impFixupCallLongReturn: For a call node that returns a long type, force the call
+// to always be in the IR form tmp = call
+//
+// 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
+//
+GenTreePtr Compiler::impFixupCallLongReturn(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
+ assert(call->gtOper == GT_CALL);
+
+ if (!varTypeIsLong(call))
+ {
+ return call;
+ }
+
+ call->gtCall.gtRetClsHnd = retClsHnd;
+
+ GenTreeCall* callNode = call->AsCall();
+
+ // The return type will remain as the incoming long type
+ callNode->gtReturnType = call->gtType;
+
+ // Initialize Return type descriptor of call node
+ ReturnTypeDesc* retTypeDesc = callNode->GetReturnTypeDesc();
+ retTypeDesc->Initialize(this, retClsHnd);
+
+ 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;
+}
+
/*****************************************************************************
For struct return values, re-type the operand in the case where the ABI
does not use a struct return buffer
return op;
}
- return impAssignStructClassToVar(op, retClsHnd);
+ return impAssignMultiRegTypeToVar(op, retClsHnd);
}
#else // !FEATURE_UNIX_AMD64_STRUCT_PASSING
assert(info.compRetNativeType != TYP_STRUCT);
return op;
}
}
- return impAssignStructClassToVar(op, retClsHnd);
+ return impAssignMultiRegTypeToVar(op, retClsHnd);
}
#endif //_TARGET_ARM_
#endif
#if FEATURE_MULTIREG_RET
-GenTreePtr Compiler::impAssignStructClassToVar(GenTreePtr op, CORINFO_CLASS_HANDLE hClass)
+GenTreePtr Compiler::impAssignMultiRegTypeToVar(GenTreePtr op, CORINFO_CLASS_HANDLE hClass)
{
unsigned tmpNum = lvaGrabTemp(true DEBUGARG("Return value temp for multireg return."));
impAssignTempGen(tmpNum, op, hClass, (unsigned) CHECK_SPILL_NONE);
// 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;
#define FEATURE_FASTTAILCALL 0 // Tail calls made as epilog+jmp
#define FEATURE_TAILCALL_OPT 0 // opportunistic Tail calls (without ".tail" prefix) made as fast tail calls.
#define FEATURE_SET_FLAGS 0 // Set to true to force the JIT to mark the trees with GTF_SET_FLAGS when the flags need to be set
+#ifdef LEGACY_BACKEND
#define FEATURE_MULTIREG_ARGS_OR_RET 0 // Support for passing and/or returning single values in more than one register
#define FEATURE_MULTIREG_ARGS 0 // Support for passing a single argument in more than one register
#define FEATURE_MULTIREG_RET 0 // Support for returning a single value in more than one register
+#else
+ #define FEATURE_MULTIREG_ARGS_OR_RET 1 // Support for passing and/or returning single values in more than one register
+ #define FEATURE_MULTIREG_ARGS 0 // Support for passing a single argument in more than one register
+ #define FEATURE_MULTIREG_RET 1 // Support for returning a single value in more than one register
+ #define MAX_RET_MULTIREG_BYTES 8 // Maximum size of a struct that could be returned in more than one register
+#endif
+
#define MAX_ARG_REG_COUNT 2 // Maximum registers used to pass an argument.
#define MAX_RET_REG_COUNT 2 // Maximum registers used to return a value.