inline api getters in crankshaft

R=verwaest@chromium.org

BUG=

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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@19110 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
dcarney@chromium.org 2014-02-05 15:52:31 +00:00
parent 5949fe1679
commit 5028ebaa8b
3 changed files with 159 additions and 57 deletions

View File

@ -5361,7 +5361,8 @@ bool HOptimizedGraphBuilder::PropertyAccessInfo::IsCompatible(
if (info->has_holder()) return false; if (info->has_holder()) return false;
if (lookup_.IsPropertyCallbacks()) { if (lookup_.IsPropertyCallbacks()) {
return accessor_.is_identical_to(info->accessor_); return accessor_.is_identical_to(info->accessor_) &&
api_holder_.is_identical_to(info->api_holder_);
} }
if (lookup_.IsConstant()) { if (lookup_.IsConstant()) {
@ -5407,9 +5408,20 @@ bool HOptimizedGraphBuilder::PropertyAccessInfo::LoadResult(Handle<Map> map) {
: Handle<AccessorPair>::cast(callback)->setter(); : Handle<AccessorPair>::cast(callback)->setter();
if (!raw_accessor->IsJSFunction()) return false; if (!raw_accessor->IsJSFunction()) return false;
Handle<JSFunction> accessor = handle(JSFunction::cast(raw_accessor)); Handle<JSFunction> accessor = handle(JSFunction::cast(raw_accessor));
if (accessor->shared()->IsApiFunction()) {
CallOptimization call_optimization(accessor); CallOptimization call_optimization(accessor);
// TODO(dcarney): temporary hack unless crankshaft can handle api calls. if (!call_optimization.is_simple_api_call()) return false;
if (call_optimization.is_simple_api_call()) return false; CallOptimization::HolderLookup holder_lookup;
api_holder_ = call_optimization.LookupHolderOfExpectedType(
map, &holder_lookup);
switch (holder_lookup) {
case CallOptimization::kHolderNotFound:
return false;
case CallOptimization::kHolderIsReceiver:
case CallOptimization::kHolderFound:
break;
}
}
accessor_ = accessor; accessor_ = accessor;
} else if (lookup_.IsConstant()) { } else if (lookup_.IsConstant()) {
constant_ = handle(lookup_.GetConstantFromMap(*map), isolate()); constant_ = handle(lookup_.GetConstantFromMap(*map), isolate());
@ -5572,7 +5584,7 @@ HInstruction* HOptimizedGraphBuilder::BuildMonomorphicAccess(
return New<HCallFunction>(function, argument_count, WRAP_AND_CALL); return New<HCallFunction>(function, argument_count, WRAP_AND_CALL);
} else if (FLAG_inline_accessors && can_inline_accessor) { } else if (FLAG_inline_accessors && can_inline_accessor) {
bool success = info->IsLoad() bool success = info->IsLoad()
? TryInlineGetter(info->accessor(), ast_id, return_id) ? TryInlineGetter(info->accessor(), info->map(), ast_id, return_id)
: TryInlineSetter(info->accessor(), ast_id, return_id, value); : TryInlineSetter(info->accessor(), ast_id, return_id, value);
if (success) return NULL; if (success) return NULL;
} }
@ -7394,8 +7406,10 @@ bool HOptimizedGraphBuilder::TryInlineConstruct(CallNew* expr,
bool HOptimizedGraphBuilder::TryInlineGetter(Handle<JSFunction> getter, bool HOptimizedGraphBuilder::TryInlineGetter(Handle<JSFunction> getter,
Handle<Map> receiver_map,
BailoutId ast_id, BailoutId ast_id,
BailoutId return_id) { BailoutId return_id) {
if (TryInlineApiGetter(getter, receiver_map, ast_id)) return true;
return TryInline(getter, return TryInline(getter,
0, 0,
NULL, NULL,
@ -7678,55 +7692,103 @@ bool HOptimizedGraphBuilder::TryInlineBuiltinMethodCall(
bool HOptimizedGraphBuilder::TryInlineApiFunctionCall(Call* expr, bool HOptimizedGraphBuilder::TryInlineApiFunctionCall(Call* expr,
HValue* receiver) { HValue* receiver) {
return TryInlineApiCall( Handle<JSFunction> function = expr->target();
expr, receiver, Handle<Map>::null(), true); int argc = expr->arguments()->length();
SmallMapList receiver_maps;
return TryInlineApiCall(function,
receiver,
&receiver_maps,
argc,
expr->id(),
kCallApiFunction);
} }
bool HOptimizedGraphBuilder::TryInlineApiMethodCall(Call* expr, bool HOptimizedGraphBuilder::TryInlineApiMethodCall(
Call* expr,
HValue* receiver, HValue* receiver,
Handle<Map> receiver_map) { SmallMapList* receiver_maps) {
return TryInlineApiCall(expr, receiver, receiver_map, false); Handle<JSFunction> function = expr->target();
int argc = expr->arguments()->length();
return TryInlineApiCall(function,
receiver,
receiver_maps,
argc,
expr->id(),
kCallApiMethod);
} }
bool HOptimizedGraphBuilder::TryInlineApiCall(Call* expr,
HValue* receiver, bool HOptimizedGraphBuilder::TryInlineApiGetter(Handle<JSFunction> function,
Handle<Map> receiver_map, Handle<Map> receiver_map,
bool is_function_call) { BailoutId ast_id) {
if (!expr->IsMonomorphic()) return false; SmallMapList receiver_maps(1, zone());
CallOptimization optimization(expr->target()); receiver_maps.Add(receiver_map, zone());
return TryInlineApiCall(function,
NULL, // Receiver is on expression stack.
&receiver_maps,
0,
ast_id,
kCallApiGetter);
}
bool HOptimizedGraphBuilder::TryInlineApiCall(Handle<JSFunction> function,
HValue* receiver,
SmallMapList* receiver_maps,
int argc,
BailoutId ast_id,
ApiCallType call_type) {
CallOptimization optimization(function);
if (!optimization.is_simple_api_call()) return false; if (!optimization.is_simple_api_call()) return false;
Handle<Map> holder_map; Handle<Map> holder_map;
if (is_function_call) { if (call_type == kCallApiFunction) {
// Cannot embed a direct reference to the global proxy map // Cannot embed a direct reference to the global proxy map
// as it maybe dropped on deserialization. // as it maybe dropped on deserialization.
CHECK(!Serializer::enabled()); CHECK(!Serializer::enabled());
receiver_map = Handle<Map>( ASSERT_EQ(0, receiver_maps->length());
expr->target()->context()->global_object()->global_receiver()->map()); receiver_maps->Add(handle(
function->context()->global_object()->global_receiver()->map()),
zone());
} }
CallOptimization::HolderLookup holder_lookup = CallOptimization::HolderLookup holder_lookup =
CallOptimization::kHolderNotFound; CallOptimization::kHolderNotFound;
Handle<JSObject> api_holder = optimization.LookupHolderOfExpectedType( Handle<JSObject> api_holder = optimization.LookupHolderOfExpectedType(
receiver_map, &holder_lookup); receiver_maps->first(), &holder_lookup);
if (holder_lookup == CallOptimization::kHolderNotFound) return false; if (holder_lookup == CallOptimization::kHolderNotFound) return false;
if (FLAG_trace_inlining) { if (FLAG_trace_inlining) {
PrintF("Inlining api function "); PrintF("Inlining api function ");
expr->target()->ShortPrint(); function->ShortPrint();
PrintF("\n"); PrintF("\n");
} }
const int argc = expr->arguments()->length(); bool drop_extra = false;
// Includes receiver. switch (call_type) {
PushArgumentsFromEnvironment(argc + 1); case kCallApiFunction:
case kCallApiMethod:
// Need to ensure the chain between receiver and api_holder is intact // Need to check that none of the receiver maps could have changed.
AddCheckMap(receiver, receiver_map); Add<HCheckMaps>(receiver, receiver_maps);
// Need to ensure the chain between receiver and api_holder is intact.
if (holder_lookup == CallOptimization::kHolderFound) { if (holder_lookup == CallOptimization::kHolderFound) {
AddCheckPrototypeMaps(api_holder, receiver_map); AddCheckPrototypeMaps(api_holder, receiver_maps->first());
} else { } else {
ASSERT_EQ(holder_lookup, CallOptimization::kHolderIsReceiver); ASSERT_EQ(holder_lookup, CallOptimization::kHolderIsReceiver);
} }
// Includes receiver.
PushArgumentsFromEnvironment(argc + 1);
// Drop function after call.
drop_extra = true;
break;
case kCallApiGetter:
// Receiver and prototype chain cannot have changed.
ASSERT_EQ(0, argc);
ASSERT_EQ(NULL, receiver);
// Receiver is on expression stack.
receiver = Pop();
Add<HPushArgument>(receiver);
break;
}
HValue* holder = NULL; HValue* holder = NULL;
switch (holder_lookup) { switch (holder_lookup) {
@ -7751,8 +7813,7 @@ bool HOptimizedGraphBuilder::TryInlineApiCall(Call* expr,
HValue* api_function_address = Add<HConstant>(ExternalReference(ref)); HValue* api_function_address = Add<HConstant>(ExternalReference(ref));
HValue* op_vals[] = { HValue* op_vals[] = {
// callee Add<HConstant>(function),
Add<HConstant>(expr->target()),
call_data, call_data,
holder, holder,
api_function_address, api_function_address,
@ -7773,8 +7834,8 @@ bool HOptimizedGraphBuilder::TryInlineApiCall(Call* expr,
code_value, argc + 1, descriptor, code_value, argc + 1, descriptor,
Vector<HValue*>(op_vals, descriptor->environment_length())); Vector<HValue*>(op_vals, descriptor->environment_length()));
Drop(1); // Drop function. if (drop_extra) Drop(1); // Drop function.
ast_context()->ReturnInstruction(call, expr->id()); ast_context()->ReturnInstruction(call, ast_id);
return true; return true;
} }
@ -7923,7 +7984,7 @@ void HOptimizedGraphBuilder::VisitCall(Call* expr) {
} }
return; return;
} }
if (TryInlineApiMethodCall(expr, receiver, map)) return; if (TryInlineApiMethodCall(expr, receiver, types)) return;
// Wrap the receiver if necessary. // Wrap the receiver if necessary.
if (NeedsWrappingFor(ToType(types->first()), known_function)) { if (NeedsWrappingFor(ToType(types->first()), known_function)) {

View File

@ -2218,6 +2218,7 @@ class HOptimizedGraphBuilder : public HGraphBuilder, public AstVisitor {
bool TryInlineCall(Call* expr); bool TryInlineCall(Call* expr);
bool TryInlineConstruct(CallNew* expr, HValue* implicit_return_value); bool TryInlineConstruct(CallNew* expr, HValue* implicit_return_value);
bool TryInlineGetter(Handle<JSFunction> getter, bool TryInlineGetter(Handle<JSFunction> getter,
Handle<Map> receiver_map,
BailoutId ast_id, BailoutId ast_id,
BailoutId return_id); BailoutId return_id);
bool TryInlineSetter(Handle<JSFunction> setter, bool TryInlineSetter(Handle<JSFunction> setter,
@ -2231,14 +2232,24 @@ class HOptimizedGraphBuilder : public HGraphBuilder, public AstVisitor {
HValue* receiver, HValue* receiver,
Handle<Map> receiver_map); Handle<Map> receiver_map);
bool TryInlineBuiltinFunctionCall(Call* expr); bool TryInlineBuiltinFunctionCall(Call* expr);
enum ApiCallType {
kCallApiFunction,
kCallApiMethod,
kCallApiGetter
};
bool TryInlineApiMethodCall(Call* expr, bool TryInlineApiMethodCall(Call* expr,
HValue* receiver, HValue* receiver,
Handle<Map> receiver_map); SmallMapList* receiver_types);
bool TryInlineApiFunctionCall(Call* expr, HValue* receiver); bool TryInlineApiFunctionCall(Call* expr, HValue* receiver);
bool TryInlineApiCall(Call* expr, bool TryInlineApiGetter(Handle<JSFunction> function,
HValue* receiver,
Handle<Map> receiver_map, Handle<Map> receiver_map,
bool is_function_call); BailoutId ast_id);
bool TryInlineApiCall(Handle<JSFunction> function,
HValue* receiver,
SmallMapList* receiver_maps,
int argc,
BailoutId ast_id,
ApiCallType call_type);
// If --trace-inlining, print a line of the inlining trace. Inlining // If --trace-inlining, print a line of the inlining trace. Inlining
// succeeded if the reason string is NULL and failed if there is a // succeeded if the reason string is NULL and failed if there is a
@ -2374,6 +2385,7 @@ class HOptimizedGraphBuilder : public HGraphBuilder, public AstVisitor {
Handle<String> name_; Handle<String> name_;
Handle<JSObject> holder_; Handle<JSObject> holder_;
Handle<JSFunction> accessor_; Handle<JSFunction> accessor_;
Handle<JSObject> api_holder_;
Handle<Object> constant_; Handle<Object> constant_;
Handle<Map> transition_; Handle<Map> transition_;
HObjectAccess access_; HObjectAccess access_;

View File

@ -21916,6 +21916,25 @@ class ApiCallOptimizationChecker {
count++; count++;
} }
// TODO(dcarney): move this to v8.h
static void SetAccessorProperty(Local<Object> object,
Local<String> name,
Local<Function> getter,
Local<Function> setter = Local<Function>()) {
i::Isolate* isolate = CcTest::i_isolate();
v8::AccessControl settings = v8::DEFAULT;
v8::PropertyAttribute attribute = v8::None;
i::Handle<i::Object> getter_i = v8::Utils::OpenHandle(*getter);
i::Handle<i::Object> setter_i = v8::Utils::OpenHandle(*setter, true);
if (setter_i.is_null()) setter_i = isolate->factory()->null_value();
i::JSObject::DefineAccessor(v8::Utils::OpenHandle(*object),
v8::Utils::OpenHandle(*name),
getter_i,
setter_i,
static_cast<PropertyAttributes>(attribute),
settings);
}
public: public:
void Run(bool use_signature, bool global) { void Run(bool use_signature, bool global) {
v8::Isolate* isolate = CcTest::isolate(); v8::Isolate* isolate = CcTest::isolate();
@ -21952,9 +21971,12 @@ class ApiCallOptimizationChecker {
Local<FunctionTemplate> function_template = FunctionTemplate::New( Local<FunctionTemplate> function_template = FunctionTemplate::New(
isolate, OptimizationCallback, data, signature); isolate, OptimizationCallback, data, signature);
Local<Function> function = function_template->GetFunction(); Local<Function> function = function_template->GetFunction();
Local<Object>::Cast( Local<Object> global_holder = Local<Object>::Cast(
inner_global->GetPrototype())->Set(v8_str("global_f"), function); inner_global->GetPrototype());
global_holder->Set(v8_str("g_f"), function);
SetAccessorProperty(global_holder, v8_str("g_p1"), function);
function_holder->Set(v8_str("f"), function); function_holder->Set(v8_str("f"), function);
SetAccessorProperty(function_holder, v8_str("p1"), function);
// Initialize expected values. // Initialize expected values.
callee = function; callee = function;
count = 0; count = 0;
@ -21980,33 +22002,40 @@ class ApiCallOptimizationChecker {
if (!use_signature) holder = receiver; if (!use_signature) holder = receiver;
// build wrap_function // build wrap_function
int key = (use_signature ? 1 : 0) + 2 * (global ? 1 : 0); int key = (use_signature ? 1 : 0) + 2 * (global ? 1 : 0);
i::ScopedVector<char> wrap_function(100); i::ScopedVector<char> wrap_function(200);
if (global) { if (global) {
i::OS::SNPrintF( i::OS::SNPrintF(
wrap_function, wrap_function,
"function wrap_%d() { var f = global_f; return f(); }\n", "function wrap_f_%d() { var f = g_f; return f(); }\n"
key); "function wrap_p1_%d() { return this.g_p1; }\n",
key, key);
} else { } else {
i::OS::SNPrintF( i::OS::SNPrintF(
wrap_function, wrap_function,
"function wrap_%d() { return receiver_subclass.f(); }\n", "function wrap_f_%d() { return receiver_subclass.f(); }\n"
key); "function wrap_p1_%d() { return receiver_subclass.p1; }\n",
key, key);
} }
// build source string // build source string
i::ScopedVector<char> source(500); i::ScopedVector<char> source(500);
i::OS::SNPrintF( i::OS::SNPrintF(
source, source,
"%s\n" // wrap_function "%s\n" // wrap functions
"function wrap2() { wrap_%d(); }\n" "function wrap_f() { wrap_f_%d(); }\n"
"wrap2();\n" "function wrap_p1() { wrap_p1_%d(); }\n"
"wrap2();\n" "wrap_f();\n"
"%%OptimizeFunctionOnNextCall(wrap_%d);\n" "wrap_f();\n"
"wrap2();\n", "%%OptimizeFunctionOnNextCall(wrap_f_%d);\n"
wrap_function.start(), key, key); "wrap_f();\n"
"wrap_p1();\n"
"wrap_p1();\n"
"%%OptimizeFunctionOnNextCall(wrap_p1_%d);\n"
"wrap_p1();\n",
wrap_function.start(), key, key, key, key);
v8::TryCatch try_catch; v8::TryCatch try_catch;
CompileRun(source.start()); CompileRun(source.start());
ASSERT(!try_catch.HasCaught()); ASSERT(!try_catch.HasCaught());
CHECK_EQ(3, count); CHECK_EQ(6, count);
} }
}; };