Skip to content

[mono][llvm] Use llvm.minimum/maximum for scalar Math.Min/Max float ops - #129593

Merged
lewing merged 4 commits into
dotnet:mainfrom
lewing:mono-math-min-max-nan-fix
Jun 20, 2026
Merged

[mono][llvm] Use llvm.minimum/maximum for scalar Math.Min/Max float ops#129593
lewing merged 4 commits into
dotnet:mainfrom
lewing:mono-math-min-max-nan-fix

Conversation

@lewing

Copy link
Copy Markdown
Member

Summary

The scalar OP_FMIN/OP_FMAX/OP_RMIN/OP_RMAX lowering in the Mono LLVM backend uses fcmp ULE/UGE + select. That sequence has two problems:

1. Wrong NaN semantics (all targets)

With LLVMRealULE:

  • ULE(NaN, x) is true → select returns lhs = NaN
  • ULE(x, NaN) is also true → select returns lhs = x

So today Math.Min(x, NaN) returns x on every Mono+LLVM target. That violates Math.Min/MathF.Min ("if either input is NaN, NaN is returned") and IEEE 754‑2019 minimum semantics.

2. AArch64 backend miscompile (LLVM 23)

Under LLVM 23 (the toolchain that arrives via the emsdk 5.0.6 upgrade in #129299), the AArch64 ISel matches select(fcmp ogt/ult/… a, b), b, a and lowers it to fminnm/fmaxnm — IEEE 754‑2008 minNum/maxNum, which is NaN‑suppressing. Result: for any NaN input the non‑NaN operand is returned, silently dropping NaN through the System.Half software conversion path (Half.op_Explicit(float) clamps with float.Min(MaxHalfValueBelowInfinity, value)).

Concretely on iossimulator-arm64 with full AOT + LLVM (the failing CI leg in #129299):

Half.AcosPi(NaN) -> +Inf instead of NaN
Half.Lerp(+Inf, -Inf, t) -> +Inf instead of NaN
Half.DegreesToRadians(NaN) -> +Inf instead of NaN
(+ 15 more in HalfTests, + 12 in HalfTests_GenericMath)

Tracked as #129507.

Fix

  • src/mono/mono/mini/llvm-intrinsics.h: add llvm.minimum.f32/.f64 and llvm.maximum.f32/.f64. Both intrinsics have been in LLVM since v12, so every toolchain in use here has them.

  • src/mono/mono/mini/mini-llvm.c: split the scalar OP_FMIN/OP_FMAX/OP_RMIN/OP_RMAX cases out of the shared integer min/max group and emit them via llvm.minimum/llvm.maximum instead of fcmp + select. These intrinsics are NaN‑propagating (IEEE 754‑2019), matching the BCL spec; on AArch64 they lower to fmin/fmax (also NaN‑propagating) rather than fminnm/fmaxnm.

  • src/mono/mono/mini/intrinsics.c: remove the mono_use_fast_math gate on the Math.Min/Max(float|double) intrinsic recognition. The gate existed because the old lowering had wrong NaN behavior; with llvm.minimum it is NaN‑correct, so the gate is no longer needed. Removing it also makes the intrinsic actually fire in the common case (which is what closes the Half miscompile — the C# Math.Min body's IsNaN(val1) guard is otherwise stripped by LLVM after inlining into call sites with a constant val1, exposing the buggy select pattern to the AArch64 backend).

Validation

On a worktree of PR #129299 (emsdk 5.0.6 / LLVM 23 23.1.0-alpha.1.26314.2), iPhone 11 Pro iOS 26.5 simulator on M5 Max / macOS 26.5.1, full AOT + LLVM (MonoForceInterpreter=false /p:MonoEnableLLVM=true):

Test classBeforeAfter
System.Tests.HalfTests1424/1442 pass, 18 fail1442/1442 pass
System.Tests.HalfTests_GenericMath344/356 pass, 12 fail356/356 pass

This closes#129507 once the emsdk upgrade in #129299 lands.

Risk / scope

  • Scalar Math.Min/Max(float|double) semantics change on all Mono+LLVM targets: today's behavior is asymmetric and spec‑violating; new behavior matches the BCL docs. Existing tests that depended on the wrong asymmetric behavior would break, but none are expected.
  • Codegen impact: llvm.minimum/maximum lower to a single instruction on AArch64 (fmin/fmax) and to a short compare‑and‑select sequence on x86 without AVX‑10 (similar shape to today's fcmp + select).
  • Intrinsics already used for SIMD min/max on AArch64 (aarch64_neon_fmin/fmax); no codegen difference for the SIMD path.

cc @vargaz@kotlarmilos@lambdageek@tannergooding

Note

This pull request was produced by GitHub Copilot during an AI-assisted investigation. See https://gist.github.com/davidnguyen-tech/a8e373243f9cb0b0a5bde847a08323a1 for the original repro write-up and #129507 for the tracking issue. The miscompile was bisected to the AArch64 SDAG combine introducing fminnm after inlining; the fix uses LLVM's NaN-propagating min/max intrinsics, which also corrects long-standing asymmetric NaN behavior in Mono's Math.Min/MathF.Min lowering on every target.

The scalar OP_FMIN/OP_FMAX/OP_RMIN/OP_RMAX lowering in the Mono LLVM
backend used `fcmp ULE/UGE + select`. That sequence has two problems:
1. **Wrong NaN semantics.** With LLVMRealULE, ULE(NaN, x) is true so
select returns lhs=NaN, but ULE(x, NaN) is also true so select
returns lhs=x. That makes `Math.Min(x, NaN)` return `x` on every
Mono+LLVM target, violating Math.Min/MathF.Min/IEEE 754-2019
`minimum` semantics ("if either input is NaN, NaN is returned").
2. **AArch64 backend miscompile.** Under LLVM 23 (the toolchain that
arrives via emsdk 5.0.6, see dotnet#129299), the AArch64 ISel matches
`select(fcmp ogt/ult/... a, b), b, a` and lowers it to
`fminnm`/`fmaxnm` (IEEE 754-2008 minNum/maxNum, NaN-suppressing),
which returns the non-NaN operand for any NaN input. The
resulting NaN-suppression silently miscompiles the System.Half
software conversion path (`Half.op_Explicit(float)`), turning
`Half.AcosPi(NaN)`, `Half.Lerp(+inf, -inf, t)`,
`Math.Min(NaN, x) -> Half`, etc. into +/-Infinity instead of NaN
and causing 30 HalfTests failures on iossimulator-arm64 with
LLVM 23 (issue dotnet#129507).
Fix:
* Add llvm.minimum / llvm.maximum intrinsics for f32/f64 to
llvm-intrinsics.h. Both have existed in LLVM since v12, so all
toolchains currently in use have them.
* Split the scalar OP_FMIN/FMAX/RMIN/RMAX cases out of the shared
integer min/max group in mini-llvm.c and emit them via
llvm.minimum.f32 / llvm.maximum.f32 (and f64) instead of fcmp+select.
These intrinsics are documented as NaN-propagating (IEEE 754-2019),
which matches the .NET BCL spec, and on AArch64 they lower to
`fmin`/`fmax` (also NaN-propagating, not `fminnm`/`fmaxnm`).
* In intrinsics.c, remove the `mono_use_fast_math` gate on the
Math.Min/Max(float|double) recognition. The gate existed because
the old lowering had wrong NaN behavior; with llvm.minimum the
lowering is NaN-correct, so the gate is no longer needed.
Removing it also makes the scalar intrinsic fire for the common
case (which is what fixes the Half miscompile: the C# `Math.Min`
body's `IsNaN(val1)` guard is otherwise stripped by LLVM after
inlining into call sites with a constant `val1`, exposing the
buggy `select` pattern to the AArch64 backend).
Validation: with this patch applied on PR dotnet#129299's branch
(emsdk 5.0.6 / LLVM 23), `dotnet build /t:Test System.Runtime.Tests`
for `System.Tests.HalfTests` and `System.Tests.HalfTests_GenericMath`
on iossimulator-arm64 with full AOT + LLVM goes from 30 failures
(1424/1442 + 344/356) to **0 failures** (1442/1442 + 356/356).
The semantic fix (asymmetric NaN handling) also benefits non-AArch64
targets and non-Half code paths.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @steveisok, @vitek-karas
See info in area-owners.md if you want to be subscribed.

CopilotAI 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.

Pull request overview

This PR updates Mono’s LLVM backend lowering for scalar floating-point min/max so it uses LLVM’s llvm.minimum/llvm.maximum intrinsics (IEEE 754-2019, NaN-propagating), instead of the existing fcmp + select sequence.

Changes:

  • Add scalar llvm.minimum/llvm.maximum intrinsic entries for float/double and route scalar OP_FMIN/OP_FMAX/OP_RMIN/OP_RMAX through them.
  • Remove the mono_use_fast_math gate for recognizing Math.Min/Max(float|double) as intrinsics so the improved lowering applies in the default configuration.

Reviewed changes

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

FileDescription
src/mono/mono/mini/mini-llvm.cSwitch scalar float/double min/max lowering from fcmp+select to llvm.minimum/maximum intrinsics.
src/mono/mono/mini/llvm-intrinsics.hRegister llvm.minimum/llvm.maximum overloads for scalar f32/f64.
src/mono/mono/mini/intrinsics.cRecognize `Math.Min/Max(float

Comment threadsrc/mono/mono/mini/intrinsics.c Outdated
Comment threadsrc/mono/mono/mini/llvm-intrinsics.h Outdated
Comment threadsrc/mono/mono/mini/mini-llvm.c Outdated
Copilot reviewer noted the source comments only mentioned MathF.Min/Max,
but these intrinsic recognition + lowering paths handle Math.Min/Max
(double,double) and (float,float) — the MathF forwarders just call into
the Math overloads.
Comment-only change; no codegen impact.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
lewing added a commit to lewing/runtime that referenced this pull request Jun 18, 2026
…umber as intrinsics
Mono's LLVM backend has end-to-end plumbing for OP_FMA, OP_FCOPYSIGN,
OP_SQRTF, etc., but a handful of obvious BCL entry points fall through
to the C# implementations:
* `MathF.Abs(float)` and `MathF.Log(float)` are not recognized in the
MathF block of intrinsics.c, even though MathF.Sqrt/Sin/Cos/Exp/Log2/
Log10/Floor/Ceiling/Truncate all are. (Math.Abs(float) is recognized
via the Math (float) fallback, but Math.Log(float) doesn't exist —
only MathF.Log does.) Adding INTRINS_LOGF + OP_LOGF closes the
gap; MathF.Abs reuses the existing OP_ABSF / INTRINS_ABSF.
* `Single.MinNumber/MaxNumber` and `Double.MinNumber/MaxNumber`
(forwarders for INumber<TSelf>.{Min,Max}Number) are IEEE 754-2008
numNum semantics: when exactly one argument is NaN, return the
non-NaN; when both are NaN, return NaN. These map exactly to
llvm.minnum / llvm.maxnum, which on AArch64 lower to a single
fminnm/fmaxnm instruction. Without recognition the C# bodies inline
to a fcmp+select chain — semantically correct, but several
instructions on every target.
Mono had no recognition for the Single/Double primitive classes
previously; this adds a focused block that only handles MinNumber and
MaxNumber. (Min/Max forward to Math/MathF and are caught by the
existing recognition there.)
Adds:
* OP_LOGF / OP_FMINNUM / OP_FMAXNUM / OP_RMINNUM / OP_RMAXNUM in
mini-ops.h.
* INTRINS_LOGF / INTRINS_MINNUM / INTRINS_MINNUMF / INTRINS_MAXNUM /
INTRINS_MAXNUMF in llvm-intrinsics.h.
* Case handlers in mini-llvm.c (OP_LOGF reuses the existing
scalar-1-arg pattern; the four num/numF ops share a single block
that selects the correct intrinsic by opcode).
* Recognition in intrinsics.c — MathF.Abs/MathF.Log added to the
existing MathF (float) block; MinNumber/MaxNumber added in a new
Single/Double block placed after the Math block.
Validation: on the PR dotnet#129299 branch (emsdk 5.0.6 / LLVM 23) stacked
with the Min/Max NaN fix from PR dotnet#129593, full AOT + LLVM run of
System.Runtime.Tests on iossimulator-arm64:
HalfTests 1442/1442 pass
HalfTests_GenericMath 356/356 pass
SingleTests 1334/1334 pass
DoubleTests 1559/1559 pass
(no regression vs the dotnet#129593-only baseline; covers MinNumber/
MaxNumber on Single/Double)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Same root cause as the existing Half exemption tracked in dotnet#103347: per
the WebAssembly spec, `f64.min` / `f32.min` (and `f64.add` etc.)
canonicalize the NaN payload of the result. After this PR removes the
`mono_use_fast_math` gate, the recognized `Math.Min(double, double)` /
`MathF.Min(float, float)` calls lower through `llvm.minimum.f64` /
`llvm.minimum.f32` and on the WASM target both LLVM 19 and LLVM 23
emit a single `f64.min` / `f32.min` instruction. The
`BFloat16.op_Explicit(float|double)` software algorithm pipes the
input NaN through that Min and then expects the original NaN payload
to survive bit-for-bit -- the test uses `AssertEqual` which does a
strict `BFloat16ToUInt16Bits` comparison (with only an existing
in-test RISC-V escape-hatch for the same payload-not-preserved class
of issue at BFloat16Tests.cs line 2532).
`HalfTests.ExplicitConversion_FromSingle` already carries the same
exemption on `TestPlatforms.Browser` for this exact root cause (see
dotnet#103347 -- area owner @tannergooding's view there is that WASM should
ideally preserve NaN payloads using bitwise-`select` style lowerings,
but the Half test was filtered out as a temporary mitigation in the
meantime). Apply the same temporary mitigation to the BFloat16
equivalents which were just missed when the BFloat16 tests were
ported from Half in dotnet#98643.
Pre-PR these tests passed on Browser because the C# `Math.Min` body
inlined to a `select(fcmp olt val2, val1), val1, val2` pattern, which
the WASM backend lowers to a `select` instruction (a bitwise pick
that preserves the operand's NaN payload). Without the intrinsic
recognition there is no `f64.min` / `f32.min` in the lowered code at
all. This PR moves WASM AOT into consistency with the jiterpreter,
which has been emitting `f64.min` for `MINT_MIN` and canonicalizing
NaN through Math.Min for some time (jiterpreter-tables.ts line 315).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

CopilotAI 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.

Pull request overview

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

Comment threadsrc/mono/mono/mini/llvm-intrinsics.h
@pavelsavara

Copy link
Copy Markdown
Member

/azp run runtime-extra-platforms

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

@davidnguyen-tech

Copy link
Copy Markdown
Member

I've verified on my machine that this fixes the System.Tests.HalfTests and System.Tests.HalfTests_GenericMath tests on #129299

Comment threadsrc/mono/mono/mini/intrinsics.c
Comment threadsrc/mono/mono/mini/llvm-intrinsics.h
lewing added a commit to lewing/runtime that referenced this pull request Jun 19, 2026
…umber as intrinsics
Mono's LLVM backend has end-to-end plumbing for OP_FMA, OP_FCOPYSIGN,
OP_SQRTF, etc., but a handful of obvious BCL entry points fall through
to the C# implementations:
* `MathF.Abs(float)` and `MathF.Log(float)` are not recognized in the
MathF block of intrinsics.c, even though MathF.Sqrt/Sin/Cos/Exp/Log2/
Log10/Floor/Ceiling/Truncate all are. (Math.Abs(float) is recognized
via the Math (float) fallback, but Math.Log(float) doesn't exist —
only MathF.Log does.) Adding INTRINS_LOGF + OP_LOGF closes the
gap; MathF.Abs reuses the existing OP_ABSF / INTRINS_ABSF.
* `Single.MinNumber/MaxNumber` and `Double.MinNumber/MaxNumber`
(forwarders for INumber<TSelf>.{Min,Max}Number) are IEEE 754-2008
numNum semantics: when exactly one argument is NaN, return the
non-NaN; when both are NaN, return NaN. These map exactly to
llvm.minnum / llvm.maxnum, which on AArch64 lower to a single
fminnm/fmaxnm instruction. Without recognition the C# bodies inline
to a fcmp+select chain — semantically correct, but several
instructions on every target.
Mono had no recognition for the Single/Double primitive classes
previously; this adds a focused block that only handles MinNumber and
MaxNumber. (Min/Max forward to Math/MathF and are caught by the
existing recognition there.)
Adds:
* OP_LOGF / OP_FMINNUM / OP_FMAXNUM / OP_RMINNUM / OP_RMAXNUM in
mini-ops.h.
* INTRINS_LOGF / INTRINS_MINNUM / INTRINS_MINNUMF / INTRINS_MAXNUM /
INTRINS_MAXNUMF in llvm-intrinsics.h.
* Case handlers in mini-llvm.c (OP_LOGF reuses the existing
scalar-1-arg pattern; the four num/numF ops share a single block
that selects the correct intrinsic by opcode).
* Recognition in intrinsics.c — MathF.Abs/MathF.Log added to the
existing MathF (float) block; MinNumber/MaxNumber added in a new
Single/Double block placed after the Math block.
Validation: on the PR dotnet#129299 branch (emsdk 5.0.6 / LLVM 23) stacked
with the Min/Max NaN fix from PR dotnet#129593, full AOT + LLVM run of
System.Runtime.Tests on iossimulator-arm64:
HalfTests 1442/1442 pass
HalfTests_GenericMath 356/356 pass
SingleTests 1334/1334 pass
DoubleTests 1559/1559 pass
(no regression vs the dotnet#129593-only baseline; covers MinNumber/
MaxNumber on Single/Double)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
lewing added a commit to lewing/runtime that referenced this pull request Jun 19, 2026
…and jiterpreter
A few small WASM-relevant gaps in Mono's interpreter and jiterpreter
math intrinsic recognition.
**Truncate / CopySign**: `Math.Truncate(double)` / `MathF.Truncate(float)`
and `Math.CopySign(double, double)` / `MathF.CopySign(float, float)` were
not recognized at the IL -> MINT_* lowering step (transform.c), so the
interpreter walked through the BCL C# implementations on every call.
Adds:
* `MINT_TRUNC` / `MINT_TRUNCF` and `MINT_COPYSIGN` / `MINT_COPYSIGNF`
in `mintops.def`, in matched D/F-block positions so the existing
`(MINT_ASINF - MINT_ASIN)` shift in transform.c continues to map the
double opcodes to their float counterparts correctly.
* `MATH_UNOP(trunc) / MATH_UNOPF(truncf)` and
`MATH_BINOP(copysign) / MATH_BINOPF(copysignf)` dispatch in
`interp.c`.
* Recognition in the existing Math/MathF block in `transform.c`
(`Truncate` joins the `T...` unop dispatch alongside `Tan`/`Tanh`;
`CopySign` joins the binary block alongside `Min`/`Max`/`Pow`/`Atan2`).
In the jiterpreter all four new opcodes lower directly to native WASM
instructions via the `mathIntrinsicTable` -- `f64.trunc`/`f32.trunc`
and `f64.copysign`/`f32.copysign`. No libm import is needed.
**ScaleB**: `Math.ScaleB(double, int)` / `MathF.ScaleB(float, int)`
already had `MINT_SCALEB` / `MINT_SCALEBF` opcodes and interp dispatch
(via libm `scalbn` / `scalbnf`), but the jiterpreter had no handler --
encountering the opcode during tracing forced a trace bailout and fell
back to the interpreter for the rest of the trace. Because the
signature is `(float, int) -> float` (not uniform float-only), it
doesn't fit `mathIntrinsicTable`'s shape, so this mirrors the existing
`MINT_FMA` special case in `jiterpreter-trace-generator.ts` -- emit a
direct `callImport("scalbn"|"scalbnf")` and declare matching imports +
type signatures in `jiterpreter.ts`. No more bailout for `Math.ScaleB`
on hot WASM traces.
Stacked on top of:
* PR dotnet#129593 ([mono][llvm] Use llvm.minimum/maximum for scalar
Math.Min/Max float ops) -- this PR's interp/jiterpreter changes are
independent of the LLVM-AOT changes there, but stacking keeps the
Mono math-intrinsic story landing in one logical sequence.
* PR ?????? ([mono][llvm] Recognize MathF.Abs/Log and Single/Double.
MinNumber/MaxNumber as intrinsics) -- same reasoning.
Validation: builds clean on osx-arm64 (`./build.sh mono+libs -os osx
-arch arm64 -c Release`). Local browser-wasm verification was blocked
by an env-side `npm install` failure (Python 3.10+ requirement for
emscripten on this host); CI's `runtime-wasm` leg will exercise both
the interpreter and jiterpreter changes against the BCL Math /
MathF tests on browser-wasm.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
lewing added a commit to lewing/runtime that referenced this pull request Jun 19, 2026
…aN rows
PR dotnet#129593 added an `[ActiveIssue("dotnet#103347", TestPlatforms.Browser)]`
on `BFloat16Tests.ExplicitConversion_From{Single,Double}` to mirror
the existing exemption on `HalfTests.ExplicitConversion_FromSingle`.
Both were applied at the `[Theory]` level, which disables the entire
parameterized test on Browser -- including all ~47 non-NaN rows
covering ULP rounding, subnormals, sign handling, and overflow.
Only the 3-4 NaN rows per theory hit the WASM NaN-payload
canonicalization issue tracked in dotnet#103347.
Split each affected theory into two methods sharing a single data
source:
- `..._TestData_NonNaN` / `..._TestData_NaN` filter the existing
`..._TestData` via `Linq.Where(IsNaN)`.
- The original test method (`ExplicitConversion_FromSingle` etc.)
keeps its name and signature and now binds to the `_NonNaN`
member-data, so it runs on every platform including Browser.
- A new `ExplicitConversion_FromSingle_NaN` (and `_FromDouble_NaN`
for BFloat16) binds to the `_NaN` member-data and carries the
`[ActiveIssue]` exemption.
Net effect: Browser regains coverage of the ~47 non-NaN rows per
theory, while still skipping the 3-4 NaN-payload rows that dotnet#103347
covers. Other platforms run both methods (same total row count as
before).
Validation: built System.Runtime.Tests for net11.0-unix and ran
`--filter "FullyQualifiedName~ExplicitConversion_FromSingle|
ExplicitConversion_FromDouble"` on host osx-arm64. Result:
`Passed: 204, Failed: 0, Skipped: 0`, covering both Half and BFloat16
NonNaN+NaN variants.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
lewing added a commit to lewing/runtime that referenced this pull request Jun 19, 2026
…umber as intrinsics
Mono's LLVM backend has end-to-end plumbing for OP_FMA, OP_FCOPYSIGN,
OP_SQRTF, etc., but a handful of obvious BCL entry points fall through
to the C# implementations:
* `MathF.Abs(float)` and `MathF.Log(float)` are not recognized in the
MathF block of intrinsics.c, even though MathF.Sqrt/Sin/Cos/Exp/Log2/
Log10/Floor/Ceiling/Truncate all are. (Math.Abs(float) is recognized
via the Math (float) fallback, but Math.Log(float) doesn't exist —
only MathF.Log does.) Adding INTRINS_LOGF + OP_LOGF closes the
gap; MathF.Abs reuses the existing OP_ABSF / INTRINS_ABSF.
* `Single.MinNumber/MaxNumber` and `Double.MinNumber/MaxNumber`
(forwarders for INumber<TSelf>.{Min,Max}Number) are IEEE 754-2008
numNum semantics: when exactly one argument is NaN, return the
non-NaN; when both are NaN, return NaN. These map exactly to
llvm.minnum / llvm.maxnum, which on AArch64 lower to a single
fminnm/fmaxnm instruction. Without recognition the C# bodies inline
to a fcmp+select chain — semantically correct, but several
instructions on every target.
Mono had no recognition for the Single/Double primitive classes
previously; this adds a focused block that only handles MinNumber and
MaxNumber. (Min/Max forward to Math/MathF and are caught by the
existing recognition there.)
Adds:
* OP_LOGF / OP_FMINNUM / OP_FMAXNUM / OP_RMINNUM / OP_RMAXNUM in
mini-ops.h.
* INTRINS_LOGF / INTRINS_MINNUM / INTRINS_MINNUMF / INTRINS_MAXNUM /
INTRINS_MAXNUMF in llvm-intrinsics.h.
* Case handlers in mini-llvm.c (OP_LOGF reuses the existing
scalar-1-arg pattern; the four num/numF ops share a single block
that selects the correct intrinsic by opcode).
* Recognition in intrinsics.c — MathF.Abs/MathF.Log added to the
existing MathF (float) block; MinNumber/MaxNumber added in a new
Single/Double block placed after the Math block.
Validation: on the PR dotnet#129299 branch (emsdk 5.0.6 / LLVM 23) stacked
with the Min/Max NaN fix from PR dotnet#129593, full AOT + LLVM run of
System.Runtime.Tests on iossimulator-arm64:
HalfTests 1442/1442 pass
HalfTests_GenericMath 356/356 pass
SingleTests 1334/1334 pass
DoubleTests 1559/1559 pass
(no regression vs the dotnet#129593-only baseline; covers MinNumber/
MaxNumber on Single/Double)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
lewing added a commit to lewing/runtime that referenced this pull request Jun 19, 2026
PR dotnet#129593 added `[ActiveIssue("dotnet#103347", TestPlatforms.Browser)]` on
`BFloat16Tests.ExplicitConversion_From{Single,Double}` to mirror the
existing exemption on `HalfTests.ExplicitConversion_FromSingle`. Both
were applied at the `[Theory]` level, disabling the entire
parameterized test on Browser including the ~47 non-NaN rows covering
ULP rounding, subnormals, sign handling, and overflow. Only the ~4
NaN rows per theory actually hit the WASM NaN-payload canonicalization
issue tracked in dotnet#103347.
Filter the NaN rows out of the `..._TestData` member-data sources
when `PlatformDetection.IsWasm` is true (covers both Browser and
WASI, which share the underlying `f32.min` / `f64.min` /
`f32.add` / `f64.add` NaN-canonicalization behavior per the
WebAssembly spec), and drop the `[ActiveIssue]` from the test
methods. The non-NaN rows then run on every platform; on WASM the
test summary simply shows fewer rows (no skip-noise from the
ActiveIssue mechanism).
Validation: built System.Runtime.Tests for net11.0-unix and ran
`--filter "FullyQualifiedName~ExplicitConversion_FromSingle|
ExplicitConversion_FromDouble"` on host osx-arm64. Result:
`Passed: 204, Failed: 0, Skipped: 0` -- same row count as before on
non-WASM platforms.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@lewing
lewing enabled auto-merge (squash) June 19, 2026 21:59
@pavelsavara

Copy link
Copy Markdown
Member

/ba-g unrelated failures

@lewing
lewing merged commit f5a2d69 into dotnet:mainJun 20, 2026
121 of 123 checks passed
pavelsavara added a commit to pavelsavara/runtime that referenced this pull request Jun 20, 2026
lewing added a commit to lewing/runtime that referenced this pull request Jun 22, 2026
…and jiterpreter
A few small WASM-relevant gaps in Mono's interpreter and jiterpreter
math intrinsic recognition.
**Truncate / CopySign**: `Math.Truncate(double)` / `MathF.Truncate(float)`
and `Math.CopySign(double, double)` / `MathF.CopySign(float, float)` were
not recognized at the IL -> MINT_* lowering step (transform.c), so the
interpreter walked through the BCL C# implementations on every call.
Adds:
* `MINT_TRUNC` / `MINT_TRUNCF` and `MINT_COPYSIGN` / `MINT_COPYSIGNF`
in `mintops.def`, in matched D/F-block positions so the existing
`(MINT_ASINF - MINT_ASIN)` shift in transform.c continues to map the
double opcodes to their float counterparts correctly.
* `MATH_UNOP(trunc) / MATH_UNOPF(truncf)` and
`MATH_BINOP(copysign) / MATH_BINOPF(copysignf)` dispatch in
`interp.c`.
* Recognition in the existing Math/MathF block in `transform.c`
(`Truncate` joins the `T...` unop dispatch alongside `Tan`/`Tanh`;
`CopySign` joins the binary block alongside `Min`/`Max`/`Pow`/`Atan2`).
In the jiterpreter all four new opcodes lower directly to native WASM
instructions via the `mathIntrinsicTable` -- `f64.trunc`/`f32.trunc`
and `f64.copysign`/`f32.copysign`. No libm import is needed.
**ScaleB**: `Math.ScaleB(double, int)` / `MathF.ScaleB(float, int)`
already had `MINT_SCALEB` / `MINT_SCALEBF` opcodes and interp dispatch
(via libm `scalbn` / `scalbnf`), but the jiterpreter had no handler --
encountering the opcode during tracing forced a trace bailout and fell
back to the interpreter for the rest of the trace. Because the
signature is `(float, int) -> float` (not uniform float-only), it
doesn't fit `mathIntrinsicTable`'s shape, so this mirrors the existing
`MINT_FMA` special case in `jiterpreter-trace-generator.ts` -- emit a
direct `callImport("scalbn"|"scalbnf")` and declare matching imports +
type signatures in `jiterpreter.ts`. No more bailout for `Math.ScaleB`
on hot WASM traces.
Stacked on top of:
* PR dotnet#129593 ([mono][llvm] Use llvm.minimum/maximum for scalar
Math.Min/Max float ops) -- this PR's interp/jiterpreter changes are
independent of the LLVM-AOT changes there, but stacking keeps the
Mono math-intrinsic story landing in one logical sequence.
* PR ?????? ([mono][llvm] Recognize MathF.Abs/Log and Single/Double.
MinNumber/MaxNumber as intrinsics) -- same reasoning.
Validation: builds clean on osx-arm64 (`./build.sh mono+libs -os osx
-arch arm64 -c Release`). Local browser-wasm verification was blocked
by an env-side `npm install` failure (Python 3.10+ requirement for
emscripten on this host); CI's `runtime-wasm` leg will exercise both
the interpreter and jiterpreter changes against the BCL Math /
MathF tests on browser-wasm.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
lewing added a commit to lewing/runtime that referenced this pull request Jun 22, 2026
…umber as intrinsics
Mono's LLVM backend has end-to-end plumbing for OP_FMA, OP_FCOPYSIGN,
OP_SQRTF, etc., but a handful of obvious BCL entry points fall through
to the C# implementations:
* `MathF.Abs(float)` and `MathF.Log(float)` are not recognized in the
MathF block of intrinsics.c, even though MathF.Sqrt/Sin/Cos/Exp/Log2/
Log10/Floor/Ceiling/Truncate all are. (Math.Abs(float) is recognized
via the Math (float) fallback, but Math.Log(float) doesn't exist —
only MathF.Log does.) Adding INTRINS_LOGF + OP_LOGF closes the
gap; MathF.Abs reuses the existing OP_ABSF / INTRINS_ABSF.
* `Single.MinNumber/MaxNumber` and `Double.MinNumber/MaxNumber`
(forwarders for INumber<TSelf>.{Min,Max}Number) are IEEE 754-2008
numNum semantics: when exactly one argument is NaN, return the
non-NaN; when both are NaN, return NaN. These map exactly to
llvm.minnum / llvm.maxnum, which on AArch64 lower to a single
fminnm/fmaxnm instruction. Without recognition the C# bodies inline
to a fcmp+select chain — semantically correct, but several
instructions on every target.
Mono had no recognition for the Single/Double primitive classes
previously; this adds a focused block that only handles MinNumber and
MaxNumber. (Min/Max forward to Math/MathF and are caught by the
existing recognition there.)
Adds:
* OP_LOGF / OP_FMINNUM / OP_FMAXNUM / OP_RMINNUM / OP_RMAXNUM in
mini-ops.h.
* INTRINS_LOGF / INTRINS_MINNUM / INTRINS_MINNUMF / INTRINS_MAXNUM /
INTRINS_MAXNUMF in llvm-intrinsics.h.
* Case handlers in mini-llvm.c (OP_LOGF reuses the existing
scalar-1-arg pattern; the four num/numF ops share a single block
that selects the correct intrinsic by opcode).
* Recognition in intrinsics.c — MathF.Abs/MathF.Log added to the
existing MathF (float) block; MinNumber/MaxNumber added in a new
Single/Double block placed after the Math block.
Validation: on the PR dotnet#129299 branch (emsdk 5.0.6 / LLVM 23) stacked
with the Min/Max NaN fix from PR dotnet#129593, full AOT + LLVM run of
System.Runtime.Tests on iossimulator-arm64:
HalfTests 1442/1442 pass
HalfTests_GenericMath 356/356 pass
SingleTests 1334/1334 pass
DoubleTests 1559/1559 pass
(no regression vs the dotnet#129593-only baseline; covers MinNumber/
MaxNumber on Single/Double)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
lewing added a commit to lewing/runtime that referenced this pull request Jun 22, 2026
PR dotnet#129593 added `[ActiveIssue("dotnet#103347", TestPlatforms.Browser)]` on
`BFloat16Tests.ExplicitConversion_From{Single,Double}` to mirror the
existing exemption on `HalfTests.ExplicitConversion_FromSingle`. Both
were applied at the `[Theory]` level, disabling the entire
parameterized test on Browser including the ~47 non-NaN rows covering
ULP rounding, subnormals, sign handling, and overflow. Only the ~4
NaN rows per theory actually hit the WASM NaN-payload canonicalization
issue tracked in dotnet#103347.
Filter the NaN rows out of the `..._TestData` member-data sources
when `PlatformDetection.IsWasm` is true (covers both Browser and
WASI, which share the underlying `f32.min` / `f64.min` /
`f32.add` / `f64.add` NaN-canonicalization behavior per the
WebAssembly spec), and drop the `[ActiveIssue]` from the test
methods. The non-NaN rows then run on every platform; on WASM the
test summary simply shows fewer rows (no skip-noise from the
ActiveIssue mechanism).
Validation: built System.Runtime.Tests for net11.0-unix and ran
`--filter "FullyQualifiedName~ExplicitConversion_FromSingle|
ExplicitConversion_FromDouble"` on host osx-arm64. Result:
`Passed: 204, Failed: 0, Skipped: 0` -- same row count as before on
non-WASM platforms.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@dotnet-milestone-botdotnet-milestone-botBot added this to the 11.0-preview6 milestone Jun 22, 2026
lewing added a commit to lewing/runtime that referenced this pull request Jun 23, 2026
…and jiterpreter
A few small WASM-relevant gaps in Mono's interpreter and jiterpreter
math intrinsic recognition.
**Truncate / CopySign**: `Math.Truncate(double)` / `MathF.Truncate(float)`
and `Math.CopySign(double, double)` / `MathF.CopySign(float, float)` were
not recognized at the IL -> MINT_* lowering step (transform.c), so the
interpreter walked through the BCL C# implementations on every call.
Adds:
* `MINT_TRUNC` / `MINT_TRUNCF` and `MINT_COPYSIGN` / `MINT_COPYSIGNF`
in `mintops.def`, in matched D/F-block positions so the existing
`(MINT_ASINF - MINT_ASIN)` shift in transform.c continues to map the
double opcodes to their float counterparts correctly.
* `MATH_UNOP(trunc) / MATH_UNOPF(truncf)` and
`MATH_BINOP(copysign) / MATH_BINOPF(copysignf)` dispatch in
`interp.c`.
* Recognition in the existing Math/MathF block in `transform.c`
(`Truncate` joins the `T...` unop dispatch alongside `Tan`/`Tanh`;
`CopySign` joins the binary block alongside `Min`/`Max`/`Pow`/`Atan2`).
In the jiterpreter all four new opcodes lower directly to native WASM
instructions via the `mathIntrinsicTable` -- `f64.trunc`/`f32.trunc`
and `f64.copysign`/`f32.copysign`. No libm import is needed.
**ScaleB**: `Math.ScaleB(double, int)` / `MathF.ScaleB(float, int)`
already had `MINT_SCALEB` / `MINT_SCALEBF` opcodes and interp dispatch
(via libm `scalbn` / `scalbnf`), but the jiterpreter had no handler --
encountering the opcode during tracing forced a trace bailout and fell
back to the interpreter for the rest of the trace. Because the
signature is `(float, int) -> float` (not uniform float-only), it
doesn't fit `mathIntrinsicTable`'s shape, so this mirrors the existing
`MINT_FMA` special case in `jiterpreter-trace-generator.ts` -- emit a
direct `callImport("scalbn"|"scalbnf")` and declare matching imports +
type signatures in `jiterpreter.ts`. No more bailout for `Math.ScaleB`
on hot WASM traces.
Stacked on top of:
* PR dotnet#129593 ([mono][llvm] Use llvm.minimum/maximum for scalar
Math.Min/Max float ops) -- this PR's interp/jiterpreter changes are
independent of the LLVM-AOT changes there, but stacking keeps the
Mono math-intrinsic story landing in one logical sequence.
* PR ?????? ([mono][llvm] Recognize MathF.Abs/Log and Single/Double.
MinNumber/MaxNumber as intrinsics) -- same reasoning.
Validation: builds clean on osx-arm64 (`./build.sh mono+libs -os osx
-arch arm64 -c Release`). Local browser-wasm verification was blocked
by an env-side `npm install` failure (Python 3.10+ requirement for
emscripten on this host); CI's `runtime-wasm` leg will exercise both
the interpreter and jiterpreter changes against the BCL Math /
MathF tests on browser-wasm.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
lewing added a commit to lewing/runtime that referenced this pull request Jun 23, 2026
PR dotnet#129593 added `[ActiveIssue("dotnet#103347", TestPlatforms.Browser)]` on
`BFloat16Tests.ExplicitConversion_From{Single,Double}` to mirror the
existing exemption on `HalfTests.ExplicitConversion_FromSingle`. Both
were applied at the `[Theory]` level, disabling the entire
parameterized test on Browser including the ~47 non-NaN rows covering
ULP rounding, subnormals, sign handling, and overflow. Only the ~4
NaN rows per theory actually hit the WASM NaN-payload canonicalization
issue tracked in dotnet#103347.
Filter the NaN rows out of the `..._TestData` member-data sources
when `PlatformDetection.IsWasm` is true (covers both Browser and
WASI, which share the underlying `f32.min` / `f64.min` /
`f32.add` / `f64.add` NaN-canonicalization behavior per the
WebAssembly spec), and drop the `[ActiveIssue]` from the test
methods. The non-NaN rows then run on every platform; on WASM the
test summary simply shows fewer rows (no skip-noise from the
ActiveIssue mechanism).
Validation: built System.Runtime.Tests for net11.0-unix and ran
`--filter "FullyQualifiedName~ExplicitConversion_FromSingle|
ExplicitConversion_FromDouble"` on host osx-arm64. Result:
`Passed: 204, Failed: 0, Skipped: 0` -- same row count as before on
non-WASM platforms.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
lewing added a commit that referenced this pull request Jun 23, 2026
…test exemption (#129727)
## Summary
Follow-up to #129593 (`Math.Min/Max` LLVM lowering) and #129699
(`MathF.Abs/Log` + `Single/Double.MinNumber/MaxNumber` intrinsic gaps).
Two related WASM-targeted improvements:
### 1. `Math.Truncate` / `Math.CopySign` / `Math.ScaleB` for interpreter
+ jiterpreter (commit 1)
A few BCL math entry points have direct WASM hardware support
(`f64.trunc`, `f64.copysign`) or already had interp opcodes (`scalbn` /
`scalbnf`) but weren't wired all the way through:
* **`Math.Truncate(double)` / `MathF.Truncate(float)`**: not recognized
at the IL→MINT_* lowering step in `transform.c`, so the interpreter
walked the BCL C# implementations on every call. Adds
`MINT_TRUNC`/`MINT_TRUNCF` opcodes in matched D/F-block positions
(preserving the existing `(MINT_ASINF - MINT_ASIN)` shift in
`transform.c`), `MATH_UNOP(trunc)` / `MATH_UNOPF(truncf)` dispatch in
`interp.c`, and recognition in `transform.c`. In the jiterpreter these
lower directly to native WASM `f64.trunc` / `f32.trunc` via
`mathIntrinsicTable` — no libm import needed.
* **`Math.CopySign(double, double)` / `MathF.CopySign(float, float)`**:
same shape. New `MINT_COPYSIGN`/`MINT_COPYSIGNF` opcodes +
`MATH_BINOP(copysign)` dispatch + recognition. Jiterpreter lowers to
native `f64.copysign` / `f32.copysign`.
* **`Math.ScaleB(double, int)` / `MathF.ScaleB(float, int)`**: already
had `MINT_SCALEB`/`MINT_SCALEBF` opcodes and interp dispatch (via libm
`scalbn`/`scalbnf`), but the jiterpreter had no handler — encountering
the opcode during tracing forced a bailout to the interpreter for the
rest of the trace. The `(float, int) -> float` signature doesn't fit the
uniform-float-only `mathIntrinsicTable` shape, so this mirrors the
existing `MINT_FMA` special case in `jiterpreter-trace-generator.ts`:
direct `callImport("scalbn"/"scalbnf")` plus matching imports and type
signatures in `jiterpreter.ts`. No more trace-abort on `Math.ScaleB` in
hot WASM code.
### 2. Refine #129593's `[ActiveIssue]` scope on Half/BFloat16
conversion theories (commit 2)
PR #129593 added `[ActiveIssue("#103347", TestPlatforms.Browser)]` on
`BFloat16Tests.ExplicitConversion_From{Single,Double}` to mirror the
existing exemption on `HalfTests.ExplicitConversion_FromSingle`. Both
were applied at the `[Theory]` level, disabling the entire parameterized
test on Browser — including the ~47 non-NaN rows covering ULP rounding,
subnormals, sign handling, and overflow. Only the ~4 NaN rows per theory
actually hit the WASM `f32.min`/`f64.min`/`f32.add` NaN-payload
canonicalization issue tracked in #103347.
Filter the NaN rows out of the `..._TestData` member-data sources when
`PlatformDetection.IsWasm` is true (covers both Browser and WASI, which
share the underlying WASM-spec NaN canonicalization behavior), and drop
the `[ActiveIssue]` markers. Non-NaN rows then run on every platform; on
WASM the test summary simply shows fewer rows (no `[ActiveIssue]`
skip-noise).
## Validation
* `./build.sh mono+libs -os osx -arch arm64 -c Release` clean (0 errors,
0 warnings).
* Built `System.Runtime.Tests` for `net11.0-unix` and ran `--filter
"FullyQualifiedName~ExplicitConversion_FromSingle|FullyQualifiedName~ExplicitConversion_FromDouble"`
on host osx-arm64: **`Passed: 204, Failed: 0, Skipped: 0`**, matching
the row count from before the test-data filter on non-WASM.
* Browser-WASM verification was attempted locally but blocked by an
emscripten `npm install` failure on this host (Python 3.10+
requirement). CI's `runtime-wasm` legs will exercise both the new
interp/jiterp opcodes (against BCL `Math` / `MathF` tests) and the
test-data filter (against the affected `HalfTests`/`BFloat16Tests` rows)
on browser-wasm.
## Risk / scope
* Interpreter additions reuse existing `MATH_UNOP`/`MATH_BINOP` macros
and the proven jiterpreter `mathIntrinsicTable` lowering — no new
infrastructure.
* SCALEB jiterpreter path mirrors the existing `MINT_FMA` special case
exactly, just with the int second arg.
* Test-data filter is pure C# in two test files; the only
platform-conditional is `PlatformDetection.IsWasm`. Filter happens at
`MemberData` enumeration time, which runs in the target test process
(i.e. the WASM runtime on WASM, the host on every other platform), so
the row-set behaves consistently with execution.
* Drops the existing `HalfTests.ExplicitConversion_FromSingle`
`[ActiveIssue]` (had been in place since #103347 was filed). #103347
stays open — the underlying WASM payload-canonicalization is unchanged;
we're just narrowing the scope of the test exemption.
cc @vargaz@kotlarmilos@lambdageek@tannergooding@pavelsavara
> [!NOTE]
> This pull request was produced by GitHub Copilot during the
AI-assisted investigation that started with #129593 and continued
through #129699.
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
eiriktsarpalis pushed a commit that referenced this pull request Jul 15, 2026
…test exemption (#129727)
## Summary
Follow-up to #129593 (`Math.Min/Max` LLVM lowering) and #129699
(`MathF.Abs/Log` + `Single/Double.MinNumber/MaxNumber` intrinsic gaps).
Two related WASM-targeted improvements:
### 1. `Math.Truncate` / `Math.CopySign` / `Math.ScaleB` for interpreter
+ jiterpreter (commit 1)
A few BCL math entry points have direct WASM hardware support
(`f64.trunc`, `f64.copysign`) or already had interp opcodes (`scalbn` /
`scalbnf`) but weren't wired all the way through:
* **`Math.Truncate(double)` / `MathF.Truncate(float)`**: not recognized
at the IL→MINT_* lowering step in `transform.c`, so the interpreter
walked the BCL C# implementations on every call. Adds
`MINT_TRUNC`/`MINT_TRUNCF` opcodes in matched D/F-block positions
(preserving the existing `(MINT_ASINF - MINT_ASIN)` shift in
`transform.c`), `MATH_UNOP(trunc)` / `MATH_UNOPF(truncf)` dispatch in
`interp.c`, and recognition in `transform.c`. In the jiterpreter these
lower directly to native WASM `f64.trunc` / `f32.trunc` via
`mathIntrinsicTable` — no libm import needed.
* **`Math.CopySign(double, double)` / `MathF.CopySign(float, float)`**:
same shape. New `MINT_COPYSIGN`/`MINT_COPYSIGNF` opcodes +
`MATH_BINOP(copysign)` dispatch + recognition. Jiterpreter lowers to
native `f64.copysign` / `f32.copysign`.
* **`Math.ScaleB(double, int)` / `MathF.ScaleB(float, int)`**: already
had `MINT_SCALEB`/`MINT_SCALEBF` opcodes and interp dispatch (via libm
`scalbn`/`scalbnf`), but the jiterpreter had no handler — encountering
the opcode during tracing forced a bailout to the interpreter for the
rest of the trace. The `(float, int) -> float` signature doesn't fit the
uniform-float-only `mathIntrinsicTable` shape, so this mirrors the
existing `MINT_FMA` special case in `jiterpreter-trace-generator.ts`:
direct `callImport("scalbn"/"scalbnf")` plus matching imports and type
signatures in `jiterpreter.ts`. No more trace-abort on `Math.ScaleB` in
hot WASM code.
### 2. Refine #129593's `[ActiveIssue]` scope on Half/BFloat16
conversion theories (commit 2)
PR #129593 added `[ActiveIssue("#103347", TestPlatforms.Browser)]` on
`BFloat16Tests.ExplicitConversion_From{Single,Double}` to mirror the
existing exemption on `HalfTests.ExplicitConversion_FromSingle`. Both
were applied at the `[Theory]` level, disabling the entire parameterized
test on Browser — including the ~47 non-NaN rows covering ULP rounding,
subnormals, sign handling, and overflow. Only the ~4 NaN rows per theory
actually hit the WASM `f32.min`/`f64.min`/`f32.add` NaN-payload
canonicalization issue tracked in #103347.
Filter the NaN rows out of the `..._TestData` member-data sources when
`PlatformDetection.IsWasm` is true (covers both Browser and WASI, which
share the underlying WASM-spec NaN canonicalization behavior), and drop
the `[ActiveIssue]` markers. Non-NaN rows then run on every platform; on
WASM the test summary simply shows fewer rows (no `[ActiveIssue]`
skip-noise).
## Validation
* `./build.sh mono+libs -os osx -arch arm64 -c Release` clean (0 errors,
0 warnings).
* Built `System.Runtime.Tests` for `net11.0-unix` and ran `--filter
"FullyQualifiedName~ExplicitConversion_FromSingle|FullyQualifiedName~ExplicitConversion_FromDouble"`
on host osx-arm64: **`Passed: 204, Failed: 0, Skipped: 0`**, matching
the row count from before the test-data filter on non-WASM.
* Browser-WASM verification was attempted locally but blocked by an
emscripten `npm install` failure on this host (Python 3.10+
requirement). CI's `runtime-wasm` legs will exercise both the new
interp/jiterp opcodes (against BCL `Math` / `MathF` tests) and the
test-data filter (against the affected `HalfTests`/`BFloat16Tests` rows)
on browser-wasm.
## Risk / scope
* Interpreter additions reuse existing `MATH_UNOP`/`MATH_BINOP` macros
and the proven jiterpreter `mathIntrinsicTable` lowering — no new
infrastructure.
* SCALEB jiterpreter path mirrors the existing `MINT_FMA` special case
exactly, just with the int second arg.
* Test-data filter is pure C# in two test files; the only
platform-conditional is `PlatformDetection.IsWasm`. Filter happens at
`MemberData` enumeration time, which runs in the target test process
(i.e. the WASM runtime on WASM, the host on every other platform), so
the row-set behaves consistently with execution.
* Drops the existing `HalfTests.ExplicitConversion_FromSingle`
`[ActiveIssue]` (had been in place since #103347 was filed). #103347
stays open — the underlying WASM payload-canonicalization is unchanged;
we're just narrowing the scope of the test exemption.
cc @vargaz@kotlarmilos@lambdageek@tannergooding@pavelsavara
> [!NOTE]
> This pull request was produced by GitHub Copilot during the
AI-assisted investigation that started with #129593 and continued
through #129699.
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
ManickaP pushed a commit to ManickaP/runtime that referenced this pull request Jul 22, 2026
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jul 23, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

MonoAOT: LLVM 23 regressions in Half NaN handling

6 participants

@lewing@pavelsavara@davidnguyen-tech@steveisok@tannergooding