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:
parent
5949fe1679
commit
5028ebaa8b
123
src/hydrogen.cc
123
src/hydrogen.cc
@ -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)) {
|
||||||
|
@ -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_;
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user