Branch: refs/heads/main
  Home:   https://github.com/WebKit/WebKit
  Commit: ed1ea245850f9bfe39534edb6a0db7d7cf714ec6
      
https://github.com/WebKit/WebKit/commit/ed1ea245850f9bfe39534edb6a0db7d7cf714ec6
  Author: Yusuke Suzuki <[email protected]>
  Date:   2026-10-09 (Fri, 09 Oct 2026)

  Changed paths:
    A JSTests/stress/movhint-removal-terminal-liveness-only-for-phantom.js
    M Source/JavaScriptCore/dfg/DFGMovHintRemovalPhase.cpp

  Log Message:
  -----------
  [JSC] MovHint removal should apply the block-terminal liveness rule only to 
phantom allocations
https://bugs.webkit.org/show_bug.cgi?id=326643
rdar://189380801

Reviewed by Yijia Huang.

MovHint removal phase treats the bytecode-live locals at every block
terminal as uses even when the terminal does not exit. This is because
of phantom allocations. Let's have a code like this.

        function g(c, s) {
            let o = { f: 1 };       // loc5
            if (c) {
                let p = o;          // loc6; o is never read after this
                return p.f + s;     // s may fail its Int32 check and exit
            }
            return o.f;
        }

    After object allocation sinking, o never escapes, so it becomes a phantom.

        Block #0:
          D@1: PhantomNewObject()
          D@2: PutHint(D@1, StructurePLoc, %S)
          D@3: PutHint(D@1, NamedPropertyPLoc(f), Int32: 1)
          D@4: MovHint(D@1, loc5)                  // o = D@1
          D@5: Branch(D@c, T:#1, F:#2)             // does not exit; loc5 
bytecode-live here

        Block #1:                                  // loc5 bytecode-live at 
head (read by `p = o`)
          D@6: MovHint(D@1, loc6)                  // p = D@1; loc5 dead in 
bytecode from here on
          D@7: ArithAdd(Int32:D@f, Int32:D@s)      // Exits. Live: loc6, not 
loc5

        Block #2:
          D@8: Return(Int32: 1)                    // o.f folded by sinking; 
does not exit

    With exit-only liveness, D@4 is removed. Nothing exits after D@4 in block 
#0.
    The only exit in block #1 (D@7) has loc5 dead, and block #2 has no exit.
    So D@4 becomes ZombieHint(loc5). Now follow OSR availability analysis:

        Tail of #0:  locals { loc5: <bottom constant> }
                     heap   { [email protected] = %S, [email protected] = 1 }

        Head of #1, after pruneByLiveness:
                     bytecode-live locals = { loc5, ... }
                     loc5 holds the bottom constant, not D@1
                     -> no live local reaches D@1
                     -> heap entries keyed on D@1 are dropped
                     heap   { }

        D@6:         loc6 = D@1          // the phantom is reachable again...
        D@7 exit:    must materialize D@1 for loc6, but D@1's Structure and f 
are gone

    The exit at D@7 needs o through a different local (loc6). The state 
describing
    o was pruned at the head of #1, because the only local that reached it at 
that
    point had already been zombified.

This patch makes liveness three-level lattice, Dead < Anchored < Observed.

    1. Exits make a local Observed, as it must exist.
    2. Block terminals make it at least Anchored.

Then when removing MovHint, we keep Anchored one only when it is
referenced from phantoms.

Test: JSTests/stress/movhint-removal-terminal-liveness-only-for-phantom.js

* JSTests/stress/movhint-removal-terminal-liveness-only-for-phantom.js: Added.
(f):
(g):
(get f):
* Source/JavaScriptCore/dfg/DFGMovHintRemovalPhase.cpp:

Canonical link: https://commits.webkit.org/323212@main



To unsubscribe from these emails, change your notification settings at 
https://github.com/WebKit/WebKit/settings/notifications

Reply via email to