diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index c19a7d2e50a71..dfa605d137d33 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -9893,6 +9893,10 @@ void Compiler::EnregisterStats::RecordLocal(const LclVarDsc* varDsc) m_returnSpCheck++; break; + case DoNotEnregisterReason::SimdUserForcesDep: + m_simdUserForcesDep++; + break; + default: unreached(); break; @@ -10014,6 +10018,7 @@ void Compiler::EnregisterStats::Dump(FILE* fout) const PRINT_STATS(m_swizzleArg, notEnreg); PRINT_STATS(m_blockOpRet, notEnreg); PRINT_STATS(m_returnSpCheck, notEnreg); + PRINT_STATS(m_simdUserForcesDep, notEnreg); fprintf(fout, "\nAddr exposed details:\n"); if (m_addrExposed == 0) diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 69d88c4a57e28..ad43296939ed9 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -385,11 +385,12 @@ enum class DoNotEnregisterReason #endif LclAddrNode, // the local is accessed with LCL_ADDR_VAR/FLD. CastTakesAddr, - StoreBlkSrc, // the local is used as STORE_BLK source. - OneAsgRetyping, // fgMorphOneAsgBlockOp prevents this local from being enregister. - SwizzleArg, // the local is passed using LCL_FLD as another type. - BlockOpRet, // the struct is returned and it promoted or there is a cast. - ReturnSpCheck // the local is used to do SP check + StoreBlkSrc, // the local is used as STORE_BLK source. + OneAsgRetyping, // fgMorphOneAsgBlockOp prevents this local from being enregister. + SwizzleArg, // the local is passed using LCL_FLD as another type. + BlockOpRet, // the struct is returned and it promoted or there is a cast. + ReturnSpCheck, // the local is used to do SP check + SimdUserForcesDep // a promoted struct was used by a SIMD/HWI node; it must be dependently promoted }; enum class AddressExposedReason @@ -10507,6 +10508,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX unsigned m_swizzleArg; unsigned m_blockOpRet; unsigned m_returnSpCheck; + unsigned m_simdUserForcesDep; unsigned m_liveInOutHndlr; unsigned m_depField; unsigned m_noRegVars; diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp index a1cfc61822216..40fc77c92d862 100644 --- a/src/coreclr/jit/lclvars.cpp +++ b/src/coreclr/jit/lclvars.cpp @@ -2584,6 +2584,10 @@ void Compiler::lvaSetVarDoNotEnregister(unsigned varNum DEBUGARG(DoNotEnregister JITDUMP("Used for SP check\n"); break; + case DoNotEnregisterReason::SimdUserForcesDep: + JITDUMP("Promoted struct used by a SIMD/HWI node\n"); + break; + default: unreached(); break; diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index b3e9e8fa82977..261b7a5d6d0e0 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -14295,11 +14295,26 @@ GenTree* Compiler::fgMorphMultiOp(GenTreeMultiOp* multiOp) for (GenTree** use : multiOp->UseEdges()) { *use = fgMorphTree(*use); - multiOp->gtFlags |= ((*use)->gtFlags & GTF_ALL_EFFECT); - if (dontCseConstArguments && (*use)->OperIsConst()) + GenTree* operand = *use; + multiOp->gtFlags |= (operand->gtFlags & GTF_ALL_EFFECT); + + if (dontCseConstArguments && operand->OperIsConst()) + { + operand->SetDoNotCSE(); + } + + // Promoted structs after morph must be in one of two states: + // a) Fully eliminated from the IR (independent promotion) OR only be + // used by "special" nodes (e. g. LHS of ASGs for multi-reg structs). + // b) Marked as do-not-enregister (dependent promotion). + // + // So here we preserve this invariant and mark any promoted structs as do-not-enreg. + // + if (operand->OperIs(GT_LCL_VAR) && lvaGetDesc(operand->AsLclVar())->lvPromoted) { - (*use)->SetDoNotCSE(); + lvaSetVarDoNotEnregister(operand->AsLclVar()->GetLclNum() + DEBUGARG(DoNotEnregisterReason::SimdUserForcesDep)); } }