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

[RuntimeAsync] Fix support for pinvokes #2847

Conversation

jakobbotsch
Copy link
Member

  • Make sure async transformation leaves a scratch BB around for the pinvoke prolog to be inserted into
  • Make sure we properly insert pinvoke epilogs for GT_RETURN_SUSPEND nodes (needed on x86)
  • Ensure the pinvoke frame related locals are not captured by the async transformation

- Make sure async transformation leaves a scratch BB around for the
  pinvoke prolog to be inserted into
- Make sure we properly insert pinvoke epilogs for `GT_RETURN_SUSPEND`
  nodes (needed on x86)
- Ensure the pinvoke frame related locals are not captured by the async
  transformation
@jakobbotsch
Copy link
Member Author

PTAL @VSadov

@jakobbotsch jakobbotsch added the area-async Runtime generate async state machines label Dec 3, 2024
@jakobbotsch jakobbotsch requested a review from VSadov December 3, 2024 17:12
private static unsafe delegate* unmanaged<int> GetFPtr() => &GetValue;

[UnmanagedCallersOnly]
private static int GetValue() => 5;
Copy link
Member

@VSadov VSadov Dec 3, 2024

Choose a reason for hiding this comment

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

A thought: what should happen if UnmanagedCallersOnly is applied to runtime async method.
Does it work today with ordinary async? (I guess, why not, they are just Task-returning methods)

Copy link
Member Author

Choose a reason for hiding this comment

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

I haven't checked, but I imagine UnmanagedCallersOnly on any function returning an object reference should result in IlegalProgramException. So I would hope that was the case for async1, and we should similarly disallow it for async2.

Copy link
Member

Choose a reason for hiding this comment

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

I haven't checked, but I imagine UnmanagedCallersOnly on any function returning an object reference should result in IlegalProgramException. So I would hope that was the case for async1, and we should similarly disallow it for async2.

I checked.
Even though the documentation only mentions that parameters must be blittable, the runtime requires that the return type is blittable as well. That rules out runtime async signatures. Even nongeneric ValueType, although a struct, is not blittable.

{
// If previous first BB was a scratch BB then we have to recreate it
// after since later phases may be relying on it.
// TODO-Cleanup: The later phases should be creating it if they need it.
Copy link
Member

Choose a reason for hiding this comment

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

Do you plan to address this TODO or this is just a "good to have" comment?

Copy link
Member

Choose a reason for hiding this comment

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

I do agree that relying on the presence of scratch is an arbitrary dependency that just happen to hold.
If creating on demand is not too hard, it would be better.

Copy link
Member Author

Choose a reason for hiding this comment

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

Do you plan to address this TODO or this is just a "good to have" comment?

I don't plan to address it here, this should rather be cleaned up upstream.

I do agree that relying on the presence of scratch is an arbitrary dependency that just happen to hold.
If creating on demand is not too hard, it would be better.

The frontend does ensure it:

// The backend requires a scratch BB into which it can safely insert a P/Invoke method prolog if one is
// required. Similarly, we need a scratch BB for poisoning and when we have Swift parameters to reassemble.
// Create it here.
if (compMethodRequiresPInvokeFrame() || compShouldPoisonFrame() || lvaHasAnySwiftStackParamToReassemble())
{
madeChanges |= fgEnsureFirstBBisScratch();
fgFirstBB->SetFlags(BBF_DONT_REMOVE);
}

It's simply that this is a very long-term invariant that is error prone and which feels unnecessary to me.

Copy link
Member

Choose a reason for hiding this comment

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

Exactly. Such dependencies have negative value by default. They must be justified by advantages or scenarios that they enable. If arranging a scratch block was difficult to do later, it could be a possible justification, but, as I understand, it is not the case.

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. Thanks!

@jakobbotsch jakobbotsch merged commit d38e953 into dotnet:feature/async2-experiment Dec 4, 2024
1 of 7 checks passed
@jakobbotsch jakobbotsch deleted the feature/async2-experiment branch December 4, 2024 09:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-async Runtime generate async state machines
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants