Skip to content

[Wasm RyuJit] codegen for some intrinsics#124575

Open
AndyAyersMS wants to merge 2 commits intodotnet:mainfrom
AndyAyersMS:WasmSomeIntrinsics
Open

[Wasm RyuJit] codegen for some intrinsics#124575
AndyAyersMS wants to merge 2 commits intodotnet:mainfrom
AndyAyersMS:WasmSomeIntrinsics

Conversation

@AndyAyersMS
Copy link
Member

Handle some math intrinsics that map to Wasm operations.

Handle some math intrinsics that map to Wasm operations.
Copilot AI review requested due to automatic review settings February 18, 2026 23:20
@AndyAyersMS
Copy link
Member Author

@dotnet/jit-contrib PTAL

@github-actions github-actions bot added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Feb 18, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds initial WebAssembly (Wasm) RyuJIT support for lowering selected System.Math intrinsics directly to Wasm numeric instructions, enabling more operations to be emitted without helper calls.

Changes:

  • Teach the importer which System.Math intrinsics are target-supported on TARGET_WASM.
  • Add Wasm codegen handling for GT_INTRINSIC and emit the corresponding Wasm instructions for supported intrinsics.

Reviewed changes

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

File Description
src/coreclr/jit/importercalls.cpp Adds a TARGET_WASM arm to Compiler::IsTargetIntrinsic for a set of System.Math intrinsics.
src/coreclr/jit/codegenwasm.cpp Handles GT_INTRINSIC in Wasm codegen and introduces CodeGen::genIntrinsic to emit Wasm ops for supported intrinsics.

@am11 am11 added the arch-wasm WebAssembly architecture label Feb 18, 2026
@dotnet-policy-service
Copy link
Contributor

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

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings February 18, 2026 23:43
@EgorBo
Copy link
Member

EgorBo commented Feb 18, 2026

I think you'd want @tannergooding's review. These intrinsics are tricky around IEEE754.

E.g. it seems f32.max in WASM corresponds to IEEE 754-2019's maximum. Is it the same for Math.Max? Same concerns for min, round

Copy link
Contributor

Copilot AI left a comment

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 2 out of 2 changed files in this pull request and generated 3 comments.

Comment on lines +1536 to +1548
case PackIntrinsicAndType(NI_System_Math_Max, TYP_FLOAT):
ins = INS_f32_max;
break;
case PackIntrinsicAndType(NI_System_Math_Max, TYP_DOUBLE):
ins = INS_f64_max;
break;

case PackIntrinsicAndType(NI_System_Math_Min, TYP_FLOAT):
ins = INS_f32_min;
break;
case PackIntrinsicAndType(NI_System_Math_Min, TYP_DOUBLE):
ins = INS_f64_min;
break;
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

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

Math.{F}.Min/Max are specified/implemented to return the NaN operand verbatim (preserving payload and, when both are NaN, preserving the first operand). Wasm f32.min/f32.max and f64.min/f64.max allow NaN canonicalization and generally won't preserve NaN payloads, so directly emitting these ops can change observable results. Consider either (a) not treating NI_System_Math_Min/Max as a target intrinsic on Wasm yet, or (b) lowering to a sequence that explicitly checks for NaN and selects the original NaN input to preserve payload semantics before/after using min/max.

Copilot uses AI. Check for mistakes.
genConsumeOperands(treeNode);

// Handle intrinsics that can be implemented by target-specific instructions
instruction ins;
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

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

instruction ins; is left uninitialized and then assigned in the switch. Some toolchains warn on this pattern even with unreached() in the default case. Initializing ins to INS_invalid (or similar) avoids potential build breaks from -Wmaybe-uninitialized and matches the pattern used in other backends.

Suggested change
instruction ins;
instruction ins = INS_invalid;

Copilot uses AI. Check for mistakes.
Comment on lines +8486 to +8497
// TODO-WASM-CQ: we can likely support more intrinsics here
switch (intrinsicName)
{
case NI_System_Math_Abs:
case NI_System_Math_Ceiling:
case NI_System_Math_Floor:
case NI_System_Math_Max:
case NI_System_Math_Min:
case NI_System_Math_Round:
case NI_System_Math_Sqrt:
case NI_System_Math_Truncate:
return true;
Copy link

Copilot AI Feb 18, 2026

Choose a reason for hiding this comment

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

IsTargetIntrinsic now claims NI_System_Math_Min/Max are implemented by target instructions on Wasm. If codegen uses Wasm min/max, be aware that Wasm min/max do not preserve NaN payloads while System.Math/MathF implementations intentionally return a NaN argument verbatim; this can be an observable behavior difference. Consider dropping Min/Max from the Wasm target-intrinsic list until codegen preserves NaN payload semantics (or implement the required NaN handling sequence in codegen).

Copilot uses AI. Check for mistakes.

#elif defined(TARGET_WASM)

// TODO-WASM-CQ: we can likely support more intrinsics here
Copy link
Member

Choose a reason for hiding this comment

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

For example:

case NI_System_Math_CopySign:         // f32.copysign / f64.copysign
case NI_System_Math_MaxNative:        // f32.max / f64.max
case NI_System_Math_MinNative:        // f32.min / f64.min
case PackIntrinsicAndType(NI_System_Math_CopySign, TYP_FLOAT):
    ins = INS_f32_copysign;
    break;
case PackIntrinsicAndType(NI_System_Math_CopySign, TYP_DOUBLE):
    ins = INS_f64_copysign;
    break;

@AndyAyersMS
Copy link
Member Author

The Wasm spec says:

NaN Propagation

When the result of a floating-point operator other than fneg, fabs, or fcopysign is a NaN, then its sign is non-deterministic and the payload is computed as follows:
• If the payload of all NaN inputs to the operator is canonical (including the case that there are no NaN inputs), then the payload of the output is canonical as well.
• Otherwise the payload is picked non-deterministically among all arithmetic NaNs; that is, its most significant bit is 1 and all others are unspecified.

If we must explicitly check for and propagate NaN inputs this may tip the scale in favor of always using a helper call (especially for binops).

@SingleAccretion
Copy link
Contributor

Notably, the NaN propagation issue affects all FP operations, not just intrinsics. What are our actual semantic requirements for it (required for all ops, required only for min/max, required for all Math)?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

arch-wasm WebAssembly architecture area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

Comments