Closed Bug 1059426 Opened 9 years ago Closed 9 years ago

IonMonkey: ArrayIndexOf repeatedly bails out without invalidating if second argument is non-primitive


(Core :: JavaScript Engine: JIT, defect)

Not set





(Reporter: djvj, Assigned: jschulte)




(1 file, 1 obsolete file)

The following test case shows the issue:

function makeArray() {
    var arr = new Array(5000);
    for (var i = 0; i < 5000; i++)
        arr[i] = (i % 27);
    return arr;

function main() {
    var arr = makeArray();
    for (var j = 0; j < 5; j++) {
        for (var i = 0; i < 5000; i++)
            arr.indexOf(5, new Number(i));


Run this, and we see roughly 20,000 bailouts.  A sample of the bailout:

[BaselineBailouts] Bailing to baseline self-hosted:122 (IonScript=0x2f4d310) (FrameType=2)
[BaselineBailouts]   Reading from snapshot offset 93 size 264
[BaselineBailouts]   Incoming frame ptr = 0x7fff04015130
[BaselineBailouts]   Callee function (self-hosted:122)
[BaselineBailouts]   Not constructing!
[BaselineBailouts]   Restoring frames:
[BaselineBailouts]     FrameNo 0
[BaselineBailouts]       Unpacking self-hosted:122
[BaselineBailouts]       [BASELINE-JS FRAME]
[BaselineBailouts]       WRITE_PTR 0x2f46d48/0x7fff04015128 PrevFramePtr    0x7fff04015168
[BaselineBailouts]       SUB_064   0x2f46d08/0x7fff040150e8 BaselineFrame
[BaselineBailouts]       FrameSize=112
[BaselineBailouts]       ScopeChain=0x7f3db0846040
[BaselineBailouts]       ReturnValue=fff9000000000000
[BaselineBailouts]       Is function!
[BaselineBailouts]       thisv=fffc7f3db0a00090
[BaselineBailouts]       frame slots 10, nargs 1, nfixed 5
[BaselineBailouts]       arg 0 = fff8800000000005
[BaselineBailouts]       WRITE_VAL 0x2f46d00/0x7fff040150e0 FixedValue      fffc7f3db0a00090
[BaselineBailouts]       WRITE_VAL 0x2f46cf8/0x7fff040150d8 FixedValue      fff8800000001388
[BaselineBailouts]       WRITE_VAL 0x2f46cf0/0x7fff040150d0 FixedValue      fff9000000000000
[BaselineBailouts]       WRITE_VAL 0x2f46ce8/0x7fff040150c8 FixedValue      fff9000000000000
[BaselineBailouts]       WRITE_VAL 0x2f46ce0/0x7fff040150c0 FixedValue      fffa000000000009
[BaselineBailouts]       pushing 0 expression stack slots
[BaselineBailouts]       Resuming at pc offset 67 (op getintrinsic) (line 127) of self-hosted:122
[BaselineBailouts]       Bailout kind: Bailout_NonPrimitiveInput
[BaselineBailouts]       Adjusted framesize -= 0: 112
[BaselineBailouts]       Set resumeAddr=0x7f3db3b60af6
[BaselineBailouts]   Done restoring frames
[BaselineBailouts]   Done restoring frames
[BaselineBailouts]   Got pc=0x2e4ccfc
[BaselineBailouts]   Restored outerScript=(self-hosted:122,43) innerScript=(self-hosted:122,43) (bailoutKind=7)

The source code being bailed out to is:

function ArrayIndexOf(searchElement ) {
    var O = ToObject(this);
    var len = ((O.length) >>> 0);
    if (len === 0)
        return -1;
    var n = arguments.length > 1 ? ToInteger(arguments[1]) : 0;

The Bailout_NonPrimitiveInput and the line of the bailout (containing ToInteger(arguments[1])) suggests that we are bailing while converting arguments[1] to an integer.

This SHOULD be triggering an invalidation and an ion-recompile with looser type information.
Attached patch v1.patch (obsolete) — Splinter Review
Improves test case by ~10x.
Attachment #8489148 - Flags: feedback?(kvijayan)
Comment on attachment 8489148 [details] [diff] [review]

Review of attachment 8489148 [details] [diff] [review]:

Nice.  Some minor style nits, but nice job finding the issue!

::: js/src/jit/MCallOptimize.cpp
@@ +2047,5 @@
> +    MIRType type = callInfo.getArg(0)->type();
> +    // Only fast-path if we know the input is in the int32 range and we're not
> +    // likely to bailout.
> +    if (getInlineReturnType() != MIRType_Int32 ||
> +        (!IsNumberType(type) && type != MIRType_Null && type != MIRType_Boolean))

Split this up into two conditionals, with appropriate comments on each.  The first is checking the return typeset, the second is checking an operand type.  Lumping the two together implies confuses them, so:

  // Only optimize cases where input is number, null, or boolean
  if (!IsNumberType(type) && ...)
      return InliningStatus_NotInlined;

  // Only optimize cases where output is int32
  if (getInlineReturnType() != MIRType_Int32)
      return InliningStatus_NotInlined;
Attachment #8489148 - Flags: feedback?(kvijayan) → feedback+
Attached patch v2.patchSplinter Review
Should I state in the comment, that MToInt32 will bail on other input, or is it fine like that?
Attachment #8489148 - Attachment is obsolete: true
Attachment #8490260 - Flags: review?(kvijayan)
Comment on attachment 8490260 [details] [diff] [review]

Review of attachment 8490260 [details] [diff] [review]:

No, I think the comments are good as is.  Thanks for working on this!  I'll run this through try.
Attachment #8490260 - Flags: review?(kvijayan) → review+
Assignee: nobody → j_schulte
Keywords: checkin-needed
OS: Windows 8.1 → All
Hardware: x86_64 → All
Closed: 9 years ago
Resolution: --- → FIXED
Target Milestone: --- → mozilla35
Just a drive-by comment. This will also deoptimize when we encounter a typeset having multiple types (that don't bail) e.g. if the inputType is "null int32", we won't do the special case.

What about using "callInfo.getArg(0)->mightBe(X)" with X checking all cases that can bail (String|Symbol|Object|Undefined) to decide to not inline ToInteger?
You need to log in before you can comment on or make changes to this bug.