Skip to content

[RemoveNonJSOps] Stub out trunc_sat and convert ops - #9313

Merged
tlively merged 3 commits into
mainfrom
sub-unsupported-js-trunc-sat
Oct 28, 2026
Merged

tlively merged 3 commits into
mainfrom
sub-unsupported-js-trunc-sat

Conversation

@tlively

@tlively tlively commented Oct 27, 2026 •

Copy link
Copy Markdown
Member

wasm2js and I64ToI32Lowering lower 32-bit and 64-bit trunc_sat instructions using non-saturating conversions (~~x, ~~x >>> 0, and lowerTruncFloatToInt) that wrap rather than saturate on out-of-range inputs. Unlike trapping trunc instructions (which trap on out-of-range inputs in the interpreter and are not constant-folded by Precompute), trunc_sat instructions do not trap in WebAssembly and are folded to saturated constants when optimizing, causing differential mismatches in Wasm2JS fuzzing. Stub out all trunc_sat unary operations in StubUnsupportedJSOpsPass::visitUnary.

Also stub out f32.convert_i64_s because its wasm2js implementation rounds twice and can therefore have slightly different results than the Wasm instruction.

`wasm2js` and `I64ToI32Lowering` lower 32-bit and 64-bit `trunc_sat` instructions using non-saturating conversions (`~~x`, `~~x >>> 0`, and `lowerTruncFloatToInt`) that wrap rather than saturate on out-of-range inputs. Unlike trapping `trunc` instructions (which trap on out-of-range inputs in the interpreter and are not constant-folded by `Precompute`), `trunc_sat` instructions do not trap in WebAssembly and are folded to saturated constants when optimizing, causing differential mismatches in `Wasm2JS` fuzzing. Stub out all `trunc_sat` unary operations in `StubUnsupportedJSOpsPass::visitUnary`.
@tlively
tlively requested a review from a team as a code owner October 27, 2026 04:09
@tlively
tlively requested review from kripken and removed request for a team October 27, 2026 04:09

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request updates the RemoveNonJSOps pass to stub out non-trapping float-to-int conversions (trunc_sat operations) and adds corresponding test coverage. The feedback suggests also stubbing out signed 64-bit integer to 32-bit float conversions (ConvertSInt64ToFloat32), as they suffer from the same double-rounding precision issues as the unsigned variant.

Comment thread src/passes/RemoveNonJSOps.cpp Outdated
Comment on lines +359 to +371
case ConvertUInt64ToFloat32:
// See detailed comment in lowerConvertIntToFloat in
// I64ToI32Lowering.cpp.
case TruncSatSFloat32ToInt32:
case TruncSatUFloat32ToInt32:
case TruncSatSFloat64ToInt32:
case TruncSatUFloat64ToInt32:
case TruncSatSFloat32ToInt64:
case TruncSatUFloat32ToInt64:
case TruncSatSFloat64ToInt64:
case TruncSatUFloat64ToInt64:
// f32.convert_i64_u does not have perfect rounding in all cases (see
// lowerConvertIntToFloat in I64ToI32Lowering.cpp), and trunc_sat
// operations are lowered using non-saturating conversions in wasm2js
// and I64ToI32Lowering.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The signed 64-bit integer to 32-bit float conversion (ConvertSInt64ToFloat32 / f32.convert_i64_s) suffers from the exact same double-rounding precision issue as the unsigned version (ConvertUInt64ToFloat32 / f32.convert_i64_u). In I64ToI32Lowering.cpp, both are lowered by first converting to f64 and then demoting to f32, which can lead to rounding mismatches during fuzzing. We should stub out ConvertSInt64ToFloat32 as well. Please also add a corresponding test case in test/lit/passes/stub-unsupported-js.wast.

      case ConvertSInt64ToFloat32:
      case ConvertUInt64ToFloat32:
      case TruncSatSFloat32ToInt32:
      case TruncSatUFloat32ToInt32:
      case TruncSatSFloat64ToInt32:
      case TruncSatUFloat64ToInt32:
      case TruncSatSFloat32ToInt64:
      case TruncSatUFloat32ToInt64:
      case TruncSatSFloat64ToInt64:
      case TruncSatUFloat64ToInt64:
        // f32.convert_i64_s and f32.convert_i64_u do not have perfect rounding
        // in all cases (see lowerConvertIntToFloat in I64ToI32Lowering.cpp),
        // and trunc_sat operations are lowered using non-saturating
        // conversions in wasm2js and I64ToI32Lowering.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

A surprisingly useful comment!

`f32.convert_i64_s` is lowered through an intermediate `f64` in `I64ToI32Lowering::lowerConvertIntToFloat` just like `f32.convert_i64_u`, so it suffers from the same double-rounding precision issue.
@kripken

kripken commented Oct 28, 2026

Copy link
Copy Markdown
Member

Should the title be [RemoveNonJSOps] [..] - ? The current prefix seems odd.

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

otherwise lgtm

@tlively tlively changed the title [StubUnsupportedJSOps] Stub out trunc_sat ops [RemoveNonJSOps] Stub out trunc_sat ops Oct 28, 2026
@tlively tlively changed the title [RemoveNonJSOps] Stub out trunc_sat ops [RemoveNonJSOps] Stub out trunc_sat and convert ops Oct 28, 2026
@tlively
tlively merged commit a2c4b9c into main Oct 28, 2026
17 checks passed
@tlively
tlively deleted the sub-unsupported-js-trunc-sat branch October 28, 2026 16:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants