Closed
      
        Bug 1395900
      
      
        Opened 8 years ago
          Closed 8 years ago
      
        
    
  
for-in loops can trigger lots of IonBuilder loop restarts 
    Categories
(Core :: JavaScript Engine: JIT, enhancement)
        Core
          
        
        
      
        
    
        JavaScript Engine: JIT
          
        
        
      
        
    Tracking
()
        RESOLVED
        FIXED
        
    
  
        
            mozilla57
        
    
  
| Tracking | Status | |
|---|---|---|
| firefox57 | --- | fixed | 
People
(Reporter: jandem, Assigned: jandem)
References
Details
Attachments
(4 files)
| 2.62 KB,
          patch         | tcampbell
:
              
              review+ | Details | Diff | Splinter Review | 
| 4.10 KB,
          patch         | tcampbell
:
              
              review+ | Details | Diff | Splinter Review | 
| 2.00 KB,
          patch         | tcampbell
:
              
              review+ | Details | Diff | Splinter Review | 
| 12.07 KB,
          patch         | tcampbell
:
              
              review+ | Details | Diff | Splinter Review | 
The function below triggers 25 (!) loop restarts in IonBuilder. This is because we don't know the IteratorMore type and also because we don't know "x" has type MIRType::String in the loop.
I noticed this first on Speedometer. A patch for this reduces the number of restarts from ~1850 to ~650 so it seems pretty effective.
var o = {y: 1};
function f() {
    for (var i = 0; i < 10000; i++) {
        for (var x in o) {}
        for (var x in o) {}
        for (var x in o) {}
        for (var x in o) {}
        for (var x in o) {}
        for (var x in o) {}
        for (var x in o) {}
        for (var x in o) {}
        for (var x in o) {}
        for (var x in o) {}
        for (var x in o) {}
        for (var x in o) {}
    }
}
f();
| Assignee | ||
| Comment 1•8 years ago
           | ||
(In reply to Jan de Mooij [:jandem] from comment #0)
> The function below triggers 25 (!) loop restarts in IonBuilder.
FWIW my patches get rid of all of these loop restarts for the function in comment 0.
| Assignee | ||
| Comment 2•8 years ago
           | ||
This does not affect Speedometer much, but I noticed it on my micro-benchmarks.
In analyzeNewLoopTypes, we first look for a pre-existing loop header so we can reuse its phi types. If we can't use the existing loop header because oldEntry->isDead(), we happily return from analyzeNewLoopTypes.
Instead, I think we should just fall through to the code after the loop, in this case.
        Attachment #8903543 -
        Flags: review?(tcampbell)
| Assignee | ||
| Comment 3•8 years ago
           | ||
This teaches analyzeNewLoopTypes about MIteratorMore's MIRType::Value type, so we don't trigger a loop abort for each for-in loop.
        Attachment #8903545 -
        Flags: review?(tcampbell)
| Assignee | ||
| Comment 4•8 years ago
           | ||
nonStringIteration_ is dead.
We used it to unbox |x| to string in |for (x in y)|, but I think I broke that a long time ago when I removed JSOP_ITERNEXT (it was merged with JSOP_ITERMORE).
        Attachment #8903546 -
        Flags: review?(tcampbell)
| Assignee | ||
| Comment 5•8 years ago
           | ||
This adds JSOP_ITERNEXT back. It's a NOP in the interpreter and Baseline, but IonBuilder can use it to unbox the value as string.
Unfortunately legacy generators can return non-strings, in that case we bail and forbid Ion compilation of the script. Legacy generators will likely be removed soon and shouldn't be used on the web.
        Attachment #8903549 -
        Flags: review?(tcampbell)
| Comment 6•8 years ago
           | ||
Comment on attachment 8903545 [details] [diff] [review]
Part 2 - Add MIteratorMore type to analyzeNewLoopTypes
Review of attachment 8903545 [details] [diff] [review]:
-----------------------------------------------------------------
::: js/src/jit/IonBuilder.cpp
@@ +563,5 @@
>      }
>      if (!loopHeaders_.append(LoopHeader(cfgBlock->startPc(), entry)))
>          return abort(AbortReason::Alloc);
>  
> +    if (loopEntry->isForIn()) {
Good find :)
        Attachment #8903545 -
        Flags: review?(tcampbell) → review+
| Comment 7•8 years ago
           | ||
Comment on attachment 8903549 [details] [diff] [review]
Part 4 - Add JSOP_ITERNEXT
Review of attachment 8903549 [details] [diff] [review]:
-----------------------------------------------------------------
Please update jsopcodes.cpp so dis() doesn't show |<unknown>|
https://searchfox.org/mozilla-central/rev/51b3d67a5ec1758bd2fe7d7b6e75ad6b6b5da223/js/src/jsopcode.cpp#678-701
::: js/src/frontend/BytecodeEmitter.cpp
@@ +7508,5 @@
>          auto loopDepth = this->stackDepth;
>  #endif
>          MOZ_ASSERT(loopDepth >= 2);
>  
> +        if (iflags == JSITER_ENUMERATE) {
Add stack comment
        Attachment #8903549 -
        Flags: review?(tcampbell) → review+
| Updated•8 years ago
           | 
        Attachment #8903546 -
        Flags: review?(tcampbell) → review+
| Comment 8•8 years ago
           | ||
Comment on attachment 8903543 [details] [diff] [review]
Part 1 - Fix issue in analyzeNewLoopTypes
Review of attachment 8903543 [details] [diff] [review]:
-----------------------------------------------------------------
If I'm reading this right, your patch will allow us to retry seeding types through |GETLOCAL; SETLOCAL| sequences (the type of other ops wont be different than first time we saw this loop header). If your micro-benchmarks show this gaining us new type info, patch seems good to me.
One side-effect is that loopHeaders_ adds new entries. An alternative would be |break| instead of |continue| and updating the matching entry instead of allocating a new one. I'm fine with leaving as is, but might be clearer to reuse the entry.
        Attachment #8903543 -
        Flags: review?(tcampbell) → review+
Pushed by jandemooij@gmail.com:
https://hg.mozilla.org/integration/mozilla-inbound/rev/f719e2cf81ea
part 1 - Try to get new loop types in analyzeNewLoopTypes even if the previous header is dead. r=tcampbell
https://hg.mozilla.org/integration/mozilla-inbound/rev/fdc73e6b6a21
part 2 - Teach analyzeNewLoopTypes about for-in iterator value slot. r=tcampbell
https://hg.mozilla.org/integration/mozilla-inbound/rev/456b4480fdbf
part 3 - Remove unused IonBuilder::nonStringIteration_. r=tcampbell
https://hg.mozilla.org/integration/mozilla-inbound/rev/57a63c3f76fb
part 4 - Add JSOP_ITERNEXT to improve iterator key type information in Ion. r=tcampbell
| Assignee | ||
| Comment 10•8 years ago
           | ||
(In reply to Ted Campbell [:tcampbell] from comment #8)
> One side-effect is that loopHeaders_ adds new entries. An alternative would
> be |break| instead of |continue| and updating the matching entry instead of
> allocating a new one. I'm fine with leaving as is, but might be clearer to
> reuse the entry.
Yeah, fair enough. I changed it to a break, updated the existing entry, and added a foundEntry bool so we don't add a new entry after the loop in this case.
| Assignee | ||
| Comment 11•8 years ago
           | ||
This seems to have improved sunspider tagcloud by 3-4%.
Sunspider now takes 185-187 ms on the Mac x64 slave. Back in March, this was ~216 ms. So we "accidentally" improved sunspider by more than 10% - simply by optimizing performance all over the place :)
|   | ||
| Comment 12•8 years ago
           | ||
| bugherder | ||
https://hg.mozilla.org/mozilla-central/rev/f719e2cf81ea
https://hg.mozilla.org/mozilla-central/rev/fdc73e6b6a21
https://hg.mozilla.org/mozilla-central/rev/456b4480fdbf
https://hg.mozilla.org/mozilla-central/rev/57a63c3f76fb
Status: ASSIGNED → RESOLVED
Closed: 8 years ago
          status-firefox57:
          --- → fixed
Resolution: --- → FIXED
Target Milestone: --- → mozilla57
          You need to log in
          before you can comment on or make changes to this bug.
        
Description
•