Revision: 24631
Author: [email protected]
Date: Wed Oct 15 12:22:15 2014 UTC
Log: Optimize Function.prototype.call
BUG=
[email protected], [email protected], [email protected]
Review URL: https://codereview.chromium.org/588573002
Patch from Petka Antonov <[email protected]>.
https://code.google.com/p/v8/source/detail?r=24631
Added:
/branches/bleeding_edge/test/mjsunit/compiler/deopt-inlined-from-call.js
/branches/bleeding_edge/test/mjsunit/compiler/inlined-call-mapcheck.js
/branches/bleeding_edge/test/mjsunit/compiler/inlined-call.js
/branches/bleeding_edge/test/mjsunit/regress/regress-385565.js
Modified:
/branches/bleeding_edge/src/hydrogen.cc
/branches/bleeding_edge/src/hydrogen.h
/branches/bleeding_edge/src/objects.h
=======================================
--- /dev/null
+++
/branches/bleeding_edge/test/mjsunit/compiler/deopt-inlined-from-call.js
Wed Oct 15 12:22:15 2014 UTC
@@ -0,0 +1,154 @@
+// Copyright 2014 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 --noalways-opt
+
+var global = this;
+
+Array.prototype.f = function() {
+ return 0;
+};
+
+(function() {
+ var called = 0;
+
+ function g(x, y, called) {
+ return called + 1;
+ }
+
+ function f(deopt, called) {
+ return g([].f.call({}), deopt + 1, called);
+ }
+
+ called = f(0, called);
+ called = f(0, called);
+ %OptimizeFunctionOnNextCall(f);
+ called = f(0, called);
+ assertOptimized(f);
+ called = f({}, called);
+ assertUnoptimized(f);
+ assertEquals(4, called);
+})();
+
+(function() {
+ // The array built-ins are only inlined if the receiver is a
+ // HConstant, this seems to require a *unique* global identifier
+ // each time.
+ global.a1 = [1,2,3,4];
+ var obj = {value: 3};
+
+ function f(b) {
+ return [].pop.call(a1) + b.value;
+ }
+
+ assertEquals(7, f(obj));
+ assertEquals(6, f(obj));
+ %OptimizeFunctionOnNextCall(f);
+ assertEquals(5, f(obj));
+ assertOptimized(f);
+ assertEquals(4, f({d: 0, value: 3}));
+ assertUnoptimized(f);
+ assertEquals(0, a1.length);
+})();
+
+
+(function() {
+ global.a2 = [1,2,3,4];
+ var obj = {value: 3};
+
+ function f(b) {
+ return [].shift.call(a2) + b.value;
+ }
+
+ assertEquals(4, f(obj));
+ assertEquals(5, f(obj));
+ %OptimizeFunctionOnNextCall(f);
+ assertEquals(6, f(obj));
+ assertOptimized(f);
+ assertEquals(7, f({d: 0, value: 3}));
+ assertUnoptimized(f);
+ assertEquals(0, a2.length);
+})();
+
+(function() {
+ global.a3 = [1,2,3,4];
+ var obj = {value: 3};
+
+ function f(b) {
+ return [].push.call(a3, b.value);
+ }
+
+ assertEquals(5, f(obj));
+ assertEquals(6, f(obj));
+ %OptimizeFunctionOnNextCall(f);
+ assertEquals(7, f(obj));
+ assertOptimized(f);
+ assertEquals(8, f({d: 0, value: 3}));
+ assertUnoptimized(f);
+ assertEquals(8, a3.length);
+ assertEquals(3, a3[7]);
+})();
+
+(function() {
+ global.a4 = [1,2,3,4];
+ var obj = {value: 3};
+
+ function f(b) {
+ return [].indexOf.call(a4, b.value);
+ }
+
+ f(obj);
+ f(obj);
+ %OptimizeFunctionOnNextCall(f);
+ var index1 = f(obj);
+ assertOptimized(f);
+ var index2 = f({d: 0, value: 3});
+ assertUnoptimized(f);
+
+ assertEquals(2, index1);
+ assertEquals(index1, index2);
+})();
+
+(function() {
+ global.a5 = [1,2,3,4];
+ var obj = {value: 3};
+
+ function f(b) {
+ return [].lastIndexOf.call(a5, b.value);
+ }
+
+ f(obj);
+ f(obj);
+ %OptimizeFunctionOnNextCall(f);
+ var index1 = f(obj);
+ assertOptimized(f);
+ var index2 = f({d: 0, value: 3});
+ assertUnoptimized(f);
+
+ assertEquals(2, index1);
+ assertEquals(index1, index2);
+})();
=======================================
--- /dev/null
+++ /branches/bleeding_edge/test/mjsunit/compiler/inlined-call-mapcheck.js
Wed Oct 15 12:22:15 2014 UTC
@@ -0,0 +1,43 @@
+// Copyright 2014 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 --noalways-opt
+
+(function() {
+ function f(x) {
+ for (i = 0; i < 1; i++) {
+ x.call(this);
+ }
+ }
+
+ function g() {}
+
+ f(g);
+ f(g);
+ %OptimizeFunctionOnNextCall(f);
+ assertThrows(function() { f('whatever') }, TypeError);
+})();
=======================================
--- /dev/null
+++ /branches/bleeding_edge/test/mjsunit/compiler/inlined-call.js Wed Oct
15 12:22:15 2014 UTC
@@ -0,0 +1,190 @@
+// Copyright 2014 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 --noalways-opt
+
+var global = this;
+
+// For the HConstant
+Array.prototype.fun = function() {
+ funRecv = this;
+ called++;
+ assertEquals(0, arguments.length);
+};
+
+Array.prototype.funStrict = function() {
+ "use strict";
+ funStrictRecv = this;
+ called++;
+ assertEquals(0, arguments.length);
+};
+
+Array.prototype.manyArgs = function() {
+ "use strict";
+ assertEquals(5, arguments.length);
+ assertEquals(0, this);
+ assertEquals(5, arguments[4]);
+ called++;
+}
+
+Array.prototype.manyArgsSloppy = function() {
+ assertEquals(global, this);
+ assertEquals(5, arguments.length);
+ assertEquals(5, arguments[4]);
+ called++;
+}
+
+var array = [];
+for (var i = 0; i < 100; ++i) {
+ array[i] = i;
+}
+
+var copy = array.slice();
+
+function unshiftsArray(num) {
+ [].unshift.call(array, num);
+}
+
+unshiftsArray(50);
+unshiftsArray(60);
+%OptimizeFunctionOnNextCall(unshiftsArray);
+unshiftsArray(80);
+unshiftsArray(50);
+unshiftsArray(60);
+
+copy.unshift(50);
+copy.unshift(60);
+copy.unshift(80);
+copy.unshift(50);
+copy.unshift(60);
+
+assertOptimized(unshiftsArray);
+assertArrayEquals(array, copy);
+
+
+var called = 0;
+var funRecv;
+
+function callNoArgs() {
+ [].fun.call();
+}
+
+callNoArgs();
+callNoArgs();
+assertEquals(this, funRecv);
+%OptimizeFunctionOnNextCall(callNoArgs);
+callNoArgs();
+assertEquals(this, funRecv);
+assertEquals(3, called);
+assertOptimized(callNoArgs);
+
+var funStrictRecv;
+called = 0;
+
+function callStrictNoArgs() {
+ [].funStrict.call();
+}
+
+callStrictNoArgs();
+callStrictNoArgs();
+assertEquals(undefined, funStrictRecv);
+%OptimizeFunctionOnNextCall(callStrictNoArgs);
+callStrictNoArgs();
+assertEquals(undefined, funStrictRecv);
+assertEquals(3, called);
+assertOptimized(callStrictNoArgs);
+
+called = 0;
+
+
+function callManyArgs() {
+ [].manyArgs.call(0, 1, 2, 3, 4, 5);
+}
+
+callManyArgs();
+callManyArgs();
+%OptimizeFunctionOnNextCall(callManyArgs);
+callManyArgs();
+assertOptimized(callManyArgs);
+assertEquals(called, 3);
+
+called = 0;
+
+
+function callManyArgsSloppy() {
+ [].manyArgsSloppy.call(null, 1, 2, 3, 4, 5);
+}
+
+callManyArgsSloppy();
+callManyArgsSloppy();
+%OptimizeFunctionOnNextCall(callManyArgsSloppy);
+callManyArgsSloppy();
+assertOptimized(callManyArgsSloppy);
+assertEquals(called, 3);
+
+var str = "hello";
+var code = str.charCodeAt(3);
+called = 0;
+function callBuiltinIndirectly() {
+ called++;
+ return "".charCodeAt.call(str, 3);
+}
+
+callBuiltinIndirectly();
+callBuiltinIndirectly();
+%OptimizeFunctionOnNextCall(callBuiltinIndirectly);
+assertEquals(code, callBuiltinIndirectly());
+assertOptimized(callBuiltinIndirectly);
+assertEquals(3, called);
+
+this.array = [1,2,3,4,5,6,7,8,9];
+var copy = this.array.slice();
+called = 0;
+
+function callInlineableBuiltinIndirectlyWhileInlined() {
+ called++;
+ return [].push.apply(array, arguments);
+}
+
+function callInlined(num) {
+ return callInlineableBuiltinIndirectlyWhileInlined(num);
+}
+
+callInlineableBuiltinIndirectlyWhileInlined(1);
+callInlineableBuiltinIndirectlyWhileInlined(2);
+%OptimizeFunctionOnNextCall(callInlineableBuiltinIndirectlyWhileInlined);
+callInlineableBuiltinIndirectlyWhileInlined(3);
+assertOptimized(callInlineableBuiltinIndirectlyWhileInlined);
+
+callInlined(1);
+callInlined(2);
+%OptimizeFunctionOnNextCall(callInlined);
+callInlined(3);
+copy.push(1, 2, 3, 1, 2, 3);
+assertOptimized(callInlined);
+assertArrayEquals(copy, this.array);
+assertEquals(6, called);
=======================================
--- /dev/null
+++ /branches/bleeding_edge/test/mjsunit/regress/regress-385565.js Wed Oct
15 12:22:15 2014 UTC
@@ -0,0 +1,70 @@
+// Copyright 2014 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 --noalways-opt
+
+var calls = 0;
+
+function callsFReceiver(o) {
+ return [].f.call(new Number(o.m), 1, 2, 3);
+}
+
+// For the HConstant
+Array.prototype.f = function() {
+ calls++;
+ return +this;
+};
+
+
+var o1 = {m: 1};
+var o2 = {a: 0, m:1};
+
+var r1 = callsFReceiver(o1);
+callsFReceiver(o1);
+%OptimizeFunctionOnNextCall(callsFReceiver);
+var r2 = callsFReceiver(o1);
+assertOptimized(callsFReceiver);
+callsFReceiver(o2);
+assertUnoptimized(callsFReceiver);
+var r3 = callsFReceiver(o1);
+
+assertEquals(1, r1);
+assertTrue(r1 === r2);
+assertTrue(r2 === r3);
+
+r1 = callsFReceiver(o1);
+callsFReceiver(o1);
+%OptimizeFunctionOnNextCall(callsFReceiver);
+r2 = callsFReceiver(o1);
+callsFReceiver(o2);
+r3 = callsFReceiver(o1);
+
+assertEquals(1, r1);
+assertTrue(r1 === r2);
+assertTrue(r2 === r3);
+
+assertEquals(10, calls);
=======================================
--- /branches/bleeding_edge/src/hydrogen.cc Wed Oct 15 12:06:41 2014 UTC
+++ /branches/bleeding_edge/src/hydrogen.cc Wed Oct 15 12:22:15 2014 UTC
@@ -8108,9 +8108,9 @@
}
-bool HOptimizedGraphBuilder::TryInlineApply(Handle<JSFunction> function,
- Call* expr,
- int arguments_count) {
+bool HOptimizedGraphBuilder::TryInlineIndirectCall(Handle<JSFunction>
function,
+ Call* expr,
+ int arguments_count) {
return TryInline(function,
arguments_count,
NULL,
@@ -8162,13 +8162,22 @@
bool HOptimizedGraphBuilder::TryInlineBuiltinMethodCall(
- Call* expr,
- HValue* receiver,
- Handle<Map> receiver_map) {
+ Call* expr, Handle<JSFunction> function, Handle<Map> receiver_map,
+ int args_count_no_receiver) {
+ if (!function->shared()->HasBuiltinFunctionId()) return false;
+ BuiltinFunctionId id = function->shared()->builtin_function_id();
+ int argument_count = args_count_no_receiver + 1; // Plus receiver.
+
+ if (receiver_map.is_null()) {
+ HValue* receiver =
environment()->ExpressionStackAt(args_count_no_receiver);
+ if (receiver->IsConstant() &&
+ HConstant::cast(receiver)->handle(isolate())->IsHeapObject()) {
+ receiver_map =
+ handle(Handle<HeapObject>::cast(
+ HConstant::cast(receiver)->handle(isolate()))->map());
+ }
+ }
// Try to inline calls like Math.* as operations in the calling function.
- if (!expr->target()->shared()->HasBuiltinFunctionId()) return false;
- BuiltinFunctionId id = expr->target()->shared()->builtin_function_id();
- int argument_count = expr->arguments()->length() + 1; // Plus receiver.
switch (id) {
case kStringCharCodeAt:
case kStringCharAt:
@@ -8277,7 +8286,7 @@
if (receiver_map->is_observed()) return false;
if (!receiver_map->is_extensible()) return false;
- Drop(expr->arguments()->length());
+ Drop(args_count_no_receiver);
HValue* result;
HValue* reduced_length;
HValue* receiver = Pop();
@@ -8353,7 +8362,7 @@
Handle<JSObject>
prototype(JSObject::cast(receiver_map->prototype()));
BuildCheckPrototypeMaps(prototype, Handle<JSObject>());
- const int argc = expr->arguments()->length();
+ const int argc = args_count_no_receiver;
if (argc != 1) return false;
HValue* value_to_push = Pop();
@@ -8410,7 +8419,7 @@
// Threshold for fast inlined Array.shift().
HConstant* inline_threshold =
Add<HConstant>(static_cast<int32_t>(16));
- Drop(expr->arguments()->length());
+ Drop(args_count_no_receiver);
HValue* receiver = Pop();
HValue* function = Pop();
HValue* result;
@@ -8716,7 +8725,47 @@
}
-bool HOptimizedGraphBuilder::TryCallApply(Call* expr) {
+void HOptimizedGraphBuilder::HandleIndirectCall(Call* expr, HValue*
function,
+ int arguments_count) {
+ Handle<JSFunction> known_function;
+ int args_count_no_receiver = arguments_count - 1;
+ if (function->IsConstant() &&
+ HConstant::cast(function)->handle(isolate())->IsJSFunction()) {
+ HValue* receiver =
environment()->ExpressionStackAt(args_count_no_receiver);
+ Handle<Map> receiver_map;
+ if (receiver->IsConstant() &&
+ HConstant::cast(receiver)->handle(isolate())->IsHeapObject()) {
+ receiver_map =
+ handle(Handle<HeapObject>::cast(
+ HConstant::cast(receiver)->handle(isolate()))->map());
+ }
+
+ known_function =
+
Handle<JSFunction>::cast(HConstant::cast(function)->handle(isolate()));
+ if (TryInlineBuiltinMethodCall(expr, known_function, receiver_map,
+ args_count_no_receiver)) {
+ if (FLAG_trace_inlining) {
+ PrintF("Inlining builtin ");
+ known_function->ShortPrint();
+ PrintF("\n");
+ }
+ return;
+ }
+
+ if (TryInlineIndirectCall(known_function, expr,
args_count_no_receiver)) {
+ return;
+ }
+ }
+
+ PushArgumentsFromEnvironment(arguments_count);
+ HInvokeFunction* call =
+ New<HInvokeFunction>(function, known_function, arguments_count);
+ Drop(1); // Function
+ ast_context()->ReturnInstruction(call, expr->id());
+}
+
+
+bool HOptimizedGraphBuilder::TryIndirectCall(Call* expr) {
DCHECK(expr->expression()->IsProperty());
if (!expr->IsMonomorphic()) {
@@ -8724,27 +8773,45 @@
}
Handle<Map> function_map = expr->GetReceiverTypes()->first();
if (function_map->instance_type() != JS_FUNCTION_TYPE ||
- !expr->target()->shared()->HasBuiltinFunctionId() ||
- expr->target()->shared()->builtin_function_id() != kFunctionApply) {
+ !expr->target()->shared()->HasBuiltinFunctionId()) {
return false;
}
- if (current_info()->scope()->arguments() == NULL) return false;
+ switch (expr->target()->shared()->builtin_function_id()) {
+ case kFunctionCall: {
+ if (expr->arguments()->length() == 0) return false;
+ BuildFunctionCall(expr);
+ return true;
+ }
+ case kFunctionApply: {
+ // For .apply, only the pattern f.apply(receiver, arguments)
+ // is supported.
+ if (current_info()->scope()->arguments() == NULL) return false;
- ZoneList<Expression*>* args = expr->arguments();
- if (args->length() != 2) return false;
+ ZoneList<Expression*>* args = expr->arguments();
+ if (args->length() != 2) return false;
- VariableProxy* arg_two = args->at(1)->AsVariableProxy();
- if (arg_two == NULL || !arg_two->var()->IsStackAllocated()) return false;
- HValue* arg_two_value = LookupAndMakeLive(arg_two->var());
- if (!arg_two_value->CheckFlag(HValue::kIsArguments)) return false;
+ VariableProxy* arg_two = args->at(1)->AsVariableProxy();
+ if (arg_two == NULL || !arg_two->var()->IsStackAllocated()) return
false;
+ HValue* arg_two_value = LookupAndMakeLive(arg_two->var());
+ if (!arg_two_value->CheckFlag(HValue::kIsArguments)) return false;
+ BuildFunctionApply(expr);
+ return true;
+ }
+ default: { return false; }
+ }
+ UNREACHABLE();
+}
- // Found pattern f.apply(receiver, arguments).
- CHECK_ALIVE_OR_RETURN(VisitForValue(args->at(0)), true);
+
+void HOptimizedGraphBuilder::BuildFunctionApply(Call* expr) {
+ ZoneList<Expression*>* args = expr->arguments();
+ CHECK_ALIVE(VisitForValue(args->at(0)));
HValue* receiver = Pop(); // receiver
HValue* function = Pop(); // f
Drop(1); // apply
+ Handle<Map> function_map = expr->GetReceiverTypes()->first();
HValue* checked_function = AddCheckMap(function, function_map);
if (function_state()->outer() == NULL) {
@@ -8756,7 +8823,6 @@
length,
elements);
ast_context()->ReturnInstruction(result, expr->id());
- return true;
} else {
// We are inside inlined function and we know exactly what is inside
// arguments object. But we need to be able to materialize at deopt.
@@ -8770,23 +8836,33 @@
for (int i = 1; i < arguments_count; i++) {
Push(arguments_values->at(i));
}
+ HandleIndirectCall(expr, function, arguments_count);
+ }
+}
- Handle<JSFunction> known_function;
- if (function->IsConstant() &&
- HConstant::cast(function)->handle(isolate())->IsJSFunction()) {
- known_function = Handle<JSFunction>::cast(
- HConstant::cast(function)->handle(isolate()));
- int args_count = arguments_count - 1; // Excluding receiver.
- if (TryInlineApply(known_function, expr, args_count)) return true;
- }
- PushArgumentsFromEnvironment(arguments_count);
- HInvokeFunction* call = New<HInvokeFunction>(
- function, known_function, arguments_count);
- Drop(1); // Function.
- ast_context()->ReturnInstruction(call, expr->id());
- return true;
- }
+// f.call(...)
+void HOptimizedGraphBuilder::BuildFunctionCall(Call* expr) {
+ HValue* function = Top(); // f
+ Handle<Map> function_map = expr->GetReceiverTypes()->first();
+ HValue* checked_function = AddCheckMap(function, function_map);
+
+ // f and call are on the stack in the unoptimized code
+ // during evaluation of the arguments.
+ CHECK_ALIVE(VisitExpressions(expr->arguments()));
+
+ int args_length = expr->arguments()->length();
+ int receiver_index = args_length - 1;
+ // Patch the receiver.
+ HValue* receiver = BuildWrapReceiver(
+ environment()->ExpressionStackAt(receiver_index), checked_function);
+ environment()->SetExpressionStackAt(receiver_index, receiver);
+
+ // Call must not be on the stack from now on.
+ int call_index = args_length + 1;
+ environment()->RemoveExpressionStackAt(call_index);
+
+ HandleIndirectCall(expr, function, args_length);
}
@@ -9052,11 +9128,12 @@
HConstant::cast(function)->handle(isolate()));
expr->set_target(known_function);
- if (TryCallApply(expr)) return;
+ if (TryIndirectCall(expr)) return;
CHECK_ALIVE(VisitExpressions(expr->arguments()));
Handle<Map> map = types->length() == 1 ? types->first() :
Handle<Map>();
- if (TryInlineBuiltinMethodCall(expr, receiver, map)) {
+ if (TryInlineBuiltinMethodCall(expr, known_function, map,
+ expr->arguments()->length())) {
if (FLAG_trace_inlining) {
PrintF("Inlining builtin ");
known_function->ShortPrint();
@@ -12090,6 +12167,18 @@
}
values_[index] = value;
}
+
+
+HValue* HEnvironment::RemoveExpressionStackAt(int index_from_top) {
+ int count = index_from_top + 1;
+ int index = values_.length() - count;
+ DCHECK(HasExpressionAt(index));
+ // Simulate popping 'count' elements and then
+ // pushing 'count - 1' elements back.
+ pop_count_ += Max(count - push_count_, 0);
+ push_count_ = Max(push_count_ - count, 0) + (count - 1);
+ return values_.Remove(index);
+}
void HEnvironment::Drop(int count) {
=======================================
--- /branches/bleeding_edge/src/hydrogen.h Wed Oct 15 12:06:41 2014 UTC
+++ /branches/bleeding_edge/src/hydrogen.h Wed Oct 15 12:22:15 2014 UTC
@@ -642,6 +642,7 @@
}
void SetExpressionStackAt(int index_from_top, HValue* value);
+ HValue* RemoveExpressionStackAt(int index_from_top);
HEnvironment* Copy() const;
HEnvironment* CopyWithoutHistory() const;
@@ -2311,8 +2312,13 @@
void EnsureArgumentsArePushedForAccess();
bool TryArgumentsAccess(Property* expr);
- // Try to optimize fun.apply(receiver, arguments) pattern.
- bool TryCallApply(Call* expr);
+ // Shared code for .call and .apply optimizations.
+ void HandleIndirectCall(Call* expr, HValue* function, int
arguments_count);
+ // Try to optimize indirect calls such as fun.apply(receiver, arguments)
+ // or fun.call(...).
+ bool TryIndirectCall(Call* expr);
+ void BuildFunctionApply(Call* expr);
+ void BuildFunctionCall(Call* expr);
bool TryHandleArrayCall(Call* expr, HValue* function);
bool TryHandleArrayCallNew(CallNew* expr, HValue* function);
@@ -2348,12 +2354,11 @@
BailoutId id,
BailoutId assignment_id,
HValue* implicit_return_value);
- bool TryInlineApply(Handle<JSFunction> function,
- Call* expr,
- int arguments_count);
- bool TryInlineBuiltinMethodCall(Call* expr,
- HValue* receiver,
- Handle<Map> receiver_map);
+ bool TryInlineIndirectCall(Handle<JSFunction> function, Call* expr,
+ int arguments_count);
+ bool TryInlineBuiltinMethodCall(Call* expr, Handle<JSFunction> function,
+ Handle<Map> receiver_map,
+ int args_count_no_receiver);
bool TryInlineBuiltinFunctionCall(Call* expr);
enum ApiCallType {
kCallApiFunction,
=======================================
--- /branches/bleeding_edge/src/objects.h Wed Oct 15 10:11:08 2014 UTC
+++ /branches/bleeding_edge/src/objects.h Wed Oct 15 12:22:15 2014 UTC
@@ -6560,6 +6560,7 @@
V(Array.prototype, pop, ArrayPop) \
V(Array.prototype, shift, ArrayShift) \
V(Function.prototype, apply, FunctionApply) \
+ V(Function.prototype, call, FunctionCall) \
V(String.prototype, charCodeAt, StringCharCodeAt) \
V(String.prototype, charAt, StringCharAt) \
V(String, fromCharCode, StringFromCharCode) \
--
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev
---
You received this message because you are subscribed to the Google Groups "v8-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
For more options, visit https://groups.google.com/d/optout.