Add monomorphic CompareNilICs and Crankshaft support
authordanno@chromium.org <danno@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Wed, 24 Apr 2013 11:32:17 +0000 (11:32 +0000)
committerdanno@chromium.org <danno@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Wed, 24 Apr 2013 11:32:17 +0000 (11:32 +0000)
R=mvstanton@chromium.org

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

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

28 files changed:
src/arm/code-stubs-arm.cc
src/arm/full-codegen-arm.cc
src/ast.cc
src/ast.h
src/code-stubs-hydrogen.cc
src/code-stubs.cc
src/code-stubs.h
src/compiler.cc
src/hydrogen-instructions.h
src/hydrogen.cc
src/hydrogen.h
src/ia32/code-stubs-ia32.cc
src/ia32/full-codegen-ia32.cc
src/ic.cc
src/ic.h
src/isolate.cc
src/lithium.cc
src/log.cc
src/objects-inl.h
src/objects.cc
src/objects.h
src/spaces.cc
src/stub-cache.cc
src/stub-cache.h
src/type-info.cc
src/type-info.h
src/x64/code-stubs-x64.cc
src/x64/full-codegen-x64.cc

index 1f69dbe0f5d778a848e09414ffcdb8435c4b1f65..7a7ec092549ae7a908dc069aa1fecfc69a49ff39 100644 (file)
@@ -96,6 +96,19 @@ void TransitionElementsKindStub::InitializeInterfaceDescriptor(
 }
 
 
+void CompareNilICStub::InitializeInterfaceDescriptor(
+    Isolate* isolate,
+    CodeStubInterfaceDescriptor* descriptor) {
+  static Register registers[] = { r0 };
+  descriptor->register_param_count_ = 1;
+  descriptor->register_params_ = registers;
+  descriptor->deoptimization_handler_ =
+      FUNCTION_ADDR(CompareNilIC_Miss);
+  descriptor->miss_handler_ =
+      ExternalReference(IC_Utility(IC::kCompareNilIC_Miss), isolate);
+}
+
+
 static void InitializeArrayConstructorDescriptor(Isolate* isolate,
     CodeStubInterfaceDescriptor* descriptor) {
   // register state
index 7ecc2b3df9493f5549608eb7060e7c5957c0a576..285490b6a6ab785ff5038b77d8fdc636fafac43d 100644 (file)
@@ -4437,28 +4437,22 @@ void FullCodeGenerator::EmitLiteralCompareNil(CompareOperation* expr,
 
   VisitForAccumulatorValue(sub_expr);
   PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
-  Heap::RootListIndex nil_value = nil == kNullValue ?
-      Heap::kNullValueRootIndex :
-      Heap::kUndefinedValueRootIndex;
-  __ LoadRoot(r1, nil_value);
-  __ cmp(r0, r1);
-  if (expr->op() == Token::EQ_STRICT) {
-    Split(eq, if_true, if_false, fall_through);
-  } else {
-    Heap::RootListIndex other_nil_value = nil == kNullValue ?
-        Heap::kUndefinedValueRootIndex :
-        Heap::kNullValueRootIndex;
-    __ b(eq, if_true);
-    __ LoadRoot(r1, other_nil_value);
+  EqualityKind kind = expr->op() == Token::EQ_STRICT
+      ? kStrictEquality : kNonStrictEquality;
+  if (kind == kStrictEquality) {
+    Heap::RootListIndex nil_value = nil == kNullValue ?
+        Heap::kNullValueRootIndex :
+        Heap::kUndefinedValueRootIndex;
+    __ LoadRoot(r1, nil_value);
     __ cmp(r0, r1);
-    __ b(eq, if_true);
-    __ JumpIfSmi(r0, if_false);
-    // It can be an undetectable object.
-    __ ldr(r1, FieldMemOperand(r0, HeapObject::kMapOffset));
-    __ ldrb(r1, FieldMemOperand(r1, Map::kBitFieldOffset));
-    __ and_(r1, r1, Operand(1 << Map::kIsUndetectable));
-    __ cmp(r1, Operand(1 << Map::kIsUndetectable));
     Split(eq, if_true, if_false, fall_through);
+  } else {
+    Handle<Code> ic = CompareNilICStub::GetUninitialized(isolate(),
+                                                         kNonStrictEquality,
+                                                         nil);
+    CallIC(ic, RelocInfo::CODE_TARGET, expr->CompareOperationFeedbackId());
+    __ cmp(r0, Operand(0));
+    Split(ne, if_true, if_false, fall_through);
   }
   context()->Plug(if_true, if_false);
 }
index d6af89b38c0fd2342a267752bab58ecc9877c0da..d241355fc1b8bf4aa654f790db2cc8bcd2f9efb5 100644 (file)
@@ -70,6 +70,11 @@ bool Expression::IsNullLiteral() {
 }
 
 
+bool Expression::IsUndefinedLiteral() {
+  return AsLiteral() != NULL && AsLiteral()->handle()->IsUndefined();
+}
+
+
 VariableProxy::VariableProxy(Isolate* isolate, Variable* var)
     : Expression(isolate),
       name_(var->name()),
@@ -352,7 +357,8 @@ static bool IsVoidOfLiteral(Expression* expr) {
 }
 
 
-// Check for the pattern: void <literal> equals <expression>
+// Check for the pattern: void <literal> equals <expression> or
+// undefined equals <expression>
 static bool MatchLiteralCompareUndefined(Expression* left,
                                          Token::Value op,
                                          Expression* right,
@@ -361,6 +367,10 @@ static bool MatchLiteralCompareUndefined(Expression* left,
     *expr = right;
     return true;
   }
+  if (left->IsUndefinedLiteral() && Token::IsEqualityOp(op)) {
+    *expr = right;
+    return true;
+  }
   return false;
 }
 
index 594b78008cceb472d831dad32270b4f5e94fc99e..2d5c200c0257d535b41e7374bb9f7a099798d75b 100644 (file)
--- a/src/ast.h
+++ b/src/ast.h
@@ -339,6 +339,9 @@ class Expression: public AstNode {
   // True iff the expression is the null literal.
   bool IsNullLiteral();
 
+  // True iff the expression is the undefined literal.
+  bool IsUndefinedLiteral();
+
   // Type feedback information for assignments and properties.
   virtual bool IsMonomorphic() {
     UNREACHABLE();
index ee93a743379b5225aebd2d9ea6a480e5dec312d9..78673d68cc218fefba2bc09a6c74ae92f724bd9c 100644 (file)
@@ -160,10 +160,12 @@ bool CodeStubGraphBuilderBase::BuildGraph() {
     stack_pop_count->ClearFlag(HValue::kCanOverflow);
   }
 
-  HReturn* hreturn_instruction = new(zone) HReturn(return_value,
-                                                   context_,
-                                                   stack_pop_count);
-  current_block()->Finish(hreturn_instruction);
+  if (!current_block()->IsFinished()) {
+    HReturn* hreturn_instruction = new(zone) HReturn(return_value,
+                                                     context_,
+                                                     stack_pop_count);
+    current_block()->Finish(hreturn_instruction);
+  }
   return true;
 }
 
@@ -517,4 +519,30 @@ Handle<Code> ArrayNArgumentsConstructorStub::GenerateCode() {
   return DoGenerateCode(this);
 }
 
+
+template <>
+HValue* CodeStubGraphBuilder<CompareNilICStub>::BuildCodeUninitializedStub() {
+  CompareNilICStub* stub = casted_stub();
+  HIfContinuation continuation;
+  Handle<Map> sentinel_map(graph()->isolate()->heap()->meta_map());
+  BuildCompareNil(GetParameter(0), stub->GetKind(),
+                  stub->GetTypes(), sentinel_map,
+                  RelocInfo::kNoPosition, &continuation);
+  IfBuilder if_nil(this, &continuation);
+  if_nil.Then();
+  if (continuation.IsFalseReachable()) {
+    if_nil.Else();
+    if_nil.Return(graph()->GetConstantSmi0());
+  }
+  if_nil.End();
+  return continuation.IsTrueReachable()
+      ? graph()->GetConstantSmi1()
+      : graph()->GetConstantUndefined();
+}
+
+
+Handle<Code> CompareNilICStub::GenerateCode() {
+  return DoGenerateCode(this);
+}
+
 } }  // namespace v8::internal
index 3a4243dc5b853a8ade75d315d346634fe4af9dab..1a2716360e543f645176b4ef7da97af0afc20770 100644 (file)
@@ -407,6 +407,42 @@ void ICCompareStub::Generate(MacroAssembler* masm) {
 }
 
 
+CompareNilICStub::Types CompareNilICStub::GetPatchedICFlags(
+    Code::ExtraICState extra_ic_state,
+    Handle<Object> object,
+    bool* already_monomorphic) {
+  Types types = TypesField::decode(extra_ic_state);
+  NilValue nil = NilValueField::decode(extra_ic_state);
+  EqualityKind kind = EqualityKindField::decode(extra_ic_state);
+  ASSERT(types != CompareNilICStub::kFullCompare);
+  *already_monomorphic =
+      (types & CompareNilICStub::kCompareAgainstMonomorphicMap) != 0;
+  if (kind == kStrictEquality) {
+    if (nil == kNullValue) {
+      return CompareNilICStub::kCompareAgainstNull;
+    } else {
+      return CompareNilICStub::kCompareAgainstUndefined;
+    }
+  } else {
+    if (object->IsNull()) {
+      types = static_cast<CompareNilICStub::Types>(
+          types | CompareNilICStub::kCompareAgainstNull);
+    } else if (object->IsUndefined()) {
+      types = static_cast<CompareNilICStub::Types>(
+          types | CompareNilICStub::kCompareAgainstUndefined);
+    } else if (object->IsUndetectableObject() || !object->IsHeapObject()) {
+        types = CompareNilICStub::kFullCompare;
+    } else if ((types & CompareNilICStub::kCompareAgainstMonomorphicMap) != 0) {
+      types = CompareNilICStub::kFullCompare;
+    } else {
+      types = static_cast<CompareNilICStub::Types>(
+          types | CompareNilICStub::kCompareAgainstMonomorphicMap);
+    }
+  }
+  return types;
+}
+
+
 void InstanceofStub::PrintName(StringStream* stream) {
   const char* args = "";
   if (HasArgsInRegisters()) {
index 55d7e5d809d12227531bded89766b17576d1e3e4..fa609240e0850ad1b427e992c101330e90c00585 100644 (file)
@@ -47,6 +47,7 @@ namespace internal {
   V(StringCompare)                       \
   V(Compare)                             \
   V(CompareIC)                           \
+  V(CompareNilIC)                        \
   V(MathPow)                             \
   V(StringLength)                        \
   V(FunctionPrototype)                   \
@@ -946,6 +947,102 @@ class ICCompareStub: public PlatformCodeStub {
 };
 
 
+class CompareNilICStub : public HydrogenCodeStub  {
+ public:
+  enum Types {
+    kCompareAgainstNull = 1 << 0,
+    kCompareAgainstUndefined = 1 << 1,
+    kCompareAgainstMonomorphicMap = 1 << 2,
+    kCompareAgainstUndetectable = 1 << 3,
+    kFullCompare = kCompareAgainstNull | kCompareAgainstUndefined |
+        kCompareAgainstUndetectable
+  };
+
+  CompareNilICStub(EqualityKind kind, NilValue nil, Types types)
+      : HydrogenCodeStub(CODE_STUB_IS_NOT_MISS), bit_field_(0) {
+    bit_field_ = EqualityKindField::encode(kind) |
+        NilValueField::encode(nil) |
+        TypesField::encode(types);
+  }
+
+  virtual InlineCacheState GetICState() {
+    Types types = GetTypes();
+    if (types == kFullCompare) {
+      return MEGAMORPHIC;
+    } else if ((types & kCompareAgainstMonomorphicMap) != 0) {
+      return MONOMORPHIC;
+    } else {
+      return PREMONOMORPHIC;
+    }
+  }
+
+  virtual Code::Kind GetCodeKind() const { return Code::COMPARE_NIL_IC; }
+
+  Handle<Code> GenerateCode();
+
+  static Handle<Code> GetUninitialized(Isolate* isolate,
+                                       EqualityKind kind,
+                                       NilValue nil) {
+    return CompareNilICStub(kind, nil).GetCode(isolate);
+  }
+
+  virtual void InitializeInterfaceDescriptor(
+      Isolate* isolate,
+      CodeStubInterfaceDescriptor* descriptor);
+
+  static void InitializeForIsolate(Isolate* isolate) {
+    CompareNilICStub compare_stub(kStrictEquality, kNullValue);
+    compare_stub.InitializeInterfaceDescriptor(
+        isolate,
+        isolate->code_stub_interface_descriptor(CodeStub::CompareNilIC));
+  }
+
+  virtual Code::ExtraICState GetExtraICState() {
+    return bit_field_;
+  }
+
+  EqualityKind GetKind() { return EqualityKindField::decode(bit_field_); }
+  NilValue GetNilValue() { return NilValueField::decode(bit_field_); }
+  Types GetTypes() { return TypesField::decode(bit_field_); }
+
+  static Types TypesFromExtraICState(
+      Code::ExtraICState state) {
+    return TypesField::decode(state);
+  }
+  static EqualityKind EqualityKindFromExtraICState(
+      Code::ExtraICState state) {
+    return EqualityKindField::decode(state);
+  }
+  static NilValue NilValueFromExtraICState(Code::ExtraICState state) {
+    return NilValueField::decode(state);
+  }
+
+  static Types GetPatchedICFlags(Code::ExtraICState extra_ic_state,
+                                 Handle<Object> object,
+                                 bool* already_monomorphic);
+
+ private:
+  friend class CompareNilIC;
+
+  class EqualityKindField : public BitField<EqualityKind, 0, 1> {};
+  class NilValueField : public BitField<NilValue, 1, 1> {};
+  class TypesField : public BitField<Types, 3, 4> {};
+
+  CompareNilICStub(EqualityKind kind, NilValue nil)
+      : HydrogenCodeStub(CODE_STUB_IS_MISS), bit_field_(0) {
+    bit_field_ = EqualityKindField::encode(kind) |
+        NilValueField::encode(nil);
+  }
+
+  virtual CodeStub::Major MajorKey() { return CompareNilIC; }
+  virtual int NotMissMinorKey() { return bit_field_; }
+
+  int bit_field_;
+
+  DISALLOW_COPY_AND_ASSIGN(CompareNilICStub);
+};
+
+
 class CEntryStub : public PlatformCodeStub {
  public:
   explicit CEntryStub(int result_size,
index 89ced419c019cdb5d12d865bd421f8ebea9daef7..e62e6b35e4b6ccccef4588111e3ba69c411ffec1 100644 (file)
@@ -147,8 +147,7 @@ Code::Flags CompilationInfo::flags() const {
     return Code::ComputeFlags(code_stub()->GetCodeKind(),
                               code_stub()->GetICState(),
                               code_stub()->GetExtraICState(),
-                              Code::NORMAL,
-                              0);
+                              Code::NORMAL, -1);
   } else {
     return Code::ComputeFlags(Code::OPTIMIZED_FUNCTION);
   }
index 6ec698179a9b7d7ed797e447297d60d8019084f4..a8e1e1ceec3efc0330f6cf7805d803dd4a12ffd4 100644 (file)
@@ -3949,6 +3949,10 @@ class HCompareObjectEqAndBranch: public HTemplateControlInstruction<2, 2> {
     return Representation::Tagged();
   }
 
+  virtual Representation observed_input_representation(int index) {
+    return Representation::Tagged();
+  }
+
   DECLARE_CONCRETE_INSTRUCTION(CompareObjectEqAndBranch)
 };
 
index 610266f3ec4b6b5a3ea490c67da0ca93cc8ab130..e34c48aee50af7e47a08c204d6a2251b38384955 100644 (file)
@@ -603,6 +603,19 @@ HConstant* HGraph::GetConstantInt32(SetOncePointer<HConstant>* pointer,
 }
 
 
+HConstant* HGraph::GetConstantSmi(SetOncePointer<HConstant>* pointer,
+                                  int32_t value) {
+  if (!pointer->is_set()) {
+    HConstant* constant =
+        new(zone()) HConstant(Handle<Object>(Smi::FromInt(value), isolate()),
+                              Representation::Tagged());
+    constant->InsertAfter(GetConstantUndefined());
+    pointer->set(constant);
+  }
+  return pointer->get();
+}
+
+
 HConstant* HGraph::GetConstant0() {
   return GetConstantInt32(&constant_0_, 0);
 }
@@ -638,6 +651,18 @@ HConstant* HGraph::GetConstant##Name() {                                       \
 DEFINE_GET_CONSTANT(True, true, HType::Boolean(), true)
 DEFINE_GET_CONSTANT(False, false, HType::Boolean(), false)
 DEFINE_GET_CONSTANT(Hole, the_hole, HType::Tagged(), false)
+DEFINE_GET_CONSTANT(Null, null, HType::Tagged(), false)
+
+
+HConstant* HGraph::GetConstantSmi0() {
+  return GetConstantSmi(&constant_smi_0_, 0);
+}
+
+
+HConstant* HGraph::GetConstantSmi1() {
+  return GetConstantSmi(&constant_smi_1_, 1);
+}
+
 
 #undef DEFINE_GET_CONSTANT
 
@@ -770,8 +795,9 @@ void HGraphBuilder::IfBuilder::CaptureContinuation(
   HBasicBlock* true_block = last_true_block_ == NULL
       ? first_true_block_
       : last_true_block_;
-  HBasicBlock* false_block =
-      did_else_ ? builder_->current_block() : first_false_block_;
+  HBasicBlock* false_block = did_else_ && (first_false_block_ != NULL)
+      ? builder_->current_block()
+      : first_false_block_;
   continuation->Capture(true_block, false_block, position_);
   captured_ = true;
   End();
@@ -804,7 +830,6 @@ void HGraphBuilder::IfBuilder::Else() {
 
 
 void HGraphBuilder::IfBuilder::Deopt() {
-  ASSERT(!(did_then_ ^ did_else_));
   HBasicBlock* block = builder_->current_block();
   block->FinishExitWithDeoptimization(HDeoptimize::kUseAll);
   if (did_else_) {
@@ -815,6 +840,19 @@ void HGraphBuilder::IfBuilder::Deopt() {
 }
 
 
+void HGraphBuilder::IfBuilder::Return(HValue* value) {
+  HBasicBlock* block = builder_->current_block();
+  block->Finish(new(zone()) HReturn(value,
+                                    builder_->environment()->LookupContext(),
+                                    builder_->graph()->GetConstantMinus1()));
+  if (did_else_) {
+    first_false_block_ = NULL;
+  } else {
+    first_true_block_ = NULL;
+  }
+}
+
+
 void HGraphBuilder::IfBuilder::End() {
   if (!captured_) {
     ASSERT(did_then_);
@@ -1669,6 +1707,53 @@ HValue* HGraphBuilder::BuildCloneShallowArray(HContext* context,
 }
 
 
+void HGraphBuilder::BuildCompareNil(
+    HValue* value,
+    EqualityKind kind,
+    CompareNilICStub::Types types,
+    Handle<Map> map,
+    int position,
+    HIfContinuation* continuation) {
+  IfBuilder if_nil(this, position);
+  bool needs_or = false;
+  if ((types & CompareNilICStub::kCompareAgainstNull) != 0) {
+    if (needs_or) if_nil.Or();
+    if_nil.If<HCompareObjectEqAndBranch>(value, graph()->GetConstantNull());
+    needs_or = true;
+  }
+  if ((types & CompareNilICStub::kCompareAgainstUndefined) != 0) {
+    if (needs_or) if_nil.Or();
+    if_nil.If<HCompareObjectEqAndBranch>(value,
+                                         graph()->GetConstantUndefined());
+    needs_or = true;
+  }
+  // Handle either undetectable or monomorphic, not both.
+  ASSERT(((types & CompareNilICStub::kCompareAgainstUndetectable) == 0) ||
+         ((types & CompareNilICStub::kCompareAgainstMonomorphicMap) == 0));
+  if ((types & CompareNilICStub::kCompareAgainstUndetectable) != 0) {
+    if (needs_or) if_nil.Or();
+    if_nil.If<HIsUndetectableAndBranch>(value);
+  } else {
+    if_nil.Then();
+    if_nil.Else();
+    if ((types & CompareNilICStub::kCompareAgainstMonomorphicMap) != 0) {
+      BuildCheckNonSmi(value);
+      // For ICs, the map checked below is a sentinel map that gets replaced by
+      // the monomorphic map when the code is used as a template to generate a
+      // new IC. For optimized functions, there is no sentinel map, the map
+      // emitted below is the actual monomorphic map.
+      BuildCheckMap(value, map);
+    } else {
+      if (kind == kNonStrictEquality) {
+        if_nil.Deopt();
+      }
+    }
+  }
+
+  if_nil.CaptureContinuation(continuation);
+}
+
+
 HOptimizedGraphBuilder::HOptimizedGraphBuilder(CompilationInfo* info,
                                                TypeFeedbackOracle* oracle)
     : HGraphBuilder(info),
@@ -10212,9 +10297,24 @@ void HOptimizedGraphBuilder::HandleLiteralCompareNil(CompareOperation* expr,
   ASSERT(current_block()->HasPredecessor());
   EqualityKind kind =
       expr->op() == Token::EQ_STRICT ? kStrictEquality : kNonStrictEquality;
-  HIsNilAndBranch* instr = new(zone()) HIsNilAndBranch(value, kind, nil);
-  instr->set_position(expr->position());
-  return ast_context()->ReturnControl(instr, expr->id());
+  HIfContinuation continuation;
+  TypeFeedbackId id = expr->CompareOperationFeedbackId();
+  CompareNilICStub::Types types;
+  if (kind == kStrictEquality) {
+    if (nil == kNullValue) {
+      types = CompareNilICStub::kCompareAgainstNull;
+    } else {
+      types = CompareNilICStub::kCompareAgainstUndefined;
+    }
+  } else {
+    types = static_cast<CompareNilICStub::Types>(
+        oracle()->CompareNilTypes(id));
+    if (types == 0) types = CompareNilICStub::kFullCompare;
+  }
+  Handle<Map> map_handle(oracle()->CompareNilMonomorphicReceiverType(id));
+  BuildCompareNil(value, kind, types, map_handle,
+                  expr->position(), &continuation);
+  return ast_context()->ReturnContinuation(&continuation, expr->id());
 }
 
 
index 9d3a78034abcaed7237940a4797c4098bb653a11..bc76cdd3de0aaee8cc1f76cfc54e15760bbde40b 100644 (file)
@@ -304,10 +304,13 @@ class HGraph: public ZoneObject {
   HConstant* GetConstantUndefined() const { return undefined_constant_.get(); }
   HConstant* GetConstant0();
   HConstant* GetConstant1();
+  HConstant* GetConstantSmi0();
+  HConstant* GetConstantSmi1();
   HConstant* GetConstantMinus1();
   HConstant* GetConstantTrue();
   HConstant* GetConstantFalse();
   HConstant* GetConstantHole();
+  HConstant* GetConstantNull();
   HConstant* GetInvalidContext();
 
   HBasicBlock* CreateBasicBlock();
@@ -395,6 +398,8 @@ class HGraph: public ZoneObject {
  private:
   HConstant* GetConstantInt32(SetOncePointer<HConstant>* pointer,
                               int32_t integer_value);
+  HConstant* GetConstantSmi(SetOncePointer<HConstant>* pointer,
+                            int32_t integer_value);
 
   void MarkAsDeoptimizingRecursively(HBasicBlock* block);
   void NullifyUnreachableInstructions();
@@ -424,10 +429,13 @@ class HGraph: public ZoneObject {
   SetOncePointer<HConstant> undefined_constant_;
   SetOncePointer<HConstant> constant_0_;
   SetOncePointer<HConstant> constant_1_;
+  SetOncePointer<HConstant> constant_smi_0_;
+  SetOncePointer<HConstant> constant_smi_1_;
   SetOncePointer<HConstant> constant_minus1_;
   SetOncePointer<HConstant> constant_true_;
   SetOncePointer<HConstant> constant_false_;
   SetOncePointer<HConstant> constant_the_hole_;
+  SetOncePointer<HConstant> constant_null_;
   SetOncePointer<HConstant> constant_invalid_context_;
   SetOncePointer<HArgumentsObject> arguments_object_;
 
@@ -890,7 +898,6 @@ class HIfContinuation {
                HBasicBlock* false_branch,
                int position) {
     ASSERT(!continuation_captured_);
-    ASSERT(true_branch != NULL || false_branch != NULL);
     true_branch_ = true_branch;
     false_branch_ = false_branch;
     position_ = position;
@@ -1125,6 +1132,8 @@ class HGraphBuilder {
       End();
     }
 
+    void Return(HValue* value);
+
    private:
     void AddCompare(HControlInstruction* compare);
 
@@ -1241,6 +1250,14 @@ class HGraphBuilder {
                                  ElementsKind kind,
                                  int length);
 
+  void BuildCompareNil(
+      HValue* value,
+      EqualityKind kind,
+      CompareNilICStub::Types types,
+      Handle<Map> map,
+      int position,
+      HIfContinuation* continuation);
+
  private:
   HGraphBuilder();
   CompilationInfo* info_;
index 8c6801d7d81651def0d7efabf8f4e4c99a17144c..c489f7fec6313874e68ad8090d194c3f399797d3 100644 (file)
@@ -139,6 +139,19 @@ void ArrayNArgumentsConstructorStub::InitializeInterfaceDescriptor(
 }
 
 
+void CompareNilICStub::InitializeInterfaceDescriptor(
+    Isolate* isolate,
+    CodeStubInterfaceDescriptor* descriptor) {
+  static Register registers[] = { eax };
+  descriptor->register_param_count_ = 1;
+  descriptor->register_params_ = registers;
+  descriptor->deoptimization_handler_ =
+      FUNCTION_ADDR(CompareNilIC_Miss);
+  descriptor->miss_handler_ =
+      ExternalReference(IC_Utility(IC::kCompareNilIC_Miss), isolate);
+}
+
+
 #define __ ACCESS_MASM(masm)
 
 
index 113ca4b480a9827d30d47cd927168f2dfd6f2284..c4f8a4cb4a5791928ac6033500980532d60ab45e 100644 (file)
@@ -4438,24 +4438,21 @@ void FullCodeGenerator::EmitLiteralCompareNil(CompareOperation* expr,
 
   VisitForAccumulatorValue(sub_expr);
   PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
-  Handle<Object> nil_value = nil == kNullValue ?
-      isolate()->factory()->null_value() :
-      isolate()->factory()->undefined_value();
-  __ cmp(eax, nil_value);
-  if (expr->op() == Token::EQ_STRICT) {
+
+  EqualityKind kind = expr->op() == Token::EQ_STRICT
+      ? kStrictEquality : kNonStrictEquality;
+  Handle<Object> nil_value = nil == kNullValue
+      ? isolate()->factory()->null_value()
+      : isolate()->factory()->undefined_value();
+  if (kind == kStrictEquality) {
+    __ cmp(eax, nil_value);
     Split(equal, if_true, if_false, fall_through);
   } else {
-    Handle<Object> other_nil_value = nil == kNullValue ?
-        isolate()->factory()->undefined_value() :
-        isolate()->factory()->null_value();
-    __ j(equal, if_true);
-    __ cmp(eax, other_nil_value);
-    __ j(equal, if_true);
-    __ JumpIfSmi(eax, if_false);
-    // It can be an undetectable object.
-    __ mov(edx, FieldOperand(eax, HeapObject::kMapOffset));
-    __ movzx_b(edx, FieldOperand(edx, Map::kBitFieldOffset));
-    __ test(edx, Immediate(1 << Map::kIsUndetectable));
+    Handle<Code> ic = CompareNilICStub::GetUninitialized(isolate(),
+                                                         kNonStrictEquality,
+                                                         nil);
+    CallIC(ic, RelocInfo::CODE_TARGET, expr->CompareOperationFeedbackId());
+    __ test(eax, eax);
     Split(not_zero, if_true, if_false, fall_through);
   }
   context()->Plug(if_true, if_false);
index e0ebdddf018c0046d83d15f8ab1c0916a84e47ad..40676abc3d1d7213d873e26d5c7067d1307c1a72 100644 (file)
--- a/src/ic.cc
+++ b/src/ic.cc
@@ -347,6 +347,7 @@ void IC::Clear(Address address) {
     case Code::CALL_IC: return CallIC::Clear(address, target);
     case Code::KEYED_CALL_IC:  return KeyedCallIC::Clear(address, target);
     case Code::COMPARE_IC: return CompareIC::Clear(address, target);
+    case Code::COMPARE_NIL_IC: return CompareNilIC::Clear(address, target);
     case Code::UNARY_OP_IC:
     case Code::BINARY_OP_IC:
     case Code::TO_BOOLEAN_IC:
@@ -2770,6 +2771,93 @@ RUNTIME_FUNCTION(Code*, CompareIC_Miss) {
 }
 
 
+Code* CompareNilIC::GetRawUninitialized(EqualityKind kind,
+                                        NilValue nil) {
+  CompareNilICStub stub(kind, nil);
+  Code* code = NULL;
+  CHECK(stub.FindCodeInCache(&code, Isolate::Current()));
+  return code;
+}
+
+
+void CompareNilIC::Clear(Address address, Code* target) {
+  if (target->ic_state() == UNINITIALIZED) return;
+  Code::ExtraICState state = target->extended_extra_ic_state();
+
+  EqualityKind kind =
+      CompareNilICStub::EqualityKindFromExtraICState(state);
+  NilValue nil =
+      CompareNilICStub::NilValueFromExtraICState(state);
+
+  SetTargetAtAddress(address, GetRawUninitialized(kind, nil));
+}
+
+
+MaybeObject* CompareNilIC::DoCompareNilSlow(EqualityKind kind,
+                                            NilValue nil,
+                                            Handle<Object> object) {
+  if (kind == kStrictEquality) {
+    if (nil == kNullValue) {
+      return Smi::FromInt(object->IsNull());
+    } else {
+      return Smi::FromInt(object->IsUndefined());
+    }
+  }
+  if (object->IsNull() || object->IsUndefined()) {
+    return Smi::FromInt(true);
+  }
+  return Smi::FromInt(object->IsUndetectableObject());
+}
+
+
+MaybeObject* CompareNilIC::CompareNil(Handle<Object> object) {
+  Code::ExtraICState extra_ic_state = target()->extended_extra_ic_state();
+
+  // Extract the current supported types from the patched IC and calculate what
+  // types must be supported as a result of the miss.
+  bool already_monomorphic;
+  CompareNilICStub::Types types =
+      CompareNilICStub::GetPatchedICFlags(extra_ic_state,
+                                          object, &already_monomorphic);
+
+  EqualityKind kind =
+      CompareNilICStub::EqualityKindFromExtraICState(extra_ic_state);
+  NilValue nil =
+      CompareNilICStub::NilValueFromExtraICState(extra_ic_state);
+
+  // Find or create the specialized stub to support the new set of types.
+  CompareNilICStub stub(kind, nil, types);
+  Handle<Code> code;
+  if ((types & CompareNilICStub::kCompareAgainstMonomorphicMap) != 0) {
+    Handle<Map> monomorphic_map(already_monomorphic
+                                ? target()->FindFirstMap()
+                                : HeapObject::cast(*object)->map());
+    code = isolate()->stub_cache()->ComputeCompareNil(monomorphic_map,
+                                                      nil,
+                                                      stub.GetTypes());
+  } else {
+    code = stub.GetCode(isolate());
+  }
+
+  patch(*code);
+
+  return DoCompareNilSlow(kind, nil, object);
+}
+
+
+void CompareNilIC::patch(Code* code) {
+  set_target(code);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, CompareNilIC_Miss) {
+  HandleScope scope(isolate);
+  Handle<Object> object = args.at<Object>(0);
+  CompareNilIC ic(isolate);
+  return ic.CompareNil(object);
+}
+
+
 RUNTIME_FUNCTION(MaybeObject*, Unreachable) {
   UNREACHABLE();
   CHECK(false);
index 6c676e60436b1365a030df50cc3260e1a2d1d3e0..4bf259a2f481f04908cdcf3774947e6611fc0474 100644 (file)
--- a/src/ic.h
+++ b/src/ic.h
@@ -59,6 +59,7 @@ namespace internal {
   ICU(UnaryOp_Patch)                                  \
   ICU(BinaryOp_Patch)                                 \
   ICU(CompareIC_Miss)                                 \
+  ICU(CompareNilIC_Miss)                              \
   ICU(Unreachable)                                    \
   ICU(ToBoolean_Patch)
 //
@@ -776,6 +777,26 @@ class CompareIC: public IC {
 };
 
 
+class CompareNilIC: public IC {
+ public:
+  explicit CompareNilIC(Isolate* isolate) : IC(EXTRA_CALL_FRAME, isolate) {}
+
+  MUST_USE_RESULT MaybeObject* CompareNil(Handle<Object> object);
+
+  static Handle<Code> GetUninitialized();
+
+  static Code* GetRawUninitialized(EqualityKind kind, NilValue nil);
+
+  static void Clear(Address address, Code* target);
+
+  void patch(Code* code);
+
+  static MUST_USE_RESULT MaybeObject* DoCompareNilSlow(EqualityKind kind,
+                                                       NilValue nil,
+                                                       Handle<Object> object);
+};
+
+
 class ToBooleanIC: public IC {
  public:
   explicit ToBooleanIC(Isolate* isolate) : IC(NO_EXTRA_FRAME, isolate) { }
@@ -790,6 +811,8 @@ void PatchInlinedSmiCode(Address address, InlinedSmiCheck check);
 
 DECLARE_RUNTIME_FUNCTION(MaybeObject*, KeyedLoadIC_MissFromStubFailure);
 DECLARE_RUNTIME_FUNCTION(MaybeObject*, KeyedStoreIC_MissFromStubFailure);
+DECLARE_RUNTIME_FUNCTION(MaybeObject*, CompareNilIC_Miss);
+
 
 } }  // namespace v8::internal
 
index 82589a1f75338bc29c254b4ed3764e707b472d0a..9ac7c7873ecf607d947627ba1128cc0002d2b4f3 100644 (file)
@@ -2243,6 +2243,7 @@ bool Isolate::Init(Deserializer* des) {
                                    DONT_TRACK_ALLOCATION_SITE, 0);
     stub.InitializeInterfaceDescriptor(
         this, code_stub_interface_descriptor(CodeStub::FastCloneShallowArray));
+    CompareNilICStub::InitializeForIsolate(this);
   }
 
   if (FLAG_parallel_recompilation) optimizing_compiler_thread_.Start();
index 58e6aa6a339bf8010a0f49bc5c2ff2510297bb04..10d7f71332f791f26fbe794551ad7dacc0120cc9 100644 (file)
@@ -329,7 +329,6 @@ void LChunk::MarkEmptyBlocks() {
             can_eliminate = false;
           }
         }
-
         if (can_eliminate) {
           label->set_replacement(GetLabel(goto_instr->block_id()));
         }
index 57abdefbaba9ae5e850b53148bdffadefa99a28f..dc1aa0bb7e346b109e46ffb50f087647ec3753a9 100644 (file)
@@ -1595,6 +1595,7 @@ void Logger::LogCodeObject(Object* object) {
       case Code::UNARY_OP_IC:   // fall through
       case Code::BINARY_OP_IC:   // fall through
       case Code::COMPARE_IC:  // fall through
+      case Code::COMPARE_NIL_IC:   // fall through
       case Code::TO_BOOLEAN_IC:  // fall through
       case Code::STUB:
         description =
index a14fccb073fb7936f1efe9f83bf9d33ddad1646a..822b31052178f2ec452c473273785e89a171539b 100644 (file)
@@ -3634,6 +3634,12 @@ Code::ExtraICState Code::extra_ic_state() {
 }
 
 
+Code::ExtraICState Code::extended_extra_ic_state() {
+  ASSERT(is_inline_cache_stub() || ic_state() == DEBUG_STUB);
+  return ExtractExtendedExtraICStateFromFlags(flags());
+}
+
+
 Code::StubType Code::type() {
   return ExtractTypeFromFlags(flags());
 }
@@ -3663,6 +3669,7 @@ int Code::major_key() {
          kind() == UNARY_OP_IC ||
          kind() == BINARY_OP_IC ||
          kind() == COMPARE_IC ||
+         kind() == COMPARE_NIL_IC ||
          kind() == LOAD_IC ||
          kind() == KEYED_LOAD_IC ||
          kind() == TO_BOOLEAN_IC);
@@ -3676,6 +3683,7 @@ void Code::set_major_key(int major) {
          kind() == UNARY_OP_IC ||
          kind() == BINARY_OP_IC ||
          kind() == COMPARE_IC ||
+         kind() == COMPARE_NIL_IC ||
          kind() == LOAD_IC ||
          kind() == KEYED_LOAD_IC ||
          kind() == STORE_IC ||
@@ -3940,13 +3948,23 @@ Code::Flags Code::ComputeFlags(Kind kind,
                                int argc,
                                InlineCacheHolderFlag holder) {
   ASSERT(argc <= Code::kMaxArguments);
+  // Since the extended extra ic state overlaps with the argument count
+  // for CALL_ICs, do so checks to make sure that they don't interfere.
+  ASSERT((kind != Code::CALL_IC &&
+          kind != Code::KEYED_CALL_IC) ||
+         (ExtraICStateField::encode(extra_ic_state) | true));
   // Compute the bit mask.
   unsigned int bits = KindField::encode(kind)
       | ICStateField::encode(ic_state)
       | TypeField::encode(type)
-      | ExtraICStateField::encode(extra_ic_state)
-      | (argc << kArgumentsCountShift)
+      | ExtendedExtraICStateField::encode(extra_ic_state)
       | CacheHolderField::encode(holder);
+  // TODO(danno): This is a bit of a hack right now since there are still
+  // clients of this API that pass "extra" values in for argc. These clients
+  // should be retrofitted to used ExtendedExtraICState.
+  if (kind != Code::COMPARE_NIL_IC) {
+    bits |= (argc << kArgumentsCountShift);
+  }
   return static_cast<Flags>(bits);
 }
 
@@ -3975,6 +3993,12 @@ Code::ExtraICState Code::ExtractExtraICStateFromFlags(Flags flags) {
 }
 
 
+Code::ExtraICState Code::ExtractExtendedExtraICStateFromFlags(
+    Flags flags) {
+  return ExtendedExtraICStateField::decode(flags);
+}
+
+
 Code::StubType Code::ExtractTypeFromFlags(Flags flags) {
   return TypeField::decode(flags);
 }
@@ -5124,7 +5148,8 @@ void Code::set_type_feedback_info(Object* value, WriteBarrierMode mode) {
 
 
 int Code::stub_info() {
-  ASSERT(kind() == COMPARE_IC || kind() == BINARY_OP_IC || kind() == LOAD_IC);
+  ASSERT(kind() == COMPARE_IC || kind() == COMPARE_NIL_IC ||
+         kind() == BINARY_OP_IC || kind() == LOAD_IC);
   Object* value = READ_FIELD(this, kTypeFeedbackInfoOffset);
   return Smi::cast(value)->value();
 }
@@ -5132,6 +5157,7 @@ int Code::stub_info() {
 
 void Code::set_stub_info(int value) {
   ASSERT(kind() == COMPARE_IC ||
+         kind() == COMPARE_NIL_IC ||
          kind() == BINARY_OP_IC ||
          kind() == STUB ||
          kind() == LOAD_IC ||
index c5ad8ea6b2cc7010a13aa655699f85fe34edb730..db694f5bbf6828c19ca47551b61dce0fc628a254 100644 (file)
@@ -9021,6 +9021,12 @@ void ObjectVisitor::VisitExternalReference(RelocInfo* rinfo) {
   VisitExternalReferences(p, p + 1);
 }
 
+byte Code::compare_nil_state() {
+  ASSERT(is_compare_nil_ic_stub());
+  return CompareNilICStub::TypesFromExtraICState(extended_extra_ic_state());
+}
+
+
 void Code::InvalidateRelocation() {
   set_relocation_info(GetHeap()->empty_byte_array());
 }
@@ -9157,6 +9163,22 @@ Map* Code::FindFirstMap() {
 }
 
 
+void Code::ReplaceFirstMap(Map* replace_with) {
+  ASSERT(is_inline_cache_stub());
+  AssertNoAllocation no_allocation;
+  int mask = RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT);
+  for (RelocIterator it(this, mask); !it.done(); it.next()) {
+    RelocInfo* info = it.rinfo();
+    Object* object = info->target_object();
+    if (object->IsMap()) {
+      info->set_target_object(replace_with);
+      return;
+    }
+  }
+  UNREACHABLE();
+}
+
+
 void Code::FindAllMaps(MapHandleList* maps) {
   ASSERT(is_inline_cache_stub());
   AssertNoAllocation no_allocation;
@@ -9352,6 +9374,7 @@ const char* Code::Kind2String(Kind kind) {
     case UNARY_OP_IC: return "UNARY_OP_IC";
     case BINARY_OP_IC: return "BINARY_OP_IC";
     case COMPARE_IC: return "COMPARE_IC";
+    case COMPARE_NIL_IC: return "COMPARE_NIL_IC";
     case TO_BOOLEAN_IC: return "TO_BOOLEAN_IC";
   }
   UNREACHABLE();
index 8de3c690e51bb2714955fee6386a4796c2a2aa88..08d2af03ffa243879b05289218bddf81db0252ce 100644 (file)
@@ -4354,6 +4354,7 @@ class Code: public HeapObject {
   V(UNARY_OP_IC)          \
   V(BINARY_OP_IC)         \
   V(COMPARE_IC)           \
+  V(COMPARE_NIL_IC)       \
   V(TO_BOOLEAN_IC)
 
   enum Kind {
@@ -4465,6 +4466,8 @@ class Code: public HeapObject {
   inline Kind kind();
   inline InlineCacheState ic_state();  // Only valid for IC stubs.
   inline ExtraICState extra_ic_state();  // Only valid for IC stubs.
+  inline ExtraICState extended_extra_ic_state();  // Only valid for
+                                                  // non-call IC stubs.
   inline StubType type();  // Only valid for monomorphic IC stubs.
   inline int arguments_count();  // Only valid for call IC stubs.
 
@@ -4480,6 +4483,7 @@ class Code: public HeapObject {
   inline bool is_unary_op_stub() { return kind() == UNARY_OP_IC; }
   inline bool is_binary_op_stub() { return kind() == BINARY_OP_IC; }
   inline bool is_compare_ic_stub() { return kind() == COMPARE_IC; }
+  inline bool is_compare_nil_ic_stub() { return kind() == COMPARE_NIL_IC; }
   inline bool is_to_boolean_ic_stub() { return kind() == TO_BOOLEAN_IC; }
 
   // [major_key]: For kind STUB or BINARY_OP_IC, the major key.
@@ -4558,6 +4562,9 @@ class Code: public HeapObject {
   inline byte to_boolean_state();
   inline void set_to_boolean_state(byte value);
 
+  // [compare_nil]: For kind COMPARE_NIL_IC tells what state the stub is in.
+  byte compare_nil_state();
+
   // [has_function_cache]: For kind STUB tells whether there is a function
   // cache is passed to the stub.
   inline bool has_function_cache();
@@ -4577,6 +4584,7 @@ class Code: public HeapObject {
   // Find the first map in an IC stub.
   Map* FindFirstMap();
   void FindAllMaps(MapHandleList* maps);
+  void ReplaceFirstMap(Map* replace);
 
   // Find the first code in an IC stub.
   Code* FindFirstCode();
@@ -4629,6 +4637,7 @@ class Code: public HeapObject {
   static inline Kind ExtractKindFromFlags(Flags flags);
   static inline InlineCacheHolderFlag ExtractCacheHolderFromFlags(Flags flags);
   static inline ExtraICState ExtractExtraICStateFromFlags(Flags flags);
+  static inline ExtraICState ExtractExtendedExtraICStateFromFlags(Flags flags);
   static inline int ExtractArgumentsCountFromFlags(Flags flags);
 
   static inline Flags RemoveTypeFromFlags(Flags flags);
@@ -4768,8 +4777,11 @@ class Code: public HeapObject {
   class TypeField: public BitField<StubType, 3, 3> {};
   class CacheHolderField: public BitField<InlineCacheHolderFlag, 6, 1> {};
   class KindField: public BitField<Kind, 7, 4> {};
-  class ExtraICStateField: public BitField<ExtraICState, 11, 5> {};
-  class IsPregeneratedField: public BitField<bool, 16, 1> {};
+  class IsPregeneratedField: public BitField<bool, 11, 1> {};
+  class ExtraICStateField: public BitField<ExtraICState, 12, 5> {};
+  class ExtendedExtraICStateField: public BitField<ExtraICState, 12,
+      PlatformSmiTagging::kSmiValueSize - 12 + 1> {};  // NOLINT
+  STATIC_ASSERT(ExtraICStateField::kShift == ExtendedExtraICStateField::kShift);
 
   // KindSpecificFlags1 layout (STUB and OPTIMIZED_FUNCTION)
   static const int kStackSlotsFirstBit = 0;
@@ -4842,6 +4854,13 @@ class Code: public HeapObject {
       PlatformSmiTagging::kSmiValueSize - Code::kArgumentsCountShift + 1;
   static const int kMaxArguments = (1 << kArgumentsBits) - 1;
 
+  // ICs can use either argument count or ExtendedExtraIC, since their storage
+  // overlaps.
+  STATIC_ASSERT(ExtraICStateField::kShift +
+                ExtraICStateField::kSize + kArgumentsBits ==
+                ExtendedExtraICStateField::kShift +
+                ExtendedExtraICStateField::kSize);
+
   // This constant should be encodable in an ARM instruction.
   static const int kFlagsNotUsedInLookup =
       TypeField::kMask | CacheHolderField::kMask;
index 7202e1bbc07556ce31efc1d758418064d2a393f0..df1c3ef126031728d13241c97f4a475df36f87ca 100644 (file)
@@ -1817,6 +1817,7 @@ static void ReportCodeKindStatistics() {
       CASE(UNARY_OP_IC);
       CASE(BINARY_OP_IC);
       CASE(COMPARE_IC);
+      CASE(COMPARE_NIL_IC);
       CASE(TO_BOOLEAN_IC);
     }
   }
index 396e92ce39a6601295a6632de73088a0e05fec2f..f099c5d3d31eab1c47d81950cf08d2efb2f34069 100644 (file)
@@ -110,18 +110,28 @@ Handle<JSObject> StubCache::StubHolder(Handle<JSObject> receiver,
 
 
 Handle<Code> StubCache::FindIC(Handle<Name> name,
-                               Handle<JSObject> stub_holder,
+                               Handle<Map> stub_holder_map,
                                Code::Kind kind,
                                Code::StubType type,
-                               Code::ExtraICState extra_ic_state) {
-  Code::Flags flags = Code::ComputeMonomorphicFlags(kind, extra_ic_state, type);
-  Handle<Object> probe(stub_holder->map()->FindInCodeCache(*name, flags),
+                               Code::ExtraICState extra_state) {
+  Code::Flags flags = Code::ComputeMonomorphicFlags(kind, extra_state, type);
+  Handle<Object> probe(stub_holder_map->FindInCodeCache(*name, flags),
                        isolate_);
   if (probe->IsCode()) return Handle<Code>::cast(probe);
   return Handle<Code>::null();
 }
 
 
+Handle<Code> StubCache::FindIC(Handle<Name> name,
+                               Handle<JSObject> stub_holder,
+                               Code::Kind kind,
+                               Code::StubType type,
+                               Code::ExtraICState extra_ic_state) {
+  return FindIC(name, Handle<Map>(stub_holder->map()), kind,
+                type, extra_ic_state);
+}
+
+
 Handle<Code> StubCache::FindHandler(Handle<Name> name,
                                     Handle<JSObject> receiver,
                                     Handle<JSObject> stub_holder,
@@ -487,7 +497,8 @@ Handle<Code> StubCache::ComputeStoreGlobal(Handle<Name> name,
                                            Handle<JSGlobalPropertyCell> cell,
                                            StrictModeFlag strict_mode) {
   Handle<Code> stub = FindIC(
-      name, receiver, Code::STORE_IC, Code::NORMAL, strict_mode);
+      name, Handle<JSObject>::cast(receiver),
+      Code::STORE_IC, Code::NORMAL, strict_mode);
   if (!stub.is_null()) return stub;
 
   StoreStubCompiler compiler(isolate_, strict_mode);
@@ -893,6 +904,32 @@ Handle<Code> StubCache::ComputeCallMiss(int argc,
 }
 
 
+Handle<Code> StubCache::ComputeCompareNil(Handle<Map> receiver_map,
+                                          NilValue nil,
+                                          CompareNilICStub::Types types) {
+  CompareNilICStub stub(kNonStrictEquality, nil, types);
+
+  Handle<String> name(isolate_->heap()->empty_string());
+  if (!receiver_map->is_shared()) {
+    Handle<Code> cached_ic = FindIC(name, receiver_map, Code::COMPARE_NIL_IC,
+                                    Code::NORMAL, stub.GetExtraICState());
+    if (!cached_ic.is_null()) return cached_ic;
+  }
+
+  Handle<Code> ic = stub.GetCode(isolate_);
+  // For monomorphic maps, use the code as a template, copying and replacing
+  // the monomorphic map that checks the object's type.
+  ic = isolate_->factory()->CopyCode(ic);
+  ic->ReplaceFirstMap(*receiver_map);
+
+  if (!receiver_map->is_shared()) {
+    Map::UpdateCodeCache(receiver_map, name, ic);
+  }
+
+  return ic;
+}
+
+
 Handle<Code> StubCache::ComputeLoadElementPolymorphic(
     MapHandleList* receiver_maps) {
   Code::Flags flags = Code::ComputeFlags(Code::KEYED_LOAD_IC, POLYMORPHIC);
index 02bb541bd92455f25b09c11e151d88609be6ed9f..dbb5e90f2392071d220de13940c420c68747ef7a 100644 (file)
@@ -30,6 +30,7 @@
 
 #include "allocation.h"
 #include "arguments.h"
+#include "code-stubs.h"
 #include "ic-inl.h"
 #include "macro-assembler.h"
 #include "objects.h"
@@ -77,6 +78,12 @@ class StubCache {
   Handle<JSObject> StubHolder(Handle<JSObject> receiver,
                               Handle<JSObject> holder);
 
+  Handle<Code> FindIC(Handle<Name> name,
+                      Handle<Map> stub_holder_map,
+                      Code::Kind kind,
+                      Code::StubType type,
+                      Code::ExtraICState extra_state = Code::kNoExtraICState);
+
   Handle<Code> FindIC(Handle<Name> name,
                       Handle<JSObject> stub_holder,
                       Code::Kind kind,
@@ -271,6 +278,12 @@ class StubCache {
 
   // ---
 
+  Handle<Code> ComputeCompareNil(Handle<Map> receiver_map,
+                                 NilValue nil,
+                                 CompareNilICStub::Types types);
+
+  // ---
+
   Handle<Code> ComputeLoadElementPolymorphic(MapHandleList* receiver_maps);
   Handle<Code> ComputeStoreElementPolymorphic(MapHandleList* receiver_maps,
                                               KeyedAccessStoreMode store_mode,
index 39a01f59e8d826c41f027133af064d8289a4ca55..3bc509a6189a93068ee2f50b6f0a90d9aeffef67 100644 (file)
@@ -218,6 +218,17 @@ Handle<Map> TypeFeedbackOracle::StoreMonomorphicReceiverType(
 }
 
 
+Handle<Map> TypeFeedbackOracle::CompareNilMonomorphicReceiverType(
+    TypeFeedbackId id) {
+  Handle<Object> maybe_code = GetInfo(id);
+  if (maybe_code->IsCode()) {
+    Map* first_map = Handle<Code>::cast(maybe_code)->FindFirstMap();
+    if (first_map != NULL) return Handle<Map>(first_map);
+  }
+  return Handle<Map>();
+}
+
+
 KeyedAccessStoreMode TypeFeedbackOracle::GetStoreMode(
     TypeFeedbackId ast_id) {
   Handle<Object> map_or_code = GetInfo(ast_id);
@@ -625,12 +636,23 @@ void TypeFeedbackOracle::CollectKeyedReceiverTypes(TypeFeedbackId ast_id,
 }
 
 
-byte TypeFeedbackOracle::ToBooleanTypes(TypeFeedbackId ast_id) {
-  Handle<Object> object = GetInfo(ast_id);
+byte TypeFeedbackOracle::ToBooleanTypes(TypeFeedbackId id) {
+  Handle<Object> object = GetInfo(id);
   return object->IsCode() ? Handle<Code>::cast(object)->to_boolean_state() : 0;
 }
 
 
+byte TypeFeedbackOracle::CompareNilTypes(TypeFeedbackId id) {
+  Handle<Object> object = GetInfo(id);
+  if (object->IsCode() &&
+      Handle<Code>::cast(object)->is_compare_nil_ic_stub()) {
+    return Handle<Code>::cast(object)->compare_nil_state();
+  } else {
+    return CompareNilICStub::kFullCompare;
+  }
+}
+
+
 // Things are a bit tricky here: The iterator for the RelocInfos and the infos
 // themselves are not GC-safe, so we first get all infos, then we create the
 // dictionary (possibly triggering GC), and finally we relocate the collected
@@ -724,6 +746,7 @@ void TypeFeedbackOracle::ProcessRelocInfos(ZoneList<RelocInfo>* infos) {
       case Code::BINARY_OP_IC:
       case Code::COMPARE_IC:
       case Code::TO_BOOLEAN_IC:
+      case Code::COMPARE_NIL_IC:
         SetInfo(ast_id, target);
         break;
 
index 583c3fc52059badd4bbcae7f3bc7e57703053465..d6d958d56d2c4cf7588518cbad77af41ec52b431 100644 (file)
@@ -253,7 +253,8 @@ class TypeFeedbackOracle: public ZoneObject {
   bool IsForInFastCase(ForInStatement* expr);
 
   Handle<Map> LoadMonomorphicReceiverType(Property* expr);
-  Handle<Map> StoreMonomorphicReceiverType(TypeFeedbackId ast_id);
+  Handle<Map> StoreMonomorphicReceiverType(TypeFeedbackId id);
+  Handle<Map> CompareNilMonomorphicReceiverType(TypeFeedbackId id);
 
   KeyedAccessStoreMode GetStoreMode(TypeFeedbackId ast_id);
 
@@ -293,6 +294,11 @@ class TypeFeedbackOracle: public ZoneObject {
   // headers!! :-P
   byte ToBooleanTypes(TypeFeedbackId ast_id);
 
+  // TODO(1571) We can't use CompareNilICStub::Types as the return value because
+  // of various cylces in our headers. Death to tons of implementations in
+  // headers!! :-P
+  byte CompareNilTypes(TypeFeedbackId ast_id);
+
   // Get type information for arithmetic operations and compares.
   TypeInfo UnaryType(UnaryOperation* expr);
   void BinaryType(BinaryOperation* expr,
index d40a5bf05d8cdf6ed3c067bddaa11a86c570ae0d..23efa4f0e3b714372516432851dd43df20ea7ecb 100644 (file)
@@ -134,6 +134,19 @@ void ArrayNArgumentsConstructorStub::InitializeInterfaceDescriptor(
 }
 
 
+void CompareNilICStub::InitializeInterfaceDescriptor(
+    Isolate* isolate,
+    CodeStubInterfaceDescriptor* descriptor) {
+  static Register registers[] = { rax };
+  descriptor->register_param_count_ = 1;
+  descriptor->register_params_ = registers;
+  descriptor->deoptimization_handler_ =
+      FUNCTION_ADDR(CompareNilIC_Miss);
+  descriptor->miss_handler_ =
+      ExternalReference(IC_Utility(IC::kCompareNilIC_Miss), isolate);
+}
+
+
 #define __ ACCESS_MASM(masm)
 
 
index f9651f01efad26a02bbdf1e86df2745c1b943258..11366ce490267896a9b9c339b28dde4c3e7dfc8c 100644 (file)
@@ -4435,24 +4435,20 @@ void FullCodeGenerator::EmitLiteralCompareNil(CompareOperation* expr,
 
   VisitForAccumulatorValue(sub_expr);
   PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
-  Heap::RootListIndex nil_value = nil == kNullValue ?
-      Heap::kNullValueRootIndex :
-      Heap::kUndefinedValueRootIndex;
-  __ CompareRoot(rax, nil_value);
-  if (expr->op() == Token::EQ_STRICT) {
+  EqualityKind kind = expr->op() == Token::EQ_STRICT
+      ? kStrictEquality : kNonStrictEquality;
+  if (kind == kStrictEquality) {
+    Heap::RootListIndex nil_value = nil == kNullValue ?
+        Heap::kNullValueRootIndex :
+        Heap::kUndefinedValueRootIndex;
+    __ CompareRoot(rax, nil_value);
     Split(equal, if_true, if_false, fall_through);
   } else {
-    Heap::RootListIndex other_nil_value = nil == kNullValue ?
-        Heap::kUndefinedValueRootIndex :
-        Heap::kNullValueRootIndex;
-    __ j(equal, if_true);
-    __ CompareRoot(rax, other_nil_value);
-    __ j(equal, if_true);
-    __ JumpIfSmi(rax, if_false);
-    // It can be an undetectable object.
-    __ movq(rdx, FieldOperand(rax, HeapObject::kMapOffset));
-    __ testb(FieldOperand(rdx, Map::kBitFieldOffset),
-             Immediate(1 << Map::kIsUndetectable));
+    Handle<Code> ic = CompareNilICStub::GetUninitialized(isolate(),
+                                                         kNonStrictEquality,
+                                                         nil);
+    CallIC(ic, RelocInfo::CODE_TARGET, expr->CompareOperationFeedbackId());
+    __ testq(rax, rax);
     Split(not_zero, if_true, if_false, fall_through);
   }
   context()->Plug(if_true, if_false);