diff --git a/src/coreclr/jit/fginline.cpp b/src/coreclr/jit/fginline.cpp index 88e70cbb57d66..9131c7eb6f355 100644 --- a/src/coreclr/jit/fginline.cpp +++ b/src/coreclr/jit/fginline.cpp @@ -340,11 +340,6 @@ class SubstitutePlaceholdersAndDevirtualizeWalker : public GenTreeVisitorgtOp2 = AssignStructInlineeToVar(inlinee, retClsHnd); } } - else if (dst->IsMultiRegLclVar()) - { - // This is no longer a multi-reg assignment -- clear the flag. - dst->AsLclVar()->ClearMultiReg(); - } } //------------------------------------------------------------------------ diff --git a/src/coreclr/jit/forwardsub.cpp b/src/coreclr/jit/forwardsub.cpp index 7ae30b2382636..f4750576d59e8 100644 --- a/src/coreclr/jit/forwardsub.cpp +++ b/src/coreclr/jit/forwardsub.cpp @@ -469,14 +469,6 @@ bool Compiler::fgForwardSubStatement(Statement* stmt) return false; } - // If lhs is mulit-reg, rhs must be too. - // - if (lhsNode->IsMultiRegNode() && !fwdSubNode->IsMultiRegNode()) - { - JITDUMP(" would change multi-reg (assignment)\n"); - return false; - } - // Don't fwd sub overly large trees. // Size limit here is ad-hoc. Need to tune. // @@ -663,16 +655,16 @@ bool Compiler::fgForwardSubStatement(Statement* stmt) // There are implicit assumptions downstream on where/how multi-reg ops // can appear. // - // Eg if fwdSubNode is a multi-reg call, parent node must be GT_ASG and the - // local being defined must be specially marked up. + // Eg if fwdSubNode is a multi-reg call, parent node must be GT_ASG and + // the local being defined must be specially marked up. // - if (varTypeIsStruct(fwdSubNode) && fwdSubNode->IsMultiRegCall()) + if (varTypeIsStruct(fwdSubNode) && fwdSubNode->IsMultiRegNode()) { GenTree* const parentNode = fsv.GetParentNode(); if (!parentNode->OperIs(GT_ASG)) { - JITDUMP(" multi-reg struct call, parent not asg\n"); + JITDUMP(" multi-reg struct node, parent not asg\n"); return false; } @@ -680,7 +672,7 @@ bool Compiler::fgForwardSubStatement(Statement* stmt) if (!parentNodeLHS->OperIs(GT_LCL_VAR)) { - JITDUMP(" multi-reg struct call, parent not asg(lcl, ...)\n"); + JITDUMP(" multi-reg struct node, parent not asg(lcl, ...)\n"); return false; } @@ -691,7 +683,6 @@ bool Compiler::fgForwardSubStatement(Statement* stmt) JITDUMP(" [marking V%02u as multi-reg-ret]", lhsLclNum); lhsVarDsc->lvIsMultiRegRet = true; - parentNodeLHSLocal->SetMultiReg(); } // If a method returns a multi-reg type, only forward sub locals, @@ -731,18 +722,9 @@ bool Compiler::fgForwardSubStatement(Statement* stmt) JITDUMP(" [marking V%02u as multi-reg-ret]", fwdLclNum); fwdVarDsc->lvIsMultiRegRet = true; - fwdSubNodeLocal->SetMultiReg(); fwdSubNodeLocal->gtFlags |= GTF_DONT_CSE; } - // If the use is a multi-reg arg, don't forward sub non-locals. - // - if (fsv.GetNode()->IsMultiRegNode() && !fwdSubNode->IsMultiRegNode()) - { - JITDUMP(" would change multi-reg (substitution)\n"); - return false; - } - // If the initial has truncate on store semantics, we need to replicate // that here with a cast. // diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index 1865e5b5cddb4..0658db6409138 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -464,7 +464,8 @@ enum GenTreeFlags : unsigned int GTF_VAR_MULTIREG = 0x02000000, // This is a struct or (on 32-bit platforms) long variable that is used or defined // to/from a multireg source or destination (e.g. a call arg or return, or an op - // that returns its result in multiple registers such as a long multiply). + // that returns its result in multiple registers such as a long multiply). Set by + // (and thus only valid after) lowering. GTF_LIVENESS_MASK = GTF_VAR_DEF | GTF_VAR_USEASG | GTF_VAR_DEATH_MASK, diff --git a/src/coreclr/jit/hwintrinsicarm64.cpp b/src/coreclr/jit/hwintrinsicarm64.cpp index b4b23fa2ca3e3..2523d3d9dd15d 100644 --- a/src/coreclr/jit/hwintrinsicarm64.cpp +++ b/src/coreclr/jit/hwintrinsicarm64.cpp @@ -1800,27 +1800,10 @@ GenTree* Compiler::impSpecialIntrinsic(NamedIntrinsic intrinsic, } } - GenTree* loadIntrinsic = gtNewSimdHWIntrinsicNode(retType, op1, intrinsic, simdBaseJitType, simdSize); - // This operation contains an implicit indirection - // it could point into the global heap or - // it could throw a null reference exception. - // - loadIntrinsic->gtFlags |= (GTF_GLOB_REF | GTF_EXCEPT); - assert(HWIntrinsicInfo::IsMultiReg(intrinsic)); - const unsigned lclNum = lvaGrabTemp(true DEBUGARG("Return value temp for multireg intrinsic")); - impAssignTempGen(lclNum, loadIntrinsic, sig->retTypeSigClass, CHECK_SPILL_ALL); - - LclVarDsc* varDsc = lvaGetDesc(lclNum); - // The following is to exclude the fields of the local to have SSA. - varDsc->lvIsMultiRegRet = true; - - GenTreeLclVar* lclVar = gtNewLclvNode(lclNum, varDsc->lvType); - lclVar->SetDoNotCSE(); - lclVar->SetMultiReg(); - - retNode = lclVar; + op1 = gtNewSimdHWIntrinsicNode(retType, op1, intrinsic, simdBaseJitType, simdSize); + retNode = impAssignMultiRegTypeToVar(op1, sig->retTypeSigClass DEBUGARG(CorInfoCallConvExtension::Managed)); break; } diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 37ceb8d7f5b16..a42812060d38b 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -1493,18 +1493,9 @@ GenTree* Compiler::impAssignStructPtr(GenTree* destAddr, } } - if (dest->OperIs(GT_LCL_VAR) && - (src->IsMultiRegNode() || - (src->OperIs(GT_RET_EXPR) && src->AsRetExpr()->gtInlineCandidate->AsCall()->HasMultiRegRetVal()))) + if (dest->OperIs(GT_LCL_VAR) && src->IsMultiRegNode()) { - if (lvaEnregMultiRegVars && varTypeIsStruct(dest)) - { - dest->AsLclVar()->SetMultiReg(); - } - if (src->OperIs(GT_CALL)) - { - lvaGetDesc(dest->AsLclVar())->lvIsMultiRegRet = true; - } + lvaGetDesc(dest->AsLclVar())->lvIsMultiRegRet = true; } dest->gtFlags |= destFlags; @@ -17023,7 +17014,7 @@ GenTree* Compiler::impAssignMultiRegTypeToVar(GenTree* op, assert(IsMultiRegReturnedType(hClass, callConv)); - // Mark the var so that fields are not promoted and stay together. + // Set "lvIsMultiRegRet" to block promotion under "!lvaEnregMultiRegVars". lvaTable[tmpNum].lvIsMultiRegRet = true; return ret; diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index e3fb7569473c2..5bf930cc82f24 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -3390,13 +3390,11 @@ void Lowering::LowerStoreLocCommon(GenTreeLclVarCommon* lclStore) DISPTREERANGE(BlockRange(), lclStore); JITDUMP("\n"); - GenTree* src = lclStore->gtGetOp1(); - LclVarDsc* varDsc = comp->lvaGetDesc(lclStore); - + GenTree* src = lclStore->gtGetOp1(); + LclVarDsc* varDsc = comp->lvaGetDesc(lclStore); const bool srcIsMultiReg = src->IsMultiRegNode(); - const bool dstIsMultiReg = lclStore->IsMultiRegLclVar(); - if (!dstIsMultiReg && varTypeIsStruct(varDsc)) + if (!srcIsMultiReg && varTypeIsStruct(varDsc)) { // TODO-Cleanup: we want to check `varDsc->lvRegStruct` as the last condition instead of `!varDsc->lvPromoted`, // but we do not set it for `CSE` vars so it is currently failing. @@ -3416,7 +3414,7 @@ void Lowering::LowerStoreLocCommon(GenTreeLclVarCommon* lclStore) } } - if (srcIsMultiReg || dstIsMultiReg) + if (srcIsMultiReg) { const ReturnTypeDesc* retTypeDesc = nullptr; if (src->OperIs(GT_CALL)) @@ -3577,7 +3575,7 @@ void Lowering::LowerStoreLocCommon(GenTreeLclVarCommon* lclStore) // src and dst can be in registers, check if we need a bitcast. if (!src->TypeIs(TYP_STRUCT) && (varTypeUsesFloatReg(lclRegType) != varTypeUsesFloatReg(src))) { - assert(!srcIsMultiReg && !dstIsMultiReg); + assert(!srcIsMultiReg); assert(lclStore->OperIsLocalStore()); assert(lclRegType != TYP_UNDEF); diff --git a/src/coreclr/jit/morphblock.cpp b/src/coreclr/jit/morphblock.cpp index ae970e0b25df2..662e527bc8ccf 100644 --- a/src/coreclr/jit/morphblock.cpp +++ b/src/coreclr/jit/morphblock.cpp @@ -56,8 +56,8 @@ class MorphInitBlockHelper FieldByField, OneAsgBlock, StructBlock, - SkipCallSrc, - SkipMultiRegIntrinsicSrc, + SkipMultiRegSrc, + SkipSingleRegCallSrc, Nop }; @@ -803,44 +803,25 @@ void MorphCopyBlockHelper::PrepareSrc() // TrySpecialCases: check special cases that require special transformations. // The current special cases include assignments with calls in RHS. // -// Notes: -// It could change multiReg flags or change m_dst node. -// void MorphCopyBlockHelper::TrySpecialCases() { -#ifdef FEATURE_HW_INTRINSICS - if (m_src->OperIsHWIntrinsic() && HWIntrinsicInfo::IsMultiReg(m_src->AsHWIntrinsic()->GetHWIntrinsicId())) + if (m_src->IsMultiRegNode()) { - assert(m_src->IsMultiRegNode()); - JITDUMP("Not morphing a multireg intrinsic\n"); - m_transformationDecision = BlockTransformation::SkipMultiRegIntrinsicSrc; - m_result = m_asg; - } -#endif // FEATURE_HW_INTRINSICS + assert(m_dst->OperIs(GT_LCL_VAR)); -#if FEATURE_MULTIREG_RET - // If this is a multi-reg return, we will not do any morphing of this node. - if (m_src->IsMultiRegCall()) - { - assert(m_dst->OperGet() == GT_LCL_VAR); - JITDUMP("Not morphing a multireg call return\n"); - m_transformationDecision = BlockTransformation::SkipCallSrc; + // This will exclude field locals (if any) from SSA: we do not have a way to + // associate multiple SSA definitions (SSA numbers) with one store. + m_dstVarDsc->lvIsMultiRegRet = true; + + JITDUMP("Not morphing a multireg node return\n"); + m_transformationDecision = BlockTransformation::SkipMultiRegSrc; m_result = m_asg; } - else if (m_dst->IsMultiRegLclVar() && !m_src->IsMultiRegNode()) + else if (m_src->IsCall() && m_dst->OperIs(GT_LCL_VAR) && m_dstVarDsc->CanBeReplacedWithItsField(m_comp)) { - m_dst->AsLclVar()->ClearMultiReg(); - } -#endif // FEATURE_MULTIREG_RET - - if (m_transformationDecision == BlockTransformation::Undefined) - { - if (m_src->IsCall() && m_dst->OperIs(GT_LCL_VAR) && m_dstVarDsc->CanBeReplacedWithItsField(m_comp)) - { - JITDUMP("Not morphing a single reg call return\n"); - m_transformationDecision = BlockTransformation::SkipCallSrc; - m_result = m_asg; - } + JITDUMP("Not morphing a single reg call return\n"); + m_transformationDecision = BlockTransformation::SkipSingleRegCallSrc; + m_result = m_asg; } } @@ -1131,12 +1112,6 @@ void MorphCopyBlockHelper::MorphStructCases() // Mark it as DoNotEnregister. m_comp->lvaSetVarDoNotEnregister(m_dstLclNum DEBUGARG(DoNotEnregisterReason::BlockOp)); } - else if (m_dst->IsMultiRegLclVar()) - { - // Handle this as lvIsMultiRegRet; this signals to SSA that it can't consider these fields - // SSA candidates (we don't have a way to represent multiple SSANums on MultiRegLclVar nodes). - m_dstVarDsc->lvIsMultiRegRet = true; - } } if (!m_srcDoFldAsg && (m_srcVarDsc != nullptr) && !m_srcSingleLclVarAsg)