Open Bug 1692855 Opened 3 years ago Updated 3 years ago

Warp can't elide MLexicalCheck

Categories

(Core :: JavaScript Engine: JIT, defect, P2)

defect

Tracking

()

People

(Reporter: anba, Unassigned)

References

(Blocks 1 open bug)

Details

Test case from bug 1362930:

function captureVar(){
    var itr = 0;
    function f(){ ++itr; }

    let start = dateNow();
    for(let i = 0; i < 50000000; ++i){
        f();
    }
    print("capture var:", dateNow() - start);
}
captureVar();

function captureLet(){
    let itr = 0;
    function f(){ ++itr; }

    let start = dateNow();
    for(let i = 0; i < 50000000; ++i){
        f();
    }
    print("capture let:", dateNow() - start);
}
captureLet();

Expected: captureLet takes the same amount of time as captureVar.
Actual: captureLet is more than twice as slow as captureVar.


For the var case, Warp folds the load and the unbox into a single MLoadFixedSlotAndUnbox, whereas for the let case Warp emits the sequence LoadFixedSlot, MLexicalCheck, and finally MUnbox. The additional MLexicalCheck is emitted because neither the frontend nor Warp are currently able to prove that the JSOp::CheckAliasedLexical isn't actually needed.

IonBuilder was able to elide the MLexicalCheck when the type-set didn't include JS_UNINITIALIZED_LEXICAL.

Severity: -- → S4
Priority: -- → P2
Severity: S4 → S3

I've briefly looked into this: We could omit the MLexicalCheck in the « LoadFixedSlot, MLexicalCheck, MUnbox » sequence by relying on that uninitialised variables are represented by JS_UNINITIALIZED_LEXICAL, so when we also use MLoadFixedSlotAndUnbox for that sequence, we should get a bailout because the value doesn't have the expected type. The downside is that we then can't differentiate between an unexpected type and an uninitialised lexical when we handle the UnboxFolding bailout kind from MLoadFixedSlotAndUnbox. But apart from that, we'd get the same performance numbers irrespective whether var or let is used.

Another option would be to set the bailout kind to UninitializedLexical when folding LoadFixedSlot/MLexicalCheck/MUnbox to MLoadFixedSlotAndUnbox, and skip folding MLexicalCheck if we've ever failed a lexical check.'

Note that it's already the case that we can get an UnboxFolding bailout in cases where we would have hit the unbox anyway, and we don't worry about it too much. We have special machinery to make sure we don't turn LICM off unless we're sure it's causing problems, because LICM is often a big win, but I think FoldLoadsWithUnbox is generally only going to give an incremental improvement (except in microbenchmarks like this one where it's the only thing happening), so it seems better to limit the number of times we recompile.

But if we set the bailout kind to UninitializedLexical and then get the wrong type, for example String instead of Int32, won't that lead to setting failedLexicalCheck on the script, which means we won't be able to move any lexical checks anymore?

Yeah, in retrospect the second half of my comment kind of supersedes the first. It is another option, but it's maybe not a very good one. :P

I think your initial suggestion is good.

You need to log in before you can comment on or make changes to this bug.