diff --git a/src/coreclr/src/jit/compiler.h b/src/coreclr/src/jit/compiler.h index 8f9b8e7535a89b..f35f6f769ed630 100644 --- a/src/coreclr/src/jit/compiler.h +++ b/src/coreclr/src/jit/compiler.h @@ -3496,19 +3496,21 @@ class Compiler assert(varDsc->lvSize() == 16); #endif // defined(TARGET_64BIT) - // We make local variable SIMD12 types 16 bytes instead of just 12. lvSize() - // already does this calculation. However, we also need to prevent mapping types if the var is a - // dependently promoted struct field, which must remain its exact size within its parent struct. - // However, we don't know this until late, so we may have already pretended the field is bigger - // before that. - if ((varDsc->lvSize() == 16) && !lvaIsFieldOfDependentlyPromotedStruct(varDsc)) + // We make local variable SIMD12 types 16 bytes instead of just 12. + // lvSize() will return 16 bytes for SIMD12, even for fields. + // However, we can't do that mapping if the var is a dependently promoted struct field. + // Such a field must remain its exact size within its parent struct unless it is a single + // field *and* it is the only field in a struct of 16 bytes. + if (varDsc->lvSize() != 16) { - return true; + return false; } - else + if (lvaIsFieldOfDependentlyPromotedStruct(varDsc)) { - return false; + LclVarDsc* parentVarDsc = lvaGetDesc(varDsc->lvParentLcl); + return (parentVarDsc->lvFieldCnt == 1) && (parentVarDsc->lvSize() == 16); } + return true; } #endif // defined(FEATURE_SIMD) diff --git a/src/coreclr/src/jit/morph.cpp b/src/coreclr/src/jit/morph.cpp index fc64cf09877f92..379cf107b62f02 100644 --- a/src/coreclr/src/jit/morph.cpp +++ b/src/coreclr/src/jit/morph.cpp @@ -3665,31 +3665,21 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call) #else // UNIX_AMD64_ABI // On Unix, structs are always passed by value. // We only need a copy if we have one of the following: - // - We have a lclVar that has been promoted and is passed in registers. // - The sizes don't match for a non-lclVar argument. // - We have a known struct type (e.g. SIMD) that requires multiple registers. - // TODO-Amd64-Unix-CQ: The first case could and should be handled without copies. // TODO-Amd64-Unix-Throughput: We don't need to keep the structDesc in the argEntry if it's not // actually passed in registers. if (argEntry->isPassedInRegisters()) { assert(argEntry->structDesc.passedInRegisters); - if (lclVar != nullptr) - { - if (lvaGetPromotionType(lclVar->AsLclVarCommon()->GetLclNum()) == - PROMOTION_TYPE_INDEPENDENT) - { - copyBlkClass = objClass; - } - } - else if (argObj->OperIs(GT_OBJ)) + if (argObj->OperIs(GT_OBJ)) { if (passingSize != structSize) { copyBlkClass = objClass; } } - else + else if (lclVar == nullptr) { // This should only be the case of a value directly producing a known struct type. assert(argObj->TypeGet() != TYP_STRUCT); @@ -9720,7 +9710,9 @@ GenTree* Compiler::fgMorphInitBlock(GenTree* tree) } #endif // LOCAL_ASSERTION_PROP - if (destLclVar->lvPromoted) + // If we have already determined that a promoted TYP_STRUCT lclVar will not be enregistered, + // we are better off doing a block init. + if (destLclVar->lvPromoted && (!destLclVar->lvDoNotEnregister || !destLclNode->TypeIs(TYP_STRUCT))) { GenTree* newTree = fgMorphPromoteLocalInitBlock(destLclNode->AsLclVar(), initVal, blockSize); @@ -10604,15 +10596,30 @@ GenTree* Compiler::fgMorphCopyBlock(GenTree* tree) // Are both dest and src promoted structs? if (destDoFldAsg && srcDoFldAsg) { - // Both structs should be of the same type, or each have a single field of the same type. + // Both structs should be of the same type, or have the same number of fields of the same type. // If not we will use a copy block. - if (lvaTable[destLclNum].lvVerTypeInfo.GetClassHandle() != - lvaTable[srcLclNum].lvVerTypeInfo.GetClassHandle()) + bool misMatchedTypes = false; + if (destLclVar->lvVerTypeInfo.GetClassHandle() != srcLclVar->lvVerTypeInfo.GetClassHandle()) { - unsigned destFieldNum = lvaTable[destLclNum].lvFieldLclStart; - unsigned srcFieldNum = lvaTable[srcLclNum].lvFieldLclStart; - if ((lvaTable[destLclNum].lvFieldCnt != 1) || (lvaTable[srcLclNum].lvFieldCnt != 1) || - (lvaTable[destFieldNum].lvType != lvaTable[srcFieldNum].lvType)) + if (destLclVar->lvFieldCnt != srcLclVar->lvFieldCnt) + { + misMatchedTypes = true; + } + else + { + for (int i = 0; i < destLclVar->lvFieldCnt; i++) + { + LclVarDsc* destFieldVarDsc = lvaGetDesc(destLclVar->lvFieldLclStart + i); + LclVarDsc* srcFieldVarDsc = lvaGetDesc(srcLclVar->lvFieldLclStart + i); + if ((destFieldVarDsc->lvType != srcFieldVarDsc->lvType) || + (destFieldVarDsc->lvFldOffset != srcFieldVarDsc->lvFldOffset)) + { + misMatchedTypes = true; + break; + } + } + } + if (misMatchedTypes) { requiresCopyBlock = true; // Mismatched types, leave as a CopyBlock JITDUMP(" with mismatched types");