You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
If you can get past these asserts and use #125752 to get past the WASM_NYI, and are using @kg's test harness, you will then hit some validation errors as the Wasm engine tries to ingest our codegen.
(1) not enough arguments on the stack for memory.copy (need 3, got 2)
[CompileError: WebAssembly.compile(): Compiling function #42 failed: not enough arguments on the stack for memory.copy (need 3, got 2) @+2439733]
// TODO-WASM: Zero-extend the 2nd operand for shifts and rotates as needed when the 1st and 2nd operand are
// different types. The shift operand width in IR is always TYP_INT; the WASM operations have the same widths
// for both the shift and shiftee. So the shift may need to be extended (zero-extended) for TYP_LONG.
Fix will be something like:
@@ -1477,6 +1477,14 @@ void CodeGen::genCodeForShift(GenTree* tree)
// different types. The shift operand width in IR is always TYP_INT; the WASM operations have the same widths
// for both the shift and shiftee. So the shift may need to be extended (zero-extended) for TYP_LONG.
+ if (treeNode->TypeIs(TYP_LONG))+ {+ assert(treeNode->gtGetOp2()->TypeIs(TYP_INT));+ // Zero-extend the shift amount to 64 bits for long shifts/rotates.+ // Wasteful if the amount was a constant, perhaps we should contain it if so.+ GetEmitter()->emitIns(INS_i64_extend_u_i32);+ }+
(3) call_indirect[2] expected type i64, found i32.load of type i32
[CompileError: WebAssembly.compile(): Compiling function #286 failed: call_indirect[2] expected type i64, found i32.load of type i32 @+1247187]
We think we are emitting i32.trunc_sat_f32_s which should be 0xfc00...
This is the case we noted earlier where representing opcodes the way we do leads to ambiguity as to whether this is a 1-byte or 2-byte opcode. Here we just emit the 0xFC.
Not sure why it changed from 3 to 4, investigating...
Ah we have an incorrect sig here. There are 5 args but we type this as ( i32, i32, i32, i32 ) -> none. This method has a generic context argument, likely we're not accounting for it somewhere.
Guessing this is over on the JIT host side in WasmFuncType -- the method desc sig probably doesn't include the dictionary parameter for generic methods.
Guessing this is over on the JIT host side in WasmFuncType -- the method desc sig probably doesn't include the dictionary parameter for generic methods.
Yeah I haven't seen this accounted for anywhere in any of the code we've written so far. I was assuming it was accounted for elsewhere in the VM as part of the signature.
and we seem to have an assumption that if the source of a block store is TYP_INT then the source must be an INIT_VAL.
Looking at this one it seems like it's just an existing bug in the JIT that isn't specific to wasm. I'm not sure what the fix is offhand. I could remove the assert, but I think that's wrong and the assert is flagging that this TYP_INT check is the wrong way to identify an initblk. Is there some non-obvious reason why the check in the assert isn't the check we actually do?
Assertion failed '(temporaryRegs.Count == 0) && "Some temporary regs were not consumed/released"'
in System.ValueTuple`4[System.__Canon,System.__Canon,System.__Canon,System.__Canon]:System.IComparable.CompareTo(System.Object):int:this'
during 'Linear scan register alloc' (IL size 44; hash 0x5cb9412b; FullOpts)
Here we have an ADD as the dest of a block store, that we rewrite to a write barrier call. We mark it as multiply used in lower, but never collect up the references later on.
snipped
So this store is nonfaulting and we don't need to request a temp for the dest.
Seems like this similar to the issue referred to here, though the twist here is that we generate a write barrier call.
Speaking of write barriers, if we want them to have a simplified calling convention (no $sp) then we must do any null checks beforehand, as they will be unable to throw an exception.
I have a fix for this but it breaks other stuff. Will keep poking at it, but I feel like it should 'just work' to request temporaries in this case. Optimizing out the temporary for every single possible scenario is going to make the code for this complex and fragile.
With local changes to turn the last few asserts into NYIs, here's where we're at for release S.P.CoreLib:
NYI_WASM: genFuncletProlog
NYI_WASM: genCallFinally
NYI_WASM: Opcode not implemented (CATCH_ARG)
NYI_WASM: alloc local frame for reverse pinvoke
NYI_WASM: emit.h:3925
NYI_WASM: gentree.cpp:16961
There are around 1500 NYIs in total and most of them look like EH. Presumably once EH is implemented we'll see more failures crop up in the code that's hidden inside EH-containing methods.
For the first one above we're collecting references in a node more than once, since the requests are not adjacent in time:
Requesting internal int register for [000266]
Collected [000266]
Collected [000265]
Consumed a temporary reg for [000265]: binop overflow check
Collected [000263]
Consumed a temporary reg for [000263]: binop overflow check
Requesting internal int register for [000267]
Collected [000267]
Collected [000266]
Consumed a temporary reg for [000266]: binop overflow check
Activity
AndyAyersMS commentedon Mar 18, 2026
FYI @dotnet/jit-contrib
[-][Wasm RyuJit] assert crossgenning wasm debug SPC[/-][+][Wasm RyuJit] asserts crossgenning wasm debug SPC[/+]AndyAyersMS commentedon Mar 19, 2026
Debug SPC Wasm Validation Errors
If you can get past these asserts and use #125752 to get past the
WASM_NYI, and are using @kg's test harness, you will then hit some validation errors as the Wasm engine tries to ingest our codegen.(1) not enough arguments on the stack for memory.copy (need 3, got 2)
which seems to be from this single instance:
Here's a bit of the Wasm stream...
JIT IR here is
Looks like we are not properly handling a contained source in codegen?
(2) i64.shl[1] expected type i64, found i32.const of type i32
This is from
wasm is
and codegen is
Think this is just a semantic mismatch, in our trees the shift amount is probably always TYP_INT.
And indeed:
runtime/src/coreclr/jit/codegenwasm.cpp
Lines 1471 to 1473 in 0d2f781
Fix will be something like:
(3) call_indirect[2] expected type i64, found i32.load of type i32
This is
also seen in
think it is this bit of wasm
and this bit of IR
TimeSpanis 8 bytes, so the call sig looks to be correct.Not sure yet why we are passing those args as
ints...(4) i64.and[0] expected type i64, found i64.eq of type i32
wasm is
Wasm relops always produce
i32results. Should be a simple fix.(5) i32.clz[0] expected type i32, found i64.load of type i64
wasm is
Seems like it should also be as simple fix... need to dispatch based on source type, and then truncate the result to 32 bits.
kg commentedon Mar 19, 2026
I'll take a look at the two block store issues.
AndyAyersMS commentedon Mar 19, 2026
I have fixes for validation cases (2-5) above. Am going to keep pushing on validation for a bit before I PR these.
AndyAyersMS commentedon Mar 19, 2026
With those the first 1530 or so of methods validate, but error case (3) starts to dominate and is painful to work around. Let me see if I can fix it.
Using the first repro, things look good heading into morph:
TimeSpanis a struct that wraps alongso indeed is passed and returned by value for Wasm.But then things go off the rails:
Fix seems fairly simple, just use the seg type for passing "in register" structs for Wasm.
AndyAyersMS commentedon Mar 19, 2026
More Debug SPC Wasm Validation Errors
Now hitting
(6) invalid numeric opcode: 0xfc41
We think we are emitting
i32.trunc_sat_f32_swhich should be0xfc00...This is the case we noted earlier where representing opcodes the way we do leads to ambiguity as to whether this is a 1-byte or 2-byte opcode. Here we just emit the
0xFC.runtime/src/coreclr/jit/instrswasm.h
Line 224 in aeffb4c
(7) invalid local index: 3
(with fixes for 2-5, bailouts for the asserts and (1), and a workaround for (6) we now have 11,000+ methods passing validation).
Not sure why it changed from 3 to 4, investigating...
Ah we have an incorrect sig here. There are 5 args but we type this as
( i32, i32, i32, i32 ) -> none. This method has a generic context argument, likely we're not accounting for it somewhere.Guessing this is over on the JIT host side in
WasmFuncType-- the method desc sig probably doesn't include the dictionary parameter for generic methods.kg commentedon Mar 20, 2026
Yeah I haven't seen this accounted for anywhere in any of the code we've written so far. I was assuming it was accounted for elsewhere in the VM as part of the signature.
kg commentedon Mar 20, 2026
Looking at this one it seems like it's just an existing bug in the JIT that isn't specific to wasm. I'm not sure what the fix is offhand. I could remove the assert, but I think that's wrong and the assert is flagging that this TYP_INT check is the wrong way to identify an initblk. Is there some non-obvious reason why the check in the assert isn't the check we actually do?
23 remaining items
AndyAyersMS commentedon Mar 31, 2026
This looks similar/same as #125756 (comment) -- a bit more detail there.
[Wasm RyuJIT] Partially implement genStructReturn (#126326)
kg commentedon Mar 31, 2026
I have a fix for this but it breaks other stuff. Will keep poking at it, but I feel like it should 'just work' to request temporaries in this case. Optimizing out the temporary for every single possible scenario is going to make the code for this complex and fragile.
kg commentedon Apr 1, 2026
With local changes to turn the last few asserts into NYIs, here's where we're at for release S.P.CoreLib:
There are around 1500 NYIs in total and most of them look like EH. Presumably once EH is implemented we'll see more failures crop up in the code that's hidden inside EH-containing methods.
[Wasm RyuJIT] Set wasm's vector width to 128 bits (#126375)
[Wasm RyuJIT] Fix for assert in obscure block store scenario (#126392)
kg commentedon Apr 8, 2026
With recent changes to RA there are new fairly common asserts (representative samples here)
Plus some that don't look related to the RA changes
AndyAyersMS commentedon Apr 8, 2026
For the first one above we're collecting references in a node more than once, since the requests are not adjacent in time:
[Wasm RyuJIT] Fix for assert in obscure block store scenario (dotnet#…
[Wasm RyuJit] fix funclet prolog and epilog codegen (#126663)
AndyAyersMS commentedon Apr 23, 2026
Believe these are all fixed now.