@@ -8212,6 +8212,91 @@ void Lowering::ContainCheckBitCast(GenTree* node)
8212
8212
}
8213
8213
}
8214
8214
8215
+ bool Lowering::TryLowerBlockStoreAsGcBulkCopyCall (GenTreeBlk* blk)
8216
+ {
8217
+ if (comp->opts .OptimizationDisabled () || !ISMETHOD (" Test" ))
8218
+ {
8219
+ return false ;
8220
+ }
8221
+
8222
+ // Replace STORE_BLK (struct copy) with CORINFO_HELP_ASSIGN_STRUCT which performs
8223
+ // bulk copy for byrefs.
8224
+ const unsigned bulkCopyThreshold = 4 ;
8225
+ if (blk->OperIs (GT_STORE_BLK) && !blk->OperIsInitBlkOp () &&
8226
+ (blk->GetLayout ()->GetGCPtrCount () >= bulkCopyThreshold))
8227
+ {
8228
+ GenTree* addr = blk->Addr ();
8229
+ GenTree* data = blk->Data ();
8230
+
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
+ }
8251
+
8252
+ // Size is a constant
8253
+ GenTreeIntCon* size = comp->gtNewIconNode ((ssize_t )gcPtrs * TARGET_POINTER_SIZE, TYP_I_IMPL);
8254
+ BlockRange ().InsertBefore (data, size);
8255
+
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));
8260
+
8261
+ GenTreeCall* call = comp->gtNewHelperCallNode (CORINFO_HELP_ASSIGN_STRUCT, TYP_VOID, destPlaceholder, dataPlaceholder, sizePlaceholder);
8262
+ comp->fgMorphArgs (call);
8263
+
8264
+ LIR::Range range = LIR::SeqTree (comp, call);
8265
+ GenTree* rangeStart = range.FirstNode ();
8266
+ GenTree* rangeEnd = range.LastNode ();
8267
+
8268
+ BlockRange ().InsertBefore (blk, std::move (range));
8269
+ blk->gtBashToNOP ();
8270
+
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 ();
8279
+
8280
+ LIR::Use dataUse;
8281
+ BlockRange ().TryGetUse (dataPlaceholder, &dataUse);
8282
+ dataUse.ReplaceWith (data);
8283
+ dataPlaceholder->SetUnusedValue ();
8284
+
8285
+ LowerRange (rangeStart, rangeEnd);
8286
+
8287
+ // Finally move all GT_PUTARG_* nodes
8288
+ // Re-use the existing logic for CFG call args here
8289
+ MoveCFGCallArgs (call);
8290
+
8291
+ BlockRange ().Remove (destPlaceholder);
8292
+ BlockRange ().Remove (sizePlaceholder);
8293
+ BlockRange ().Remove (dataPlaceholder);
8294
+ return true ;
8295
+ }
8296
+ }
8297
+ return false ;
8298
+ }
8299
+
8215
8300
// ------------------------------------------------------------------------
8216
8301
// LowerBlockStoreAsHelperCall: Lower a block store node as a memset/memcpy call
8217
8302
//
0 commit comments