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

(Blocks 1 open bug)

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.