| // Copyright 2013 the V8 project authors. All rights reserved. |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are |
| // met: |
| // |
| // * Redistributions of source code must retain the above copyright |
| // notice, this list of conditions and the following disclaimer. |
| // * Redistributions in binary form must reproduce the above |
| // copyright notice, this list of conditions and the following |
| // disclaimer in the documentation and/or other materials provided |
| // with the distribution. |
| // * Neither the name of Google Inc. nor the names of its |
| // contributors may be used to endorse or promote products derived |
| // from this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| // Flags: --allow-natives-syntax --expose-gc |
| |
| |
| // Test stores on a join path. |
| (function testJoin() { |
| function constructor() { |
| this.a = 0; |
| } |
| function join(mode, expected) { |
| var object = new constructor(); |
| if (mode) { |
| object.a = 1; |
| } else { |
| object.a = 2; |
| } |
| assertEquals(expected, object.a); |
| } |
| %PrepareFunctionForOptimization(join); |
| join(true, 1); join(true, 1); |
| join(false, 2); join(false, 2); |
| %OptimizeFunctionOnNextCall(join); |
| join(true, 1); join(false, 2); |
| })(); |
| |
| |
| // Test loads and stores inside a loop. |
| (function testLoop() { |
| function constructor() { |
| this.a = 0; |
| this.b = 23; |
| } |
| function loop() { |
| var object = new constructor(); |
| for (var i = 1; i < 10; i++) { |
| object.a = object.a + i; |
| assertEquals(i*(i+1)/2, object.a); |
| assertEquals(23, object.b); |
| } |
| assertEquals(45, object.a); |
| assertEquals(23, object.b); |
| } |
| %PrepareFunctionForOptimization(loop); |
| loop(); loop(); |
| %OptimizeFunctionOnNextCall(loop); |
| loop(); loop(); |
| })(); |
| |
| |
| // Test loads and stores inside nested loop. |
| (function testNested() { |
| function constructor() { |
| this.a = 0; |
| this.b = 0; |
| this.c = 23; |
| } |
| function nested() { |
| var object = new constructor(); |
| for (var i = 1; i < 10; i++) { |
| object.a = object.a + i; |
| assertEquals(i*(i+1)/2, object.a); |
| assertEquals((i-1)*6, object.b); |
| assertEquals(23, object.c); |
| for (var j = 1; j < 4; j++) { |
| object.b = object.b + j; |
| assertEquals(i*(i+1)/2, object.a); |
| assertEquals((i-1)*6+j*(j+1)/2, object.b); |
| assertEquals(23, object.c); |
| } |
| assertEquals(i*(i+1)/2, object.a); |
| assertEquals(i*6, object.b); |
| assertEquals(23, object.c); |
| } |
| assertEquals(45, object.a); |
| assertEquals(54, object.b); |
| assertEquals(23, object.c); |
| } |
| %PrepareFunctionForOptimization(nested); |
| nested(); nested(); |
| %OptimizeFunctionOnNextCall(nested); |
| nested(); nested(); |
| })(); |
| |
| |
| // Test deoptimization with captured objects in local variables. |
| (function testDeoptLocal() { |
| var deopt = { deopt:false }; |
| function constructor1() { |
| this.a = 1.0; |
| this.b = 2.3; |
| this.c = 3.0; |
| } |
| function constructor2(o) { |
| this.d = o; |
| this.e = 4.5; |
| } |
| function func() { |
| var o1 = new constructor1(); |
| var o2 = new constructor2(o1); |
| deopt.deopt; |
| assertEquals(1.0, o1.a); |
| assertEquals(2.3, o2.d.b); |
| assertEquals(3.0, o2.d.c); |
| assertEquals(4.5, o2.e); |
| } |
| %PrepareFunctionForOptimization(func); |
| func(); func(); |
| %OptimizeFunctionOnNextCall(func); |
| func(); func(); |
| delete deopt.deopt; |
| func(); func(); |
| })(); |
| |
| |
| // Test deoptimization with captured objects on operand stack. |
| (function testDeoptOperand() { |
| var deopt = { deopt:false }; |
| function constructor1() { |
| this.a = 1.0; |
| this.b = 2.3; |
| deopt.deopt; |
| assertEquals(1.0, this.a); |
| assertEquals(2.3, this.b); |
| this.b = 2.7; |
| this.c = 3.0; |
| this.d = 4.5; |
| } |
| function constructor2() { |
| this.e = 5.0; |
| this.f = new constructor1(); |
| assertEquals(1.0, this.f.a); |
| assertEquals(2.7, this.f.b); |
| assertEquals(3.0, this.f.c); |
| assertEquals(4.5, this.f.d); |
| assertEquals(5.0, this.e); |
| this.e = 5.9; |
| this.g = 6.7; |
| } |
| function func() { |
| var o = new constructor2(); |
| assertEquals(1.0, o.f.a); |
| assertEquals(2.7, o.f.b); |
| assertEquals(3.0, o.f.c); |
| assertEquals(4.5, o.f.d); |
| assertEquals(5.9, o.e); |
| assertEquals(6.7, o.g); |
| } |
| %PrepareFunctionForOptimization(func); |
| func(); func(); |
| %OptimizeFunctionOnNextCall(func); |
| func(); func(); |
| delete deopt.deopt; |
| func(); func(); |
| })(); |
| |
| |
| // Test map checks on captured objects. |
| (function testMapCheck() { |
| var sum = 0; |
| function getter() { return 27; } |
| function setter(v) { sum += v; } |
| function constructor() { |
| this.x = 23; |
| this.y = 42; |
| } |
| function check(x, y) { |
| var o = new constructor(); |
| assertEquals(x, o.x); |
| assertEquals(y, o.y); |
| } |
| var monkey = Object.create(null, { |
| x: { get:getter, set:setter }, |
| y: { get:getter, set:setter } |
| }); |
| %PrepareFunctionForOptimization(check); |
| check(23, 42); check(23, 42); |
| %OptimizeFunctionOnNextCall(check); |
| check(23, 42); check(23, 42); |
| constructor.prototype = monkey; |
| check(27, 27); check(27, 27); |
| assertEquals(130, sum); |
| })(); |
| |
| |
| // Test OSR into a loop with captured objects. |
| (function testOSR() { |
| function constructor() { |
| this.a = 23; |
| } |
| function osr1(length) { |
| assertEquals(23, (new constructor()).a); |
| var result = 0; |
| for (var i = 0; i < length; i++) { |
| result = (result + i) % 99; |
| } |
| return result; |
| } |
| function osr2(length) { |
| var result = 0; |
| for (var i = 0; i < length; i++) { |
| result = (result + i) % 99; |
| } |
| assertEquals(23, (new constructor()).a); |
| return result; |
| } |
| function osr3(length) { |
| var result = 0; |
| var o = new constructor(); |
| for (var i = 0; i < length; i++) { |
| result = (result + i) % 99; |
| } |
| assertEquals(23, o.a); |
| return result; |
| } |
| function test(closure) { |
| assertEquals(45, closure(10)); |
| assertEquals(45, closure(10)); |
| assertEquals(10, closure(50000)); |
| } |
| test(osr1); |
| test(osr2); |
| test(osr3); |
| })(); |
| |
| |
| // Test out-of-bounds access on captured objects. |
| (function testOOB() { |
| function cons1() { |
| this.x = 1; |
| this.y = 2; |
| this.z = 3; |
| } |
| function cons2() { |
| this.a = 7; |
| } |
| function oob(constructor, branch) { |
| var o = new constructor(); |
| if (branch) { |
| return o.a; |
| } else { |
| return o.z; |
| } |
| } |
| %PrepareFunctionForOptimization(oob); |
| assertEquals(3, oob(cons1, false)); |
| assertEquals(3, oob(cons1, false)); |
| assertEquals(7, oob(cons2, true)); |
| assertEquals(7, oob(cons2, true)); |
| gc(); // Clears type feedback of constructor call. |
| assertEquals(7, oob(cons2, true)); |
| assertEquals(7, oob(cons2, true)); |
| %OptimizeFunctionOnNextCall(oob); |
| assertEquals(7, oob(cons2, true)); |
| })(); |
| |
| |
| // Test non-shallow nested graph of captured objects. |
| (function testDeep() { |
| var deopt = { deopt:false }; |
| function constructor1() { |
| this.x = 23; |
| } |
| function constructor2(nested) { |
| this.a = 17; |
| this.b = nested; |
| this.c = 42; |
| } |
| function deep() { |
| var o1 = new constructor1(); |
| var o2 = new constructor2(o1); |
| assertEquals(17, o2.a); |
| assertEquals(23, o2.b.x); |
| assertEquals(42, o2.c); |
| o1.x = 99; |
| deopt.deopt; |
| assertEquals(99, o1.x); |
| assertEquals(99, o2.b.x); |
| } |
| %PrepareFunctionForOptimization(deep); |
| deep(); deep(); |
| %OptimizeFunctionOnNextCall(deep); |
| deep(); deep(); |
| delete deopt.deopt; |
| deep(); deep(); |
| })(); |
| |
| |
| // Test non-shallow nested graph of captured objects with duplicates |
| (function testDeepDuplicate() { |
| function constructor1() { |
| this.x = 23; |
| } |
| function constructor2(nested) { |
| this.a = 17; |
| this.b = nested; |
| this.c = 42; |
| } |
| function deep(shouldDeopt) { |
| var o1 = new constructor1(); |
| var o2 = new constructor2(o1); |
| var o3 = new constructor2(o1); |
| assertEquals(17, o2.a); |
| assertEquals(23, o2.b.x); |
| assertEquals(42, o2.c); |
| o3.c = 54; |
| o1.x = 99; |
| if (shouldDeopt) %DeoptimizeFunction(deep); |
| assertEquals(99, o1.x); |
| assertEquals(99, o2.b.x); |
| assertEquals(99, o3.b.x); |
| assertEquals(54, o3.c); |
| assertEquals(17, o3.a); |
| assertEquals(42, o2.c); |
| assertEquals(17, o2.a); |
| o3.b.x = 1; |
| assertEquals(1, o1.x); |
| } |
| %PrepareFunctionForOptimization(deep); |
| deep(false); deep(false); |
| %OptimizeFunctionOnNextCall(deep); |
| deep(false); deep(false); |
| deep(true); deep(true); |
| })(); |
| |
| |
| // Test non-shallow nested graph of captured objects with inline |
| (function testDeepInline() { |
| function h() { |
| return { y : 3 }; |
| } |
| |
| function g(x) { |
| var u = { x : h() }; |
| %DeoptimizeFunction(f); |
| return u; |
| } |
| |
| function f() { |
| var l = { dummy : { } }; |
| var r = g(l); |
| assertEquals(3, r.x.y); |
| } |
| |
| %PrepareFunctionForOptimization(f); |
| f(); f(); f(); |
| %OptimizeFunctionOnNextCall(f); |
| f(); |
| })(); |
| |
| |
| // Test two nested objects |
| (function testTwoNestedObjects() { |
| function f() { |
| var l = { x : { y : 111 } }; |
| var l2 = { x : { y : 111 } }; |
| %DeoptimizeFunction(f); |
| assertEquals(111, l.x.y); |
| assertEquals(111, l2.x.y); |
| } |
| |
| %PrepareFunctionForOptimization(f); |
| f(); f(); f(); |
| %OptimizeFunctionOnNextCall(f); |
| f(); |
| })(); |
| |
| |
| // Test a nested object and a duplicate |
| (function testTwoObjectsWithDuplicate() { |
| function f() { |
| var l = { x : { y : 111 } }; |
| var dummy = { d : 0 }; |
| var l2 = l.x; |
| %DeoptimizeFunction(f); |
| assertEquals(111, l.x.y); |
| assertEquals(111, l2.y); |
| assertEquals(0, dummy.d); |
| } |
| |
| %PrepareFunctionForOptimization(f); |
| f(); f(); f(); |
| %OptimizeFunctionOnNextCall(f); |
| f(); |
| })(); |
| |
| |
| // Test materialization of a field that requires a Smi value. |
| (function testSmiField() { |
| var deopt = { deopt:false }; |
| function constructor() { |
| this.x = 1; |
| } |
| function field(x) { |
| var o = new constructor(); |
| o.x = x; |
| deopt.deopt |
| assertEquals(x, o.x); |
| } |
| %PrepareFunctionForOptimization(field); |
| field(1); field(2); |
| %OptimizeFunctionOnNextCall(field); |
| field(3); field(4); |
| delete deopt.deopt; |
| field(5.5); field(6.5); |
| })(); |
| |
| |
| // Test materialization of a field that requires a heap object value. |
| (function testHeapObjectField() { |
| var deopt = { deopt:false }; |
| function constructor() { |
| this.x = {}; |
| } |
| function field(x) { |
| var o = new constructor(); |
| o.x = x; |
| deopt.deopt |
| assertEquals(x, o.x); |
| } |
| %PrepareFunctionForOptimization(field); |
| field({}); field({}); |
| %OptimizeFunctionOnNextCall(field); |
| field({}); field({}); |
| delete deopt.deopt; |
| field(1); field(2); |
| })(); |