<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<!DOCTYPE bugzilla SYSTEM "https://bugs.webkit.org/page.cgi?id=bugzilla.dtd">

<bugzilla version="5.0.4.1"
          urlbase="https://bugs.webkit.org/"
          
          maintainer="admin@webkit.org"
>

    <bug>
          <bug_id>312689</bug_id>
          
          <creation_ts>2026-04-18 13:08:49 -0700</creation_ts>
          <short_desc>BBQ JIT Constant Folding Signed Zero Mishandling in `computeFloatingPointMinOrMax`</short_desc>
          <delta_ts>2026-04-22 11:19:25 -0700</delta_ts>
          <reporter_accessible>1</reporter_accessible>
          <cclist_accessible>1</cclist_accessible>
          <classification_id>1</classification_id>
          <classification>Unclassified</classification>
          <product>WebKit</product>
          <component>WebAssembly</component>
          <version>WebKit Nightly Build</version>
          <rep_platform>PC</rep_platform>
          <op_sys>Linux</op_sys>
          <bug_status>RESOLVED</bug_status>
          <resolution>FIXED</resolution>
          
          
          <bug_file_loc></bug_file_loc>
          <status_whiteboard></status_whiteboard>
          <keywords>InRadar</keywords>
          <priority>P2</priority>
          <bug_severity>Minor</bug_severity>
          <target_milestone>---</target_milestone>
          
          
          <everconfirmed>1</everconfirmed>
          <reporter>parkjuny</reporter>
          <assigned_to name="Keith Miller">keith_miller</assigned_to>
          <cc>keith_miller</cc>
    
    <cc>webkit-bug-importer</cc>
          

      

      

      

          <comment_sort_order>oldest_to_newest</comment_sort_order>  
          <long_desc isprivate="0" >
    <commentid>2201876</commentid>
    <comment_count>0</comment_count>
      <attachid>479203</attachid>
    <who name="">parkjuny</who>
    <bug_when>2026-04-18 13:08:49 -0700</bug_when>
    <thetext>Created attachment 479203
poc.js

## Summary

The `computeFloatingPointMinOrMax` function in the BBQ JIT (`WasmBBQJIT.h:1679`) uses `std::min`/`std::max` for constant folding of `f32.min`, `f32.max`, `f64.min`, and `f64.max`. Because `+0.0 == -0.0` in IEEE 754, `std::min(+0.0, -0.0)` returns `+0.0` and `std::max(-0.0, +0.0)` returns `-0.0` — both wrong. This produces an observable **IPInt vs BBQ discrepancy**: the same Wasm functions return correct results before tier-up (IPInt interpreter) and wrong results after (BBQ JIT).

## Bug

### Detail

The buggy function is `computeFloatingPointMinOrMax` at `WasmBBQJIT.h:1679–1691`:

```cpp
// Source/JavaScriptCore/wasm/WasmBBQJIT.h:1679-1691
template&lt;MinOrMax IsMinOrMax, typename FloatType&gt;
constexpr FloatType computeFloatingPointMinOrMax(FloatType left, FloatType right)
{
    if (std::isnan(left))
        return left;
    if (std::isnan(right))
        return right;

    if constexpr (IsMinOrMax == MinOrMax::Min)
        return std::min&lt;FloatType&gt;(left, right);  // BUG: returns left when left==right
    else
        return std::max&lt;FloatType&gt;(left, right);  // BUG: returns left when left==right
}
```

This is the **constant folding** path, invoked via `EMIT_BINARY` (`WasmBBQJIT.h:1576`) when both operands are compile-time constants. All four operations use it (`WasmBBQJIT.cpp:2325–2387`). The baked-in wrong constant is returned on every call after tier-up.

The **runtime path** (`emitFloatingPointMinOrMax`, `WasmBBQJIT.cpp:2257`) is correct: on ARM64 it uses native `floatMin`/`doubleMin`/`floatMax`/`doubleMax` JIT operations; on other architectures it uses bitwise OR (min) and AND (max) on equal operands to correctly yield −0 and +0 respectively.

A correct reference implementation already exists in `MathCommon.h:285–302` (`fMax` at 285, `fMin` at 295), which explicitly guards for the opposite-sign zero case before calling `std::min`/`std::max`.

### Trigger Conditions

1. A WebAssembly module contains `f32.min`, `f32.max`, `f64.min`, or `f64.max` where **both operands are compile-time constants** of opposite-sign zero
2. The function is called enough times to trigger BBQ tier-up (~1000 calls; no special flags required)
3. After tier-up, BBQ&apos;s constant-folded result — with the wrong sign — is returned on every subsequent call

## Version

### Reproduced Version

- `main` branch latest commit (2026/04/19): `a4390137a403`
- JavaScriptCore (WebKit trunk)

## Reproduction Case

### Release Build

```bash
jsc poc.js
```

```
f32.min(+0,-0): IPInt=-0 BBQ=+0 expected=-0 FAIL
f32.max(-0,+0): IPInt=+0 BBQ=-0 expected=+0 FAIL
f64.min(+0,-0): IPInt=-0 BBQ=+0 expected=-0 FAIL
f64.max(-0,+0): IPInt=+0 BBQ=-0 expected=+0 FAIL
```

All four operations produce correct results in the IPInt interpreter and wrong results after BBQ tier-up, confirming the bug is isolated to BBQ constant folding.

### PoC Code (`poc.js`)

```js
// JSC BBQ JIT signed zero constant folding bug
// IPInt (interpreter) is correct; BBQ JIT constant folding is not.

function uleb128(n) {
    const out = [];
    do { let b = n &amp; 0x7f; n &gt;&gt;&gt;= 7; if (n) b |= 0x80; out.push(b); } while (n);
    return out;
}
function sec(id, body) { return [id, ...uleb128(body.length), ...body]; }
function str(s) { return [...uleb128(s.length), ...s.split(&apos;&apos;).map(c =&gt; c.charCodeAt(0))]; }
function fn(code) { const b = [0x00, ...code, 0x0b]; return [...uleb128(b.length), ...b]; }

const wasm = new Uint8Array([
    0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00,
    ...sec(0x01, [0x02,
        0x60, 0x00, 0x01, 0x7d,  // () -&gt; f32
        0x60, 0x00, 0x01, 0x7c,  // () -&gt; f64
    ]),
    ...sec(0x03, [0x04, 0x00, 0x00, 0x01, 0x01]),
    ...sec(0x07, [0x04,
        ...str(&quot;f32_min&quot;), 0x00, 0x00,  // f32.min(+0, -0)
        ...str(&quot;f32_max&quot;), 0x00, 0x01,  // f32.max(-0, +0)
        ...str(&quot;f64_min&quot;), 0x00, 0x02,  // f64.min(+0, -0)
        ...str(&quot;f64_max&quot;), 0x00, 0x03,  // f64.max(-0, +0)
    ]),
    ...sec(0x0a, [0x04,
        ...fn([0x43,0x00,0x00,0x00,0x00, 0x43,0x00,0x00,0x00,0x80, 0x96]),  // f32.min(+0, -0)
        ...fn([0x43,0x00,0x00,0x00,0x80, 0x43,0x00,0x00,0x00,0x00, 0x97]),  // f32.max(-0, +0)
        ...fn([0x44,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x44,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80, 0xa4]),  // f64.min(+0, -0)
        ...fn([0x44,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80, 0x44,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0xa5]),  // f64.max(-0, +0)
    ]),
]);

const instance = new WebAssembly.Instance(new WebAssembly.Module(wasm));
const sign = x =&gt; (x === 0 &amp;&amp; 1/x &lt; 0) ? &quot;-0&quot; : &quot;+0&quot;;

// Before BBQ tier-up: IPInt interpreter (correct)
// Access instance.exports.X() directly to go through the full Wasm dispatch path
const before = [
    instance.exports.f32_min(), instance.exports.f32_max(),
    instance.exports.f64_min(), instance.exports.f64_max(),
];

// Trigger BBQ tier-up (~1000 calls via full dispatch path)
for (let i = 0; i &lt; 1000; i++) {
    instance.exports.f32_min(); instance.exports.f32_max();
    instance.exports.f64_min(); instance.exports.f64_max();
}

// After BBQ tier-up: constant folding (wrong)
const after = [
    instance.exports.f32_min(), instance.exports.f32_max(),
    instance.exports.f64_min(), instance.exports.f64_max(),
];

const expected = [&quot;-0&quot;, &quot;+0&quot;, &quot;-0&quot;, &quot;+0&quot;];
const labels   = [&quot;f32.min(+0,-0)&quot;, &quot;f32.max(-0,+0)&quot;, &quot;f64.min(+0,-0)&quot;, &quot;f64.max(-0,+0)&quot;];

for (let i = 0; i &lt; 4; i++) {
    const ok = sign(after[i]) === expected[i];
    print(`${labels[i]}: IPInt=${sign(before[i])} BBQ=${sign(after[i])} expected=${expected[i]} ${ok ? &quot;PASS&quot; : &quot;FAIL&quot;}`);
}
```

## Suggested Patch

### File: `Source/JavaScriptCore/wasm/WasmBBQJIT.h`

```diff
diff --git a/Source/JavaScriptCore/wasm/WasmBBQJIT.h b/Source/JavaScriptCore/wasm/WasmBBQJIT.h
index 15a502d297b8..90ea157544e3 100644
--- a/Source/JavaScriptCore/wasm/WasmBBQJIT.h
+++ b/Source/JavaScriptCore/wasm/WasmBBQJIT.h
@@ -1684,6 +1684,16 @@ public:
         if (std::isnan(right))
             return right;
 
+        // std::min/std::max return the first argument when equal (+0 == -0), so the sign
+        // of the result depends on argument order for opposite-sign zeros — handle explicitly.
+        if (left == static_cast&lt;FloatType&gt;(0.0) &amp;&amp; right == static_cast&lt;FloatType&gt;(0.0)
+            &amp;&amp; std::signbit(left) != std::signbit(right)) {
+            if constexpr (IsMinOrMax == MinOrMax::Min)
+                return static_cast&lt;FloatType&gt;(-0.0);
+            else
+                return static_cast&lt;FloatType&gt;(0.0);
+        }
+
         if constexpr (IsMinOrMax == MinOrMax::Min)
             return std::min&lt;FloatType&gt;(left, right);
         else
```

The fix adds an explicit guard for the opposite-sign zero case before delegating to `std::min`/`std::max`, mirroring the existing correct handling in `MathCommon.h:285–302`. Confirmed: applying the patch makes all four PoC cases PASS; reverting restores FAIL.

### Credit Information

Reporter credit: Junyoung Park (@candymate) of KAIST Hacking Lab</thetext>
  </long_desc><long_desc isprivate="0" >
    <commentid>2202032</commentid>
    <comment_count>1</comment_count>
    <who name="Radar WebKit Bug Importer">webkit-bug-importer</who>
    <bug_when>2026-04-19 13:22:12 -0700</bug_when>
    <thetext>&lt;rdar://problem/175122289&gt;</thetext>
  </long_desc><long_desc isprivate="0" >
    <commentid>2203243</commentid>
    <comment_count>2</comment_count>
    <who name="Keith Miller">keith_miller</who>
    <bug_when>2026-04-22 11:14:28 -0700</bug_when>
    <thetext>Pull request: https://github.com/WebKit/WebKit/pull/63326</thetext>
  </long_desc><long_desc isprivate="0" >
    <commentid>2203245</commentid>
    <comment_count>3</comment_count>
    <who name="Keith Miller">keith_miller</who>
    <bug_when>2026-04-22 11:16:32 -0700</bug_when>
    <thetext>*** Bug 312994 has been marked as a duplicate of this bug. ***</thetext>
  </long_desc><long_desc isprivate="0" >
    <commentid>2203247</commentid>
    <comment_count>4</comment_count>
    <who name="EWS">ews-feeder</who>
    <bug_when>2026-04-22 11:19:23 -0700</bug_when>
    <thetext>Committed 311790@main (a03517078f7e): &lt;https://commits.webkit.org/311790@main&gt;

Reviewed commits have been landed. Closing PR #63326 and removing active labels.</thetext>
  </long_desc>
      
          <attachment
              isobsolete="0"
              ispatch="0"
              isprivate="0"
          >
            <attachid>479203</attachid>
            <date>2026-04-18 13:08:49 -0700</date>
            <delta_ts>2026-04-18 13:08:49 -0700</delta_ts>
            <desc>poc.js</desc>
            <filename>poc.js</filename>
            <type>text/javascript</type>
            <size>2681</size>
            <attacher>parkjuny</attacher>
            
              <data encoding="base64">Ly8gSlNDIEJCUSBKSVQgc2lnbmVkIHplcm8gY29uc3RhbnQgZm9sZGluZyBidWcKLy8gSVBJbnQg
KGludGVycHJldGVyKSBpcyBjb3JyZWN0OyBCQlEgSklUIGNvbnN0YW50IGZvbGRpbmcgaXMgbm90
LgoKZnVuY3Rpb24gdWxlYjEyOChuKSB7CiAgICBjb25zdCBvdXQgPSBbXTsKICAgIGRvIHsgbGV0
IGIgPSBuICYgMHg3ZjsgbiA+Pj49IDc7IGlmIChuKSBiIHw9IDB4ODA7IG91dC5wdXNoKGIpOyB9
IHdoaWxlIChuKTsKICAgIHJldHVybiBvdXQ7Cn0KZnVuY3Rpb24gc2VjKGlkLCBib2R5KSB7IHJl
dHVybiBbaWQsIC4uLnVsZWIxMjgoYm9keS5sZW5ndGgpLCAuLi5ib2R5XTsgfQpmdW5jdGlvbiBz
dHIocykgeyByZXR1cm4gWy4uLnVsZWIxMjgocy5sZW5ndGgpLCAuLi5zLnNwbGl0KCcnKS5tYXAo
YyA9PiBjLmNoYXJDb2RlQXQoMCkpXTsgfQpmdW5jdGlvbiBmbihjb2RlKSB7IGNvbnN0IGIgPSBb
MHgwMCwgLi4uY29kZSwgMHgwYl07IHJldHVybiBbLi4udWxlYjEyOChiLmxlbmd0aCksIC4uLmJd
OyB9Cgpjb25zdCB3YXNtID0gbmV3IFVpbnQ4QXJyYXkoWwogICAgMHgwMCwgMHg2MSwgMHg3Mywg
MHg2ZCwgMHgwMSwgMHgwMCwgMHgwMCwgMHgwMCwKICAgIC4uLnNlYygweDAxLCBbMHgwMiwKICAg
ICAgICAweDYwLCAweDAwLCAweDAxLCAweDdkLCAgLy8gKCkgLT4gZjMyCiAgICAgICAgMHg2MCwg
MHgwMCwgMHgwMSwgMHg3YywgIC8vICgpIC0+IGY2NAogICAgXSksCiAgICAuLi5zZWMoMHgwMywg
WzB4MDQsIDB4MDAsIDB4MDAsIDB4MDEsIDB4MDFdKSwKICAgIC4uLnNlYygweDA3LCBbMHgwNCwK
ICAgICAgICAuLi5zdHIoImYzMl9taW4iKSwgMHgwMCwgMHgwMCwgIC8vIGYzMi5taW4oKzAsIC0w
KQogICAgICAgIC4uLnN0cigiZjMyX21heCIpLCAweDAwLCAweDAxLCAgLy8gZjMyLm1heCgtMCwg
KzApCiAgICAgICAgLi4uc3RyKCJmNjRfbWluIiksIDB4MDAsIDB4MDIsICAvLyBmNjQubWluKCsw
LCAtMCkKICAgICAgICAuLi5zdHIoImY2NF9tYXgiKSwgMHgwMCwgMHgwMywgIC8vIGY2NC5tYXgo
LTAsICswKQogICAgXSksCiAgICAuLi5zZWMoMHgwYSwgWzB4MDQsCiAgICAgICAgLi4uZm4oWzB4
NDMsMHgwMCwweDAwLDB4MDAsMHgwMCwgMHg0MywweDAwLDB4MDAsMHgwMCwweDgwLCAweDk2XSks
ICAvLyBmMzIubWluKCswLCAtMCkKICAgICAgICAuLi5mbihbMHg0MywweDAwLDB4MDAsMHgwMCww
eDgwLCAweDQzLDB4MDAsMHgwMCwweDAwLDB4MDAsIDB4OTddKSwgIC8vIGYzMi5tYXgoLTAsICsw
KQogICAgICAgIC4uLmZuKFsweDQ0LDB4MDAsMHgwMCwweDAwLDB4MDAsMHgwMCwweDAwLDB4MDAs
MHgwMCwgMHg0NCwweDAwLDB4MDAsMHgwMCwweDAwLDB4MDAsMHgwMCwweDAwLDB4ODAsIDB4YTRd
KSwgIC8vIGY2NC5taW4oKzAsIC0wKQogICAgICAgIC4uLmZuKFsweDQ0LDB4MDAsMHgwMCwweDAw
LDB4MDAsMHgwMCwweDAwLDB4MDAsMHg4MCwgMHg0NCwweDAwLDB4MDAsMHgwMCwweDAwLDB4MDAs
MHgwMCwweDAwLDB4MDAsIDB4YTVdKSwgIC8vIGY2NC5tYXgoLTAsICswKQogICAgXSksCl0pOwoK
Y29uc3QgaW5zdGFuY2UgPSBuZXcgV2ViQXNzZW1ibHkuSW5zdGFuY2UobmV3IFdlYkFzc2VtYmx5
Lk1vZHVsZSh3YXNtKSk7CmNvbnN0IHNpZ24gPSB4ID0+ICh4ID09PSAwICYmIDEveCA8IDApID8g
Ii0wIiA6ICIrMCI7CgovLyBCZWZvcmUgQkJRIHRpZXItdXA6IElQSW50IGludGVycHJldGVyIChj
b3JyZWN0KQovLyBBY2Nlc3MgaW5zdGFuY2UuZXhwb3J0cy5YKCkgZGlyZWN0bHkgdG8gZ28gdGhy
b3VnaCB0aGUgZnVsbCBXYXNtIGRpc3BhdGNoIHBhdGgKY29uc3QgYmVmb3JlID0gWwogICAgaW5z
dGFuY2UuZXhwb3J0cy5mMzJfbWluKCksIGluc3RhbmNlLmV4cG9ydHMuZjMyX21heCgpLAogICAg
aW5zdGFuY2UuZXhwb3J0cy5mNjRfbWluKCksIGluc3RhbmNlLmV4cG9ydHMuZjY0X21heCgpLApd
OwoKLy8gVHJpZ2dlciBCQlEgdGllci11cCAofjEwMDAgY2FsbHMgdmlhIGZ1bGwgZGlzcGF0Y2gg
cGF0aCkKZm9yIChsZXQgaSA9IDA7IGkgPCAxMDAwOyBpKyspIHsKICAgIGluc3RhbmNlLmV4cG9y
dHMuZjMyX21pbigpOyBpbnN0YW5jZS5leHBvcnRzLmYzMl9tYXgoKTsKICAgIGluc3RhbmNlLmV4
cG9ydHMuZjY0X21pbigpOyBpbnN0YW5jZS5leHBvcnRzLmY2NF9tYXgoKTsKfQoKLy8gQWZ0ZXIg
QkJRIHRpZXItdXA6IGNvbnN0YW50IGZvbGRpbmcgKHdyb25nKQpjb25zdCBhZnRlciA9IFsKICAg
IGluc3RhbmNlLmV4cG9ydHMuZjMyX21pbigpLCBpbnN0YW5jZS5leHBvcnRzLmYzMl9tYXgoKSwK
ICAgIGluc3RhbmNlLmV4cG9ydHMuZjY0X21pbigpLCBpbnN0YW5jZS5leHBvcnRzLmY2NF9tYXgo
KSwKXTsKCmNvbnN0IGV4cGVjdGVkID0gWyItMCIsICIrMCIsICItMCIsICIrMCJdOwpjb25zdCBs
YWJlbHMgICA9IFsiZjMyLm1pbigrMCwtMCkiLCAiZjMyLm1heCgtMCwrMCkiLCAiZjY0Lm1pbigr
MCwtMCkiLCAiZjY0Lm1heCgtMCwrMCkiXTsKCmZvciAobGV0IGkgPSAwOyBpIDwgNDsgaSsrKSB7
CiAgICBjb25zdCBvayA9IHNpZ24oYWZ0ZXJbaV0pID09PSBleHBlY3RlZFtpXTsKICAgIHByaW50
KGAke2xhYmVsc1tpXX06IElQSW50PSR7c2lnbihiZWZvcmVbaV0pfSBCQlE9JHtzaWduKGFmdGVy
W2ldKX0gZXhwZWN0ZWQ9JHtleHBlY3RlZFtpXX0gJHtvayA/ICJQQVNTIiA6ICJGQUlMIn1gKTsK
fQo=
</data>

          </attachment>
      

    </bug>

</bugzilla>