Skip to content

Commit

Permalink
JIT: enable tail calls and copy omission for implicit byref structs (#…
Browse files Browse the repository at this point in the history
…33004)

Handle cases where a caller passes an implicit byref argument struct to
a callee in tail position. There is no need to create a local copy or
to block tail calling.

To prove this is safe we must show that we're not introducing aliasing by
not copying the arguments. We do a simplistic and limited form of alias
analysis.

This pattern comes up increasingly often as we write more layered code
operating on spans and similar structs.
  • Loading branch information
AndyAyersMS committed Mar 11, 2020
1 parent aa3870d commit 514db28
Show file tree
Hide file tree
Showing 8 changed files with 939 additions and 61 deletions.
49 changes: 49 additions & 0 deletions src/coreclr/src/jit/gentree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16219,6 +16219,55 @@ bool GenTree::IsLocalAddrExpr(Compiler* comp, GenTreeLclVarCommon** pLclVarTree,
return false;
}

//------------------------------------------------------------------------
// IsImplicitByrefParameterValue: determine if this tree is the entire
// value of a local implicit byref parameter
//
// Arguments:
// compiler -- compiler instance
//
// Return Value:
// GenTreeLclVar node for the local, or nullptr.
//
GenTreeLclVar* GenTree::IsImplicitByrefParameterValue(Compiler* compiler)
{
#if defined(TARGET_AMD64) || defined(TARGET_ARM64)

GenTreeLclVar* lcl = nullptr;

if (OperIs(GT_LCL_VAR))
{
lcl = AsLclVar();
}
else if (OperIs(GT_OBJ))
{
GenTree* addr = AsIndir()->Addr();

if (addr->OperIs(GT_LCL_VAR))
{
lcl = addr->AsLclVar();
}
else if (addr->OperIs(GT_ADDR))
{
GenTree* base = addr->AsOp()->gtOp1;

if (base->OperIs(GT_LCL_VAR))
{
lcl = base->AsLclVar();
}
}
}

if ((lcl != nullptr) && compiler->lvaIsImplicitByRefLocal(lcl->GetLclNum()))
{
return lcl;
}

#endif // defined(TARGET_AMD64) || defined(TARGET_ARM64)

return nullptr;
}

//------------------------------------------------------------------------
// IsLclVarUpdateTree: Determine whether this is an assignment tree of the
// form Vn = Vn 'oper' 'otherTree' where Vn is a lclVar
Expand Down
4 changes: 4 additions & 0 deletions src/coreclr/src/jit/gentree.h
Original file line number Diff line number Diff line change
Expand Up @@ -1840,6 +1840,10 @@ struct GenTree
// yields an address into a local
GenTreeLclVarCommon* IsLocalAddrExpr();

// Determine if this tree represents the value of an entire implict byref parameter,
// and if so return the tree for the parameter.
GenTreeLclVar* IsImplicitByrefParameterValue(Compiler* compiler);

// Determine if this is a LclVarCommon node and return some additional info about it in the
// two out parameters.
bool IsLocalExpr(Compiler* comp, GenTreeLclVarCommon** pLclVarTree, FieldSeqNode** pFldSeq);
Expand Down
61 changes: 59 additions & 2 deletions src/coreclr/src/jit/lclmorph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1097,10 +1097,67 @@ class LocalAddressVisitor final : public GenTreeVisitor<LocalAddressVisitor>
{
return;
}

LclVarDsc* varDsc = m_compiler->lvaGetDesc(lclNum);
JITDUMP("LocalAddressVisitor incrementing ref count from %d to %d for V%02d\n", varDsc->lvRefCnt(RCS_EARLY),
varDsc->lvRefCnt(RCS_EARLY) + 1, lclNum);
JITDUMP("LocalAddressVisitor incrementing ref count from %d to %d for implict by-ref V%02d\n",
varDsc->lvRefCnt(RCS_EARLY), varDsc->lvRefCnt(RCS_EARLY) + 1, lclNum);
varDsc->incLvRefCnt(1, RCS_EARLY);

// See if this struct is an argument to a call. This information is recorded
// via the weighted early ref count for the local, and feeds the undo promotion
// heuristic.
//
// It can be approximate, so the pattern match below need not be exhaustive.
// But the pattern should at least subset the implicit byref cases that are
// handled in fgCanFastTailCall and fgMakeOutgoingStructArgCopy.
//
// CALL(OBJ(ADDR(LCL_VAR...)))
bool isArgToCall = false;
bool keepSearching = true;
for (int i = 0; i < m_ancestors.Height() && keepSearching; i++)
{
GenTree* node = m_ancestors.Top(i);
switch (i)
{
case 0:
{
keepSearching = node->OperIs(GT_LCL_VAR);
}
break;

case 1:
{
keepSearching = node->OperIs(GT_ADDR);
}
break;

case 2:
{
keepSearching = node->OperIs(GT_OBJ);
}
break;

case 3:
{
keepSearching = false;
isArgToCall = node->IsCall();
}
break;
default:
{
keepSearching = false;
}
break;
}
}

if (isArgToCall)
{
JITDUMP("LocalAddressVisitor incrementing weighted ref count from %d to %d"
" for implict by-ref V%02d arg passed to call\n",
varDsc->lvRefCntWtd(RCS_EARLY), varDsc->lvRefCntWtd(RCS_EARLY) + 1, lclNum);
varDsc->incLvRefCntWtd(1, RCS_EARLY);
}
}
};

Expand Down
Loading

0 comments on commit 514db28

Please sign in to comment.