From 5028ebaa8b816141c4aed5c04513838b0187b1f1 Mon Sep 17 00:00:00 2001 From: "dcarney@chromium.org" Date: Wed, 5 Feb 2014 15:52:31 +0000 Subject: [PATCH] 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 --- src/hydrogen.cc | 139 ++++++++++++++++++++++++++++++++++-------------- src/hydrogen.h | 22 ++++++-- test/cctest/test-api.cc | 59 ++++++++++++++------ 3 files changed, 161 insertions(+), 59 deletions(-) diff --git a/src/hydrogen.cc b/src/hydrogen.cc index 4b4a158..8caeab7 100644 --- a/src/hydrogen.cc +++ b/src/hydrogen.cc @@ -5361,7 +5361,8 @@ bool HOptimizedGraphBuilder::PropertyAccessInfo::IsCompatible( if (info->has_holder()) return false; 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()) { @@ -5407,9 +5408,20 @@ bool HOptimizedGraphBuilder::PropertyAccessInfo::LoadResult(Handle map) { : Handle::cast(callback)->setter(); if (!raw_accessor->IsJSFunction()) return false; Handle accessor = handle(JSFunction::cast(raw_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 (accessor->shared()->IsApiFunction()) { + CallOptimization call_optimization(accessor); + 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; } else if (lookup_.IsConstant()) { constant_ = handle(lookup_.GetConstantFromMap(*map), isolate()); @@ -5572,7 +5584,7 @@ HInstruction* HOptimizedGraphBuilder::BuildMonomorphicAccess( return New(function, argument_count, WRAP_AND_CALL); } else if (FLAG_inline_accessors && can_inline_accessor) { 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); if (success) return NULL; } @@ -7394,8 +7406,10 @@ bool HOptimizedGraphBuilder::TryInlineConstruct(CallNew* expr, bool HOptimizedGraphBuilder::TryInlineGetter(Handle getter, + Handle receiver_map, BailoutId ast_id, BailoutId return_id) { + if (TryInlineApiGetter(getter, receiver_map, ast_id)) return true; return TryInline(getter, 0, NULL, @@ -7678,54 +7692,102 @@ bool HOptimizedGraphBuilder::TryInlineBuiltinMethodCall( bool HOptimizedGraphBuilder::TryInlineApiFunctionCall(Call* expr, HValue* receiver) { - return TryInlineApiCall( - expr, receiver, Handle::null(), true); + Handle function = expr->target(); + int argc = expr->arguments()->length(); + SmallMapList receiver_maps; + return TryInlineApiCall(function, + receiver, + &receiver_maps, + argc, + expr->id(), + kCallApiFunction); } -bool HOptimizedGraphBuilder::TryInlineApiMethodCall(Call* expr, - HValue* receiver, - Handle receiver_map) { - return TryInlineApiCall(expr, receiver, receiver_map, false); -} - -bool HOptimizedGraphBuilder::TryInlineApiCall(Call* expr, - HValue* receiver, - Handle receiver_map, - bool is_function_call) { - if (!expr->IsMonomorphic()) return false; - CallOptimization optimization(expr->target()); +bool HOptimizedGraphBuilder::TryInlineApiMethodCall( + Call* expr, + HValue* receiver, + SmallMapList* receiver_maps) { + Handle function = expr->target(); + int argc = expr->arguments()->length(); + return TryInlineApiCall(function, + receiver, + receiver_maps, + argc, + expr->id(), + kCallApiMethod); +} + + +bool HOptimizedGraphBuilder::TryInlineApiGetter(Handle function, + Handle receiver_map, + BailoutId ast_id) { + SmallMapList receiver_maps(1, zone()); + 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 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; Handle holder_map; - if (is_function_call) { + if (call_type == kCallApiFunction) { // Cannot embed a direct reference to the global proxy map // as it maybe dropped on deserialization. CHECK(!Serializer::enabled()); - receiver_map = Handle( - expr->target()->context()->global_object()->global_receiver()->map()); + ASSERT_EQ(0, receiver_maps->length()); + receiver_maps->Add(handle( + function->context()->global_object()->global_receiver()->map()), + zone()); } CallOptimization::HolderLookup holder_lookup = CallOptimization::kHolderNotFound; Handle api_holder = optimization.LookupHolderOfExpectedType( - receiver_map, &holder_lookup); + receiver_maps->first(), &holder_lookup); if (holder_lookup == CallOptimization::kHolderNotFound) return false; if (FLAG_trace_inlining) { PrintF("Inlining api function "); - expr->target()->ShortPrint(); + function->ShortPrint(); PrintF("\n"); } - const int argc = expr->arguments()->length(); - // Includes receiver. - PushArgumentsFromEnvironment(argc + 1); - - // Need to ensure the chain between receiver and api_holder is intact - AddCheckMap(receiver, receiver_map); - if (holder_lookup == CallOptimization::kHolderFound) { - AddCheckPrototypeMaps(api_holder, receiver_map); - } else { - ASSERT_EQ(holder_lookup, CallOptimization::kHolderIsReceiver); + bool drop_extra = false; + switch (call_type) { + case kCallApiFunction: + case kCallApiMethod: + // Need to check that none of the receiver maps could have changed. + Add(receiver, receiver_maps); + // Need to ensure the chain between receiver and api_holder is intact. + if (holder_lookup == CallOptimization::kHolderFound) { + AddCheckPrototypeMaps(api_holder, receiver_maps->first()); + } else { + 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(receiver); + break; } HValue* holder = NULL; @@ -7751,8 +7813,7 @@ bool HOptimizedGraphBuilder::TryInlineApiCall(Call* expr, HValue* api_function_address = Add(ExternalReference(ref)); HValue* op_vals[] = { - // callee - Add(expr->target()), + Add(function), call_data, holder, api_function_address, @@ -7773,8 +7834,8 @@ bool HOptimizedGraphBuilder::TryInlineApiCall(Call* expr, code_value, argc + 1, descriptor, Vector(op_vals, descriptor->environment_length())); - Drop(1); // Drop function. - ast_context()->ReturnInstruction(call, expr->id()); + if (drop_extra) Drop(1); // Drop function. + ast_context()->ReturnInstruction(call, ast_id); return true; } @@ -7923,7 +7984,7 @@ void HOptimizedGraphBuilder::VisitCall(Call* expr) { } return; } - if (TryInlineApiMethodCall(expr, receiver, map)) return; + if (TryInlineApiMethodCall(expr, receiver, types)) return; // Wrap the receiver if necessary. if (NeedsWrappingFor(ToType(types->first()), known_function)) { diff --git a/src/hydrogen.h b/src/hydrogen.h index ba57e43..aa2e171 100644 --- a/src/hydrogen.h +++ b/src/hydrogen.h @@ -2218,6 +2218,7 @@ class HOptimizedGraphBuilder : public HGraphBuilder, public AstVisitor { bool TryInlineCall(Call* expr); bool TryInlineConstruct(CallNew* expr, HValue* implicit_return_value); bool TryInlineGetter(Handle getter, + Handle receiver_map, BailoutId ast_id, BailoutId return_id); bool TryInlineSetter(Handle setter, @@ -2231,14 +2232,24 @@ class HOptimizedGraphBuilder : public HGraphBuilder, public AstVisitor { HValue* receiver, Handle receiver_map); bool TryInlineBuiltinFunctionCall(Call* expr); + enum ApiCallType { + kCallApiFunction, + kCallApiMethod, + kCallApiGetter + }; bool TryInlineApiMethodCall(Call* expr, HValue* receiver, - Handle receiver_map); + SmallMapList* receiver_types); bool TryInlineApiFunctionCall(Call* expr, HValue* receiver); - bool TryInlineApiCall(Call* expr, - HValue* receiver, - Handle receiver_map, - bool is_function_call); + bool TryInlineApiGetter(Handle function, + Handle receiver_map, + BailoutId ast_id); + bool TryInlineApiCall(Handle 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 // succeeded if the reason string is NULL and failed if there is a @@ -2374,6 +2385,7 @@ class HOptimizedGraphBuilder : public HGraphBuilder, public AstVisitor { Handle name_; Handle holder_; Handle accessor_; + Handle api_holder_; Handle constant_; Handle transition_; HObjectAccess access_; diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc index c49aea7..31fad53 100644 --- a/test/cctest/test-api.cc +++ b/test/cctest/test-api.cc @@ -21916,6 +21916,25 @@ class ApiCallOptimizationChecker { count++; } + // TODO(dcarney): move this to v8.h + static void SetAccessorProperty(Local object, + Local name, + Local getter, + Local setter = Local()) { + i::Isolate* isolate = CcTest::i_isolate(); + v8::AccessControl settings = v8::DEFAULT; + v8::PropertyAttribute attribute = v8::None; + i::Handle getter_i = v8::Utils::OpenHandle(*getter); + i::Handle 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(attribute), + settings); + } + public: void Run(bool use_signature, bool global) { v8::Isolate* isolate = CcTest::isolate(); @@ -21952,9 +21971,12 @@ class ApiCallOptimizationChecker { Local function_template = FunctionTemplate::New( isolate, OptimizationCallback, data, signature); Local function = function_template->GetFunction(); - Local::Cast( - inner_global->GetPrototype())->Set(v8_str("global_f"), function); + Local global_holder = Local::Cast( + 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); + SetAccessorProperty(function_holder, v8_str("p1"), function); // Initialize expected values. callee = function; count = 0; @@ -21980,33 +22002,40 @@ class ApiCallOptimizationChecker { if (!use_signature) holder = receiver; // build wrap_function int key = (use_signature ? 1 : 0) + 2 * (global ? 1 : 0); - i::ScopedVector wrap_function(100); + i::ScopedVector wrap_function(200); if (global) { i::OS::SNPrintF( wrap_function, - "function wrap_%d() { var f = global_f; return f(); }\n", - key); + "function wrap_f_%d() { var f = g_f; return f(); }\n" + "function wrap_p1_%d() { return this.g_p1; }\n", + key, key); } else { i::OS::SNPrintF( wrap_function, - "function wrap_%d() { return receiver_subclass.f(); }\n", - key); + "function wrap_f_%d() { return receiver_subclass.f(); }\n" + "function wrap_p1_%d() { return receiver_subclass.p1; }\n", + key, key); } // build source string i::ScopedVector source(500); i::OS::SNPrintF( source, - "%s\n" // wrap_function - "function wrap2() { wrap_%d(); }\n" - "wrap2();\n" - "wrap2();\n" - "%%OptimizeFunctionOnNextCall(wrap_%d);\n" - "wrap2();\n", - wrap_function.start(), key, key); + "%s\n" // wrap functions + "function wrap_f() { wrap_f_%d(); }\n" + "function wrap_p1() { wrap_p1_%d(); }\n" + "wrap_f();\n" + "wrap_f();\n" + "%%OptimizeFunctionOnNextCall(wrap_f_%d);\n" + "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; CompileRun(source.start()); ASSERT(!try_catch.HasCaught()); - CHECK_EQ(3, count); + CHECK_EQ(6, count); } }; -- 2.7.4