[wasm] Implement WasmBase LeadingZeroCount/TrailingZeroCount JIT intrinsics#130938
Conversation
…insics
The internal `System.Runtime.Intrinsics.Wasm.WasmBase` class (already on main)
declares LeadingZeroCount/TrailingZeroCount (int/uint/long/ulong) as self-recursive
intrinsic stubs, but RyuJIT-wasm left them unimplemented (NI_Illegal in the WasmBase
IsaRange), so they fell back to the managed software implementation.
Wire the two intrinsics into the RyuJIT-wasm backend:
- hwintrinsic.cpp: give WasmBase a real IsaRange { FIRST_NI_WasmBase, LAST_NI_WasmBase }.
- hwintrinsiclistwasm.h: add the two scalar HARDWARE_INTRINSIC entries
(HW_Category_Scalar, HW_Flag_SpecialImport|HW_Flag_NoFloatingPointUsed), matching the
established xarch scalar LeadingZeroCount/TrailingZeroCount pattern.
- hwintrinsicwasm.cpp: special-import each to a GT_INTRINSIC over the existing
NI_PRIMITIVE_LeadingZeroCount/TrailingZeroCount, which wasm codegen already lowers to
i32/i64.clz and i32/i64.ctz.
System.Numerics.BitOperations (LeadingZeroCount/Log2/TrailingZeroCount) routes through
WasmBase.IsSupported, so this makes core bit operations use native wasm clz/ctz on
RyuJIT-wasm instead of the software fallback.
No public API change: WasmBase is internal and already shipped on main, so no API review
is required.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: eddc353d-673e-4210-9bb6-6d0b5f6f9245
|
Azure Pipelines: Successfully started running 5 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. |
|
Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch |
|
Validated end-to-end under wasm ReadyToRun in the R2R-on-wasm prototype (which has the runtime execution path not yet on Both cases lower into the existing Note AI-assisted comment. |
There was a problem hiding this comment.
Pull request overview
Implements missing RyuJIT-wasm support for the System.Runtime.Intrinsics.Wasm.WasmBase scalar LeadingZeroCount / TrailingZeroCount intrinsics so they import as existing NI_PRIMITIVE_*ZeroCount GT_INTRINSIC nodes (which wasm codegen already lowers to i32/i64.clz and i32/i64.ctz).
Changes:
- Enables the WasmBase instruction set by giving it a real
IsaRangein the HW intrinsic ISA range table. - Adds WasmBase
LeadingZeroCount/TrailingZeroCountentries to the wasm HW intrinsic list withHW_Flag_SpecialImport. - Special-imports the two intrinsics in
hwintrinsicwasm.cppby producingGT_INTRINSICnodes overNI_PRIMITIVE_LeadingZeroCount/NI_PRIMITIVE_TrailingZeroCount.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
src/coreclr/jit/hwintrinsicwasm.cpp |
Special-import WasmBase LZC/TZC to existing NI_PRIMITIVE_*ZeroCount GT_INTRINSIC nodes. |
src/coreclr/jit/hwintrinsiclistwasm.h |
Adds the WasmBase scalar intrinsic table entries and defines FIRST_NI_WasmBase / LAST_NI_WasmBase. |
src/coreclr/jit/hwintrinsic.cpp |
Updates the wasm ISA range table so WasmBase maps to the new named-intrinsic range. |
|
cc @dotnet/wasm-contrib |
|
Since these are technically in the hardware intrinsics table, I think we should construct the nodes with |
|
Thanks @adamperlin. I looked at routing these through Note This comment was authored with the assistance of GitHub Copilot. |
|
Azure Pipelines: Successfully started running 5 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. |
Address review feedback (@tannergooding): these two intrinsics are always special-imported and rewritten into NI_PRIMITIVE_*ZeroCount GT_INTRINSIC nodes, so a GenTreeHWIntrinsic with the NI_WasmBase_* id is never created. HW_Flag_InvalidNodeId expresses that precisely -- HasSpecialImport() already covers it (so it still routes through impSpecialIntrinsic), and it additionally asserts the id never materializes as a HW intrinsic node -- matching how the Vector helper intrinsics are flagged. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: eddc353d-673e-4210-9bb6-6d0b5f6f9245
|
Rerunning failed tests. |
|
/ba-g checked build timeouts are not related |
Summary
The internal
System.Runtime.Intrinsics.Wasm.WasmBaseclass (already onmain) declaresLeadingZeroCount/TrailingZeroCount(int/uint/long/ulong) as self-recursive intrinsic stubs, but the RyuJIT-wasm backend left them unimplemented — the WasmBaseIsaRangewas{ NI_Illegal, NI_Illegal }, so these fell back to the managed software implementation.This wires the two intrinsics into the RyuJIT-wasm backend so they lower to native wasm
clz/ctz:hwintrinsic.cpp— give WasmBase a real IsaRange{ FIRST_NI_WasmBase, LAST_NI_WasmBase }.hwintrinsiclistwasm.h— add the two scalarHARDWARE_INTRINSICentries (HW_Category_Scalar,HW_Flag_SpecialImport|HW_Flag_NoFloatingPointUsed), matching the established xarch scalarLeadingZeroCount/TrailingZeroCountpattern (simdSize0).hwintrinsicwasm.cpp— special-import each to aGT_INTRINSICover the existingNI_PRIMITIVE_LeadingZeroCount/NI_PRIMITIVE_TrailingZeroCount, which wasm codegen already lowers toi32.clz/i64.clzandi32.ctz/i64.ctz(seecodegenwasm.cpp).Consumer:
System.Numerics.BitOperations(LeadingZeroCount/Log2/TrailingZeroCount) routes throughWasmBase.LeadingZeroCount(value)whenWasmBase.IsSupported. Implementing these makes core bit operations use native wasmclz/ctzon RyuJIT-wasm instead of the software fallback.API review
No new or changed public API.
WasmBaseisinternaland already shipped onmain; this PR only implements its JIT lowering. No API review required.Validation
clrjit_universal_wasm_arm64) builds with 0 warnings / 0 errors after a full recompile of the changed sources (clr.jit -c Release). Baselineclr+libs -rc Releaseandclr -os browser -c Releasealso build clean.NI_PRIMITIVE_*ZeroCountGT_INTRINSICnodes forTYP_INT/TYP_LONGare already handled bycodegenwasm.cpp(emitsi32/i64.clz/ctz), so the intrinsics reuse a proven codegen path.corerun.jsfails atcoreclr_initialize(0x80070057) even for the pre-builttieringtest.dllsmoke test — a pre-existing bring-up limitation of the experimental runtime in this environment (note theSystem.Private.CoreLib.NotReadyYet.wasmartifact), unrelated to this change and occurring before any JIT/intrinsic code runs. ExistingSystem.Numerics.BitOperationstests already coverLeadingZeroCount/TrailingZeroCountfor all overloads and will exercise this path once the wasm runtime can execute.Opening as draft because end-to-end runtime execution could not be verified locally (blocked by the experimental runtime, not by this change).
Note
This pull request was authored with the assistance of GitHub Copilot.