Repository navigation
Conversation
superpmi, mcs and the collector, counter and simple shims are now C#, published with NativeAOT under the same names, command lines, outputs and locations, so they are drop-in replacements; the native sources are deleted. - SuperPmi.Shared has the method context format and the recording, replay and dumping of the JIT-EE calls. superpmi and the shims reuse crossgen2's JIT interface (CorInfoImpl_generated.cs and its native JitInterfaceWrapper). - The shims call the EE through native forwarders generated by the ThunkGenerator, which capture the EE's exceptions and rethrow them at the boundaries. The native glue is linked in statically. - The output matches native on Linux and Windows x64: replay, mcs, collections and call counts. The win-x86 and linux-arm64 cross builds were checked too. Co-authored-by: Cursor <cursoragent@cursor.com>
|
Azure Pipelines: Successfully started running 5 pipeline(s). 10 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
|
@jkoritzinsky, @jkotas, @jakobbotsch: the JIT-EE interface layer is shared with crossgen2/ILC; the rest is a C# rewrite that fast-forwards SuperPMI to the point where it no longer depends on the CoreCLR PAL, which is why I started on this during the PAL cleanup. The remaining C++ is about 600 lines of glue that uses only the JIT-EE interface headers. Based on my testing, it's a drop-in replacement: same binaries in the same places, same command lines and outputs, and no CI changes needed. |
|
@dotnet/jit-contrib Thoughts about this rewrite? It sounds good to me, as long as it does not negatively impact team productivity and validations based on superpmi. |
This is something we've thought about doing but never tried. I'd say it is worth serious consideration.
|
|
Thanks @AndyAyersMS.
|
a8c6eec to
cdb024b
Compare
cdb024b to
4db2623
Compare
We should verify if it is handled by TPDiff properly as well because we are thinking to utilize TPDiff for Arm PRs. |
|
I'm not so sure about making superpmi-shim-collector rely on NativeAOT. In bring-up scenarios I would expect that we want to be able to record SuperPMI collections well before we have a working NativeAOT for the target platform. I don't have too many concerns about moving the other tooling and replaying side, however. |
These days, we typically have a working coreclr+nativeaot runtimes ported in one shot, since most of the asm helpers are shared. Besides, we start needing SPMI type of tooling once we have a working stack. NativeAOT went from an optional to a required component on desktop platforms (where we run the spmi host). |
Well, working NativeAOT depends on working JIT. If the JIT is not working my go to dev loop would have me using natively compiled superpmi-shim-collector on the target when isolating the repro. |
AFAIK, (and I think it’s written in the porting docs) new platforms are always cross compiled during the port work, and we use host platform toolchain to emit for the target until the port is completed. At least this is how five platforms were ported in past few years. |
Sure, how superpmi-shim-collector and coreclr are compiled for for the target does not matter much to this workflow. |
I don't think the workflow changes in practice:
|
I do not think bring-ups like LoongArch64 had working crossgen2/ILC until a much later point-in-time. All the focus was on coreclr/corerun and the initial work was around running trivial examples with corerun. It is also the approach pointed at by https://github.com/dotnet/runtime/blob/main/docs/design/coreclr/botr/guide-for-porting.md. I assume most of this PR is LLM-coded? It seems likely that during bring-up an LLM could just create an ad-hoc native version of superpmi-shim-collector if necessary, so in practice this may not even matter. |
|
Yup, at the time LA64 and RV64 were ported, nativeaot and surrounding infra was handwritten. These days we are in a much better situation a) due to more code sharing (e.g. src/native/minipal, src/coreclr/minipal, src/coreclr/runtime etc. are shared layers) and b) LLM can accelerate going from zero to working hello-world app on a new target over a weekend: #128783. |
|
I agree with @am11 that that the historical way of doing new architecture ports is not necessarily the best way of doing new architecture ports going forward. #128783 suggests that NAOT is a better target for initial bring up. Also, I expect that we will eventually get to a place where AOT/R2R compilation is required for the JITed runtime startup. In that world, doing AOT bring up first is going to be the only viable path. |
…ng jitmetadatalist.h Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
|
/azp run runtime-coreclr superpmi-collect-test |
|
Azure Pipelines: Successfully started running 1 pipeline(s). |
… side is built against Co-authored-by: Cursor <cursoragent@cursor.com>
Could you please re-trigger it? The NativeAOT collection legs failed to load the collector shim: CI builds the native side against libc++ (LLVM's C++ library, on top of the libstdc++ ABI), but the shim was only linked with libstdc++. It now links libc++ the same way the CMake-built binaries do. |
|
/azp run runtime-coreclr superpmi-collect-test |
|
Azure Pipelines: Successfully started running 1 pipeline(s). |
The shims caught the exceptions that the EE threw from JIT-EE calls, carried them through managed code, and rethrew them when control returned to native code. On Windows, the EE's managed exceptions are structured exceptions, which std::exception_ptr doesn't capture, so the EE got an SEHException instead of its own exception; and by the time of the rethrow, the EE had finished handling the original exception anyway. Now no managed code is on the stack while the JIT runs. The native side calls the JIT and the EE itself, and calls into managed code only to set up and finish the method context, to record the calls (the collector), or to count them (the counter); an exception that the EE throws propagates through native frames only, as with the native shims. The recording and forwarding code is generated by the ThunkGenerator. Co-authored-by: Cursor <cursoragent@cursor.com>
|
@jakobbotsch, there were two types of issues spotted by CI:
Could you please rerun |
|
/azp run runtime-coreclr superpmi-collect-test |
|
Azure Pipelines will not run the associated pipelines, because the pull request was updated after the run command was issued. Review the pull request again and issue a new run command. |
|
/azp run runtime-coreclr superpmi-collect-test |
|
Azure Pipelines: Successfully started running 1 pipeline(s). |
|
Can you validate that collecting and replaying a JIT crash (AV/segfault/other hardware exception) works properly with this change? |
…Unix The native tools got these through the PAL. Now a small fault guard handles the faults whose instruction is in the JIT while the thread is in a call to it, and leaves the others to the previously installed handlers (NativeAOT's, or the runtime's): the collector saves the method context of the crashing method before the process goes down, and superpmi reports the method as failed and goes on with the next one. Co-authored-by: Cursor <cursoragent@cursor.com>
|
Validated with a scratch JIT that faults (null write / div-by-zero, or
|


superpmi, mcs and the collector, counter and simple shims are now C#, published with NativeAOT under the same names, command lines, outputs and locations, so they are drop-in replacements; the native sources are deleted.