2017-06-02 12:30:04 +00:00
|
|
|
// Copyright 2017 the V8 project authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
|
|
// found in the LICENSE file.
|
|
|
|
|
|
|
|
// Flags: --allow-natives-syntax
|
|
|
|
|
|
|
|
(function() {
|
|
|
|
"use strict";
|
|
|
|
function bar() { return this; }
|
|
|
|
|
|
|
|
function foo(x) {
|
|
|
|
return bar.bind(x);
|
|
|
|
}
|
|
|
|
|
2019-03-01 10:19:54 +00:00
|
|
|
%PrepareFunctionForOptimization(foo);
|
2017-06-02 12:30:04 +00:00
|
|
|
assertEquals(0, foo(0)());
|
|
|
|
assertEquals(1, foo(1)());
|
|
|
|
%OptimizeFunctionOnNextCall(foo);
|
|
|
|
assertEquals("", foo("")());
|
|
|
|
})();
|
|
|
|
|
|
|
|
(function() {
|
|
|
|
"use strict";
|
|
|
|
function bar(x) { return x; }
|
|
|
|
|
|
|
|
function foo(x) {
|
|
|
|
return bar.bind(undefined, x);
|
|
|
|
}
|
|
|
|
|
2019-03-01 10:19:54 +00:00
|
|
|
%PrepareFunctionForOptimization(foo);
|
2017-06-02 12:30:04 +00:00
|
|
|
assertEquals(0, foo(0)());
|
|
|
|
assertEquals(1, foo(1)());
|
|
|
|
%OptimizeFunctionOnNextCall(foo);
|
|
|
|
assertEquals("", foo("")());
|
|
|
|
})();
|
|
|
|
|
|
|
|
(function() {
|
|
|
|
function bar(x) { return x; }
|
|
|
|
|
|
|
|
function foo(x) {
|
|
|
|
return bar.bind(undefined, x);
|
|
|
|
}
|
|
|
|
|
2019-03-01 10:19:54 +00:00
|
|
|
%PrepareFunctionForOptimization(foo);
|
2017-06-02 12:30:04 +00:00
|
|
|
assertEquals(0, foo(0)());
|
|
|
|
assertEquals(1, foo(1)());
|
|
|
|
%OptimizeFunctionOnNextCall(foo);
|
|
|
|
assertEquals("", foo("")());
|
|
|
|
})();
|
|
|
|
|
|
|
|
(function() {
|
|
|
|
"use strict";
|
|
|
|
function bar(x, y) { return x + y; }
|
|
|
|
|
|
|
|
function foo(x, y) {
|
|
|
|
return bar.bind(undefined, x, y);
|
|
|
|
}
|
|
|
|
|
2019-03-01 10:19:54 +00:00
|
|
|
%PrepareFunctionForOptimization(foo);
|
2017-06-02 12:30:04 +00:00
|
|
|
assertEquals(0, foo(0, 0)());
|
|
|
|
assertEquals(2, foo(1, 1)());
|
|
|
|
%OptimizeFunctionOnNextCall(foo);
|
|
|
|
assertEquals("ab", foo("a", "b")());
|
|
|
|
assertEquals(0, foo(0, 1).length);
|
|
|
|
assertEquals("bound bar", foo(1, 2).name)
|
|
|
|
})();
|
|
|
|
|
|
|
|
(function() {
|
|
|
|
function bar(x, y) { return x + y; }
|
|
|
|
|
|
|
|
function foo(x, y) {
|
|
|
|
return bar.bind(undefined, x, y);
|
|
|
|
}
|
|
|
|
|
2019-03-01 10:19:54 +00:00
|
|
|
%PrepareFunctionForOptimization(foo);
|
2017-06-02 12:30:04 +00:00
|
|
|
assertEquals(0, foo(0, 0)());
|
|
|
|
assertEquals(2, foo(1, 1)());
|
|
|
|
%OptimizeFunctionOnNextCall(foo);
|
|
|
|
assertEquals("ab", foo("a", "b")());
|
|
|
|
assertEquals(0, foo(0, 1).length);
|
|
|
|
assertEquals("bound bar", foo(1, 2).name)
|
|
|
|
})();
|
[turbofan] Unfold bound functions at call sites.
So far the JSCallReducer was only able to unfold constant
JSBoundFunction targets for JSCall nodes, which is not the
common case. With the introduction of JSCreateBoundFunction
operator earlier, we can now also recognize calls to bound
functions where the bind happens earlier in the function,
i.e. as the example of
a.map(f.bind(self))
in https://twitter.com/BenLesh/status/920700003974123520, which
is a handy way to use Function#bind. So this transformation
takes a node like
JSCall(JSCreateBoundFunction(bound_target_function,
bound_this,
a1,...,aN),
receiver, p1,...,pM)
and turns that into
JSCall(bound_target_function, bound_this, a1,...,aN,p1,...,pM)
allowing TurboFan to further inline the bound_target_function
at this call site if that's also inlinable (i.e. it's a known
constant JSFunction or the result of a JSCreateClosure call).
This improves the micro-benchmark from
arrowCall: 55 ms.
boundCall: 221 ms.
arrowMap: 181 ms.
boundMap: 806 ms.
to
arrowCall: 71 ms.
boundCall: 76 ms.
arrowMap: 188 ms.
boundMap: 186 ms.
so that Function#bind in this case is as fast as using closures,
which is an up to 4.3x improvement in the Array#map example.
Bug: v8:5257, v8:6961
Change-Id: Ibca650faad912bf9db1db6fbc48772e7551289a6
Reviewed-on: https://chromium-review.googlesource.com/727799
Reviewed-by: Jaroslav Sevcik <jarin@chromium.org>
Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#48713}
2017-10-19 05:42:49 +00:00
|
|
|
|
|
|
|
(function() {
|
|
|
|
function bar(f) { return f(1); }
|
|
|
|
|
|
|
|
function foo(g) { return bar(g.bind(null, 2)); }
|
|
|
|
|
2019-03-01 10:19:54 +00:00
|
|
|
%PrepareFunctionForOptimization(foo);
|
[turbofan] Unfold bound functions at call sites.
So far the JSCallReducer was only able to unfold constant
JSBoundFunction targets for JSCall nodes, which is not the
common case. With the introduction of JSCreateBoundFunction
operator earlier, we can now also recognize calls to bound
functions where the bind happens earlier in the function,
i.e. as the example of
a.map(f.bind(self))
in https://twitter.com/BenLesh/status/920700003974123520, which
is a handy way to use Function#bind. So this transformation
takes a node like
JSCall(JSCreateBoundFunction(bound_target_function,
bound_this,
a1,...,aN),
receiver, p1,...,pM)
and turns that into
JSCall(bound_target_function, bound_this, a1,...,aN,p1,...,pM)
allowing TurboFan to further inline the bound_target_function
at this call site if that's also inlinable (i.e. it's a known
constant JSFunction or the result of a JSCreateClosure call).
This improves the micro-benchmark from
arrowCall: 55 ms.
boundCall: 221 ms.
arrowMap: 181 ms.
boundMap: 806 ms.
to
arrowCall: 71 ms.
boundCall: 76 ms.
arrowMap: 188 ms.
boundMap: 186 ms.
so that Function#bind in this case is as fast as using closures,
which is an up to 4.3x improvement in the Array#map example.
Bug: v8:5257, v8:6961
Change-Id: Ibca650faad912bf9db1db6fbc48772e7551289a6
Reviewed-on: https://chromium-review.googlesource.com/727799
Reviewed-by: Jaroslav Sevcik <jarin@chromium.org>
Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#48713}
2017-10-19 05:42:49 +00:00
|
|
|
assertEquals(3, foo((x, y) => x + y));
|
|
|
|
assertEquals(1, foo((x, y) => x - y));
|
|
|
|
%OptimizeFunctionOnNextCall(foo);
|
|
|
|
assertEquals(3, foo((x, y) => x + y));
|
|
|
|
assertEquals(1, foo((x, y) => x - y));
|
|
|
|
})();
|
|
|
|
|
|
|
|
(function() {
|
|
|
|
function add(x, y) { return x + y; }
|
|
|
|
|
|
|
|
function foo(a) { return a.map(add.bind(null, 1)); }
|
|
|
|
|
2019-03-01 10:19:54 +00:00
|
|
|
%PrepareFunctionForOptimization(foo);
|
[turbofan] Unfold bound functions at call sites.
So far the JSCallReducer was only able to unfold constant
JSBoundFunction targets for JSCall nodes, which is not the
common case. With the introduction of JSCreateBoundFunction
operator earlier, we can now also recognize calls to bound
functions where the bind happens earlier in the function,
i.e. as the example of
a.map(f.bind(self))
in https://twitter.com/BenLesh/status/920700003974123520, which
is a handy way to use Function#bind. So this transformation
takes a node like
JSCall(JSCreateBoundFunction(bound_target_function,
bound_this,
a1,...,aN),
receiver, p1,...,pM)
and turns that into
JSCall(bound_target_function, bound_this, a1,...,aN,p1,...,pM)
allowing TurboFan to further inline the bound_target_function
at this call site if that's also inlinable (i.e. it's a known
constant JSFunction or the result of a JSCreateClosure call).
This improves the micro-benchmark from
arrowCall: 55 ms.
boundCall: 221 ms.
arrowMap: 181 ms.
boundMap: 806 ms.
to
arrowCall: 71 ms.
boundCall: 76 ms.
arrowMap: 188 ms.
boundMap: 186 ms.
so that Function#bind in this case is as fast as using closures,
which is an up to 4.3x improvement in the Array#map example.
Bug: v8:5257, v8:6961
Change-Id: Ibca650faad912bf9db1db6fbc48772e7551289a6
Reviewed-on: https://chromium-review.googlesource.com/727799
Reviewed-by: Jaroslav Sevcik <jarin@chromium.org>
Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#48713}
2017-10-19 05:42:49 +00:00
|
|
|
assertEquals([1, 2, 3], foo([0, 1, 2]));
|
|
|
|
assertEquals([2, 3, 4], foo([1, 2, 3]));
|
|
|
|
%OptimizeFunctionOnNextCall(foo);
|
|
|
|
assertEquals([1, 2, 3], foo([0, 1, 2]));
|
|
|
|
assertEquals([2, 3, 4], foo([1, 2, 3]));
|
|
|
|
})();
|
2017-10-19 07:27:51 +00:00
|
|
|
|
|
|
|
(function() {
|
|
|
|
const add = (x, y) => x + y;
|
|
|
|
const inc = add.bind(null, 1);
|
|
|
|
|
|
|
|
function foo(inc) { return inc(1); }
|
|
|
|
|
2019-03-01 10:19:54 +00:00
|
|
|
%PrepareFunctionForOptimization(foo);
|
2017-10-19 07:27:51 +00:00
|
|
|
assertEquals(2, foo(inc));
|
|
|
|
assertEquals(2, foo(inc));
|
|
|
|
%OptimizeFunctionOnNextCall(foo);
|
|
|
|
assertEquals(2, foo(inc));
|
|
|
|
})();
|
2017-11-28 10:41:04 +00:00
|
|
|
|
|
|
|
(function() {
|
|
|
|
const A = class A {};
|
|
|
|
const B = A.bind();
|
|
|
|
|
|
|
|
function foo() { return new B; }
|
|
|
|
|
2019-03-01 10:19:54 +00:00
|
|
|
%PrepareFunctionForOptimization(foo);
|
2017-11-28 10:41:04 +00:00
|
|
|
assertInstanceof(foo(), A);
|
|
|
|
assertInstanceof(foo(), B);
|
|
|
|
%OptimizeFunctionOnNextCall(foo);
|
|
|
|
assertInstanceof(foo(), A);
|
|
|
|
assertInstanceof(foo(), B);
|
|
|
|
})();
|
|
|
|
|
|
|
|
(function() {
|
|
|
|
const A = class A {
|
|
|
|
constructor(x, y, z) {
|
|
|
|
this.x = x;
|
|
|
|
this.y = y;
|
|
|
|
this.z = z;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
const B = A.bind(null, 1, 2);
|
|
|
|
|
|
|
|
function foo(z) { return new B(z); }
|
|
|
|
|
2019-03-01 10:19:54 +00:00
|
|
|
%PrepareFunctionForOptimization(foo);
|
2017-11-28 10:41:04 +00:00
|
|
|
assertEquals(1, foo(3).x);
|
|
|
|
assertEquals(2, foo(3).y);
|
|
|
|
assertEquals(3, foo(3).z);
|
|
|
|
%OptimizeFunctionOnNextCall(foo);
|
|
|
|
assertEquals(1, foo(3).x);
|
|
|
|
assertEquals(2, foo(3).y);
|
|
|
|
assertEquals(3, foo(3).z);
|
|
|
|
})();
|
|
|
|
|
|
|
|
(function() {
|
|
|
|
const A = class A {};
|
|
|
|
|
|
|
|
function foo() {
|
|
|
|
const B = A.bind();
|
|
|
|
return new B;
|
|
|
|
}
|
|
|
|
|
2019-03-01 10:19:54 +00:00
|
|
|
%PrepareFunctionForOptimization(foo);
|
2017-11-28 10:41:04 +00:00
|
|
|
assertInstanceof(foo(), A);
|
|
|
|
assertInstanceof(foo(), A);
|
|
|
|
%OptimizeFunctionOnNextCall(foo);
|
|
|
|
assertInstanceof(foo(), A);
|
|
|
|
})();
|
|
|
|
|
|
|
|
(function() {
|
|
|
|
const A = class A {
|
|
|
|
constructor(x, y, z) {
|
|
|
|
this.x = x;
|
|
|
|
this.y = y;
|
|
|
|
this.z = z;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
function foo(z) {
|
|
|
|
const B = A.bind(null, 1, 2);
|
|
|
|
return new B(z);
|
|
|
|
}
|
|
|
|
|
2019-03-01 10:19:54 +00:00
|
|
|
%PrepareFunctionForOptimization(foo);
|
2017-11-28 10:41:04 +00:00
|
|
|
assertEquals(1, foo(3).x);
|
|
|
|
assertEquals(2, foo(3).y);
|
|
|
|
assertEquals(3, foo(3).z);
|
|
|
|
%OptimizeFunctionOnNextCall(foo);
|
|
|
|
assertEquals(1, foo(3).x);
|
|
|
|
assertEquals(2, foo(3).y);
|
|
|
|
assertEquals(3, foo(3).z);
|
|
|
|
})();
|
2017-11-28 18:19:30 +00:00
|
|
|
|
|
|
|
(function() {
|
|
|
|
const A = class A {};
|
|
|
|
const B = A.bind();
|
|
|
|
|
|
|
|
function foo(B) {
|
|
|
|
return new B;
|
|
|
|
}
|
|
|
|
|
2019-03-01 10:19:54 +00:00
|
|
|
%PrepareFunctionForOptimization(foo);
|
2017-11-28 18:19:30 +00:00
|
|
|
assertInstanceof(foo(B), A);
|
|
|
|
assertInstanceof(foo(B), A);
|
|
|
|
%OptimizeFunctionOnNextCall(foo);
|
|
|
|
assertInstanceof(foo(B), A);
|
|
|
|
})();
|
|
|
|
|
|
|
|
(function() {
|
|
|
|
const A = class A {
|
|
|
|
constructor(x, y, z) {
|
|
|
|
this.x = x;
|
|
|
|
this.y = y;
|
|
|
|
this.z = z;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
const B = A.bind(null, 1, 2);
|
|
|
|
|
|
|
|
function foo(B, z) {
|
|
|
|
return new B(z);
|
|
|
|
}
|
|
|
|
|
2019-03-01 10:19:54 +00:00
|
|
|
%PrepareFunctionForOptimization(foo);
|
2017-11-28 18:19:30 +00:00
|
|
|
assertEquals(1, foo(B, 3).x);
|
|
|
|
assertEquals(2, foo(B, 3).y);
|
|
|
|
assertEquals(3, foo(B, 3).z);
|
|
|
|
%OptimizeFunctionOnNextCall(foo);
|
|
|
|
assertEquals(1, foo(B, 3).x);
|
|
|
|
assertEquals(2, foo(B, 3).y);
|
|
|
|
assertEquals(3, foo(B, 3).z);
|
|
|
|
})();
|
|
|
|
|
|
|
|
(function() {
|
|
|
|
const A = class A {
|
|
|
|
constructor(value) {
|
|
|
|
this.value = value;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
const C = class C extends A {
|
|
|
|
constructor() { super(1); }
|
|
|
|
};
|
|
|
|
const B = C.__proto__ = A.bind(null, 1);
|
|
|
|
|
2019-03-01 10:19:54 +00:00
|
|
|
%PrepareFunctionForOptimization(C);
|
2017-11-28 18:19:30 +00:00
|
|
|
assertInstanceof(new C(), A);
|
|
|
|
assertInstanceof(new C(), B);
|
|
|
|
assertInstanceof(new C(), C);
|
|
|
|
assertEquals(1, new C().value);
|
|
|
|
%OptimizeFunctionOnNextCall(C);
|
|
|
|
assertInstanceof(new C(), A);
|
|
|
|
assertInstanceof(new C(), B);
|
|
|
|
assertInstanceof(new C(), C);
|
|
|
|
assertEquals(1, new C().value);
|
|
|
|
})();
|
|
|
|
|
|
|
|
(function() {
|
|
|
|
const A = class A {};
|
|
|
|
const B = A.bind();
|
|
|
|
|
|
|
|
function bar(B, ...args) {
|
|
|
|
return new B(...args);
|
|
|
|
}
|
|
|
|
function foo(B) {
|
|
|
|
return bar(B)
|
|
|
|
}
|
|
|
|
|
2019-03-01 10:19:54 +00:00
|
|
|
%PrepareFunctionForOptimization(foo);
|
2017-11-28 18:19:30 +00:00
|
|
|
assertInstanceof(foo(B), A);
|
|
|
|
assertInstanceof(foo(B), A);
|
|
|
|
%OptimizeFunctionOnNextCall(foo);
|
|
|
|
assertInstanceof(foo(B), A);
|
|
|
|
})();
|
|
|
|
|
|
|
|
(function() {
|
|
|
|
const A = class A {
|
|
|
|
constructor(x, y, z) {
|
|
|
|
this.x = x;
|
|
|
|
this.y = y;
|
|
|
|
this.z = z;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
const B = A.bind(null, 1, 2);
|
|
|
|
|
|
|
|
function bar(B, ...args) {
|
|
|
|
return new B(...args);
|
|
|
|
}
|
|
|
|
function foo(B, z) {
|
|
|
|
return bar(B, z);
|
|
|
|
}
|
|
|
|
|
2019-03-01 10:19:54 +00:00
|
|
|
%PrepareFunctionForOptimization(foo);
|
2017-11-28 18:19:30 +00:00
|
|
|
assertEquals(1, foo(B, 3).x);
|
|
|
|
assertEquals(2, foo(B, 3).y);
|
|
|
|
assertEquals(3, foo(B, 3).z);
|
|
|
|
%OptimizeFunctionOnNextCall(foo);
|
|
|
|
assertEquals(1, foo(B, 3).x);
|
|
|
|
assertEquals(2, foo(B, 3).y);
|
|
|
|
assertEquals(3, foo(B, 3).z);
|
|
|
|
})();
|