Repository navigation
Conversation
|
Azure Pipelines: Successfully started running 3 pipeline(s). 13 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
|
Tagging subscribers to this area: @agocke, @dotnet/ilc-contrib |
|
What's the practical use for this?
This will produce code that does not follow runtime ABI - no unwind/GC info for GC stackwalks, etc. I do not see how this can work. This feels like an experiment that won't be ever accepted into main. |
I'm trying to integrate NAOT into a game engine, so I would like to see if LTO that allows managed code (mostly reverse P/Invokes) to be inlined into the native callers can reduce the interop overhead and make things faster, as well as potentially enable whole program optimizations provided by the native linker. |
LLVM supports
available_externallyfunction definitions, allowing their bodies to participate in optimization without emitting an out-of-line implementation. This allows native LTO callers to inline managed code while retaining the original RyuJIT codegen.This PR adds
--llvm-ltooption to export eligible methods as LLVM IR using a simple translator. The translator shares scalar semantics with the type-preinitialization interpreter and checks native relocations for unmodeled runtime behavior.This covers scalar methods on x64 and ARM64. The translator supports integer and floating-point operations, conversions, branches, switches, local addresses, and calls with known targets, including function pointers. EH, GC-dependent operations, potentially throwing operations, backedges, recursion, and unknown indirect calls are rejected for now because the necessary managed runtime semantics are not modeled.
Most code in the translator is carried from the existing type preinitializer.
Inlining native functions into managed callers are not supported for now, because this would require either importing native IR into RyuJIT before codegen, or emitting replacement bodies with the required managed ABI, GC, and unwind support, which is a complex topic. Maybe in the future we can make use of the outcome from NativeAOT-LLVM to extend the support for both directions.
For UCO support, the translator allocates the reverse P/Invoke frame and calls
RhpReversePInvokeon entry, and callsRhpReversePInvokeReturnbefore every return. When translating a managed call inside a UCO body, it copies the IR as__llvm_lto_inline_<method>which usesavailable_externally alwaysinline, as well as its nested calls, for example:Since
available_externallydoesn't emit the out-of-line definition, if the__llvm_lto_inline_helper doesn't get inlined by LLVM, the linking will fail due to unresolved symbols. This ensures that we won't leave an unrecognized native frame during GC stack walk.Also added some smoke tests.
Example:
In C#:
In C:
In csproj:
On Windows x64, linking the same NativeAOT object and native bitcode archive:
Before:
After: