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
The Wasm signature string (see "Wasm Signature String Encoding" in docs/design/coreclr/botr/readytorun-format.md) has no distinct token for the hidden generic context. WasmLowering.GetSignature emits it as the pointer char (i on wasm32, l on wasm64), which is indistinguishable from an explicit pointer-sized argument. The VM's GetSignatureKey (src/coreclr/vm/wasm/helpers.cpp) does the same when building thunk lookup keys.
Signature-keyed thunks (WasmR2RToInterpreterThunkNode, WasmInterpreterToR2RThunkNode, WasmImportThunk) only have the signature string, so they can recognize the generic context only by position: when the async continuation a immediately follows it. That is WasmLowering.HasGenericContextBeforeAsync, and the special casing built on it in WasmThunkArgLayout and RaiseSignature (including the roundtrip a<ctx> → <ctx>a swap). Without an async continuation this doesn't matter: the hidden-argument slot and the first argument slot are the same, so the layout is identical.
This positional inference is what let the generic context and async continuation get swapped in the first place (#133953, #134660, #133627, fixed in #134676). It was called out in review as a likely source of future bugs.
Proposal
Add a dedicated signature token for the hidden generic context and use it everywhere the semantic signature string is produced or parsed:
WasmLowering.GetSignature emits the token when LoweringFlags.HasGenericContextArg is set. The Wasm function type is unchanged (still one pointer-sized param).
WasmLowering.RaiseSignature parses it. This removes the roundtrip swap and HasGenericContextBeforeAsync.
WasmThunkArgLayout models the generic context as the hidden instantiation argument (methodRequiresInstArg) whenever the token is present, async or not.
The VM's GetSignatureKey emits the same token in its semantic (non-Wasm-calling-convention) key, so string lookups for the R2R↔interpreter thunks keep matching.
The portable call-helper tokenizer (InteropSignature / InterpToNativeGenerator) and readytorun-format.md learn the token.
Considerations
The thunk key strings change on both the crossgen2 and VM sides, so they must change together. Images compiled by an older crossgen2 would register keys that the newer runtime no longer looks up.
Signatures that differ only in having a generic context versus an explicit pointer argument would no longer share a thunk. This is a small image-size cost.
The structural V keys used by virtual dispatch thunks are unaffected.
Description
The Wasm signature string (see "Wasm Signature String Encoding" in
docs/design/coreclr/botr/readytorun-format.md) has no distinct token for the hidden generic context.WasmLowering.GetSignatureemits it as the pointer char (ion wasm32,lon wasm64), which is indistinguishable from an explicit pointer-sized argument. The VM'sGetSignatureKey(src/coreclr/vm/wasm/helpers.cpp) does the same when building thunk lookup keys.Signature-keyed thunks (
WasmR2RToInterpreterThunkNode,WasmInterpreterToR2RThunkNode,WasmImportThunk) only have the signature string, so they can recognize the generic context only by position: when the async continuationaimmediately follows it. That isWasmLowering.HasGenericContextBeforeAsync, and the special casing built on it inWasmThunkArgLayoutandRaiseSignature(including the roundtripa<ctx>→<ctx>aswap). Without an async continuation this doesn't matter: the hidden-argument slot and the first argument slot are the same, so the layout is identical.This positional inference is what let the generic context and async continuation get swapped in the first place (#133953, #134660, #133627, fixed in #134676). It was called out in review as a likely source of future bugs.
Proposal
Add a dedicated signature token for the hidden generic context and use it everywhere the semantic signature string is produced or parsed:
WasmLowering.GetSignatureemits the token whenLoweringFlags.HasGenericContextArgis set. The Wasm function type is unchanged (still one pointer-sized param).WasmLowering.RaiseSignatureparses it. This removes the roundtrip swap andHasGenericContextBeforeAsync.WasmThunkArgLayoutmodels the generic context as the hidden instantiation argument (methodRequiresInstArg) whenever the token is present, async or not.GetSignatureKeyemits the same token in its semantic (non-Wasm-calling-convention) key, so string lookups for the R2R↔interpreter thunks keep matching.InteropSignature/InterpToNativeGenerator) andreadytorun-format.mdlearn the token.Considerations
Vkeys used by virtual dispatch thunks are unaffected.Follow-up to #134676.
Note
This issue was drafted with GitHub Copilot.