Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow enregistering more structs args #39326

Merged
merged 5 commits into from
Oct 7, 2020

Conversation

CarolEidt
Copy link
Contributor

Allow HFAs and other structs with matching fields and registers.

Fix #37924

@Dotnet-GitSync-Bot Dotnet-GitSync-Bot added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Jul 14, 2020
@CarolEidt
Copy link
Contributor Author

This results in improvements primarily for Arm targets. Here are the crossgen diffs for frameworks & benchmarks (using altjits for x64UX, Arm and Arm64):

Target Smaller Larger Size Diff Faster Slower PerfScore Diff
X64Ux 4 0 -300 (-0.000%) 4 0 -91.10 (-0.000%)
Arm64 2409 17 -45880 (-0.044%) 2426 11 -18290.00 (-0.002%)
Arm32 300 6 -9848 (-0.014%) 301 6 -3762.80 (-0.000%)

The regressions result primarily from register allocation issues (we don't prefer callee-save for floating point registers, so promoting a floating point field is not always profitable if it's live across a call).

@CarolEidt
Copy link
Contributor Author

@dotnet/jit-contrib PTAL
The first commit is #39284, which is still open (waiting for some test re-runs).

sandreenko
sandreenko previously approved these changes Jul 15, 2020
Copy link
Contributor

@sandreenko sandreenko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like a risky change, but I understand that it is better to get it into the Prev rather that an RC.
I have a few small question and hope you could run stress tests before the merge.
LGTM in case I don't wake up before the snap.

{
canPromote = false;
}
#ifdef UNIX_AMD64_ABI
#if defined(FEATURE_SIMD) && defined(TARGET_ARMARCH)
// Don't promote a struct with a struct field unless it is the only field or an opaque SIMD type.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: This is a perfect logical expression, but took me some time to check all cases. Maybe it could be rephrased a little bit? Maybe "A struct field that is not an opaque SIMD type blocks promotion if it it is not the only field." or delete the comment here and put it near !compiler->isOpaqueSIMDType(structPromotionInfo.fields[i].fldTypeHnd) saying that `opaque SIMD fields can be promoted'.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment was reworded, hopefully it's more clear now.

if (varTypeIsStruct(structPromotionInfo.fields[i].fldType) &&
!compiler->isOpaqueSIMDType(structPromotionInfo.fields[i].fldTypeHnd))
{
canPromote = false;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does not it create aljit issues similar to #38980?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It may, though I haven't run into them. That, however, is an altjit-specific issue so I don't think it should hold up this PR.

src/coreclr/src/jit/lclvars.cpp Show resolved Hide resolved
//
// Arguments:
// lclNode - the GT_LCL_VAR node.
// varDsc - the local variable
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// varDsc - the local variable
// varDsc - the local variable descriptor?

else if (varDsc->lvIsStructField)
{
noway_assert(varDsc->lvParentLcl < lvaCount);
lvaTable[varDsc->lvParentLcl].lvStkOffs = varDsc->lvStkOffs;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we leave it set to BAD_STK_OFFS after this change?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, this was duplicative; the field offsets are set in the promoted struct case, and don't need to be set again when they are encountered.

@@ -17401,6 +17405,9 @@ void Compiler::fgPromoteStructs()
lvaStructPromotionInfo structPromotionInfo;
bool tooManyLocalsReported = false;

// Clear the structPromotionHelper, since it is conservative about looking up SIMD info.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I am not following it. Could you please explain? structPromotionHelper has structPromotionInfo that is a private temporary buffer that we use to keep information about the last analyzed type. We access it only in TryPromoteStructVar and nowhere else.
Where could we access structPromotionHelper->structPromotionInfo.typeHnd that is cleared here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The cache is used by the inliner, during which it is conservative about looking up SIMD info. I clarified the comment a bit.

{
int nextArgNum = argNum + i;
regNumber nextRegNum =
genMapRegArgNumToRegNum(nextArgNum, regArgTab[nextArgNum].getRegType(compiler));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: extract regArgTab[nextArgNum] to avoid the line break and use it in the next line as well.

@CarolEidt
Copy link
Contributor Author

@dotnet/jit-contrib PTAL
There are two new commits:

  • 5592adf which fixes a failure on x64 Linux.
  • a05526c which addresses a previous PR feedback.

Prior to the latest push, I ran jitStressRegs, and there were some test build failures. There were no actual test failures, but having rebased, once the normal CI tests complete I'll run jitStressRegs again and hopefully the test build will succeed.

@CarolEidt
Copy link
Contributor Author

ping @dotnet/jit-contrib
This has now passed both the regular CI and jitstressregs

@AndyAyersMS
Copy link
Member

AndyAyersMS commented Oct 1, 2020

Does this fix the lack of enregistration/promotion in InsertionSort seen over on #41741 (Span passed by-value)?

It would show up in PMI diffs, I think (for arm64).

Copy link
Contributor

@sandreenko sandreenko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@@ -17503,6 +17511,11 @@ void Compiler::fgPromoteStructs()
lvaStructPromotionInfo structPromotionInfo;
bool tooManyLocalsReported = false;

// Clear the structPromotionHelper, since it is used during inlining, at which point it
// may be conservative about looking up SIMD info.
// We don't want to preserve those conservative decisions for the actual struct promotion.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the explanation and the comment, I would probably rephrase it as: since it is used during importation for inlining decisions.

the place that uses it before we call struct promotion is:

// Note if the callee's class is a promotable struct
if ((info.compClassAttr & CORINFO_FLG_VALUECLASS) != 0)
{
assert(structPromotionHelper != nullptr);
if (structPromotionHelper->CanPromoteStructType(info.compClassHnd))
{
inlineResult->Note(InlineObservation::CALLEE_CLASS_PROMOTABLE);
}

@CarolEidt
Copy link
Contributor Author

@AndyAyersMS - this doesn't fix the InsertionSort case. For some reason the parameter is still marked do-not-enreg[SF] ld-addr-op; I haven't analyzed it to determine why.

Here are the diffs:

Arch OS What Delta Methods Improved Methods Regressed
Arm64 Windows Crossgen fx+tests -6676 (-0.01%) 307 13
x64 Linux Crossgen fx+tests -1781 (-0.00%) 57 0
Arm64 Windows PMI fx+benchmarks -9952 (-0.02%) 447 4
x64 Linux PMI fx+benchmarks -3837 (-0.01%) 136 20

I'll have a look at InsertionSort before merging.

@CarolEidt
Copy link
Contributor Author

It turns out that these changes only support enregistering HFAs for Arm64; other multi-reg structs aren't promoted. I have a change that handles them, but will submit a separate PR for that.

@CarolEidt CarolEidt merged commit 65a27aa into dotnet:master Oct 7, 2020
@CarolEidt CarolEidt deleted the EnregStructArgs branch October 7, 2020 18:35
@ghost ghost locked as resolved and limited conversation to collaborators Dec 8, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Enable promotion of structs passed in multiple registers with matching fields
4 participants