diff --git a/src/coreclr/src/jit/codegenarmarch.cpp b/src/coreclr/src/jit/codegenarmarch.cpp index 6e55a28da2cf7c..34ee4c3de53096 100644 --- a/src/coreclr/src/jit/codegenarmarch.cpp +++ b/src/coreclr/src/jit/codegenarmarch.cpp @@ -1394,8 +1394,8 @@ void CodeGen::genMultiRegCallStoreToLocal(GenTree* treeNode) genConsumeRegs(op1); - ReturnTypeDesc* pRetTypeDesc = call->GetReturnTypeDesc(); - unsigned regCount = pRetTypeDesc->GetReturnRegCount(); + const ReturnTypeDesc* pRetTypeDesc = call->GetReturnTypeDesc(); + const unsigned regCount = pRetTypeDesc->GetReturnRegCount(); if (treeNode->GetRegNum() != REG_NA) { @@ -2610,9 +2610,9 @@ void CodeGen::genCallInstruction(GenTreeCall* call) } // Determine return value size(s). - ReturnTypeDesc* pRetTypeDesc = call->GetReturnTypeDesc(); - emitAttr retSize = EA_PTRSIZE; - emitAttr secondRetSize = EA_UNKNOWN; + const ReturnTypeDesc* pRetTypeDesc = call->GetReturnTypeDesc(); + emitAttr retSize = EA_PTRSIZE; + emitAttr secondRetSize = EA_UNKNOWN; if (call->HasMultiRegRetVal()) { @@ -3880,12 +3880,9 @@ void CodeGen::genStructReturn(GenTree* treeNode) GenTree* actualOp1 = op1->gtSkipReloadOrCopy(); GenTreeCall* call = actualOp1->AsCall(); - ReturnTypeDesc* pRetTypeDesc; - unsigned regCount; - unsigned matchingCount = 0; - - pRetTypeDesc = call->GetReturnTypeDesc(); - regCount = pRetTypeDesc->GetReturnRegCount(); + const ReturnTypeDesc* pRetTypeDesc = call->GetReturnTypeDesc(); + const unsigned regCount = pRetTypeDesc->GetReturnRegCount(); + unsigned matchingCount = 0; var_types regType[MAX_RET_REG_COUNT]; regNumber returnReg[MAX_RET_REG_COUNT]; diff --git a/src/coreclr/src/jit/codegencommon.cpp b/src/coreclr/src/jit/codegencommon.cpp index 452d4f47a6fc6a..eeae703b43c478 100644 --- a/src/coreclr/src/jit/codegencommon.cpp +++ b/src/coreclr/src/jit/codegencommon.cpp @@ -11554,7 +11554,7 @@ void CodeGen::genReturn(GenTree* treeNode) { if (varTypeIsLong(compiler->info.compRetNativeType)) { - retTypeDesc.InitializeLongReturnType(compiler); + retTypeDesc.InitializeLongReturnType(); } else // we must have a struct return type { diff --git a/src/coreclr/src/jit/codegenlinear.cpp b/src/coreclr/src/jit/codegenlinear.cpp index e3973ba565119c..e7a662b69cb628 100644 --- a/src/coreclr/src/jit/codegenlinear.cpp +++ b/src/coreclr/src/jit/codegenlinear.cpp @@ -1081,10 +1081,10 @@ void CodeGen::genUnspillRegIfNeeded(GenTree* tree) } else if (unspillTree->IsMultiRegCall()) { - GenTreeCall* call = unspillTree->AsCall(); - ReturnTypeDesc* retTypeDesc = call->GetReturnTypeDesc(); - unsigned regCount = retTypeDesc->GetReturnRegCount(); - GenTreeCopyOrReload* reloadTree = nullptr; + GenTreeCall* call = unspillTree->AsCall(); + const ReturnTypeDesc* retTypeDesc = call->GetReturnTypeDesc(); + const unsigned regCount = retTypeDesc->GetReturnRegCount(); + GenTreeCopyOrReload* reloadTree = nullptr; if (tree->OperGet() == GT_RELOAD) { reloadTree = tree->AsCopyOrReload(); @@ -1904,9 +1904,9 @@ void CodeGen::genProduceReg(GenTree* tree) // know which of its result regs needs to be spilled. if (tree->IsMultiRegCall()) { - GenTreeCall* call = tree->AsCall(); - ReturnTypeDesc* retTypeDesc = call->GetReturnTypeDesc(); - unsigned regCount = retTypeDesc->GetReturnRegCount(); + GenTreeCall* call = tree->AsCall(); + const ReturnTypeDesc* retTypeDesc = call->GetReturnTypeDesc(); + const unsigned regCount = retTypeDesc->GetReturnRegCount(); for (unsigned i = 0; i < regCount; ++i) { @@ -1987,9 +1987,9 @@ void CodeGen::genProduceReg(GenTree* tree) // Mark all the regs produced by call node. if (tree->IsMultiRegCall()) { - GenTreeCall* call = tree->AsCall(); - ReturnTypeDesc* retTypeDesc = call->GetReturnTypeDesc(); - unsigned regCount = retTypeDesc->GetReturnRegCount(); + const GenTreeCall* call = tree->AsCall(); + const ReturnTypeDesc* retTypeDesc = call->GetReturnTypeDesc(); + const unsigned regCount = retTypeDesc->GetReturnRegCount(); for (unsigned i = 0; i < regCount; ++i) { @@ -2006,10 +2006,10 @@ void CodeGen::genProduceReg(GenTree* tree) // A multi-reg GT_COPY node produces those regs to which // copy has taken place. - GenTreeCopyOrReload* copy = tree->AsCopyOrReload(); - GenTreeCall* call = copy->gtGetOp1()->AsCall(); - ReturnTypeDesc* retTypeDesc = call->GetReturnTypeDesc(); - unsigned regCount = retTypeDesc->GetReturnRegCount(); + const GenTreeCopyOrReload* copy = tree->AsCopyOrReload(); + const GenTreeCall* call = copy->gtGetOp1()->AsCall(); + const ReturnTypeDesc* retTypeDesc = call->GetReturnTypeDesc(); + const unsigned regCount = retTypeDesc->GetReturnRegCount(); for (unsigned i = 0; i < regCount; ++i) { diff --git a/src/coreclr/src/jit/codegenxarch.cpp b/src/coreclr/src/jit/codegenxarch.cpp index 2070291a208080..0f9396cfecf84d 100644 --- a/src/coreclr/src/jit/codegenxarch.cpp +++ b/src/coreclr/src/jit/codegenxarch.cpp @@ -137,14 +137,14 @@ void CodeGen::genEmitGSCookieCheck(bool pushReg) ReturnTypeDesc retTypeDesc; if (varTypeIsLong(compiler->info.compRetNativeType)) { - retTypeDesc.InitializeLongReturnType(compiler); + retTypeDesc.InitializeLongReturnType(); } else // we must have a struct return type { retTypeDesc.InitializeStructReturnType(compiler, compiler->info.compMethodInfo->args.retTypeClass); } - unsigned regCount = retTypeDesc.GetReturnRegCount(); + const unsigned regCount = retTypeDesc.GetReturnRegCount(); // Only x86 and x64 Unix ABI allows multi-reg return and // number of result regs should be equal to MAX_RET_REG_COUNT. @@ -1179,7 +1179,7 @@ void CodeGen::genStructReturn(GenTree* treeNode) ReturnTypeDesc retTypeDesc; retTypeDesc.InitializeStructReturnType(compiler, varDsc->lvVerTypeInfo.GetClassHandle()); - unsigned regCount = retTypeDesc.GetReturnRegCount(); + const unsigned regCount = retTypeDesc.GetReturnRegCount(); assert(regCount == MAX_RET_REG_COUNT); if (varTypeIsEnregisterable(op1)) @@ -1243,10 +1243,10 @@ void CodeGen::genStructReturn(GenTree* treeNode) genConsumeRegs(op1); - GenTree* actualOp1 = op1->gtSkipReloadOrCopy(); - GenTreeCall* call = actualOp1->AsCall(); - ReturnTypeDesc* retTypeDesc = call->GetReturnTypeDesc(); - unsigned regCount = retTypeDesc->GetReturnRegCount(); + const GenTree* actualOp1 = op1->gtSkipReloadOrCopy(); + const GenTreeCall* call = actualOp1->AsCall(); + const ReturnTypeDesc* retTypeDesc = call->GetReturnTypeDesc(); + const unsigned regCount = retTypeDesc->GetReturnRegCount(); assert(regCount == MAX_RET_REG_COUNT); // Handle circular dependency between call allocated regs and ABI return regs. @@ -2043,7 +2043,7 @@ void CodeGen::genMultiRegCallStoreToLocal(GenTree* treeNode) genConsumeRegs(op1); - ReturnTypeDesc* retTypeDesc = call->GetReturnTypeDesc(); + const ReturnTypeDesc* retTypeDesc = call->GetReturnTypeDesc(); assert(retTypeDesc->GetReturnRegCount() == MAX_RET_REG_COUNT); unsigned regCount = retTypeDesc->GetReturnRegCount(); @@ -2154,8 +2154,8 @@ void CodeGen::genMultiRegCallStoreToLocal(GenTree* treeNode) genConsumeRegs(op1); - ReturnTypeDesc* retTypeDesc = call->GetReturnTypeDesc(); - unsigned regCount = retTypeDesc->GetReturnRegCount(); + const ReturnTypeDesc* retTypeDesc = call->GetReturnTypeDesc(); + unsigned regCount = retTypeDesc->GetReturnRegCount(); assert(regCount == MAX_RET_REG_COUNT); // Stack store @@ -5501,9 +5501,9 @@ void CodeGen::genCallInstruction(GenTreeCall* call) } // Determine return value size(s). - ReturnTypeDesc* retTypeDesc = call->GetReturnTypeDesc(); - emitAttr retSize = EA_PTRSIZE; - emitAttr secondRetSize = EA_UNKNOWN; + const ReturnTypeDesc* retTypeDesc = call->GetReturnTypeDesc(); + emitAttr retSize = EA_PTRSIZE; + emitAttr secondRetSize = EA_UNKNOWN; if (call->HasMultiRegRetVal()) { @@ -5766,7 +5766,7 @@ void CodeGen::genCallInstruction(GenTreeCall* call) if (call->HasMultiRegRetVal()) { assert(retTypeDesc != nullptr); - unsigned regCount = retTypeDesc->GetReturnRegCount(); + const unsigned regCount = retTypeDesc->GetReturnRegCount(); // If regs allocated to call node are different from ABI return // regs in which the call has returned its result, move the result diff --git a/src/coreclr/src/jit/decomposelongs.cpp b/src/coreclr/src/jit/decomposelongs.cpp index 77112049271eae..855d60813ca02a 100644 --- a/src/coreclr/src/jit/decomposelongs.cpp +++ b/src/coreclr/src/jit/decomposelongs.cpp @@ -1362,7 +1362,7 @@ GenTree* DecomposeLongs::DecomposeShift(LIR::Use& use) GenTreeCall::Use* argList = m_compiler->gtNewCallArgs(loOp1, hiOp1, shiftByOp); - GenTree* call = m_compiler->gtNewHelperCallNode(helper, TYP_LONG, argList); + GenTreeCall* call = m_compiler->gtNewHelperCallNode(helper, TYP_LONG, argList); call->gtFlags |= shift->gtFlags & GTF_ALL_EFFECT; if (shift->IsUnusedValue()) @@ -1370,11 +1370,7 @@ GenTree* DecomposeLongs::DecomposeShift(LIR::Use& use) call->SetUnusedValue(); } - GenTreeCall* callNode = call->AsCall(); - ReturnTypeDesc* retTypeDesc = callNode->GetReturnTypeDesc(); - retTypeDesc->InitializeLongReturnType(m_compiler); - - call = m_compiler->fgMorphArgs(callNode); + call = m_compiler->fgMorphArgs(call); Range().InsertAfter(shift, LIR::SeqTree(m_compiler, call)); Range().Remove(shift); diff --git a/src/coreclr/src/jit/gentree.cpp b/src/coreclr/src/jit/gentree.cpp index 8e341ce7dab5a3..5c8dc779a14e15 100644 --- a/src/coreclr/src/jit/gentree.cpp +++ b/src/coreclr/src/jit/gentree.cpp @@ -622,15 +622,12 @@ void GenTree::CopyReg(GenTree* from) // bool GenTree::gtHasReg() const { - bool hasReg; + bool hasReg = false; if (IsMultiRegCall()) { - // Have to cast away const-ness because GetReturnTypeDesc() is a non-const method - GenTree* tree = const_cast(this); - GenTreeCall* call = tree->AsCall(); - unsigned regCount = call->GetReturnTypeDesc()->GetReturnRegCount(); - hasReg = false; + const GenTreeCall* call = AsCall(); + const unsigned regCount = call->GetReturnTypeDesc()->GetReturnRegCount(); // A Multi-reg call node is said to have regs, if it has // reg assigned to each of its result registers. @@ -645,11 +642,9 @@ bool GenTree::gtHasReg() const } else if (IsCopyOrReloadOfMultiRegCall()) { - GenTree* tree = const_cast(this); - GenTreeCopyOrReload* copyOrReload = tree->AsCopyOrReload(); - GenTreeCall* call = copyOrReload->gtGetOp1()->AsCall(); - unsigned regCount = call->GetReturnTypeDesc()->GetReturnRegCount(); - hasReg = false; + const GenTreeCopyOrReload* copyOrReload = AsCopyOrReload(); + const GenTreeCall* call = copyOrReload->gtGetOp1()->AsCall(); + const unsigned regCount = call->GetReturnTypeDesc()->GetReturnRegCount(); // A Multi-reg copy or reload node is said to have regs, // if it has valid regs in any of the positions. @@ -693,9 +688,7 @@ int GenTree::GetRegisterDstCount() const } else if (IsMultiRegCall()) { - // temporarily cast away const-ness as AsCall() method is not declared const - GenTree* temp = const_cast(this); - return temp->AsCall()->GetReturnTypeDesc()->GetReturnRegCount(); + return AsCall()->GetReturnTypeDesc()->GetReturnRegCount(); } else if (IsCopyOrReload()) { @@ -750,9 +743,8 @@ regMaskTP GenTree::gtGetRegMask() const if (IsMultiRegCall()) { // temporarily cast away const-ness as AsCall() method is not declared const - resultMask = genRegMask(GetRegNum()); - GenTree* temp = const_cast(this); - resultMask |= temp->AsCall()->GetOtherRegMask(); + resultMask = genRegMask(GetRegNum()); + resultMask |= AsCall()->GetOtherRegMask(); } else if (IsCopyOrReloadOfMultiRegCall()) { @@ -760,10 +752,9 @@ regMaskTP GenTree::gtGetRegMask() const // positions that need to be copied or reloaded. Hence we need // to consider only those registers for computing reg mask. - GenTree* tree = const_cast(this); - GenTreeCopyOrReload* copyOrReload = tree->AsCopyOrReload(); - GenTreeCall* call = copyOrReload->gtGetOp1()->AsCall(); - unsigned regCount = call->GetReturnTypeDesc()->GetReturnRegCount(); + const GenTreeCopyOrReload* copyOrReload = AsCopyOrReload(); + const GenTreeCall* call = copyOrReload->gtGetOp1()->AsCall(); + const unsigned regCount = call->GetReturnTypeDesc()->GetReturnRegCount(); resultMask = RBM_NONE; for (unsigned i = 0; i < regCount; ++i) @@ -778,9 +769,8 @@ regMaskTP GenTree::gtGetRegMask() const #if FEATURE_ARG_SPLIT else if (OperIsPutArgSplit()) { - GenTree* tree = const_cast(this); - GenTreePutArgSplit* splitArg = tree->AsPutArgSplit(); - unsigned regCount = splitArg->gtNumRegs; + const GenTreePutArgSplit* splitArg = AsPutArgSplit(); + const unsigned regCount = splitArg->gtNumRegs; resultMask = RBM_NONE; for (unsigned i = 0; i < regCount; ++i) @@ -6182,21 +6172,14 @@ GenTreeCall* Compiler::gtNewCallNode( // Initialize spill flags of gtOtherRegs node->ClearOtherRegFlags(); -#if defined(TARGET_X86) || defined(TARGET_ARM) - // Initialize the multi-reg long return info if necessary +#if !defined(TARGET_64BIT) if (varTypeIsLong(node)) { - // The return type will remain as the incoming long type - node->gtReturnType = node->gtType; - + assert(node->gtReturnType == node->gtType); // Initialize Return type descriptor of call node - ReturnTypeDesc* retTypeDesc = node->GetReturnTypeDesc(); - retTypeDesc->InitializeLongReturnType(this); - - // must be a long returned in two registers - assert(retTypeDesc->GetReturnRegCount() == 2); + node->InitializeLongReturnType(); } -#endif // defined(TARGET_X86) || defined(TARGET_ARM) +#endif // !defined(TARGET_64BIT) return node; } @@ -10217,8 +10200,8 @@ void Compiler::gtDispRegVal(GenTree* tree) { // 0th reg is GettRegNum(), which is already printed above. // Print the remaining regs of a multi-reg call node. - GenTreeCall* call = tree->AsCall(); - unsigned regCount = call->GetReturnTypeDesc()->TryGetReturnRegCount(); + const GenTreeCall* call = tree->AsCall(); + const unsigned regCount = call->GetReturnTypeDesc()->TryGetReturnRegCount(); for (unsigned i = 1; i < regCount; ++i) { printf(",%s", compRegVarName(call->GetRegNumByIdx(i))); @@ -10226,9 +10209,9 @@ void Compiler::gtDispRegVal(GenTree* tree) } else if (tree->IsCopyOrReloadOfMultiRegCall()) { - GenTreeCopyOrReload* copyOrReload = tree->AsCopyOrReload(); - GenTreeCall* call = tree->gtGetOp1()->AsCall(); - unsigned regCount = call->GetReturnTypeDesc()->TryGetReturnRegCount(); + const GenTreeCopyOrReload* copyOrReload = tree->AsCopyOrReload(); + const GenTreeCall* call = tree->gtGetOp1()->AsCall(); + const unsigned regCount = call->GetReturnTypeDesc()->TryGetReturnRegCount(); for (unsigned i = 1; i < regCount; ++i) { printf(",%s", compRegVarName(copyOrReload->GetRegNumByIdx(i))); @@ -15315,9 +15298,7 @@ GenTree* Compiler::gtNewRefCOMfield(GenTree* objPtr, #if FEATURE_MULTIREG_RET if (varTypeIsStruct(call)) { - // Initialize Return type descriptor of call node. - ReturnTypeDesc* retTypeDesc = call->GetReturnTypeDesc(); - retTypeDesc->InitializeStructReturnType(this, structType); + call->InitializeStructReturnType(this, structType); } #endif // FEATURE_MULTIREG_RET @@ -16529,7 +16510,7 @@ bool GenTree::isContained() const else if (OperKind() & GTK_RELOP) { // We have to cast away const-ness since AsOp() method is non-const. - GenTree* childNode = const_cast(this)->AsOp()->gtOp1; + const GenTree* childNode = AsOp()->gtGetOp1(); assert((isMarkedContained == false) || childNode->IsSIMDEqualityOrInequality()); } @@ -18909,16 +18890,10 @@ void ReturnTypeDesc::InitializeStructReturnType(Compiler* comp, CORINFO_CLASS_HA // InitializeLongReturnType: // Initialize the Return Type Descriptor for a method that returns a TYP_LONG // -// Arguments -// comp - Compiler Instance -// -// Return Value -// None -// -void ReturnTypeDesc::InitializeLongReturnType(Compiler* comp) +void ReturnTypeDesc::InitializeLongReturnType() { + assert(!m_inited); #if defined(TARGET_X86) || defined(TARGET_ARM) - // Setups up a ReturnTypeDesc for returning a long using two registers // assert(MAX_RET_REG_COUNT >= 2); @@ -18950,7 +18925,7 @@ void ReturnTypeDesc::InitializeLongReturnType(Compiler* comp) // x86 and ARM return long in multiple registers. // ARM and ARM64 return HFA struct in multiple registers. // -regNumber ReturnTypeDesc::GetABIReturnReg(unsigned idx) +regNumber ReturnTypeDesc::GetABIReturnReg(unsigned idx) const { unsigned count = GetReturnRegCount(); assert(idx < count); @@ -19079,7 +19054,7 @@ regNumber ReturnTypeDesc::GetABIReturnReg(unsigned idx) // of return registers and wants to know the set of return registers. // // static -regMaskTP ReturnTypeDesc::GetABIReturnRegs() +regMaskTP ReturnTypeDesc::GetABIReturnRegs() const { regMaskTP resultMask = RBM_NONE; diff --git a/src/coreclr/src/jit/gentree.h b/src/coreclr/src/jit/gentree.h index 12645fb12d219d..3ef59b801aa8e7 100644 --- a/src/coreclr/src/jit/gentree.h +++ b/src/coreclr/src/jit/gentree.h @@ -3600,8 +3600,8 @@ struct ReturnTypeDesc void InitializeStructReturnType(Compiler* comp, CORINFO_CLASS_HANDLE retClsHnd); // Initialize the Return Type Descriptor for a method that returns a TYP_LONG - // Only needed for X86 - void InitializeLongReturnType(Compiler* comp); + // Only needed for X86 and arm32. + void InitializeLongReturnType(); // Reset type descriptor to defaults void Reset() @@ -3680,6 +3680,7 @@ struct ReturnTypeDesc } else { + assert(m_inited); return ((m_regType[0] != TYP_UNKNOWN) && (m_regType[1] != TYP_UNKNOWN)); } } @@ -3695,7 +3696,7 @@ struct ReturnTypeDesc // var_type of the return register specified by its index. // asserts if the index does not have a valid register return type. - var_types GetReturnRegType(unsigned index) + var_types GetReturnRegType(unsigned index) const { var_types result = m_regType[index]; assert(result != TYP_UNKNOWN); @@ -3711,10 +3712,10 @@ struct ReturnTypeDesc } // Get ith ABI return register - regNumber GetABIReturnReg(unsigned idx); + regNumber GetABIReturnReg(unsigned idx) const; // Get reg mask of ABI return registers - regMaskTP GetABIReturnRegs(); + regMaskTP GetABIReturnRegs() const; }; class TailCallSiteInfo @@ -3918,7 +3919,6 @@ struct GenTreeCall final : public GenTree #if FEATURE_MULTIREG_RET // State required to support multi-reg returning call nodes. - // For now it is enabled only for x64 unix. // // TODO-AllArch: enable for all call nodes to unify single-reg and multi-reg returns. ReturnTypeDesc gtReturnTypeDesc; @@ -3940,12 +3940,8 @@ struct GenTreeCall final : public GenTree // Returns // Type descriptor of the value returned by call // - // Note: - // Right now implemented only for x64 unix and yet to be - // implemented for other multi-reg target arch (Arm64/Arm32/x86). - // // TODO-AllArch: enable for all call nodes to unify single-reg and multi-reg returns. - ReturnTypeDesc* GetReturnTypeDesc() + const ReturnTypeDesc* GetReturnTypeDesc() const { #if FEATURE_MULTIREG_RET return >ReturnTypeDesc; @@ -3954,6 +3950,27 @@ struct GenTreeCall final : public GenTree #endif } + void InitializeLongReturnType() + { +#if FEATURE_MULTIREG_RET + gtReturnTypeDesc.InitializeLongReturnType(); +#endif + } + + void InitializeStructReturnType(Compiler* comp, CORINFO_CLASS_HANDLE retClsHnd) + { +#if FEATURE_MULTIREG_RET + gtReturnTypeDesc.InitializeStructReturnType(comp, retClsHnd); +#endif + } + + void ResetReturnType() + { +#if FEATURE_MULTIREG_RET + gtReturnTypeDesc.Reset(); +#endif + } + //--------------------------------------------------------------------------- // GetRegNumByIdx: get ith return register allocated to this call node. // @@ -4220,18 +4237,29 @@ struct GenTreeCall final : public GenTree // bool HasMultiRegRetVal() const { -#if defined(TARGET_X86) - return varTypeIsLong(gtType); -#elif FEATURE_MULTIREG_RET && defined(TARGET_ARM) - return varTypeIsLong(gtType) || (varTypeIsStruct(gtType) && !HasRetBufArg()); +#ifdef FEATURE_MULTIREG_RET +#if defined(TARGET_X86) || defined(TARGET_ARM) + if (varTypeIsLong(gtType)) + { + return true; + } #elif defined(FEATURE_HFA) && defined(TARGET_ARM64) // SIMD types are returned in vector regs on ARM64. - return (gtType == TYP_STRUCT) && !HasRetBufArg(); -#elif FEATURE_MULTIREG_RET - return varTypeIsStruct(gtType) && !HasRetBufArg(); -#else + if (varTypeIsSIMD(gtType)) + { + return false; + } +#endif // FEATURE_HFA && TARGET_ARM64 + + if (!varTypeIsStruct(gtType) || HasRetBufArg()) + { + return false; + } + // Now it is a struct that is returned in registers. + return GetReturnTypeDesc()->IsMultiRegRetType(); +#else // !FEATURE_MULTIREG_RET return false; -#endif +#endif // !FEATURE_MULTIREG_RET } // Returns true if VM has flagged this method as CORINFO_FLG_PINVOKE. diff --git a/src/coreclr/src/jit/importer.cpp b/src/coreclr/src/jit/importer.cpp index 41c5e2540bb177..b2beb44b528ce4 100644 --- a/src/coreclr/src/jit/importer.cpp +++ b/src/coreclr/src/jit/importer.cpp @@ -8888,9 +8888,7 @@ GenTree* Compiler::impFixupCallStructReturn(GenTreeCall* call, CORINFO_CLASS_HAN call->gtRetClsHnd = retClsHnd; #if FEATURE_MULTIREG_RET - // Initialize Return type descriptor of call node - ReturnTypeDesc* retTypeDesc = call->GetReturnTypeDesc(); - retTypeDesc->InitializeStructReturnType(this, retClsHnd); + call->InitializeStructReturnType(this, retClsHnd); #endif // FEATURE_MULTIREG_RET #ifdef UNIX_AMD64_ABI @@ -8898,55 +8896,57 @@ GenTree* Compiler::impFixupCallStructReturn(GenTreeCall* call, CORINFO_CLASS_HAN // Not allowed for FEATURE_CORCLR which is the only SKU available for System V OSs. assert(!call->IsVarargs() && "varargs not allowed for System V OSs."); - // The return type will remain as the incoming struct type unless normalized to a - // single eightbyte return type below. - call->gtReturnType = call->gtType; - - unsigned retRegCount = retTypeDesc->GetReturnRegCount(); - if (retRegCount != 0) + const ReturnTypeDesc* retTypeDesc = call->GetReturnTypeDesc(); + const unsigned retRegCount = retTypeDesc->GetReturnRegCount(); + if (retRegCount == 0) + { + // struct not returned in registers i.e returned via hiddden retbuf arg. + call->gtCallMoreFlags |= GTF_CALL_M_RETBUFFARG; + } + else if (retRegCount == 1) { - if (retRegCount == 1) + if (!compDoOldStructRetyping()) { - // See if the struct size is smaller than the return - // type size... - if (retTypeDesc->IsEnclosingType()) + return call; + } + // See if the struct size is smaller than the return + // type size... + if (retTypeDesc->IsEnclosingType()) + { + // If we know for sure this call will remain a call, + // retype and return value via a suitable temp. + if ((!call->CanTailCall()) && (!call->IsInlineCandidate())) { - // If we know for sure this call will remain a call, - // retype and return value via a suitable temp. - if ((!call->CanTailCall()) && (!call->IsInlineCandidate())) - { - call->gtReturnType = retTypeDesc->GetReturnRegType(0); - return impAssignSmallStructTypeToVar(call, retClsHnd); - } + call->gtReturnType = retTypeDesc->GetReturnRegType(0); + return impAssignSmallStructTypeToVar(call, retClsHnd); } else { - // Return type is same size as struct, so we can - // simply retype the call. - call->gtReturnType = retTypeDesc->GetReturnRegType(0); + call->gtReturnType = call->gtType; } } else { - // must be a struct returned in two registers - assert(retRegCount == 2); - - if ((!call->CanTailCall()) && (!call->IsInlineCandidate())) - { - // Force a call returning multi-reg struct to be always of the IR form - // tmp = call - // - // 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 impAssignMultiRegTypeToVar(call, retClsHnd); - } + // Return type is same size as struct, so we can + // simply retype the call. + call->gtReturnType = retTypeDesc->GetReturnRegType(0); } } else { - // struct not returned in registers i.e returned via hiddden retbuf arg. - call->gtCallMoreFlags |= GTF_CALL_M_RETBUFFARG; + // must be a struct returned in two registers + assert(retRegCount == 2); + + if ((!call->CanTailCall()) && (!call->IsInlineCandidate())) + { + // Force a call returning multi-reg struct to be always of the IR form + // tmp = call + // + // 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 impAssignMultiRegTypeToVar(call, retClsHnd); + } } #else // not UNIX_AMD64_ABI @@ -9001,7 +9001,7 @@ GenTree* Compiler::impFixupCallStructReturn(GenTreeCall* call, CORINFO_CLASS_HAN } #if FEATURE_MULTIREG_RET - unsigned retRegCount = retTypeDesc->GetReturnRegCount(); + const unsigned retRegCount = call->GetReturnTypeDesc()->GetReturnRegCount(); assert(retRegCount != 0); if (retRegCount >= 2) @@ -16267,7 +16267,7 @@ void Compiler::impMarkLclDstNotPromotable(unsigned tmpNum, GenTree* src, CORINFO GenTree* Compiler::impAssignSmallStructTypeToVar(GenTree* op, CORINFO_CLASS_HANDLE hClass) { - unsigned tmpNum = lvaGrabTemp(true DEBUGARG("Return value temp for small struct return.")); + unsigned tmpNum = lvaGrabTemp(true DEBUGARG("Return value temp for small struct return")); impAssignTempGen(tmpNum, op, hClass, (unsigned)CHECK_SPILL_ALL); GenTree* ret = gtNewLclvNode(tmpNum, lvaTable[tmpNum].lvType); return ret; @@ -16287,7 +16287,7 @@ GenTree* Compiler::impAssignSmallStructTypeToVar(GenTree* op, CORINFO_CLASS_HAND GenTree* Compiler::impAssignMultiRegTypeToVar(GenTree* op, CORINFO_CLASS_HANDLE hClass) { - unsigned tmpNum = lvaGrabTemp(true DEBUGARG("Return value temp for multireg return.")); + unsigned tmpNum = lvaGrabTemp(true DEBUGARG("Return value temp for multireg return")); impAssignTempGen(tmpNum, op, hClass, (unsigned)CHECK_SPILL_ALL); GenTree* ret = gtNewLclvNode(tmpNum, lvaTable[tmpNum].lvType); diff --git a/src/coreclr/src/jit/lower.cpp b/src/coreclr/src/jit/lower.cpp index 9389b21325f35e..854c5d0fcf9569 100644 --- a/src/coreclr/src/jit/lower.cpp +++ b/src/coreclr/src/jit/lower.cpp @@ -3057,7 +3057,12 @@ void Lowering::LowerRetStruct(GenTreeUnOp* ret) LowerRetStructLclVar(ret); break; +#ifdef FEATURE_SIMD case GT_SIMD: +#endif // FEATURE_SIMD +#ifdef FEATURE_HW_INTRINSICS + case GT_HWINTRINSIC: +#endif // FEATURE_HW_INTRINSICS case GT_LCL_FLD: { GenTreeUnOp* bitcast = new (comp, GT_BITCAST) GenTreeOp(GT_BITCAST, ret->TypeGet(), retVal, nullptr); diff --git a/src/coreclr/src/jit/lsraarmarch.cpp b/src/coreclr/src/jit/lsraarmarch.cpp index fab94aaf112520..1a75bcbe2a727c 100644 --- a/src/coreclr/src/jit/lsraarmarch.cpp +++ b/src/coreclr/src/jit/lsraarmarch.cpp @@ -126,9 +126,9 @@ int LinearScan::BuildIndir(GenTreeIndir* indirTree) // int LinearScan::BuildCall(GenTreeCall* call) { - bool hasMultiRegRetVal = false; - ReturnTypeDesc* retTypeDesc = nullptr; - regMaskTP dstCandidates = RBM_NONE; + bool hasMultiRegRetVal = false; + const ReturnTypeDesc* retTypeDesc = nullptr; + regMaskTP dstCandidates = RBM_NONE; int srcCount = 0; int dstCount = 0; diff --git a/src/coreclr/src/jit/lsrabuild.cpp b/src/coreclr/src/jit/lsrabuild.cpp index 7a41dd28e713ab..429fb7d2922797 100644 --- a/src/coreclr/src/jit/lsrabuild.cpp +++ b/src/coreclr/src/jit/lsrabuild.cpp @@ -2699,7 +2699,7 @@ void LinearScan::BuildDefs(GenTree* tree, int dstCount, regMaskTP dstCandidates) { fixedReg = true; } - ReturnTypeDesc* retTypeDesc = nullptr; + const ReturnTypeDesc* retTypeDesc = nullptr; if (tree->IsMultiRegCall()) { retTypeDesc = tree->AsCall()->GetReturnTypeDesc(); @@ -3074,9 +3074,9 @@ int LinearScan::BuildStoreLoc(GenTreeLclVarCommon* storeLoc) assert(storeLoc->OperGet() == GT_STORE_LCL_VAR); // srcCount = number of registers in which the value is returned by call - GenTreeCall* call = op1->AsCall(); - ReturnTypeDesc* retTypeDesc = call->GetReturnTypeDesc(); - srcCount = retTypeDesc->GetReturnRegCount(); + const GenTreeCall* call = op1->AsCall(); + const ReturnTypeDesc* retTypeDesc = call->GetReturnTypeDesc(); + srcCount = retTypeDesc->GetReturnRegCount(); for (int i = 0; i < srcCount; ++i) { @@ -3271,9 +3271,9 @@ int LinearScan::BuildReturn(GenTree* tree) { noway_assert(op1->IsMultiRegCall()); - ReturnTypeDesc* retTypeDesc = op1->AsCall()->GetReturnTypeDesc(); - int srcCount = retTypeDesc->GetReturnRegCount(); - useCandidates = retTypeDesc->GetABIReturnRegs(); + const ReturnTypeDesc* retTypeDesc = op1->AsCall()->GetReturnTypeDesc(); + const int srcCount = retTypeDesc->GetReturnRegCount(); + useCandidates = retTypeDesc->GetABIReturnRegs(); for (int i = 0; i < srcCount; i++) { BuildUse(op1, useCandidates, i); diff --git a/src/coreclr/src/jit/lsraxarch.cpp b/src/coreclr/src/jit/lsraxarch.cpp index f784b554538716..e4b0b054050489 100644 --- a/src/coreclr/src/jit/lsraxarch.cpp +++ b/src/coreclr/src/jit/lsraxarch.cpp @@ -1033,11 +1033,11 @@ int LinearScan::BuildShiftRotate(GenTree* tree) // int LinearScan::BuildCall(GenTreeCall* call) { - bool hasMultiRegRetVal = false; - ReturnTypeDesc* retTypeDesc = nullptr; - int srcCount = 0; - int dstCount = 0; - regMaskTP dstCandidates = RBM_NONE; + bool hasMultiRegRetVal = false; + const ReturnTypeDesc* retTypeDesc = nullptr; + int srcCount = 0; + int dstCount = 0; + regMaskTP dstCandidates = RBM_NONE; assert(!call->isContained()); if (call->TypeGet() != TYP_VOID) diff --git a/src/coreclr/src/jit/morph.cpp b/src/coreclr/src/jit/morph.cpp index fdff5b7298d6d1..dc1963dbf2d9d8 100644 --- a/src/coreclr/src/jit/morph.cpp +++ b/src/coreclr/src/jit/morph.cpp @@ -63,38 +63,41 @@ GenTree* Compiler::fgMorphIntoHelperCall(GenTree* tree, int helper, GenTreeCall: // The helper call ought to be semantically equivalent to the original node, so preserve its VN. tree->ChangeOper(GT_CALL, GenTree::PRESERVE_VN); - tree->AsCall()->gtCallType = CT_HELPER; - tree->AsCall()->gtCallMethHnd = eeFindHelper(helper); - tree->AsCall()->gtCallThisArg = nullptr; - tree->AsCall()->gtCallArgs = args; - tree->AsCall()->gtCallLateArgs = nullptr; - tree->AsCall()->fgArgInfo = nullptr; - tree->AsCall()->gtRetClsHnd = nullptr; - tree->AsCall()->gtCallMoreFlags = 0; - tree->AsCall()->gtInlineCandidateInfo = nullptr; - tree->AsCall()->gtControlExpr = nullptr; + GenTreeCall* call = tree->AsCall(); + + call->gtCallType = CT_HELPER; + call->gtCallMethHnd = eeFindHelper(helper); + call->gtCallThisArg = nullptr; + call->gtCallArgs = args; + call->gtCallLateArgs = nullptr; + call->fgArgInfo = nullptr; + call->gtRetClsHnd = nullptr; + call->gtCallMoreFlags = 0; + call->gtInlineCandidateInfo = nullptr; + call->gtControlExpr = nullptr; #if DEBUG // Helper calls are never candidates. - tree->AsCall()->gtInlineObservation = InlineObservation::CALLSITE_IS_CALL_TO_HELPER; + call->gtInlineObservation = InlineObservation::CALLSITE_IS_CALL_TO_HELPER; #endif // DEBUG #ifdef FEATURE_READYTORUN_COMPILER - tree->AsCall()->gtEntryPoint.addr = nullptr; - tree->AsCall()->gtEntryPoint.accessType = IAT_VALUE; + call->gtEntryPoint.addr = nullptr; + call->gtEntryPoint.accessType = IAT_VALUE; #endif +#if FEATURE_MULTIREG_RET + call->ResetReturnType(); + call->ClearOtherRegs(); + call->ClearOtherRegFlags(); #ifndef TARGET_64BIT if (varTypeIsLong(tree)) { - GenTreeCall* callNode = tree->AsCall(); - ReturnTypeDesc* retTypeDesc = callNode->GetReturnTypeDesc(); - retTypeDesc->Reset(); - retTypeDesc->InitializeLongReturnType(this); - callNode->ClearOtherRegs(); + call->InitializeLongReturnType(); } #endif // !TARGET_64BIT +#endif // FEATURE_MULTIREG_RET if (tree->OperMayThrow(this)) { @@ -115,7 +118,7 @@ GenTree* Compiler::fgMorphIntoHelperCall(GenTree* tree, int helper, GenTreeCall: if (morphArgs) { - tree = fgMorphArgs(tree->AsCall()); + tree = fgMorphArgs(call); } return tree; diff --git a/src/coreclr/src/jit/regset.cpp b/src/coreclr/src/jit/regset.cpp index a550df415c0302..92ef88f8c74b05 100644 --- a/src/coreclr/src/jit/regset.cpp +++ b/src/coreclr/src/jit/regset.cpp @@ -304,9 +304,9 @@ void RegSet::rsSpillTree(regNumber reg, GenTree* tree, unsigned regIdx /* =0 */) if (tree->IsMultiRegCall()) { - call = tree->AsCall(); - ReturnTypeDesc* retTypeDesc = call->GetReturnTypeDesc(); - treeType = retTypeDesc->GetReturnRegType(regIdx); + call = tree->AsCall(); + const ReturnTypeDesc* retTypeDesc = call->GetReturnTypeDesc(); + treeType = retTypeDesc->GetReturnRegType(regIdx); } #ifdef TARGET_ARM else if (tree->OperIsPutArgSplit()) diff --git a/src/coreclr/src/jit/target.h b/src/coreclr/src/jit/target.h index cbd6d46ceb267a..6fd88968026c3a 100644 --- a/src/coreclr/src/jit/target.h +++ b/src/coreclr/src/jit/target.h @@ -232,10 +232,10 @@ typedef unsigned char regNumberSmall; #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_PASS_SINGLEREG_BYTES 8 // Maximum size of a struct passed in a single register (double). - #define MAX_PASS_MULTIREG_BYTES 0 // No multireg arguments (note this seems wrong as MAX_ARG_REG_COUNT is 2) + #define MAX_PASS_MULTIREG_BYTES 0 // No multireg arguments #define MAX_RET_MULTIREG_BYTES 8 // Maximum size of a struct that could be returned in more than one register - #define MAX_ARG_REG_COUNT 2 // Maximum registers used to pass an argument. + #define MAX_ARG_REG_COUNT 1 // Maximum registers used to pass an argument. #define MAX_RET_REG_COUNT 2 // Maximum registers used to return a value. #define MAX_MULTIREG_COUNT 2 // Maxiumum number of registers defined by a single instruction (including calls).