From d41158297592422c273b6a599234b25fe7bb8fe7 Mon Sep 17 00:00:00 2001 From: Joseph Tremoulet Date: Mon, 13 Mar 2017 11:49:16 -0700 Subject: [PATCH] Scalarize single-field-to-lclVar block copy Morph has some logic to scalarize struct copies where the RHS is a scalar and the LHS is a single-field struct. This change adds the symmetric logic where the LHS is a scalar and the RHS is a single-field struct. This pattern shows up in Span methods that initialize the data pointer field, due to the way scalar replacement operates on spans. Commit migrated from https://github.com/dotnet/coreclr/commit/a9fbca32b572413dcccfe0314766edaed9c8934a --- src/coreclr/src/jit/morph.cpp | 96 +++++++++++++++++++++++++++++-------------- 1 file changed, 66 insertions(+), 30 deletions(-) diff --git a/src/coreclr/src/jit/morph.cpp b/src/coreclr/src/jit/morph.cpp index f9cfbf5..e64e83d 100644 --- a/src/coreclr/src/jit/morph.cpp +++ b/src/coreclr/src/jit/morph.cpp @@ -9750,8 +9750,9 @@ GenTreePtr Compiler::fgMorphCopyBlock(GenTreePtr tree) // Check to see if we are required to do a copy block because the struct contains holes // and either the src or dest is externally visible // - bool requiresCopyBlock = false; - bool srcSingleLclVarAsg = false; + bool requiresCopyBlock = false; + bool srcSingleLclVarAsg = false; + bool destSingleLclVarAsg = false; if ((destLclVar != nullptr) && (srcLclVar == destLclVar)) { @@ -9867,6 +9868,30 @@ GenTreePtr Compiler::fgMorphCopyBlock(GenTreePtr tree) } } } + else + { + assert(srcDoFldAsg); + // Check for the symmetric case (which happens for the _pointer field of promoted spans): + // + // [000240] -----+------ /--* lclVar struct(P) V18 tmp9 + // /--* byref V18._value (offs=0x00) -> V30 tmp21 + // [000245] -A------R--- * = struct (copy) + // [000244] -----+------ \--* obj(8) struct + // [000243] -----+------ \--* addr byref + // [000242] D----+-N---- \--* lclVar byref V28 tmp19 + // + if (blockWidthIsConst && (srcLclVar->lvFieldCnt == 1) && (destLclVar != nullptr) && + (blockWidth == genTypeSize(destLclVar->TypeGet()))) + { + // Check for type agreement + unsigned fieldLclNum = lvaTable[srcLclNum].lvFieldLclStart; + var_types srcType = lvaTable[fieldLclNum].TypeGet(); + if (destLclVar->TypeGet() == srcType) + { + destSingleLclVarAsg = true; + } + } + } } // If we require a copy block the set both of the field assign bools to false @@ -9883,7 +9908,7 @@ GenTreePtr Compiler::fgMorphCopyBlock(GenTreePtr tree) // when they are not reg-sized non-field-addressed structs and we are using a CopyBlock // or the struct is not promoted // - if (!destDoFldAsg && (destLclVar != nullptr)) + if (!destDoFldAsg && (destLclVar != nullptr) && !destSingleLclVarAsg) { if (!destLclVar->lvRegStruct) { @@ -10137,45 +10162,56 @@ GenTreePtr Compiler::fgMorphCopyBlock(GenTreePtr tree) noway_assert(srcLclNum != BAD_VAR_NUM); unsigned fieldLclNum = lvaTable[srcLclNum].lvFieldLclStart + i; - if (addrSpill) + if (destSingleLclVarAsg) { - assert(addrSpillTemp != BAD_VAR_NUM); - dest = gtNewLclvNode(addrSpillTemp, TYP_BYREF); + noway_assert(fieldCnt == 1); + noway_assert(destLclVar != nullptr); + noway_assert(addrSpill == nullptr); + + dest = gtNewLclvNode(destLclNum, destLclVar->TypeGet()); } else { - dest = gtCloneExpr(destAddr); - noway_assert(dest != nullptr); - - // Is the address of a local? - GenTreeLclVarCommon* lclVarTree = nullptr; - bool isEntire = false; - bool* pIsEntire = (blockWidthIsConst ? &isEntire : nullptr); - if (dest->DefinesLocalAddr(this, blockWidth, &lclVarTree, pIsEntire)) + if (addrSpill) + { + assert(addrSpillTemp != BAD_VAR_NUM); + dest = gtNewLclvNode(addrSpillTemp, TYP_BYREF); + } + else { - lclVarTree->gtFlags |= GTF_VAR_DEF; - if (!isEntire) + dest = gtCloneExpr(destAddr); + noway_assert(dest != nullptr); + + // Is the address of a local? + GenTreeLclVarCommon* lclVarTree = nullptr; + bool isEntire = false; + bool* pIsEntire = (blockWidthIsConst ? &isEntire : nullptr); + if (dest->DefinesLocalAddr(this, blockWidth, &lclVarTree, pIsEntire)) { - lclVarTree->gtFlags |= GTF_VAR_USEASG; + lclVarTree->gtFlags |= GTF_VAR_DEF; + if (!isEntire) + { + lclVarTree->gtFlags |= GTF_VAR_USEASG; + } } } - } - GenTreePtr fieldOffsetNode = gtNewIconNode(lvaTable[fieldLclNum].lvFldOffset, TYP_I_IMPL); - // Have to set the field sequence -- which means we need the field handle. - CORINFO_CLASS_HANDLE classHnd = lvaTable[srcLclNum].lvVerTypeInfo.GetClassHandle(); - CORINFO_FIELD_HANDLE fieldHnd = - info.compCompHnd->getFieldInClass(classHnd, lvaTable[fieldLclNum].lvFldOrdinal); - curFieldSeq = GetFieldSeqStore()->CreateSingleton(fieldHnd); - fieldOffsetNode->gtIntCon.gtFieldSeq = curFieldSeq; + GenTreePtr fieldOffsetNode = gtNewIconNode(lvaTable[fieldLclNum].lvFldOffset, TYP_I_IMPL); + // Have to set the field sequence -- which means we need the field handle. + CORINFO_CLASS_HANDLE classHnd = lvaTable[srcLclNum].lvVerTypeInfo.GetClassHandle(); + CORINFO_FIELD_HANDLE fieldHnd = + info.compCompHnd->getFieldInClass(classHnd, lvaTable[fieldLclNum].lvFldOrdinal); + curFieldSeq = GetFieldSeqStore()->CreateSingleton(fieldHnd); + fieldOffsetNode->gtIntCon.gtFieldSeq = curFieldSeq; - dest = gtNewOperNode(GT_ADD, TYP_BYREF, dest, fieldOffsetNode); + dest = gtNewOperNode(GT_ADD, TYP_BYREF, dest, fieldOffsetNode); - dest = gtNewOperNode(GT_IND, lvaTable[fieldLclNum].TypeGet(), dest); + dest = gtNewOperNode(GT_IND, lvaTable[fieldLclNum].TypeGet(), dest); - // !!! The destination could be on stack. !!! - // This flag will let us choose the correct write barrier. - dest->gtFlags |= GTF_IND_TGTANYWHERE; + // !!! The destination could be on stack. !!! + // This flag will let us choose the correct write barrier. + dest->gtFlags |= GTF_IND_TGTANYWHERE; + } } if (srcDoFldAsg) -- 2.7.4