[mono][wasm] Fix delegate over static abstract interface method under gsharedvt - #131880
Conversation
… gsharedvt For a static abstract interface method resolved through gsharedvt, ldftn + delegate creation fell back to a generic newobj ctor-call. Under llvmonly this called the 3-parameter runtime delegate constructor with an extra generic-sharing argument, producing a wasm 'function signature mismatch' trap in the containing cctor. Create the delegate directly via mini_llvmonly_init_delegate instead. Fixes dotnet#130545
|
Azure Pipelines: Successfully started running 4 pipeline(s). 12 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
There was a problem hiding this comment.
Pull request overview
This PR adjusts Mono’s ldftn + delegate construction peephole under llvmonly to correctly handle delegates created over static abstract interface methods resolved via gsharedvt (runtime rgctx lookup), and adds a regression test covering the reported shape.
Changes:
- Add an
llvmonly-only delegate construction fast-path forgshared_static_virtualthat resolves the implementation viaMONO_RGCTX_INFO_VIRT_METHOD, setsdel->method, and initializes the delegate viamini_llvmonly_init_delegate. - Add a new loader regression test project and test that mirrors the static-abstract +
Lazy<>delegate creation pattern and reaches the generic instantiation via reflection.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| src/mono/mono/mini/method-to-ir.c | Adds an llvmonly gshared-static-virtual delegate creation path to avoid the generic newobj fallback. |
| src/tests/Loader/classloader/StaticVirtualMethods/Regression/GitHub_130545.cs | Adds a regression test reproducing the delegate-over-static-abstract-through-gsharedvt shape. |
| src/tests/Loader/classloader/StaticVirtualMethods/Regression/GitHub_130545.csproj | Adds a minimal test project to build the new regression test. |
|
Tagging subscribers to 'arch-wasm': @lewing, @pavelsavara |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.
Suppressed comments (2)
src/tests/Loader/classloader/StaticVirtualMethods/Regression/GitHub_130545.cs:62
- Prefer Unsafe.BitCast for same-size type punning instead of Unsafe.As. This avoids byref reinterpretation and matches the repo guidance for this pattern while preserving the intended "packed" shape.
public uint PackedValue
{
get => Unsafe.As<Rgba32, uint>(ref this);
set => Unsafe.As<Rgba32, uint>(ref this) = value;
}
src/mono/mono/mini/method-to-ir.c:11975
- The new llvmonly gshared-static-virtual delegate fast path skips the UnmanagedCallersOnly guard that the existing ldftn+delegate optimization applies. If this IL shape ever occurs for an [UnmanagedCallersOnly] method, this path would now create a delegate instead of failing consistently.
if (gshared_static_virtual && cfg->llvm_only && (sp > stack_start) && (next_ip + 4 < end) && ip_in_bb (cfg, cfg->cbb, next_ip) && (next_ip [0] == CEE_NEWOBJ)) {
MonoMethod *ctor_method = mini_get_method (cfg, method, read32 (next_ip + 1), NULL, generic_context);
if (ctor_method && (m_class_get_parent (ctor_method->klass) == mono_defaults.multicastdelegate_class)) {
|
/azp run runtime-wasm |
|
Azure Pipelines: Successfully started running 1 pipeline(s). |
Fixes #130545
Problem
Under Mono WebAssembly AOT (
llvmonly), reaching a static field initialized from a static-abstract interface method through a value-type generic instantiation (gsharedvt) traps with:The original report is the SixLabors.ImageSharp
PixelOperations<TPixel>shape, wherePixelOperations<TPixel>..cctorbuilds aLazy<>from the static-abstract method groupTPixel.CreatePixelOperations. It is AOT-only — the same code runs fine interpreted, and disablingRunAOTCompilationavoids it.Root cause
In
mono_method_to_ir, theCEE_LDFTN+ delegate-newobjpeephole that normally routes delegate creation through the optimizedllvmonlypath (mini_llvmonly_init_delegate) is guarded by!gshared_static_virtual. For a static-abstract method resolved via gsharedvt, that guard skips the fast path and falls back to a genericnewobj Func<T>::.ctorcall.Because
mono_method_needs_static_rgctx_invoke(Func<T>::.ctor)returnstrue, thecall_indirectin the containing cctor is emitted with an extra generic-sharing argument (a 4-parameter signature), but the real target is the 3-parameter runtime delegate constructor (ves_icall_mono_delegate_ctor). A 4-argument call to a 3-parameter function is a hard wasm signature mismatch, so the cctor traps. This is the only case that uses the genericnewobjdelegate-ctor fallback underllvmonly, which is why only this shape reproduces.Fix
Handle
gshared_static_virtualin thellvmonlydelegate-ctor path: resolve the target viaMONO_RGCTX_INFO_VIRT_METHOD, setdel->method, and create the delegate directly throughmini_llvmonly_init_delegate(which derivesmethod_ptr/invoke_implcorrectly), instead of the genericnewobjconstructor call. The change is gated oncfg->llvm_only.Test
Adds a positive regression
[Fact]underLoader/classloader/StaticVirtualMethods/Regressionthat mirrors thePixelOperations<TPixel>shape and reaches it throughMakeGenericMethodto stay on the shared gsharedvt path.Verified against the Mono wasm AOT browser sample:
function signature mismatch,WASM EXIT 1WASM EXIT 0The reproduction is AOT-heuristic sensitive (documented in the test): reducing the surrounding types further lets the JIT fully specialize the generic method and masks the bug.
Note
This PR description was drafted with GitHub Copilot.