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

Do not setup both an inlined call frame and perform a reverse pinvoke… #55092

Merged
merged 1 commit into from
Jul 8, 2021

Conversation

davidwrighton
Copy link
Member

… in the same function

  • Pushing/popping the frame chain requires that the thread be in cooperative mode
  • Before the reverse pinvoke logic engages, the thread is in preemptive mode
  • The current implementation of InlinedCallFrame and reverse p/invoke sets up the InlinedCallFrame before the reverse p/invoke transition, which is unsafe
  • A reverse pinvoke function directly calling back into native code is fairly rare, and so optimizing the situation is of marginal utility.
  • The fix is to use the pinvoke helpers logic to setup the pinvoke instead of relying on the InlinedCallFrame logic. This avoid the problem of incorrect ordering in the prolog/epilog, but moving inlined call frame handling to point of use.

Fix bug #45326

… in the same function

- Pushing/popping the frame chain requires that the thread be in cooperative mode
- Before the reverse pinvoke logic engages, the thread is in preemptive mode
- The current implementation of InlinedCallFrame and reverse p/invoke sets up the InlinedCallFrame before the reverse p/invoke transition, which is unsafe
- A reverse pinvoke function directly calling back into native code is fairly rare, and so optimizing the situation is of marginal utility.
- The fix is to use the pinvoke helpers logic to setup the pinvoke instead of relying on the InlinedCallFrame logic. This avoid the problem of incorrect ordering in the prolog/epilog, but moving inlined call frame handling to point of use.

Fix bug dotnet#45326
@davidwrighton davidwrighton requested a review from jkotas July 2, 2021 18:05
@dotnet-issue-labeler dotnet-issue-labeler bot added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Jul 2, 2021
@davidwrighton
Copy link
Member Author

@dotnet/jit-contrib I'd like to have somebody from the JIT team review this.

@MichalStrehovsky Does this cause problems for NativeAOT? I don't know if the native aot project has these helpers.

@jkotas
Copy link
Member

jkotas commented Jul 2, 2021

@MichalStrehovsky Does this cause problems for NativeAOT? I don't know if the native aot project has these helpers

It will work just fine for NativeAOT. NativeAOT always uses JIT_FLAG_USE_PINVOKE_HELPERS.

@jkotas
Copy link
Member

jkotas commented Jul 2, 2021

The current implementation of InlinedCallFrame and reverse p/invoke sets up the InlinedCallFrame before the reverse p/invoke transition, which is unsafe

It would be better to reorder these instead. I assume that it is too hard to do for some reason?

@tannergooding
Copy link
Member

tannergooding commented Jul 2, 2021

A reverse pinvoke function directly calling back into native code is fairly rare, and so optimizing the situation is of marginal utility.

Is this actually rare? Things like the WNDPROC callback for Win32/WinForms is a reverse P/Invoke and that may itself query some information from native to access more data.

Edit Just browsing https://source.dot.net/#q=WndProc shows a number of handlers that themselves call P/Invokes relatively early.

@davidwrighton
Copy link
Member Author

@jkotas reordering them would certainly be a better choice, but it isn't trivial for me, as I'm no expert in the details of how the JIT really works. As the reverse p/invoke is added to the prolog/epilog in the flowgraph stage, and the inlined call frame logic is handled during lower; however, I'm not comfortable making such a change myself, as the fix looked to be more involved than I was prepared to handle, and prolog/epilog code is notoriously fussy to get right. That said, if this gets checked in in the current state, I'll open an issue against the JIT team to look at coming up with the more optimal fix. I've been able to get this to reproduce reliably by running the determinism test in a loop under jitstress1 mode, so verifying a more complex fix is not a difficult task.

@tannergooding Rare is possibly the wrong word.... possibly, less common would be a better term. There are certainly scenarios in which the reverse pinvoke to pinvoke logic is in the same function, but I don't see that its all THAT common. (Also, the penalty from using the helper functions isn't terrible.) I'm pretty sure that we don't have significant test coverage of this scenario in our test beds as the characteristic assertion messages are quite rare.

@davidwrighton davidwrighton requested a review from EgorBo July 7, 2021 23:00
@EgorBo
Copy link
Member

EgorBo commented Jul 7, 2021

Codegen diff example:

[UnmanagedCallersOnly]
static void Test() => Thread.Yield();

diff: https://www.diffchecker.com/PZkiY22r

I'm not a pinvoke expert, but I can try to investigate how to still have inlined call frame here if CORINFO_HELP_JIT_PINVOKE_BEGIN helpers noticeably affect performance (and for .NET 7.0 I guess) for this specific case.

@davidwrighton davidwrighton merged commit df7f02c into dotnet:main Jul 8, 2021
@ghost ghost locked as resolved and limited conversation to collaborators Aug 7, 2021
@davidwrighton davidwrighton deleted the fix_45326_take2 branch April 13, 2023 18:48
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.

4 participants