-
Notifications
You must be signed in to change notification settings - Fork 4.9k
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
Fix EH handling in PInvoke stubs and remove workaround #327
Conversation
The fix in the JIT is to inline the raw PInvoke call if: 1) We are compiling a PInvoke IL Stub 2) We are compiling the stub without a secret parameter In crossgen1/jit, the raw pinvoke call is a calli operation that uses the secret parameter passed to the stub, but with crossgen2, that parameter is not used and we have a separate pinvoke stub for each pinvoke.
src/coreclr/src/jit/importer.cpp
Outdated
@@ -6393,6 +6393,15 @@ bool Compiler::impCanPInvokeInlineCallSite(BasicBlock* block) | |||
// jit\jit64\ebvts\mcpp\sources2\ijw\__clrcall\vector_ctor_dtor.02\deldtor_clr.exe | |||
if (block->hasTryIndex()) | |||
{ | |||
if (opts.jitFlags->IsSet(JitFlags::JIT_FLAG_IL_STUB) && | |||
opts.jitFlags->IsSet(JitFlags::JIT_FLAG_PUBLISH_SECRET_PARAM)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this condition correct?
JIT_FLAG_PUBLISH_SECRET_PARAM
is never set in crossgen2. It means that this if
is going to be always false in crossgen2 and this change has no effect on what crossgen2 does.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops... Typo probably during the repro reforking
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed. Thanks for catching this Jan
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need the JIT_FLAG_PUBLISH_SECRET_PARAM
part of the condition at all? What is not going to work if the condition is just if (opts.jitFlags->IsSet(JitFlags::JIT_FLAG_IL_STUB)
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is not going to work if the condition is just if (opts.jitFlags->IsSet(JitFlags::JIT_FLAG_IL_STUB) ?
In my mind, i think it should work, but I put a more conservative check to avoid an unintentional break somewhere else. I don't know enough context outside of crossgen2 to be able to say with confidence that we should remove the check for JIT_FLAG_PUBLISH_SECRET_PARAM
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some interop stubs generated at runtime are not shared and do not have CORJIT_FLAG_PUBLISH_SECRET_PARAM
. It is not reliable to use CORJIT_FLAG_PUBLISH_SECRET_PARAM
to tell whether you are generating stub for crossgen2. (And even if it was reliable, it would be rather cryptic to use this flag for this purpose.)
It means that you are still potentially changing the behavior for the JIT case with this change. The potential bugs are just going to be more subtle and harder to find.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see. Ok let me remove CORJIT_FLAG_PUBLISH_SECRET_PARAM
and see if tests are going to start to fail in CI/outerloop. If all tests are passing, I'll merge without CORJIT_FLAG_PUBLISH_SECRET_PARAM
.
Sounds good?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be most appropriate to change the condition to use CORJIT_FLAG_USE_PINVOKE_HELPERS
(see my other comment).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CORJIT_FLAG_USE_PINVOKE_HELPERS
happens to be used by crossgen only, so you can be sure that you are not changing any of the JIT behavior.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good.
It would be nice to verify that the within try check bailout is still really needed. Can you open an issue to look into that?
What's that? |
Make sure we really understand why we can't just delete that whole ifdef'd block, and ideally, add a test case. |
Ah, yes. Will do. |
The problem described by the comment on the ifdefed block can't happen with
|
That was my first fix attempt, and it wasn't working (see #248 for the error I got). Based on what I debugged in the EH handling, we need to teach the runtime to handle inlined pinvokes where the caller has EH blocks. The unwinding after executing the catch block wasn't working correctly. This is a much more involved change BTW, if we fix the EH handling, it will be general goodness because we'll be able to inline pinvokes with marshaling into callers. |
Thinking about it more, I think that this fix will most likely work too, and look better (this is similar to my initial fix, which is along the line of what you're thinking too, but limited to the stubs only): if (block->hasTryIndex())
{
if (opts.jitFlags->IsSet(JitFlags::JIT_FLAG_IL_STUB) && opts.ShouldUsePInvokeHelpers())
return true;
return false;
} I will try this out tomorrow |
Yes, that would look better. Thanks! |
Fix for #248
Reverts workaround from #249
The fix in the JIT is to inline the raw PInvoke call if:
In crossgen1/jit, the raw pinvoke call is a calli operation that uses the secret parameter passed to the stub, and gets inlined by-design (we don't even call
impCanPInvokeInlineCallSite
for it).With crossgen2, the secret parameter is not used and we have a separate pinvoke stub for each pinvoke. Each pinvoke IL stub generated by crossgen2 will have some marshaling operations if applicable, and a call to the raw pinvoke target (which gets wrapped by calls to JIT_PInvokeBegin/End helpers). This raw pinvoke call has to be inlined for IL stubs, even in the presence of EH blocks due to marshaling, otherwise we would get an IL stub that infinitely recursively calls itself because it can't inline the actual pinvoke call.
cc @dotnet/crossgen-contrib @dotnet/jit-contrib