Status: Assigned
Owner: [email protected]
Labels: Type-Bug Priority-Medium

New issue 2250 by [email protected]: LICM is too aggressive with moving checks across conditional control flow
http://code.google.com/p/v8/issues/detail?id=2250

In the example below SMI check for b generated for inlining of equals invocation (marked with (*)) will be hoisted out of the loop across the typeof b === "object" condition and cause an immediate deopt:

function eq(a, b) {
  if (typeof b === "object") {
    return b.equals(a);  // (*)
  }
  return a === b;
}

Object.prototype.equals = function (other) {
  return (this === other);
};

function test() {
  for (var i = 0; !eq(i, 10); i++)
    ;
}

eq({}, {});
eq({}, {});
eq(1, 1);
eq(1, 1);
test();
%OptimizeFunctionOnNextCall(test);
test();

Produced code before the loop:

                  ;;; @20: constant-t.
0x39928dbf    63  ba14000000     mov edx,0x14
                  ;;; @21: gap.
                  ;;; @22: check-non-smi.
0x39928dc4    68  f7c201000000   test edx,0x1
0x39928dca 74 0f8444120e05 jz 0x3ea0a014 ;; deoptimization bailout 2
                  ;;; @23: gap.
                  ;;; @24: constant-t.
0x39928dd0    80  ba14000000     mov edx,0x14
                  ;;; @25: gap.
                  ;;; @26: check-maps.
0x39928dd5 85 817aff41bd4025 cmp [edx+0xff],0x2540bd41 ;; object: 0x2540bd41 <Map(elements=3)> 0x39928ddc 92 0f853c120e05 jnz 0x3ea0a01e ;; deoptimization bailout 3
                  ;;; @27: gap.
                  ;;; @28: check-prototype-maps.
0x39928de2 98 bab581334d mov edx,0x4d3381b5 ;; object: 0x4d3381b5 <an Object> 0x39928de7 103 817affc1ec4025 cmp [edx+0xff],0x2540ecc1 ;; object: 0x2540ecc1 <Map(elements=3)> 0x39928dee 110 0f8534120e05 jnz 0x3ea0a028 ;; deoptimization bailout 4

If we switch a and b in eq (see below) then we will inline equals invocation (marked (*)) and correctly emit CompareObjectEqAndBranch for strict equality (marked by (**)) guarded by two CheckNonSmi for a and b. LICM then will move CheckNonSmi for b out of the loop causing immediate deopt as well:

function eq(a, b) {
  if (typeof a === "object") {
    return a.equals(b);  // (*)
  }
  return a === b;
}

Object.prototype.equals = function (other) {
  return (this === other); // (**)
};

function test() {
  for (var i = 0; !eq(i, 10); i++)
    ;
}

eq({}, {});
eq({}, {});
eq(1, 1);
eq(1, 1);
test();
%OptimizeFunctionOnNextCall(test);
test();

Parts of hydrogen we emit:

block B1 (before the loop):
    0    6    t19  Constant 10  range[10,10,m0=0] type[smi]
    0    0    t44  CheckNonSmi t19
    0    0    t45  CheckMaps t19 [0x2080bd41] type[smi]

block B2 in the loop (notice TypeofIsAndBranch for result of Change i to t):

   0    0    v25  EnterInlined eq, id=4
0 5 t97 Change i13 i to t range[-2147483648,2147483647,m0=0] changes[NewSpacePromotion] type[number] 0 0 v28 TypeofIsAndBranch t97 == 0x4220bf6d <String[6]: object> goto (B6, B3)

block B7 in the loop (inlining of equals):
    0    0    t36  CheckNonSmi t97
    0    0    t37  CheckMaps t97 [0x2080bd41] type[number]
    0    0    v40  Simulate id=22 push i13, push t19
    0    0    v41  EnterInlined Object.equals, id=4
    0    0    v46  CompareObjectEqAndBranch t97 t19 goto (B13, B8)

Another problem here is that no matter how many time we deopt and reopt we will continue to produce the wrong code.

--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev

Reply via email to