if (blkOp->OperIs(GT_STORE_OBJ))
{
+ assert(!blkOp->gtBlkOpGcUnsafe);
assert(blkOp->OperIsCopyBlkOp());
assert(blkOp->AsObj()->GetLayout()->HasGCPtr());
genCodeForCpObj(blkOp->AsObj());
return;
}
- if (blkOp->gtBlkOpGcUnsafe)
- {
- GetEmitter()->emitDisableGC();
- }
bool isCopyBlk = blkOp->OperIsCopyBlkOp();
switch (blkOp->gtBlkOpKind)
{
case GenTreeBlk::BlkOpKindHelper:
+ assert(!blkOp->gtBlkOpGcUnsafe);
if (isCopyBlk)
{
genCodeForCpBlkHelper(blkOp);
case GenTreeBlk::BlkOpKindUnroll:
if (isCopyBlk)
{
+ if (blkOp->gtBlkOpGcUnsafe)
+ {
+ GetEmitter()->emitDisableGC();
+ }
genCodeForCpBlkUnroll(blkOp);
+ if (blkOp->gtBlkOpGcUnsafe)
+ {
+ GetEmitter()->emitEnableGC();
+ }
}
else
{
+ assert(!blkOp->gtBlkOpGcUnsafe);
genCodeForInitBlkUnroll(blkOp);
}
break;
default:
unreached();
}
-
- if (blkOp->gtBlkOpGcUnsafe)
- {
- GetEmitter()->emitEnableGC();
- }
}
//------------------------------------------------------------------------
if (storeBlkNode->OperIs(GT_STORE_OBJ))
{
+#ifndef JIT32_GCENCODER
assert(!storeBlkNode->gtBlkOpGcUnsafe);
+#endif
assert(storeBlkNode->OperIsCopyBlkOp());
assert(storeBlkNode->AsObj()->GetLayout()->HasGCPtr());
genCodeForCpObj(storeBlkNode->AsObj());
return;
}
-#ifdef JIT32_GCENCODER
- assert(!storeBlkNode->gtBlkOpGcUnsafe);
-#else
- if (storeBlkNode->gtBlkOpGcUnsafe)
- {
- GetEmitter()->emitDisableGC();
- }
-#endif // JIT32_GCENCODER
-
bool isCopyBlk = storeBlkNode->OperIsCopyBlkOp();
switch (storeBlkNode->gtBlkOpKind)
{
#ifdef _TARGET_AMD64_
case GenTreeBlk::BlkOpKindHelper:
+ assert(!storeBlkNode->gtBlkOpGcUnsafe);
if (isCopyBlk)
{
genCodeForCpBlkHelper(storeBlkNode);
break;
#endif // _TARGET_AMD64_
case GenTreeBlk::BlkOpKindRepInstr:
+#ifndef JIT32_GCENCODER
+ assert(!storeBlkNode->gtBlkOpGcUnsafe);
+#endif
if (isCopyBlk)
{
genCodeForCpBlkRepMovs(storeBlkNode);
case GenTreeBlk::BlkOpKindUnroll:
if (isCopyBlk)
{
+#ifndef JIT32_GCENCODER
+ if (storeBlkNode->gtBlkOpGcUnsafe)
+ {
+ GetEmitter()->emitDisableGC();
+ }
+#endif
genCodeForCpBlkUnroll(storeBlkNode);
+#ifndef JIT32_GCENCODER
+ if (storeBlkNode->gtBlkOpGcUnsafe)
+ {
+ GetEmitter()->emitEnableGC();
+ }
+#endif
}
else
{
+#ifndef JIT32_GCENCODER
+ assert(!storeBlkNode->gtBlkOpGcUnsafe);
+#endif
genCodeForInitBlkUnroll(storeBlkNode);
}
break;
default:
unreached();
}
-
-#ifndef JIT32_GCENCODER
- if (storeBlkNode->gtBlkOpGcUnsafe)
- {
- GetEmitter()->emitEnableGC();
- }
-#endif // !defined(JIT32_GCENCODER)
}
//
// The VM doesn't allow such large array elements but let's be sure.
noway_assert(scale <= INT32_MAX);
#else // !_TARGET_64BIT_
- tmpReg = node->GetSingleTempReg();
+ tmpReg = node->GetSingleTempReg();
#endif // !_TARGET_64BIT_
GetEmitter()->emitIns_R_I(emitter::inst3opImulForReg(tmpReg), EA_PTRSIZE, indexReg,
GenTree* fgMorphGetStructAddr(GenTree** pTree, CORINFO_CLASS_HANDLE clsHnd, bool isRValue = false);
GenTree* fgMorphBlkNode(GenTree* tree, bool isDest);
GenTree* fgMorphBlockOperand(GenTree* tree, var_types asgType, unsigned blockWidth, bool isDest);
- void fgMorphUnsafeBlk(GenTreeObj* obj);
GenTree* fgMorphCopyBlock(GenTree* tree);
GenTree* fgMorphForRegisterFP(GenTree* tree);
GenTree* fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac = nullptr);
case GT_OBJ:
copy =
new (this, GT_OBJ) GenTreeObj(tree->TypeGet(), tree->AsObj()->Addr(), tree->AsObj()->GetLayout());
- copy->gtBlk.gtBlkOpGcUnsafe = tree->gtBlk.gtBlkOpGcUnsafe;
break;
case GT_BLK:
copy = new (this, GT_BLK)
GenTreeBlk(GT_BLK, tree->TypeGet(), tree->AsBlk()->Addr(), tree->AsBlk()->GetLayout());
- copy->gtBlk.gtBlkOpGcUnsafe = tree->gtBlk.gtBlkOpGcUnsafe;
break;
case GT_DYN_BLK:
- copy = new (this, GT_DYN_BLK) GenTreeDynBlk(tree->AsOp()->gtOp1, tree->gtDynBlk.gtDynamicSize);
- copy->gtBlk.gtBlkOpGcUnsafe = tree->gtBlk.gtBlkOpGcUnsafe;
+ copy = new (this, GT_DYN_BLK) GenTreeDynBlk(tree->AsOp()->gtGetOp1(), tree->AsDynBlk()->gtDynamicSize);
break;
case GT_BOX:
BlkOpKindUnroll,
} gtBlkOpKind;
+#ifndef JIT32_GCENCODER
bool gtBlkOpGcUnsafe;
+#endif
GenTreeBlk(genTreeOps oper, var_types type, GenTree* addr, ClassLayout* layout)
: GenTreeIndir(oper, type, addr, nullptr)
, m_layout(layout)
, gtBlkOpKind(BlkOpKindInvalid)
+#ifndef JIT32_GCENCODER
, gtBlkOpGcUnsafe(false)
+#endif
{
assert(OperIsBlk(oper));
assert((layout != nullptr) || OperIs(GT_DYN_BLK, GT_STORE_DYN_BLK));
}
GenTreeBlk(genTreeOps oper, var_types type, GenTree* addr, GenTree* data, ClassLayout* layout)
- : GenTreeIndir(oper, type, addr, data), m_layout(layout), gtBlkOpKind(BlkOpKindInvalid), gtBlkOpGcUnsafe(false)
+ : GenTreeIndir(oper, type, addr, data)
+ , m_layout(layout)
+ , gtBlkOpKind(BlkOpKindInvalid)
+#ifndef JIT32_GCENCODER
+ , gtBlkOpGcUnsafe(false)
+#endif
{
assert(OperIsBlk(oper));
assert((layout != nullptr) || OperIs(GT_DYN_BLK, GT_STORE_DYN_BLK));
src->AsIndir()->Addr()->ClearContained();
}
- if (blkNode->OperIs(GT_STORE_OBJ) && (!blkNode->AsObj()->GetLayout()->HasGCPtr() || blkNode->gtBlkOpGcUnsafe))
+ if (blkNode->OperIs(GT_STORE_OBJ))
{
- blkNode->SetOper(GT_STORE_BLK);
+ if (!blkNode->AsObj()->GetLayout()->HasGCPtr())
+ {
+ blkNode->SetOper(GT_STORE_BLK);
+ }
+ else if (dstAddr->OperIsLocalAddr() && (size <= CPBLK_UNROLL_LIMIT))
+ {
+ // If the size is small enough to unroll then we need to mark the block as non-interruptible
+ // to actually allow unrolling. The generated code does not report GC references loaded in the
+ // temporary register(s) used for copying.
+ blkNode->SetOper(GT_STORE_BLK);
+ blkNode->gtBlkOpGcUnsafe = true;
+ }
}
if (blkNode->OperIs(GT_STORE_OBJ))
src->AsIndir()->Addr()->ClearContained();
}
- if (blkNode->OperIs(GT_STORE_OBJ) && (!blkNode->AsObj()->GetLayout()->HasGCPtr() || blkNode->gtBlkOpGcUnsafe))
+ if (blkNode->OperIs(GT_STORE_OBJ))
{
- blkNode->SetOper(GT_STORE_BLK);
+ if (!blkNode->AsObj()->GetLayout()->HasGCPtr())
+ {
+ blkNode->SetOper(GT_STORE_BLK);
+ }
+#ifndef JIT32_GCENCODER
+ else if (dstAddr->OperIsLocalAddr() && (size <= CPBLK_UNROLL_LIMIT))
+ {
+ // If the size is small enough to unroll then we need to mark the block as non-interruptible
+ // to actually allow unrolling. The generated code does not report GC references loaded in the
+ // temporary register(s) used for copying.
+ // This is not supported for the JIT32_GCENCODER.
+ blkNode->SetOper(GT_STORE_BLK);
+ blkNode->gtBlkOpGcUnsafe = true;
+ }
+#endif
}
if (blkNode->OperIs(GT_STORE_OBJ))
return tree;
}
-//------------------------------------------------------------------------
-// fgMorphUnsafeBlk: Convert a CopyObj with a dest on the stack to a GC Unsafe CopyBlk
-//
-// Arguments:
-// dest - the GT_OBJ or GT_STORE_OBJ
-//
-// Assumptions:
-// The destination must be known (by the caller) to be on the stack.
-//
-// Notes:
-// If we have a CopyObj with a dest on the stack, and its size is small enough
-// to be completely unrolled (i.e. between [16..64] bytes), we will convert it into a
-// GC Unsafe CopyBlk that is non-interruptible.
-// This is not supported for the JIT32_GCENCODER, in which case this method is a no-op.
-//
-void Compiler::fgMorphUnsafeBlk(GenTreeObj* dest)
-{
-#if defined(CPBLK_UNROLL_LIMIT) && !defined(JIT32_GCENCODER)
- assert(dest->GetLayout()->HasGCPtr());
- unsigned blockWidth = dest->GetLayout()->GetSize();
-#ifdef DEBUG
- bool destOnStack = false;
- GenTree* destAddr = dest->Addr();
- assert(destAddr->IsLocalAddrExpr() != nullptr);
-#endif
- if ((blockWidth >= (2 * TARGET_POINTER_SIZE)) && (blockWidth <= CPBLK_UNROLL_LIMIT))
- {
- genTreeOps newOper = (dest->gtOper == GT_OBJ) ? GT_BLK : GT_STORE_BLK;
- dest->SetOper(newOper);
- dest->AsBlk()->gtBlkOpGcUnsafe = true; // Mark as a GC unsafe copy block
- }
-#endif // defined(CPBLK_UNROLL_LIMIT) && !defined(JIT32_GCENCODER)
-}
-
//------------------------------------------------------------------------
// fgMorphCopyBlock: Perform the Morphing of block copy
//
}
#endif // _TARGET_ARM_
- if (dest->OperGet() == GT_OBJ && dest->AsBlk()->gtBlkOpGcUnsafe)
- {
- requiresCopyBlock = true;
- }
-
// Can't use field by field assignment if the src is a call.
if (rhs->OperGet() == GT_CALL)
{
asg->AsOp()->gtOp1 = dest;
asg->gtFlags |= (dest->gtFlags & GTF_ALL_EFFECT);
- // Note that the unrolling of CopyBlk is only implemented on some platforms.
- // Currently that includes x64 and ARM but not x86: the code generation for this
- // construct requires the ability to mark certain regions of the generated code
- // as non-interruptible, and the GC encoding for the latter platform does not
- // have this capability.
-
- // If we have a CopyObj with a dest on the stack
- // we will convert it into an GC Unsafe CopyBlk that is non-interruptible
- // when its size is small enough to be completely unrolled (i.e. between [16..64] bytes).
- // (This is not supported for the JIT32_GCENCODER, for which fgMorphUnsafeBlk is a no-op.)
- //
- if (destOnStack && (dest->OperGet() == GT_OBJ))
- {
- fgMorphUnsafeBlk(dest->AsObj());
- }
-
// Eliminate the "OBJ or BLK" node on the rhs.
rhs = fgMorphBlockOperand(rhs, asgType, blockWidth, false /*!isBlkReqd*/);
asg->AsOp()->gtOp2 = rhs;
GenTreeObj* objNode = comp->gtNewObjNode(structHnd, location)->AsObj();
objNode->ChangeOper(GT_STORE_OBJ);
objNode->SetData(value);
- comp->fgMorphUnsafeBlk(objNode);
storeBlk = objNode;
}
else