From 8737b2208574a324bb1c35dbb7bd7dd318ea5098 Mon Sep 17 00:00:00 2001 From: Jakob Botsch Nielsen Date: Mon, 23 Jan 2023 17:43:26 +0100 Subject: [PATCH] JIT: Allow forwarding field accesses off of implicit byrefs (#80852) 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. --- src/coreclr/jit/gentree.cpp | 65 ++++++++++++++++++++++++++++--------- src/coreclr/jit/gentree.h | 5 ++- src/coreclr/jit/morph.cpp | 12 ++++--- 3 files changed, 58 insertions(+), 24 deletions(-) diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 7520269674ef18..9abd4477eac0d9 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -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) diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index 222a93848e2c67..7b6463b46fbd46 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -1971,9 +1971,8 @@ struct GenTree return const_cast(static_cast(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. diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index dfc4ab03fd2b3d..21d87671a41438 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -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()) @@ -3993,7 +3995,7 @@ void Compiler::fgMakeOutgoingStructArgCopy(GenTreeCall* call, CallArg* arg) { if (implicitByRefLcl != nullptr) { - arg->SetEarlyNode(lcl); + arg->SetEarlyNode(implicitByRefLclAddr); } else { @@ -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) { @@ -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())) {