Revert "Optimize Function.prototype.call"

This reverts commit r21840.

R=danno@chromium.org
LOG=y
BUG=chromium:385565

Review URL: https://codereview.chromium.org/347573002

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@21887 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
dcarney@chromium.org 2014-06-18 14:04:41 +00:00
parent 0be13a7719
commit bd3f8a524e
4 changed files with 50 additions and 286 deletions

View File

@ -7923,9 +7923,9 @@ bool HOptimizedGraphBuilder::TryInlineSetter(Handle<JSFunction> setter,
} }
bool HOptimizedGraphBuilder::TryInlineIndirectCall(Handle<JSFunction> function, bool HOptimizedGraphBuilder::TryInlineApply(Handle<JSFunction> function,
Call* expr, Call* expr,
int arguments_count) { int arguments_count) {
return TryInline(function, return TryInline(function,
arguments_count, arguments_count,
NULL, NULL,
@ -7977,22 +7977,12 @@ bool HOptimizedGraphBuilder::TryInlineBuiltinFunctionCall(Call* expr) {
bool HOptimizedGraphBuilder::TryInlineBuiltinMethodCall( bool HOptimizedGraphBuilder::TryInlineBuiltinMethodCall(
Call* expr, Call* expr,
Handle<JSFunction> function, HValue* receiver,
Handle<Map> receiver_map, 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. // 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) { switch (id) {
case kStringCharCodeAt: case kStringCharCodeAt:
case kStringCharAt: case kStringCharAt:
@ -8100,7 +8090,7 @@ bool HOptimizedGraphBuilder::TryInlineBuiltinMethodCall(
if (receiver_map->is_observed()) return false; if (receiver_map->is_observed()) return false;
ASSERT(receiver_map->is_extensible()); ASSERT(receiver_map->is_extensible());
Drop(args_count_no_receiver); Drop(expr->arguments()->length());
HValue* result; HValue* result;
HValue* reduced_length; HValue* reduced_length;
HValue* receiver = Pop(); HValue* receiver = Pop();
@ -8176,7 +8166,7 @@ bool HOptimizedGraphBuilder::TryInlineBuiltinMethodCall(
Handle<JSObject> prototype(JSObject::cast(receiver_map->prototype())); Handle<JSObject> prototype(JSObject::cast(receiver_map->prototype()));
BuildCheckPrototypeMaps(prototype, Handle<JSObject>()); BuildCheckPrototypeMaps(prototype, Handle<JSObject>());
const int argc = args_count_no_receiver; const int argc = expr->arguments()->length();
if (argc != 1) return false; if (argc != 1) return false;
HValue* value_to_push = Pop(); HValue* value_to_push = Pop();
@ -8233,7 +8223,7 @@ bool HOptimizedGraphBuilder::TryInlineBuiltinMethodCall(
// Threshold for fast inlined Array.shift(). // Threshold for fast inlined Array.shift().
HConstant* inline_threshold = Add<HConstant>(static_cast<int32_t>(16)); HConstant* inline_threshold = Add<HConstant>(static_cast<int32_t>(16));
Drop(args_count_no_receiver); Drop(expr->arguments()->length());
HValue* receiver = Pop(); HValue* receiver = Pop();
HValue* function = Pop(); HValue* function = Pop();
HValue* result; HValue* result;
@ -8544,83 +8534,31 @@ bool HOptimizedGraphBuilder::TryInlineApiCall(Handle<JSFunction> function,
} }
void HOptimizedGraphBuilder::HandleIndirectCall(Call* expr, bool HOptimizedGraphBuilder::TryCallApply(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()) {
known_function = Handle<JSFunction>::cast(
HConstant::cast(function)->handle(isolate()));
if (TryInlineIndirectCall(known_function, expr, args_count_no_receiver)) {
return;
}
Handle<Map> map;
if (TryInlineBuiltinMethodCall(expr, known_function, map,
args_count_no_receiver)) {
if (FLAG_trace_inlining) {
PrintF("Inlining builtin ");
known_function->ShortPrint();
PrintF("\n");
}
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) {
ASSERT(expr->expression()->IsProperty()); ASSERT(expr->expression()->IsProperty());
if (!expr->IsMonomorphic()) { if (!expr->IsMonomorphic()) {
return false; return false;
} }
Handle<Map> function_map = expr->GetReceiverTypes()->first(); Handle<Map> function_map = expr->GetReceiverTypes()->first();
if (function_map->instance_type() != JS_FUNCTION_TYPE || if (function_map->instance_type() != JS_FUNCTION_TYPE ||
!expr->target()->shared()->HasBuiltinFunctionId()) { !expr->target()->shared()->HasBuiltinFunctionId() ||
expr->target()->shared()->builtin_function_id() != kFunctionApply) {
return false; return false;
} }
switch (expr->target()->shared()->builtin_function_id()) { if (current_info()->scope()->arguments() == NULL) return false;
case kFunctionCall: {
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;
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();
}
void HOptimizedGraphBuilder::BuildFunctionApply(Call* expr) {
ZoneList<Expression*>* args = expr->arguments(); ZoneList<Expression*>* args = expr->arguments();
CHECK_ALIVE(VisitForValue(args->at(0))); 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;
// Found pattern f.apply(receiver, arguments).
CHECK_ALIVE_OR_RETURN(VisitForValue(args->at(0)), true);
HValue* receiver = Pop(); // receiver HValue* receiver = Pop(); // receiver
HValue* function = Pop(); // f HValue* function = Pop(); // f
Drop(1); // apply Drop(1); // apply
@ -8634,6 +8572,7 @@ void HOptimizedGraphBuilder::BuildFunctionApply(Call* expr) {
length, length,
elements); elements);
ast_context()->ReturnInstruction(result, expr->id()); ast_context()->ReturnInstruction(result, expr->id());
return true;
} else { } else {
// We are inside inlined function and we know exactly what is inside // We are inside inlined function and we know exactly what is inside
// arguments object. But we need to be able to materialize at deopt. // arguments object. But we need to be able to materialize at deopt.
@ -8647,37 +8586,26 @@ void HOptimizedGraphBuilder::BuildFunctionApply(Call* expr) {
for (int i = 1; i < arguments_count; i++) { for (int i = 1; i < arguments_count; i++) {
Push(arguments_values->at(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 = Pop(); // f
HValue* receiver;
ZoneList<Expression*>* args = expr->arguments();
int args_length = args->length();
Drop(1); // call
if (args_length == 0) {
receiver = graph()->GetConstantUndefined();
args_length = 1;
} else {
CHECK_ALIVE(VisitForValue(args->at(0)));
receiver = Pop();
}
receiver = BuildWrapReceiver(receiver, function);
Push(function);
Push(receiver);
for (int i = 1; i < args_length; i++) {
CHECK_ALIVE(VisitForValue(args->at(i)));
}
HandleIndirectCall(expr, function, args_length);
}
HValue* HOptimizedGraphBuilder::ImplicitReceiverFor(HValue* function, HValue* HOptimizedGraphBuilder::ImplicitReceiverFor(HValue* function,
Handle<JSFunction> target) { Handle<JSFunction> target) {
SharedFunctionInfo* shared = target->shared(); SharedFunctionInfo* shared = target->shared();
@ -8939,12 +8867,11 @@ void HOptimizedGraphBuilder::VisitCall(Call* expr) {
HConstant::cast(function)->handle(isolate())); HConstant::cast(function)->handle(isolate()));
expr->set_target(known_function); expr->set_target(known_function);
if (TryIndirectCall(expr)) return; if (TryCallApply(expr)) return;
CHECK_ALIVE(VisitExpressions(expr->arguments())); CHECK_ALIVE(VisitExpressions(expr->arguments()));
Handle<Map> map = types->length() == 1 ? types->first() : Handle<Map>(); Handle<Map> map = types->length() == 1 ? types->first() : Handle<Map>();
if (TryInlineBuiltinMethodCall(expr, known_function, map, if (TryInlineBuiltinMethodCall(expr, receiver, map)) {
expr->arguments()->length())) {
if (FLAG_trace_inlining) { if (FLAG_trace_inlining) {
PrintF("Inlining builtin "); PrintF("Inlining builtin ");
known_function->ShortPrint(); known_function->ShortPrint();

View File

@ -2321,13 +2321,8 @@ class HOptimizedGraphBuilder : public HGraphBuilder, public AstVisitor {
void EnsureArgumentsArePushedForAccess(); void EnsureArgumentsArePushedForAccess();
bool TryArgumentsAccess(Property* expr); bool TryArgumentsAccess(Property* expr);
// Shared code for .call and .apply optimizations. // Try to optimize fun.apply(receiver, arguments) pattern.
void HandleIndirectCall(Call* expr, HValue* function, int arguments_count); bool TryCallApply(Call* expr);
// 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 TryHandleArrayCall(Call* expr, HValue* function);
bool TryHandleArrayCallNew(CallNew* expr, HValue* function); bool TryHandleArrayCallNew(CallNew* expr, HValue* function);
@ -2363,13 +2358,12 @@ class HOptimizedGraphBuilder : public HGraphBuilder, public AstVisitor {
BailoutId id, BailoutId id,
BailoutId assignment_id, BailoutId assignment_id,
HValue* implicit_return_value); HValue* implicit_return_value);
bool TryInlineIndirectCall(Handle<JSFunction> function, bool TryInlineApply(Handle<JSFunction> function,
Call* expr, Call* expr,
int arguments_count); int arguments_count);
bool TryInlineBuiltinMethodCall(Call* expr, bool TryInlineBuiltinMethodCall(Call* expr,
Handle<JSFunction> function, HValue* receiver,
Handle<Map> receiver_map, Handle<Map> receiver_map);
int args_count_no_receiver);
bool TryInlineBuiltinFunctionCall(Call* expr); bool TryInlineBuiltinFunctionCall(Call* expr);
enum ApiCallType { enum ApiCallType {
kCallApiFunction, kCallApiFunction,

View File

@ -7013,7 +7013,6 @@ class Script: public Struct {
V(Array.prototype, pop, ArrayPop) \ V(Array.prototype, pop, ArrayPop) \
V(Array.prototype, shift, ArrayShift) \ V(Array.prototype, shift, ArrayShift) \
V(Function.prototype, apply, FunctionApply) \ V(Function.prototype, apply, FunctionApply) \
V(Function.prototype, call, FunctionCall) \
V(String.prototype, charCodeAt, StringCharCodeAt) \ V(String.prototype, charCodeAt, StringCharCodeAt) \
V(String.prototype, charAt, StringCharAt) \ V(String.prototype, charAt, StringCharAt) \
V(String, fromCharCode, StringFromCharCode) \ V(String, fromCharCode, StringFromCharCode) \

View File

@ -1,156 +0,0 @@
// Copyright 2014 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 --noalways-opt
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;
// For the HConstant
Array.prototype.fun = function() {
funRecv = this;
called++;
assertEquals(0, arguments.length);
};
function callNoArgs() {
[].fun.call();
}
callNoArgs();
callNoArgs();
assertEquals(this, funRecv);
%OptimizeFunctionOnNextCall(callNoArgs);
callNoArgs();
assertEquals(this, funRecv);
assertEquals(3, called);
assertOptimized(callNoArgs);
var funStrictRecv;
called = 0;
Array.prototype.funStrict = function() {
"use strict";
funStrictRecv = this;
called++;
assertEquals(0, arguments.length);
};
function callStrictNoArgs() {
[].funStrict.call();
}
callStrictNoArgs();
callStrictNoArgs();
assertEquals(undefined, funStrictRecv);
%OptimizeFunctionOnNextCall(callStrictNoArgs);
callStrictNoArgs();
assertEquals(undefined, funStrictRecv);
assertEquals(3, called);
assertOptimized(callStrictNoArgs);
called = 0;
Array.prototype.manyArgs = function() {
"use strict";
assertEquals(5, arguments.length);
assertEquals(0, this);
assertEquals(5, arguments[4]);
called++;
}
function callManyArgs() {
[].manyArgs.call(0, 1, 2, 3, 4, 5);
}
callManyArgs();
callManyArgs();
%OptimizeFunctionOnNextCall(callManyArgs);
callManyArgs();
assertOptimized(callManyArgs);
assertEquals(called, 3);
called = 0;
Array.prototype.manyArgsSloppy = function() {
assertTrue(this instanceof Number);
assertEquals(5, arguments.length);
assertEquals(0, this.valueOf());
assertEquals(5, arguments[4]);
called++;
}
function callManyArgsSloppy() {
[].manyArgsSloppy.call(0, 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);
}
callInlined(1);
callInlined(2);
%OptimizeFunctionOnNextCall(callInlineableBuiltinIndirectlyWhileInlined);
%OptimizeFunctionOnNextCall(callInlined);
callInlined(3);
copy.push(1, 2, 3);
assertOptimized(callInlined);
assertOptimized(callInlineableBuiltinIndirectlyWhileInlined);
assertArrayEquals(copy, this.array);
assertEquals(3, called);