From f8ddf3a2625e6280fa11c623899c0ce48d5bf68d Mon Sep 17 00:00:00 2001 From: "danno@chromium.org" Date: Wed, 24 Apr 2013 11:32:17 +0000 Subject: [PATCH] Add monomorphic CompareNilICs and Crankshaft support 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 --- src/arm/code-stubs-arm.cc | 13 ++++ src/arm/full-codegen-arm.cc | 34 +++++------ src/ast.cc | 12 +++- src/ast.h | 3 + src/code-stubs-hydrogen.cc | 36 +++++++++-- src/code-stubs.cc | 36 +++++++++++ src/code-stubs.h | 97 +++++++++++++++++++++++++++++ src/compiler.cc | 3 +- src/hydrogen-instructions.h | 4 ++ src/hydrogen.cc | 112 ++++++++++++++++++++++++++++++++-- src/hydrogen.h | 19 +++++- src/ia32/code-stubs-ia32.cc | 13 ++++ src/ia32/full-codegen-ia32.cc | 29 ++++----- src/ic.cc | 88 ++++++++++++++++++++++++++ src/ic.h | 23 +++++++ src/isolate.cc | 1 + src/lithium.cc | 1 - src/log.cc | 1 + src/objects-inl.h | 32 +++++++++- src/objects.cc | 23 +++++++ src/objects.h | 23 ++++++- src/spaces.cc | 1 + src/stub-cache.cc | 47 ++++++++++++-- src/stub-cache.h | 13 ++++ src/type-info.cc | 27 +++++++- src/type-info.h | 8 ++- src/x64/code-stubs-x64.cc | 13 ++++ src/x64/full-codegen-x64.cc | 28 ++++----- 28 files changed, 660 insertions(+), 80 deletions(-) diff --git a/src/arm/code-stubs-arm.cc b/src/arm/code-stubs-arm.cc index 1f69dbe0f..7a7ec0925 100644 --- a/src/arm/code-stubs-arm.cc +++ b/src/arm/code-stubs-arm.cc @@ -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 diff --git a/src/arm/full-codegen-arm.cc b/src/arm/full-codegen-arm.cc index 7ecc2b3df..285490b6a 100644 --- a/src/arm/full-codegen-arm.cc +++ b/src/arm/full-codegen-arm.cc @@ -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 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); } diff --git a/src/ast.cc b/src/ast.cc index d6af89b38..d241355fc 100644 --- a/src/ast.cc +++ b/src/ast.cc @@ -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 equals +// Check for the pattern: void equals or +// undefined equals 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; } diff --git a/src/ast.h b/src/ast.h index 594b78008..2d5c200c0 100644 --- 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(); diff --git a/src/code-stubs-hydrogen.cc b/src/code-stubs-hydrogen.cc index ee93a7433..78673d68c 100644 --- a/src/code-stubs-hydrogen.cc +++ b/src/code-stubs-hydrogen.cc @@ -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 ArrayNArgumentsConstructorStub::GenerateCode() { return DoGenerateCode(this); } + +template <> +HValue* CodeStubGraphBuilder::BuildCodeUninitializedStub() { + CompareNilICStub* stub = casted_stub(); + HIfContinuation continuation; + Handle 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 CompareNilICStub::GenerateCode() { + return DoGenerateCode(this); +} + } } // namespace v8::internal diff --git a/src/code-stubs.cc b/src/code-stubs.cc index 3a4243dc5..1a2716360 100644 --- a/src/code-stubs.cc +++ b/src/code-stubs.cc @@ -407,6 +407,42 @@ void ICCompareStub::Generate(MacroAssembler* masm) { } +CompareNilICStub::Types CompareNilICStub::GetPatchedICFlags( + Code::ExtraICState extra_ic_state, + Handle 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( + types | CompareNilICStub::kCompareAgainstNull); + } else if (object->IsUndefined()) { + types = static_cast( + 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( + types | CompareNilICStub::kCompareAgainstMonomorphicMap); + } + } + return types; +} + + void InstanceofStub::PrintName(StringStream* stream) { const char* args = ""; if (HasArgsInRegisters()) { diff --git a/src/code-stubs.h b/src/code-stubs.h index 55d7e5d80..fa609240e 100644 --- a/src/code-stubs.h +++ b/src/code-stubs.h @@ -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 GenerateCode(); + + static Handle 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, + bool* already_monomorphic); + + private: + friend class CompareNilIC; + + class EqualityKindField : public BitField {}; + class NilValueField : public BitField {}; + class TypesField : public BitField {}; + + 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, diff --git a/src/compiler.cc b/src/compiler.cc index 89ced419c..e62e6b35e 100644 --- a/src/compiler.cc +++ b/src/compiler.cc @@ -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); } diff --git a/src/hydrogen-instructions.h b/src/hydrogen-instructions.h index 6ec698179..a8e1e1cee 100644 --- a/src/hydrogen-instructions.h +++ b/src/hydrogen-instructions.h @@ -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) }; diff --git a/src/hydrogen.cc b/src/hydrogen.cc index 610266f3e..e34c48aee 100644 --- a/src/hydrogen.cc +++ b/src/hydrogen.cc @@ -603,6 +603,19 @@ HConstant* HGraph::GetConstantInt32(SetOncePointer* pointer, } +HConstant* HGraph::GetConstantSmi(SetOncePointer* pointer, + int32_t value) { + if (!pointer->is_set()) { + HConstant* constant = + new(zone()) HConstant(Handle(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, + 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(value, graph()->GetConstantNull()); + needs_or = true; + } + if ((types & CompareNilICStub::kCompareAgainstUndefined) != 0) { + if (needs_or) if_nil.Or(); + if_nil.If(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(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( + oracle()->CompareNilTypes(id)); + if (types == 0) types = CompareNilICStub::kFullCompare; + } + Handle map_handle(oracle()->CompareNilMonomorphicReceiverType(id)); + BuildCompareNil(value, kind, types, map_handle, + expr->position(), &continuation); + return ast_context()->ReturnContinuation(&continuation, expr->id()); } diff --git a/src/hydrogen.h b/src/hydrogen.h index 9d3a78034..bc76cdd3d 100644 --- a/src/hydrogen.h +++ b/src/hydrogen.h @@ -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* pointer, int32_t integer_value); + HConstant* GetConstantSmi(SetOncePointer* pointer, + int32_t integer_value); void MarkAsDeoptimizingRecursively(HBasicBlock* block); void NullifyUnreachableInstructions(); @@ -424,10 +429,13 @@ class HGraph: public ZoneObject { SetOncePointer undefined_constant_; SetOncePointer constant_0_; SetOncePointer constant_1_; + SetOncePointer constant_smi_0_; + SetOncePointer constant_smi_1_; SetOncePointer constant_minus1_; SetOncePointer constant_true_; SetOncePointer constant_false_; SetOncePointer constant_the_hole_; + SetOncePointer constant_null_; SetOncePointer constant_invalid_context_; SetOncePointer 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, + int position, + HIfContinuation* continuation); + private: HGraphBuilder(); CompilationInfo* info_; diff --git a/src/ia32/code-stubs-ia32.cc b/src/ia32/code-stubs-ia32.cc index 8c6801d7d..c489f7fec 100644 --- a/src/ia32/code-stubs-ia32.cc +++ b/src/ia32/code-stubs-ia32.cc @@ -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) diff --git a/src/ia32/full-codegen-ia32.cc b/src/ia32/full-codegen-ia32.cc index 113ca4b48..c4f8a4cb4 100644 --- a/src/ia32/full-codegen-ia32.cc +++ b/src/ia32/full-codegen-ia32.cc @@ -4438,24 +4438,21 @@ void FullCodeGenerator::EmitLiteralCompareNil(CompareOperation* expr, VisitForAccumulatorValue(sub_expr); PrepareForBailoutBeforeSplit(expr, true, if_true, if_false); - Handle 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 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 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 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); diff --git a/src/ic.cc b/src/ic.cc index e0ebdddf0..40676abc3 100644 --- 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) { + 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) { + 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; + if ((types & CompareNilICStub::kCompareAgainstMonomorphicMap) != 0) { + Handle 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 = args.at(0); + CompareNilIC ic(isolate); + return ic.CompareNil(object); +} + + RUNTIME_FUNCTION(MaybeObject*, Unreachable) { UNREACHABLE(); CHECK(false); diff --git a/src/ic.h b/src/ic.h index 6c676e604..4bf259a2f 100644 --- 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); + + static Handle 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); +}; + + 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 diff --git a/src/isolate.cc b/src/isolate.cc index 82589a1f7..9ac7c7873 100644 --- a/src/isolate.cc +++ b/src/isolate.cc @@ -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(); diff --git a/src/lithium.cc b/src/lithium.cc index 58e6aa6a3..10d7f7133 100644 --- a/src/lithium.cc +++ b/src/lithium.cc @@ -329,7 +329,6 @@ void LChunk::MarkEmptyBlocks() { can_eliminate = false; } } - if (can_eliminate) { label->set_replacement(GetLabel(goto_instr->block_id())); } diff --git a/src/log.cc b/src/log.cc index 57abdefba..dc1aa0bb7 100644 --- a/src/log.cc +++ b/src/log.cc @@ -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 = diff --git a/src/objects-inl.h b/src/objects-inl.h index a14fccb07..822b31052 100644 --- a/src/objects-inl.h +++ b/src/objects-inl.h @@ -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(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 || diff --git a/src/objects.cc b/src/objects.cc index c5ad8ea6b..db694f5bb 100644 --- a/src/objects.cc +++ b/src/objects.cc @@ -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(); diff --git a/src/objects.h b/src/objects.h index 8de3c690e..08d2af03f 100644 --- a/src/objects.h +++ b/src/objects.h @@ -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 {}; class CacheHolderField: public BitField {}; class KindField: public BitField {}; - class ExtraICStateField: public BitField {}; - class IsPregeneratedField: public BitField {}; + class IsPregeneratedField: public BitField {}; + class ExtraICStateField: public BitField {}; + class ExtendedExtraICStateField: public BitField {}; // 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; diff --git a/src/spaces.cc b/src/spaces.cc index 7202e1bbc..df1c3ef12 100644 --- a/src/spaces.cc +++ b/src/spaces.cc @@ -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); } } diff --git a/src/stub-cache.cc b/src/stub-cache.cc index 396e92ce3..f099c5d3d 100644 --- a/src/stub-cache.cc +++ b/src/stub-cache.cc @@ -110,18 +110,28 @@ Handle StubCache::StubHolder(Handle receiver, Handle StubCache::FindIC(Handle name, - Handle stub_holder, + Handle 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 probe(stub_holder->map()->FindInCodeCache(*name, flags), + Code::ExtraICState extra_state) { + Code::Flags flags = Code::ComputeMonomorphicFlags(kind, extra_state, type); + Handle probe(stub_holder_map->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); return Handle::null(); } +Handle StubCache::FindIC(Handle name, + Handle stub_holder, + Code::Kind kind, + Code::StubType type, + Code::ExtraICState extra_ic_state) { + return FindIC(name, Handle(stub_holder->map()), kind, + type, extra_ic_state); +} + + Handle StubCache::FindHandler(Handle name, Handle receiver, Handle stub_holder, @@ -487,7 +497,8 @@ Handle StubCache::ComputeStoreGlobal(Handle name, Handle cell, StrictModeFlag strict_mode) { Handle stub = FindIC( - name, receiver, Code::STORE_IC, Code::NORMAL, strict_mode); + name, Handle::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 StubCache::ComputeCallMiss(int argc, } +Handle StubCache::ComputeCompareNil(Handle receiver_map, + NilValue nil, + CompareNilICStub::Types types) { + CompareNilICStub stub(kNonStrictEquality, nil, types); + + Handle name(isolate_->heap()->empty_string()); + if (!receiver_map->is_shared()) { + Handle cached_ic = FindIC(name, receiver_map, Code::COMPARE_NIL_IC, + Code::NORMAL, stub.GetExtraICState()); + if (!cached_ic.is_null()) return cached_ic; + } + + Handle 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 StubCache::ComputeLoadElementPolymorphic( MapHandleList* receiver_maps) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_LOAD_IC, POLYMORPHIC); diff --git a/src/stub-cache.h b/src/stub-cache.h index 02bb541bd..dbb5e90f2 100644 --- a/src/stub-cache.h +++ b/src/stub-cache.h @@ -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 StubHolder(Handle receiver, Handle holder); + Handle FindIC(Handle name, + Handle stub_holder_map, + Code::Kind kind, + Code::StubType type, + Code::ExtraICState extra_state = Code::kNoExtraICState); + Handle FindIC(Handle name, Handle stub_holder, Code::Kind kind, @@ -271,6 +278,12 @@ class StubCache { // --- + Handle ComputeCompareNil(Handle receiver_map, + NilValue nil, + CompareNilICStub::Types types); + + // --- + Handle ComputeLoadElementPolymorphic(MapHandleList* receiver_maps); Handle ComputeStoreElementPolymorphic(MapHandleList* receiver_maps, KeyedAccessStoreMode store_mode, diff --git a/src/type-info.cc b/src/type-info.cc index 39a01f59e..3bc509a61 100644 --- a/src/type-info.cc +++ b/src/type-info.cc @@ -218,6 +218,17 @@ Handle TypeFeedbackOracle::StoreMonomorphicReceiverType( } +Handle TypeFeedbackOracle::CompareNilMonomorphicReceiverType( + TypeFeedbackId id) { + Handle maybe_code = GetInfo(id); + if (maybe_code->IsCode()) { + Map* first_map = Handle::cast(maybe_code)->FindFirstMap(); + if (first_map != NULL) return Handle(first_map); + } + return Handle(); +} + + KeyedAccessStoreMode TypeFeedbackOracle::GetStoreMode( TypeFeedbackId ast_id) { Handle map_or_code = GetInfo(ast_id); @@ -625,12 +636,23 @@ void TypeFeedbackOracle::CollectKeyedReceiverTypes(TypeFeedbackId ast_id, } -byte TypeFeedbackOracle::ToBooleanTypes(TypeFeedbackId ast_id) { - Handle object = GetInfo(ast_id); +byte TypeFeedbackOracle::ToBooleanTypes(TypeFeedbackId id) { + Handle object = GetInfo(id); return object->IsCode() ? Handle::cast(object)->to_boolean_state() : 0; } +byte TypeFeedbackOracle::CompareNilTypes(TypeFeedbackId id) { + Handle object = GetInfo(id); + if (object->IsCode() && + Handle::cast(object)->is_compare_nil_ic_stub()) { + return Handle::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* 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; diff --git a/src/type-info.h b/src/type-info.h index 583c3fc52..d6d958d56 100644 --- a/src/type-info.h +++ b/src/type-info.h @@ -253,7 +253,8 @@ class TypeFeedbackOracle: public ZoneObject { bool IsForInFastCase(ForInStatement* expr); Handle LoadMonomorphicReceiverType(Property* expr); - Handle StoreMonomorphicReceiverType(TypeFeedbackId ast_id); + Handle StoreMonomorphicReceiverType(TypeFeedbackId id); + Handle 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, diff --git a/src/x64/code-stubs-x64.cc b/src/x64/code-stubs-x64.cc index d40a5bf05..23efa4f0e 100644 --- a/src/x64/code-stubs-x64.cc +++ b/src/x64/code-stubs-x64.cc @@ -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) diff --git a/src/x64/full-codegen-x64.cc b/src/x64/full-codegen-x64.cc index f9651f01e..11366ce49 100644 --- a/src/x64/full-codegen-x64.cc +++ b/src/x64/full-codegen-x64.cc @@ -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 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); -- 2.34.1