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

[NativeAOT] macOS/iOS: Emit simple compact unwinding information #88724

Merged
merged 4 commits into from
Aug 3, 2023

Conversation

filipnavara
Copy link
Member

Apple platforms can represent certain unwinding sequences more efficiently using a single 32-bit "compact unwinding" code instead of a DWARF EH frame. This format cannot accurately unwind prologs and epilogs. However, we already handle epilogs manually since JIT doesn't generate the DWARF code for it, and prologs could be handled in a similar manner.

This is minimum viable prototype that implements the following:

  1. Emits a single specific ARM64 unwind code for an empty frame prolog. Any other prolog would need changes in JIT to produce compatible prologs (eg. point frame pointer to the top of the frame, not bottom, store registers in correct order, always store registers in pairs).
  2. Emit a prolog length into LSDA block if DWARF information is omitted. This allows the runtime to quickly identify if we are in the prolog or not.

This empty frame prolog accounts for about 30% of the DWARF code / methods in a typical executable, so it has significant impact even without handing the more complex cases.

Contributes to #76371, #88292

@ghost ghost added the community-contribution Indicates that the PR has been added by a community member label Jul 12, 2023
@filipnavara filipnavara requested a review from VSadov July 12, 2023 10:19
@ghost
Copy link

ghost commented Jul 12, 2023

Tagging subscribers to this area: @agocke, @MichalStrehovsky, @jkotas
See info in area-owners.md if you want to be subscribed.

Issue Details

Apple platforms can represent certain unwinding sequences more efficiently using a single 32-bit "compact unwinding" code instead of a DWARF EH frame. This format cannot accurately unwind prologs and epilogs. However, we already handle epilogs manually since JIT doesn't generate the DWARF code for it, and prologs could be handled in a similar manner.

This is minimum viable prototype that implements the following:

  1. Emits a single specific ARM64 unwind code for an empty frame prolog. Any other prolog would need changes in JIT to produce compatible prologs (eg. point frame pointer to the top of the frame, not bottom, store registers in correct order, always store registers in pairs).
  2. Emit a prolog length into LSDA block if DWARF information is omitted. This allows the runtime to quickly identify if we are in the prolog or not.

This empty frame prolog accounts for about 30% of the DWARF code / methods in a typical executable, so it has significant impact even without handing the more complex cases.

Contributes to #76371, #88292

Author: filipnavara
Assignees: -
Labels:

area-NativeAOT-coreclr

Milestone: -

@jkotas
Copy link
Member

jkotas commented Jul 18, 2023

The general idea looks good to me. The size savings are very nice.

The only wrinkle is encoding of the prolog sizes. It looks odd that the prolog sizes are encoded explicitly, and the epilog sizes are inferred from the code. What would it take to infer the prolog size from the compact unwind info or by inspecting the code?

@jkotas
Copy link
Member

jkotas commented Jul 18, 2023

What would it take to infer the prolog size from the compact unwind info or by inspecting the code?

It should be only needed during GetReturnAddressHijackInfo. It should not be needed during regular unwind. We should never call general unwind in prolog or epilog.

@filipnavara
Copy link
Member Author

I originally inferred the prolog size from the instructions but it feels a bit fragile once you start doing complex prologs (not included in the PR). The compact unwind code by itself is not sufficient. It describes the stack layout but not which instructions were used to produce it. Due to various ways how ARM64 code can fold pointer adjustments into single instruction it would be fragile for the general case (again, not issue for this single unwind code that's produced in this PR).

If you feel strongly about inferring the prolog size from code I will do it for this simple case and we can extend it later to cover the hard cases.

@jkotas
Copy link
Member

jkotas commented Jul 18, 2023

We took on this fragility in epilogs, so I think it would be fine to take on it in prologs too and save a bit more size.

@filipnavara
Copy link
Member Author

We took on this fragility in epilogs, so I think it would be fine to take on it in prologs too and save a bit more size.

Done. Let's see if the CI passes... (but it seemed to work locally and crossgen2 compiled the framework just fine)

@dotnet dotnet deleted a comment from azure-pipelines bot Aug 2, 2023
@dotnet dotnet deleted a comment from azure-pipelines bot Aug 2, 2023
@jkotas
Copy link
Member

jkotas commented Aug 2, 2023

/azp run runtime-extra-platforms

@azure-pipelines
Copy link

No commit pushedDate could be found for PR 88724 in repo dotnet/runtime

@jkotas
Copy link
Member

jkotas commented Aug 2, 2023

/azp run runtime-extra-platforms

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

Copy link
Member

@jkotas jkotas left a comment

Choose a reason for hiding this comment

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

LGTM. @VSadov Could you please take a look as well?

@VSadov
Copy link
Member

VSadov commented Aug 2, 2023

@VSadov Could you please take a look as well?

I will take a look.

Copy link
Member

@VSadov VSadov left a comment

Choose a reason for hiding this comment

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

LGTM. (just a couple nits). Thanks!!!

@VSadov
Copy link
Member

VSadov commented Aug 3, 2023

Thanks!

@VSadov VSadov merged commit 5bf45ce into dotnet:main Aug 3, 2023
106 checks passed
@filipnavara filipnavara deleted the compact-unwind branch August 3, 2023 15:01
@jkotas
Copy link
Member

jkotas commented Aug 4, 2023

This is causing a lot of intermittent crashes on Linux arm64 . For example: https://helixre8s23ayyeko0k025g8.blob.core.windows.net/dotnet-runtime-refs-pull-89969-merge-fdbcd47e91a0464083/System.Collections.Concurrent.Tests/1/console.07e57ce1.log?helixlogtype=result

In checked builds, the crash is typically assertion failure in UnixNativeCodeManager::GetReturnAddressHijackInfo:

* thread #1, name = 'Microsoft.Exten', stop reason = signal SIGABRT
  * frame #0: 0x0000ff5c2f85deac
    frame #1: 0x0000ff5c2f84aaa0
    frame #2: 0x0000aad8f9112abc Microsoft.Extensions.Configuration.EnvironmentVariables.Tests`Assert(expr=<unavailable>, file=<unavailable>, line_num=<unavailable>, message=<unavailable>)
    frame #3: 0x0000aad8f9170dd0 Microsoft.Extensions.Configuration.EnvironmentVariables.Tests`UnixNativeCodeManager::GetReturnAddressHijackInfo(this=0x0000000000000001, pMethodInfo=0x0000ff5c2f99ac00, pRegisterSet=0x0000ff1b91ffb0d0, ppvRetAddrLocation=0x0000ff5c2f99a000, pRetValueKind=0x0000ff1b91ff78bc) at P:810:5
    frame #4: 0x0000aad8f911dfb0 Microsoft.Extensions.Configuration.EnvironmentVariables.Tests`Thread::HijackReturnAddressWorker(this=0x0000ff1b91ffb7f8, frameIterator=0x0000ff1b91ff7918, pfnHijackFunction=(Microsoft.Extensions.Configuration.EnvironmentVariables.Tests`RhpGcProbeHijack))())
    frame #5: 0x0000aad8f911dbdc Microsoft.Extensions.Configuration.EnvironmentVariables.Tests`Thread::HijackCallback(UNIX_CONTEXT*, void*) [inlined] Thread::HijackReturnAddress(this=0x0000ff1b91ffb7f8, pSuspendCtx=0x0000ff1b91ff7ca0)())
    frame #6: 0x0000aad8f911db6c Microsoft.Extensions.Configuration.EnvironmentVariables.Tests`Thread::HijackCallback(pThreadContext=<unavailable>, pThreadToHijack=<unavailable>)
    frame #7: 0x0000aad8f9167810 Microsoft.Extensions.Configuration.EnvironmentVariables.Tests`ActivationHandler(code=34, siginfo=0x0000ff1b91ff7c20, context=0x0000ff1b91ff7ca0)
    frame #8: 0x0000ff5c2fabe5b0 linux-vdso.so.1
    frame #9: 0x0000aad8f9508434 Microsoft.Extensions.Configuration.EnvironmentVariables.Tests`S_P_CoreLib_System_Collections_Generic_List_1<System___Canon>__Sort_2(this=<unavailable>, comparison=<unavailable>) at List.cs:1091
    frame #10: 0x0000aad8f943c130 Microsoft.Extensions.Configuration.EnvironmentVariables.Tests`Microsoft_Extensions_Configuration_Microsoft_Extensions_Configuration_ConfigurationProvider__GetChildKeys(this=<unavailable>, earlierKeys=<unavailable>, parentPath=<unavailable>) at ConfigurationProvider.cs:91

@filipnavara
Copy link
Member Author

This is causing a lot of intermittent crashes on Linux arm64 .

Thanks for alert, I will check. I must have messed up some condition because it should have had no effect on Linux.

@filipnavara
Copy link
Member Author

I see the mistake now, will submit a fix shortly...

@ghost ghost locked as resolved and limited conversation to collaborators Sep 3, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-NativeAOT-coreclr community-contribution Indicates that the PR has been added by a community member
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants