From: danno@chromium.org Date: Fri, 5 Jul 2013 10:34:02 +0000 (+0000) Subject: Generate StoreGlobal stubs with Hydrogen X-Git-Tag: upstream/4.7.83~13511 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=345cc98a25f0a667a932bb0ac5b7464223e42e25;p=platform%2Fupstream%2Fv8.git Generate StoreGlobal stubs with Hydrogen - Constants globals are inlined into Hydrogen code using code dependencies that invalidate the Crankshafted code when global PropertyCells or the global object change. - The more general case generates code that is just as good as the hand-written assembly stubs on all platforms. R=rossberg@chromium.org, ulan@chromium.org Committed: http://code.google.com/p/v8/source/detail?r=15419 Review URL: https://codereview.chromium.org/16925008 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@15512 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- diff --git a/src/arm/code-stubs-arm.cc b/src/arm/code-stubs-arm.cc index 6bce220..c5e4db8 100644 --- a/src/arm/code-stubs-arm.cc +++ b/src/arm/code-stubs-arm.cc @@ -237,6 +237,17 @@ void UnaryOpStub::InitializeInterfaceDescriptor( } +void StoreGlobalStub::InitializeInterfaceDescriptor( + Isolate* isolate, + CodeStubInterfaceDescriptor* descriptor) { + static Register registers[] = { r1, r2, r0 }; + descriptor->register_param_count_ = 3; + descriptor->register_params_ = registers; + descriptor->deoptimization_handler_ = + FUNCTION_ADDR(StoreIC_MissFromStubFailure); +} + + #define __ ACCESS_MASM(masm) diff --git a/src/arm/lithium-gap-resolver-arm.cc b/src/arm/lithium-gap-resolver-arm.cc index 352fbb9..902817e 100644 --- a/src/arm/lithium-gap-resolver-arm.cc +++ b/src/arm/lithium-gap-resolver-arm.cc @@ -219,7 +219,6 @@ void LGapResolver::EmitMove(int index) { ASSERT(destination->IsStackSlot()); __ str(source_register, cgen_->ToMemOperand(destination)); } - } else if (source->IsStackSlot()) { MemOperand source_operand = cgen_->ToMemOperand(source); if (destination->IsRegister()) { @@ -255,6 +254,10 @@ void LGapResolver::EmitMove(int index) { } else { __ LoadObject(dst, cgen_->ToHandle(constant_source)); } + } else if (source->IsDoubleRegister()) { + DwVfpRegister result = cgen_->ToDoubleRegister(destination); + double v = cgen_->ToDouble(constant_source); + __ Vmov(result, v, ip); } else { ASSERT(destination->IsStackSlot()); ASSERT(!in_cycle_); // Constant moves happen after all cycles are gone. diff --git a/src/ast.cc b/src/ast.cc index 589bd5a..7da9b33 100644 --- a/src/ast.cc +++ b/src/ast.cc @@ -675,6 +675,7 @@ void CallNew::RecordTypeFeedback(TypeFeedbackOracle* oracle) { if (is_monomorphic_) { target_ = oracle->GetCallNewTarget(this); Object* value = allocation_info_cell_->value(); + ASSERT(!value->IsTheHole()); if (value->IsSmi()) { elements_kind_ = static_cast(Smi::cast(value)->value()); } diff --git a/src/code-stubs-hydrogen.cc b/src/code-stubs-hydrogen.cc index 6f835f7..aed0989 100644 --- a/src/code-stubs-hydrogen.cc +++ b/src/code-stubs-hydrogen.cc @@ -809,7 +809,7 @@ HValue* CodeStubGraphBuilder::BuildCodeInitializedStub() { IfBuilder if_true(this); if_true.If(GetParameter(0), stub->GetTypes()); if_true.Then(); - if_true.Return(graph()->GetConstant1()); + if_true.Return(graph()->GetConstant1()); if_true.Else(); if_true.End(); return graph()->GetConstant0(); @@ -821,4 +821,49 @@ Handle ToBooleanStub::GenerateCode() { } +template <> +HValue* CodeStubGraphBuilder::BuildCodeInitializedStub() { + StoreGlobalStub* stub = casted_stub(); + Handle hole(isolate()->heap()->the_hole_value(), isolate()); + Handle placeholer_value(Smi::FromInt(0), isolate()); + Handle placeholder_cell = + isolate()->factory()->NewPropertyCell(placeholer_value); + + HParameter* receiver = GetParameter(0); + HParameter* value = GetParameter(2); + + if (stub->is_constant()) { + // Assume every store to a constant value changes it. + current_block()->FinishExitWithDeoptimization(HDeoptimize::kUseAll); + set_current_block(NULL); + } else { + HValue* cell = Add(placeholder_cell, Representation::Tagged()); + // Check that the map of the global has not changed. + AddInstruction(HCheckMaps::New(receiver, + Handle(isolate()->heap()->meta_map()), + zone())); + + // Load the payload of the global parameter cell. A hole indicates that the + // property has been deleted and that the store must be handled by the + // runtime. + HObjectAccess access(HObjectAccess::ForCellPayload(isolate())); + HValue* cell_contents = Add(cell, access); + IfBuilder builder(this); + HValue* hole_value = Add(hole, Representation::Tagged()); + builder.If(cell_contents, hole_value); + builder.Then(); + builder.Deopt(); + builder.Else(); + Add(cell, access, value); + builder.End(); + } + return value; +} + + +Handle StoreGlobalStub::GenerateCode() { + return DoGenerateCode(this); +} + + } } // namespace v8::internal diff --git a/src/code-stubs.cc b/src/code-stubs.cc index c6405d0..802e720 100644 --- a/src/code-stubs.cc +++ b/src/code-stubs.cc @@ -85,6 +85,14 @@ Code::Kind CodeStub::GetCodeKind() const { } +Handle CodeStub::GetCodeCopyFromTemplate(Isolate* isolate) { + Handle ic = GetCode(isolate); + ic = isolate->factory()->CopyCode(ic); + RecordCodeGeneration(*ic, isolate); + return ic; +} + + Handle PlatformCodeStub::GenerateCode() { Isolate* isolate = Isolate::Current(); Factory* factory = isolate->factory(); diff --git a/src/code-stubs.h b/src/code-stubs.h index 881218e..0eefe8c 100644 --- a/src/code-stubs.h +++ b/src/code-stubs.h @@ -90,6 +90,7 @@ namespace internal { V(ArrayConstructor) \ V(InternalArrayConstructor) \ V(ProfileEntryHook) \ + V(StoreGlobal) \ /* IC Handler stubs */ \ V(LoadField) \ V(KeyedLoadField) @@ -138,6 +139,8 @@ class CodeStub BASE_EMBEDDED { // Retrieve the code for the stub. Generate the code if needed. Handle GetCode(Isolate* isolate); + // Retrieve the code for the stub, make and return a copy of the code. + Handle GetCodeCopyFromTemplate(Isolate* isolate); static Major MajorKeyFromKey(uint32_t key) { return static_cast(MajorKeyBits::decode(key)); } @@ -523,6 +526,50 @@ class FastNewBlockContextStub : public PlatformCodeStub { int MinorKey() { return slots_; } }; +class StoreGlobalStub : public HydrogenCodeStub { + public: + StoreGlobalStub(StrictModeFlag strict_mode, bool is_constant) { + bit_field_ = StrictModeBits::encode(strict_mode) | + IsConstantBits::encode(is_constant); + } + + virtual Handle GenerateCode(); + + virtual void InitializeInterfaceDescriptor( + Isolate* isolate, + CodeStubInterfaceDescriptor* descriptor); + + virtual Code::Kind GetCodeKind() const { return Code::STORE_IC; } + virtual InlineCacheState GetICState() { return MONOMORPHIC; } + virtual Code::ExtraICState GetExtraICState() { return bit_field_; } + + bool is_constant() { + return IsConstantBits::decode(bit_field_); + } + void set_is_constant(bool value) { + bit_field_ = IsConstantBits::update(bit_field_, value); + } + + Representation representation() { + return Representation::FromKind(RepresentationBits::decode(bit_field_)); + } + void set_representation(Representation r) { + bit_field_ = RepresentationBits::update(bit_field_, r.kind()); + } + + private: + virtual int NotMissMinorKey() { return GetExtraICState(); } + Major MajorKey() { return StoreGlobal; } + + class StrictModeBits: public BitField {}; + class IsConstantBits: public BitField {}; + class RepresentationBits: public BitField {}; + + int bit_field_; + + DISALLOW_COPY_AND_ASSIGN(StoreGlobalStub); +}; + class UnaryOpStub : public HydrogenCodeStub { public: diff --git a/src/heap.cc b/src/heap.cc index f3eb225..90f8837 100644 --- a/src/heap.cc +++ b/src/heap.cc @@ -2855,9 +2855,9 @@ MaybeObject* Heap::AllocateCell(Object* value) { MaybeObject* Heap::AllocatePropertyCell(Object* value) { Object* result; - { MaybeObject* maybe_result = AllocateRawPropertyCell(); - if (!maybe_result->ToObject(&result)) return maybe_result; - } + MaybeObject* maybe_result = AllocateRawPropertyCell(); + if (!maybe_result->ToObject(&result)) return maybe_result; + HeapObject::cast(result)->set_map_no_write_barrier( global_property_cell_map()); PropertyCell* cell = PropertyCell::cast(result); @@ -2865,6 +2865,8 @@ MaybeObject* Heap::AllocatePropertyCell(Object* value) { SKIP_WRITE_BARRIER); cell->set_value(value); cell->set_type(Type::None()); + maybe_result = cell->SetValueInferType(value); + if (maybe_result->IsFailure()) return maybe_result; return result; } diff --git a/src/heap.h b/src/heap.h index f020c7f..d6f41a8 100644 --- a/src/heap.h +++ b/src/heap.h @@ -261,6 +261,7 @@ namespace internal { V(map_field_string, "%map") \ V(elements_field_string, "%elements") \ V(length_field_string, "%length") \ + V(cell_value_string, "%cell_value") \ V(function_class_string, "Function") \ V(properties_field_symbol, "%properties") \ V(payload_field_symbol, "%payload") \ diff --git a/src/hydrogen-instructions.cc b/src/hydrogen-instructions.cc index 859c1ed..f93df27 100644 --- a/src/hydrogen-instructions.cc +++ b/src/hydrogen-instructions.cc @@ -2175,6 +2175,7 @@ HConstant::HConstant(Handle handle, Representation r) has_double_value_(false), is_internalized_string_(false), is_not_in_new_space_(true), + is_cell_(false), boolean_value_(handle->BooleanValue()) { if (handle_->IsHeapObject()) { Heap* heap = Handle::cast(handle)->GetHeap(); @@ -2191,6 +2192,9 @@ HConstant::HConstant(Handle handle, Representation r) type_from_value_ = HType::TypeFromValue(handle_); is_internalized_string_ = handle_->IsInternalizedString(); } + + is_cell_ = !handle_.is_null() && + (handle_->IsCell() || handle_->IsPropertyCell()); Initialize(r); } @@ -2201,6 +2205,7 @@ HConstant::HConstant(Handle handle, HType type, bool is_internalize_string, bool is_not_in_new_space, + bool is_cell, bool boolean_value) : handle_(handle), unique_id_(unique_id), @@ -2209,6 +2214,7 @@ HConstant::HConstant(Handle handle, has_double_value_(false), is_internalized_string_(is_internalize_string), is_not_in_new_space_(is_not_in_new_space), + is_cell_(is_cell), boolean_value_(boolean_value), type_from_value_(type) { ASSERT(!handle.is_null()); @@ -2228,6 +2234,7 @@ HConstant::HConstant(int32_t integer_value, has_double_value_(true), is_internalized_string_(false), is_not_in_new_space_(is_not_in_new_space), + is_cell_(false), boolean_value_(integer_value != 0), int32_value_(integer_value), double_value_(FastI2D(integer_value)) { @@ -2246,6 +2253,7 @@ HConstant::HConstant(double double_value, has_double_value_(true), is_internalized_string_(false), is_not_in_new_space_(is_not_in_new_space), + is_cell_(false), boolean_value_(double_value != 0 && !std::isnan(double_value)), int32_value_(DoubleToInt32(double_value)), double_value_(double_value) { @@ -2268,9 +2276,17 @@ void HConstant::Initialize(Representation r) { } set_representation(r); SetFlag(kUseGVN); - if (representation().IsInteger32()) { - ClearGVNFlag(kDependsOnOsrEntries); +} + + +bool HConstant::EmitAtUses() { + ASSERT(IsLinked()); + if (block()->graph()->has_osr()) { + return block()->graph()->IsStandardConstant(this); } + if (IsCell()) return false; + if (representation().IsDouble()) return false; + return true; } @@ -2291,6 +2307,7 @@ HConstant* HConstant::CopyToRepresentation(Representation r, Zone* zone) const { type_from_value_, is_internalized_string_, is_not_in_new_space_, + is_cell_, boolean_value_); } @@ -3830,6 +3847,13 @@ HObjectAccess HObjectAccess::ForField(Handle map, } +HObjectAccess HObjectAccess::ForCellPayload(Isolate* isolate) { + return HObjectAccess( + kInobject, Cell::kValueOffset, + Handle(isolate->heap()->cell_value_string())); +} + + void HObjectAccess::SetGVNFlags(HValue *instr, bool is_store) { // set the appropriate GVN flags for a given load or store instruction if (is_store) { diff --git a/src/hydrogen-instructions.h b/src/hydrogen-instructions.h index 94f1361..6e10a38 100644 --- a/src/hydrogen-instructions.h +++ b/src/hydrogen-instructions.h @@ -3291,6 +3291,7 @@ class HConstant: public HTemplateInstruction<0> { HType type, bool is_internalized_string, bool is_not_in_new_space, + bool is_cell, bool boolean_value); Handle handle() { @@ -3339,6 +3340,10 @@ class HConstant: public HTemplateInstruction<0> { unique_id_ == UniqueValueId(heap->empty_string()); } + bool IsCell() const { + return is_cell_; + } + virtual Representation RequiredInputRepresentation(int index) { return Representation::None(); } @@ -3350,7 +3355,7 @@ class HConstant: public HTemplateInstruction<0> { return Representation::Tagged(); } - virtual bool EmitAtUses() { return !representation().IsDouble(); } + virtual bool EmitAtUses(); virtual void PrintDataTo(StringStream* stream); virtual HType CalculateInferredType(); bool IsInteger() { return handle()->IsSmi(); } @@ -3465,6 +3470,7 @@ class HConstant: public HTemplateInstruction<0> { bool has_double_value_ : 1; bool is_internalized_string_ : 1; // TODO(yangguo): make this part of HType. bool is_not_in_new_space_ : 1; + bool is_cell_ : 1; bool boolean_value_ : 1; int32_t int32_value_; double double_value_; @@ -5118,6 +5124,9 @@ inline bool ReceiverObjectNeedsWriteBarrier(HValue* object, HInnerAllocatedObject::cast(object)->base_object(), new_space_dominator); } + if (object->IsConstant() && HConstant::cast(object)->IsCell()) { + return false; + } if (object != new_space_dominator) return true; if (object->IsAllocateObject()) return false; if (object->IsAllocate()) { @@ -5379,6 +5388,9 @@ class HObjectAccess { static HObjectAccess ForField(Handle map, LookupResult *lookup, Handle name = Handle::null()); + // Create an access for the payload of a Cell or JSGlobalPropertyCell. + static HObjectAccess ForCellPayload(Isolate* isolate); + void PrintTo(StringStream* stream); inline bool Equals(HObjectAccess that) const { diff --git a/src/hydrogen.cc b/src/hydrogen.cc index 98c1d85..66e8a24 100644 --- a/src/hydrogen.cc +++ b/src/hydrogen.cc @@ -1,4 +1,4 @@ -// Copyright 2012 the V8 project authors. All rights reserved. +// Copyright 2013 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -649,6 +649,7 @@ HConstant* HGraph::GetConstant##Name() { \ htype, \ false, \ true, \ + false, \ boolean_value); \ constant->InsertAfter(GetConstantUndefined()); \ constant_##name##_.set(constant); \ @@ -671,6 +672,19 @@ HConstant* HGraph::GetInvalidContext() { } +bool HGraph::IsStandardConstant(HConstant* constant) { + if (constant == GetConstantUndefined()) return true; + if (constant == GetConstant0()) return true; + if (constant == GetConstant1()) return true; + if (constant == GetConstantMinus1()) return true; + if (constant == GetConstantTrue()) return true; + if (constant == GetConstantFalse()) return true; + if (constant == GetConstantHole()) return true; + if (constant == GetConstantNull()) return true; + return false; +} + + HGraphBuilder::IfBuilder::IfBuilder(HGraphBuilder* builder, int position) : builder_(builder), position_(position), @@ -1005,9 +1019,9 @@ HReturn* HGraphBuilder::AddReturn(HValue* value) { } -void HGraphBuilder::AddSoftDeoptimize() { +void HGraphBuilder::AddSoftDeoptimize(SoftDeoptimizeMode mode) { isolate()->counters()->soft_deopts_requested()->Increment(); - if (FLAG_always_opt) return; + if (FLAG_always_opt && mode == CAN_OMIT_SOFT_DEOPT) return; if (current_block()->IsDeoptimizing()) return; Add(); isolate()->counters()->soft_deopts_inserted()->Increment(); @@ -4998,9 +5012,20 @@ void HOptimizedGraphBuilder::VisitVariableProxy(VariableProxy* expr) { if (type == kUseCell) { Handle global(current_info()->global_object()); Handle cell(global->GetPropertyCell(&lookup)); - HLoadGlobalCell* instr = - new(zone()) HLoadGlobalCell(cell, lookup.GetPropertyDetails()); - return ast_context()->ReturnInstruction(instr, expr->id()); + if (cell->type()->IsConstant()) { + cell->AddDependentCompilationInfo(top_info()); + Handle constant_object = cell->type()->AsConstant(); + if (constant_object->IsConsString()) { + constant_object = + FlattenGetString(Handle::cast(constant_object)); + } + HConstant* constant = new(zone()) HConstant(constant_object); + return ast_context()->ReturnInstruction(constant, expr->id()); + } else { + HLoadGlobalCell* instr = + new(zone()) HLoadGlobalCell(cell, lookup.GetPropertyDetails()); + return ast_context()->ReturnInstruction(instr, expr->id()); + } } else { HValue* context = environment()->LookupContext(); HGlobalObject* global_object = new(zone()) HGlobalObject(context); @@ -5911,8 +5936,21 @@ void HOptimizedGraphBuilder::HandleGlobalVariableAssignment( if (type == kUseCell) { Handle global(current_info()->global_object()); Handle cell(global->GetPropertyCell(&lookup)); - HInstruction* instr = Add(value, cell, - lookup.GetPropertyDetails()); + if (cell->type()->IsConstant()) { + IfBuilder builder(this); + HValue* constant = Add(cell->type()->AsConstant()); + if (cell->type()->AsConstant()->IsNumber()) { + builder.IfCompare(value, constant, Token::EQ); + } else { + builder.If(value, constant); + } + builder.Then(); + builder.Else(); + AddSoftDeoptimize(MUST_EMIT_SOFT_DEOPT); + builder.End(); + } + HInstruction* instr = + Add(value, cell, lookup.GetPropertyDetails()); instr->set_position(position); if (instr->HasObservableSideEffects()) { AddSimulate(ast_id, REMOVABLE_SIMULATE); diff --git a/src/hydrogen.h b/src/hydrogen.h index b3a57c0..e951b19 100644 --- a/src/hydrogen.h +++ b/src/hydrogen.h @@ -333,6 +333,8 @@ class HGraph: public ZoneObject { HConstant* GetConstantNull(); HConstant* GetInvalidContext(); + bool IsStandardConstant(HConstant* constant); + HBasicBlock* CreateBasicBlock(); HArgumentsObject* GetArgumentsObject() const { return arguments_object_.get(); @@ -1131,7 +1133,12 @@ class HGraphBuilder { HValue* AddLoadJSBuiltin(Builtins::JavaScript builtin, HContext* context); - void AddSoftDeoptimize(); + enum SoftDeoptimizeMode { + MUST_EMIT_SOFT_DEOPT, + CAN_OMIT_SOFT_DEOPT + }; + + void AddSoftDeoptimize(SoftDeoptimizeMode mode = CAN_OMIT_SOFT_DEOPT); class IfBuilder { public: diff --git a/src/ia32/code-stubs-ia32.cc b/src/ia32/code-stubs-ia32.cc index b4a06b4..31f4f4b 100644 --- a/src/ia32/code-stubs-ia32.cc +++ b/src/ia32/code-stubs-ia32.cc @@ -241,6 +241,17 @@ void UnaryOpStub::InitializeInterfaceDescriptor( } +void StoreGlobalStub::InitializeInterfaceDescriptor( + Isolate* isolate, + CodeStubInterfaceDescriptor* descriptor) { + static Register registers[] = { edx, ecx, eax }; + descriptor->register_param_count_ = 3; + descriptor->register_params_ = registers; + descriptor->deoptimization_handler_ = + FUNCTION_ADDR(StoreIC_MissFromStubFailure); +} + + #define __ ACCESS_MASM(masm) diff --git a/src/ia32/lithium-codegen-ia32.h b/src/ia32/lithium-codegen-ia32.h index d05da8a..7e66ac2 100644 --- a/src/ia32/lithium-codegen-ia32.h +++ b/src/ia32/lithium-codegen-ia32.h @@ -115,6 +115,7 @@ class LCodeGen BASE_EMBEDDED { Immediate ToSmiImmediate(LOperand* op) const { return Immediate(Smi::FromInt(ToInteger32(LConstantOperand::cast(op)))); } + double ToDouble(LConstantOperand* op) const; // Support for non-sse2 (x87) floating point stack handling. // These functions maintain the depth of the stack (either 0 or 1) @@ -293,7 +294,6 @@ class LCodeGen BASE_EMBEDDED { XMMRegister ToDoubleRegister(int index) const; int ToInteger32(LConstantOperand* op) const; - double ToDouble(LConstantOperand* op) const; Operand BuildFastArrayOperand(LOperand* elements_pointer, LOperand* key, Representation key_representation, diff --git a/src/ia32/lithium-gap-resolver-ia32.cc b/src/ia32/lithium-gap-resolver-ia32.cc index 86bfe2f..a75ce21 100644 --- a/src/ia32/lithium-gap-resolver-ia32.cc +++ b/src/ia32/lithium-gap-resolver-ia32.cc @@ -313,6 +313,31 @@ void LGapResolver::EmitMove(int index) { } else { __ LoadObject(dst, cgen_->ToHandle(constant_source)); } + } else if (destination->IsDoubleRegister()) { + double v = cgen_->ToDouble(constant_source); + uint64_t int_val = BitCast(v); + int32_t lower = static_cast(int_val); + int32_t upper = static_cast(int_val >> kBitsPerInt); + if (CpuFeatures::IsSupported(SSE2)) { + CpuFeatureScope scope(cgen_->masm(), SSE2); + XMMRegister dst = cgen_->ToDoubleRegister(destination); + if (int_val == 0) { + __ xorps(dst, dst); + } else { + __ push(Immediate(upper)); + __ push(Immediate(lower)); + __ movdbl(dst, Operand(esp, 0)); + __ add(esp, Immediate(kDoubleSize)); + } + } else { + __ push(Immediate(upper)); + __ push(Immediate(lower)); + if (cgen_->X87StackNonEmpty()) { + cgen_->PopX87(); + } + cgen_->PushX87DoubleOperand(MemOperand(esp, 0)); + __ add(esp, Immediate(kDoubleSize)); + } } else { ASSERT(destination->IsStackSlot()); Operand dst = cgen_->ToOperand(destination); diff --git a/src/ic.cc b/src/ic.cc index 8c1df53..02f2f0f 100644 --- a/src/ic.cc +++ b/src/ic.cc @@ -1668,7 +1668,7 @@ void StoreIC::UpdateCaches(LookupResult* lookup, ASSERT(!lookup->IsHandler()); Handle code = ComputeStoreMonomorphic( - lookup, strict_mode, receiver, name); + lookup, strict_mode, receiver, name, value); if (code.is_null()) { Handle stub = strict_mode == kStrictMode ? generic_stub_strict() : generic_stub(); @@ -1684,7 +1684,8 @@ void StoreIC::UpdateCaches(LookupResult* lookup, Handle StoreIC::ComputeStoreMonomorphic(LookupResult* lookup, StrictModeFlag strict_mode, Handle receiver, - Handle name) { + Handle name, + Handle value) { Handle holder(lookup->holder()); switch (lookup->type()) { case FIELD: @@ -1699,7 +1700,7 @@ Handle StoreIC::ComputeStoreMonomorphic(LookupResult* lookup, Handle cell( global->GetPropertyCell(lookup), isolate()); return isolate()->stub_cache()->ComputeStoreGlobal( - name, global, cell, strict_mode); + name, global, cell, value, strict_mode); } ASSERT(holder.is_identical_to(receiver)); return isolate()->stub_cache()->ComputeStoreNormal(strict_mode); @@ -2093,7 +2094,8 @@ MaybeObject* KeyedStoreIC::Store(State state, Handle KeyedStoreIC::ComputeStoreMonomorphic(LookupResult* lookup, StrictModeFlag strict_mode, Handle receiver, - Handle name) { + Handle name, + Handle value) { // If the property has a non-field type allowing map transitions // where there is extra room in the object, we leave the IC in its // current state. @@ -2245,6 +2247,20 @@ RUNTIME_FUNCTION(MaybeObject*, StoreIC_Miss) { } +RUNTIME_FUNCTION(MaybeObject*, StoreIC_MissFromStubFailure) { + HandleScope scope(isolate); + ASSERT(args.length() == 3); + StoreIC ic(IC::EXTRA_CALL_FRAME, isolate); + IC::State state = IC::StateFrom(ic.target(), args[0], args[1]); + Code::ExtraICState extra_ic_state = ic.target()->extra_ic_state(); + return ic.Store(state, + Code::GetStrictMode(extra_ic_state), + args.at(0), + args.at(1), + args.at(2)); +} + + RUNTIME_FUNCTION(MaybeObject*, StoreIC_ArrayLength) { SealHandleScope shs(isolate); diff --git a/src/ic.h b/src/ic.h index f6b4800..61bf848 100644 --- a/src/ic.h +++ b/src/ic.h @@ -559,7 +559,8 @@ class StoreIC: public IC { virtual Handle ComputeStoreMonomorphic(LookupResult* lookup, StrictModeFlag strict_mode, Handle receiver, - Handle name); + Handle name, + Handle value); private: void set_target(Code* code) { @@ -626,7 +627,8 @@ class KeyedStoreIC: public StoreIC { virtual Handle ComputeStoreMonomorphic(LookupResult* lookup, StrictModeFlag strict_mode, Handle receiver, - Handle name); + Handle name, + Handle value); virtual void UpdateMegamorphicCache(Map* map, Name* name, Code* code) { } virtual Handle megamorphic_stub() { @@ -819,6 +821,7 @@ void PatchInlinedSmiCode(Address address, InlinedSmiCheck check); DECLARE_RUNTIME_FUNCTION(MaybeObject*, KeyedLoadIC_MissFromStubFailure); DECLARE_RUNTIME_FUNCTION(MaybeObject*, KeyedStoreIC_MissFromStubFailure); DECLARE_RUNTIME_FUNCTION(MaybeObject*, UnaryOpIC_Miss); +DECLARE_RUNTIME_FUNCTION(MaybeObject*, StoreIC_MissFromStubFailure); DECLARE_RUNTIME_FUNCTION(MaybeObject*, CompareNilIC_Miss); DECLARE_RUNTIME_FUNCTION(MaybeObject*, ToBooleanIC_Miss); diff --git a/src/objects-inl.h b/src/objects-inl.h index fe054da..61ae90e 100644 --- a/src/objects-inl.h +++ b/src/objects-inl.h @@ -3787,6 +3787,7 @@ int Code::major_key() { kind() == BINARY_OP_IC || kind() == COMPARE_IC || kind() == COMPARE_NIL_IC || + kind() == STORE_IC || kind() == LOAD_IC || kind() == KEYED_LOAD_IC || kind() == TO_BOOLEAN_IC); diff --git a/src/objects.cc b/src/objects.cc index 2abfa73..ebcc233 100644 --- a/src/objects.cc +++ b/src/objects.cc @@ -630,12 +630,23 @@ Object* JSObject::GetNormalizedProperty(LookupResult* result) { } -Object* JSObject::SetNormalizedProperty(LookupResult* result, Object* value) { +Handle JSObject::SetNormalizedProperty(Handle object, + LookupResult* result, + Handle value) { + CALL_HEAP_FUNCTION(object->GetIsolate(), + object->SetNormalizedProperty(result, *value), + Object); +} + + +MaybeObject* JSObject::SetNormalizedProperty(LookupResult* result, + Object* value) { ASSERT(!HasFastProperties()); if (IsGlobalObject()) { PropertyCell* cell = PropertyCell::cast( property_dictionary()->ValueAt(result->GetDictionaryEntry())); - cell->set_value(value); + MaybeObject* maybe_type = cell->SetValueInferType(value); + if (maybe_type->IsFailure()) return maybe_type; } else { property_dictionary()->ValueAtPut(result->GetDictionaryEntry(), value); } @@ -691,7 +702,8 @@ MaybeObject* JSObject::SetNormalizedProperty(Name* name, if (IsGlobalObject()) { PropertyCell* cell = PropertyCell::cast(property_dictionary()->ValueAt(entry)); - cell->set_value(value); + MaybeObject* maybe_type = cell->SetValueInferType(value); + if (maybe_type->IsFailure()) return maybe_type; // Please note we have to update the property details. property_dictionary()->DetailsAtPut(entry, details); } else { @@ -723,7 +735,9 @@ MaybeObject* JSObject::DeleteNormalizedProperty(Name* name, DeleteMode mode) { set_map(new_map); } PropertyCell* cell = PropertyCell::cast(dictionary->ValueAt(entry)); - cell->set_value(cell->GetHeap()->the_hole_value()); + MaybeObject* maybe_type = + cell->SetValueInferType(cell->GetHeap()->the_hole_value()); + if (maybe_type->IsFailure()) return maybe_type; dictionary->DetailsAtPut(entry, details.AsDeleted()); } else { Object* deleted = dictionary->DeleteProperty(entry, mode); @@ -1930,7 +1944,9 @@ MaybeObject* JSObject::AddSlowProperty(Name* name, int entry = dict->FindEntry(name); if (entry != NameDictionary::kNotFound) { store_value = dict->ValueAt(entry); - PropertyCell::cast(store_value)->set_value(value); + MaybeObject* maybe_type = + PropertyCell::cast(store_value)->SetValueInferType(value); + if (maybe_type->IsFailure()) return maybe_type; // Assign an enumeration index to the property and update // SetNextEnumerationIndex. int index = dict->NextEnumerationIndex(); @@ -1944,7 +1960,9 @@ MaybeObject* JSObject::AddSlowProperty(Name* name, heap->AllocatePropertyCell(value); if (!maybe_store_value->ToObject(&store_value)) return maybe_store_value; } - PropertyCell::cast(store_value)->set_value(value); + MaybeObject* maybe_type = + PropertyCell::cast(store_value)->SetValueInferType(value); + if (maybe_type->IsFailure()) return maybe_type; } PropertyDetails details = PropertyDetails(attributes, NORMAL, 0); Object* result; @@ -15813,6 +15831,46 @@ void PropertyCell::set_type(Type* type, WriteBarrierMode ignored) { } +Type* PropertyCell::UpdateType(Handle cell, + Handle value) { + Isolate* isolate = cell->GetIsolate(); + Handle old_type(cell->type(), isolate); + Handle new_type((value->IsSmi() || value->IsUndefined()) + ? Type::Constant(value, isolate) + : Type::Any(), isolate); + + if (new_type->Is(old_type)) { + return *old_type; + } + + cell->dependent_code()->DeoptimizeDependentCodeGroup( + isolate, DependentCode::kPropertyCellChangedGroup); + + if (old_type->Is(Type::None()) || old_type->Is(Type::Undefined())) { + return *new_type; + } + + return Type::Any(); +} + + +MaybeObject* PropertyCell::SetValueInferType(Object* value, + WriteBarrierMode ignored) { + set_value(value, ignored); + if (!Type::Any()->Is(type())) { + IdempotentPointerToHandleCodeTrampoline trampoline(GetIsolate()); + MaybeObject* maybe_type = trampoline.CallWithReturnValue( + &PropertyCell::UpdateType, + Handle(this), + Handle(value, GetIsolate())); + if (maybe_type->IsFailure()) return maybe_type; + Type* new_type = static_cast(maybe_type); + set_type(new_type); + } + return value; +} + + void PropertyCell::AddDependentCompilationInfo(CompilationInfo* info) { Handle dep(dependent_code()); Handle codes = diff --git a/src/objects.h b/src/objects.h index e875763..eb6d882 100644 --- a/src/objects.h +++ b/src/objects.h @@ -1886,9 +1886,16 @@ class JSObject: public JSReceiver { // Handles the special representation of JS global objects. Object* GetNormalizedProperty(LookupResult* result); + // Sets the property value in a normalized object given (key, value). + // Handles the special representation of JS global objects. + static Handle SetNormalizedProperty(Handle object, + LookupResult* result, + Handle value); + // Sets the property value in a normalized object given a lookup result. // Handles the special representation of JS global objects. - Object* SetNormalizedProperty(LookupResult* result, Object* value); + MUST_USE_RESULT MaybeObject* SetNormalizedProperty(LookupResult* result, + Object* value); // Sets the property value in a normalized object given (key, value, details). // Handles the special representation of JS global objects. @@ -8583,6 +8590,14 @@ class PropertyCell: public Cell { // property. DECL_ACCESSORS(dependent_code, DependentCode) + // Sets the value of the cell and updates the type field to be the union + // of the cell's current type and the value's type. If the change causes + // a change of the type of the cell's contents, code dependent on the cell + // will be deoptimized. + MUST_USE_RESULT MaybeObject* SetValueInferType( + Object* value, + WriteBarrierMode mode = UPDATE_WRITE_BARRIER); + // Casting. static inline PropertyCell* cast(Object* obj); @@ -8610,6 +8625,9 @@ class PropertyCell: public Cell { void AddDependentCode(Handle code); + static Type* UpdateType(Handle cell, + Handle value); + private: DECL_ACCESSORS(type_raw, Object) DISALLOW_IMPLICIT_CONSTRUCTORS(PropertyCell); diff --git a/src/runtime.cc b/src/runtime.cc index 9f0bfb1..81018fe 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -2121,7 +2121,8 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_InitializeConstGlobal) { } else if (lookup.IsNormal()) { if (global->GetNormalizedProperty(&lookup)->IsTheHole() || !lookup.IsReadOnly()) { - global->SetNormalizedProperty(&lookup, *value); + HandleScope scope(isolate); + JSObject::SetNormalizedProperty(Handle(global), &lookup, value); } } else { // Ignore re-initialization of constants that have already been @@ -2210,7 +2211,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_InitializeConstContextSlot) { } } else if (lookup.IsNormal()) { if (object->GetNormalizedProperty(&lookup)->IsTheHole()) { - object->SetNormalizedProperty(&lookup, *value); + JSObject::SetNormalizedProperty(object, &lookup, value); } } else { // We should not reach here. Any real, named property should be diff --git a/src/stub-cache.cc b/src/stub-cache.cc index 1e0882f..0ccd5e6 100644 --- a/src/stub-cache.cc +++ b/src/stub-cache.cc @@ -499,15 +499,30 @@ Handle StubCache::ComputeStoreNormal(StrictModeFlag strict_mode) { Handle StubCache::ComputeStoreGlobal(Handle name, Handle receiver, Handle cell, + Handle value, StrictModeFlag strict_mode) { - Handle stub = FindIC( - name, Handle::cast(receiver), - Code::STORE_IC, Code::NORMAL, strict_mode); - if (!stub.is_null()) return stub; + Isolate* isolate = cell->GetIsolate(); + Handle union_type(PropertyCell::UpdateType(cell, value), isolate); + bool is_constant = union_type->IsConstant(); + StoreGlobalStub stub(strict_mode, is_constant); - StoreStubCompiler compiler(isolate_, strict_mode); - Handle code = compiler.CompileStoreGlobal(receiver, cell, name); + Handle code = FindIC( + name, Handle::cast(receiver), + Code::STORE_IC, Code::NORMAL, stub.GetExtraICState()); + if (!code.is_null()) return code; + + if (is_constant) return stub.GetCode(isolate_); + + // Replace the placeholder cell and global object map with the actual global + // cell and receiver map. + Handle cell_map(isolate_->heap()->global_property_cell_map()); + Handle meta_map(isolate_->heap()->meta_map()); + Handle receiver_map(receiver->map(), isolate_); + code = stub.GetCodeCopyFromTemplate(isolate_); + code->ReplaceNthObject(1, *meta_map, *receiver_map); + code->ReplaceNthObject(1, *cell_map, *cell); JSObject::UpdateMapCodeCache(receiver, name, code); + return code; } @@ -922,12 +937,8 @@ Handle StubCache::ComputeCompareNil(Handle receiver_map, 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); + Handle ic = stub.GetCodeCopyFromTemplate(isolate_); + ic->ReplaceNthObject(1, isolate_->heap()->meta_map(), *receiver_map); if (!receiver_map->is_shared()) { Map::UpdateCodeCache(receiver_map, name, ic); diff --git a/src/stub-cache.h b/src/stub-cache.h index 6d70d34..5317ce8 100644 --- a/src/stub-cache.h +++ b/src/stub-cache.h @@ -184,6 +184,7 @@ class StubCache { Handle ComputeStoreGlobal(Handle name, Handle object, Handle cell, + Handle value, StrictModeFlag strict_mode); Handle ComputeStoreCallback(Handle name, diff --git a/src/x64/code-stubs-x64.cc b/src/x64/code-stubs-x64.cc index 6f16011..e4136ba 100644 --- a/src/x64/code-stubs-x64.cc +++ b/src/x64/code-stubs-x64.cc @@ -1,4 +1,4 @@ -// Copyright 2012 the V8 project authors. All rights reserved. +// Copyright 2013 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -237,6 +237,17 @@ void UnaryOpStub::InitializeInterfaceDescriptor( } +void StoreGlobalStub::InitializeInterfaceDescriptor( + Isolate* isolate, + CodeStubInterfaceDescriptor* descriptor) { + static Register registers[] = { rdx, rcx, rax }; + descriptor->register_param_count_ = 3; + descriptor->register_params_ = registers; + descriptor->deoptimization_handler_ = + FUNCTION_ADDR(StoreIC_MissFromStubFailure); +} + + #define __ ACCESS_MASM(masm) diff --git a/src/x64/lithium-gap-resolver-x64.cc b/src/x64/lithium-gap-resolver-x64.cc index aed4f36..adb1afa 100644 --- a/src/x64/lithium-gap-resolver-x64.cc +++ b/src/x64/lithium-gap-resolver-x64.cc @@ -202,6 +202,20 @@ void LGapResolver::EmitMove(int index) { } else { __ LoadObject(dst, cgen_->ToHandle(constant_source)); } + } else if (destination->IsDoubleRegister()) { + double v = cgen_->ToDouble(constant_source); + uint64_t int_val = BitCast(v); + int32_t lower = static_cast(int_val); + int32_t upper = static_cast(int_val >> (kBitsPerInt)); + XMMRegister dst = cgen_->ToDoubleRegister(destination); + if (int_val == 0) { + __ xorps(dst, dst); + } else { + __ push(Immediate(upper)); + __ push(Immediate(lower)); + __ movsd(dst, Operand(rsp, 0)); + __ addq(rsp, Immediate(kDoubleSize)); + } } else { ASSERT(destination->IsStackSlot()); Operand dst = cgen_->ToOperand(destination); diff --git a/test/mjsunit/regress/regress-2618.js b/test/mjsunit/regress/regress-2618.js index 638b71e..99f05ca 100644 --- a/test/mjsunit/regress/regress-2618.js +++ b/test/mjsunit/regress/regress-2618.js @@ -30,7 +30,7 @@ function f() { do { do { - for (i = 0; i < 10000000; i++) { + for (var i = 0; i < 10000000; i++) { // This should run long enough to trigger OSR. } } while (false); @@ -46,7 +46,7 @@ function g() { do { do { - for (i = 0; i < 1; i++) { } + for (var i = 0; i < 1; i++) { } } while (false); } while (false); @@ -58,7 +58,7 @@ function g() { do { do { do { - for (i = 0; i < 10000000; i++) { } + for (var i = 0; i < 10000000; i++) { } } while (false); } while (false); } while (false);