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
PR #131328 fixed a wasm ABI bug (#131299): the crossgen R2R ArgIterator under-aligned Vector128<T> arguments. The crossgen type system reports InstanceFieldAlignment == 8 for v128, while the runtime and interpreter 16-align v128 arguments via getClassAlignmentRequirement. The 8-byte desync accumulated per v128 argument, corrupting later arguments (e.g. the trailing generic-context byref) and producing a spurious NullReferenceException on the vectorized Base64Url.DecodeFromUtf8 path.
The fix landed without an automated regression test because there is no CI-runnable home for one today (see "Why deferred"). This issue tracks adding a deterministic crossgen ArgIterator unit test that locks in the invariant, so a future regression can't silently reintroduce the desync.
Why deferred (harness reality)
wasm R2R is not CI-runnable yet — there is no wasm corerun to exercise the path end-to-end.
The cDAC ARGITER stress harness cannot catch it.src/native/managed/cdac/tests/StressTests runs a debuggee under corerun (x64/x86/ARM) and compares cDAC ArgIterator<CdacTypeHandle>-derived GCRefMap blobs against the runtime's ComputeCallRefMap. The Wasm32 alignment code is a separate switch branch entered only for a Wasm32 TransitionBlock; an x64/ARM corerun run never enters it. So a stress run would go green with the wasm bug present — false confidence.
No existing xunit project reaches crossgen's ArgIterator<TypeHandle>.ILCompiler.ReadyToRun has no InternalsVisibleTo, and ILCompiler.ReadyToRun.Tests references crossgen2 with ReferenceOutputAssembly=false (build-only, no API access) and has no [Fact] files.
The fix is otherwise validated by equivalence (the pre-review fix and the reshaped fix produce byte-identical composites), a green browser run, and an artifact-level disassembly of WasmR2RToInterpreterThunk showing 16-aligned v128 slots for a v128-argument method.
Scaffolding required
A test project (or an addition to an existing one) that compile-references ILCompiler.ReadyToRun so it can see ArgIterator<TypeHandle>, ArgIteratorData<TypeHandle>, TypeHandle, and TransitionBlock.
InternalsVisibleTo from ILCompiler.ReadyToRun to that test project (these types are internal).
A crossgen TypeSystemContext with Target.Architecture == Wasm32 whose SystemModule resolves System.Runtime.Intrinsics.Vector128\1`.
Because this is standalone crossgen/ILCompiler infrastructure, it is best done as part of broader crossgen ArgIterator unit-test coverage rather than bolted on for a single test.
Test to add
The v128 argument must be preceded by an i64 so the 8-vs-16 alignment is observable (otherwise both alignments land the v128 at the same offset). Signature: static void M(long, Vector128<int>, ref int).
Variant B — inline construction (no BuildArgIterator dependency)
TransitionBlocktransitionBlock=TransitionBlock.FromTarget(context.Target.Architecture,context.Target.OperatingSystem==TargetOS.Windows,context.Target.IsApplePlatform,context.Target.Abi==TargetAbi.NativeAotArmel);varparameterTypes=newTypeHandle[signature.Length];for(inti=0;i<parameterTypes.Length;i++)parameterTypes[i]=newTypeHandle(signature[i]);varargIteratorData=newArgIteratorData<TypeHandle>(hasThis:false,isVarArg:false,parameterTypes,newTypeHandle(signature.ReturnType));varargit=newArgIterator<TypeHandle>(transitionBlock,argIteratorData,CallingConventions.ManagedStatic,hasParamType:false,hasAsyncContinuation:false,extraFunctionPointerArg:false,forcedByRefParams:newbool[parameterTypes.Length],skipFirstArg:false,extraObjectFirstArg:false,isWindows:context.Target.IsWindows,objectTypeHandle:newTypeHandle(context.GetWellKnownType(WellKnownType.Object)),intPtrTypeHandle:newTypeHandle(context.GetWellKnownType(WellKnownType.IntPtr)));// ... same GetNextOffset loop + assertions as Variant A.
Both variants are fix-shape-agnostic (they drive GetNextOffset() and check offsets; they never reference the removed RequiresAlign16OnWasm predicate), so they validate the invariant regardless of whether the fix is the predicate form or the GetFieldAlignment fold.
Interim evidence (already available)
WasmR2RToInterpreterThunk disassembly for a v128-argument method showing 16-aligned v128 slots (to be attached).
Equivalence: pre-review fix composites are byte-identical to the reshaped fix composites.
Green browser run of the relevant SIMD suite.
Note
This issue was authored with the assistance of GitHub Copilot.
Summary
PR #131328 fixed a wasm ABI bug (#131299): the crossgen R2R ArgIterator under-aligned
Vector128<T>arguments. The crossgen type system reportsInstanceFieldAlignment == 8for v128, while the runtime and interpreter 16-align v128 arguments viagetClassAlignmentRequirement. The 8-byte desync accumulated per v128 argument, corrupting later arguments (e.g. the trailing generic-context byref) and producing a spuriousNullReferenceExceptionon the vectorizedBase64Url.DecodeFromUtf8path.The fix landed without an automated regression test because there is no CI-runnable home for one today (see "Why deferred"). This issue tracks adding a deterministic crossgen ArgIterator unit test that locks in the invariant, so a future regression can't silently reintroduce the desync.
Why deferred (harness reality)
corerunto exercise the path end-to-end.src/native/managed/cdac/tests/StressTestsruns a debuggee undercorerun(x64/x86/ARM) and compares cDACArgIterator<CdacTypeHandle>-derived GCRefMap blobs against the runtime'sComputeCallRefMap. The Wasm32 alignment code is a separateswitchbranch entered only for a Wasm32TransitionBlock; an x64/ARMcorerunrun never enters it. So a stress run would go green with the wasm bug present — false confidence.ArgIterator<TypeHandle>.ILCompiler.ReadyToRunhas noInternalsVisibleTo, andILCompiler.ReadyToRun.Testsreferences crossgen2 withReferenceOutputAssembly=false(build-only, no API access) and has no[Fact]files.The fix is otherwise validated by equivalence (the pre-review fix and the reshaped fix produce byte-identical composites), a green browser run, and an artifact-level disassembly of
WasmR2RToInterpreterThunkshowing 16-aligned v128 slots for a v128-argument method.Scaffolding required
ILCompiler.ReadyToRunso it can seeArgIterator<TypeHandle>,ArgIteratorData<TypeHandle>,TypeHandle, andTransitionBlock.InternalsVisibleTofromILCompiler.ReadyToRunto that test project (these types are internal).TypeSystemContextwithTarget.Architecture == Wasm32whoseSystemModuleresolvesSystem.Runtime.Intrinsics.Vector128\1`.Because this is standalone crossgen/ILCompiler infrastructure, it is best done as part of broader crossgen ArgIterator unit-test coverage rather than bolted on for a single test.
Test to add
The v128 argument must be preceded by an
i64so the 8-vs-16 alignment is observable (otherwise both alignments land the v128 at the same offset). Signature:static void M(long, Vector128<int>, ref int).Wasm32 ArgIterator offset math:
_wasmOfsStack = ALIGN_UP(_wasmOfsStack, align); argOfs = OffsetOfArgs + _wasmOfsStack; _wasmOfsStack += ALIGN_UP(argSize, 8).long: align 8 -> rel 0, advance to 8.Vector128<int>(size 16): fixed align 16 -> rel 16, advance to 32. reverted align 8 -> rel 8, advance to 24.ref int: align 8 -> fixed rel 32; reverted rel 24.Expected: reverted crossgen ->
offsets == [0, 8, 24](asserts fail); fixed ->[0, 16, 32](pass).Variant A — via
GCRefMapBuilder.BuildArgIteratorVariant B — inline construction (no
BuildArgIteratordependency)Both variants are fix-shape-agnostic (they drive
GetNextOffset()and check offsets; they never reference the removedRequiresAlign16OnWasmpredicate), so they validate the invariant regardless of whether the fix is the predicate form or theGetFieldAlignmentfold.Interim evidence (already available)
WasmR2RToInterpreterThunkdisassembly for a v128-argument method showing 16-aligned v128 slots (to be attached).Note
This issue was authored with the assistance of GitHub Copilot.