Skip to content

[wasm R2R] Align 8-byte stack locals and keep frames 16-aligned#129815

Merged
AndyAyersMS merged 1 commit into
dotnet:mainfrom
AndyAyersMS:fix-wasm-8byte-align-129802
Jun 25, 2026
Merged

[wasm R2R] Align 8-byte stack locals and keep frames 16-aligned#129815
AndyAyersMS merged 1 commit into
dotnet:mainfrom
AndyAyersMS:fix-wasm-8byte-align-129802

Conversation

@AndyAyersMS

Copy link
Copy Markdown
Member

Fixes #129802.

Problem

On the WebAssembly CoreCLR + ReadyToRun (crossgen2-wasm) configuration, 64-bit
JS interop values (long/BigInt, double, DateTime, Int52) were corrupted
crossing the managed↔JS boundary: corrupted = (original & 0xFFFFFFFF) << 32. The JS
side reads/writes these slots through the 8-byte HEAP64/HEAPF64 typed-array views
(addr >>> 3), which require 8-byte alignment; the marshaler buffer was landing at a
4-mod-8 address, so the view addressed the wrong word.

Root cause

wasm is TARGET_WASM32 (4-byte pointers) and does not define TARGET_64BIT, so:

  • The per-local 8-byte alignment padding in lvaAllocLocalAndSetVirtualOffset was gated
    on TARGET_64BIT and compiled out, leaving 8-byte stack locals only 4-byte aligned.
  • lvaAlignFrame was a no-op for wasm, so the frame size was only 4-byte aligned. Since
    the shadow stack pointer is threaded through calls (calleeSP = callerSP - frameSize),
    a single 4-mod-8-sized frame drifts every deeper frame — and every stackalloc'd
    marshaler buffer — out of 8-byte alignment.

Fix

  • Enable the existing 8-byte-local alignment for TARGET_WASM.
  • Round compLclFrameSize up to STACK_ALIGN in lvaAlignFrame for wasm so the shadow
    SP stays aligned across calls.

Validation

  • The wasm cross-JIT rebuilds clean.
  • crossgen2-wasm JitDump of a method with the marshaler pattern (address-taken long +
    stackalloc Span) confirms every 8-byte local now lands at an 8-aligned frame offset
    and the frame size is a multiple of 16 (the Pad … size=8 events only fire from the
    newly enabled code block).
  • A JSImport BigInt/double echo repro passes on the interpreter (matching the
    issue's interpreter baseline).

A black-box R2R pass/fail is best covered by the existing
System.Runtime.InteropServices.JavaScript.UnitTests suite on the coreclr-wasm-R2R lane.

Note

This PR was generated with the assistance of GitHub Copilot and reviewed before posting.

8-byte locals were only 4-byte aligned on wasm, so JS interop reads through
the 8-byte HEAP64/HEAPF64 views landed on the wrong word. Align >=8 byte
locals to 8 and round the frame to STACK_ALIGN. Fixes dotnet#129802.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates CoreCLR JIT frame layout for WebAssembly targets to ensure (a) stack locals requiring 8-byte alignment are actually 8-byte aligned, and (b) the overall frame size stays aligned to STACK_ALIGN so alignment doesn’t drift across nested calls.

Changes:

  • Enable the existing “align >=8-byte locals” padding logic for TARGET_WASM in lvaAllocLocalAndSetVirtualOffset.
  • Implement TARGET_WASM frame-size alignment in lvaAlignFrame by rounding compLclFrameSize up to STACK_ALIGN (and reserving worst-case padding during non-final layout passes).

@AndyAyersMS

Copy link
Copy Markdown
Member Author

@pavelsavara I can't fully validate this, so give it a try if you can.

@adamperlin PTAL
fyi @dotnet/wasm-contrib

@AndyAyersMS AndyAyersMS requested a review from adamperlin June 24, 2026 18:29
@pavelsavara

Copy link
Copy Markdown
Member
image

🎉

@pavelsavara pavelsavara left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks

@AndyAyersMS AndyAyersMS added area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI arch-wasm WebAssembly architecture and removed area-VM-coreclr labels Jun 24, 2026
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch
See info in area-owners.md if you want to be subscribed.

@AndyAyersMS AndyAyersMS merged commit 787e0ac into dotnet:main Jun 25, 2026
157 checks passed
pavelsavara added a commit to pavelsavara/runtime that referenced this pull request Jun 25, 2026
@dotnet-milestone-bot dotnet-milestone-bot Bot added this to the 11.0-preview7 milestone Jun 25, 2026
AndyAyersMS added a commit that referenced this pull request Jun 27, 2026
…29917)

#129815's alignment padding displaced the fixed-offset wasm EH slots and
broke EH dispatch. Shift them down past the pad.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

arch-wasm WebAssembly architecture area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[wasm][R2R] 64-bit JS interop values (long/BigInt, double) corrupted - marshaler buffer not 8-byte aligned for HEAP64

3 participants