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:
parent
0be13a7719
commit
bd3f8a524e
159
src/hydrogen.cc
159
src/hydrogen.cc
@ -7923,9 +7923,9 @@ bool HOptimizedGraphBuilder::TryInlineSetter(Handle<JSFunction> setter,
|
||||
}
|
||||
|
||||
|
||||
bool HOptimizedGraphBuilder::TryInlineIndirectCall(Handle<JSFunction> function,
|
||||
Call* expr,
|
||||
int arguments_count) {
|
||||
bool HOptimizedGraphBuilder::TryInlineApply(Handle<JSFunction> function,
|
||||
Call* expr,
|
||||
int arguments_count) {
|
||||
return TryInline(function,
|
||||
arguments_count,
|
||||
NULL,
|
||||
@ -7977,22 +7977,12 @@ bool HOptimizedGraphBuilder::TryInlineBuiltinFunctionCall(Call* expr) {
|
||||
|
||||
bool HOptimizedGraphBuilder::TryInlineBuiltinMethodCall(
|
||||
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());
|
||||
}
|
||||
}
|
||||
HValue* receiver,
|
||||
Handle<Map> receiver_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:
|
||||
@ -8100,7 +8090,7 @@ bool HOptimizedGraphBuilder::TryInlineBuiltinMethodCall(
|
||||
if (receiver_map->is_observed()) return false;
|
||||
ASSERT(receiver_map->is_extensible());
|
||||
|
||||
Drop(args_count_no_receiver);
|
||||
Drop(expr->arguments()->length());
|
||||
HValue* result;
|
||||
HValue* reduced_length;
|
||||
HValue* receiver = Pop();
|
||||
@ -8176,7 +8166,7 @@ bool HOptimizedGraphBuilder::TryInlineBuiltinMethodCall(
|
||||
Handle<JSObject> prototype(JSObject::cast(receiver_map->prototype()));
|
||||
BuildCheckPrototypeMaps(prototype, Handle<JSObject>());
|
||||
|
||||
const int argc = args_count_no_receiver;
|
||||
const int argc = expr->arguments()->length();
|
||||
if (argc != 1) return false;
|
||||
|
||||
HValue* value_to_push = Pop();
|
||||
@ -8233,7 +8223,7 @@ bool HOptimizedGraphBuilder::TryInlineBuiltinMethodCall(
|
||||
// Threshold for fast inlined Array.shift().
|
||||
HConstant* inline_threshold = Add<HConstant>(static_cast<int32_t>(16));
|
||||
|
||||
Drop(args_count_no_receiver);
|
||||
Drop(expr->arguments()->length());
|
||||
HValue* receiver = Pop();
|
||||
HValue* function = Pop();
|
||||
HValue* result;
|
||||
@ -8544,83 +8534,31 @@ bool HOptimizedGraphBuilder::TryInlineApiCall(Handle<JSFunction> function,
|
||||
}
|
||||
|
||||
|
||||
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()) {
|
||||
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) {
|
||||
bool HOptimizedGraphBuilder::TryCallApply(Call* expr) {
|
||||
ASSERT(expr->expression()->IsProperty());
|
||||
|
||||
if (!expr->IsMonomorphic()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Handle<Map> function_map = expr->GetReceiverTypes()->first();
|
||||
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;
|
||||
}
|
||||
|
||||
switch (expr->target()->shared()->builtin_function_id()) {
|
||||
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;
|
||||
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();
|
||||
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* function = Pop(); // f
|
||||
Drop(1); // apply
|
||||
@ -8634,6 +8572,7 @@ void HOptimizedGraphBuilder::BuildFunctionApply(Call* expr) {
|
||||
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.
|
||||
@ -8647,37 +8586,26 @@ void HOptimizedGraphBuilder::BuildFunctionApply(Call* expr) {
|
||||
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 = 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,
|
||||
Handle<JSFunction> target) {
|
||||
SharedFunctionInfo* shared = target->shared();
|
||||
@ -8939,12 +8867,11 @@ void HOptimizedGraphBuilder::VisitCall(Call* expr) {
|
||||
HConstant::cast(function)->handle(isolate()));
|
||||
expr->set_target(known_function);
|
||||
|
||||
if (TryIndirectCall(expr)) return;
|
||||
if (TryCallApply(expr)) return;
|
||||
CHECK_ALIVE(VisitExpressions(expr->arguments()));
|
||||
|
||||
Handle<Map> map = types->length() == 1 ? types->first() : Handle<Map>();
|
||||
if (TryInlineBuiltinMethodCall(expr, known_function, map,
|
||||
expr->arguments()->length())) {
|
||||
if (TryInlineBuiltinMethodCall(expr, receiver, map)) {
|
||||
if (FLAG_trace_inlining) {
|
||||
PrintF("Inlining builtin ");
|
||||
known_function->ShortPrint();
|
||||
|
@ -2321,13 +2321,8 @@ class HOptimizedGraphBuilder : public HGraphBuilder, public AstVisitor {
|
||||
void EnsureArgumentsArePushedForAccess();
|
||||
bool TryArgumentsAccess(Property* 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);
|
||||
// Try to optimize fun.apply(receiver, arguments) pattern.
|
||||
bool TryCallApply(Call* expr);
|
||||
|
||||
bool TryHandleArrayCall(Call* expr, HValue* function);
|
||||
bool TryHandleArrayCallNew(CallNew* expr, HValue* function);
|
||||
@ -2363,13 +2358,12 @@ class HOptimizedGraphBuilder : public HGraphBuilder, public AstVisitor {
|
||||
BailoutId id,
|
||||
BailoutId assignment_id,
|
||||
HValue* implicit_return_value);
|
||||
bool TryInlineIndirectCall(Handle<JSFunction> function,
|
||||
Call* expr,
|
||||
int arguments_count);
|
||||
bool TryInlineApply(Handle<JSFunction> function,
|
||||
Call* expr,
|
||||
int arguments_count);
|
||||
bool TryInlineBuiltinMethodCall(Call* expr,
|
||||
Handle<JSFunction> function,
|
||||
Handle<Map> receiver_map,
|
||||
int args_count_no_receiver);
|
||||
HValue* receiver,
|
||||
Handle<Map> receiver_map);
|
||||
bool TryInlineBuiltinFunctionCall(Call* expr);
|
||||
enum ApiCallType {
|
||||
kCallApiFunction,
|
||||
|
@ -7013,7 +7013,6 @@ class Script: public Struct {
|
||||
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) \
|
||||
|
@ -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);
|
Loading…
Reference in New Issue
Block a user