-
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
NativeAOT: Add win-x86 support #99372
NativeAOT: Add win-x86 support #99372
Conversation
Tagging subscribers to this area: @agocke, @MichalStrehovsky, @jkotas Issue DetailsKeeping this as draft as I may split it into more PRs. There are some unresolved issues which may warrant a discussion first. Calling conventions and name manglingThe first commit in the PR deals with the calling convention and name mangling. Generally, there are three unmanaged calling conventions on x86:
In addition, there are managed calling conventions:
We need to bridge between the managed calling convention and the unmanaged one. Some helper calls are implemented in the managed code, while others are implemented in the native code. There are two parts to this bridging - argument reordering and name mangling. The argument reordering is handled with the For name mangling we basically have two options - using the fastcall one, or using something else like undecorated names. I prototyped both but this PR uses the undecorated names. Using the fastcall mangled ones would mean that we have to add knowledge about method signatures to JitHelper.cs on the managed side. That proved to be tedious and also doesn't extend well to the custom calling convention used by several helpers. The undecorated names are easy to handle on the managed side but tricky to handle on the native side. MSVC doesn't have a native way to export methods under alternative names. The trick is to use alternatename linker/pragma. The downside of the trick is that linker doesn't see the comment pragma if there's no other reference to the .obj file inside a static library. There happened to be exactly two files that have only Name mangling of imports and exportsImport libraries on x86 require mangled names. Likewise, the export are usually decorated and then stripped by the linker (through .def file or other means). The code is roughly based on https://github.com/bflattened/runtime/pull/60/files. The mangling is used for Data variables are mangled as Math imports from LibCOther platforms used Math helpers in ASMDue to calling convention differences it was easier to implement the subset of math helper function in native runtime in ASM code. Most of the methods call C runtime. Some of them depend on xmm registers which rises the baseline (fully updated Windows 7 requires SSE2 instruction set anyway). The implementation is mostly minimum viable prototype and I anticipate that few of the helper methods will be moved to managed code soon.
|
cc @SingleAccretion please check the bits that would conflict with the LLVM branch. |
src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Compilation.cs
Outdated
Show resolved
Hide resolved
Hmm. Looks like the build is broken on the CI machine. I'll check on the matching MSVC version. Worst case it's possible to split the macro magic into zero-argument and 1+-argument macros. That works even with the old preprocessor. Also, ARM hits this annoyance:
(will fix it globally once other builds finish/fail) |
This looks fine. These calls should really be DllImports w/ SuppressGCTransition on all plaforms (#13820, #39474 was an attempt to do that). Or maybe we bite the bullet and reimplement them in C# (#9001). We have been copying floating point math methods implementation for System.Numerics.Tensors, so we have taken on the burden of maintaining out own float algorithms anyway. |
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.
Could you please split the math helpers and the find&replaces changes the FCalls/QCalls into a separate PR that will be merged first, so that the rest is easier to review?
Sure. FWIW these changes are mostly isolated in the first commit, so viewing commit by commit is easier for this draft PR. |
src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs
Outdated
Show resolved
Hide resolved
src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs
Outdated
Show resolved
Hide resolved
@@ -119,7 +119,9 @@ | |||
|
|||
<PropertyGroup> | |||
<!-- CLR NativeAot only builds in a subset of the matrix --> | |||
<NativeAotSupported Condition="('$(TargetOS)' == 'windows' or '$(TargetOS)' == 'linux' or '$(TargetOS)' == 'osx' or '$(TargetOS)' == 'maccatalyst' or '$(TargetOS)' == 'iossimulator' or '$(TargetOS)' == 'ios' or '$(TargetOS)' == 'tvossimulator' or '$(TargetOS)' == 'tvos' or '$(TargetOS)' == 'freebsd') and ('$(TargetArchitecture)' == 'x64' or '$(TargetArchitecture)' == 'arm64' or '$(TargetArchitecture)' == 'arm')">true</NativeAotSupported> | |||
<_NativeAotSupportedOS Condition="'$(TargetOS)' == 'windows' or '$(TargetOS)' == 'linux' or '$(TargetOS)' == 'osx' or '$(TargetOS)' == 'maccatalyst' or '$(TargetOS)' == 'iossimulator' or '$(TargetOS)' == 'ios' or '$(TargetOS)' == 'tvossimulator' or '$(TargetOS)' == 'tvos' or '$(TargetOS)' == 'freebsd'">true</_NativeAotSupportedOS> |
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 like we can invert it NativeAotUnsupportedOS
; that set seems to be getting smaller. 😅
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 would love to do that eventually;)
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.
Seems like the unsupported OSes are tizen
, wasi
, and browser
. Not sure of the status for haiku
, illumos
, solaris
and netbsd
which happen to have some build system support.
Unsupported archs are RV64, LA64, PowerPC (supported by Mono), WASM, and linux-x86. We may be able to shrink this list before .NET 9 ships.
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.
Yup, something like:
<NativeAotUnsupportedOS Condition="$([MSBuild]::ValueOrDefault(',browser,haiku,illumos,netbsd,tizen,wasi,', '').Contains(',$(TargetOS),'))">true</NativeAotUnsupportedOS>
usage: Condition="'$(NativeAotUnsupportedOS)' != 'true'"
Unsupported archs are RV64, LA64, PowerPC (supported by Mono), WASM, and linux-x86
and s390x.
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.
FWIW, this only needs to create a subset of what can already build with CoreCLR but does not build for native AOT (so that we don't break ./build.sh clr
for a platform that already works otherwise).
I don't think browser/wasi/powerpc/s390 builds with CoreCLR, so we don't mind if native AOT makes ./build.sh clr
more broken. If someone brings up a new platform and wants native AOT out of the way, they can exclude it, same way they can exclude anything else they don't (yet) want as part of bringup.
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 don't think browser/wasi/powerpc/s390 builds with CoreCLR,
Given we have to ensure that clr.iltools and clr.packages remain intact:
buildArgs: -s mono+libs+host+packs+libs.tests+clr.iltools+clr.packages -c $(_BuildConfig) /p:ArchiveTests=true |
we might as well just list the 'not yet supported' ones without classifying them further.
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.
Given we have to ensure that clr.iltools and clr.packages remain intact:
Clr.iltools "just works" because clr.iltools doesn't include coreclr VM and doesn't include any native AOT parts either, same for clr.packages.
Does building the clr subset (the entire clr subset, not just ilasm/dasm) work on s390 today? If not, I don't see why we'd need an extra exclusion for the native aot part. We shouldn't need to add extra exclusions (that are specific to native AOT) every time someone adds an obscure platform for mono. We either skip the entire clr build (i.e. we do the thing where build is skipped for all of coreclr, not just native AOT - except for iltools), or let it do whatever it does (break, probably). I think the latter is fine because that's how it is today.
2284c11
to
42cb8d5
Compare
Rebased on top of main. I'll update the top comment in the morning. |
src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/RuntimeExports.cs
Outdated
Show resolved
Hide resolved
src/tests/nativeaot/SmokeTests/ComWrappers/ComWrappersNative.def
Outdated
Show resolved
Hide resolved
...eclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/InteropHelpers.cs
Outdated
Show resolved
Hide resolved
src/coreclr/tools/Common/TypeSystem/Interop/InteropStateManager.cs
Outdated
Show resolved
Hide resolved
@@ -446,31 +447,33 @@ public PInvokeDelegateWrapperHashtable(InteropStateManager interopStateManager, | |||
} | |||
} | |||
|
|||
private sealed class PInvokeLazyFixupFieldHashtable : LockFreeReaderHashtable<MethodDesc, PInvokeLazyFixupField> | |||
private readonly record struct PInvokeLazyFixupFieldKey(MethodDesc Method, int SignatureBytes); |
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.
Why is the SignatureBytes part of the key? The number is derived from MethodDesc - it should not need to be part of the key.
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.
That’s the part I don’t like but I don’t know how to propagate it into the value in LockFreeReaderHashtable
.
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 made another attempt at this, but it still feels odd. LockFreeReaderHashtable
is designed to create the value from the key and there's no easy way to pass the additional information into the PInvokeLazyFixupField
constructor.
...eclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/InteropHelpers.cs
Outdated
Show resolved
Hide resolved
I started running a bigger set of tests. It looks promising but there are several notable issues that we hit:
Results on Debug build:
The failed EH test:
The baseservices tests is supposed to be skipped through issues.targets but that didn't trigger:
|
Change Rhp[U]L[Div/Mod] from FCall to QCall, affects only x86 and arm
…tRem/RhpDblRem with it.
Co-authored-by: Aaron Robinson <arobins@microsoft.com>
Co-authored-by: Jan Kotas <jkotas@microsoft.com>
6533c0d
to
543a4c8
Compare
(Rebased to resolve conflicts with |
@MichalStrehovsky Could you please review this PR as well? |
…"atoi" is imported multiple times with different calling convention
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.
LGTM modulo the two things. Thank you!
public MethodSignature NativeSignature | ||
{ | ||
get; | ||
set; |
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.
Can this be passed to the constructor? We have plenty of prior art for similar situations:
runtime/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs
Lines 1500 to 1517 in 3eb8c7f
protected struct SerializedFrozenObjectKey : IEquatable<SerializedFrozenObjectKey> | |
{ | |
public readonly MetadataType OwnerType; | |
public readonly int AllocationSiteId; | |
public readonly TypePreinit.ISerializableReference SerializableObject; | |
public SerializedFrozenObjectKey(MetadataType ownerType, int allocationSiteId, TypePreinit.ISerializableReference obj) | |
{ | |
Debug.Assert(ownerType.HasStaticConstructor); | |
OwnerType = ownerType; | |
AllocationSiteId = allocationSiteId; | |
SerializableObject = obj; | |
} | |
public override bool Equals(object obj) => obj is SerializedFrozenObjectKey && Equals((SerializedFrozenObjectKey)obj); | |
public bool Equals(SerializedFrozenObjectKey other) => OwnerType == other.OwnerType && AllocationSiteId == other.AllocationSiteId; | |
public override int GetHashCode() => HashCode.Combine(OwnerType.GetHashCode(), AllocationSiteId); | |
} |
Above struct contains TypePreinit.ISerializableReference SerializableObject
but it's not part of equality/hashcode computation. It just smuggles the value through the hash table.
I don't think we have prior art for mutable type system entities on the other hand.
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 didn’t find a way to do that with LockFreeReaderHashtable
(see comments above). I tried several but it always felt odd.
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 didn’t find a way to do that with
LockFreeReaderHashtable
(see comments above). I tried several but it always felt odd.
Does this not work? 60f1f17
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.
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.
That comment was on the record
that was making the "passenger field" part of the key because that's how records work.
What I linked uses it as a passenger, exactly how it's used in the #99372 (comment) and elsewhere within the compiler. It's not part of hash code, it's not part of equality checks.
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 misunderstood the comment then. I thought that it generally should not be part of the key (although the record
part doesn't really matter here since the equality is handled in the overridden methods).
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.
(Thanks. I hate this approach a bit less than the field with setter even if I would ideally prefer something similar to ConcurrentDictionary.GetOrAdd.)
…rd.csproj Co-authored-by: Michal Strehovský <MichalStrehovsky@users.noreply.github.com>
Passes the NativeAOT smoke tests in Debug and Checked configurations:
Math helpers in ASM
Due to calling convention differences it was easier to implement the subset of math helper function in native runtime in ASM code. Most of the methods call C runtime. Some of them depend on xmm registers which rises the baseline (fully updated Windows 7 requires SSE2 instruction set anyway). The implementation is mostly minimum viable prototype and I anticipate that few of the helper methods will be moved to managed code soon.