Repository navigation
Port runtime PR dotnet/runtime#133111 R2R changes to SDK Crossgen tasks - #56395
Conversation
Bring the SDK's shared Crossgen2/ReadyToRun task implementation in sync
with the equivalent runtime-side changes from dotnet/runtime PR #133111
("Use SDK pipeline for WebAssembly framework R2R"):
- Track CrossgenTool, Crossgen2Tool, and the new
@(_ReadyToRunCompilerInputs) item as incremental inputs to
_CreateR2RImages, so R2R images are recompiled when the compiler or
its dependencies change.
- Forward the runtime-private $(_PublishReadyToRunCrossgen2ExtraArgs)
property alongside the existing public extra-args property on both
R2R compiler invocations.
- Extend R2R output-path calculation to support WebAssembly output
(.wasm), which Crossgen2 now emits directly, while preserving the
existing Mach-O intermediate (.o) to final (.dylib) linking behavior
for assemblies that are compiled separately.
Composite component assemblies keep their original publish identity
(no container-format extension rewriting or native-link metadata),
matching pre-existing SDK behavior; only images that are compiled
separately go through the container-aware output-path helper.
Added focused regression tests covering WASM output/publish paths,
preserved Mach-O native-link metadata for separately compiled images,
unaffected composite component paths, and the new target-level
incremental input/argument wiring.
Co-authored-by: Copilot App <[email protected]>
|
Azure Pipelines: Successfully started running 1 pipeline(s). 2 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
This comment has been minimized.
This comment has been minimized.
|
dotnet/runtime#134618 enables The added tests cover separately compiled wasm images and Mach-O composite components, but not the wasm composite owner. Could you add a case such as Note This comment was generated with GitHub Copilot assistance. |
|
@copilot apply feedback #56395 (comment) |
Co-authored-by: maraf <[email protected]>
Co-authored-by: maraf <[email protected]>
Co-authored-by: maraf <[email protected]>
Fixed in 37f4fbc. The XML assertions now handle nullable values safely and report missing required attributes clearly; the focused test passes.
Applied the linked build-feedback fix in 37f4fbc. The focused ReadyToRun task test now passes. |
Preserve forward slashes from RelativePath metadata in cross-platform assertions and verify the composite WASM owner is published without native linking. Co-authored-by: Copilot App <[email protected]>
lewing
left a comment
There was a problem hiding this comment.
The wasm composite owner test added in 0ad552b covers my earlier comment, thanks. One remaining question inline about behavior differing from dotnet/runtime#133111.
Note
This review was generated with GitHub Copilot assistance.
Apply container-aware output naming to all WebAssembly inputs so composite component publish items match the .wasm stubs emitted by Crossgen2, while preserving SDK-specific Mach-O behavior. Co-authored-by: Copilot App <[email protected]>
Keep the task tests focused on unit behavior and stop copying the shipping targets file into the test output. Co-authored-by: Copilot App <[email protected]>
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
The incremental target behavior lacks an automated MSBuild-level regression test.
Review effort: Balanced
Findings: 1
Open (1)
What changed in this PR
Ports WebAssembly ReadyToRun behavior from the runtime fork into the SDK’s shared Crossgen pipeline.
Changes:
- Adds WASM output-path handling while preserving Mach-O behavior.
- Tracks compiler inputs for incremental R2R builds and forwards private compiler arguments.
- Adds focused task-level path and metadata tests.
| File | Description |
|---|---|
PrepareForReadyToRunCompilation.cs |
Computes WASM and Mach-O compiler/publish paths. |
Microsoft.NET.CrossGen.targets |
Adds incremental inputs and private Crossgen2 arguments. |
GivenAPrepareForReadyToRunCompilation.cs |
Tests WASM and Mach-O task outputs. |
Co-authored-by: Copilot App <[email protected]>
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
## Why CoreCLR WASI composite ReadyToRun currently names the composite owner image `composite-r2r.wasm`. `PrepareForReadyToRunCompilation` in Crossgen2Tasks forces that name whenever `Crossgen2Tool` TargetOS is `wasi`. The browser composite work in #134618 uses the task's default name instead: `<entry>.r2r.wasm`, the main assembly name with `.r2r.wasm`. This PR drops the TargetOS special case so WASI gets the same owner name as browser. It lands ahead of the dotnet/sdk#56395 port, so that PR doesn't carry the WASI rule into the SDK. The two delivery models stay different on purpose. Browser instantiates the composite at runtime through the JS loader. WASI composes it offline into the host with `wasm-merge`/`wasm-opt`. Only the naming contract changes. ## Design The composite's own file name, as written by crossgen2, is the single authority. It is also the owner name every component stub records. - **`PrepareForReadyToRunCompilation`:** the `wasi` branch is removed. A wasm composite owner is now `<entry>.r2r.wasm` on both targets. - **`WasiApp.CoreCLR.targets`:** the composite path now comes from the planned composite compilation, `@(_ReadyToRunCompileList)` with `CreateCompositeImage=true`, via `%(OutputR2RImage)`. The build errors unless there is exactly one composite compilation. Because the path isn't hardcoded, WASI keeps working with either SDK naming. - **`wasi_r2r_probe.hpp`:** the host reserves a 256-byte composite name buffer, zero-initialized, and exports its address and capacity (`wasi_r2r_composite_name_base`, `wasi_r2r_composite_name_cap`). The probe serves the composite only when the requested name matches the recorded name, and only if that name is non-empty. An uncomposed host therefore serves no composite. There is no wildcard matching and no name compiled into C++. - **`ComposeWasiReadyToRun`:** the composer reads the two new exports and checks that the name is non-empty, contains no NUL, and fits the buffer. Its shim now imports `webcil.memory` and includes an active data segment that writes `Path.GetFileName(CompositePath)`, NUL-terminated, into the host buffer. This is the same mechanism that already installs the payload. A prebuilt host, such as the runtime-test corerun, can therefore be composed with a composite of any name. - **Docs:** updated the WASI host composition section of `docs/design/mono/webcil.md` and `docs/workflow/building/coreclr/wasi-r2r.md`. ## Validation - `./build.sh clr+libs+host+packs -os wasi -arch wasm -c Release` finished with 0 warnings and 0 errors. - `./dotnet.sh publish src/mono/sample/wasi/console/Wasi.Console.Sample.csproj -c Release -p:TargetOS=wasi -p:TargetArchitecture=wasm -p:RuntimeFlavor=CoreCLR -p:PublishReadyToRun=true` succeeded. The composer logged `composite='Wasi.Console.Sample.r2r.wasm'`. - The published app ran under wasmtime with `DOTNET_ReadyToRunLogFile` set. It exited 0, and the log shows `Ready to Run initialized successfully` for System.Private.CoreLib, Wasi.Console.Sample, System.Runtime, System.Console, System.Threading and System.Runtime.InteropServices. - **Negative check:** I composed the same host with a copy of the composite renamed to `Wrong.r2r.wasm`. Startup then traps in `EEStartup` because CoreLib's owner composite isn't served. A name mismatch fails loudly rather than silently interpreting. - **Non-R2R publish** of the same sample: the app runs on the interpreter, and the log shows `Ready to Run header not found`. - **Not run:** browser `ReadyToRunTests`. The removed code only executed when TargetOS was `wasi`, and the browser naming path is unchanged. ## Interaction with #134813 This PR merges cleanly with #134813's head (checked with `git merge-tree`); it doesn't depend on #134813. The runtime-test harness in #134813 passes the same `composite-r2r.wasm` path to both crossgen2 and `WasiR2RComposer.proj`. The composer records that name in the shared corerun, so the harness needs no changes. I did not build or run the runtime tests with both PRs merged. ## Follow-up in dotnet/sdk#56395 - Remove the `wasi` TargetOS block from `CreateReadyToRunFileToPublish`. - Change `It_uses_the_fixed_wasm_path_for_a_wasi_composite_owner` to expect `<entry>.r2r.wasm`, the same as browser. > [!NOTE] > This pull request description was generated with assistance from GitHub Copilot. Co-authored-by: Copilot App <[email protected]>
Co-authored-by: Copilot App <[email protected]>
## Summary - Enable `PublishReadyToRunComposite` for CoreCLR browser-wasm when WebCIL is enabled. Composite is opt-in; the default per-assembly R2R mode is unchanged. - Publish the composite owner (`<entry>.r2r.wasm`, the name crossgen2 records in each component stub) as a static web asset with a `readyToRunComposite` trait, defined through `DefineStaticWebAssets`. - `GenerateWasmBootJson` routes the owner to `resources.coreAssembly` and flags it `isCompositeImage: true`. The loader keeps its `.wasm` virtual path and leaves it out of the TPA, and component stubs stay in the TPA. Owner identity comes from the flag, not the file name, so an ordinary library named `*.r2r` still loads as a managed assembly. - Route composite component stubs through `ConvertDllsToWebcil`, keep the linked R2R closure across repeated trimmed publishes, and prune stale per-app images and stubs. - Invalidate per-app R2R images when the composite mode or crossgen2 arguments change. Component stubs share the per-assembly `<name>.wasm` names, so without this, switching composite to per-assembly shipped stale stubs that failed at startup. - Composite needs ReadyToRun tasks that name the owner `<entry>.r2r.wasm`. The base SDK doesn't yet (dotnet/sdk#56395), so a composite publish with stock SDK tasks fails fast with an actionable error. Wasm.Build.Tests use the in-repo Crossgen2Tasks, which now also ship in the `BuildWasmApps` Helix payload. ## Validation On `main` as of 2026-09-29, including WebCIL wrapper v2 (#134312), WASI composite in the shared Crossgen2Tasks (#133265), and the browser R2R strip defaults (#134690): - Rebuilt the CoreCLR browser Release runtime pack (`clr+libs+host`) and the WebAssembly SDK pack. - `Wasm.Build.Tests.ReadyToRunTests`: 10/10 passed. This covers: - composite trimmed, untrimmed and native-relinked, run in Chrome - a repeated trimmed publish - a composite → per-assembly → composite switch - per-assembly publish, native relink and dev-loop build - composite without WebCIL (negative) - Negative checks: - With the composite mode removed from the invalidation stamp, the mode-switch assertion fails. - With stock SDK ReadyToRun tasks, composite publish hits the new error. - CI: `PublishRunAllPagesComposite` passed on Helix on Linux and Windows. The remaining failures matched known issues (#133953, #133373). Benchmark (`IndexOfMax<double>(3079)`, browser, earlier revision of this PR): composite R2R ran the SIMD path with a median of **4,040 ns/op**, vs. **1,013,940 ns/op** interpreted and **10,652,060 ns/op** with per-assembly R2R. Part of testing #134559. That issue's default per-assembly scenario is not changed by this PR. > [!NOTE] > This pull request description was generated with GitHub Copilot assistance. --------- Co-authored-by: Copilot App <[email protected]>
Composite CoreCLR WASM R2R needs ReadyToRun SDK tasks that name the owner image <entry>.r2r.wasm (dotnet/sdk#56395), which the SDK shipped in the BrowserWasmCoreCLR perf artifact does not have yet. dotnet/runtime#135204 stages runtime's wasm-aware Crossgen2Tasks shim into that artifact as staging/Crossgen2Tasks. Copy the shim into the Helix payload when present, export its location as PERFLAB_WASM_CROSSGEN2_TASKS_DIR for the r2r_composite lane only, and in composite mode pass it to the WebAssembly SDK through Crossgen2SdkOverridePropsPath/Crossgen2SdkOverrideTargetsPath. These must be environment properties because the SDK reads them during props evaluation. Per-assembly R2R keeps the SDK's own ReadyToRun tasks. TODO: remove once the SDK carries dotnet/sdk#56395 (dotnet/runtime#135023). Co-authored-by: Copilot App <[email protected]>
…ifact (#135204) The new composite R2R microbenchmark lane in dotnet/performance#5324 (`coreclr_r2r_composite_v8`) publishes CoreCLR browser-wasm with `PublishReadyToRunComposite`. #134618's `_WasmCoreClrValidateCompositeTasks` rejects that publish unless the SDK ReadyToRun tasks name the owner `<entry>.r2r.wasm`. The SDK tasks only learn to do that in dotnet/sdk#56395, which hasn't reached runtime's global.json SDK yet (11.0.100-rc.1.26420.103). This PR copies the in-tree wasm-aware task shim, the same one Wasm.Build.Tests use via `Crossgen2SdkOverridePropsPath`/`Crossgen2SdkOverrideTargetsPath`, into the `BrowserWasmCoreCLR` perf artifact. The shim comes from `artifacts/bin/Crossgen2Tasks/<config>/`, which `Build.proj` already produces. Staged layout (flat, no config subfolder; this is the path dotnet/performance consumes): ``` staging/Crossgen2Tasks/Crossgen2Tasks.dll staging/Crossgen2Tasks/Microsoft.NET.CrossGen.props staging/Crossgen2Tasks/Microsoft.NET.CrossGen.targets ``` The step fails if the source directory or any of these three files is missing. Only the CoreCLR leg (`includeCoreClrToolchainPacks: true`) changes; the Mono artifact is untouched. This is temporary: remove it once dotnet/sdk#56395 reaches global.json. Tracked by #135023. Validation: YAML parses (`python3 yaml.safe_load`) and `git diff --check` is clean. The pipeline itself hasn't run yet. > [!NOTE] > This PR was generated with the help of GitHub Copilot. Co-authored-by: Copilot App <[email protected]> Copilot-Session: a287cfc0-a881-4272-9024-bdaf9d9db4ef
* Add CoreCLR WASM composite R2R microbenchmark lane Add a coreclr_r2r_composite_v8 job (r2rRunType r2r_composite) alongside the per-assembly coreclr_r2r_v8 lane. The mode flows through run_performance_job.py (R2RType=r2r_composite, a separate PerfLab history) to a new --wasm-ready-to-run-composite micro_benchmarks option, which sets PERFLAB_WASM_READY_TO_RUN_COMPOSITE for MSBuild. MicroBenchmarks.Wasm.targets now sets PublishReadyToRunComposite from the selected mode, validates it, and in composite mode verifies that exactly one <entry>.r2r.wasm composite image was produced with component inputs and that it was defined as the readyToRunComposite publish asset that GenerateWasmBootJson routes to coreAssembly. Depends on dotnet/runtime#134618. Co-authored-by: Copilot App <[email protected]> * Use runtime's Crossgen2Tasks shim for CoreCLR WASM composite R2R Composite CoreCLR WASM R2R needs ReadyToRun SDK tasks that name the owner image <entry>.r2r.wasm (dotnet/sdk#56395), which the SDK shipped in the BrowserWasmCoreCLR perf artifact does not have yet. dotnet/runtime#135204 stages runtime's wasm-aware Crossgen2Tasks shim into that artifact as staging/Crossgen2Tasks. Copy the shim into the Helix payload when present, export its location as PERFLAB_WASM_CROSSGEN2_TASKS_DIR for the r2r_composite lane only, and in composite mode pass it to the WebAssembly SDK through Crossgen2SdkOverridePropsPath/Crossgen2SdkOverrideTargetsPath. These must be environment properties because the SDK reads them during props evaluation. Per-assembly R2R keeps the SDK's own ReadyToRun tasks. TODO: remove once the SDK carries dotnet/sdk#56395 (dotnet/runtime#135023). Co-authored-by: Copilot App <[email protected]> * Clear inherited Crossgen2 SDK override paths for WASM R2R configure_wasm_crossgen2_sdk_override now removes inherited Crossgen2SdkOverridePropsPath/Crossgen2SdkOverrideTargetsPath before deciding whether to apply the shim, so per-assembly R2R always keeps the SDK's ReadyToRun tasks and a composite run without a staged shim cannot pick up a stale path. PERFLAB_WASM_CROSSGEN2_TASKS_DIR remains the only way to select a shim. Co-authored-by: Copilot App <[email protected]> * Fail fast on a present but incomplete Crossgen2Tasks shim A BrowserWasmCoreCLR directory artifact with a staging/Crossgen2Tasks folder that lacks the required files was treated like an artifact without the shim, silently falling back to the SDK ReadyToRun tasks. Only a missing folder (directory artifacts) or no extracted entries (archives) now means "no shim"; anything else must contain Crossgen2Tasks.dll and Microsoft.NET.CrossGen.props/.targets or build_wasm_coreclr_payload raises ValueError. Add archive coverage. Co-authored-by: Copilot App <[email protected]> --------- Co-authored-by: Copilot App <[email protected]>



Why
dotnet/runtime'ssrc/tasks/Crossgen2Tasksis a simplified fork of the SDK's shared Crossgen2/ReadyToRun task implementation. Runtime PR dotnet/runtime#133111 ("Use SDK pipeline for WebAssembly framework R2R") made changes to the shared task logic that were never migrated back into this SDK repo. This PR ports the task-and-target portions that apply to the SDK, excluding runtime-only WebCIL and runtime-pack policy.What changed
Microsoft.NET.CrossGen.targets:_CreateR2RImagesnow tracks@(CrossgenTool),@(Crossgen2Tool), and@(_ReadyToRunCompilerInputs)as incremental inputs, so R2R images are recompiled when the compiler or its dependencies change. Both R2R compiler invocations also forward the runtime-private$(_PublishReadyToRunCrossgen2ExtraArgs)property alongside the existing public arguments.PrepareForReadyToRunCompilation.cs: the R2R output-path helper now supports WebAssembly output. Separately compiled WASM images and composite component stubs use the.wasmpaths emitted by Crossgen2, and a composite owner is published as<entry>.r2r.wasm..ooutput linked into a published.dylib, while Mach-O composite components retain their managed assembly identity and do not receive native-link metadata.Testing
Added focused
PrepareForReadyToRunCompilationunit coverage for:Local validation:
11.0.100-devSDK,UseMonoRuntime=false, andPublishReadyToRun=true.Microsoft.NET.Build.Tasks.dllranPrepareForReadyToRunCompilationand produced 37.wasmpaths in both_ReadyToRunCompileListand_ReadyToRunFilesToPublish.Note
This PR description was generated with the assistance of GitHub Copilot.