Skip to content

Commit

Permalink
Extract LowerStoreIndirCommon, LowerIndir.
Browse files Browse the repository at this point in the history
Extract without changes, I will need to add additional calls to them later.
  • Loading branch information
Sergey Andreenko committed Jun 25, 2020
1 parent ef2709d commit eecb41a
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 55 deletions.
134 changes: 79 additions & 55 deletions src/coreclr/src/jit/lower.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,64 +113,11 @@ GenTree* Lowering::LowerNode(GenTree* node)
{
case GT_NULLCHECK:
case GT_IND:
// Process struct typed indirs separately unless they are unused;
// they only appear as the source of a block copy operation or a return node.
if (!node->TypeIs(TYP_STRUCT) || node->IsUnusedValue())
{
// TODO-Cleanup: We're passing isContainable = true but ContainCheckIndir rejects
// address containment in some cases so we end up creating trivial (reg + offfset)
// or (reg + reg) LEAs that are not necessary.
TryCreateAddrMode(node->AsIndir()->Addr(), true);
ContainCheckIndir(node->AsIndir());

if (node->OperIs(GT_NULLCHECK) || node->IsUnusedValue())
{
// A nullcheck is essentially the same as an indirection with no use.
// The difference lies in whether a target register must be allocated.
// On XARCH we can generate a compare with no target register as long as the addresss
// is not contained.
// On ARM64 we can generate a load to REG_ZR in all cases.
// However, on ARM we must always generate a load to a register.
// In the case where we require a target register, it is better to use GT_IND, since
// GT_NULLCHECK is a non-value node and would therefore require an internal register
// to use as the target. That is non-optimal because it will be modeled as conflicting
// with the source register(s).
// So, to summarize:
// - On ARM64, always use GT_NULLCHECK for a dead indirection.
// - On ARM, always use GT_IND.
// - On XARCH, use GT_IND if we have a contained address, and GT_NULLCHECK otherwise.
// In all cases, change the type to TYP_INT.
//
node->gtType = TYP_INT;
#ifdef TARGET_ARM64
bool useNullCheck = true;
#elif TARGET_ARM
bool useNullCheck = false;
#else // TARGET_XARCH
bool useNullCheck = !node->AsIndir()->Addr()->isContained();
#endif // !TARGET_XARCH

if (useNullCheck && node->OperIs(GT_IND))
{
node->ChangeOper(GT_NULLCHECK);
node->ClearUnusedValue();
}
else if (!useNullCheck && node->OperIs(GT_NULLCHECK))
{
node->ChangeOper(GT_IND);
node->SetUnusedValue();
}
}
}
LowerIndir(node->AsIndir());
break;

case GT_STOREIND:
assert(node->TypeGet() != TYP_STRUCT);
TryCreateAddrMode(node->AsIndir()->Addr(), true);
if (!comp->codeGen->gcInfo.gcIsWriteBarrierStoreIndNode(node))
{
LowerStoreIndir(node->AsIndir());
}
LowerStoreIndirCommon(node->AsIndir());
break;

case GT_ADD:
Expand Down Expand Up @@ -6365,3 +6312,80 @@ void Lowering::ContainCheckBitCast(GenTree* node)
op1->SetContained();
}
}

//------------------------------------------------------------------------
// LowerStoreIndirCommon: a common logic to lower StoreIndir.
//
// Arguments:
// ind - the store indirection node we are lowering.
//
void Lowering::LowerStoreIndirCommon(GenTreeIndir* ind)
{
assert(ind->OperIs(GT_STOREIND));
assert(ind->TypeGet() != TYP_STRUCT);
TryCreateAddrMode(ind->Addr(), true);
if (!comp->codeGen->gcInfo.gcIsWriteBarrierStoreIndNode(ind))
{
LowerStoreIndir(ind);
}
}

//------------------------------------------------------------------------
// LowerIndir: a common logic to lower IND load or NullCheck.
//
// Arguments:
// node - the ind node we are lowering.
//
void Lowering::LowerIndir(GenTreeIndir* ind)
{
assert(ind->OperIs(GT_IND, GT_NULLCHECK));
// Process struct typed indirs separately unless they are unused;
// they only appear as the source of a block copy operation or a return node.
if (!ind->TypeIs(TYP_STRUCT) || ind->IsUnusedValue())
{
// TODO-Cleanup: We're passing isContainable = true but ContainCheckIndir rejects
// address containment in some cases so we end up creating trivial (reg + offfset)
// or (reg + reg) LEAs that are not necessary.
TryCreateAddrMode(ind->Addr(), true);
ContainCheckIndir(ind);

if (ind->OperIs(GT_NULLCHECK) || ind->IsUnusedValue())
{
// A nullcheck is essentially the same as an indirection with no use.
// The difference lies in whether a target register must be allocated.
// On XARCH we can generate a compare with no target register as long as the addresss
// is not contained.
// On ARM64 we can generate a load to REG_ZR in all cases.
// However, on ARM we must always generate a load to a register.
// In the case where we require a target register, it is better to use GT_IND, since
// GT_NULLCHECK is a non-value node and would therefore require an internal register
// to use as the target. That is non-optimal because it will be modeled as conflicting
// with the source register(s).
// So, to summarize:
// - On ARM64, always use GT_NULLCHECK for a dead indirection.
// - On ARM, always use GT_IND.
// - On XARCH, use GT_IND if we have a contained address, and GT_NULLCHECK otherwise.
// In all cases, change the type to TYP_INT.
//
ind->gtType = TYP_INT;
#ifdef TARGET_ARM64
bool useNullCheck = true;
#elif TARGET_ARM
bool useNullCheck = false;
#else // TARGET_XARCH
bool useNullCheck = !ind->Addr()->isContained();
#endif // !TARGET_XARCH

if (useNullCheck && ind->OperIs(GT_IND))
{
ind->ChangeOper(GT_NULLCHECK);
ind->ClearUnusedValue();
}
else if (!useNullCheck && ind->OperIs(GT_NULLCHECK))
{
ind->ChangeOper(GT_IND);
ind->SetUnusedValue();
}
}
}
}
2 changes: 2 additions & 0 deletions src/coreclr/src/jit/lower.h
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,8 @@ class Lowering final : public Phase
#endif // defined(TARGET_XARCH)

// Per tree node member functions
void LowerStoreIndirCommon(GenTreeIndir* ind);
void LowerIndir(GenTreeIndir* ind);
void LowerStoreIndir(GenTreeIndir* node);
GenTree* LowerAdd(GenTreeOp* node);
bool LowerUnsignedDivOrMod(GenTreeOp* divMod);
Expand Down

0 comments on commit eecb41a

Please sign in to comment.