@@ -8212,89 +8212,109 @@ void Lowering::ContainCheckBitCast(GenTree* node)
82128212 }
82138213}
82148214
8215+ // ------------------------------------------------------------------------
8216+ // TryLowerBlockStoreAsGcBulkCopyCall: Lower a block store node as a CORINFO_HELP_ASSIGN_STRUCT call
8217+ //
8218+ // Arguments:
8219+ // blkNode - The block store node to lower
8220+ //
82158221bool Lowering::TryLowerBlockStoreAsGcBulkCopyCall (GenTreeBlk* blk)
82168222{
8217- if (comp->opts .OptimizationDisabled () || ! ISMETHOD ( " Test " ) )
8223+ if (comp->opts .OptimizationDisabled ())
82188224 {
82198225 return false ;
82208226 }
82218227
82228228 // Replace STORE_BLK (struct copy) with CORINFO_HELP_ASSIGN_STRUCT which performs
82238229 // bulk copy for byrefs.
82248230 const unsigned bulkCopyThreshold = 4 ;
8225- if (blk->OperIs (GT_STORE_BLK) && !blk->OperIsInitBlkOp () &&
8226- (blk->GetLayout ()->GetGCPtrCount () >= bulkCopyThreshold))
8231+ if (!blk->OperIs (GT_STORE_BLK) || blk->OperIsInitBlkOp () || (blk->GetLayout ()->GetGCPtrCount () < bulkCopyThreshold))
82278232 {
8228- GenTree* addr = blk-> Addr () ;
8229- GenTree* data = blk-> Data ();
8233+ return false ;
8234+ }
82308235
8231- const unsigned gcPtrs = blk->GetLayout ()-> GetGCPtrCount ();
8232- if (! CheckedOps::MulOverflows (( int )gcPtrs, TARGET_POINTER_SIZE, true ))
8233- {
8234- if (data->OperIs (GT_IND))
8235- {
8236- // Drop GT_IND nodes
8237- BlockRange ().Remove (data);
8238- data = data->AsIndir ()->Addr ();
8239- }
8240- else
8241- {
8242- assert (data->OperIs (GT_LCL_VAR, GT_LCL_FLD));
8243-
8244- // Convert local to LCL_ADDR
8245- unsigned lclOffset = data->AsLclVarCommon ()->GetLclOffs ();
8246- data->ChangeOper (GT_LCL_ADDR);
8247- data->ChangeType (TYP_I_IMPL);
8248- data->AsLclFld ()->SetLclOffs (lclOffset);
8249- data->ClearContained ();
8250- }
8236+ GenTree* dest = blk->Addr ();
8237+ GenTree* data = blk-> Data ();
8238+
8239+ if (data->OperIs (GT_IND))
8240+ {
8241+ // Drop GT_IND nodes
8242+ BlockRange ().Remove (data);
8243+ data = data->AsIndir ()->Addr ();
8244+ }
8245+ else
8246+ {
8247+ assert (data->OperIs (GT_LCL_VAR, GT_LCL_FLD));
8248+
8249+ // Convert local to LCL_ADDR
8250+ unsigned lclOffset = data->AsLclVarCommon ()->GetLclOffs ();
8251+ data->ChangeOper (GT_LCL_ADDR);
8252+ data->ChangeType (TYP_I_IMPL);
8253+ data->AsLclFld ()->SetLclOffs (lclOffset);
8254+ data->ClearContained ();
8255+ }
82518256
8252- // Size is a constant
8253- GenTreeIntCon* size = comp->gtNewIconNode ((ssize_t )gcPtrs * TARGET_POINTER_SIZE, TYP_I_IMPL);
8254- BlockRange ().InsertBefore (data, size);
8257+ // Size is a constant
8258+ GenTreeIntCon* size = comp->gtNewIconNode ((ssize_t )blk->GetLayout ()->GetSize (), TYP_I_IMPL);
8259+ BlockRange ().InsertBefore (data, size);
8260+
8261+ // A hacky way to safely call fgMorphTree in Lower
8262+ GenTree* destPlaceholder = comp->gtNewZeroConNode (dest->TypeGet ());
8263+ GenTree* dataPlaceholder = comp->gtNewZeroConNode (genActualType (data));
8264+ GenTree* sizePlaceholder = comp->gtNewZeroConNode (genActualType (size));
82558265
8256- // A hacky way to safely call fgMorphTree in Lower
8257- GenTree* destPlaceholder = comp->gtNewZeroConNode (addr->TypeGet ());
8258- GenTree* dataPlaceholder = comp->gtNewZeroConNode (genActualType (data));
8259- GenTree* sizePlaceholder = comp->gtNewZeroConNode (genActualType (size));
8266+ GenTreeCall* call = comp->gtNewHelperCallNode (CORINFO_HELP_ASSIGN_STRUCT, TYP_VOID, destPlaceholder,
8267+ dataPlaceholder, sizePlaceholder);
8268+ comp->fgMorphArgs (call);
82608269
8261- GenTreeCall* call = comp->gtNewHelperCallNode (CORINFO_HELP_ASSIGN_STRUCT, TYP_VOID, destPlaceholder, dataPlaceholder, sizePlaceholder);
8262- comp->fgMorphArgs (call);
8270+ LIR::Range range = LIR::SeqTree (comp, call);
8271+ GenTree* rangeStart = range.FirstNode ();
8272+ GenTree* rangeEnd = range.LastNode ();
82638273
8264- LIR::Range range = LIR::SeqTree (comp, call);
8265- GenTree* rangeStart = range.FirstNode ();
8266- GenTree* rangeEnd = range.LastNode ();
8274+ BlockRange ().InsertBefore (blk, std::move (range));
8275+ blk->gtBashToNOP ();
82678276
8268- BlockRange ().InsertBefore (blk, std::move (range));
8269- blk->gtBashToNOP ();
8277+ LIR::Use destUse;
8278+ LIR::Use sizeUse;
8279+ BlockRange ().TryGetUse (destPlaceholder, &destUse);
8280+ BlockRange ().TryGetUse (sizePlaceholder, &sizeUse);
8281+ destUse.ReplaceWith (dest);
8282+ sizeUse.ReplaceWith (size);
8283+ destPlaceholder->SetUnusedValue ();
8284+ sizePlaceholder->SetUnusedValue ();
82708285
8271- LIR::Use destUse;
8272- LIR::Use sizeUse;
8273- BlockRange ().TryGetUse (destPlaceholder, &destUse);
8274- BlockRange ().TryGetUse (sizePlaceholder, &sizeUse);
8275- destUse.ReplaceWith (addr);
8276- sizeUse.ReplaceWith (size);
8277- destPlaceholder->SetUnusedValue ();
8278- sizePlaceholder->SetUnusedValue ();
8286+ LIR::Use dataUse;
8287+ BlockRange ().TryGetUse (dataPlaceholder, &dataUse);
8288+ dataUse.ReplaceWith (data);
8289+ dataPlaceholder->SetUnusedValue ();
82798290
8280- LIR::Use dataUse;
8281- BlockRange ().TryGetUse (dataPlaceholder, &dataUse);
8282- dataUse.ReplaceWith (data);
8283- dataPlaceholder->SetUnusedValue ();
8291+ LowerRange (rangeStart, rangeEnd);
82848292
8285- LowerRange (rangeStart, rangeEnd);
8293+ // Finally move all GT_PUTARG_* nodes
8294+ // Re-use the existing logic for CFG call args here
8295+ MoveCFGCallArgs (call);
82868296
8287- // Finally move all GT_PUTARG_* nodes
8288- // Re-use the existing logic for CFG call args here
8289- MoveCFGCallArgs (call );
8297+ BlockRange (). Remove (destPlaceholder);
8298+ BlockRange (). Remove (sizePlaceholder);
8299+ BlockRange (). Remove (dataPlaceholder );
82908300
8291- BlockRange ().Remove (destPlaceholder);
8292- BlockRange ().Remove (sizePlaceholder);
8293- BlockRange ().Remove (dataPlaceholder);
8294- return true ;
8301+ // Add implicit nullchecks for dest and data if needed:
8302+ //
8303+ auto wrapWithNullcheck = [&](GenTree* node) {
8304+ if (comp->fgAddrCouldBeNull (node))
8305+ {
8306+ LIR::Use nodeUse;
8307+ BlockRange ().TryGetUse (node, &nodeUse);
8308+ GenTree* nodeClone = comp->gtNewLclvNode (nodeUse.ReplaceWithLclVar (comp), genActualType (node));
8309+ GenTree* nullcheck = comp->gtNewNullCheck (nodeClone, comp->compCurBB );
8310+ BlockRange ().InsertAfter (nodeUse.Def (), nodeClone, nullcheck);
8311+ LowerNode (nullcheck);
82958312 }
8296- }
8297- return false ;
8313+ };
8314+ wrapWithNullcheck (dest);
8315+ wrapWithNullcheck (data);
8316+
8317+ return true ;
82988318}
82998319
83008320// ------------------------------------------------------------------------
0 commit comments