Skip to content

Add a CI check that the checked-in wasm CoreCLR call helpers are up to date - #135156

Merged
lewing merged 3 commits into
mainfrom
lewing-wasm-callhelpers-drift-check
Oct 7, 2026
Merged

lewing merged 3 commits into
mainfrom
lewing-wasm-callhelpers-drift-check

Conversation

@lewing

@lewing lewing commented Oct 3, 2026

Copy link
Copy Markdown
Member

The checked-in CoreCLR WebAssembly call-helper tables (src/coreclr/vm/wasm/{browser,wasi}/callhelpers-*.cpp) are generated from the framework's [UnmanagedCallersOnly] methods, P/Invokes and QCalls. Nothing checked that they still match the managed code. A stale table still builds, but the corerun that links it crashes. #134978 enabled FEATURE_PERFTRACING on WASI without regenerating the tables. The EventPipeEventProvider.Callback reverse thunk was missing, and every corerun built from the checked-in tables then failed at startup with GetUnmanagedCallersOnlyThunk: unknown thunk in CoreCLR_WASI_RuntimeTests. Library tests did not catch it because app relink generates per-app tables.

Related: #133187

Changes

  • Generator: generate-coreclr-helpers.proj gains an optional CallHelperTargetOS property (browser or wasi). The .sh and .cmd scripts expose it as -t / --target-os, so a build that produced only one flavor can regenerate just that flavor. An invalid value gets a clear error.
  • CI step: the new eng/pipelines/common/templates/wasm-coreclr-callhelpers-check.yml regenerates the leg's own flavor. If src/coreclr/vm/wasm/<os>/ then differs from what is committed, it fails with a ##vso error, the diff, and the exact build and regenerate commands. It runs as a post-build step on two existing legs, so no new leg is added:
    • the Release browser_wasm CoreCLR leg (Linux host only; the step skips itself on browser_wasm_win);
    • the wasi_wasm CoreCLR build-only leg.
  • Triggers: a new wasm_coreclr_callhelpers path subset is OR'd into both legs' conditions, so managed-only interop changes run the check. It covers src/libraries/*/src/*, src/coreclr/System.Private.CoreLib/*, src/coreclr/tools/aot/ILCompiler.ReadyToRun/*, src/coreclr/tools/Common/*, src/coreclr/vm/wasm/* and eng/wasm/WasmPInvokeModules.props. This deliberately widens which library PRs run these two legs.
  • Docs and default configuration: the scripts now default to -c Release. generate-coreclr-helpers.md documents --target-os, the canonical configuration, and the CI check.
  • Regenerated WASI table (separate commit): changes comments only in wasi/callhelpers-pinvoke.cpp, so the check starts green.

Does the output depend on configuration?

I regenerated the tables from several configurations and diffed the results:

Flavor Inputs compared Result
wasi Release vs Debug (clr+libs -c ...) Same entries. One comment differs: Debug System.Net.NameResolution still references SystemNative_GetErrNo; Release trims it.
wasi Release libs + Release CoreLib vs Release libs + Checked CoreLib (-rc Checked -lc Release) Byte-identical
wasi Release crossgen2 vs Checked crossgen2 (same scan path) Byte-identical
browser Release vs Debug Byte-identical, and identical to main

Entries do not depend on configuration, so one set of tables serves every configuration's corerun. Only the // <assembly>, ... comments on P/Invoke entries vary, because library trimming differs by configuration. Release is therefore the canonical configuration: CI builds Release, and the check compares against it exactly. The WASI table on main had been generated from Debug libraries. It also predated #130463, which moved the WASI socket interop into CoreLib. Regenerating from Release changes three comments:

-    DllImportEntry(SystemNative_GetErrNo) // System.Net.NameResolution, System.Private.CoreLib
+    DllImportEntry(SystemNative_GetErrNo) // System.Private.CoreLib
-    DllImportEntry(SystemNative_GetWasiSocketDescriptor) // System.Net.Sockets
+    DllImportEntry(SystemNative_GetWasiSocketDescriptor) // System.Private.CoreLib
-    DllImportEntry(SystemNative_WasiSubscribeSocketPollable) // System.Net.Sockets
+    DllImportEntry(SystemNative_WasiSubscribeSocketPollable) // System.Private.CoreLib

Validation (local, macOS arm64 host)

Command Result
./build.sh clr+libs -os wasi -c Release Passed
./build.sh clr+libs -os wasi -c Debug Passed
./build.sh clr+libs -os wasi -rc Checked -lc Release Passed
./build.sh clr+libs -os browser -c Release / -c Debug Passed (emsdk needed EMSDK_PYTHON pointing at Python ≥ 3.10 locally)
src/coreclr/vm/wasm/generate-coreclr-helpers.sh -c Release --target-os wasi Passed; regenerated only wasi/, browser/ untouched
src/coreclr/vm/wasm/generate-coreclr-helpers.sh -c Release --target-os browser Passed; no diff against main
src/coreclr/vm/wasm/generate-coreclr-helpers.sh (no --target-os) Passed; both flavors generated as before
generate-coreclr-helpers.sh -t foo Fails as expected: Invalid target OS "foo"
dotnet build generate-coreclr-helpers.proj -t:GenerateCallHelpers -p:CallHelperTargetOS=linux Fails as expected: Unknown CallHelperTargetOS 'linux'
Check-step script body, osGroup=wasi, _BuildConfig=Release, on main's stale WASI table Exit 1; error, diff and fix commands printed
Same script on this branch's clean tree Exit 0: "The checked-in wasi call helpers are up to date."
Same script after committing a WASI table with one reverse-thunk entry removed Exit 1; diff shows the missing entry

Not validated: the pipeline YAML (the step template, the leg hookups, and the new path subset with its conditions) can only be verified in CI. The .cmd changes have not been run on Windows.

Note

This PR description was generated with the help of GitHub Copilot.

lewing and others added 2 commits October 2, 2026 22:46
…o date

The browser and wasi call-helper tables under src/coreclr/vm/wasm/ are
generated from the framework's [UnmanagedCallersOnly] methods, P/Invokes and
QCalls, but nothing verified they matched the managed code. A stale table
builds fine and crashes the corerun that links it at startup.

- generate-coreclr-helpers.proj: add CallHelperTargetOS (browser|wasi) so a
  build that produced one flavor can regenerate just that flavor; expose it
  as -t/--target-os in the .sh/.cmd scripts.
- Default the scripts to Release. Entries are identical across Debug,
  Release and Checked, but the per-import assembly comments are not
  (library trimming differs), so the tables are canonically generated from
  Release, which is what CI builds.
- New wasm-coreclr-callhelpers-check.yml step, run on the Release
  browser_wasm CoreCLR leg (Linux host) and the wasi_wasm CoreCLR build-only
  leg: regenerates the leg's flavor and fails with the diff and the fix
  commands when src/coreclr/vm/wasm/<os>/ changes.
- New wasm_coreclr_callhelpers path subset (library sources, CoreLib,
  crossgen2 ReadyToRun/Common, WasmPInvokeModules.props) so managed-only
  changes run those legs.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The wasi tables predated moving the SocketAsyncEngine interop into CoreLib
(#130463) and were generated from Debug libraries. Only the per-import
assembly comments change; the entries are identical.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@lewing
lewing requested review from pavelsavara and a balanced review from Copilot October 3, 2026 03:51
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 6 pipeline(s).
10 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 added the arch-wasm WebAssembly architecture label Oct 3, 2026
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to 'arch-wasm': @lewing, @pavelsavara
See info in area-owners.md if you want to be subscribed.

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.

Copilot review overview

🟡 Changes recommended

The Windows script cannot process --target-os and mishandles help arguments because its target label is missing.

Review effort: Balanced
Findings: 1 High severity

Open (1)
What changed in this PR

Adds CI validation to keep checked-in CoreCLR WebAssembly call-helper tables synchronized with managed framework code.

Changes:

  • Adds target-specific helper generation for browser and WASI.
  • Runs regeneration checks in existing CI legs with expanded path triggers.
  • Documents Release as canonical and refreshes WASI comments.

Summary: Needs changes because the Windows wrapper’s new argument parsing is broken.

File Description
src/​coreclr/​vm/​wasm/​wasi/​callhelpers-pinvoke.cpp Refreshes generated assembly comments.
src/​coreclr/​vm/​wasm/​generate-coreclr-helpers.sh Adds target selection and Release default.
src/​coreclr/​vm/​wasm/​generate-coreclr-helpers.proj Supports target-specific generation.
src/​coreclr/​vm/​wasm/​generate-coreclr-helpers.md Documents generation and CI validation.
src/​coreclr/​vm/​wasm/​generate-coreclr-helpers.cmd Adds Windows target selection; contains a blocking parser defect.
eng/​pipelines/​runtime.yml Hooks checks into browser and WASI legs.
eng/​pipelines/​common/​templates/​wasm-coreclr-callhelpers-check.yml Implements regeneration comparison.
eng/​pipelines/​common/​templates/​wasi-wasm-coreclr-build-only.yml Adds the WASI post-build check.
eng/​pipelines/​common/​evaluate-default-paths.yml Adds relevant path triggers.

Comment thread src/coreclr/vm/wasm/generate-coreclr-helpers.cmd Outdated
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

@radekdoulik radekdoulik 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.

LGTM, it would be nice to have it produce patch file in the artifacts.

@lewing

lewing commented Oct 7, 2026

Copy link
Copy Markdown
Member Author

/ba-g failures are unrelated

@lewing
lewing merged commit 5f4c615 into main Oct 7, 2026
210 of 214 checks passed
@lewing
lewing deleted the lewing-wasm-callhelpers-drift-check branch October 7, 2026 13:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

arch-wasm WebAssembly architecture area-VM-coreclr

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants