Closed Bug 606892 Opened 14 years ago Closed 13 years ago

TM: most Function guards are unnecessary

Categories

(Core :: JavaScript Engine, defect)

x86
macOS
defect
Not set
normal

Tracking

()

RESOLVED WONTFIX

People

(Reporter: n.nethercote, Assigned: n.nethercote)

References

Details

When we unbox an object, if it's a non-function object at trace time, we emit a guard that exits if the object is a function, like this:

  ldi24 = ldi.objclasp ldi23[4]
  guard(class is Function)4 = eqi ldi24, clasp2/*......*/
  xt11: xt guard(class is Function)4 -> pc=...... imacpc=(nil) sp+48 rp+0 (......)

However, we almost always test the non-function object is another guard
shortly afterwards.  For example, if we access an array-of-arrays we have
code like this:

  ldi24 = ldi.objclasp ldi23[4]
  guard(class is Function)4 = eqi ldi24, clasp2/*......*/
  xt11: xt guard(class is Function)4 -> pc=...... imacpc=(nil) sp+48 rp+0 (......)
  sti.sp sp[32] = ldi23
  sti.sp sp[40] = ldi1
  guard(class is Array)7 = eqi ldi24, clasp/*......*/
  xf19: xf guard(class is Array)7 -> pc=...... imacpc=(nil) sp+48 rp+0 (......)

The (weak) 'is Function' guard is subsumed by the (strong) 'is Array' guard.

Similarly, if we access a property of an object we see code like this:

      ldi6 = ldi.objclasp ldi5[4]
      clasp2 = immi 0x83b1500
      guard(class is Function) = eqi ldi6, clasp2/*0x83b1500*/
      xt1: xt guard(class is Function) -> pc=0x8cbc383 imacpc=(nil) sp+16 rp+0 (GuardID=005)
      sti.sp sp[0] = ldi5
      objShape = ldi.objshape ldi5[12]
      immi2 = immi 456
      guard_kshape = eqi objShape, immi2/*456*/
      xf5: xf guard_kshape -> pc=0x8cbc384 imacpc=(nil) sp+8 rp+0 (GuardID=006)

I think the shape guard again subsumes the 'is Function' guard (though I'm less
certain;  the array-of-array case above is clear, whereas this case requires
knowledge of how shapes work).

I tried just removing all the 'is Function' guards;  only two jit-tests
failed.  So they're rarely necessary.  Can we find a way to remove the
unnecessary ones?

As a single data point, ai-astar (run under -j) executes 15% fewer
instructions with the 'is Function' guards removed -- it has three
unnecessary ones in its hottest loop.
An object's shape depends on its prototype object's identity, so a function object whose proto is Function.prototype is enough to determine that its shape cannot match any non-function object's shape. Setting __proto__ assigns a unique, "own" shape, so that's not an issue either.

Thus if at record time we emit a shape guard for a non-function object, we know no function object can match it, and vice versa.

Aside: most functions have the same shape.

/be
Good discovery!  It seems difficult to kill in the forward pass, since, at least from the uses of unbox_value I skimmed, its not clear when we are about to emit the stronger guard.

I wonder if we could add a backwards pass which keeps a table of live (LIns*, guard condition) pairs and uses this table to kill guards on LIns*'s which are implied by a live condition.  Guard instructions would add entries to the table and uses of LIns*'s would kill entries in the table.  To just solve the problem at hand, the "guard condition" could simply be the two-element ordered set "guard on specific non-function class" > "guard not a function".
A pass in the LIR reader pipeline (which reads the code backwards) is the obvious thing.  NJ compile time is less of an issue than it used to be because with '-j -m -p' fewer things in SunSpider trace these days.
Blocks: 622494
TM's days are numbered:  WONTFIX.
Status: ASSIGNED → RESOLVED
Closed: 13 years ago
Resolution: --- → WONTFIX
You need to log in before you can comment on or make changes to this bug.