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

[mono][mini] Prefer calling llvmaot compiled method instead of inlining it with JIT #107401

Merged
merged 2 commits into from
Sep 16, 2024

Conversation

BrzVlad
Copy link
Member

@BrzVlad BrzVlad commented Sep 5, 2024

The motivation for this is to fix crashes due to different HW intrinsics support between llvm and JIT. For example, a llvmaot compiled method might check if we have a set of HW intrinsics available and proceed with a code path that will end up calling a method that assumes intrinsics support that is not present in the aot image (because the gsharedvt version is not emitted for example). In this scenario, the called method will end up being compiled with JIT where we would crash due to missing HW intrinsics support.

#106914

Copy link
Contributor

Tagging subscribers to this area: @lambdageek, @steveisok
See info in area-owners.md if you want to be subscribed.

@BrzVlad
Copy link
Member Author

BrzVlad commented Sep 5, 2024

For example, for the following api:

SearchValues<char> _searchValues = SearchValues.Create (string);
char[] _textExcept;
 _textExcept.AsSpan().IndexOfAnyExcept(_searchValues);

If no HW intrinsic support is enabled, the SearchValues will be a BitmapCharSearchValues, otherwise it will be a AsciiCharSearchValues<TOptimizations> where TOptimizations is IndexOfAnyAsciiSearcher.Default, assuming support for HW intrinsics in PackSources. IndexOfAnyAsciiSearcher.IndexOfAnyCore is not guaranteed to be executed from AOT code because we might not have gsharedvt enabled in non-fullaot scenarios. Because the creation of SearchValues happens in llvm compiled code we end up calling IndexOfAnyAsciiSearcher.IndexOfAnyCore with JIT, which continues to inline IndexOfAnyAsciiSearcher.IndexOfAnyLookup and then PackSources where we crash, due to missing SIMD support. If, for whatever reason, PackSources wouldn't be present in the llvmaot image, then the same crash would happen.

While we have some support at emitting certain intrinsics with JIT, the overall simd support in jit is disabled. Bringing it on par with llvm might be work we don't want to invest in.

Some of the methods making use of simd api are decorated with CompExactlyDependsOn attribute. Not sure if this is something that might be of some use for us, or whether CoreCLR might run into issues like this with R2R compilation mixed with jit at runtime. @tannergooding

Copy link
Member

@lambdageek lambdageek left a comment

Choose a reason for hiding this comment

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

This feels like it will cause perf regressions - I think this will mean we never inline hot methods that were also emitted into the AOT image. Can we add a MonoAotMethodFlags flag like MONO_AOT_METHOD_FLAG_HAS_LLVM_INTRINSICS and avoid inlining only those methods?

@matouskozak
Copy link
Member

Thank you Vlad for investigating this issue. Do I understand the issue correctly that we only hit this in AOT-llvm scenario where JIT (mini) is allowed?

Would we hit the same issue if we run fullAOT-llvm which uses mini codegen (less intrinsics support) for the parts that are not possible to AOT compile? The same if we would use fullAOT-llvm with interpreter fallback (also less intrinsics support)?

@BrzVlad
Copy link
Member Author

BrzVlad commented Sep 5, 2024

The issue was triggered by using normal aot --aot=llvm with fallback to JIT at runtime. This seems to be caused by not having by default gsharedvt enabled for normal aot compilation. So all fullaot builds don't have this issue, since all possible code making use of these intrinsics happens to be precompiled. However, if we would do a fullaot compilation where some methods making use of intrinsics have llvm compilation failure and we fallback to aot compilation with old compiler or they are simply not compiled for whatever other reason and we would fallback to interpreter at runtime, then the same issue would still arise. But I don't think these scenarios are that likely to happen in practice.

I think, in the wild, this issue would be visible on android, where we could use some partial/profiled aot with llvm enabled, with JIT fallback for everything else.

@tannergooding
Copy link
Member

Some of the methods making use of simd api are decorated with CompExactlyDependsOn attribute. Not sure if this is something that might be of some use for us, or whether CoreCLR might run into issues like this with R2R compilation mixed with jit at runtime. @tannergooding

https://github.com/dotnet/runtime/blob/main/docs/design/coreclr/botr/vectors-and-intrinsics.md has some information on how these attributes are used. They are primarily for R2R (since its partial compilation) as normal JIT or NAOT usage is "full" and self-consistent.

It sounds like this would indeed be a place for the partial AOT scenario on Mono could utilize this information as well. Notably none of these paths should be actually reachable if one of the CompExactlyDependsOn ISAs listed isn't available, as higher call sites should themselves be guarding things here.

So it's also possible that the partial AOT scenario could handle this by ensuring the relevant dead code elimination happens, by ensuring that use of the platform specific intrinsics correctly throws PlatformNotSupportedException, or similar.

@BrzVlad
Copy link
Member Author

BrzVlad commented Sep 10, 2024

/azp run runtime-llvm

Copy link

Azure Pipelines successfully started running 1 pipeline(s).

…th JIT

The motivation for this is to fix crashes due to different HW intrinsics support between llvm and JIT. For example, a llvmaot compiled method might check if we have a set of HW intrinsics available and proceed with a code path that will end up calling a method that assumes intrinsics support that is not present in the aot image (because the gsharedvt version is not emitted for example). In this scenario, the called method will end up being compiled with JIT where we would crash due to missing HW intrinsics support.
We include a new flag when compiling aot method. When loading aot method, we will include mapping from code to flags so we can look it up later.
@BrzVlad
Copy link
Member Author

BrzVlad commented Sep 11, 2024

/azp run runtime-llvm

Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@BrzVlad BrzVlad merged commit cdc8418 into dotnet:main Sep 16, 2024
89 checks passed
@BrzVlad
Copy link
Member Author

BrzVlad commented Sep 16, 2024

/backport to release/9.0

Copy link
Contributor

Started backporting to release/9.0: https://github.com/dotnet/runtime/actions/runs/10879961822

jtschuster pushed a commit to jtschuster/runtime that referenced this pull request Sep 17, 2024
…ng it with JIT (dotnet#107401)

* [mono][mini] Prefer llvmaot compiled method instead of inlining it with JIT

The motivation for this is to fix crashes due to different HW intrinsics support between llvm and JIT. For example, a llvmaot compiled method might check if we have a set of HW intrinsics available and proceed with a code path that will end up calling a method that assumes intrinsics support that is not present in the aot image (because the gsharedvt version is not emitted for example). In this scenario, the called method will end up being compiled with JIT where we would crash due to missing HW intrinsics support.

* [mono][mini] Prefer llvm compiled method only if it uses simd intrinsics

We include a new flag when compiling aot method. When loading aot method, we will include mapping from code to flags so we can look it up later.
sirntar pushed a commit to sirntar/runtime that referenced this pull request Sep 30, 2024
…ng it with JIT (dotnet#107401)

* [mono][mini] Prefer llvmaot compiled method instead of inlining it with JIT

The motivation for this is to fix crashes due to different HW intrinsics support between llvm and JIT. For example, a llvmaot compiled method might check if we have a set of HW intrinsics available and proceed with a code path that will end up calling a method that assumes intrinsics support that is not present in the aot image (because the gsharedvt version is not emitted for example). In this scenario, the called method will end up being compiled with JIT where we would crash due to missing HW intrinsics support.

* [mono][mini] Prefer llvm compiled method only if it uses simd intrinsics

We include a new flag when compiling aot method. When loading aot method, we will include mapping from code to flags so we can look it up later.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants