Move function prototype handling into a special handler rather than IC
authorverwaest@chromium.org <verwaest@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 22 Jul 2014 14:27:53 +0000 (14:27 +0000)
committerverwaest@chromium.org <verwaest@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 22 Jul 2014 14:27:53 +0000 (14:27 +0000)
Adjust hydrogen handling of function.prototype to be based on map feedback. Handle non-instance prototype loading using an IC rather than in the hydrogen instruction. In the future, remove the special instruction and replace by multiple hydrogen instructions.

BUG=
R=mvstanton@chromium.org

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

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@22526 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

12 files changed:
src/arm/lithium-codegen-arm.cc
src/arm64/lithium-codegen-arm64.cc
src/ast.h
src/code-stubs.h
src/hydrogen.cc
src/ia32/lithium-codegen-ia32.cc
src/ic.cc
src/stub-cache.cc
src/type-info.cc
src/type-info.h
src/typing.cc
src/x64/lithium-codegen-x64.cc

index beb3c8f8174c396948220f36f32c1a6a454f371d..188f5a7fb8a5bf94b7122c29745f958f3ead7133 100644 (file)
@@ -3143,17 +3143,6 @@ void LCodeGen::DoLoadFunctionPrototype(LLoadFunctionPrototype* instr) {
   Register function = ToRegister(instr->function());
   Register result = ToRegister(instr->result());
 
-  // Check that the function really is a function. Load map into the
-  // result register.
-  __ CompareObjectType(function, result, scratch, JS_FUNCTION_TYPE);
-  DeoptimizeIf(ne, instr->environment());
-
-  // Make sure that the function has an instance prototype.
-  Label non_instance;
-  __ ldrb(scratch, FieldMemOperand(result, Map::kBitFieldOffset));
-  __ tst(scratch, Operand(1 << Map::kHasNonInstancePrototype));
-  __ b(ne, &non_instance);
-
   // Get the prototype or initial map from the function.
   __ ldr(result,
          FieldMemOperand(function, JSFunction::kPrototypeOrInitialMapOffset));
@@ -3170,12 +3159,6 @@ void LCodeGen::DoLoadFunctionPrototype(LLoadFunctionPrototype* instr) {
 
   // Get the prototype from the initial map.
   __ ldr(result, FieldMemOperand(result, Map::kPrototypeOffset));
-  __ jmp(&done);
-
-  // Non-instance prototype: Fetch prototype from constructor field
-  // in initial map.
-  __ bind(&non_instance);
-  __ ldr(result, FieldMemOperand(result, Map::kConstructorOffset));
 
   // All done.
   __ bind(&done);
index 3f6be920af459a8b1d9aff48f7e0a2076faacf24..4725b98a508240855116ce5ad81ddf98d76c1e2d 100644 (file)
@@ -3332,16 +3332,6 @@ void LCodeGen::DoLoadFunctionPrototype(LLoadFunctionPrototype* instr) {
   Register result = ToRegister(instr->result());
   Register temp = ToRegister(instr->temp());
 
-  // Check that the function really is a function. Leaves map in the result
-  // register.
-  __ CompareObjectType(function, result, temp, JS_FUNCTION_TYPE);
-  DeoptimizeIf(ne, instr->environment());
-
-  // Make sure that the function has an instance prototype.
-  Label non_instance;
-  __ Ldrb(temp, FieldMemOperand(result, Map::kBitFieldOffset));
-  __ Tbnz(temp, Map::kHasNonInstancePrototype, &non_instance);
-
   // Get the prototype or initial map from the function.
   __ Ldr(result, FieldMemOperand(function,
                                  JSFunction::kPrototypeOrInitialMapOffset));
@@ -3357,12 +3347,6 @@ void LCodeGen::DoLoadFunctionPrototype(LLoadFunctionPrototype* instr) {
 
   // Get the prototype from the initial map.
   __ Ldr(result, FieldMemOperand(result, Map::kPrototypeOffset));
-  __ B(&done);
-
-  // Non-instance prototype: fetch prototype from constructor field in initial
-  // map.
-  __ Bind(&non_instance);
-  __ Ldr(result, FieldMemOperand(result, Map::kConstructorOffset));
 
   // All done.
   __ Bind(&done);
index 3295b375acc6f302b787724da688c595c0c9924e..ecb42c936c27c9c328ae3c794af56d685fc2312a 100644 (file)
--- a/src/ast.h
+++ b/src/ast.h
@@ -1684,7 +1684,6 @@ class Property V8_FINAL : public Expression, public FeedbackSlotInterface {
   BailoutId LoadId() const { return load_id_; }
 
   bool IsStringAccess() const { return is_string_access_; }
-  bool IsFunctionPrototype() const { return is_function_prototype_; }
 
   // Type feedback information.
   virtual bool IsMonomorphic() V8_OVERRIDE {
@@ -1702,7 +1701,6 @@ class Property V8_FINAL : public Expression, public FeedbackSlotInterface {
   }
   void set_is_uninitialized(bool b) { is_uninitialized_ = b; }
   void set_is_string_access(bool b) { is_string_access_ = b; }
-  void set_is_function_prototype(bool b) { is_function_prototype_ = b; }
   void mark_for_call() { is_for_call_ = true; }
   bool IsForCall() { return is_for_call_; }
 
@@ -1716,10 +1714,7 @@ class Property V8_FINAL : public Expression, public FeedbackSlotInterface {
   int PropertyFeedbackSlot() const { return property_feedback_slot_; }
 
  protected:
-  Property(Zone* zone,
-           Expression* obj,
-           Expression* key,
-           int pos)
+  Property(Zone* zone, Expression* obj, Expression* key, int pos)
       : Expression(zone, pos),
         obj_(obj),
         key_(key),
@@ -1727,8 +1722,7 @@ class Property V8_FINAL : public Expression, public FeedbackSlotInterface {
         property_feedback_slot_(kInvalidFeedbackSlot),
         is_for_call_(false),
         is_uninitialized_(false),
-        is_string_access_(false),
-        is_function_prototype_(false) { }
+        is_string_access_(false) {}
 
  private:
   Expression* obj_;
@@ -1740,7 +1734,6 @@ class Property V8_FINAL : public Expression, public FeedbackSlotInterface {
   bool is_for_call_ : 1;
   bool is_uninitialized_ : 1;
   bool is_string_access_ : 1;
-  bool is_function_prototype_ : 1;
 };
 
 
index 973818470d9778dfc42a5efe61063d13e26a4329..f50abda45ff23227cc6d03723b62dde3d5748cfc 100644 (file)
@@ -899,14 +899,26 @@ class CallIC_ArrayStub: public CallICStub {
 };
 
 
-class FunctionPrototypeStub: public ICStub {
+// TODO(verwaest): Translate to hydrogen code stub.
+class FunctionPrototypeStub : public PlatformCodeStub {
  public:
   FunctionPrototypeStub(Isolate* isolate, Code::Kind kind)
-      : ICStub(isolate, kind) { }
+      : PlatformCodeStub(isolate) {
+    bit_field_ = KindBits::encode(kind);
+  }
   virtual void Generate(MacroAssembler* masm);
+  virtual Code::Kind GetCodeKind() const { return Code::HANDLER; }
+  virtual InlineCacheState GetICState() { return MONOMORPHIC; }
+  virtual ExtraICState GetExtraICState() const { return kind(); }
+
+ protected:
+  class KindBits : public BitField<Code::Kind, 0, 4> {};
+  virtual Code::Kind kind() const { return KindBits::decode(bit_field_); }
 
  private:
   virtual CodeStub::Major MajorKey() const { return FunctionPrototype; }
+  virtual int MinorKey() const { return bit_field_; }
+  int bit_field_;
 };
 
 
index 0290d7c6140d2693dc254481d9571b12e6888459..b65b222991b468e2fb5658f53d6b270cf481eee3 100644 (file)
@@ -6070,6 +6070,11 @@ bool HOptimizedGraphBuilder::PropertyAccessInfo::LookupInPrototypes() {
 bool HOptimizedGraphBuilder::PropertyAccessInfo::CanAccessMonomorphic() {
   if (!CanInlinePropertyAccess(type_)) return false;
   if (IsJSObjectFieldAccessor()) return IsLoad();
+  if (this->map()->function_with_prototype() &&
+      !this->map()->has_non_instance_prototype() &&
+      name_.is_identical_to(isolate()->factory()->prototype_string())) {
+    return IsLoad();
+  }
   if (!LookupDescriptor()) return false;
   if (lookup_.IsFound()) {
     if (IsLoad()) return true;
@@ -6161,6 +6166,12 @@ HInstruction* HOptimizedGraphBuilder::BuildMonomorphicAccess(
     return New<HLoadNamedField>(object, checked_object, access);
   }
 
+  if (info->name().is_identical_to(isolate()->factory()->prototype_string()) &&
+      info->map()->function_with_prototype()) {
+    ASSERT(!info->map()->has_non_instance_prototype());
+    return New<HLoadFunctionPrototype>(checked_object);
+  }
+
   HValue* checked_holder = checked_object;
   if (info->has_holder()) {
     Handle<JSObject> prototype(JSObject::cast(info->map()->prototype()));
@@ -6575,8 +6586,7 @@ void HOptimizedGraphBuilder::HandleCompoundAssignment(Assignment* expr) {
     CHECK_ALIVE(VisitForValue(prop->obj()));
     HValue* object = Top();
     HValue* key = NULL;
-    if ((!prop->IsFunctionPrototype() && !prop->key()->IsPropertyName()) ||
-        prop->IsStringAccess()) {
+    if (!prop->key()->IsPropertyName() || prop->IsStringAccess()) {
       CHECK_ALIVE(VisitForValue(prop->key()));
       key = Top();
     }
@@ -7286,11 +7296,6 @@ void HOptimizedGraphBuilder::BuildLoad(Property* expr,
     AddInstruction(char_code);
     instr = NewUncasted<HStringCharFromCode>(char_code);
 
-  } else if (expr->IsFunctionPrototype()) {
-    HValue* function = Pop();
-    BuildCheckHeapObject(function);
-    instr = New<HLoadFunctionPrototype>(function);
-
   } else if (expr->key()->IsPropertyName()) {
     Handle<String> name = expr->key()->AsLiteral()->AsPropertyName();
     HValue* object = Pop();
@@ -7330,8 +7335,7 @@ void HOptimizedGraphBuilder::VisitProperty(Property* expr) {
   if (TryArgumentsAccess(expr)) return;
 
   CHECK_ALIVE(VisitForValue(expr->obj()));
-  if ((!expr->IsFunctionPrototype() && !expr->key()->IsPropertyName()) ||
-      expr->IsStringAccess()) {
+  if (!expr->key()->IsPropertyName() || expr->IsStringAccess()) {
     CHECK_ALIVE(VisitForValue(expr->key()));
   }
 
@@ -10025,8 +10029,7 @@ void HOptimizedGraphBuilder::VisitCountOperation(CountOperation* expr) {
   HValue* object = Top();
 
   HValue* key = NULL;
-  if ((!prop->IsFunctionPrototype() && !prop->key()->IsPropertyName()) ||
-      prop->IsStringAccess()) {
+  if (!prop->key()->IsPropertyName() || prop->IsStringAccess()) {
     CHECK_ALIVE(VisitForValue(prop->key()));
     key = Top();
   }
index 6762a6c6612abca46ac0c58303cb07a430a16720..ced3a235839893e5c542a26fe703c83c8b300ef5 100644 (file)
@@ -2998,16 +2998,6 @@ void LCodeGen::DoLoadFunctionPrototype(LLoadFunctionPrototype* instr) {
   Register temp = ToRegister(instr->temp());
   Register result = ToRegister(instr->result());
 
-  // Check that the function really is a function.
-  __ CmpObjectType(function, JS_FUNCTION_TYPE, result);
-  DeoptimizeIf(not_equal, instr->environment());
-
-  // Check whether the function has an instance prototype.
-  Label non_instance;
-  __ test_b(FieldOperand(result, Map::kBitFieldOffset),
-            1 << Map::kHasNonInstancePrototype);
-  __ j(not_zero, &non_instance, Label::kNear);
-
   // Get the prototype or initial map from the function.
   __ mov(result,
          FieldOperand(function, JSFunction::kPrototypeOrInitialMapOffset));
@@ -3023,12 +3013,6 @@ void LCodeGen::DoLoadFunctionPrototype(LLoadFunctionPrototype* instr) {
 
   // Get the prototype from the initial map.
   __ mov(result, FieldOperand(result, Map::kPrototypeOffset));
-  __ jmp(&done, Label::kNear);
-
-  // Non-instance prototype: Fetch prototype from constructor field
-  // in the function's map.
-  __ bind(&non_instance);
-  __ mov(result, FieldOperand(result, Map::kConstructorOffset));
 
   // All done.
   __ bind(&done);
index 829db7d9e65627b089b7a07034eac63603ede41b..0910ae83f5368d18b6931256e57c8c9caa9ff12f 100644 (file)
--- a/src/ic.cc
+++ b/src/ic.cc
@@ -558,31 +558,6 @@ MaybeHandle<Object> LoadIC::Load(Handle<Object> object, Handle<String> name) {
     return TypeError("non_object_property_load", object, name);
   }
 
-  if (FLAG_use_ic) {
-    // Use specialized code for getting prototype of functions.
-    if (object->IsJSFunction() &&
-        String::Equals(isolate()->factory()->prototype_string(), name) &&
-        Handle<JSFunction>::cast(object)->should_have_prototype()) {
-      Handle<Code> stub;
-      if (state() == UNINITIALIZED) {
-        stub = pre_monomorphic_stub();
-      } else if (state() == PREMONOMORPHIC) {
-        FunctionPrototypeStub function_prototype_stub(isolate(), kind());
-        stub = function_prototype_stub.GetCode();
-      } else if (!FLAG_compiled_keyed_generic_loads && state() != MEGAMORPHIC) {
-        ASSERT(state() != GENERIC);
-        stub = megamorphic_stub();
-      } else if (FLAG_compiled_keyed_generic_loads && state() != GENERIC) {
-        stub = generic_stub();
-      }
-      if (!stub.is_null()) {
-        set_target(*stub);
-        if (FLAG_trace_ic) PrintF("[LoadIC : +#prototype /function]\n");
-      }
-      return Accessors::FunctionGetPrototype(Handle<JSFunction>::cast(object));
-    }
-  }
-
   // Check if the name is trivially convertible to an index and get
   // the element or char if so.
   uint32_t index;
@@ -959,6 +934,15 @@ Handle<Code> LoadIC::CompileHandler(LookupResult* lookup, Handle<Object> object,
     }
   }
 
+  // Use specialized code for getting prototype of functions.
+  if (object->IsJSFunction() &&
+      String::Equals(isolate()->factory()->prototype_string(), name) &&
+      Handle<JSFunction>::cast(object)->should_have_prototype()) {
+    Handle<Code> stub;
+    FunctionPrototypeStub function_prototype_stub(isolate(), kind());
+    return function_prototype_stub.GetCode();
+  }
+
   Handle<HeapType> type = receiver_type();
   Handle<JSObject> holder(lookup->holder());
   bool receiver_is_holder = object.is_identical_to(holder);
index 40c26bf99b2edcf5da1e5cb5fc5152ccd1d55aae..285a51351004d97556ea023cfb5cdd9701da92b7 100644 (file)
@@ -42,11 +42,10 @@ static Code::Flags CommonStubCacheChecks(Name* name, Map* map,
   ASSERT(!heap->InNewSpace(name));
   ASSERT(name->IsUniqueName());
 
-  // The state bits are not important to the hash function because
-  // the stub cache only contains monomorphic stubs. Make sure that
-  // the bits are the least significant so they will be the ones
-  // masked out.
-  ASSERT(Code::ExtractICStateFromFlags(flags) == MONOMORPHIC);
+  // The state bits are not important to the hash function because the stub
+  // cache only contains handlers. Make sure that the bits are the least
+  // significant so they will be the ones masked out.
+  ASSERT_EQ(Code::HANDLER, Code::ExtractKindFromFlags(flags));
   STATIC_ASSERT((Code::ICStateField::kMask & 1) == 1);
 
   // Make sure that the code type and cache holder are not included in the hash.
index d7041ee3876f3a0458a9ae2ac45f610a4cc064fb..0771caef93acd18bebeee277830d990e346676e3 100644 (file)
@@ -175,16 +175,6 @@ bool TypeFeedbackOracle::LoadIsBuiltin(
 }
 
 
-bool TypeFeedbackOracle::LoadIsStub(TypeFeedbackId id, ICStub* stub) {
-  Handle<Object> object = GetInfo(id);
-  if (!object->IsCode()) return false;
-  Handle<Code> code = Handle<Code>::cast(object);
-  if (!code->is_load_stub()) return false;
-  if (code->ic_state() != MONOMORPHIC) return false;
-  return stub->Describes(*code);
-}
-
-
 void TypeFeedbackOracle::CompareType(TypeFeedbackId id,
                                      Type** left_type,
                                      Type** right_type,
@@ -264,16 +254,12 @@ Type* TypeFeedbackOracle::CountType(TypeFeedbackId id) {
 }
 
 
-void TypeFeedbackOracle::PropertyReceiverTypes(
-    TypeFeedbackId id, Handle<String> name,
-    SmallMapList* receiver_types, bool* is_prototype) {
+void TypeFeedbackOracle::PropertyReceiverTypes(TypeFeedbackId id,
+                                               Handle<String> name,
+                                               SmallMapList* receiver_types) {
   receiver_types->Clear();
-  FunctionPrototypeStub proto_stub(isolate(), Code::LOAD_IC);
-  *is_prototype = LoadIsStub(id, &proto_stub);
-  if (!*is_prototype) {
-    Code::Flags flags = Code::ComputeHandlerFlags(Code::LOAD_IC);
-    CollectReceiverTypes(id, name, flags, receiver_types);
-  }
+  Code::Flags flags = Code::ComputeHandlerFlags(Code::LOAD_IC);
+  CollectReceiverTypes(id, name, flags, receiver_types);
 }
 
 
index 706921adb08d8ec8fbc84237033db974b6562567..61dcf1c7a5f55c343fe9d5a5b7fe9e6c4fb82dd5 100644 (file)
@@ -41,10 +41,8 @@ class TypeFeedbackOracle: public ZoneObject {
 
   KeyedAccessStoreMode GetStoreMode(TypeFeedbackId id);
 
-  void PropertyReceiverTypes(TypeFeedbackId id,
-                             Handle<String> name,
-                             SmallMapList* receiver_types,
-                             bool* is_prototype);
+  void PropertyReceiverTypes(TypeFeedbackId id, Handle<String> name,
+                             SmallMapList* receiver_types);
   void KeyedPropertyReceiverTypes(TypeFeedbackId id,
                                   SmallMapList* receiver_types,
                                   bool* is_string);
@@ -70,7 +68,6 @@ class TypeFeedbackOracle: public ZoneObject {
   Handle<AllocationSite> GetCallNewAllocationSite(int slot);
 
   bool LoadIsBuiltin(TypeFeedbackId id, Builtins::Name builtin_id);
-  bool LoadIsStub(TypeFeedbackId id, ICStub* stub);
 
   // TODO(1571) We can't use ToBooleanStub::Types as the return value because
   // of various cycles in our headers. Death to tons of implementations in
index 98e6dc3e7841b0d573a825e0e54777c487729e4e..84dbe815e1fed66fe0e538d94310080d3fd33a19 100644 (file)
@@ -485,10 +485,7 @@ void AstTyper::VisitProperty(Property* expr) {
       Literal* lit_key = expr->key()->AsLiteral();
       ASSERT(lit_key != NULL && lit_key->value()->IsString());
       Handle<String> name = Handle<String>::cast(lit_key->value());
-      bool is_prototype;
-      oracle()->PropertyReceiverTypes(
-          id, name, expr->GetReceiverTypes(), &is_prototype);
-      expr->set_is_function_prototype(is_prototype);
+      oracle()->PropertyReceiverTypes(id, name, expr->GetReceiverTypes());
     } else {
       bool is_string;
       oracle()->KeyedPropertyReceiverTypes(
index 18b230539b1afc193841f3c9fdb588b77f6d6352..7bc61c8d6493132fffad41a40b28bb081113a198 100644 (file)
@@ -3018,16 +3018,6 @@ void LCodeGen::DoLoadFunctionPrototype(LLoadFunctionPrototype* instr) {
   Register function = ToRegister(instr->function());
   Register result = ToRegister(instr->result());
 
-  // Check that the function really is a function.
-  __ CmpObjectType(function, JS_FUNCTION_TYPE, result);
-  DeoptimizeIf(not_equal, instr->environment());
-
-  // Check whether the function has an instance prototype.
-  Label non_instance;
-  __ testb(FieldOperand(result, Map::kBitFieldOffset),
-           Immediate(1 << Map::kHasNonInstancePrototype));
-  __ j(not_zero, &non_instance, Label::kNear);
-
   // Get the prototype or initial map from the function.
   __ movp(result,
          FieldOperand(function, JSFunction::kPrototypeOrInitialMapOffset));
@@ -3043,12 +3033,6 @@ void LCodeGen::DoLoadFunctionPrototype(LLoadFunctionPrototype* instr) {
 
   // Get the prototype from the initial map.
   __ movp(result, FieldOperand(result, Map::kPrototypeOffset));
-  __ jmp(&done, Label::kNear);
-
-  // Non-instance prototype: Fetch prototype from constructor field
-  // in the function's map.
-  __ bind(&non_instance);
-  __ movp(result, FieldOperand(result, Map::kConstructorOffset));
 
   // All done.
   __ bind(&done);