Skip to content

[wasm] Use ordinary struct alignment for Int128/UInt128 on wasm - #131421

Closed
lewing wants to merge 2 commits into
dotnet:mainfrom
lewing:wasm-int128-struct-alignment
Closed

[wasm] Use ordinary struct alignment for Int128/UInt128 on wasm#131421
lewing wants to merge 2 commits into
dotnet:mainfrom
lewing:wasm-int128-struct-alignment

Conversation

@lewing

@lewing lewing commented Jul 27, 2026

Copy link
Copy Markdown
Member

Int128/UInt128 were given a 16-byte alignment requirement on wasm by both the VM and crossgen2, annotated // sizeof(v128). Int128 is not a v128 — the only 128-bit value type in WebAssembly is v128, and WasmLowering.IsWasmV128Type recognizes only Vector128<T> and a 128-bit Vector<T>, so Int128 uses the generic by-reference struct ABI.

Every other alignment override in CheckForSystemTypes exists because the managed type corresponds to a fundamental data type in the target ABI (Vector128<T>__m128, and so on). Int128 has no counterpart on wasm, so it takes ordinary struct alignment — the 8 its two ulong fields produce. On the crossgen2 side that means routing wasm through the existing 32-bit path alongside ARM.

Symptom

The 16 was observable as wrong codegen at the interpreter→R2R boundary:

Int128 a = 1, b = 1;
a == b          // True
a.Equals(b)     // False   <-- R2R only
a.CompareTo(b)  // -1      <-- R2R only

Root cause

Wasm signature encoding spells a by-reference struct argument as S<N>, and RaiseSignature resolves S16 through GetCachedStructOfSize, which keys only on size and keeps the first struct seen at that size. The thunk therefore used that struct's 8-byte alignment while the interpreter used Int128's 16, placing the argument at argsBase+24 instead of argsBase+32. Equals and CompareTo read a shifted value; operator == takes its operands differently and was unaffected.

This also makes S<N> sound rather than accidentally correct. Single-field structs wrapping a v128 are unwrapped by LowerType and encode as V, and a v128 field is the only source of 16-byte alignment on wasm — so once Int128 takes its natural alignment, nothing reaching S<N> requires more than 8.

The VM and crossgen2 must change together or managed field layout desyncs.

Validation

Browser wasm, against builds with and without the change, rebuilding both corerun and the R2R composite for each variant:

R2R off R2R on
Int128 repro, before correct Equals=False, CompareTo=±1 on equal values
Int128 repro, after correct correct

System.Text.Json, the Int128-bearing classes, 1496 tests, run on a branch carrying the other outstanding wasm R2R fixes so the suite completes:

R2R off R2R on
before 2 failed (0 Int128) 20 failed (15 Int128)
after 2 failed (0 Int128) 2 failed (0 Int128)

Those 2 fail with R2R off in both arms — pre-existing and unrelated.

Microsoft.Bcl.Memory 550/550 and System.Runtime.CompilerServices.Unsafe 128/128, R2R on and off.

Alternative considered

Encoding Int128 as V instead does not work. V is not an alignment tag — it selects a different calling convention, passing the value in a wasm v128 local rather than by reference. The JIT has no notion of Int128 at all, so R2R code faults with RuntimeError: function signature mismatch. Confirmed experimentally.

clang on wasm32 agrees with the by-reference treatment: it legalizes __int128 into two i64 parameters rather than any 128-bit entity, and passes an equivalent 16-byte struct as byval align 8. _Alignof(__int128) == 16 there is a memory-layout artifact, not a calling-convention requirement, since there is no 128-bit slot to align to.

wasm-only by construction — the VM change is inside #elif defined(TARGET_WASM) and the crossgen2 change adds TargetArchitecture.Wasm32 to an existing condition.

Note

This pull request was created with the assistance of GitHub Copilot.

Int128/UInt128 were given a 16-byte alignment requirement on wasm by both the VM
and crossgen2, annotated "sizeof(v128)". Int128 is not a v128: the only 128-bit
value type in WebAssembly is v128, and WasmLowering.IsWasmV128Type recognizes
only Vector128<T> and a 128-bit Vector<T>, so Int128 uses the generic
by-reference struct ABI.

Every other alignment override in CheckForSystemTypes exists because the managed
type corresponds to a fundamental data type in the target ABI (Vector128<T> to
__m128, and so on). Int128 has no counterpart on wasm, so it takes ordinary
struct alignment, which is the 8 its two ulong fields produce. On the crossgen2
side that means routing wasm through the existing 32-bit path.

The 16 was observable as wrong codegen at the interpreter-to-R2R boundary. Wasm
signature encoding spells a by-reference struct argument as S<N>, and
RaiseSignature resolves S16 through GetCachedStructOfSize, which keys only on
size and keeps the first struct seen at that size. The thunk therefore used that
struct's 8-byte alignment while the interpreter used Int128's 16, placing the
argument at argsBase+24 instead of argsBase+32. Int128.Equals and CompareTo then
read a shifted value while operator== was unaffected.

This also makes S<N> sound rather than accidentally correct: single-field structs
wrapping a v128 are unwrapped by LowerType and encode as V, and a v128 field is
the only source of 16-byte alignment on wasm, so once Int128 takes its natural
alignment nothing reaching S<N> requires more than 8.

The VM and crossgen2 must change together or managed field layout desyncs.

Validated on browser wasm against builds with and without the change, rebuilding
both corerun and the R2R composite for each:

  Int128 repro, R2R on:  before, Equals=False and CompareTo=+/-1 on equal values;
                         after, all correct. R2R off correct in both.
  System.Text.Json, the Int128-bearing classes, 1496 tests, on a branch carrying
  the other outstanding wasm R2R fixes: before, R2R-on 20 failed (15 Int128);
  after, 2 failed (0 Int128). Those 2 also fail with R2R off, so they are
  pre-existing and unrelated.
  Microsoft.Bcl.Memory 550/550 and Unsafe 128/128, R2R on and off.

Encoding Int128 as V instead does not work: V selects a different calling
convention, passing the value in a wasm v128 local rather than by reference, and
the JIT has no notion of Int128, so R2R code faults with "function signature
mismatch".

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 50ddfb2c-b6f8-4847-81e7-44d37d44a175
Copilot AI review requested due to automatic review settings July 27, 2026 14:59
@lewing
lewing requested a review from MichalStrehovsky as a code owner July 27, 2026 14:59
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).
13 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

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

@lewing

lewing commented Jul 27, 2026

Copy link
Copy Markdown
Member Author

I believe clang sets __int128 alignment to 16 and passes it as two 64 bit args so there is an argument to be made for special handling here but that change is larger and there is no current consumer.

@lewing

lewing commented Jul 27, 2026

Copy link
Copy Markdown
Member Author

cc @dotnet/wasm-contrib

@lewing
lewing requested a review from jakobbotsch July 27, 2026 15:06

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 changes how System.Int128/System.UInt128 alignment is treated on WebAssembly so that wasm uses ordinary struct alignment (8) instead of a forced 16-byte alignment, keeping VM and crossgen2/R2R field layout consistent and avoiding ABI mismatches at interpreter↔R2R boundaries.

Changes:

  • In CoreCLR VM CheckForSystemTypes, switch wasm Int128/UInt128 alignment requirement from 16 to 8.
  • In crossgen2 field layout, treat TargetArchitecture.Wasm32 like existing 32-bit ARM behavior by using the standard metadata layout engine for Int128/UInt128.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
src/coreclr/vm/methodtablebuilder.cpp Updates wasm Int128/UInt128 alignment requirement to 8 in system type layout overrides.
src/coreclr/tools/Common/Compiler/Int128FieldLayoutAlgorithm.cs Routes wasm32 through the “32-bit metadata layout” path to match VM layout and avoid 16-byte override.

Comment on lines 33 to 35
// 32bit platforms use standard metadata layout engine
if (defType.Context.Target.Architecture == TargetArchitecture.ARM)
if (defType.Context.Target.Architecture is TargetArchitecture.ARM or TargetArchitecture.Wasm32)
{

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Correct that the comment is imprecise — X86 is 32-bit and does take the 16-byte override. That imprecision predates this change though, and the condition it describes is right there on the next line, so I'd rather not expand it here: rewording a pre-existing comment is unrelated churn, and per repo convention comments are kept minimal because inaccurate ones mislead and need maintenance.

Happy to fix it if a maintainer would prefer.

Note

This comment was generated by GitHub Copilot.

Comment on lines +34 to 37
if (defType.Context.Target.Architecture is TargetArchitecture.ARM or TargetArchitecture.Wasm32)
{
layoutFromMetadata.LayoutAbiStable = true;
layoutFromMetadata.IsInt128OrHasInt128Fields = true;

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Added Wasm32 cases to TestAlignmentBehavior_AutoAlignmentRulesWithOSDependence in 5fe365d. Both fail without the layout change (alignment 16 rather than 8), so they actually guard the new branch rather than just documenting it.

Note

This comment was generated by GitHub Copilot.

Covers the new Wasm32 branch in Int128FieldLayoutAlgorithm alongside the
existing ARM/ARM64/X86/X64 cases. Both new cases fail without the layout
change (alignment 16 rather than 8).

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 50ddfb2c-b6f8-4847-81e7-44d37d44a175
Copilot AI review requested due to automatic review settings July 27, 2026 15:10
@lewing
lewing requested a review from tannergooding July 27, 2026 15:18

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

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

Comment on lines +10820 to 10824
// Wasm has no 128-bit scalar type to match, so use the natural field alignment.
pLayout->SetAlignmentRequirement(8);
#else
#error Unknown architecture
#endif // TARGET_64BIT
@tannergooding

Copy link
Copy Markdown
Member

I believe clang sets __int128 alignment to 16 and passes it as two 64 bit args so there is an argument to be made for special handling here but that change is larger and there is no current consumer.

This impacts packing and layout, something we really need to have correct from v1 as it can be breaking (behavioral, impact interop, invalidate R2R images, etc) to change it later.

The general tooling docs describing the basic C ABI (not required, just existing tooling convention) explicitly calls out 16: https://github.com/WebAssembly/tool-conventions/blob/main/BasicCABI.md#data-representation

emscripten then currently chooses 16-byte alignment/packing as well, in part because LLVM doesn't override it: https://github.com/llvm/llvm-project/blob/6baa9c9ac22c60e00471c6412387b5a48aa12e3e/clang/lib/Basic/Targets/WebAssembly.h#L28-L92 and https://github.com/llvm/llvm-project/blob/6baa9c9ac22c60e00471c6412387b5a48aa12e3e/llvm/lib/TargetParser/TargetDataLayout.cpp#L523-L533

Rust notably had a similar bug where they didn't use 16-byte alignment as expected and had to fix it last year: rust-lang/rust#133991

So I don't think we can take this and rather need to ensure the interpreter correctly handles this for stack data.

@lewing

lewing commented Jul 27, 2026

Copy link
Copy Markdown
Member Author

we have the same problem for v256/512, for which there are no intrinsic types either

@tannergooding

tannergooding commented Jul 27, 2026

Copy link
Copy Markdown
Member

we have the same problem for v256/512, for which there is no intrinsic type either

Those ones will require every ecosystem to take a break though, if WASM adds official types in the future. As of now, the layout/alignment should be correct as they should be recognized as 2 or 4x V128 fields

I think it's quite a bit different from Int128 which already has a defined contract today and where it will impact layout/interop with other targets.

Is getting the interpreter to handle the 16-byte stack alignment required here (and for Decimal128 which has its own PR up fixing layout on all targets, not just WASM), that much more complex?

@lewing

lewing commented Jul 27, 2026

Copy link
Copy Markdown
Member Author

You're right, and the BasicCABI table refutes my reasoning rather than just outweighing it. The row is:

General type C Type sizeof Alignment Wasm Value Type
Integer __int128_t 16 16 (none)

My argument was "there is no corresponding wasm value type, therefore no ABI basis for the alignment override." The spec assigns alignment 16 to a type whose value type is explicitly (none), so that inference doesn't hold, and layout is a contract independently of how arguments are passed. Withdrawing this.

On the interpreter question: the interpreter looks like the side that's already correct. Tracing the original failure, it placed the Int128 argument at argsBase+32 (16-aligned); the R2R thunk placed it at argsBase+24. The thunk isn't consulting the type — LowerSignature spells a by-reference aggregate as S<N> with N = size only, and RaiseSignature turns S16 back into a type via GetCachedStructOfSize, which keys on size alone and keeps whichever 16-byte struct was lowered first.

Teaching S<N> about alignment would make the two agree, but it would still be the wrong calling convention: clang passes __int128 as two i64s by value, and a 16-byte struct as one i32 pointer.

callee    (long long, __int128,            int) -> (i32, i64, i64, i64, i32)
callee_s16(long long, struct{ulong,ulong}, int) -> (i32, i64, i32, i32)

That divergence is reachable rather than theoretical, since LowerSignature also drives IsUnmanagedCallersOnly. So Int128 likely needs its own signature encoding rather than reusing S<N> — closer to what V does for v128, though the wasm-level lowering would also need JIT support, as Int128 currently appears nowhere in src/coreclr/jit/. Still working that out.

Separately, the signature round-trip loses alignment for the vector types too, and that one is independent of whatever we decide for Int128. Reproduced on current main with no runtime changes, in a library crossgen'd for wasm where an 8-aligned 32-byte struct is lowered before the vector:

struct Blob32 { long a, b, c, d; }                       // 32 bytes, align 8  -> S32
static int Sum256(long tag, Vector256<int> v, int t)     // 32 bytes, align 32 -> S32
static int Sum512(long tag, Vector512<int> v, int t)     // 64 bytes, align 64 -> S64
R2R off:  Blob32 OK   Blob64 OK   Vector256 OK               Vector512 OK
R2R on:   Blob32 OK   Blob64 OK   Vector256 got=1002/1018    Vector512 got=1002/1026

Thunk signatures read out of the R2R image show the collision — Blob32 and Vector256<int> both emit S32:

WasmInterpreterToR2RThunk(llS32ip)   // Blob32
WasmInterpreterToR2RThunk(ilS32ip)   // Vector256<int>

Agreed no ecosystem contract is at stake for those yet. Worth noting the alignment isn't currently what you'd expect from 2x/4x V128 though: wasm has no branch in the Vector256/Vector512 handling in either CheckForSystemTypes or VectorFieldLayoutAlgorithm, so it falls to the final else and gets 32 and 64 rather than 16.

Note

This comment was generated by GitHub Copilot.

@lewing

lewing commented Jul 27, 2026

Copy link
Copy Markdown
Member Author

we have the same problem for v256/512, for which there is no intrinsic type either

Those ones will require every ecosystem to take a break though, if WASM adds official types in the future. As of now, the layout/alignment should be correct as they should be recognized as 2 or 4x V128 fields

I think it's quite a bit different from Int128 which already has a defined contract today and where it will impact layout/interop with other targets.

Is getting the interpreter to handle the 16-byte stack alignment required here (and for Decimal128 which has its own PR up fixing layout on all targets, not just WASM), that much more complex?

it requires another type signature I think, probably not huge but let me look.

@lewing

lewing commented Jul 27, 2026

Copy link
Copy Markdown
Member Author

we have the same problem for v256/512, for which there is no intrinsic type either

Those ones will require every ecosystem to take a break though, if WASM adds official types in the future. As of now, the layout/alignment should be correct as they should be recognized as 2 or 4x V128 fields

I think it's quite a bit different from Int128 which already has a defined contract today and where it will impact layout/interop with other targets.

Is getting the interpreter to handle the 16-byte stack alignment required here (and for Decimal128 which has its own PR up fixing layout on all targets, not just WASM), that much more complex?

My point was that they are also currently broken and have 32/64 alignment which probably doesn't make sense for the current wasm abi. So the question becomes how should we pass them? multiple v128s, or as a struct?

@lewing
lewing marked this pull request as draft July 27, 2026 15:57
@tannergooding

Copy link
Copy Markdown
Member

My point was that they are also currently broken and have 32/64 alignment which probably doesn't make sense for the current wasm abi. So the question becomes how should we pass them? multiple v128s, or as a struct?

They are defined as struct V256 { V128 lower, V128 upper } and struct V512 { V256 lower, V256 upper } and should be treated the same as equivalent user-defined structs (which is likewise how Arm64 treats them).

So a struct containing 2x V128 fields and a struct containing 2x structs, each containing 2x V128 fields. -- I expect WASM treats this the same as a struct containing 2x and 4x V128 fields, respectively. But some ABIs and our own layout algorithm/recursion should already be taking that into account when reporting things back.

@lewing

lewing commented Jul 27, 2026

Copy link
Copy Markdown
Member Author

My point was that they are also currently broken and have 32/64 alignment which probably doesn't make sense for the current wasm abi. So the question becomes how should we pass them? multiple v128s, or as a struct?

They are defined as struct V256 { V128 lower, V128 upper } and struct V512 { V256 lower, V256 upper } and should be treated the same as equivalent user-defined structs (which is likewise how Arm64 treats them).

So a struct containing 2x V128 fields and a struct containing 2x structs, each containing 2x V128 fields. -- I expect WASM treats this the same as a struct containing 2x and 4x V128 fields, respectively. But some ABIs and our own layout algorithm/recursion should already be taking that into account when reporting things back.

Right, I'm telling you the current handling is active broken because there is an alignment mismatch.

@tannergooding

Copy link
Copy Markdown
Member

Right, I got that. I was explaining what the expected behavior is. If its incorrect today, then we probably need to fix it (I have no preference on whether that's the same or a separate PR)

@lewing

lewing commented Jul 27, 2026

Copy link
Copy Markdown
Member Author

Working on a separate prototype that can express the alignment and decomposition that matches the abi in the call signature without adding entirely new arg types.

@lewing lewing added the NO-MERGE The PR is not ready for merge yet (see discussion for detailed reasons) label Jul 27, 2026
@lewing lewing closed this Jul 27, 2026
radekdoulik added a commit that referenced this pull request Aug 4, 2026
## Summary

CoreCLR browser-WASM cannot use `fork` or managed process creation, so
runtime
tests marked `RequiresProcessIsolation` were omitted from merged
runners. This
PR enables those tests without adding Node-specific process APIs to
managed
runtime code:

- the merged runner emits a plan using its existing filtering and
striping;
- the host Bash or Batch script executes each existing per-test wrapper
in a
  fresh child shell;
- the merged runner imports the child results into its normal xUnit
report.

The PR also includes the runtime-test infrastructure, platform gating,
and
focused runtime/test fixes needed to make priority-0 and priority-1
CoreCLR
browser-WASM coverage pass consistently.

This reuses the existing CoreCLR `corerun` wrappers; it does not
introduce a
parallel `browserhost` scheduler or duplicate per-test execution
semantics.

## Execution model

The browser run has three phases:

1. **Plan:** the generated merged runner writes the selected
out-of-process
   assembly paths.
2. **Execute:** the host runs each existing `.sh` or `.cmd` wrapper and
records
   its output, exit code, pass/skip state, and skip reason.
3. **Import:** the normal merged run executes in-process tests and
reports the
   precomputed child outcomes through the existing xUnit summary.

Existing wrappers remain authoritative for test arguments, environment
variables, pre/post commands, expected exit codes, and per-test
timeouts.
Merged-runner arguments and stripe state are cleared while child
wrappers run
and restored afterward.

The result format is versioned and bound to a per-run token. Missing
plans,
wrappers, results, malformed records, or stale tokens fail as
infrastructure
errors. A child in which every test is skipped is reported as skipped
rather
than passed. Empty plan/status environment settings are treated as
unset.

The host orchestration marker is emitted only for CoreCLR browser-WASM
merged
runners that contain out-of-process tests.

## Browser-WASM enablement and fixes

- **Discovery:** only buildable projects with runnable wrappers are
added to
  merged-runner OOP plans.
- **Interpreter:** `InterpreterTester` runs `Interpreter.dll` directly
through
  the host OOP path with the required interpreter environment instead of
  calling `Process.Start` inside the browser.
- **ReadyToRun assets:** host Crossgen2 assets are staged for
cross-architecture runs, and emitted WASM components, including
composite
  output, are validated.
- **Composite ReadyToRun tests:** affected CoreCLR browser tests carry
an
  ActiveIssue for
[#131767](#131767). Their
browser
projects do not force Crossgen2, allowing the managed wrapper to report
the
skip before CoreCLR probes the unsupported composite WebCIL layout.
Other
  targets retain their existing composite R2R coverage.
- **Host paths:** Batch orchestration retains Windows paths for host
file
operations while exposing Unix-style absolute paths to browser managed
code.
- **Multithreading:** `FeatureMultithreading` is available during test
project
evaluation. Tests whose only requirement is managed multithreading use
that
global capability, so they remain excluded on single-threaded targets
and
  become buildable when threads are enabled.
- **Precise platform scope:** tests requiring background/server GC,
native
threads, child processes, unsupported browser APIs, mutable host
filesystem
behavior, or impractical interpreter workloads are gated or excluded at
the
  narrowest applicable level.
- **Shutdown lifecycle:** the CoreCLR browser shutdown wrapper is
installed
  from Emscripten `preRun`, after native exports are bound, so explicit
  `Environment.Exit` values are preserved and no finalization work is
  scheduled while the runtime exits.
- **Background GC information test:** the browser CoreCLR
  `GetGCMemoryInfo` test carries an ActiveIssue for
[#131766](#131766), which tracks
the
assertion from requesting `GCKind.Background` when background GC is not
  compiled. This PR does not change the product behavior.
- **Test assumptions:** focused adaptations remove incidental process,
  threading, filesystem, and host-tool assumptions while preserving the
  behavior each test is intended to cover.
- **Post-merge CI coverage:** build 1528104 exposed 30 deterministic OOP
failures after newer `main` changes activated more browser scenarios.
The
  eager-fixup lock-order failure is fixed on `main` by
[#131355](#131355). Composite
WebCIL
loading and background-GC information remain tracked separately by
#131767
and #131766. The `Int128` field-layout correctness fix is proceeding in
[#131421](#131421); other
unsupported
  cases are gated only for CoreCLR browser-WASM with explicit re-enable
  prerequisites.

No shipping public API is added.

## Deferred scope

Per-test native assets, UCO thunk generation, and native relinking are
intentionally deferred. Tests that need them remain guarded by their
existing
tracking issues.

The remaining non-multithreading/non-relinking exclusions were audited:
41 represent recoverable coverage and 16 exercise browser-inapplicable
contracts. The recoverable set and its prerequisites are tracked by
[#131321](#131321), with links
from each
suppression site. The newly exposed native-relinking test remains
tracked by
[#123946](#123946).

## Validation

The following complete-suite results were recorded before the two
product fixes
were moved to their dedicated tracking issues:

| CoreCLR browser-WASM run | Total | Passed | Skipped | Failed |
| --- | ---: | ---: | ---: | ---: |
| Priority 0 after the `main` merge | 3,970 | 3,412 | 558 | 0 |
| Priority 0 OOP subset | 485 | 408 | 77 | 0 |
| Earlier full priority 0+1 validation | 14,726 | 13,850 | 876 | 0 |
| Earlier priority 0+1 OOP subset | 740 | 651 | 89 | 0 |

Current-head validation after removing those fixes:

- rebuilt the Checked CoreCLR browser-WASM runtime and libraries with
zero
  warnings or errors;
- cleanly built all ten affected browser test projects;
- ran all ten generated wrappers against the unfixed runtime: every
wrapper
exited with the expected status and reported #131766 or #131767 through
the
  OOP skip-status protocol, without either runtime assertion;
- verified that the shared non-R2R browser `BasicTest` remains enabled;
- verified on macOS that the R2R variant still forces Crossgen2, emits
and
  validates its composite image, and passes.

Additional earlier validation:

- composite and non-composite WASM ReadyToRun output;
- native `NativeLibraryTests` asset staging;
- plan/import success, failure, filtering, striping,
stale/malformed/missing
  records, environment-sensitive wrappers, and all-skipped children;
- unset, empty, and non-empty plan/status environment settings;
- direct browser-WASM `InterpreterTester` execution under Node;
- generated Bash syntax and generated Batch structure/path conversion;
- Checked browser runtime rebuild plus a fresh priority-0 payload and
test
  layout after the `main` merge;
- all 22 new gates across CoreCLR browser, WASI, desktop, and Mono
browser
  evaluations, preserving the pre-existing Mono ReadyToRun exclusions;
- the complete 72-runner Node run, including the ReadyToRun, base
services,
  JIT, Loader, and tracing work items that failed in build 1528104.

| Representative CoreCLR browser library suite | Total | Passed |
Skipped | Failed |
| --- | ---: | ---: | ---: | ---: |
| `System.Threading.Timer.Tests` | 33 | 16 | 17 | 0 |
| `System.Runtime.InteropServices.JavaScript.Tests` | 476 | 474 | 2 | 0
|
| `System.Reflection.Emit.Tests` (Debug) | 2,028 | 2,017 | 11 | 0 |
| `System.Runtime.Tests` (Debug) | 75,726 | 75,559 | 167 | 0 |

The generated Batch path has been inspected and generated successfully
but
still requires execution on Windows CI.

> [!NOTE]
> This pull request description was updated with GitHub Copilot.

---------

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 44f4b4ea-409c-4a52-89a0-f488a89488b9
radekdoulik added a commit that referenced this pull request Aug 5, 2026
…-1 tests (#131883)

## Summary

PR #131110 enabled CoreCLR browser-WASM out-of-process runtime tests.
The newly enabled priority-1 coverage exposed eight unique failures.

Four were composite WebCIL/ReadyToRun failures tracked by #131767 and
are fixed on `main` by #131354. This change removes their temporary
`ActiveIssue` annotations and Crossgen2 bypasses, restoring the intended
R2R coverage. The unrelated field-layout suppression for #131421
remains.

The other four tests are narrowly gated:

- `GC/API/GC/GetTotalAllocatedBytesServerGC` is excluded on CoreCLR
browser, which does not provide Server GC.
- `GC/API/GC/GetTotalMemoryConcurrent` uses
`PlatformDetection.IsMultithreadingSupported` through `ConditionalFact`.
- `baseservices/threading/regressions/2164/foreground-shutdown` retains
its explicit `Main` and returns success before creating a thread when
multithreading is unsupported.
- `JIT/jit64/regress/ddb/113574` is excluded only on CoreCLR browser
because its multi-billion-iteration optimizing-JIT workload exceeds the
browser interpreter's CI timeout.

The runtime capability checks skip single-threaded browser and WASI
while preserving desktop and threaded-browser coverage. The Server GC
and interpreter-workload exclusions are tracked in #131321.

## Testing

Built the Checked browser runtime from `main`, including #131354, then
built and ran the complete priority-1 browser-WASM Node suite:

| Scope | Total | Passed | Failed | Skipped |
| --- | ---: | ---: | ---: | ---: |
| Full suite | 14,745 | 13,823 | 0 | 922 |
| Out-of-process subset | 672 | 560 | 0 | 112 |

The four former WebCIL/R2R failures now pass:

- `Regressions/coreclr/GitHub_49826/test49826`
- `Regressions/coreclr/GitHub_49982/test49982`
- `readytorun/tests/genericsload/callgenericctor`
- `readytorun/tests/genericsload/usegenericfield`

Additional focused validation for the runtime multithreading gates:

- 8 MSBuild evaluations across desktop, threaded browser,
single-threaded browser, and single-threaded WASI; neither project is
permanently marked unsupported.
- 2 targeted Checked browser builds, both with 0 warnings and 0 errors.
- 2 single-threaded browser wrapper runs, both passing with
expected/actual exit code 100.
- Native `foreground-shutdown` execution exited with 100 after 2.28
seconds, confirming that its foreground thread still keeps the process
alive after `Main` returns.

> [!NOTE]
> This pull request was created with assistance from GitHub Copilot.

Copilot-Session: 19e61792-38f9-4f80-8d38-3e3e12d1275e
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-VM-coreclr NO-MERGE The PR is not ready for merge yet (see discussion for detailed reasons)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants