Skip to content

MonoAOT: LLVM 23 regressions in FullAOT JIT test outcomes #129508

Description

@pavelsavara

Summary

Observed on PR branch emsdk-upgrade / #129299: runtime-llvm linux-x64 FullAOT runtime tests regress under LLVM 23 (e.g. nullabletypes returns Expected 100, Actual 666). Each affected test is disabled via [ActiveIssue("https://github.com/dotnet/runtime/issues/129508", ...)] on the emsdk-upgrade branch. The failures have multiple distinct root causes (see below).

All scenarios now have fixes in flight.

Failing scenarios

Fixes

  • nullabletypes[mono][amd64] Pass small straddling vtypes in registers in both backends #129702[mono][amd64] Pass small straddling vtypes in registers in the LLVM backend.
    Root cause: a ≤16-byte all-integer value type whose nested field straddles the 8-byte SysV eightbyte boundary (e.g. Nullable<T> with an 8-byte T) was passed byval by the partially-shared caller — which LLVM 18+ lowers onto the stack — while the concrete callee reads it from two integer registers, corrupting the value. Fixed by passing such vtypes in registers in the amd64 LLVM call-info. amd64-only.

  • Runtime_105619[mono] Fix re-entrant AssemblyLoadContext resolution stack overflow under full-AOT #129693[mono] Fix re-entrant AssemblyLoadContext resolution stack overflow under full-AOT.
    This test is not a codegen failure: its TestEntryPoint loads the assembly into a collectible AssemblyLoadContext and invokes a method via reflection (the Fuzzlyn payload itself NREs on uninitialized statics and is swallowed by try {} catch {}), so the only failure mode is a process crash during the ALC load/invoke — the re-entrant ALC resolution stack overflow that [mono] Fix re-entrant AssemblyLoadContext resolution stack overflow under full-AOT #129693 fixes. (Structural match; merged-harness confirmation pending.)

  • call05_large / call05_small[mono] AOT-compile methods that use the IL jmp opcode #129708[mono] AOT-compile methods that use the IL jmp opcode.
    Root cause: these tests jmp to another method from a method that also uses localloc. The non-llvm_only CEE_JMP path emitted an OP_TAILCALL and called DISABLE_AOT(), so the method was excluded from the AOT image; under full-AOT (aot-only) the call then tried to JIT it and failed. Fixed by emitting jmp as a plain call + return for AOT, keeping the method in the image. Pre-existing; newly exposed by the x64 Mono full-AOT leg.

  • WPF_3226[mono] AOT-compile marshalling wrappers for layout classes with class-typed fields #129710[mono] AOT-compile marshalling wrappers for layout classes with class-typed fields.
    Root cause: can_marshal_struct() in the AOT compiler did not handle MONO_TYPE_CLASS fields, so a [StructLayout] class whose fields are themselves marshalable layout classes (e.g. MINMAXINFO with two POINT fields) was treated as non-marshalable and its StructureToPtr/PtrToStructure wrappers were not emitted into the AOT image; the call then failed to JIT under aot-only. Fixed by recursing into can_marshal_struct() for class-typed fields. Pre-existing; newly exposed by the x64 Mono full-AOT leg.

  • b143840[mono] Keep in-flight exception alive across LLVM resume unwind #129713[mono] Keep in-flight exception alive across LLVM resume unwind.
    Root cause: when unwinding through an LLVM-compiled finally/fault handler, the in-flight exception object was stored as a raw pointer in ResumeState.ex_obj (marked /* FIXME: GC */) across the managed handler (e.g. the Monitor.Exit of a synchronized wrapper). That handler can reach a GC safepoint, and a moving GC relocates the exception, leaving the pointer stale; mono_resume_unwind then passes it to mono_object_isinst_checked during the catch search and dereferences a garbage MonoClass → intermittent SIGSEGV under load. Fixed by keeping the exception alive in a pinned GC handle across the resume window (as the llvmonly path already does). Pre-existing; newly exposed by the x64 Mono full-AOT leg.

  • UnitTest_GVM_TypeLoadException[mono] Defer NEWOBJ type-load failures to runtime in AOT mode #129715[mono] Defer NEWOBJ type-load failures to runtime in AOT mode.
    Root cause: each of the four RunInvalidTestN helpers does newobj of a type with an invalid covariant-return override. Resolving the constructor fails with a type-load error during AOT compilation, so the AOT compiler aborted and excluded the whole method from the image; under full-AOT (aot-only) the call then attempted a JIT compile and threw ExecutionEngineException instead of the expected TypeLoadException, which the test's catch did not handle. Fixed by deferring the type-load failure to a runtime throw in AOT (turning the method into one that throws TypeLoadException, as the ldfld path already does), matching the JIT behavior. The IsNativeAot annotation is kept (NativeAOT has the same limitation). Pre-existing; newly exposed by the x64 Mono full-AOT leg.

Note

This issue description was updated with the assistance of GitHub Copilot.

Metadata

Metadata

Assignees

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions