inline api getters in crankshaft
authordcarney@chromium.org <dcarney@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Wed, 5 Feb 2014 15:52:31 +0000 (15:52 +0000)
committerdcarney@chromium.org <dcarney@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Wed, 5 Feb 2014 15:52:31 +0000 (15:52 +0000)
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
src/hydrogen.h
test/cctest/test-api.cc

index 4b4a158..8caeab7 100644 (file)
@@ -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> map) {
         : Handle<AccessorPair>::cast(callback)->setter();
     if (!raw_accessor->IsJSFunction()) return false;
     Handle<JSFunction> 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<HCallFunction>(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<JSFunction> getter,
+                                             Handle<Map> 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<Map>::null(), true);
+  Handle<JSFunction> 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<Map> receiver_map) {
-  return TryInlineApiCall(expr, receiver, receiver_map, false);
-}
-
-bool HOptimizedGraphBuilder::TryInlineApiCall(Call* expr,
-                                              HValue* receiver,
-                                              Handle<Map> 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<JSFunction> function = expr->target();
+  int argc = expr->arguments()->length();
+  return TryInlineApiCall(function,
+                          receiver,
+                          receiver_maps,
+                          argc,
+                          expr->id(),
+                          kCallApiMethod);
+}
+
+
+bool HOptimizedGraphBuilder::TryInlineApiGetter(Handle<JSFunction> function,
+                                                Handle<Map> 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<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;
   Handle<Map> 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<Map>(
-        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<JSObject> 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<HCheckMaps>(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<HPushArgument>(receiver);
+      break;
   }
 
   HValue* holder = NULL;
@@ -7751,8 +7813,7 @@ bool HOptimizedGraphBuilder::TryInlineApiCall(Call* expr,
   HValue* api_function_address = Add<HConstant>(ExternalReference(ref));
 
   HValue* op_vals[] = {
-    // callee
-    Add<HConstant>(expr->target()),
+    Add<HConstant>(function),
     call_data,
     holder,
     api_function_address,
@@ -7773,8 +7834,8 @@ bool HOptimizedGraphBuilder::TryInlineApiCall(Call* expr,
       code_value, argc + 1, descriptor,
       Vector<HValue*>(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)) {
index ba57e43..aa2e171 100644 (file)
@@ -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<JSFunction> getter,
+                       Handle<Map> receiver_map,
                        BailoutId ast_id,
                        BailoutId return_id);
   bool TryInlineSetter(Handle<JSFunction> setter,
@@ -2231,14 +2232,24 @@ class HOptimizedGraphBuilder : public HGraphBuilder, public AstVisitor {
                                   HValue* receiver,
                                   Handle<Map> receiver_map);
   bool TryInlineBuiltinFunctionCall(Call* expr);
+  enum ApiCallType {
+    kCallApiFunction,
+    kCallApiMethod,
+    kCallApiGetter
+  };
   bool TryInlineApiMethodCall(Call* expr,
                               HValue* receiver,
-                              Handle<Map> receiver_map);
+                              SmallMapList* receiver_types);
   bool TryInlineApiFunctionCall(Call* expr, HValue* receiver);
-  bool TryInlineApiCall(Call* expr,
-                        HValue* receiver,
-                        Handle<Map> receiver_map,
-                        bool is_function_call);
+  bool TryInlineApiGetter(Handle<JSFunction> function,
+                          Handle<Map> receiver_map,
+                          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
   // 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<JSObject> holder_;
     Handle<JSFunction> accessor_;
+    Handle<JSObject> api_holder_;
     Handle<Object> constant_;
     Handle<Map> transition_;
     HObjectAccess access_;
index c49aea7..31fad53 100644 (file)
@@ -21916,6 +21916,25 @@ class ApiCallOptimizationChecker {
     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:
     void Run(bool use_signature, bool global) {
       v8::Isolate* isolate = CcTest::isolate();
@@ -21952,9 +21971,12 @@ class ApiCallOptimizationChecker {
       Local<FunctionTemplate> function_template = FunctionTemplate::New(
           isolate, OptimizationCallback, data, signature);
       Local<Function> function = function_template->GetFunction();
-      Local<Object>::Cast(
-          inner_global->GetPrototype())->Set(v8_str("global_f"), function);
+      Local<Object> global_holder = Local<Object>::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<char> wrap_function(100);
+      i::ScopedVector<char> 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<char> 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);
     }
 };