Open Bug 1354114 Opened 8 years ago Updated 2 years ago

Optimize away more ArrayIteratorNext instructions

Categories

(Core :: JavaScript Engine: JIT, enhancement, P3)

enhancement

Tracking

()

People

(Reporter: evilpie, Unassigned)

References

(Blocks 1 open bug)

Details

(Keywords: triage-deferred)

Attachments

(1 file)

for-of on arrays and array destructuring still has a bunch of unnecessary overhead, we need to try and eliminate that in Ion. See Jan's bug 1353170 comment 10 for ideas.
Attached file func03.pdf (deleted) —
So it seems to me like we don't eliminate the {value, done} object in next. [Escape] Check object newobject327 = newobject constant326 resumepoint mode=After (caller in block5) constant291 constant292 newarrayiterator41 typebarrier324 constant293 constant293 constant293 constant293 constant293 newarrayiterator41 newobject327 at self-hosted:538 in test.js line 89 > Function:7 [Escape] is escaped by phi72 = phi constant64 newobject327 after test.js line 89 > Function:7 constant64 is undefined and defined before the loop entry
Flags: needinfo?(jdemooij)
I think that's because of the bytecode we emit for for-of. We leave the {value, done} object on the stack and then at the top of the loop we access .value. arai, would it be possible to restructure this so when we reach the backedge we have VALUE on the stack instead of RESULT? Ideally the RESULT object would not flow into any phis so we can more easily optimize it away in the JIT.
Flags: needinfo?(jdemooij) → needinfo?(arai.unmht)
(In reply to Jan de Mooij [:jandem] from comment #2) > I think that's because of the bytecode we emit for for-of. We leave the > {value, done} object on the stack and then at the top of the loop we access > .value. > > arai, would it be possible to restructure this so when we reach the backedge > we have VALUE on the stack instead of RESULT? Ideally the RESULT object > would not flow into any phis so we can more easily optimize it away in the > JIT. if we can have extra branch at the end of the loop, it's possible to remove RESULT from the stack before backedge. I'll prepare some draft patch.
something like this: symbol 1 # y y @@iterator callelem # y ITERFUNC swap # ITERFUNC y calliter 0 # ITER checkisobj 3 # ITER undefined # ITER undefined goto ENTRY # ITER undefined # loop { HEAD: loophead # ITER VALUE uninitialized # ITER VALUE UNINITIALIZED initlexical 0 # ITER VALUE UNINITIALIZED pop # ITER VALUE try CATCH # ITER VALUE # try { ... # ITER UNKNOWN goto TRY-END # ITER UNKNOWN # } catch { CATCH: jumptarget # ITER VALUE exception # ITER VALUE EXC dupat 2 # ITER VALUE EXC ITER undefined # ITER VALUE EXC ITER undefined strictne # ITER VALUE EXC (ITER !== undefined) ifeq ITER-END # ITER VALUE EXC # if { jumptarget # ITER VALUE EXC dupat 2 # ITER VALUE EXC ITER dup # ITER VALUE EXC ITER ITER callprop "return" # ITER VALUE EXC ITER RETURN dup # ITER VALUE EXC ITER RETURN RETURN undefined # ITER VALUE EXC ITER RETURN RETURN undefined ne # ITER VALUE EXC ITER RETURN (RETURN != undefined) ifeq NO-RETURN # ITER VALUE EXC ITER RETURN # if { jumptarget # ITER VALUE EXC ITER RETURN checkiscallable 0 # ITER VALUE EXC ITER RETURN swap # ITER VALUE EXC RETURN ITER undefined # ITER VALUE EXC RETURN ITER undefined try CATCH2 # ITER VALUE EXC RETURN ITER undefined # try { dupat 2 # ITER VALUE EXC RETURN ITER undefined RETURN dupat 2 # ITER VALUE EXC RETURN ITER undefined RETURN ITER call 0 # ITER VALUE EXC RETURN ITER undefined RETURN() swap # ITER VALUE EXC RETURN ITER RETURN() undefined pop # ITER VALUE EXC RETURN ITER RETURN() goto TRY-END2 # ITER VALUE EXC RETURN ITER RETURN() # } catch { CATCH2: jumptarget # ITER VALUE EXC RETURN ITER undefined exception # ITER VALUE EXC RETURN ITER undefined EXC pop # ITER VALUE EXC RETURN ITER undefined nop # ITER VALUE EXC RETURN ITER undefined # } TRY-END2: jumptarget # ITER VALUE EXC RETURN ITER merged<RETURN()> unpick 2 # ITER VALUE EXC merged<RETURN()> RETURN ITER pop # ITER VALUE EXC merged<RETURN()> RETURN pop # ITER VALUE EXC merged<RETURN()> goto HAS-RETURN-END # ITER VALUE EXC merged<RETURN()> # } else { NO-RETURN: jumptarget # ITER VALUE EXC ITER RETURN pop # ITER VALUE EXC ITER # } HAS-RETURN-END: jumptarget # ITER VALUE EXC merged<RETURN()> pop # ITER VALUE EXC # } ITER-END: jumptarget # ITER VALUE EXC throw # ITER VALUE goto TRY-END # !!! UNREACHABLE !!! nop # !!! UNREACHABLE !!! # } TRY-END: jumptarget # ITER UNKNOWN ENTRY: loopentry 129 # ITER undefined pop # ITER dup # ITER ITER dup # ITER ITER ITER callprop "next" # ITER ITER NEXFUNC swap # ITER NEXFUNC ITER call 0 # ITER RESULT checkisobj 0 # ITER RESULT dup # ITER RESULT RESULT getprop "done" # ITER RESULT DONE ifeq NOT-DONE # ITER RESULT # if { pop # ITER undefined # ITER undefined true # ITER undefined TRUE # } else { NOT-DONE: getprop "value" # ITER VALUE false # ITER VALUE FALSE # } ifeq HEAD # ITER merged<VALUE> # } DONE: jumptarget # ITER merged<VALUE> pop # pop #
forgot "goto" after "true" in then-branch :P
Depends on: 1354554
I'll fix in bug 1354554
Flags: needinfo?(arai.unmht)
Thanks to bug 1354554 we are now almost as fast as v8 on this benchmark. There is still some branches inside the loop and we now introduced unnecessary boxing for the iterator value.
So there two branches here that we should definitely optimize way. For the first I already have a patch: We ended up with a compare typebarrier constant, where typebarrier was wrapping another int32 constant. I think we can just constant fold MTyperBarriers with a constant. The second branch that is coming from ToLength: We get a branch for the the if (v <= 0). This is a bit silly, we have int32 so we don't have to worry about -0, and the v is an arraylength node so we already know it can't be < 0, just = 0.
Depends on: 1355943
Depends on: 1356207
Keywords: triage-deferred
Priority: -- → P3
Severity: normal → S3
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: