Skip to content

Commit

Permalink
JIT: Generalize last-use copy elision for implicit byrefs
Browse files Browse the repository at this point in the history
Allow omitting the copy if we have _any_ last use before the call, not
just if the candidate itself is dead. For example, for something like

M(s, s.A);

the first argument can now have its copy elided if the access in the
second argument is the last use.

Fix dotnet#80494
  • Loading branch information
jakobbotsch committed Jan 19, 2023
1 parent 4544bdd commit ef6380b
Showing 1 changed file with 79 additions and 0 deletions.
79 changes: 79 additions & 0 deletions src/coreclr/jit/morph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3981,6 +3981,85 @@ void Compiler::fgMakeOutgoingStructArgCopy(GenTreeCall* call, CallArg* arg)
if (!omitCopy && fgDidEarlyLiveness)
{
omitCopy = !varDsc->lvPromoted && ((lcl->gtFlags & GTF_VAR_DEATH) != 0);

if (!varDsc->lvPromoted && !omitCopy)
{
struct Visitor : GenTreeVisitor<Visitor>
{
enum
{
DoPreOrder = true,
UseExecutionOrder = true,
};

Visitor(Compiler* comp, GenTree* firstTree, unsigned lclNum)
: GenTreeVisitor(comp), m_firstTree(firstTree), m_lclNum(lclNum)
{
}

bool HasInterference = false;

fgWalkResult PreOrderVisit(GenTree** use, GenTree* user)
{
GenTree* node = *use;
if (node == m_firstTree)
{
m_started = true;
return WALK_SKIP_SUBTREES;
}

if (!m_started)
{
return WALK_CONTINUE;
}

if ((user != nullptr) && user->IsCall())
{
GenTreeLclVarCommon* lcl = node->IsImplicitByrefParameterValue(m_compiler);
if ((lcl == nullptr) && node->OperIsLocal())
{
lcl = node->AsLclVarCommon();
}

if ((lcl != nullptr) && (lcl->GetLclNum() == m_lclNum))
{
CallArg* userArg = user->AsCall()->gtArgs.FindByNode(node);
if ((userArg != nullptr) && userArg->AbiInfo.PassedByRef)
{
// Future candidate that will interfere.
HasInterference = true;
return WALK_ABORT;
}
}
}

if (node->OperIsLocal())
{
GenTreeLclVarCommon* lcl = node->AsLclVarCommon();
if ((lcl->GetLclNum() == m_lclNum) && ((lcl->gtFlags & GTF_VAR_DEATH) != 0))
{
// Variable dies before the call and there is no other candidate before it.
// This means we can still omit the copy.
return WALK_ABORT;
}
}

return WALK_CONTINUE;
}

private:
GenTree* m_firstTree;
unsigned m_lclNum;
bool m_started = false;
};

Visitor visitor(this, argx, varNum);
GenTree* callCopy = call;
if (visitor.WalkTree(&callCopy, nullptr) == WALK_ABORT)
{
omitCopy = !visitor.HasInterference;
}
}
}

if (!omitCopy && (totalAppearances == 1))
Expand Down

0 comments on commit ef6380b

Please sign in to comment.