dotnet / dotnet/runtime

[wasm][R2R] crossgen2 emits function types exceeding the 1000-parameter wasm limit, silently disabling R2R for the assembly

Open
#132,855 3 comments 0 reactions 0 assignees View on GitHub
arch-wasm area-CodeGen-coreclr disabled-test os-browser
Dominant language
C#
Stars
18.3k
Forks
5.6k
PR merge metrics
PR metrics pending

Description

### Description

`crossgen2` targeting wasm can emit a function type whose parameter count exceeds the **1000-parameter implementation limit**, producing an R2R image that no engine will instantiate. The runtime catches the failure and silently falls back to interpreting that assembly, so the only symptom is a log line and a large, invisible loss of R2R coverage.

This is the CoreCLR/crossgen2 recurrence of a bug already found and fixed on the Mono side:

- [#76719](https://github.com/dotnet/runtime/issues/76719) — *"Wasm AOT can produce functions with too many parameters"* (2022, closed). Same root cause, and it names the same trigger file. It explicitly recommends *"detect it during the build process and either not AOT the method or fail the build."*
- [#80243](https://github.com/dotnet/runtime/pull/80243) — *"[wasm] Disable llvm for methods/calls with more than 1000 parameters"* — the Mono fix.

crossgen2 has no equivalent guard.

**The limit is documented and not V8-specific.** V8 enforces max 1000 params (also 1000 results, 50 000 locals, 65 520 `br_table` entries). `wasmtime` and `wasm-tools` enforce the same parameter cap, so the emitted module is invalid for any conforming engine, not merely unloadable in browsers.

### Reproduction Steps

Build a browser-wasm R2R image over the `System.Text.Json` test closure:

```bash
./build.sh clr+libs -os browser -c Release

./dotnet.sh build src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj \
/p:TargetOS=browser /p:TargetArchitecture=wasm /p:RuntimeFlavor=CoreCLR \
/p:Configuration=Release /p:TargetFramework=net11.0 /p:PublishReadyToRun=true
```

Then run it (flat directory, `corerun.js`, per-assembly R2R images deployed beside the app):

```bash
DOTNET_ReadyToRun=1 APP_ASSEMBLIES=EXTERNAL \
node --experimental-wasm-exnref --stack-size=8192 corerun.js -c "$PWD" \
WasmTestRunner.dll System.Text.Json.Tests.dll
```

Reproduces with **composite** (`crossgen2 --composite --targetos:browser`) as well — it is not specific to either mode.

**The trigger** is `ClassWithManyConstructorParameters` in
[`src/libraries/System.Text.Json/tests/Common/TestClasses/TestClasses.Constructor.cs`](https://github.com/dotnet/runtime/blob/main/src/libraries/System.Text.Json/tests/Common/TestClasses/TestClasses.Constructor.cs) (~line 2559), whose constructor takes `int P000 … int P999` — exactly 1000 managed parameters. Lowered to wasm that becomes **1003** params (1000 managed + 3 ABI), which is over the cap.

This is the same test type named in #76719.

### Expected behavior

`crossgen2` declines to compile a method whose lowered wasm signature would exceed the implementation limit, leaving it to the interpreter/JIT — the behaviour #80243 gave Mono. Ideally it also emits a diagnostic rather than failing silently at load.

### Actual behavior

The emitted module contains a `functype` with 1003 `i32` params. In the composite image the offending type sits at file offset `0x187B` (`60 eb 07` — `0x60` functype, LEB `0xeb 0x07` = 1003, followed by 1003 × `0x7f`).

Every engine rejects it:

| engine | 1000 params | 1003 params |
| --- | --- | --- |
| Chrome 152.0.7977.65 | accepted | `WebAssembly.Module(): param count of 1003 exceeds internal limit of 1000` |
| node 26.4.0 (V8) | accepted | same message |
| wasmtime 45.0.2 | — | `function params size is out of bounds` |
| `wasm-tools parse` | accepted | `function params size is out of bounds` |

At runtime this surfaces as:

```
Failed to construct WebAssembly module for Webcil image: {
wasmPath: 'System.Text.Json.Tests.wasm',
errorMessage: 'WebAssembly.Module(): param count of 1003 exceeds internal limit of 1000 @+1403'
}
```

and then, in `DOTNET_ReadyToRunLogFile` output:

```
Ready to Run header not found: "System.Text.Json.Tests".
Ready to Run initialized successfully: "System.Text.Json".
```

i.e. the assembly is silently downgraded to interpreted while the rest of the closure stays R2R. **The run otherwise proceeds normally**, so without checking the R2R log there is no indication that an assembly lost R2R entirely.

### Regression?

Not a regression in CoreCLR — this is the first time the wasm R2R path has been exercised over this test closure, as far as I can tell. It is a recurrence of a known Mono-side defect (#76719) in a different compiler.

### Known Workarounds

None at the crossgen2 level. The affected assembly runs interpreted, which is correct but silently costs all R2R coverage for it.

### Configuration

- `dotnet/runtime` `main` (plus an in-progress WASI R2R integration delta; the affected code paths are unmodified from `main`).
- Target: `browser-wasm`, `RuntimeFlavor=CoreCLR`, `Configuration=Release`.
- Host: macOS arm64; engines as listed in the table above.

### Other information

Adjacent limits worth auditing in crossgen2's wasm backend, since they have the same shape and the same silent-failure mode — V8 caps **returns at 1000**, **locals at 50 000**, and **`br_table` entries at 65 520**. The `br_table` one may be worth checking first: wasm R2R already emits `br_table` for `TypeCode`-style dispatch.

Related wasm R2R validity issues, all closed, suggesting module-validity checking is worth doing systematically rather than case by case: #129335, #129743, #129526.

> [!NOTE]
> This issue was drafted with GitHub Copilot (AI-generated) and reviewed by the author.

Contributor guide

Open the contributing guide

Research direction

Start by reproducing the failure with the browser-wasm R2R build commands and the System.Text.Json test closure. Inspect the crossgen2 wasm backend and the ClassWithManyConstructorParameters case in src/libraries/System.Text.Json/tests/Common/TestClasses/TestClasses.Constructor.cs. Done means an over-limit lowered signature is declined or diagnosed, and the affected assembly no longer produces an invalid R2R image.

Written by the indexing model from the issue text.

Assessment

Tech stack
wasm
Domain
compilers
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.