Conversation
Preserve variable debug information produced by RyuJIT for ReadyToRun WebAssembly code, including scope ranges across relooper block ordering and packed wasm local register locations. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The end-to-end variable-debug-info validation exposed the hidden wasm portable-entry-pointer argument as a source local. The argument is appended after user arguments, but unlike the wasm stack-pointer argument it was not recorded or excluded by compMap2ILvarNum. AddDoubles therefore reported the hidden i32 argument as source local 0, alongside the real f64 parameters. Record the argument's local number when it is created, map it to UNKNOWN_ILNUM, and account for it when mapping later internal locals back to IL variable numbers. Replace the count-only wasm R2R checks with complete exact records for the AddDoubles parameters and SumWithFinally local: variable identity, native range, location kind, packed wasm local, and frame-pointer-relative offset. Mutate the local-index bits of one packed register and prove the exact oracle rejects the corrupted record. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
|
Azure Pipelines: Successfully started running 7 pipeline(s). 9 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
Contributor
|
Tagging subscribers to this area: @steveisok, @tommcdon, @dotnet/dotnet-diag |
Contributor
|
Tagging subscribers to 'arch-wasm': @lewing, @pavelsavara |
Define the packed WASM debug-register bit layout in ICorDebugInfo and have the JIT derive its register masks from that shared encoding contract. Assert that the JIT register representation and WasmValueType count remain compatible with the debug-info format. Document that the static ReadyToRun reader's compiled-in shift must move with a versioned R2R debug-info format change, since it has no live target descriptor from which to discover a different layout. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
lewing
force-pushed
the
lewing-r2r-cdac-for-wasm
branch
from
September 14, 2026 17:53
685807e to
d5f26e6
Compare
Publish the shared WASM register type shift and value-type count through the target data descriptor so version-skewed readers can reject incompatible variable debug information. Document the producer-owned encoding and extend the static ReadyToRun reader coverage for reserved and unsupported value-type codes. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Keep the WASM stack-walk context's SP, virtual IP, and logical frame pointer coherent with the native RtlVirtualUnwind and Frame::UpdateRegDisplay paths. - Derive IP and logical FP for the R2R InlinedCallFrame marker. - Use TransitionBlock's saved R2R SP, including lazy return-address recovery, and avoid treating the generic fallback argument area as a shadow frame. - Copy the full serialized WASM context for software exception frames. - Detect reverse P/Invoke from GC info so native caller bytes are never probed as a possible R2R shadow frame; retain caller SP with IP/FP cleared. - Expose a documented StackWalk function identity containing the raw shared table index, owning module, image runtime-function index, and nullable funclet classification. Reject native/interpreter handles before reading shadow-frame memory while preserving a known raw index when range lookup itself fails. Add native-layout transition fixtures, real adjacent root/funclet range coverage, parent/nested/terminator/localloc unwind coverage, and false-frame reverse-P/Invoke tests. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Decode the WASM register representation produced by ReadyToRun debug info as symbolic WasmLocal/WasmLocalPair locations with an encoding-specific value type. Engine-owned locals remain symbolic; stack locations resolve from the logical frame pointer already maintained by the StackWalk context. Advertise the register type shift and supported value count as WASM cDAC data descriptor globals. Require and validate those values before reading a non-empty variable stream so a reader/target format mismatch fails explicitly instead of plausibly decoding the wrong local or type. Reject malformed packed registers and impossible shift/count combinations. Keep current stack encoding exact: base register 2 means context FP. Remove speculative decoded stack-base locals whose values the reader could not honor. Preserve the original packed VarLoc through DacDbi for consumers that need the ICorDebugInfo representation. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Characterize the existing ClrDataValue behavior relied on by symbolic WASM locals. A value with no native locations reports zero locations, while GetBytes and GetAddress fail instead of returning a successful plausible-zero value. This commit adds the regression boundary only; the zero-location failure behavior predates the WASM variable reader. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
lewing
force-pushed
the
lewing-r2r-cdac-for-wasm
branch
from
September 14, 2026 18:53
d5f26e6 to
69ad34f
Compare
This was referenced Sep 14, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Decode ReadyToRun WebAssembly variable locations through cDAC while keeping wasm unwind state and function identity in the shared StackWalk contract.
WasmLocal(Index, WasmDebugValueType)/WasmLocalPairvalues.VLT_STKfrom the logical frame pointer already stored in the frame context.IStackWalk.ICorDebugInfo::VarLocdata through DacDbi.0.Temporary stack
Important
This draft depends on #133086 and temporarily includes its producer commits because GitHub cannot target the fork-only
lewing-r2r-wasm-debug-infobranch from an upstream pull request. After #133086 merges, those commits will disappear when this branch is rebased ontomain.Lower-layer #133086 head:
bf831077c6d51ee05f570f86ec534f52a058747c.Included producer commits:
e8a353f9c13—[wasm] Preserve R2R variable debug info3979515fee3—[wasm] Exclude portable entry pointer from debug variables98296005063—[wasm] Centralize variable debug register encoding constantsbf831077c6d—[wasm] Advertise variable debug register encodingAll producer constants and target descriptor globals are now owned by #133086. The consumer history is exactly three commits:
f1bce1b092d—[cdac][wasm] Maintain frame context and expose function identity4468c51ff7e—[cdac][wasm] Decode ReadyToRun variable locations69ad34f88ef—[cdac][wasm] Characterize unavailable variable valuesNo native stack registration is intended.
StackWalk context and function identity
Native WASM
RtlVirtualUnwindupdatesInterpreterSP,InterpreterIP, and the funclet-resolvedInterpreterFPtogether. cDAC now maintains the same invariant:InlinedCallFramemarkerINLINED_PINVOKE_FROM_R2Rderives IP/FP fromCallSiteSPrather than treating marker1as an IP.TransitionFrameuses its saved R2R SP, lazily derives a zero return address, and derives FP only when the saved SP/IP are valid. The genericTransitionBlock + sizeofargument-area fallback is never parsed as a shadow frame.IStackWalk.GetWasmFunctionIdentityis valid only for ReadyToRun frameless frames and returns:Runtime shared-table indices are globally relocated by each module's
tableBase, but V8func_indexvalues are module-local. Consumers therefore need both the owning module and image-relative runtime-function index to select the correct wasm script and translate through its element section. Native markers and interpreter frames are rejected before any stack bytes are read.The prior standalone public
WasmFrameLayouthelper has been removed; wasm-specific unwind helpers remain internal, consistent with #130988 review feedback.Variable locations
RyuJIT packs a local index and JIT debug value type into the 32-bit
RegNumpayload. The encoding constants now live inICorDebugInfo; cDAC receives the shift and value-count through WASM-only numeric descriptor globals:WasmDebugRegisterTypeShiftWasmDebugValueTypeCountA non-empty WASM Vars stream with missing, mismatched, or impossible encoding metadata fails explicitly. Invalid packed registers do not fall back to ordinary native register locations.
WasmDebugValueTypeintentionally names JIT debug-encoding vocabulary, not the complete WebAssembly specification type set. Managed references currently collapse to the JIT's machineI32/I64representation; unknown codes remain unsupported.Current JIT stack locations encode base register
2. cDAC resolves those locations fromcontext.FramePointer. SpeculativeWasmBaseLocalfields were removed because a future producer that named a real FP local would require explicit/versioned semantics, not silently ignoring that local's value.Exact producer and consumer validation
The pre-#133086 browser baseline contained 5,323 functions with debug bounds and exactly 0 variable entries.
Exact producer records were validated for every range in
AddDoublesandSumWithFinally, including:Raw variable bytes from the producer image passed through cDAC and
DacDbiImpl.ConvertToVarLocwith exact packed registers and stack offsets.Live root-frame validation
The live
$var1value varied across processes, confirming that availability plus exact function/local/type is the correct assertion—not an exact process-specific raw value.Live funclet stack validation
A no-opt
SumWithFinallyspecimen was paused inside its real finally funclet:This validates the containing-method path and parent-frame stack address without using #131161's synthetic virtual-IP base as live truth.
Tests
tools.cdactests: UnitTests, DataGeneratorTests, and UsageTests all passed.clr+libsbuild: 0 warnings, 0 errors.Guard mutations were visibly applied and each went red before restoration:
_totalSize == 0GetBytesguard returned successful plausible zero and failed its characterization.External consumer compatibility
Tank validated the redesigned reader through every cDAC surface currently exposed by the
Blazor-Playground/nesmCDP/MCP consumer:Nesm.CdpcDAC gates: 10 passed, 0 failed, 0 skipped.Nesm.McpcDAC gates: 4 passed, 0 failed, 0 skipped.cdac_support_unavailable, not zero discovery or a fabricated value.Sample.Test.PrintMeaning, module-local function index6, instruction/break offset1627, V8$var1index1, typei32, nonzero value, andCoreclr/WasmcDAC provenance.Nesm does not yet expose
IDebugInfo.GetMethodVarInfo/ DacDbiDoVars, so the finalWasmLocal(Index=1, I32)descriptor-to-$var1join remains a named nesm follow-up. The restacked consumer patch undersrc/native/managed/cdachas the same stable patch-id as the externally validatedd5f26e6consumer logic; this restack only moved producer-owned constants and descriptor advertisement into the updated #133086 lower layer.Accuracy and limitations
ClrDataValuebehavior; it does not introduce_totalSize == 0 -> E_NOINTERFACE.VLT_STK_BYREFbest-effort dereference behavior is unchanged and may retain address zero on an unreadable pointer._legacyImplcomparison.v128encodings have synthetic decode coverage but their live V8 representations remain unproven.GetMethodVarInfo, so the descriptor-to-CDP join remains a nesm follow-up.Producer ownership and redesigned-reader external validation are satisfied. The PR remains draft pending Larry's decision and lower-layer #133086 readiness; it is not being marked ready by this update.
Note
This pull request description was generated with GitHub Copilot.