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

Support generic arguments for calli in CoreCLR #97079

Merged
merged 47 commits into from
Feb 22, 2024

Conversation

hez2010
Copy link
Contributor

@hez2010 hez2010 commented Jan 17, 2024

Adding generic arguments support for calli in CoreCLR.

We don't need to handle generics in DynamicMethodDesc as it won't be a generic method definition and the generic arguments here can only be blittable types (so no generic sharing).

Note that this has already been supported by NativeAOT.

Console.WriteLine(Call<int, int>((delegate* unmanaged<int, int>)&TestII, 42));
Console.WriteLine(Call<float, float>((delegate* unmanaged<float, float>)&TestFF, 42.42f));
Console.WriteLine(Call<float, int>((delegate* unmanaged<float, int>)&TestFI, 42.42f));

Console.WriteLine(Foo<int>.Call<int>((delegate* unmanaged<int, int>)&TestII, 42));

unsafe static U Call<T, U>(void* pfn, T arg)
{
    return ((delegate* unmanaged<T, U>)pfn)(arg);
}

[UnmanagedCallersOnly]
static int TestII(int arg) => arg;

[UnmanagedCallersOnly]
static float TestFF(float arg) => arg;

[UnmanagedCallersOnly]
static int TestFI(float arg) => (int)arg;

class Foo<T>
{
    public unsafe static U Call<U>(void* pfn, T arg)
    {
        return ((delegate* unmanaged<T, U>)pfn)(arg);
    }
}

Output:

42
42.42
42
42

Resolves #9136

@ghost ghost added the community-contribution Indicates that the PR has been added by a community member label Jan 17, 2024
@hez2010 hez2010 changed the title Support generic arguments infor calli Support generic arguments for calli Jan 17, 2024
@hez2010 hez2010 changed the title Support generic arguments for calli Support generic arguments for calli in CoreCLR Jan 17, 2024
@jkotas
Copy link
Member

jkotas commented Jan 17, 2024

here can only be blittable types (so no generic sharing).

We still need a reasonable failure mode for the unsupported cases. Please make sure to add tests for them.

@jkotas
Copy link
Member

jkotas commented Jan 17, 2024

Also #9136 (comment) : Would it make sense to enable this only when runtime marshalling is disabled for the assembly?

@hez2010
Copy link
Contributor Author

hez2010 commented Jan 18, 2024

Would it make sense to enable this only when runtime marshalling is disabled for the assembly?

We already disallow any runtime marshalling against UnmanagedCallerOnly methods, so I assumes we only need an error mode when the signature is unexpected?

@MichalPetryka
Copy link
Contributor

Would it make sense to enable this only when runtime marshalling is disabled for the assembly?

We already disallow any runtime marshalling against UnmanagedCallerOnly methods, so I assumes we only need an error mode when the signature is unexpected?

Your change applies to all unmanaged pointers though, not just UnmanagedCallersaOnly.

@hez2010 hez2010 force-pushed the feature/generic-calli branch from 1e45b6f to 0327254 Compare January 21, 2024 12:00
@hez2010
Copy link
Contributor Author

hez2010 commented Jan 21, 2024

Also #9136 (comment) : Would it make sense to enable this only when runtime marshalling is disabled for the assembly?

I added a guard to make sure no marshaling is required for calli.

@jkotas
Copy link
Member

jkotas commented Jan 22, 2024

This needs tests and fixes for bugs discovered by the tests.

@hez2010 hez2010 force-pushed the feature/generic-calli branch from 551e349 to a10142c Compare January 23, 2024 13:55
src/coreclr/vm/ceeload.h Outdated Show resolved Hide resolved
Co-authored-by: Jan Kotas <jkotas@microsoft.com>
@@ -1221,6 +1221,9 @@
<ExcludeList Include="$(XunitTestBinBase)/Interop/StringMarshalling/BSTR/BSTRTest/**">
<Issue>Crashes during LLVM AOT compilation.</Issue>
</ExcludeList>
<ExcludeList Include="$(XunitTestBinBase)/Interop/MarshalAPI/FunctionPointer/FunctionPtrTest/*">
<Issue>Generic calli support not implemented yet in mono.</Issue>
Copy link
Member

@jkotas jkotas Feb 15, 2024

Choose a reason for hiding this comment

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

How hard is it to implement generic unmanaged calli support in Mono?

cc @vargaz @lambdageek

Copy link
Member

@AaronRobinsonMSFT AaronRobinsonMSFT Feb 15, 2024

Choose a reason for hiding this comment

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

Based on my conversations with @lambdageek I think generics support at some of these interop margins are a bit tricky in mono.

Copy link
Member

Choose a reason for hiding this comment

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

Hmm, that's unfortunate.

We try to maintain feature parity between all runtimes. We need to figure out what we are going to do about Mono support before enabling it for CoreCLR.

Copy link
Member

Choose a reason for hiding this comment

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

The example from the issue descriptions works in net8.0 Mono already with JIT and interpreter. I haven't tried the examples from the rest of the added test.


The thing that Aaron is talking about is that mono doesn't have great support for creating wrapper methods that are themselves generic (ie: they have generic method params). We would need that for the generic UnsafeAccessor work - the issue there is that we need to generate a generic method. Likewise if it was legal to do something like this:

void *pGenericFn = &SomeGenericUnsafeCallersOnlyMethod;

[UnsafeCallersOnly]
unsafe void SomeGenericUnsafeCallersOnlyMethod<T>(T *) where T : unmanaged { ... }

we would need to generate a generic wrapper.


For unmanaged function pointers we create wrappers around the &Func - ldftn SomeUnmanagedCallersOnlyMethod actually returns the address of the wrapper that has the native, not managed, calling convention.

At the calli, we don't need to do anything special almost all the time.

The one place where we might get into trouble is if we need to do a call out using generic sharing, or gsharedvt (universal generic sharing): That is Foo<int>.Call<int>(...) from the issue description, but compiled for ios, for example. I haven't tried it.

Copy link
Contributor

Choose a reason for hiding this comment

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

The failures are probably going to be with generic sharing and aot. The JIT should work fine.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hmm, that's unfortunate.

We try to maintain feature parity between all runtimes. We need to figure out what we are going to do about Mono support before enabling it for CoreCLR.

The tests were actually passing on mono, except mono-llvm-aot failed to build the negative tests (RunInvalidGenericFunctionPointerTest).

Copy link
Member

Choose a reason for hiding this comment

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

Could you please factor out the negative cases into separate test and only disable those for Mono?

Handling of negative cases by llvm-aot should not be blocking.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Seems that even if we removed negative cases, mono-aot-llvm still fail to compile (but with a different error message).

@hez2010
Copy link
Contributor Author

hez2010 commented Feb 18, 2024

Is there anything I can do to make this PR get merged? Maybe I should disable the generic calli tests until mono impl ready to unblock the CI?

@jkotas
Copy link
Member

jkotas commented Feb 18, 2024

until mono impl ready

Do you plan to work on the mono impl?

@hez2010
Copy link
Contributor Author

hez2010 commented Feb 19, 2024

until mono impl ready

Do you plan to work on the mono impl?

I can try but I don't have experience with the mono codebase. Maybe need some help when implement this for mono.

@jkotas
Copy link
Member

jkotas commented Feb 20, 2024

I can try but I don't have experience with the mono codebase. Maybe need some help when implement this for mono.

@vargaz @lambdageek Do you have any advice for how to implement this for Mono?

(If you prefer to merge this PR as is and deal with the mono implementation separately, that's fine too.)

@vargaz
Copy link
Contributor

vargaz commented Feb 20, 2024

Would be better to merge this and disable the failing tests on mono.

@jkotas
Copy link
Member

jkotas commented Feb 20, 2024

Would be better to merge this and disable the failing tests on mono.

@hez2010 Could you please do that?

@hez2010
Copy link
Contributor Author

hez2010 commented Feb 21, 2024

Done. CI failures are unrelated.

@@ -21,6 +21,8 @@
<Compile Include="LayoutClass/*.cs" />
<Compile Include="ArrayMarshalling/**/*.cs" />
<Compile Include="MarshalAPI/**/*.cs" />
<!-- Disable build of GenericFunctionPointer tests on mono -->
<Compile Condition="'$(RuntimeFlavor)' == 'mono'" Remove="MarshalAPI/FunctionPointer/GenericFunctionPointer.cs" />
Copy link
Member

Choose a reason for hiding this comment

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

I think that the conditional compilation like this is violating invariants of the test builds. From https://github.com/dotnet/runtime/blob/main/docs/workflow/testing/coreclr/test-configuration.md#adding-test-guidelines: "Therefore the managed portion of each test must not contain - Target platform dependent conditionally included files"

The pattern to disable tests for Mono is <CLRTestTargetUnsupported Condition="'$(RuntimeFlavor)' == 'mono'">true</CLRTestTargetUnsupported>.

Copy link
Member

Choose a reason for hiding this comment

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

We do build the tests separately for Mono and CoreCLR builds (and we depend on this in the test source generator today).

I think this is fine for now (and is better than splitting out these tests into a separate process just to avoid running these tests on Mono).

@@ -21,6 +21,8 @@
<Compile Include="LayoutClass/*.cs" />
<Compile Include="ArrayMarshalling/**/*.cs" />
<Compile Include="MarshalAPI/**/*.cs" />
<!-- Disable build of GenericFunctionPointer tests on mono -->
Copy link
Member

Choose a reason for hiding this comment

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

This should be a link to an issue that tracks implementing this feature in Mono.

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.

@dotnet/interop-contrib Could you please review this as well?

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.

Thank you!

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-Interop-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.

Unmanaged calli not supported with generic arguments
7 participants