-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
Typeforward System.Runtime.CompilerServices.Unsafe to SPC for netcore #45475
Comments
To clarify, this is more than type-forwarding:
|
Yep, that's why I wrote "We should unify them and type forward ..."
Is there any reason why they should not have the same surface area? Does the lack of lack of C# declaration in SPC buy us anything if they are runtime intrinsics anyway? |
I don't understand. The ones not declared in C# also aren't implemented in the runtime. |
I meant that we have 3 different types of implementations (intrinsics, SPC/C# version, SRCU/IL version). I see that SPC version is missing about 2 different method sets (e.g. Subtract* with overloads). I didn't look closely but I expect the missing methods can be implemented as C# code in SPC which would expand the non-intrinsics methods in SPC and we could type-forward. I don't know what is the history about having only a subset of them available in SPC only though and if there is an important reason for that but as of today not all Unsafe methods in SPC are implemented as intrinsics for all codegens. |
They were just added as they were needed by corelib. |
@stephentoub do you think this would be feasible to do? Also @marek-safar do we know more or less how big the size wins would be here? Just trying to see if the end goal will be worth the effort here to see if we should plan this for 6.0. |
It's feasible. I don't know if it's worthwhile or not. And we would still need to maintain the ilproj implementation for the OOB. If it moves the needle, it's "just work" (adding the missing APIs and implementations provided by the JIT, renaming things, type forwarding, whatever package work is required, etc.) It might also have an implication on being able to extend the type further in the OOB. |
@joperezr I think the size win is probably around 4k but also drop of an assembly file which might be worth more than. |
I might be missing something, but if we are going to need the typeforward then presumably we wouldn't be getting rid of the assembly file right? |
The illinker by default rewrites type forwarders with the declaration types. |
I probably won't be able to get to it this week. My own timeframe otherwise depends on my availability outside of the generic math work. I'd be fine with someone else picking up the IL expansion part if they were willing/able. |
Note that the Mono side of Internal.Runtime.CompilerServices.Unsafe is implemented as JIT and interpreter intrinsics. The IL expansion won't apply there. |
I have been working on the this. I've started by changing the internal Unsafe implementation and to the System namespace and fixing up corelib to refer to it that way. I've then cross referenced the CoreCLR Crossgen2 and Mono source and identifying what is implemented by each of them and it's interesting in places so I thought it might be worth putting my notes here to see if any of them need discussion.
The list of methods that need to be added into corelib to contain the complete current set of unsafe methods is: +public static void Copy<T>(void* destination, ref T source)
+public static void Copy<T>(ref T destination, void* source)
+public static void CopyBlock(void* destination, void* source, uint byteCount)
+public static void CopyBlock(ref byte destination, ref byte source, uint byteCount)
+public static void CopyBlockUnaligned(void* destination, void* source, uint byteCount)
+public static void CopyBlockUnaligned(ref byte destination, ref byte source, uint byteCount)
+public static void InitBlock(void* startAddress, byte value, uint byteCount)
+public static void InitBlock(ref byte startAddress, byte value, uint byteCount)
+public static void InitBlockUnaligned(void* startAddress, byte value, uint byteCount)
+public static ref T Unbox<T>(object box) where T : struct
+public static ref T Add<T>(ref T source, nuint elementOffset)
+public static ref T Subtract<T>(ref T source, int elementOffset)
+public static void* Subtract<T>(void* source, int elementOffset)
+public static ref T Subtract<T>(ref T source, nint elementOffset)
+public static ref T Subtract<T>(ref T source, nuint elementOffset)
+public static ref T SubtractByteOffset<T>(ref T source, nint byteOffset)
+public static ref T SubtractByteOffset<T>(ref T source, nuint byteOffset)
|
Thank you for looking into this!
If they are unused, you can just delete them.
This implementation technique will tend to generate lower quality code in some situations. I would avoid this technique and just do the intrinsic expansion for everything. We won't need to have discussion about the perf impact of this technique during PR review. |
It'd make sense to have parity for supported intrinsic on both runtimes (@SamMonoRT can help with implementation) |
Is that not what #64314 fixed? |
Yes, looks like it was fixed while I was waiting for a merge and I didn't want to pull. I'll update. |
To contradict @jkotas for Mono I think a simpler C# implementation in terms of another overload is preferable at least initially if we get a smaller/cleaner PR to review. |
After talking with @lambdageek about this I needed to check the mono interpreter as well as the compiler. This leaves me with a slightly more complex table that shows what will need to be provided to make this work (scroll right):
I think that some of the currently il only items can be implemented in terms of each other, it seems likely that the Subtract overloads for example only need one real implementation with the others calling into it. They may also be implementable using the existing intrinsics like Add. If we can fill in those c# implementations it will lower the work required on the mono side. I need to know how the current System.Runtime.CompilerServices.Unsafe assembly is going to be handled. If it's going to be moved permanently in-box like System.Buffers was then there is no need for it to be an il project and it can be made a very simple c# project like System.Buffers. If it needs to still be shipped out of box then conditional compilation is going to be needed. My original intention was to do this in a single PR because any half implemented state breaks a lot of things. I'm not sure that's feasible given the amount of changes that need to be made so it may need to be a couple of PRs that are merged at the same time when they're both ready. It isn't going to be a small PR, it's 160+ files just updating the managed private assemblies. It will still need the coreclr and nativeaot work as well as the in-box move and possible changes to any projects. |
Thanks for working on this.
Presumably we want to ship a new version of the package that unifies the surface area with what's in corelib, and we no longer have harvesting of previously built assets, so I think we'll need to keep the whole existing implementation around as-is, but with build system changes so that the ilproj isn't used for netcoreapp current and instead for netcoreapp current the Unsafe type is typeforwarded down to corelib. @ericstj, please correct me if I'm wrong.
That would be preferable where possible, basically just deleting the reference to the Unsafe assembly. |
We should not be adding any new public APIs as part of this work, so there should be nothing to unify. We won't be able to continue shipping the S.R.CS nuget package once we move the implementation to CoreLib. We may be able to ship a new version of the package in .NET 6, but we will definitely have to stop shipping it in .NET 7 to avoid versioning hell. I would vote to stop shipping it now to simplify things and switch it to same plan as e.g. System.Buffers as @Wraith2 suggested. S.R.CS is the only package that uses ilproj that is a constant source of issues.
Yes, we should clean this up, but it can wait for follow-up PR. |
Really? If someone has a reference to the existing nuget package that defines |
We should dead-end the package. Think of this in a similar way to For up-stack projects with TargetFrameworks lower than So to be clear: <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<IsPartialFacadeAssembly>true</IsPartialFacadeAssembly>
<ExcludeResourcesImport>true</ExcludeResourcesImport>
<TargetFrameworks>$(NetCoreAppCurrent)</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="$(CoreLibProject)" />
</ItemGroup>
</Project> |
Is this different from what I said? Just trying to fix any mental-model discrepancies for the future. |
I misunderstood "unifies the surface area with what's in corelib" as adding the few extra corelib APIs from the table above as new public APIs. I think you just meant unifying the type identity via type forwarding. I agree with that. Also, you said that we'll need to keep the whole existing implementation around as-is. It should not be necessary for dead-ending the package as @ericstj explained. |
Yes
I'm not clear on how this works. If someone has a project that's multi-targeting net7.0 and netstandard2.0, and they're referencing System.Runtime.CompilerServices.Unsafe, what do they do? Do they need to specify a different version of the nuget package to get the best behavior when compiling for each? The old package won't unify the type identities on net7.0, and the new package wouldn't contain the old implementation that works with netstandard2.0 (if we don't keep it around to ship it nor harvest the previously built assemblies). I'm missing something. |
They will reference System.Runtime.CompilerServices.Unsafe 6.0.0. When compiling for netstandard2.0, it will work just like it works today. When compiling for net7.0, there will be duplicate System.Runtime.CompilerServices.Unsafe reference assembly: one from the 6.0 package and one from net7.0 runtime refpack. The system will pick up the one from net7.0 because it has higher version. The reference assembly from the nuget package won't make it to the compiler command line. |
I see. Ok. Thanks. |
A few of the intrinsics like Add and Subtract can be implemented using unsafe methods so I've filled those in for non CoreCLR targets as is done in the existing code. Unless managed implementations of any additional methods can be provided this leaves the list of work required for mono as getting rid of the red crosses in this table:
|
How is this going to affect wasm? |
@ericstj as I just saw this, the |
I can't remember which similar project I looked at when I wrote that. Maybe |
Right now we have two different implementations of System.Runtime.CompilerServices.Unsafe type. One in System.Runtime.CompilerServices assembly and the second one in System.Private.Corelib assembly. We should unify them and type forward to SPC implementation. This would reduce code duplication and simplify JIT/AOT maintenance for the intrinsics as well as drop dependency on IL compiler for libraries build.
The text was updated successfully, but these errors were encountered: