Skip to content

Commit

Permalink
JIT: Allow forwarding field accesses off of implicit byrefs (dotnet#8…
Browse files Browse the repository at this point in the history
…0852)

The JIT currently allows forwarding implicit byrefs at their last uses
to calls, but only if the full implicit byref is used. This change
allows the JIT to forward any such access off of an implicit byref
parameter.
  • Loading branch information
jakobbotsch authored and mdh1418 committed Jan 24, 2023
1 parent f4426b9 commit 8737b22
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 24 deletions.
65 changes: 49 additions & 16 deletions src/coreclr/jit/gentree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16963,38 +16963,71 @@ const GenTreeLclVarCommon* GenTree::IsLocalAddrExpr() const
}

//------------------------------------------------------------------------
// IsImplicitByrefParameterValue: determine if this tree is the entire
// value of a local implicit byref parameter
// IsImplicitByrefParameterValuePreMorph:
// Determine if this tree represents the value of an implicit byref
// parameter, and if so return the tree for the parameter. To be used
// before implicit byrefs have been morphed.
//
// Arguments:
// compiler -- compiler instance
// compiler - compiler instance
//
// Return Value:
// GenTreeLclVar node for the local, or nullptr.
// Node for the local, or nullptr.
//
GenTreeLclVar* GenTree::IsImplicitByrefParameterValue(Compiler* compiler)
GenTreeLclVarCommon* GenTree::IsImplicitByrefParameterValuePreMorph(Compiler* compiler)
{
#if FEATURE_IMPLICIT_BYREFS && !defined(TARGET_LOONGARCH64) // TODO-LOONGARCH64-CQ: enable this.

GenTreeLclVar* lcl = nullptr;
GenTreeLclVarCommon* lcl = OperIsLocal() ? AsLclVarCommon() : nullptr;

if (OperIs(GT_LCL_VAR))
if ((lcl != nullptr) && compiler->lvaIsImplicitByRefLocal(lcl->GetLclNum()))
{
lcl = AsLclVar();
return lcl;
}
else if (OperIsIndir())

#endif // FEATURE_IMPLICIT_BYREFS && !defined(TARGET_LOONGARCH64)

return nullptr;
}

//------------------------------------------------------------------------
// IsImplicitByrefParameterValuePostMorph:
// Determine if this tree represents the value of an implicit byref
// parameter, and if so return the tree for the parameter. To be used after
// implicit byrefs have been morphed.
//
// Arguments:
// compiler - compiler instance
// addr - [out] tree representing the address computation on top of the implicit byref.
// Will be the same as the return value if the whole implicit byref is used, for example.
//
// Return Value:
// Node for the local, or nullptr.
//
GenTreeLclVar* GenTree::IsImplicitByrefParameterValuePostMorph(Compiler* compiler, GenTree** addr)
{
#if FEATURE_IMPLICIT_BYREFS && !defined(TARGET_LOONGARCH64) // TODO-LOONGARCH64-CQ: enable this.

if (!OperIsIndir())
{
GenTree* addr = AsIndir()->Addr();
return nullptr;
}

if (addr->OperIs(GT_LCL_VAR, GT_LCL_VAR_ADDR))
{
lcl = addr->AsLclVar();
}
*addr = AsIndir()->Addr();
GenTree* innerAddr = *addr;

while (innerAddr->OperIs(GT_ADD) && innerAddr->gtGetOp2()->IsCnsIntOrI())
{
innerAddr = innerAddr->gtGetOp1();
}

if ((lcl != nullptr) && compiler->lvaIsImplicitByRefLocal(lcl->GetLclNum()))
if (innerAddr->OperIs(GT_LCL_VAR))
{
return lcl;
GenTreeLclVar* lcl = innerAddr->AsLclVar();
if (compiler->lvaIsImplicitByRefLocal(lcl->GetLclNum()))
{
return lcl;
}
}

#endif // FEATURE_IMPLICIT_BYREFS && !defined(TARGET_LOONGARCH64)
Expand Down
5 changes: 2 additions & 3 deletions src/coreclr/jit/gentree.h
Original file line number Diff line number Diff line change
Expand Up @@ -1971,9 +1971,8 @@ struct GenTree
return const_cast<GenTreeLclVarCommon*>(static_cast<const GenTree*>(this)->IsLocalAddrExpr());
}

// Determine if this tree represents the value of an entire implicit byref parameter,
// and if so return the tree for the parameter.
GenTreeLclVar* IsImplicitByrefParameterValue(Compiler* compiler);
GenTreeLclVarCommon* IsImplicitByrefParameterValuePreMorph(Compiler* compiler);
GenTreeLclVar* IsImplicitByrefParameterValuePostMorph(Compiler* compiler, GenTree** addr);

// Determine whether this is an assignment tree of the form X = X (op) Y,
// where Y is an arbitrary tree, and X is a lclVar.
Expand Down
12 changes: 7 additions & 5 deletions src/coreclr/jit/morph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3947,7 +3947,9 @@ void Compiler::fgMakeOutgoingStructArgCopy(GenTreeCall* call, CallArg* arg)
//
if (opts.OptimizationEnabled() && arg->AbiInfo.PassedByRef)
{
GenTreeLclVarCommon* implicitByRefLcl = argx->IsImplicitByrefParameterValue(this);
GenTree* implicitByRefLclAddr;
GenTreeLclVarCommon* implicitByRefLcl =
argx->IsImplicitByrefParameterValuePostMorph(this, &implicitByRefLclAddr);

GenTreeLclVarCommon* lcl = implicitByRefLcl;
if ((lcl == nullptr) && argx->OperIsLocal())
Expand Down Expand Up @@ -3993,7 +3995,7 @@ void Compiler::fgMakeOutgoingStructArgCopy(GenTreeCall* call, CallArg* arg)
{
if (implicitByRefLcl != nullptr)
{
arg->SetEarlyNode(lcl);
arg->SetEarlyNode(implicitByRefLclAddr);
}
else
{
Expand Down Expand Up @@ -5920,8 +5922,8 @@ bool Compiler::fgCallHasMustCopyByrefParameter(GenTreeCall* callee)
// and so still be able to avoid a struct copy.
if (opts.OptimizationEnabled())
{
// First, see if this arg is an implicit byref param.
GenTreeLclVar* const lcl = arg.GetNode()->IsImplicitByrefParameterValue(this);
// First, see if this is an arg off of an implicit byref param.
GenTreeLclVarCommon* const lcl = arg.GetNode()->IsImplicitByrefParameterValuePreMorph(this);

if (lcl != nullptr)
{
Expand Down Expand Up @@ -5993,7 +5995,7 @@ bool Compiler::fgCallHasMustCopyByrefParameter(GenTreeCall* callee)
if (arg2.AbiInfo.IsStruct && arg2.AbiInfo.PassedByRef)
{
GenTreeLclVarCommon* const lcl2 =
arg2.GetNode()->IsImplicitByrefParameterValue(this);
arg2.GetNode()->IsImplicitByrefParameterValuePreMorph(this);

if ((lcl2 != nullptr) && (lclNum == lcl2->GetLclNum()))
{
Expand Down

0 comments on commit 8737b22

Please sign in to comment.