diff options
Diffstat (limited to 'deps/v8/src/hydrogen.cc')
-rw-r--r-- | deps/v8/src/hydrogen.cc | 2256 |
1 files changed, 699 insertions, 1557 deletions
diff --git a/deps/v8/src/hydrogen.cc b/deps/v8/src/hydrogen.cc index 097216ef8..b2badcdb5 100644 --- a/deps/v8/src/hydrogen.cc +++ b/deps/v8/src/hydrogen.cc @@ -26,6 +26,7 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "hydrogen.h" +#include "hydrogen-gvn.h" #include <algorithm> @@ -33,11 +34,13 @@ #include "codegen.h" #include "full-codegen.h" #include "hashmap.h" +#include "hydrogen-environment-liveness.h" #include "lithium-allocator.h" #include "parser.h" #include "scopeinfo.h" #include "scopes.h" #include "stub-cache.h" +#include "typing.h" #if V8_TARGET_ARCH_IA32 #include "ia32/lithium-codegen-ia32.h" @@ -71,6 +74,7 @@ HBasicBlock::HBasicBlock(HGraph* graph) last_instruction_index_(-1), deleted_phis_(4, graph->zone()), parent_loop_header_(NULL), + inlined_entry_block_(NULL), is_inline_return_target_(false), is_deoptimizing_(false), dominates_loop_successors_(false), @@ -130,10 +134,13 @@ HDeoptimize* HBasicBlock::CreateDeoptimize( HDeoptimize::UseEnvironment has_uses) { ASSERT(HasEnvironment()); if (has_uses == HDeoptimize::kNoUses) - return new(zone()) HDeoptimize(0, zone()); + return new(zone()) HDeoptimize(0, 0, 0, zone()); HEnvironment* environment = last_environment(); - HDeoptimize* instr = new(zone()) HDeoptimize(environment->length(), zone()); + int first_local_index = environment->first_local_index(); + int first_expression_index = environment->first_expression_index(); + HDeoptimize* instr = new(zone()) HDeoptimize( + environment->length(), first_local_index, first_expression_index, zone()); for (int i = 0; i < environment->length(); i++) { HValue* val = environment->values()->at(i); instr->AddEnvironmentValue(val, zone()); @@ -156,8 +163,11 @@ HSimulate* HBasicBlock::CreateSimulate(BailoutId ast_id, HSimulate* instr = new(zone()) HSimulate(ast_id, pop_count, zone(), removable); +#ifdef DEBUG + instr->set_closure(environment->closure()); +#endif // Order of pushed values: newest (top of stack) first. This allows - // HSimulate::MergeInto() to easily append additional pushed values + // HSimulate::MergeWith() to easily append additional pushed values // that are older (from further down the stack). for (int i = 0; i < push_count; ++i) { instr->AddPushedValue(environment->ExpressionStackAt(i)); @@ -192,7 +202,7 @@ void HBasicBlock::Goto(HBasicBlock* block, if (block->IsInlineReturnTarget()) { AddInstruction(new(zone()) HLeaveInlined()); - last_environment_ = last_environment()->DiscardInlined(drop_extra); + UpdateEnvironment(last_environment()->DiscardInlined(drop_extra)); } if (add_simulate) AddSimulate(BailoutId::None()); @@ -209,7 +219,7 @@ void HBasicBlock::AddLeaveInlined(HValue* return_value, ASSERT(target->IsInlineReturnTarget()); ASSERT(return_value != NULL); AddInstruction(new(zone()) HLeaveInlined()); - last_environment_ = last_environment()->DiscardInlined(drop_extra); + UpdateEnvironment(last_environment()->DiscardInlined(drop_extra)); last_environment()->Push(return_value); AddSimulate(BailoutId::None()); HGoto* instr = new(zone()) HGoto(target); @@ -224,6 +234,12 @@ void HBasicBlock::SetInitialEnvironment(HEnvironment* env) { } +void HBasicBlock::UpdateEnvironment(HEnvironment* env) { + last_environment_ = env; + graph()->update_maximum_environment_size(env->first_expression_index()); +} + + void HBasicBlock::SetJoinId(BailoutId ast_id) { int length = predecessors_.length(); ASSERT(length > 0); @@ -511,7 +527,8 @@ class ReachabilityAnalyzer BASE_EMBEDDED { void HGraph::Verify(bool do_full_verify) const { Heap::RelocationLock(isolate()->heap()); - ALLOW_HANDLE_DEREF(isolate(), "debug mode verification"); + AllowHandleDereference allow_deref; + AllowDeferredHandleDereference allow_deferred_deref; for (int i = 0; i < blocks_.length(); i++) { HBasicBlock* block = blocks_.at(i); @@ -591,24 +608,10 @@ void HGraph::Verify(bool do_full_verify) const { #endif -HConstant* HGraph::GetConstantInt32(SetOncePointer<HConstant>* pointer, - int32_t value) { +HConstant* HGraph::GetConstant(SetOncePointer<HConstant>* pointer, + int32_t value) { if (!pointer->is_set()) { - HConstant* constant = - new(zone()) HConstant(value, Representation::Integer32()); - constant->InsertAfter(GetConstantUndefined()); - pointer->set(constant); - } - return pointer->get(); -} - - -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()); + HConstant* constant = new(zone()) HConstant(value); constant->InsertAfter(GetConstantUndefined()); pointer->set(constant); } @@ -617,17 +620,17 @@ HConstant* HGraph::GetConstantSmi(SetOncePointer<HConstant>* pointer, HConstant* HGraph::GetConstant0() { - return GetConstantInt32(&constant_0_, 0); + return GetConstant(&constant_0_, 0); } HConstant* HGraph::GetConstant1() { - return GetConstantInt32(&constant_1_, 1); + return GetConstant(&constant_1_, 1); } HConstant* HGraph::GetConstantMinus1() { - return GetConstantInt32(&constant_minus1_, -1); + return GetConstant(&constant_minus1_, -1); } @@ -655,21 +658,11 @@ 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 HConstant* HGraph::GetInvalidContext() { - return GetConstantInt32(&constant_invalid_context_, 0xFFFFC0C7); + return GetConstant(&constant_invalid_context_, 0xFFFFC0C7); } @@ -731,8 +724,7 @@ HInstruction* HGraphBuilder::IfBuilder::IfCompare( HInstruction* HGraphBuilder::IfBuilder::IfCompareMap(HValue* left, Handle<Map> map) { HCompareMap* compare = - new(zone()) HCompareMap(left, map, - first_true_block_, first_false_block_); + new(zone()) HCompareMap(left, map, first_true_block_, first_false_block_); AddCompare(compare); return compare; } @@ -811,9 +803,16 @@ void HGraphBuilder::IfBuilder::Then() { did_then_ = true; if (needs_compare_) { // Handle if's without any expressions, they jump directly to the "else" - // branch. - builder_->current_block()->GotoNoSimulate(first_false_block_); - first_true_block_ = NULL; + // branch. However, we must pretend that the "then" branch is reachable, + // so that the graph builder visits it and sees any live range extending + // constructs within it. + HConstant* constant_false = builder_->graph()->GetConstantFalse(); + ToBooleanStub::Types boolean_type = ToBooleanStub::no_types(); + boolean_type.Add(ToBooleanStub::BOOLEAN); + HBranch* branch = + new(zone()) HBranch(constant_false, first_true_block_, + first_false_block_, boolean_type); + builder_->current_block()->Finish(branch); } builder_->set_current_block(first_true_block_); } @@ -833,6 +832,7 @@ void HGraphBuilder::IfBuilder::Else() { void HGraphBuilder::IfBuilder::Deopt() { HBasicBlock* block = builder_->current_block(); block->FinishExitWithDeoptimization(HDeoptimize::kUseAll); + builder_->set_current_block(NULL); if (did_else_) { first_false_block_ = NULL; } else { @@ -843,9 +843,10 @@ 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())); + HValue* context = builder_->environment()->LookupContext(); + HValue* parameter_count = builder_->graph()->GetConstantMinus1(); + block->FinishExit(new(zone()) HReturn(value, context, parameter_count)); + builder_->set_current_block(NULL); if (did_else_) { first_false_block_ = NULL; } else { @@ -898,13 +899,11 @@ HGraphBuilder::LoopBuilder::LoopBuilder(HGraphBuilder* builder, HValue* HGraphBuilder::LoopBuilder::BeginBody( HValue* initial, HValue* terminating, - Token::Value token, - Representation input_representation) { + Token::Value token) { HEnvironment* env = builder_->environment(); phi_ = new(zone()) HPhi(env->values()->length(), zone()); header_block_->AddPhi(phi_); phi_->AddInput(initial); - phi_->AssumeRepresentation(Representation::Integer32()); env->Push(initial); builder_->current_block()->GotoNoSimulate(header_block_); @@ -918,9 +917,6 @@ HValue* HGraphBuilder::LoopBuilder::BeginBody( builder_->set_current_block(header_block_); HCompareIDAndBranch* compare = new(zone()) HCompareIDAndBranch(phi_, terminating, token); - compare->set_observed_input_representation(input_representation, - input_representation); - compare->AssumeRepresentation(input_representation); compare->SetSuccessorAt(0, body_block_); compare->SetSuccessorAt(1, exit_block_); builder_->current_block()->Finish(compare); @@ -934,7 +930,6 @@ HValue* HGraphBuilder::LoopBuilder::BeginBody( increment_ = HSub::New(zone(), context_, phi_, one); } increment_->ClearFlag(HValue::kCanOverflow); - increment_->AssumeRepresentation(Representation::Integer32()); builder_->AddInstruction(increment_); return increment_; } else { @@ -954,7 +949,6 @@ void HGraphBuilder::LoopBuilder::EndBody() { increment_ = HSub::New(zone(), context_, phi_, one); } increment_->ClearFlag(HValue::kCanOverflow); - increment_->AssumeRepresentation(Representation::Integer32()); builder_->AddInstruction(increment_); } @@ -999,20 +993,8 @@ void HGraphBuilder::AddSimulate(BailoutId id, } -HBoundsCheck* HGraphBuilder::AddBoundsCheck(HValue* index, - HValue* length, - BoundsCheckKeyMode key_mode, - Representation r) { - if (!index->type().IsSmi()) { - index = new(graph()->zone()) HCheckSmiOrInt32(index); - AddInstruction(HCheckSmiOrInt32::cast(index)); - } - if (!length->type().IsSmi()) { - length = new(graph()->zone()) HCheckSmiOrInt32(length); - AddInstruction(HCheckSmiOrInt32::cast(length)); - } - HBoundsCheck* result = new(graph()->zone()) HBoundsCheck( - index, length, key_mode, r); +HBoundsCheck* HGraphBuilder::AddBoundsCheck(HValue* index, HValue* length) { + HBoundsCheck* result = new(graph()->zone()) HBoundsCheck(index, length); AddInstruction(result); return result; } @@ -1130,11 +1112,6 @@ HInstruction* HGraphBuilder::BuildFastElementAccess( switch (elements_kind) { case FAST_SMI_ELEMENTS: case FAST_HOLEY_SMI_ELEMENTS: - if (!val->type().IsSmi()) { - // Smi-only arrays need a smi check. - AddInstruction(new(zone) HCheckSmi(val)); - } - // Fall through. case FAST_ELEMENTS: case FAST_HOLEY_ELEMENTS: case FAST_DOUBLE_ELEMENTS: @@ -1195,21 +1172,15 @@ HValue* HGraphBuilder::BuildCheckForCapacityGrow(HValue* object, new_length->AssumeRepresentation(Representation::Integer32()); new_length->ClearFlag(HValue::kCanOverflow); - Factory* factory = isolate()->factory(); Representation representation = IsFastElementsKind(kind) ? Representation::Smi() : Representation::Tagged(); - HInstruction* length_store = AddInstruction(new(zone) HStoreNamedField( - object, - factory->length_field_string(), - new_length, true, - representation, - JSArray::kLengthOffset)); - length_store->SetGVNFlag(kChangesArrayLengths); + AddStore(object, HObjectAccess::ForArrayLength(), new_length, + representation); } length_checker.Else(); - AddBoundsCheck(key, length, ALLOW_SMI_KEY); + AddBoundsCheck(key, length); environment()->Push(elements); length_checker.End(); @@ -1258,8 +1229,7 @@ HInstruction* HGraphBuilder::BuildUncheckedMonomorphicElementAccess( ElementsKind elements_kind, bool is_store, LoadKeyedHoleMode load_mode, - KeyedAccessStoreMode store_mode, - Representation checked_index_representation) { + KeyedAccessStoreMode store_mode) { ASSERT(!IsExternalArrayElementsKind(elements_kind) || !is_js_array); Zone* zone = this->zone(); // No GVNFlag is necessary for ElementsKind if there is an explicit dependency @@ -1286,8 +1256,9 @@ HInstruction* HGraphBuilder::BuildUncheckedMonomorphicElementAccess( } HInstruction* length = NULL; if (is_js_array) { - length = AddInstruction( - HLoadNamedField::NewArrayLength(zone, object, mapcheck, HType::Smi())); + length = AddLoad(object, HObjectAccess::ForArrayLength(), mapcheck, + Representation::Smi()); + length->set_type(HType::Smi()); } else { length = AddInstruction(new(zone) HFixedArrayBaseLength(elements)); } @@ -1314,8 +1285,7 @@ HInstruction* HGraphBuilder::BuildUncheckedMonomorphicElementAccess( return result; } else { ASSERT(store_mode == STANDARD_STORE); - checked_key = AddBoundsCheck( - key, length, ALLOW_SMI_KEY, checked_index_representation); + checked_key = AddBoundsCheck(key, length); HLoadExternalArrayPointer* external_elements = new(zone) HLoadExternalArrayPointer(elements); AddInstruction(external_elements); @@ -1328,24 +1298,22 @@ HInstruction* HGraphBuilder::BuildUncheckedMonomorphicElementAccess( fast_elements || IsFastDoubleElementsKind(elements_kind)); + // In case val is stored into a fast smi array, assure that the value is a smi + // before manipulating the backing store. Otherwise the actual store may + // deopt, leaving the backing store in an invalid state. if (is_store && IsFastSmiElementsKind(elements_kind) && !val->type().IsSmi()) { - AddInstruction(new(zone) HCheckSmi(val)); + val = AddInstruction(new(zone) HForceRepresentation( + val, Representation::Smi())); } if (IsGrowStoreMode(store_mode)) { NoObservableSideEffectsScope no_effects(this); - elements = BuildCheckForCapacityGrow(object, elements, elements_kind, length, key, is_js_array); - if (!key->type().IsSmi()) { - checked_key = AddInstruction(new(zone) HCheckSmiOrInt32(key)); - } else { - checked_key = key; - } + checked_key = key; } else { - checked_key = AddBoundsCheck( - key, length, ALLOW_SMI_KEY, checked_index_representation); + checked_key = AddBoundsCheck(key, length); if (is_store && (fast_elements || fast_smi_only_elements)) { if (store_mode == STORE_NO_TRANSITION_HANDLE_COW) { @@ -1391,7 +1359,7 @@ HValue* HGraphBuilder::BuildAllocateElements(HValue* context, total_size->ClearFlag(HValue::kCanOverflow); HAllocate::Flags flags = HAllocate::DefaultFlags(kind); - if (FLAG_pretenure_literals) { + if (isolate()->heap()->ShouldGloballyPretenure()) { // TODO(hpayer): When pretenuring can be internalized, flags can become // private to HAllocate. if (IsFastDoubleElementsKind(kind)) { @@ -1410,32 +1378,28 @@ HValue* HGraphBuilder::BuildAllocateElements(HValue* context, } -void HGraphBuilder::BuildInitializeElements(HValue* elements, - ElementsKind kind, - HValue* capacity) { - Zone* zone = this->zone(); +void HGraphBuilder::BuildInitializeElementsHeader(HValue* elements, + ElementsKind kind, + HValue* capacity) { Factory* factory = isolate()->factory(); Handle<Map> map = IsFastDoubleElementsKind(kind) ? factory->fixed_double_array_map() : factory->fixed_array_map(); - BuildStoreMap(elements, map); - Handle<String> fixed_array_length_field_name = factory->length_field_string(); + AddStoreMapConstant(elements, map); Representation representation = IsFastElementsKind(kind) ? Representation::Smi() : Representation::Tagged(); - HInstruction* store_length = - new(zone) HStoreNamedField(elements, fixed_array_length_field_name, - capacity, true, representation, - FixedArray::kLengthOffset); - AddInstruction(store_length); + AddStore(elements, HObjectAccess::ForFixedArrayLength(), capacity, + representation); } -HValue* HGraphBuilder::BuildAllocateAndInitializeElements(HValue* context, - ElementsKind kind, - HValue* capacity) { +HValue* HGraphBuilder::BuildAllocateElementsAndInitializeElementsHeader( + HValue* context, + ElementsKind kind, + HValue* capacity) { HValue* new_elements = BuildAllocateElements(context, kind, capacity); - BuildInitializeElements(new_elements, kind, capacity); + BuildInitializeElementsHeader(new_elements, kind, capacity); return new_elements; } @@ -1446,7 +1410,7 @@ HInnerAllocatedObject* HGraphBuilder::BuildJSArrayHeader(HValue* array, HValue* allocation_site_payload, HValue* length_field) { - BuildStoreMap(array, array_map); + AddStore(array, HObjectAccess::ForMap(), array_map); HConstant* empty_fixed_array = new(zone()) HConstant( @@ -1454,21 +1418,9 @@ HInnerAllocatedObject* HGraphBuilder::BuildJSArrayHeader(HValue* array, Representation::Tagged()); AddInstruction(empty_fixed_array); - AddInstruction(new(zone()) HStoreNamedField(array, - isolate()->factory()->properties_field_symbol(), - empty_fixed_array, - true, - Representation::Tagged(), - JSArray::kPropertiesOffset)); - - HInstruction* length_store = AddInstruction( - new(zone()) HStoreNamedField(array, - isolate()->factory()->length_field_string(), - length_field, - true, - Representation::Tagged(), - JSArray::kLengthOffset)); - length_store->SetGVNFlag(kChangesArrayLengths); + HObjectAccess access = HObjectAccess::ForPropertiesPointer(); + AddStore(array, access, empty_fixed_array); + AddStore(array, HObjectAccess::ForArrayLength(), length_field); if (mode == TRACK_ALLOCATION_SITE) { BuildCreateAllocationSiteInfo(array, @@ -1482,58 +1434,17 @@ HInnerAllocatedObject* HGraphBuilder::BuildJSArrayHeader(HValue* array, } HInnerAllocatedObject* elements = new(zone()) HInnerAllocatedObject( - array, - elements_location); + array, elements_location); AddInstruction(elements); - HInstruction* elements_store = AddInstruction( - new(zone()) HStoreNamedField( - array, - isolate()->factory()->elements_field_string(), - elements, - true, - Representation::Tagged(), - JSArray::kElementsOffset)); - elements_store->SetGVNFlag(kChangesElementsPointer); - + AddStore(array, HObjectAccess::ForElementsPointer(), elements); return elements; } -HInstruction* HGraphBuilder::BuildStoreMap(HValue* object, - HValue* map) { - Zone* zone = this->zone(); - Factory* factory = isolate()->factory(); - Handle<String> map_field_name = factory->map_field_string(); - HInstruction* store_map = - new(zone) HStoreNamedField(object, map_field_name, map, - true, Representation::Tagged(), - JSObject::kMapOffset); - store_map->ClearGVNFlag(kChangesInobjectFields); - store_map->SetGVNFlag(kChangesMaps); - AddInstruction(store_map); - return store_map; -} - - -HInstruction* HGraphBuilder::BuildStoreMap(HValue* object, - Handle<Map> map) { - Zone* zone = this->zone(); - HValue* map_constant = - AddInstruction(new(zone) HConstant(map, Representation::Tagged())); - return BuildStoreMap(object, map_constant); -} - - HLoadNamedField* HGraphBuilder::AddLoadElements(HValue* object, - HValue* typecheck) { - HLoadNamedField* instr = new(zone()) HLoadNamedField(object, true, - Representation::Tagged(), JSObject::kElementsOffset, typecheck); - AddInstruction(instr); - instr->SetGVNFlag(kDependsOnElementsPointer); - instr->ClearGVNFlag(kDependsOnMaps); - instr->ClearGVNFlag(kDependsOnInobjectFields); - return instr; + HValue* typecheck) { + return AddLoad(object, HObjectAccess::ForElementsPointer(), typecheck); } @@ -1568,16 +1479,13 @@ void HGraphBuilder::BuildNewSpaceArrayCheck(HValue* length, ElementsKind kind) { Heap* heap = isolate()->heap(); int element_size = IsFastDoubleElementsKind(kind) ? kDoubleSize : kPointerSize; - int max_size = heap->MaxNewSpaceAllocationSize() / element_size; + int max_size = heap->MaxRegularSpaceAllocationSize() / element_size; max_size -= JSArray::kSize / element_size; - HConstant* max_size_constant = - new(zone) HConstant(max_size, Representation::Integer32()); + HConstant* max_size_constant = new(zone) HConstant(max_size); AddInstruction(max_size_constant); // Since we're forcing Integer32 representation for this HBoundsCheck, // there's no need to Smi-check the index. - AddInstruction(new(zone) - HBoundsCheck(length, max_size_constant, - DONT_ALLOW_SMI_KEY, Representation::Integer32())); + AddInstruction(new(zone) HBoundsCheck(length, max_size_constant)); } @@ -1586,25 +1494,18 @@ HValue* HGraphBuilder::BuildGrowElementsCapacity(HValue* object, ElementsKind kind, HValue* length, HValue* new_capacity) { - Zone* zone = this->zone(); HValue* context = environment()->LookupContext(); BuildNewSpaceArrayCheck(new_capacity, kind); - HValue* new_elements = - BuildAllocateAndInitializeElements(context, kind, new_capacity); + HValue* new_elements = BuildAllocateElementsAndInitializeElementsHeader( + context, kind, new_capacity); BuildCopyElements(context, elements, kind, new_elements, kind, length, new_capacity); - Factory* factory = isolate()->factory(); - HInstruction* elements_store = AddInstruction(new(zone) HStoreNamedField( - object, - factory->elements_field_string(), - new_elements, true, Representation::Tagged(), - JSArray::kElementsOffset)); - elements_store->SetGVNFlag(kChangesElementsPointer); + AddStore(object, HObjectAccess::ForElementsPointer(), new_elements); return new_elements; } @@ -1644,10 +1545,15 @@ void HGraphBuilder::BuildFillElementsWithHole(HValue* context, } } + // Since we're about to store a hole value, the store instruction below must + // assume an elements kind that supports heap object values. + if (IsFastSmiOrObjectElementsKind(elements_kind)) { + elements_kind = FAST_HOLEY_ELEMENTS; + } + if (unfold_loop) { for (int i = 0; i < initial_capacity; i++) { - HInstruction* key = AddInstruction(new(zone) - HConstant(i, Representation::Integer32())); + HInstruction* key = AddInstruction(new(zone) HConstant(i)); AddInstruction(new(zone) HStoreKeyed(elements, key, hole, elements_kind)); } } else { @@ -1690,8 +1596,14 @@ void HGraphBuilder::BuildCopyElements(HValue* context, from_elements_kind, ALLOW_RETURN_HOLE)); - AddInstruction(new(zone()) HStoreKeyed(to_elements, key, element, - to_elements_kind)); + ElementsKind holey_kind = IsFastSmiElementsKind(to_elements_kind) + ? FAST_HOLEY_ELEMENTS : to_elements_kind; + HInstruction* holey_store = AddInstruction( + new(zone()) HStoreKeyed(to_elements, key, element, holey_kind)); + // Allow NaN hole values to converted to their tagged counterparts. + if (IsFastHoleyElementsKind(to_elements_kind)) { + holey_store->SetFlag(HValue::kAllowUndefinedAsNaN); + } builder.EndBody(); @@ -1709,7 +1621,6 @@ HValue* HGraphBuilder::BuildCloneShallowArray(HContext* context, ElementsKind kind, int length) { Zone* zone = this->zone(); - Factory* factory = isolate()->factory(); NoObservableSideEffectsScope no_effects(this); @@ -1739,16 +1650,8 @@ HValue* HGraphBuilder::BuildCloneShallowArray(HContext* context, // Copy the JS array part. for (int i = 0; i < JSArray::kSize; i += kPointerSize) { if ((i != JSArray::kElementsOffset) || (length == 0)) { - HInstruction* value = AddInstruction(new(zone) HLoadNamedField( - boilerplate, true, Representation::Tagged(), i)); - if (i != JSArray::kMapOffset) { - AddInstruction(new(zone) HStoreNamedField(object, - factory->empty_string(), - value, true, - Representation::Tagged(), i)); - } else { - BuildStoreMap(object, value); - } + HObjectAccess access = HObjectAccess::ForJSArrayOffset(i); + AddStore(object, access, AddLoad(boilerplate, access)); } } @@ -1763,21 +1666,12 @@ HValue* HGraphBuilder::BuildCloneShallowArray(HContext* context, HValue* boilerplate_elements = AddLoadElements(boilerplate); HValue* object_elements = AddInstruction(new(zone) HInnerAllocatedObject(object, elems_offset)); - AddInstruction(new(zone) HStoreNamedField(object, - factory->elements_field_string(), - object_elements, true, - Representation::Tagged(), - JSObject::kElementsOffset)); + AddStore(object, HObjectAccess::ForElementsPointer(), object_elements); // Copy the elements array header. for (int i = 0; i < FixedArrayBase::kHeaderSize; i += kPointerSize) { - HInstruction* value = - AddInstruction(new(zone) HLoadNamedField( - boilerplate_elements, true, Representation::Tagged(), i)); - AddInstruction(new(zone) HStoreNamedField(object_elements, - factory->empty_string(), - value, true, - Representation::Tagged(), i)); + HObjectAccess access = HObjectAccess::ForFixedArrayHeader(i); + AddStore(object_elements, access, AddLoad(boilerplate_elements, access)); } // Copy the elements array contents. @@ -1785,8 +1679,7 @@ HValue* HGraphBuilder::BuildCloneShallowArray(HContext* context, // copying loops with constant length up to a given boundary and use this // helper here instead. for (int i = 0; i < length; i++) { - HValue* key_constant = - AddInstruction(new(zone) HConstant(i, Representation::Integer32())); + HValue* key_constant = AddInstruction(new(zone) HConstant(i)); HInstruction* value = AddInstruction(new(zone) HLoadKeyed(boilerplate_elements, key_constant, @@ -1805,7 +1698,6 @@ HValue* HGraphBuilder::BuildCloneShallowArray(HContext* context, void HGraphBuilder::BuildCompareNil( HValue* value, - EqualityKind kind, CompareNilICStub::Types types, Handle<Map> map, int position, @@ -1840,9 +1732,7 @@ void HGraphBuilder::BuildCompareNil( // emitted below is the actual monomorphic map. BuildCheckMap(value, map); } else { - if (kind == kNonStrictEquality) { - if_nil.Deopt(); - } + if_nil.Deopt(); } } @@ -1857,61 +1747,82 @@ HValue* HGraphBuilder::BuildCreateAllocationSiteInfo(HValue* previous_object, HInnerAllocatedObject(previous_object, previous_object_size); AddInstruction(alloc_site); Handle<Map> alloc_site_map(isolate()->heap()->allocation_site_info_map()); - BuildStoreMap(alloc_site, alloc_site_map); - AddInstruction(new(zone()) HStoreNamedField(alloc_site, - isolate()->factory()->payload_string(), - payload, - true, - Representation::Tagged(), - AllocationSiteInfo::kPayloadOffset)); + AddStoreMapConstant(alloc_site, alloc_site_map); + HObjectAccess access = HObjectAccess::ForAllocationSitePayload(); + AddStore(alloc_site, access, payload); return alloc_site; } HInstruction* HGraphBuilder::BuildGetNativeContext(HValue* context) { + // Get the global context, then the native context HInstruction* global_object = AddInstruction(new(zone()) - HGlobalObject(context)); - HInstruction* native_context = AddInstruction(new(zone()) - HLoadNamedField(global_object, true, Representation::Tagged(), - GlobalObject::kNativeContextOffset)); - return native_context; + HGlobalObject(context)); + HObjectAccess access = HObjectAccess::ForJSObjectOffset( + GlobalObject::kNativeContextOffset); + return AddLoad(global_object, access); } HInstruction* HGraphBuilder::BuildGetArrayFunction(HValue* context) { HInstruction* native_context = BuildGetNativeContext(context); - int offset = Context::kHeaderSize + - kPointerSize * Context::ARRAY_FUNCTION_INDEX; - HInstruction* array_function = AddInstruction(new(zone()) - HLoadNamedField(native_context, true, Representation::Tagged(), offset)); - return array_function; + HInstruction* index = AddInstruction(new(zone()) + HConstant(Context::ARRAY_FUNCTION_INDEX, Representation::Integer32())); + + return AddInstruction(new (zone()) + HLoadKeyed(native_context, index, NULL, FAST_ELEMENTS)); } HGraphBuilder::JSArrayBuilder::JSArrayBuilder(HGraphBuilder* builder, ElementsKind kind, HValue* allocation_site_payload, - AllocationSiteMode mode) : + bool disable_allocation_sites) : builder_(builder), kind_(kind), - allocation_site_payload_(allocation_site_payload) { - if (mode == DONT_TRACK_ALLOCATION_SITE) { - mode_ = mode; - } else { - mode_ = AllocationSiteInfo::GetMode(kind); - } + allocation_site_payload_(allocation_site_payload), + constructor_function_(NULL) { + mode_ = disable_allocation_sites + ? DONT_TRACK_ALLOCATION_SITE + : AllocationSiteInfo::GetMode(kind); +} + + +HGraphBuilder::JSArrayBuilder::JSArrayBuilder(HGraphBuilder* builder, + ElementsKind kind, + HValue* constructor_function) : + builder_(builder), + kind_(kind), + mode_(DONT_TRACK_ALLOCATION_SITE), + allocation_site_payload_(NULL), + constructor_function_(constructor_function) { } HValue* HGraphBuilder::JSArrayBuilder::EmitMapCode(HValue* context) { HInstruction* native_context = builder()->BuildGetNativeContext(context); - int offset = Context::kHeaderSize + - kPointerSize * Context::JS_ARRAY_MAPS_INDEX; - HInstruction* map_array = AddInstruction(new(zone()) - HLoadNamedField(native_context, true, Representation::Tagged(), offset)); - offset = kind_ * kPointerSize + FixedArrayBase::kHeaderSize; - return AddInstruction(new(zone()) HLoadNamedField( - map_array, true, Representation::Tagged(), offset)); + + HInstruction* index = builder()->AddInstruction(new(zone()) + HConstant(Context::JS_ARRAY_MAPS_INDEX, Representation::Integer32())); + + HInstruction* map_array = builder()->AddInstruction(new(zone()) + HLoadKeyed(native_context, index, NULL, FAST_ELEMENTS)); + + HInstruction* kind_index = builder()->AddInstruction(new(zone()) + HConstant(kind_, Representation::Integer32())); + + return builder()->AddInstruction(new(zone()) + HLoadKeyed(map_array, kind_index, NULL, FAST_ELEMENTS)); +} + + +HValue* HGraphBuilder::JSArrayBuilder::EmitInternalMapCode() { + // Find the map near the constructor function + HObjectAccess access = HObjectAccess::ForPrototypeOrInitialMap(); + return AddInstruction( + builder()->BuildLoadNamedField(constructor_function_, + access, + Representation::Tagged())); } @@ -2001,7 +1912,12 @@ HValue* HGraphBuilder::JSArrayBuilder::AllocateArray(HValue* size_in_bytes, AddInstruction(new_object); // Fill in the fields: map, properties, length - HValue* map = EmitMapCode(context); + HValue* map; + if (constructor_function_ != NULL) { + map = EmitInternalMapCode(); + } else { + map = EmitMapCode(context); + } elements_location_ = builder()->BuildJSArrayHeader(new_object, map, mode_, @@ -2009,7 +1925,7 @@ HValue* HGraphBuilder::JSArrayBuilder::AllocateArray(HValue* size_in_bytes, length_field); // Initialize the elements - builder()->BuildInitializeElements(elements_location_, kind_, capacity); + builder()->BuildInitializeElementsHeader(elements_location_, kind_, capacity); if (fill_with_hole) { builder()->BuildFillElementsWithHole(context, elements_location_, kind_, @@ -2020,11 +1936,43 @@ HValue* HGraphBuilder::JSArrayBuilder::AllocateArray(HValue* size_in_bytes, } -HOptimizedGraphBuilder::HOptimizedGraphBuilder(CompilationInfo* info, - TypeFeedbackOracle* oracle) +HStoreNamedField* HGraphBuilder::AddStore(HValue *object, + HObjectAccess access, + HValue *val, + Representation representation) { + HStoreNamedField *instr = new(zone()) + HStoreNamedField(object, access, val, representation); + AddInstruction(instr); + return instr; +} + + +HLoadNamedField* HGraphBuilder::AddLoad(HValue *object, + HObjectAccess access, + HValue *typecheck, + Representation representation) { + HLoadNamedField *instr = + new(zone()) HLoadNamedField(object, access, typecheck, representation); + AddInstruction(instr); + return instr; +} + + +HStoreNamedField* HGraphBuilder::AddStoreMapConstant(HValue *object, + Handle<Map> map) { + HValue* constant = + AddInstruction(new(zone()) HConstant(map, Representation::Tagged())); + HStoreNamedField *instr = + new(zone()) HStoreNamedField(object, HObjectAccess::ForMap(), constant); + AddInstruction(instr); + return instr; +} + + +HOptimizedGraphBuilder::HOptimizedGraphBuilder(CompilationInfo* info) : HGraphBuilder(info), function_state_(NULL), - initial_function_state_(this, info, oracle, NORMAL_RETURN), + initial_function_state_(this, info, NORMAL_RETURN), ast_context_(NULL), break_scope_(NULL), inlined_count_(0), @@ -2103,7 +2051,8 @@ HGraph::HGraph(CompilationInfo* info) use_optimistic_licm_(false), has_soft_deoptimize_(false), depends_on_empty_array_proto_elements_(false), - type_change_checksum_(0) { + type_change_checksum_(0), + maximum_environment_size_(0) { if (info->IsStub()) { HydrogenCodeStub* stub = info->code_stub(); CodeStubInterfaceDescriptor* descriptor = @@ -2128,7 +2077,7 @@ HBasicBlock* HGraph::CreateBasicBlock() { void HGraph::FinalizeUniqueValueIds() { - AssertNoAllocation no_gc; + DisallowHeapAllocation no_gc; ASSERT(!isolate()->optimizing_compiler_thread()->IsOptimizerThread()); for (int i = 0; i < blocks()->length(); ++i) { for (HInstruction* instr = blocks()->at(i)->first(); @@ -2142,6 +2091,22 @@ void HGraph::FinalizeUniqueValueIds() { void HGraph::Canonicalize() { HPhase phase("H_Canonicalize", this); + // Before removing no-op instructions, save their semantic value. + // We must be careful not to set the flag unnecessarily, because GVN + // cannot identify two instructions when their flag value differs. + for (int i = 0; i < blocks()->length(); ++i) { + HInstruction* instr = blocks()->at(i)->first(); + while (instr != NULL) { + if (instr->IsArithmeticBinaryOperation() && + instr->representation().IsInteger32() && + instr->HasAtLeastOneUseWithFlagAndNoneWithout( + HInstruction::kTruncatingToInt32)) { + instr->SetFlag(HInstruction::kAllUsesTruncatingToInt32); + } + instr = instr->next(); + } + } + // Perform actual Canonicalization pass. for (int i = 0; i < blocks()->length(); ++i) { HInstruction* instr = blocks()->at(i)->first(); while (instr != NULL) { @@ -2842,251 +2807,6 @@ void HRangeAnalysis::AddRange(HValue* value, Range* range) { } -void TraceGVN(const char* msg, ...) { - va_list arguments; - va_start(arguments, msg); - OS::VPrint(msg, arguments); - va_end(arguments); -} - -// Wrap TraceGVN in macros to avoid the expense of evaluating its arguments when -// --trace-gvn is off. -#define TRACE_GVN_1(msg, a1) \ - if (FLAG_trace_gvn) { \ - TraceGVN(msg, a1); \ - } - -#define TRACE_GVN_2(msg, a1, a2) \ - if (FLAG_trace_gvn) { \ - TraceGVN(msg, a1, a2); \ - } - -#define TRACE_GVN_3(msg, a1, a2, a3) \ - if (FLAG_trace_gvn) { \ - TraceGVN(msg, a1, a2, a3); \ - } - -#define TRACE_GVN_4(msg, a1, a2, a3, a4) \ - if (FLAG_trace_gvn) { \ - TraceGVN(msg, a1, a2, a3, a4); \ - } - -#define TRACE_GVN_5(msg, a1, a2, a3, a4, a5) \ - if (FLAG_trace_gvn) { \ - TraceGVN(msg, a1, a2, a3, a4, a5); \ - } - - -HValueMap::HValueMap(Zone* zone, const HValueMap* other) - : array_size_(other->array_size_), - lists_size_(other->lists_size_), - count_(other->count_), - present_flags_(other->present_flags_), - array_(zone->NewArray<HValueMapListElement>(other->array_size_)), - lists_(zone->NewArray<HValueMapListElement>(other->lists_size_)), - free_list_head_(other->free_list_head_) { - OS::MemCopy( - array_, other->array_, array_size_ * sizeof(HValueMapListElement)); - OS::MemCopy( - lists_, other->lists_, lists_size_ * sizeof(HValueMapListElement)); -} - - -void HValueMap::Kill(GVNFlagSet flags) { - GVNFlagSet depends_flags = HValue::ConvertChangesToDependsFlags(flags); - if (!present_flags_.ContainsAnyOf(depends_flags)) return; - present_flags_.RemoveAll(); - for (int i = 0; i < array_size_; ++i) { - HValue* value = array_[i].value; - if (value != NULL) { - // Clear list of collisions first, so we know if it becomes empty. - int kept = kNil; // List of kept elements. - int next; - for (int current = array_[i].next; current != kNil; current = next) { - next = lists_[current].next; - HValue* value = lists_[current].value; - if (value->gvn_flags().ContainsAnyOf(depends_flags)) { - // Drop it. - count_--; - lists_[current].next = free_list_head_; - free_list_head_ = current; - } else { - // Keep it. - lists_[current].next = kept; - kept = current; - present_flags_.Add(value->gvn_flags()); - } - } - array_[i].next = kept; - - // Now possibly drop directly indexed element. - value = array_[i].value; - if (value->gvn_flags().ContainsAnyOf(depends_flags)) { // Drop it. - count_--; - int head = array_[i].next; - if (head == kNil) { - array_[i].value = NULL; - } else { - array_[i].value = lists_[head].value; - array_[i].next = lists_[head].next; - lists_[head].next = free_list_head_; - free_list_head_ = head; - } - } else { - present_flags_.Add(value->gvn_flags()); // Keep it. - } - } - } -} - - -HValue* HValueMap::Lookup(HValue* value) const { - uint32_t hash = static_cast<uint32_t>(value->Hashcode()); - uint32_t pos = Bound(hash); - if (array_[pos].value != NULL) { - if (array_[pos].value->Equals(value)) return array_[pos].value; - int next = array_[pos].next; - while (next != kNil) { - if (lists_[next].value->Equals(value)) return lists_[next].value; - next = lists_[next].next; - } - } - return NULL; -} - - -void HValueMap::Resize(int new_size, Zone* zone) { - ASSERT(new_size > count_); - // Hashing the values into the new array has no more collisions than in the - // old hash map, so we can use the existing lists_ array, if we are careful. - - // Make sure we have at least one free element. - if (free_list_head_ == kNil) { - ResizeLists(lists_size_ << 1, zone); - } - - HValueMapListElement* new_array = - zone->NewArray<HValueMapListElement>(new_size); - memset(new_array, 0, sizeof(HValueMapListElement) * new_size); - - HValueMapListElement* old_array = array_; - int old_size = array_size_; - - int old_count = count_; - count_ = 0; - // Do not modify present_flags_. It is currently correct. - array_size_ = new_size; - array_ = new_array; - - if (old_array != NULL) { - // Iterate over all the elements in lists, rehashing them. - for (int i = 0; i < old_size; ++i) { - if (old_array[i].value != NULL) { - int current = old_array[i].next; - while (current != kNil) { - Insert(lists_[current].value, zone); - int next = lists_[current].next; - lists_[current].next = free_list_head_; - free_list_head_ = current; - current = next; - } - // Rehash the directly stored value. - Insert(old_array[i].value, zone); - } - } - } - USE(old_count); - ASSERT(count_ == old_count); -} - - -void HValueMap::ResizeLists(int new_size, Zone* zone) { - ASSERT(new_size > lists_size_); - - HValueMapListElement* new_lists = - zone->NewArray<HValueMapListElement>(new_size); - memset(new_lists, 0, sizeof(HValueMapListElement) * new_size); - - HValueMapListElement* old_lists = lists_; - int old_size = lists_size_; - - lists_size_ = new_size; - lists_ = new_lists; - - if (old_lists != NULL) { - OS::MemCopy(lists_, old_lists, old_size * sizeof(HValueMapListElement)); - } - for (int i = old_size; i < lists_size_; ++i) { - lists_[i].next = free_list_head_; - free_list_head_ = i; - } -} - - -void HValueMap::Insert(HValue* value, Zone* zone) { - ASSERT(value != NULL); - // Resizing when half of the hashtable is filled up. - if (count_ >= array_size_ >> 1) Resize(array_size_ << 1, zone); - ASSERT(count_ < array_size_); - count_++; - uint32_t pos = Bound(static_cast<uint32_t>(value->Hashcode())); - if (array_[pos].value == NULL) { - array_[pos].value = value; - array_[pos].next = kNil; - } else { - if (free_list_head_ == kNil) { - ResizeLists(lists_size_ << 1, zone); - } - int new_element_pos = free_list_head_; - ASSERT(new_element_pos != kNil); - free_list_head_ = lists_[free_list_head_].next; - lists_[new_element_pos].value = value; - lists_[new_element_pos].next = array_[pos].next; - ASSERT(array_[pos].next == kNil || lists_[array_[pos].next].value != NULL); - array_[pos].next = new_element_pos; - } -} - - -HSideEffectMap::HSideEffectMap() : count_(0) { - memset(data_, 0, kNumberOfTrackedSideEffects * kPointerSize); -} - - -HSideEffectMap::HSideEffectMap(HSideEffectMap* other) : count_(other->count_) { - *this = *other; // Calls operator=. -} - - -HSideEffectMap& HSideEffectMap::operator= (const HSideEffectMap& other) { - if (this != &other) { - OS::MemCopy(data_, other.data_, kNumberOfTrackedSideEffects * kPointerSize); - } - return *this; -} - -void HSideEffectMap::Kill(GVNFlagSet flags) { - for (int i = 0; i < kNumberOfTrackedSideEffects; i++) { - GVNFlag changes_flag = HValue::ChangesFlagFromInt(i); - if (flags.Contains(changes_flag)) { - if (data_[i] != NULL) count_--; - data_[i] = NULL; - } - } -} - - -void HSideEffectMap::Store(GVNFlagSet flags, HInstruction* instr) { - for (int i = 0; i < kNumberOfTrackedSideEffects; i++) { - GVNFlag changes_flag = HValue::ChangesFlagFromInt(i); - if (flags.Contains(changes_flag)) { - if (data_[i] == NULL) count_++; - data_[i] = instr; - } - } -} - - class HStackCheckEliminator BASE_EMBEDDED { public: explicit HStackCheckEliminator(HGraph* graph) : graph_(graph) { } @@ -3130,581 +2850,6 @@ void HStackCheckEliminator::Process() { } -// Simple sparse set with O(1) add, contains, and clear. -class SparseSet { - public: - SparseSet(Zone* zone, int capacity) - : capacity_(capacity), - length_(0), - dense_(zone->NewArray<int>(capacity)), - sparse_(zone->NewArray<int>(capacity)) { -#ifndef NVALGRIND - // Initialize the sparse array to make valgrind happy. - memset(sparse_, 0, sizeof(sparse_[0]) * capacity); -#endif - } - - bool Contains(int n) const { - ASSERT(0 <= n && n < capacity_); - int d = sparse_[n]; - return 0 <= d && d < length_ && dense_[d] == n; - } - - bool Add(int n) { - if (Contains(n)) return false; - dense_[length_] = n; - sparse_[n] = length_; - ++length_; - return true; - } - - void Clear() { length_ = 0; } - - private: - int capacity_; - int length_; - int* dense_; - int* sparse_; - - DISALLOW_COPY_AND_ASSIGN(SparseSet); -}; - - -class HGlobalValueNumberer BASE_EMBEDDED { - public: - explicit HGlobalValueNumberer(HGraph* graph, CompilationInfo* info) - : graph_(graph), - info_(info), - removed_side_effects_(false), - block_side_effects_(graph->blocks()->length(), graph->zone()), - loop_side_effects_(graph->blocks()->length(), graph->zone()), - visited_on_paths_(graph->zone(), graph->blocks()->length()) { -#ifdef DEBUG - ASSERT(info->isolate()->optimizing_compiler_thread()->IsOptimizerThread() || - !info->isolate()->heap()->IsAllocationAllowed()); -#endif - block_side_effects_.AddBlock(GVNFlagSet(), graph_->blocks()->length(), - graph_->zone()); - loop_side_effects_.AddBlock(GVNFlagSet(), graph_->blocks()->length(), - graph_->zone()); - } - - // Returns true if values with side effects are removed. - bool Analyze(); - - private: - GVNFlagSet CollectSideEffectsOnPathsToDominatedBlock( - HBasicBlock* dominator, - HBasicBlock* dominated); - void AnalyzeGraph(); - void ComputeBlockSideEffects(); - void LoopInvariantCodeMotion(); - void ProcessLoopBlock(HBasicBlock* block, - HBasicBlock* before_loop, - GVNFlagSet loop_kills, - GVNFlagSet* accumulated_first_time_depends, - GVNFlagSet* accumulated_first_time_changes); - bool AllowCodeMotion(); - bool ShouldMove(HInstruction* instr, HBasicBlock* loop_header); - - HGraph* graph() { return graph_; } - CompilationInfo* info() { return info_; } - Zone* zone() const { return graph_->zone(); } - - HGraph* graph_; - CompilationInfo* info_; - bool removed_side_effects_; - - // A map of block IDs to their side effects. - ZoneList<GVNFlagSet> block_side_effects_; - - // A map of loop header block IDs to their loop's side effects. - ZoneList<GVNFlagSet> loop_side_effects_; - - // Used when collecting side effects on paths from dominator to - // dominated. - SparseSet visited_on_paths_; -}; - - -bool HGlobalValueNumberer::Analyze() { - removed_side_effects_ = false; - ComputeBlockSideEffects(); - if (FLAG_loop_invariant_code_motion) { - LoopInvariantCodeMotion(); - } - AnalyzeGraph(); - return removed_side_effects_; -} - - -void HGlobalValueNumberer::ComputeBlockSideEffects() { - // The Analyze phase of GVN can be called multiple times. Clear loop side - // effects before computing them to erase the contents from previous Analyze - // passes. - for (int i = 0; i < loop_side_effects_.length(); ++i) { - loop_side_effects_[i].RemoveAll(); - } - for (int i = graph_->blocks()->length() - 1; i >= 0; --i) { - // Compute side effects for the block. - HBasicBlock* block = graph_->blocks()->at(i); - HInstruction* instr = block->first(); - int id = block->block_id(); - GVNFlagSet side_effects; - while (instr != NULL) { - side_effects.Add(instr->ChangesFlags()); - if (instr->IsSoftDeoptimize()) { - block_side_effects_[id].RemoveAll(); - side_effects.RemoveAll(); - break; - } - instr = instr->next(); - } - block_side_effects_[id].Add(side_effects); - - // Loop headers are part of their loop. - if (block->IsLoopHeader()) { - loop_side_effects_[id].Add(side_effects); - } - - // Propagate loop side effects upwards. - if (block->HasParentLoopHeader()) { - int header_id = block->parent_loop_header()->block_id(); - loop_side_effects_[header_id].Add(block->IsLoopHeader() - ? loop_side_effects_[id] - : side_effects); - } - } -} - - -SmartArrayPointer<char> GetGVNFlagsString(GVNFlagSet flags) { - char underlying_buffer[kLastFlag * 128]; - Vector<char> buffer(underlying_buffer, sizeof(underlying_buffer)); -#if DEBUG - int offset = 0; - const char* separator = ""; - const char* comma = ", "; - buffer[0] = 0; - uint32_t set_depends_on = 0; - uint32_t set_changes = 0; - for (int bit = 0; bit < kLastFlag; ++bit) { - if ((flags.ToIntegral() & (1 << bit)) != 0) { - if (bit % 2 == 0) { - set_changes++; - } else { - set_depends_on++; - } - } - } - bool positive_changes = set_changes < (kLastFlag / 2); - bool positive_depends_on = set_depends_on < (kLastFlag / 2); - if (set_changes > 0) { - if (positive_changes) { - offset += OS::SNPrintF(buffer + offset, "changes ["); - } else { - offset += OS::SNPrintF(buffer + offset, "changes all except ["); - } - for (int bit = 0; bit < kLastFlag; ++bit) { - if (((flags.ToIntegral() & (1 << bit)) != 0) == positive_changes) { - switch (static_cast<GVNFlag>(bit)) { -#define DECLARE_FLAG(type) \ - case kChanges##type: \ - offset += OS::SNPrintF(buffer + offset, separator); \ - offset += OS::SNPrintF(buffer + offset, #type); \ - separator = comma; \ - break; -GVN_TRACKED_FLAG_LIST(DECLARE_FLAG) -GVN_UNTRACKED_FLAG_LIST(DECLARE_FLAG) -#undef DECLARE_FLAG - default: - break; - } - } - } - offset += OS::SNPrintF(buffer + offset, "]"); - } - if (set_depends_on > 0) { - separator = ""; - if (set_changes > 0) { - offset += OS::SNPrintF(buffer + offset, ", "); - } - if (positive_depends_on) { - offset += OS::SNPrintF(buffer + offset, "depends on ["); - } else { - offset += OS::SNPrintF(buffer + offset, "depends on all except ["); - } - for (int bit = 0; bit < kLastFlag; ++bit) { - if (((flags.ToIntegral() & (1 << bit)) != 0) == positive_depends_on) { - switch (static_cast<GVNFlag>(bit)) { -#define DECLARE_FLAG(type) \ - case kDependsOn##type: \ - offset += OS::SNPrintF(buffer + offset, separator); \ - offset += OS::SNPrintF(buffer + offset, #type); \ - separator = comma; \ - break; -GVN_TRACKED_FLAG_LIST(DECLARE_FLAG) -GVN_UNTRACKED_FLAG_LIST(DECLARE_FLAG) -#undef DECLARE_FLAG - default: - break; - } - } - } - offset += OS::SNPrintF(buffer + offset, "]"); - } -#else - OS::SNPrintF(buffer, "0x%08X", flags.ToIntegral()); -#endif - size_t string_len = strlen(underlying_buffer) + 1; - ASSERT(string_len <= sizeof(underlying_buffer)); - char* result = new char[strlen(underlying_buffer) + 1]; - OS::MemCopy(result, underlying_buffer, string_len); - return SmartArrayPointer<char>(result); -} - - -void HGlobalValueNumberer::LoopInvariantCodeMotion() { - TRACE_GVN_1("Using optimistic loop invariant code motion: %s\n", - graph_->use_optimistic_licm() ? "yes" : "no"); - for (int i = graph_->blocks()->length() - 1; i >= 0; --i) { - HBasicBlock* block = graph_->blocks()->at(i); - if (block->IsLoopHeader()) { - GVNFlagSet side_effects = loop_side_effects_[block->block_id()]; - TRACE_GVN_2("Try loop invariant motion for block B%d %s\n", - block->block_id(), - *GetGVNFlagsString(side_effects)); - - GVNFlagSet accumulated_first_time_depends; - GVNFlagSet accumulated_first_time_changes; - HBasicBlock* last = block->loop_information()->GetLastBackEdge(); - for (int j = block->block_id(); j <= last->block_id(); ++j) { - ProcessLoopBlock(graph_->blocks()->at(j), block, side_effects, - &accumulated_first_time_depends, - &accumulated_first_time_changes); - } - } - } -} - - -void HGlobalValueNumberer::ProcessLoopBlock( - HBasicBlock* block, - HBasicBlock* loop_header, - GVNFlagSet loop_kills, - GVNFlagSet* first_time_depends, - GVNFlagSet* first_time_changes) { - HBasicBlock* pre_header = loop_header->predecessors()->at(0); - GVNFlagSet depends_flags = HValue::ConvertChangesToDependsFlags(loop_kills); - TRACE_GVN_2("Loop invariant motion for B%d %s\n", - block->block_id(), - *GetGVNFlagsString(depends_flags)); - HInstruction* instr = block->first(); - while (instr != NULL) { - HInstruction* next = instr->next(); - bool hoisted = false; - if (instr->CheckFlag(HValue::kUseGVN)) { - TRACE_GVN_4("Checking instruction %d (%s) %s. Loop %s\n", - instr->id(), - instr->Mnemonic(), - *GetGVNFlagsString(instr->gvn_flags()), - *GetGVNFlagsString(loop_kills)); - bool can_hoist = !instr->gvn_flags().ContainsAnyOf(depends_flags); - if (can_hoist && !graph()->use_optimistic_licm()) { - can_hoist = block->IsLoopSuccessorDominator(); - } - - if (can_hoist) { - bool inputs_loop_invariant = true; - for (int i = 0; i < instr->OperandCount(); ++i) { - if (instr->OperandAt(i)->IsDefinedAfter(pre_header)) { - inputs_loop_invariant = false; - } - } - - if (inputs_loop_invariant && ShouldMove(instr, loop_header)) { - TRACE_GVN_1("Hoisting loop invariant instruction %d\n", instr->id()); - // Move the instruction out of the loop. - instr->Unlink(); - instr->InsertBefore(pre_header->end()); - if (instr->HasSideEffects()) removed_side_effects_ = true; - hoisted = true; - } - } - } - if (!hoisted) { - // If an instruction is not hoisted, we have to account for its side - // effects when hoisting later HTransitionElementsKind instructions. - GVNFlagSet previous_depends = *first_time_depends; - GVNFlagSet previous_changes = *first_time_changes; - first_time_depends->Add(instr->DependsOnFlags()); - first_time_changes->Add(instr->ChangesFlags()); - if (!(previous_depends == *first_time_depends)) { - TRACE_GVN_1("Updated first-time accumulated %s\n", - *GetGVNFlagsString(*first_time_depends)); - } - if (!(previous_changes == *first_time_changes)) { - TRACE_GVN_1("Updated first-time accumulated %s\n", - *GetGVNFlagsString(*first_time_changes)); - } - } - instr = next; - } -} - - -bool HGlobalValueNumberer::AllowCodeMotion() { - return info()->IsStub() || info()->opt_count() + 1 < FLAG_max_opt_count; -} - - -bool HGlobalValueNumberer::ShouldMove(HInstruction* instr, - HBasicBlock* loop_header) { - // If we've disabled code motion or we're in a block that unconditionally - // deoptimizes, don't move any instructions. - return AllowCodeMotion() && !instr->block()->IsDeoptimizing(); -} - - -GVNFlagSet HGlobalValueNumberer::CollectSideEffectsOnPathsToDominatedBlock( - HBasicBlock* dominator, HBasicBlock* dominated) { - GVNFlagSet side_effects; - for (int i = 0; i < dominated->predecessors()->length(); ++i) { - HBasicBlock* block = dominated->predecessors()->at(i); - if (dominator->block_id() < block->block_id() && - block->block_id() < dominated->block_id() && - visited_on_paths_.Add(block->block_id())) { - side_effects.Add(block_side_effects_[block->block_id()]); - if (block->IsLoopHeader()) { - side_effects.Add(loop_side_effects_[block->block_id()]); - } - side_effects.Add(CollectSideEffectsOnPathsToDominatedBlock( - dominator, block)); - } - } - return side_effects; -} - - -// Each instance of this class is like a "stack frame" for the recursive -// traversal of the dominator tree done during GVN (the stack is handled -// as a double linked list). -// We reuse frames when possible so the list length is limited by the depth -// of the dominator tree but this forces us to initialize each frame calling -// an explicit "Initialize" method instead of a using constructor. -class GvnBasicBlockState: public ZoneObject { - public: - static GvnBasicBlockState* CreateEntry(Zone* zone, - HBasicBlock* entry_block, - HValueMap* entry_map) { - return new(zone) - GvnBasicBlockState(NULL, entry_block, entry_map, NULL, zone); - } - - HBasicBlock* block() { return block_; } - HValueMap* map() { return map_; } - HSideEffectMap* dominators() { return &dominators_; } - - GvnBasicBlockState* next_in_dominator_tree_traversal( - Zone* zone, - HBasicBlock** dominator) { - // This assignment needs to happen before calling next_dominated() because - // that call can reuse "this" if we are at the last dominated block. - *dominator = block(); - GvnBasicBlockState* result = next_dominated(zone); - if (result == NULL) { - GvnBasicBlockState* dominator_state = pop(); - if (dominator_state != NULL) { - // This branch is guaranteed not to return NULL because pop() never - // returns a state where "is_done() == true". - *dominator = dominator_state->block(); - result = dominator_state->next_dominated(zone); - } else { - // Unnecessary (we are returning NULL) but done for cleanness. - *dominator = NULL; - } - } - return result; - } - - private: - void Initialize(HBasicBlock* block, - HValueMap* map, - HSideEffectMap* dominators, - bool copy_map, - Zone* zone) { - block_ = block; - map_ = copy_map ? map->Copy(zone) : map; - dominated_index_ = -1; - length_ = block->dominated_blocks()->length(); - if (dominators != NULL) { - dominators_ = *dominators; - } - } - bool is_done() { return dominated_index_ >= length_; } - - GvnBasicBlockState(GvnBasicBlockState* previous, - HBasicBlock* block, - HValueMap* map, - HSideEffectMap* dominators, - Zone* zone) - : previous_(previous), next_(NULL) { - Initialize(block, map, dominators, true, zone); - } - - GvnBasicBlockState* next_dominated(Zone* zone) { - dominated_index_++; - if (dominated_index_ == length_ - 1) { - // No need to copy the map for the last child in the dominator tree. - Initialize(block_->dominated_blocks()->at(dominated_index_), - map(), - dominators(), - false, - zone); - return this; - } else if (dominated_index_ < length_) { - return push(zone, - block_->dominated_blocks()->at(dominated_index_), - dominators()); - } else { - return NULL; - } - } - - GvnBasicBlockState* push(Zone* zone, - HBasicBlock* block, - HSideEffectMap* dominators) { - if (next_ == NULL) { - next_ = - new(zone) GvnBasicBlockState(this, block, map(), dominators, zone); - } else { - next_->Initialize(block, map(), dominators, true, zone); - } - return next_; - } - GvnBasicBlockState* pop() { - GvnBasicBlockState* result = previous_; - while (result != NULL && result->is_done()) { - TRACE_GVN_2("Backtracking from block B%d to block b%d\n", - block()->block_id(), - previous_->block()->block_id()) - result = result->previous_; - } - return result; - } - - GvnBasicBlockState* previous_; - GvnBasicBlockState* next_; - HBasicBlock* block_; - HValueMap* map_; - HSideEffectMap dominators_; - int dominated_index_; - int length_; -}; - -// This is a recursive traversal of the dominator tree but it has been turned -// into a loop to avoid stack overflows. -// The logical "stack frames" of the recursion are kept in a list of -// GvnBasicBlockState instances. -void HGlobalValueNumberer::AnalyzeGraph() { - HBasicBlock* entry_block = graph_->entry_block(); - HValueMap* entry_map = new(zone()) HValueMap(zone()); - GvnBasicBlockState* current = - GvnBasicBlockState::CreateEntry(zone(), entry_block, entry_map); - - while (current != NULL) { - HBasicBlock* block = current->block(); - HValueMap* map = current->map(); - HSideEffectMap* dominators = current->dominators(); - - TRACE_GVN_2("Analyzing block B%d%s\n", - block->block_id(), - block->IsLoopHeader() ? " (loop header)" : ""); - - // If this is a loop header kill everything killed by the loop. - if (block->IsLoopHeader()) { - map->Kill(loop_side_effects_[block->block_id()]); - } - - // Go through all instructions of the current block. - HInstruction* instr = block->first(); - while (instr != NULL) { - HInstruction* next = instr->next(); - GVNFlagSet flags = instr->ChangesFlags(); - if (!flags.IsEmpty()) { - // Clear all instructions in the map that are affected by side effects. - // Store instruction as the dominating one for tracked side effects. - map->Kill(flags); - dominators->Store(flags, instr); - TRACE_GVN_2("Instruction %d %s\n", instr->id(), - *GetGVNFlagsString(flags)); - } - if (instr->CheckFlag(HValue::kUseGVN)) { - ASSERT(!instr->HasObservableSideEffects()); - HValue* other = map->Lookup(instr); - if (other != NULL) { - ASSERT(instr->Equals(other) && other->Equals(instr)); - TRACE_GVN_4("Replacing value %d (%s) with value %d (%s)\n", - instr->id(), - instr->Mnemonic(), - other->id(), - other->Mnemonic()); - if (instr->HasSideEffects()) removed_side_effects_ = true; - instr->DeleteAndReplaceWith(other); - } else { - map->Add(instr, zone()); - } - } - if (instr->IsLinked() && - instr->CheckFlag(HValue::kTrackSideEffectDominators)) { - for (int i = 0; i < kNumberOfTrackedSideEffects; i++) { - HValue* other = dominators->at(i); - GVNFlag changes_flag = HValue::ChangesFlagFromInt(i); - GVNFlag depends_on_flag = HValue::DependsOnFlagFromInt(i); - if (instr->DependsOnFlags().Contains(depends_on_flag) && - (other != NULL)) { - TRACE_GVN_5("Side-effect #%d in %d (%s) is dominated by %d (%s)\n", - i, - instr->id(), - instr->Mnemonic(), - other->id(), - other->Mnemonic()); - instr->SetSideEffectDominator(changes_flag, other); - } - } - } - instr = next; - } - - HBasicBlock* dominator_block; - GvnBasicBlockState* next = - current->next_in_dominator_tree_traversal(zone(), &dominator_block); - - if (next != NULL) { - HBasicBlock* dominated = next->block(); - HValueMap* successor_map = next->map(); - HSideEffectMap* successor_dominators = next->dominators(); - - // Kill everything killed on any path between this block and the - // dominated block. We don't have to traverse these paths if the - // value map and the dominators list is already empty. If the range - // of block ids (block_id, dominated_id) is empty there are no such - // paths. - if ((!successor_map->IsEmpty() || !successor_dominators->IsEmpty()) && - dominator_block->block_id() + 1 < dominated->block_id()) { - visited_on_paths_.Clear(); - GVNFlagSet side_effects_on_all_paths = - CollectSideEffectsOnPathsToDominatedBlock(dominator_block, - dominated); - successor_map->Kill(side_effects_on_all_paths); - successor_dominators->Kill(side_effects_on_all_paths); - } - } - current = next; - } -} - - void HInferRepresentation::AddToWorklist(HValue* current) { if (current->representation().IsTagged()) return; if (!current->CheckFlag(HValue::kFlexibleRepresentation)) return; @@ -3788,23 +2933,6 @@ void HInferRepresentation::Analyze() { } // Use the phi reachability information from step 2 to - // push information about values which can't be converted to integer - // without deoptimization through the phi use-def chains, avoiding - // unnecessary deoptimizations later. - for (int i = 0; i < phi_count; ++i) { - HPhi* phi = phi_list->at(i); - bool cti = phi->AllOperandsConvertibleToInteger(); - if (cti) continue; - - for (BitVector::Iterator it(connected_phis.at(i)); - !it.Done(); - it.Advance()) { - HPhi* phi = phi_list->at(it.Current()); - phi->set_is_convertible_to_integer(false); - } - } - - // Use the phi reachability information from step 2 to // sum up the non-phi use counts of all connected phis. for (int i = 0; i < phi_count; ++i) { HPhi* phi = phi_list->at(i); @@ -4011,8 +3139,8 @@ void HGraph::InsertRepresentationChangeForUse(HValue* value, // change instructions for them. HInstruction* new_value = NULL; bool is_truncating = use_value->CheckFlag(HValue::kTruncatingToInt32); - bool deoptimize_on_undefined = - use_value->CheckFlag(HValue::kDeoptimizeOnUndefined); + bool allow_undefined_as_nan = + use_value->CheckFlag(HValue::kAllowUndefinedAsNaN); if (value->IsConstant()) { HConstant* constant = HConstant::cast(value); // Try to create a new copy of the constant with the new representation. @@ -4023,7 +3151,7 @@ void HGraph::InsertRepresentationChangeForUse(HValue* value, if (new_value == NULL) { new_value = new(zone()) HChange(value, to, - is_truncating, deoptimize_on_undefined); + is_truncating, allow_undefined_as_nan); } new_value->InsertBefore(next); @@ -4080,9 +3208,8 @@ void HGraph::InsertRepresentationChanges() { HValue* use = it.value(); Representation input_representation = use->RequiredInputRepresentation(it.index()); - if ((input_representation.IsInteger32() && - !use->CheckFlag(HValue::kTruncatingToInt32)) || - input_representation.IsDouble()) { + if (!input_representation.IsInteger32() || + !use->CheckFlag(HValue::kTruncatingToInt32)) { if (FLAG_trace_representation) { PrintF("#%d Phi is not truncating because of #%d %s\n", phi->id(), it.value()->id(), it.value()->Mnemonic()); @@ -4130,8 +3257,8 @@ void HGraph::InsertRepresentationChanges() { void HGraph::RecursivelyMarkPhiDeoptimizeOnUndefined(HPhi* phi) { - if (phi->CheckFlag(HValue::kDeoptimizeOnUndefined)) return; - phi->SetFlag(HValue::kDeoptimizeOnUndefined); + if (!phi->CheckFlag(HValue::kAllowUndefinedAsNaN)) return; + phi->ClearFlag(HValue::kAllowUndefinedAsNaN); for (int i = 0; i < phi->OperandCount(); ++i) { HValue* input = phi->OperandAt(i); if (input->IsPhi()) { @@ -4151,12 +3278,11 @@ void HGraph::MarkDeoptimizeOnUndefined() { // if one of its uses has this flag set. for (int i = 0; i < phi_list()->length(); i++) { HPhi* phi = phi_list()->at(i); - if (phi->representation().IsDouble()) { - for (HUseIterator it(phi->uses()); !it.Done(); it.Advance()) { - if (it.value()->CheckFlag(HValue::kDeoptimizeOnUndefined)) { - RecursivelyMarkPhiDeoptimizeOnUndefined(phi); - break; - } + for (HUseIterator it(phi->uses()); !it.Done(); it.Advance()) { + HValue* use_value = it.value(); + if (!use_value->CheckFlag(HValue::kAllowUndefinedAsNaN)) { + RecursivelyMarkPhiDeoptimizeOnUndefined(phi); + break; } } } @@ -4399,7 +3525,9 @@ void HGraph::ComputeMinusZeroChecks() { Representation from = change->value()->representation(); ASSERT(from.Equals(change->from())); if (from.IsInteger32()) { - ASSERT(change->to().IsTagged() || change->to().IsDouble()); + ASSERT(change->to().IsTagged() || + change->to().IsDouble() || + change->to().IsSmi()); ASSERT(visited.IsEmpty()); PropagateMinusZeroChecks(change->value(), &visited); visited.Clear(); @@ -4414,11 +3542,9 @@ void HGraph::ComputeMinusZeroChecks() { // a (possibly inlined) function. FunctionState::FunctionState(HOptimizedGraphBuilder* owner, CompilationInfo* info, - TypeFeedbackOracle* oracle, InliningKind inlining_kind) : owner_(owner), compilation_info_(info), - oracle_(oracle), call_context_(NULL), inlining_kind_(inlining_kind), function_return_(NULL), @@ -4431,18 +3557,16 @@ FunctionState::FunctionState(HOptimizedGraphBuilder* owner, if (owner->ast_context()->IsTest()) { HBasicBlock* if_true = owner->graph()->CreateBasicBlock(); HBasicBlock* if_false = owner->graph()->CreateBasicBlock(); - if_true->MarkAsInlineReturnTarget(); - if_false->MarkAsInlineReturnTarget(); + if_true->MarkAsInlineReturnTarget(owner->current_block()); + if_false->MarkAsInlineReturnTarget(owner->current_block()); TestContext* outer_test_context = TestContext::cast(owner->ast_context()); Expression* cond = outer_test_context->condition(); - TypeFeedbackOracle* outer_oracle = outer_test_context->oracle(); // The AstContext constructor pushed on the context stack. This newed // instance is the reason that AstContext can't be BASE_EMBEDDED. - test_context_ = - new TestContext(owner, cond, outer_oracle, if_true, if_false); + test_context_ = new TestContext(owner, cond, if_true, if_false); } else { function_return_ = owner->graph()->CreateBasicBlock(); - function_return()->MarkAsInlineReturnTarget(); + function_return()->MarkAsInlineReturnTarget(owner->current_block()); } // Set this after possibly allocating a new TestContext above. call_context_ = owner->ast_context(); @@ -4673,8 +3797,7 @@ void TestContext::BuildBranch(HValue* value) { } HBasicBlock* empty_true = builder->graph()->CreateBasicBlock(); HBasicBlock* empty_false = builder->graph()->CreateBasicBlock(); - TypeFeedbackId test_id = condition()->test_id(); - ToBooleanStub::Types expected(oracle()->ToBooleanTypes(test_id)); + ToBooleanStub::Types expected(condition()->to_boolean_types()); HBranch* test = new(zone()) HBranch(value, empty_true, empty_false, expected); builder->current_block()->Finish(test); @@ -4729,7 +3852,7 @@ void HOptimizedGraphBuilder::VisitForTypeOf(Expression* expr) { void HOptimizedGraphBuilder::VisitForControl(Expression* expr, HBasicBlock* true_block, HBasicBlock* false_block) { - TestContext for_test(this, expr, oracle(), true_block, false_block); + TestContext for_test(this, expr, true_block, false_block); Visit(expr); } @@ -4862,6 +3985,11 @@ bool HGraph::Optimize(SmartArrayPointer<char>* bailout_reason) { Verify(true); #endif + if (FLAG_analyze_environment_liveness) { + EnvironmentSlotLivenessAnalyzer esla(this); + esla.AnalyzeAndTrim(); + } + PropagateDeoptimizingMark(); if (!CheckConstPhiUses()) { *bailout_reason = SmartArrayPointer<char>(StrDup( @@ -4999,7 +4127,7 @@ class BoundsCheckKey : public ZoneObject { static BoundsCheckKey* Create(Zone* zone, HBoundsCheck* check, int32_t* offset) { - if (!check->index()->representation().IsInteger32()) return NULL; + if (!check->index()->representation().IsSmiOrInteger32()) return NULL; HValue* index_base = NULL; HConstant* constant = NULL; @@ -5095,7 +4223,7 @@ class BoundsCheckBbData: public ZoneObject { // returns false, otherwise it returns true. bool CoverCheck(HBoundsCheck* new_check, int32_t new_offset) { - ASSERT(new_check->index()->representation().IsInteger32()); + ASSERT(new_check->index()->representation().IsSmiOrInteger32()); bool keep_new_check = false; if (new_offset > upper_offset_) { @@ -5204,8 +4332,8 @@ class BoundsCheckBbData: public ZoneObject { HValue* index_context = IndexContext(*add, check); if (index_context == NULL) return false; - HConstant* new_constant = new(BasicBlock()->zone()) - HConstant(new_offset, Representation::Integer32()); + HConstant* new_constant = new(BasicBlock()->zone()) HConstant( + new_offset, representation); if (*add == NULL) { new_constant->InsertBefore(check); (*add) = HAdd::New( @@ -5337,7 +4465,7 @@ void HGraph::EliminateRedundantBoundsChecks() { static void DehoistArrayIndex(ArrayInstructionInterface* array_operation) { HValue* index = array_operation->GetKey()->ActualValue(); - if (!index->representation().IsInteger32()) return; + if (!index->representation().IsSmiOrInteger32()) return; HConstant* constant; HValue* subexpression; @@ -5449,7 +4577,6 @@ void HGraph::MarkLive(HValue* ref, HValue* instr, ZoneList<HValue*>* worklist) { if (FLAG_trace_dead_code_elimination) { HeapStringAllocator allocator; StringStream stream(&allocator); - ALLOW_HANDLE_DEREF(isolate(), "debug mode printing"); if (ref != NULL) { ref->PrintTo(&stream); } else { @@ -5738,9 +4865,8 @@ void HOptimizedGraphBuilder::VisitContinueStatement( ASSERT(current_block() != NULL); ASSERT(current_block()->HasPredecessor()); int drop_extra = 0; - HBasicBlock* continue_block = break_scope()->Get(stmt->target(), - CONTINUE, - &drop_extra); + HBasicBlock* continue_block = break_scope()->Get( + stmt->target(), BreakAndContinueScope::CONTINUE, &drop_extra); Drop(drop_extra); current_block()->Goto(continue_block); set_current_block(NULL); @@ -5752,9 +4878,8 @@ void HOptimizedGraphBuilder::VisitBreakStatement(BreakStatement* stmt) { ASSERT(current_block() != NULL); ASSERT(current_block()->HasPredecessor()); int drop_extra = 0; - HBasicBlock* break_block = break_scope()->Get(stmt->target(), - BREAK, - &drop_extra); + HBasicBlock* break_block = break_scope()->Get( + stmt->target(), BreakAndContinueScope::BREAK, &drop_extra); Drop(drop_extra); current_block()->Goto(break_block); set_current_block(NULL); @@ -5845,6 +4970,7 @@ void HOptimizedGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) { ASSERT(!HasStackOverflow()); ASSERT(current_block() != NULL); ASSERT(current_block()->HasPredecessor()); + // We only optimize switch statements with smi-literal smi comparisons, // with a bounded number of clauses. const int kCaseClauseLimit = 128; @@ -5854,6 +4980,11 @@ void HOptimizedGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) { return Bailout("SwitchStatement: too many clauses"); } + ASSERT(stmt->switch_type() != SwitchStatement::UNKNOWN_SWITCH); + if (stmt->switch_type() == SwitchStatement::GENERIC_SWITCH) { + return Bailout("SwitchStatement: mixed or non-literal switch labels"); + } + HValue* context = environment()->LookupContext(); CHECK_ALIVE(VisitForValue(stmt->tag())); @@ -5861,34 +4992,11 @@ void HOptimizedGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) { HValue* tag_value = Pop(); HBasicBlock* first_test_block = current_block(); - SwitchType switch_type = UNKNOWN_SWITCH; - - // 1. Extract clause type - for (int i = 0; i < clause_count; ++i) { - CaseClause* clause = clauses->at(i); - if (clause->is_default()) continue; - - if (switch_type == UNKNOWN_SWITCH) { - if (clause->label()->IsSmiLiteral()) { - switch_type = SMI_SWITCH; - } else if (clause->label()->IsStringLiteral()) { - switch_type = STRING_SWITCH; - } else { - return Bailout("SwitchStatement: non-literal switch label"); - } - } else if ((switch_type == STRING_SWITCH && - !clause->label()->IsStringLiteral()) || - (switch_type == SMI_SWITCH && - !clause->label()->IsSmiLiteral())) { - return Bailout("SwitchStatement: mixed label types are not supported"); - } - } - HUnaryControlInstruction* string_check = NULL; HBasicBlock* not_string_block = NULL; // Test switch's tag value if all clauses are string literals - if (switch_type == STRING_SWITCH) { + if (stmt->switch_type() == SwitchStatement::STRING_SWITCH) { string_check = new(zone()) HIsStringAndBranch(tag_value); first_test_block = graph()->CreateBasicBlock(); not_string_block = graph()->CreateBasicBlock(); @@ -5900,7 +5008,7 @@ void HOptimizedGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) { set_current_block(first_test_block); } - // 2. Build all the tests, with dangling true branches + // 1. Build all the tests, with dangling true branches BailoutId default_id = BailoutId::None(); for (int i = 0; i < clause_count; ++i) { CaseClause* clause = clauses->at(i); @@ -5908,9 +5016,6 @@ void HOptimizedGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) { default_id = clause->EntryId(); continue; } - if (switch_type == SMI_SWITCH) { - clause->RecordTypeFeedback(oracle()); - } // Generate a compare and branch. CHECK_ALIVE(VisitForValue(clause->label())); @@ -5921,13 +5026,9 @@ void HOptimizedGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) { HControlInstruction* compare; - if (switch_type == SMI_SWITCH) { + if (stmt->switch_type() == SwitchStatement::SMI_SWITCH) { if (!clause->IsSmiCompare()) { - // Finish with deoptimize and add uses of enviroment values to - // account for invisible uses. - current_block()->FinishExitWithDeoptimization(HDeoptimize::kUseAll); - set_current_block(NULL); - break; + AddSoftDeoptimize(); } HCompareIDAndBranch* compare_ = @@ -5951,7 +5052,7 @@ void HOptimizedGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) { } // Save the current block to use for the default or to join with the - // exit. This block is NULL if we deoptimized. + // exit. HBasicBlock* last_block = current_block(); if (not_string_block != NULL) { @@ -5959,7 +5060,7 @@ void HOptimizedGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) { last_block = CreateJoin(last_block, not_string_block, join_id); } - // 3. Loop over the clauses and the linked list of tests in lockstep, + // 2. Loop over the clauses and the linked list of tests in lockstep, // translating the clause bodies. HBasicBlock* curr_test_block = first_test_block; HBasicBlock* fall_through_block = NULL; @@ -6246,7 +5347,7 @@ void HOptimizedGraphBuilder::VisitForInStatement(ForInStatement* stmt) { return Bailout("ForInStatement optimization is disabled"); } - if (!oracle()->IsForInFastCase(stmt)) { + if (stmt->for_in_type() != ForInStatement::FAST_FOR_IN) { return Bailout("ForInStatement is not fast case"); } @@ -6272,8 +5373,7 @@ void HOptimizedGraphBuilder::VisitForInStatement(ForInStatement* stmt) { HInstruction* enum_length = AddInstruction(new(zone()) HMapEnumLength(map)); - HInstruction* start_index = AddInstruction(new(zone()) HConstant( - Handle<Object>(Smi::FromInt(0), isolate()), Representation::Integer32())); + HInstruction* start_index = AddInstruction(new(zone()) HConstant(0)); Push(map); Push(array); @@ -6359,6 +5459,14 @@ void HOptimizedGraphBuilder::VisitForInStatement(ForInStatement* stmt) { } +void HOptimizedGraphBuilder::VisitForOfStatement(ForOfStatement* stmt) { + ASSERT(!HasStackOverflow()); + ASSERT(current_block() != NULL); + ASSERT(current_block()->HasPredecessor()); + return Bailout("ForOfStatement"); +} + + void HOptimizedGraphBuilder::VisitTryCatchStatement(TryCatchStatement* stmt) { ASSERT(!HasStackOverflow()); ASSERT(current_block() != NULL); @@ -6387,8 +5495,7 @@ void HOptimizedGraphBuilder::VisitDebuggerStatement(DebuggerStatement* stmt) { static Handle<SharedFunctionInfo> SearchSharedFunctionInfo( Code* unoptimized_code, FunctionLiteral* expr) { int start_position = expr->start_position(); - RelocIterator it(unoptimized_code); - for (;!it.done(); it.next()) { + for (RelocIterator it(unoptimized_code); !it.done(); it.next()) { RelocInfo* rinfo = it.rinfo(); if (rinfo->rmode() != RelocInfo::EMBEDDED_OBJECT) continue; Object* obj = rinfo->target_object(); @@ -6409,8 +5516,7 @@ void HOptimizedGraphBuilder::VisitFunctionLiteral(FunctionLiteral* expr) { ASSERT(current_block() != NULL); ASSERT(current_block()->HasPredecessor()); Handle<SharedFunctionInfo> shared_info = - SearchSharedFunctionInfo(info()->shared_info()->code(), - expr); + SearchSharedFunctionInfo(info()->shared_info()->code(), expr); if (shared_info.is_null()) { shared_info = Compiler::BuildFunctionInfo(expr, info()->script()); } @@ -6553,7 +5659,7 @@ void HOptimizedGraphBuilder::VisitVariableProxy(VariableProxy* expr) { case Variable::PARAMETER: case Variable::LOCAL: { - HValue* value = environment()->Lookup(variable); + HValue* value = LookupAndMakeLive(variable); if (value == graph()->GetConstantHole()) { ASSERT(IsDeclaredVariableMode(variable->mode()) && variable->mode() != VAR); @@ -6690,6 +5796,12 @@ static bool IsFastLiteral(Handle<JSObject> boilerplate, int* max_properties, int* data_size, int* pointer_size) { + if (boilerplate->map()->is_deprecated()) { + Handle<Object> result = + JSObject::TryMigrateInstance(boilerplate); + if (result->IsSmi()) return false; + } + ASSERT(max_depth >= 0 && *max_properties >= 0); if (max_depth == 0) return false; @@ -6836,7 +5948,6 @@ void HOptimizedGraphBuilder::VisitObjectLiteral(ObjectLiteral* expr) { case ObjectLiteral::Property::COMPUTED: if (key->handle()->IsInternalizedString()) { if (property->emit_store()) { - property->RecordTypeFeedback(oracle()); CHECK_ALIVE(VisitForValue(value)); HValue* value = Pop(); Handle<Map> map = property->GetReceiverType(); @@ -7007,18 +6118,11 @@ void HOptimizedGraphBuilder::VisitArrayLiteral(ArrayLiteral* expr) { elements = AddLoadElements(literal); - HValue* key = AddInstruction( - new(zone()) HConstant(Handle<Object>(Smi::FromInt(i), isolate()), - Representation::Integer32())); + HValue* key = AddInstruction(new(zone()) HConstant(i)); switch (boilerplate_elements_kind) { case FAST_SMI_ELEMENTS: case FAST_HOLEY_SMI_ELEMENTS: - if (!value->type().IsSmi()) { - // Smi-only arrays need a smi check. - AddInstruction(new(zone()) HCheckSmi(value)); - // Fall through. - } case FAST_ELEMENTS: case FAST_HOLEY_ELEMENTS: case FAST_DOUBLE_ELEMENTS: @@ -7064,20 +6168,6 @@ static bool ComputeLoadStoreField(Handle<Map> type, } -static int ComputeLoadStoreFieldIndex(Handle<Map> type, - LookupResult* lookup) { - ASSERT(lookup->IsField() || lookup->IsTransitionToField(*type)); - if (lookup->IsField()) { - return lookup->GetLocalFieldIndexFromMap(*type); - } else { - Map* transition = lookup->GetTransitionMapFromMap(*type); - int descriptor = transition->LastAdded(); - int index = transition->instance_descriptors()->GetFieldIndex(descriptor); - return index - type->inobject_properties(); - } -} - - static Representation ComputeLoadStoreRepresentation(Handle<Map> type, LookupResult* lookup) { if (lookup->IsField()) { @@ -7142,43 +6232,37 @@ HInstruction* HOptimizedGraphBuilder::BuildStoreNamedField( zone())); } - int index = ComputeLoadStoreFieldIndex(map, lookup); - bool is_in_object = index < 0; + HObjectAccess field_access = HObjectAccess::ForField(map, lookup, name); Representation representation = ComputeLoadStoreRepresentation(map, lookup); - int offset = index * kPointerSize; - if (index < 0) { - // Negative property indices are in-object properties, indexed - // from the end of the fixed part of the object. - offset += map->instance_size(); - } else { - offset += FixedArray::kHeaderSize; - } bool transition_to_field = lookup->IsTransitionToField(*map); + + HStoreNamedField *instr; if (FLAG_track_double_fields && representation.IsDouble()) { if (transition_to_field) { + // The store requires a mutable HeapNumber to be allocated. NoObservableSideEffectsScope no_side_effects(this); HInstruction* heap_number_size = AddInstruction(new(zone()) HConstant( HeapNumber::kSize, Representation::Integer32())); HInstruction* double_box = AddInstruction(new(zone()) HAllocate( environment()->LookupContext(), heap_number_size, HType::HeapNumber(), HAllocate::CAN_ALLOCATE_IN_NEW_SPACE)); - BuildStoreMap(double_box, isolate()->factory()->heap_number_map()); - AddInstruction(new(zone()) HStoreNamedField( - double_box, name, value, true, - Representation::Double(), HeapNumber::kValueOffset)); - value = double_box; - representation = Representation::Tagged(); + AddStoreMapConstant(double_box, isolate()->factory()->heap_number_map()); + AddStore(double_box, HObjectAccess::ForHeapNumberValue(), + value, Representation::Double()); + instr = new(zone()) HStoreNamedField(object, field_access, double_box); } else { - HInstruction* double_box = AddInstruction(new(zone()) HLoadNamedField( - object, is_in_object, Representation::Tagged(), offset)); + // Already holds a HeapNumber; load the box and write its value field. + HInstruction* double_box = AddLoad(object, field_access); double_box->set_type(HType::HeapNumber()); - return new(zone()) HStoreNamedField( - double_box, name, value, true, - Representation::Double(), HeapNumber::kValueOffset); + instr = new(zone()) HStoreNamedField(double_box, + HObjectAccess::ForHeapNumberValue(), value, Representation::Double()); } + } else { + // This is a non-double store. + instr = new(zone()) HStoreNamedField( + object, field_access, value, representation); } - HStoreNamedField* instr = new(zone()) HStoreNamedField( - object, name, value, is_in_object, representation, offset); + if (transition_to_field) { Handle<Map> transition(lookup->GetTransitionMapFromMap(*map)); instr->set_transition(transition); @@ -7248,9 +6332,10 @@ bool HOptimizedGraphBuilder::HandlePolymorphicArrayLengthLoad( BuildCheckNonSmi(object); HInstruction* typecheck = - AddInstruction(HCheckMaps::New(object, types, zone())); - HInstruction* instr = - HLoadNamedField::NewArrayLength(zone(), object, typecheck); + AddInstruction(HCheckMaps::New(object, types, zone())); + HInstruction* instr = new(zone()) + HLoadNamedField(object, HObjectAccess::ForArrayLength(), typecheck); + instr->set_position(expr->position()); ast_context()->ReturnInstruction(instr, expr->id()); return true; @@ -7270,53 +6355,42 @@ void HOptimizedGraphBuilder::HandlePolymorphicLoadNamedField(Property* expr, // Use monomorphic load if property lookup results in the same field index // for all maps. Requires special map check on the set of all handled maps. HInstruction* instr = NULL; - if (types->length() > 0 && types->length() <= kMaxLoadPolymorphism) { - LookupResult lookup(isolate()); - int previous_field_offset = 0; - bool previous_field_is_in_object = false; - Representation representation = Representation::None(); - int count; - for (count = 0; count < types->length(); ++count) { - Handle<Map> map = types->at(count); - if (!ComputeLoadStoreField(map, name, &lookup, false)) break; - - int index = ComputeLoadStoreFieldIndex(map, &lookup); - Representation new_representation = - ComputeLoadStoreRepresentation(map, &lookup); - bool is_in_object = index < 0; - int offset = index * kPointerSize; - - if (index < 0) { - // Negative property indices are in-object properties, indexed - // from the end of the fixed part of the object. - offset += map->instance_size(); - } else { - offset += FixedArray::kHeaderSize; - } - - if (count == 0) { - previous_field_offset = offset; - previous_field_is_in_object = is_in_object; - representation = new_representation; - } else if (offset != previous_field_offset || - is_in_object != previous_field_is_in_object || - (FLAG_track_fields && - !representation.IsCompatibleForLoad(new_representation))) { - break; - } - - representation = representation.generalize(new_representation); - } - - if (count == types->length()) { - AddInstruction(HCheckMaps::New(object, types, zone())); - instr = DoBuildLoadNamedField( - object, previous_field_is_in_object, - representation, previous_field_offset); + LookupResult lookup(isolate()); + int count; + Representation representation = Representation::None(); + HObjectAccess access = HObjectAccess::ForMap(); // initial value unused. + for (count = 0; + count < types->length() && count < kMaxLoadPolymorphism; + ++count) { + Handle<Map> map = types->at(count); + if (!ComputeLoadStoreField(map, name, &lookup, false)) break; + + HObjectAccess new_access = HObjectAccess::ForField(map, &lookup, name); + Representation new_representation = + ComputeLoadStoreRepresentation(map, &lookup); + + if (count == 0) { + // First time through the loop; set access and representation. + access = new_access; + representation = new_representation; + } else if (!representation.IsCompatibleForLoad(new_representation)) { + // Representations did not match. + break; + } else if (access.offset() != new_access.offset()) { + // Offsets did not match. + break; + } else if (access.IsInobject() != new_access.IsInobject()) { + // In-objectness did not match. + break; } } - if (instr == NULL) { + if (count == types->length()) { + // Everything matched; can use monomorphic load. + AddInstruction(HCheckMaps::New(object, types, zone())); + instr = BuildLoadNamedField(object, access, representation); + } else { + // Something did not match; must use a polymorphic load. HValue* context = environment()->LookupContext(); instr = new(zone()) HLoadNamedFieldPolymorphic( context, object, types, name, zone()); @@ -7407,7 +6481,6 @@ void HOptimizedGraphBuilder::HandlePolymorphicStoreNamedField( void HOptimizedGraphBuilder::HandlePropertyAssignment(Assignment* expr) { Property* prop = expr->target()->AsProperty(); ASSERT(prop != NULL); - expr->RecordTypeFeedback(oracle(), zone()); CHECK_ALIVE(VisitForValue(prop->obj())); if (prop->key()->IsPropertyName()) { @@ -7553,7 +6626,7 @@ void HOptimizedGraphBuilder::HandleCompoundAssignment(Assignment* expr) { if (var->mode() == CONST) { return Bailout("unsupported const compound assignment"); } - Bind(var, Top()); + BindIfLive(var, Top()); break; case Variable::CONTEXT: { @@ -7605,8 +6678,6 @@ void HOptimizedGraphBuilder::HandleCompoundAssignment(Assignment* expr) { return ast_context()->ReturnValue(Pop()); } else if (prop != NULL) { - prop->RecordTypeFeedback(oracle(), zone()); - if (prop->key()->IsPropertyName()) { // Named property. CHECK_ALIVE(VisitForValue(prop->obj())); @@ -7688,7 +6759,6 @@ void HOptimizedGraphBuilder::HandleCompoundAssignment(Assignment* expr) { Push(load); if (has_side_effects) AddSimulate(prop->LoadId(), REMOVABLE_SIMULATE); - CHECK_ALIVE(VisitForValue(expr->value())); HValue* right = Pop(); HValue* left = Pop(); @@ -7699,7 +6769,6 @@ void HOptimizedGraphBuilder::HandleCompoundAssignment(Assignment* expr) { AddSimulate(operation->id(), REMOVABLE_SIMULATE); } - expr->RecordTypeFeedback(oracle(), zone()); HandleKeyedElementAccess(obj, key, instr, expr, expr->AssignmentId(), RelocInfo::kNoPosition, true, // is_store @@ -7782,7 +6851,7 @@ void HOptimizedGraphBuilder::VisitAssignment(Assignment* expr) { // permitted. CHECK_ALIVE(VisitForValue(expr->value(), ARGUMENTS_ALLOWED)); HValue* value = Pop(); - Bind(var, value); + BindIfLive(var, value); return ast_context()->ReturnValue(value); } @@ -7873,40 +6942,22 @@ void HOptimizedGraphBuilder::VisitThrow(Throw* expr) { } -HLoadNamedField* HOptimizedGraphBuilder::BuildLoadNamedField( +HLoadNamedField* HGraphBuilder::BuildLoadNamedField( HValue* object, - Handle<Map> map, - LookupResult* lookup) { - int index = lookup->GetLocalFieldIndexFromMap(*map); - // Negative property indices are in-object properties, indexed from the end of - // the fixed part of the object. Non-negative property indices are in the - // properties array. - int inobject = index < 0; - Representation representation = lookup->representation(); - int offset = inobject - ? index * kPointerSize + map->instance_size() - : index * kPointerSize + FixedArray::kHeaderSize; - return DoBuildLoadNamedField(object, inobject, representation, offset); -} - - -HLoadNamedField* HGraphBuilder::DoBuildLoadNamedField( - HValue* object, - bool inobject, - Representation representation, - int offset) { + HObjectAccess access, + Representation representation) { bool load_double = false; if (representation.IsDouble()) { representation = Representation::Tagged(); load_double = FLAG_track_double_fields; } HLoadNamedField* field = - new(zone()) HLoadNamedField(object, inobject, representation, offset); + new(zone()) HLoadNamedField(object, access, NULL, representation); if (load_double) { AddInstruction(field); field->set_type(HType::HeapNumber()); - return new(zone()) HLoadNamedField( - field, true, Representation::Double(), HeapNumber::kValueOffset); + return new(zone()) HLoadNamedField(field, + HObjectAccess::ForHeapNumberValue(), NULL, Representation::Double()); } return field; } @@ -7947,7 +6998,8 @@ HInstruction* HOptimizedGraphBuilder::BuildLoadNamedMonomorphic( if (name->Equals(isolate()->heap()->length_string())) { if (map->instance_type() == JS_ARRAY_TYPE) { AddCheckMapsWithTransitions(object, map); - return HLoadNamedField::NewArrayLength(zone(), object, object); + return new(zone()) HLoadNamedField(object, + HObjectAccess::ForArrayLength()); } } @@ -7955,7 +7007,9 @@ HInstruction* HOptimizedGraphBuilder::BuildLoadNamedMonomorphic( map->LookupDescriptor(NULL, *name, &lookup); if (lookup.IsField()) { AddCheckMap(object, map); - return BuildLoadNamedField(object, map, &lookup); + return BuildLoadNamedField(object, + HObjectAccess::ForField(map, &lookup, name), + ComputeLoadStoreRepresentation(map, &lookup)); } // Handle a load of a constant known function. @@ -7974,9 +7028,11 @@ HInstruction* HOptimizedGraphBuilder::BuildLoadNamedMonomorphic( AddCheckMap(object, map); AddInstruction( new(zone()) HCheckPrototypeMaps(prototype, holder, zone())); - HValue* holder_value = AddInstruction( - new(zone()) HConstant(holder, Representation::Tagged())); - return BuildLoadNamedField(holder_value, holder_map, &lookup); + HValue* holder_value = AddInstruction(new(zone()) + HConstant(holder, Representation::Tagged())); + return BuildLoadNamedField(holder_value, + HObjectAccess::ForField(holder_map, &lookup, name), + ComputeLoadStoreRepresentation(map, &lookup)); } // Handle a load of a constant function somewhere in the prototype chain. @@ -8170,6 +7226,7 @@ HValue* HOptimizedGraphBuilder::HandlePolymorphicElementAccess( // If only one map is left after transitioning, handle this case // monomorphically. + ASSERT(num_untransitionable_maps >= 1); if (num_untransitionable_maps == 1) { HInstruction* instr = NULL; if (untransitionable_map->has_slow_elements_kind()) { @@ -8252,11 +7309,11 @@ HValue* HOptimizedGraphBuilder::HandlePolymorphicElementAccess( current_block()->Finish(typecheck); set_current_block(if_jsarray); - HInstruction* length; - length = AddInstruction( - HLoadNamedField::NewArrayLength(zone(), object, typecheck, - HType::Smi())); - checked_key = AddBoundsCheck(key, length, ALLOW_SMI_KEY); + HInstruction* length = AddLoad(object, HObjectAccess::ForArrayLength(), + typecheck, Representation::Smi()); + length->set_type(HType::Smi()); + + checked_key = AddBoundsCheck(key, length); access = AddInstruction(BuildFastElementAccess( elements, checked_key, val, elements_kind_branch, elements_kind, is_store, NEVER_RETURN_HOLE, STANDARD_STORE)); @@ -8274,7 +7331,7 @@ HValue* HOptimizedGraphBuilder::HandlePolymorphicElementAccess( set_current_block(if_fastobject); length = AddInstruction(new(zone()) HFixedArrayBaseLength(elements)); - checked_key = AddBoundsCheck(key, length, ALLOW_SMI_KEY); + checked_key = AddBoundsCheck(key, length); access = AddInstruction(BuildFastElementAccess( elements, checked_key, val, elements_kind_branch, elements_kind, is_store, NEVER_RETURN_HOLE, STANDARD_STORE)); @@ -8412,9 +7469,7 @@ bool HOptimizedGraphBuilder::TryArgumentsAccess(Property* expr) { // Number of arguments without receiver. int argument_count = environment()-> arguments_environment()->parameter_count() - 1; - result = new(zone()) HConstant( - Handle<Object>(Smi::FromInt(argument_count), isolate()), - Representation::Integer32()); + result = new(zone()) HConstant(argument_count); } } else { Push(graph()->GetArgumentsObject()); @@ -8437,8 +7492,7 @@ bool HOptimizedGraphBuilder::TryArgumentsAccess(Property* expr) { int argument_count = environment()-> arguments_environment()->parameter_count() - 1; HInstruction* length = AddInstruction(new(zone()) HConstant( - Handle<Object>(Smi::FromInt(argument_count), isolate()), - Representation::Integer32())); + argument_count)); HInstruction* checked_key = AddBoundsCheck(key, length); result = new(zone()) HAccessArgumentsAt(elements, length, checked_key); } @@ -8452,7 +7506,6 @@ void HOptimizedGraphBuilder::VisitProperty(Property* expr) { ASSERT(!HasStackOverflow()); ASSERT(current_block() != NULL); ASSERT(current_block()->HasPredecessor()); - expr->RecordTypeFeedback(oracle(), zone()); if (TryArgumentsAccess(expr)) return; @@ -8943,19 +7996,15 @@ bool HOptimizedGraphBuilder::TryInline(CallKind call_kind, // After this point, we've made a decision to inline this function (so // TryInline should always return true). - // Save the pending call context and type feedback oracle. Set up new ones - // for the inlined function. + // Type-check the inlined function. ASSERT(target_shared->has_deoptimization_support()); - Handle<Code> unoptimized_code(target_shared->code()); - TypeFeedbackOracle target_oracle( - unoptimized_code, - Handle<Context>(target->context()->native_context()), - isolate(), - zone()); + AstTyper::Type(&target_info); + + // Save the pending call context. Set up new one for the inlined function. // The function state is new-allocated because we need to delete it // in two different places. FunctionState* target_state = new FunctionState( - this, &target_info, &target_oracle, inlining_kind); + this, &target_info, inlining_kind); HConstant* undefined = graph()->GetConstantUndefined(); bool undefined_receiver = HEnvironment::UseUndefinedReceiver( @@ -9002,7 +8051,8 @@ bool HOptimizedGraphBuilder::TryInline(CallKind call_kind, function_state()->inlining_kind(), function->scope()->arguments(), arguments_values, - undefined_receiver); + undefined_receiver, + zone()); function_state()->set_entry(enter_inlined); AddInstruction(enter_inlined); @@ -9029,6 +8079,7 @@ bool HOptimizedGraphBuilder::TryInline(CallKind call_kind, // Update inlined nodes count. inlined_count_ += nodes_added; + Handle<Code> unoptimized_code(target_shared->code()); ASSERT(unoptimized_code->kind() == Code::FUNCTION); Handle<TypeFeedbackInfo> type_info( TypeFeedbackInfo::cast(unoptimized_code->type_feedback_info())); @@ -9081,6 +8132,8 @@ bool HOptimizedGraphBuilder::TryInline(CallKind call_kind, HBasicBlock* if_true = inlined_test_context()->if_true(); HBasicBlock* if_false = inlined_test_context()->if_false(); + HEnterInlined* entry = function_state()->entry(); + // Pop the return test context from the expression context stack. ASSERT(ast_context() == inlined_test_context()); ClearInlinedTestContext(); @@ -9088,11 +8141,13 @@ bool HOptimizedGraphBuilder::TryInline(CallKind call_kind, // Forward to the real test context. if (if_true->HasPredecessor()) { + entry->RegisterReturnTarget(if_true, zone()); if_true->SetJoinId(ast_id); HBasicBlock* true_target = TestContext::cast(ast_context())->if_true(); if_true->Goto(true_target, function_state()); } if (if_false->HasPredecessor()) { + entry->RegisterReturnTarget(if_false, zone()); if_false->SetJoinId(ast_id); HBasicBlock* false_target = TestContext::cast(ast_context())->if_false(); if_false->Goto(false_target, function_state()); @@ -9101,6 +8156,7 @@ bool HOptimizedGraphBuilder::TryInline(CallKind call_kind, return true; } else if (function_return()->HasPredecessor()) { + function_state()->entry()->RegisterReturnTarget(function_return(), zone()); function_return()->SetJoinId(ast_id); set_current_block(function_return()); } else { @@ -9245,7 +8301,8 @@ bool HOptimizedGraphBuilder::TryInlineBuiltinMethodCall( HValue* context = environment()->LookupContext(); ASSERT(!expr->holder().is_null()); AddInstruction(new(zone()) HCheckPrototypeMaps( - oracle()->GetPrototypeForPrimitiveCheck(STRING_CHECK), + Call::GetPrototypeForPrimitiveCheck(STRING_CHECK, + expr->holder()->GetIsolate()), expr->holder(), zone())); HInstruction* char_code = @@ -9311,10 +8368,8 @@ bool HOptimizedGraphBuilder::TryInlineBuiltinMethodCall( result = HUnaryMathOperation::New(zone(), context, left, kMathPowHalf); } else if (exponent == -0.5) { - HConstant* double_one = - new(zone()) HConstant(Handle<Object>(Smi::FromInt(1), - isolate()), - Representation::Double()); + HConstant* double_one = new(zone()) HConstant( + 1, Representation::Double()); AddInstruction(double_one); HInstruction* sqrt = HUnaryMathOperation::New(zone(), context, left, kMathPowHalf); @@ -9407,7 +8462,7 @@ bool HOptimizedGraphBuilder::TryCallApply(Call* expr) { VariableProxy* arg_two = args->at(1)->AsVariableProxy(); if (arg_two == NULL || !arg_two->var()->IsStackAllocated()) return false; - HValue* arg_two_value = environment()->Lookup(arg_two->var()); + HValue* arg_two_value = LookupAndMakeLive(arg_two->var()); if (!arg_two_value->CheckFlag(HValue::kIsArguments)) return false; // Found pattern f.apply(receiver, arguments). @@ -9560,8 +8615,6 @@ void HOptimizedGraphBuilder::VisitCall(Call* expr) { } // Named function call. - expr->RecordTypeFeedback(oracle(), CALL_AS_METHOD); - if (TryCallApply(expr)) return; CHECK_ALIVE(VisitForValue(prop->obj())); @@ -9627,7 +8680,6 @@ void HOptimizedGraphBuilder::VisitCall(Call* expr) { } } else { - expr->RecordTypeFeedback(oracle(), CALL_AS_FUNCTION); VariableProxy* proxy = expr->expression()->AsVariableProxy(); bool global_call = proxy != NULL && proxy->var()->IsUnallocated(); @@ -9755,7 +8807,8 @@ void HOptimizedGraphBuilder::VisitCall(Call* expr) { static bool IsAllocationInlineable(Handle<JSFunction> constructor) { return constructor->has_initial_map() && constructor->initial_map()->instance_type() == JS_OBJECT_TYPE && - constructor->initial_map()->instance_size() < HAllocateObject::kMaxSize; + constructor->initial_map()->instance_size() < HAllocate::kMaxInlineSize && + constructor->initial_map()->InitialPropertiesLength() == 0; } @@ -9763,9 +8816,9 @@ void HOptimizedGraphBuilder::VisitCallNew(CallNew* expr) { ASSERT(!HasStackOverflow()); ASSERT(current_block() != NULL); ASSERT(current_block()->HasPredecessor()); - expr->RecordTypeFeedback(oracle()); int argument_count = expr->arguments()->length() + 1; // Plus constructor. HValue* context = environment()->LookupContext(); + Factory* factory = isolate()->factory(); if (FLAG_inline_construct && expr->IsMonomorphic() && @@ -9785,20 +8838,84 @@ void HOptimizedGraphBuilder::VisitCallNew(CallNew* expr) { constructor->shared()->CompleteInobjectSlackTracking(); } - // Replace the constructor function with a newly allocated receiver. - HInstruction* receiver = new(zone()) HAllocateObject(context, constructor); - // Index of the receiver from the top of the expression stack. + // Calculate instance size from initial map of constructor. + ASSERT(constructor->has_initial_map()); + Handle<Map> initial_map(constructor->initial_map()); + int instance_size = initial_map->instance_size(); + ASSERT(initial_map->InitialPropertiesLength() == 0); + + // Allocate an instance of the implicit receiver object. + HValue* size_in_bytes = + AddInstruction(new(zone()) HConstant(instance_size, + Representation::Integer32())); + + HAllocate::Flags flags = HAllocate::DefaultFlags(); + if (FLAG_pretenuring_call_new && + isolate()->heap()->ShouldGloballyPretenure()) { + flags = static_cast<HAllocate::Flags>( + flags | HAllocate::CAN_ALLOCATE_IN_OLD_POINTER_SPACE); + } + + HInstruction* receiver = + AddInstruction(new(zone()) HAllocate(context, + size_in_bytes, + HType::JSObject(), + flags)); + HAllocate::cast(receiver)->set_known_initial_map(initial_map); + + // Load the initial map from the constructor. + HValue* constructor_value = + AddInstruction(new(zone()) HConstant(constructor, + Representation::Tagged())); + HValue* initial_map_value = + AddLoad(constructor_value, HObjectAccess::ForJSObjectOffset( + JSFunction::kPrototypeOrInitialMapOffset)); + + // Initialize map and fields of the newly allocated object. + { NoObservableSideEffectsScope no_effects(this); + ASSERT(initial_map->instance_type() == JS_OBJECT_TYPE); + AddStore(receiver, + HObjectAccess::ForJSObjectOffset(JSObject::kMapOffset), + initial_map_value); + HValue* empty_fixed_array = + AddInstruction(new(zone()) HConstant(factory->empty_fixed_array(), + Representation::Tagged())); + AddStore(receiver, + HObjectAccess::ForJSObjectOffset(JSObject::kPropertiesOffset), + empty_fixed_array); + AddStore(receiver, + HObjectAccess::ForJSObjectOffset(JSObject::kElementsOffset), + empty_fixed_array); + if (initial_map->inobject_properties() != 0) { + HConstant* undefined = graph()->GetConstantUndefined(); + for (int i = 0; i < initial_map->inobject_properties(); i++) { + int property_offset = JSObject::kHeaderSize + i * kPointerSize; + AddStore(receiver, + HObjectAccess::ForJSObjectOffset(property_offset), + undefined); + } + } + } + + // Replace the constructor function with a newly allocated receiver using + // the index of the receiver from the top of the expression stack. const int receiver_index = argument_count - 1; - AddInstruction(receiver); ASSERT(environment()->ExpressionStackAt(receiver_index) == function); environment()->SetExpressionStackAt(receiver_index, receiver); if (TryInlineConstruct(expr, receiver)) return; - // TODO(mstarzinger): For now we remove the previous HAllocateObject and - // add HPushArgument for the arguments in case inlining failed. What we - // actually should do is emit HInvokeFunction on the constructor instead - // of using HCallNew as a fallback. + // TODO(mstarzinger): For now we remove the previous HAllocate and all + // corresponding instructions and instead add HPushArgument for the + // arguments in case inlining failed. What we actually should do is for + // inlining to try to build a subgraph without mutating the parent graph. + HInstruction* instr = current_block()->last(); + while (instr != initial_map_value) { + HInstruction* prev_instr = instr->previous(); + instr->DeleteAndReplaceWith(NULL); + instr = prev_instr; + } + initial_map_value->DeleteAndReplaceWith(NULL); receiver->DeleteAndReplaceWith(NULL); check->DeleteAndReplaceWith(NULL); environment()->SetExpressionStackAt(receiver_index, function); @@ -9818,19 +8935,7 @@ void HOptimizedGraphBuilder::VisitCallNew(CallNew* expr) { CHECK_ALIVE(VisitArgumentList(expr->arguments())); HCallNew* call; if (use_call_new_array) { - // TODO(mvstanton): It would be better to use the already created global - // property cell that is shared by full code gen. That way, any transition - // information that happened after crankshaft won't be lost. The right - // way to do that is to begin passing the cell to the type feedback oracle - // instead of just the value in the cell. Do this in a follow-up checkin. - Handle<Object> feedback = oracle()->GetInfo(expr->CallNewFeedbackId()); - ASSERT(feedback->IsSmi()); - Handle<JSGlobalPropertyCell> cell = - isolate()->factory()->NewJSGlobalPropertyCell(feedback); - - // TODO(mvstanton): Here we should probably insert code to check if the - // type cell elements kind is different from when we compiled, and deopt - // in that case. Do this in a follow-up checin. + Handle<JSGlobalPropertyCell> cell = expr->allocation_info_cell(); call = new(zone()) HCallNewArray(context, constructor, argument_count, cell); } else { @@ -9912,6 +9017,7 @@ void HOptimizedGraphBuilder::VisitUnaryOperation(UnaryOperation* expr) { } } + void HOptimizedGraphBuilder::VisitDelete(UnaryOperation* expr) { Property* prop = expr->expression()->AsProperty(); VariableProxy* proxy = expr->expression()->AsVariableProxy(); @@ -9968,7 +9074,7 @@ void HOptimizedGraphBuilder::VisitSub(UnaryOperation* expr) { HValue* context = environment()->LookupContext(); HInstruction* instr = HMul::New(zone(), context, value, graph()->GetConstantMinus1()); - TypeInfo info = oracle()->UnaryType(expr); + TypeInfo info = expr->type(); Representation rep = ToRepresentation(info); if (info.IsUninitialized()) { AddSoftDeoptimize(); @@ -9985,7 +9091,7 @@ void HOptimizedGraphBuilder::VisitSub(UnaryOperation* expr) { void HOptimizedGraphBuilder::VisitBitNot(UnaryOperation* expr) { CHECK_ALIVE(VisitForValue(expr->expression())); HValue* value = Pop(); - TypeInfo info = oracle()->UnaryType(expr); + TypeInfo info = expr->type(); if (info.IsUninitialized()) { AddSoftDeoptimize(); } @@ -10042,7 +9148,7 @@ HInstruction* HOptimizedGraphBuilder::BuildIncrement( bool returns_original_input, CountOperation* expr) { // The input to the count operation is on top of the expression stack. - TypeInfo info = oracle()->IncrementType(expr); + TypeInfo info = expr->type(); Representation rep = ToRepresentation(info); if (rep.IsTagged()) { rep = Representation::Integer32(); @@ -10118,7 +9224,7 @@ void HOptimizedGraphBuilder::VisitCountOperation(CountOperation* expr) { case Variable::PARAMETER: case Variable::LOCAL: - Bind(var, after); + BindIfLive(var, after); break; case Variable::CONTEXT: { @@ -10156,7 +9262,6 @@ void HOptimizedGraphBuilder::VisitCountOperation(CountOperation* expr) { } else { // Argument of the count operation is a property. ASSERT(prop != NULL); - prop->RecordTypeFeedback(oracle(), zone()); if (prop->key()->IsPropertyName()) { // Named property. @@ -10239,7 +9344,6 @@ void HOptimizedGraphBuilder::VisitCountOperation(CountOperation* expr) { after = BuildIncrement(returns_original_input, expr); input = environment()->ExpressionStackAt(0); - expr->RecordTypeFeedback(oracle(), zone()); HandleKeyedElementAccess(obj, key, after, expr, expr->AssignmentId(), RelocInfo::kNoPosition, true, // is_store @@ -10274,7 +9378,7 @@ HInstruction* HOptimizedGraphBuilder::BuildStringCharCodeAt( if (i < 0 || i >= s->length()) { return new(zone()) HConstant(OS::nan_value(), Representation::Double()); } - return new(zone()) HConstant(s->Get(i), Representation::Integer32()); + return new(zone()) HConstant(s->Get(i)); } } BuildCheckNonSmi(string); @@ -10348,8 +9452,11 @@ HInstruction* HOptimizedGraphBuilder::BuildBinaryOperation( HValue* left, HValue* right) { HValue* context = environment()->LookupContext(); - TypeInfo left_info, right_info, result_info, combined_info; - oracle()->BinaryType(expr, &left_info, &right_info, &result_info); + TypeInfo left_info = expr->left_type(); + TypeInfo right_info = expr->right_type(); + TypeInfo result_info = expr->result_type(); + bool has_fixed_right_arg = expr->has_fixed_right_arg(); + int fixed_right_arg_value = expr->fixed_right_arg_value(); Representation left_rep = ToRepresentation(left_info); Representation right_rep = ToRepresentation(right_info); Representation result_rep = ToRepresentation(result_info); @@ -10379,7 +9486,12 @@ HInstruction* HOptimizedGraphBuilder::BuildBinaryOperation( instr = HMul::New(zone(), context, left, right); break; case Token::MOD: - instr = HMod::New(zone(), context, left, right); + instr = HMod::New(zone(), + context, + left, + right, + has_fixed_right_arg, + fixed_right_arg_value); break; case Token::DIV: instr = HDiv::New(zone(), context, left, right); @@ -10507,8 +9619,7 @@ void HOptimizedGraphBuilder::VisitLogicalExpression(BinaryOperation* expr) { // We need an extra block to maintain edge-split form. HBasicBlock* empty_block = graph()->CreateBasicBlock(); HBasicBlock* eval_right = graph()->CreateBasicBlock(); - TypeFeedbackId test_id = expr->left()->test_id(); - ToBooleanStub::Types expected(oracle()->ToBooleanTypes(test_id)); + ToBooleanStub::Types expected(expr->left()->to_boolean_types()); HBranch* test = is_logical_and ? new(zone()) HBranch(left_value, eval_right, empty_block, expected) : new(zone()) HBranch(left_value, empty_block, eval_right, expected); @@ -10676,16 +9787,17 @@ void HOptimizedGraphBuilder::VisitCompareOperation(CompareOperation* expr) { return ast_context()->ReturnControl(instr, expr->id()); } - TypeInfo left_type, right_type, overall_type_info; - oracle()->CompareType(expr, &left_type, &right_type, &overall_type_info); - Representation combined_rep = ToRepresentation(overall_type_info); + TypeInfo left_type = expr->left_type(); + TypeInfo right_type = expr->right_type(); + TypeInfo overall_type = expr->overall_type(); + Representation combined_rep = ToRepresentation(overall_type); Representation left_rep = ToRepresentation(left_type); Representation right_rep = ToRepresentation(right_type); // Check if this expression was ever executed according to type feedback. // Note that for the special typeof/null/undefined cases we get unknown here. - if (overall_type_info.IsUninitialized()) { + if (overall_type.IsUninitialized()) { AddSoftDeoptimize(); - overall_type_info = left_type = right_type = TypeInfo::Unknown(); + overall_type = left_type = right_type = TypeInfo::Unknown(); } CHECK_ALIVE(VisitForValue(expr->left())); @@ -10757,12 +9869,12 @@ void HOptimizedGraphBuilder::VisitCompareOperation(CompareOperation* expr) { HIn* result = new(zone()) HIn(context, left, right); result->set_position(expr->position()); return ast_context()->ReturnInstruction(result, expr->id()); - } else if (overall_type_info.IsNonPrimitive()) { + } else if (overall_type.IsNonPrimitive()) { switch (op) { case Token::EQ: case Token::EQ_STRICT: { // Can we get away with map check and not instance type check? - Handle<Map> map = oracle()->GetCompareMap(expr); + Handle<Map> map = expr->map(); if (!map.is_null()) { AddCheckMapsWithTransitions(left, map); AddCheckMapsWithTransitions(right, map); @@ -10784,7 +9896,7 @@ void HOptimizedGraphBuilder::VisitCompareOperation(CompareOperation* expr) { default: return Bailout("Unsupported non-primitive compare"); } - } else if (overall_type_info.IsInternalizedString() && + } else if (overall_type.IsInternalizedString() && Token::IsEqualityOp(op)) { BuildCheckNonSmi(left); AddInstruction(HCheckInstanceType::NewIsInternalizedString(left, zone())); @@ -10819,22 +9931,22 @@ void HOptimizedGraphBuilder::HandleLiteralCompareNil(CompareOperation* expr, ASSERT(!HasStackOverflow()); ASSERT(current_block() != NULL); ASSERT(current_block()->HasPredecessor()); - EqualityKind kind = - expr->op() == Token::EQ_STRICT ? kStrictEquality : kNonStrictEquality; HIfContinuation continuation; - TypeFeedbackId id = expr->CompareOperationFeedbackId(); CompareNilICStub::Types types; - if (kind == kStrictEquality) { - types.Add((nil == kNullValue) ? CompareNilICStub::NULL_TYPE : - CompareNilICStub::UNDEFINED); - } else { - types = CompareNilICStub::Types(oracle()->CompareNilTypes(id)); - if (types.IsEmpty()) { - types = CompareNilICStub::Types::FullCompare(); - } + if (expr->op() == Token::EQ_STRICT) { + IfBuilder if_nil(this); + if_nil.If<HCompareObjectEqAndBranch>( + value, (nil == kNullValue) ? graph()->GetConstantNull() + : graph()->GetConstantUndefined()); + if_nil.Then(); + if_nil.Else(); + if_nil.CaptureContinuation(&continuation); + return ast_context()->ReturnContinuation(&continuation, expr->id()); } - Handle<Map> map_handle(oracle()->CompareNilMonomorphicReceiverType(id)); - BuildCompareNil(value, kind, types, map_handle, + types = CompareNilICStub::Types(expr->compare_nil_types()); + if (types.IsEmpty()) types = CompareNilICStub::Types::FullCompare(); + Handle<Map> map_handle = expr->map(); + BuildCompareNil(value, types, map_handle, expr->position(), &continuation); return ast_context()->ReturnContinuation(&continuation, expr->id()); } @@ -10867,8 +9979,7 @@ HInstruction* HOptimizedGraphBuilder::BuildFastLiteral( HAllocate::Flags flags = HAllocate::CAN_ALLOCATE_IN_NEW_SPACE; // TODO(hpayer): add support for old data space - if (FLAG_pretenure_literals && - isolate()->heap()->ShouldGloballyPretenure() && + if (isolate()->heap()->ShouldGloballyPretenure() && data_size == 0) { flags = static_cast<HAllocate::Flags>( flags | HAllocate::CAN_ALLOCATE_IN_OLD_POINTER_SPACE); @@ -10896,15 +10007,7 @@ void HOptimizedGraphBuilder::BuildEmitDeepCopy( int* offset, AllocationSiteMode mode) { Zone* zone = this->zone(); - Factory* factory = isolate()->factory(); - - HInstruction* original_boilerplate = AddInstruction(new(zone) HConstant( - original_boilerplate_object, Representation::Tagged())); - - bool create_allocation_site_info = mode == TRACK_ALLOCATION_SITE && - boilerplate_object->map()->CanTrackAllocationSite(); - // Only elements backing stores for non-COW arrays need to be copied. Handle<FixedArrayBase> elements(boilerplate_object->elements()); Handle<FixedArrayBase> original_elements( original_boilerplate_object->elements()); @@ -10918,138 +10021,36 @@ void HOptimizedGraphBuilder::BuildEmitDeepCopy( elements->map() != isolate()->heap()->fixed_cow_array_map()) ? elements->Size() : 0; int elements_offset = *offset + object_size; - if (create_allocation_site_info) { - elements_offset += AllocationSiteInfo::kSize; - *offset += AllocationSiteInfo::kSize; - } *offset += object_size + elements_size; - HValue* object_elements = BuildCopyObjectHeader(boilerplate_object, target, + // Copy object elements if non-COW. + HValue* object_elements = BuildEmitObjectHeader(boilerplate_object, target, object_offset, elements_offset, elements_size); + if (object_elements != NULL) { + BuildEmitElements(elements, original_elements, kind, object_elements, + target, offset); + } // Copy in-object properties. HValue* object_properties = AddInstruction(new(zone) HInnerAllocatedObject(target, object_offset)); + BuildEmitInObjectProperties(boilerplate_object, original_boilerplate_object, + object_properties, target, offset); - Handle<DescriptorArray> descriptors( - boilerplate_object->map()->instance_descriptors()); - int limit = boilerplate_object->map()->NumberOfOwnDescriptors(); - - int copied_fields = 0; - for (int i = 0; i < limit; i++) { - PropertyDetails details = descriptors->GetDetails(i); - if (details.type() != FIELD) continue; - copied_fields++; - int index = descriptors->GetFieldIndex(i); - int property_offset = boilerplate_object->GetInObjectPropertyOffset(index); - Handle<Name> name(descriptors->GetKey(i)); - Handle<Object> value = - Handle<Object>(boilerplate_object->InObjectPropertyAt(index), - isolate()); - if (value->IsJSObject()) { - Handle<JSObject> value_object = Handle<JSObject>::cast(value); - Handle<JSObject> original_value_object = Handle<JSObject>::cast( - Handle<Object>(original_boilerplate_object->InObjectPropertyAt(index), - isolate())); - HInstruction* value_instruction = - AddInstruction(new(zone) HInnerAllocatedObject(target, *offset)); - AddInstruction(new(zone) HStoreNamedField( - object_properties, name, value_instruction, true, - Representation::Tagged(), property_offset)); - BuildEmitDeepCopy(value_object, original_value_object, target, - offset, DONT_TRACK_ALLOCATION_SITE); - } else { - Representation representation = details.representation(); - HInstruction* value_instruction = AddInstruction(new(zone) HConstant( - value, Representation::Tagged())); - if (representation.IsDouble()) { - HInstruction* double_box = - AddInstruction(new(zone) HInnerAllocatedObject(target, *offset)); - BuildStoreMap(double_box, factory->heap_number_map()); - AddInstruction(new(zone) HStoreNamedField( - double_box, name, value_instruction, true, - Representation::Double(), HeapNumber::kValueOffset)); - value_instruction = double_box; - *offset += HeapNumber::kSize; - } - AddInstruction(new(zone) HStoreNamedField( - object_properties, name, value_instruction, true, - Representation::Tagged(), property_offset)); - } - } - - int inobject_properties = boilerplate_object->map()->inobject_properties(); - HInstruction* value_instruction = AddInstruction(new(zone) HConstant( - factory->one_pointer_filler_map(), Representation::Tagged())); - for (int i = copied_fields; i < inobject_properties; i++) { - AddInstruction(new(zone) HStoreNamedField( - object_properties, factory->unknown_field_string(), value_instruction, - true, Representation::Tagged(), - boilerplate_object->GetInObjectPropertyOffset(i))); - } - - // Build Allocation Site Info if desired - if (create_allocation_site_info) { + // Create allocation site info. + if (mode == TRACK_ALLOCATION_SITE && + boilerplate_object->map()->CanTrackAllocationSite()) { + elements_offset += AllocationSiteInfo::kSize; + *offset += AllocationSiteInfo::kSize; + HInstruction* original_boilerplate = AddInstruction(new(zone) HConstant( + original_boilerplate_object, Representation::Tagged())); BuildCreateAllocationSiteInfo(target, JSArray::kSize, original_boilerplate); } - - if (object_elements != NULL) { - HInstruction* boilerplate_elements = AddInstruction(new(zone) HConstant( - elements, Representation::Tagged())); - - int elements_length = elements->length(); - HValue* object_elements_length = - AddInstruction(new(zone) HConstant( - elements_length, Representation::Integer32())); - - BuildInitializeElements(object_elements, kind, object_elements_length); - - // Copy elements backing store content. - if (elements->IsFixedDoubleArray()) { - for (int i = 0; i < elements_length; i++) { - HValue* key_constant = - AddInstruction(new(zone) HConstant(i, Representation::Integer32())); - HInstruction* value_instruction = - AddInstruction(new(zone) HLoadKeyed( - boilerplate_elements, key_constant, NULL, kind)); - AddInstruction(new(zone) HStoreKeyed( - object_elements, key_constant, value_instruction, kind)); - } - } else if (elements->IsFixedArray()) { - Handle<FixedArray> fast_elements = Handle<FixedArray>::cast(elements); - Handle<FixedArray> original_fast_elements = - Handle<FixedArray>::cast(original_elements); - for (int i = 0; i < elements_length; i++) { - Handle<Object> value(fast_elements->get(i), isolate()); - HValue* key_constant = - AddInstruction(new(zone) HConstant(i, Representation::Integer32())); - if (value->IsJSObject()) { - Handle<JSObject> value_object = Handle<JSObject>::cast(value); - Handle<JSObject> original_value_object = Handle<JSObject>::cast( - Handle<Object>(original_fast_elements->get(i), isolate())); - HInstruction* value_instruction = - AddInstruction(new(zone) HInnerAllocatedObject(target, *offset)); - AddInstruction(new(zone) HStoreKeyed( - object_elements, key_constant, value_instruction, kind)); - BuildEmitDeepCopy(value_object, original_value_object, target, - offset, DONT_TRACK_ALLOCATION_SITE); - } else { - HInstruction* value_instruction = - AddInstruction(new(zone) HLoadKeyed( - boilerplate_elements, key_constant, NULL, kind)); - AddInstruction(new(zone) HStoreKeyed( - object_elements, key_constant, value_instruction, kind)); - } - } - } else { - UNREACHABLE(); - } - } } -HValue* HOptimizedGraphBuilder::BuildCopyObjectHeader( +HValue* HOptimizedGraphBuilder::BuildEmitObjectHeader( Handle<JSObject> boilerplate_object, HInstruction* target, int object_offset, @@ -11057,13 +10058,12 @@ HValue* HOptimizedGraphBuilder::BuildCopyObjectHeader( int elements_size) { ASSERT(boilerplate_object->properties()->length() == 0); Zone* zone = this->zone(); - Factory* factory = isolate()->factory(); HValue* result = NULL; HValue* object_header = AddInstruction(new(zone) HInnerAllocatedObject(target, object_offset)); Handle<Map> boilerplate_object_map(boilerplate_object->map()); - BuildStoreMap(object_header, boilerplate_object_map); + AddStoreMapConstant(object_header, boilerplate_object_map); HInstruction* elements; if (elements_size == 0) { @@ -11076,23 +10076,15 @@ HValue* HOptimizedGraphBuilder::BuildCopyObjectHeader( target, elements_offset)); result = elements; } - HInstruction* elements_store = AddInstruction(new(zone) HStoreNamedField( - object_header, - factory->elements_field_string(), - elements, - true, Representation::Tagged(), JSObject::kElementsOffset)); - elements_store->SetGVNFlag(kChangesElementsPointer); + AddStore(object_header, HObjectAccess::ForElementsPointer(), elements); Handle<Object> properties_field = Handle<Object>(boilerplate_object->properties(), isolate()); ASSERT(*properties_field == isolate()->heap()->empty_fixed_array()); HInstruction* properties = AddInstruction(new(zone) HConstant( properties_field, Representation::None())); - AddInstruction(new(zone) HStoreNamedField(object_header, - factory->empty_string(), - properties, true, - Representation::Tagged(), - JSObject::kPropertiesOffset)); + HObjectAccess access = HObjectAccess::ForPropertiesPointer(); + AddStore(object_header, access, properties); if (boilerplate_object->IsJSArray()) { Handle<JSArray> boilerplate_array = @@ -11101,22 +10093,178 @@ HValue* HOptimizedGraphBuilder::BuildCopyObjectHeader( Handle<Object>(boilerplate_array->length(), isolate()); HInstruction* length = AddInstruction(new(zone) HConstant( length_field, Representation::None())); + ASSERT(boilerplate_array->length()->IsSmi()); Representation representation = IsFastElementsKind(boilerplate_array->GetElementsKind()) ? Representation::Smi() : Representation::Tagged(); - HInstruction* length_store = AddInstruction(new(zone) HStoreNamedField( - object_header, - factory->length_field_string(), - length, - true, representation, JSArray::kLengthOffset)); - length_store->SetGVNFlag(kChangesArrayLengths); + AddStore(object_header, HObjectAccess::ForArrayLength(), + length, representation); } return result; } +void HOptimizedGraphBuilder::BuildEmitInObjectProperties( + Handle<JSObject> boilerplate_object, + Handle<JSObject> original_boilerplate_object, + HValue* object_properties, + HInstruction* target, + int* offset) { + Zone* zone = this->zone(); + Handle<DescriptorArray> descriptors( + boilerplate_object->map()->instance_descriptors()); + int limit = boilerplate_object->map()->NumberOfOwnDescriptors(); + + int copied_fields = 0; + for (int i = 0; i < limit; i++) { + PropertyDetails details = descriptors->GetDetails(i); + if (details.type() != FIELD) continue; + copied_fields++; + int index = descriptors->GetFieldIndex(i); + int property_offset = boilerplate_object->GetInObjectPropertyOffset(index); + Handle<Name> name(descriptors->GetKey(i)); + Handle<Object> value = + Handle<Object>(boilerplate_object->InObjectPropertyAt(index), + isolate()); + + // The access for the store depends on the type of the boilerplate. + HObjectAccess access = boilerplate_object->IsJSArray() ? + HObjectAccess::ForJSArrayOffset(property_offset) : + HObjectAccess::ForJSObjectOffset(property_offset); + + if (value->IsJSObject()) { + Handle<JSObject> value_object = Handle<JSObject>::cast(value); + Handle<JSObject> original_value_object = Handle<JSObject>::cast( + Handle<Object>(original_boilerplate_object->InObjectPropertyAt(index), + isolate())); + HInstruction* value_instruction = + AddInstruction(new(zone) HInnerAllocatedObject(target, *offset)); + + AddStore(object_properties, access, value_instruction); + + BuildEmitDeepCopy(value_object, original_value_object, target, + offset, DONT_TRACK_ALLOCATION_SITE); + } else { + Representation representation = details.representation(); + HInstruction* value_instruction = AddInstruction(new(zone) HConstant( + value, Representation::Tagged())); + + if (representation.IsDouble()) { + // Allocate a HeapNumber box and store the value into it. + HInstruction* double_box = + AddInstruction(new(zone) HInnerAllocatedObject(target, *offset)); + AddStoreMapConstant(double_box, + isolate()->factory()->heap_number_map()); + AddStore(double_box, HObjectAccess::ForHeapNumberValue(), + value_instruction, Representation::Double()); + value_instruction = double_box; + *offset += HeapNumber::kSize; + } + + AddStore(object_properties, access, value_instruction); + } + } + + int inobject_properties = boilerplate_object->map()->inobject_properties(); + HInstruction* value_instruction = AddInstruction(new(zone) + HConstant(isolate()->factory()->one_pointer_filler_map(), + Representation::Tagged())); + for (int i = copied_fields; i < inobject_properties; i++) { + ASSERT(boilerplate_object->IsJSObject()); + int property_offset = boilerplate_object->GetInObjectPropertyOffset(i); + HObjectAccess access = HObjectAccess::ForJSObjectOffset(property_offset); + AddStore(object_properties, access, value_instruction); + } +} + + +void HOptimizedGraphBuilder::BuildEmitElements( + Handle<FixedArrayBase> elements, + Handle<FixedArrayBase> original_elements, + ElementsKind kind, + HValue* object_elements, + HInstruction* target, + int* offset) { + Zone* zone = this->zone(); + + int elements_length = elements->length(); + HValue* object_elements_length = + AddInstruction(new(zone) HConstant(elements_length)); + + BuildInitializeElementsHeader(object_elements, kind, object_elements_length); + + // Copy elements backing store content. + if (elements->IsFixedDoubleArray()) { + BuildEmitFixedDoubleArray(elements, kind, object_elements); + } else if (elements->IsFixedArray()) { + BuildEmitFixedArray(elements, original_elements, kind, object_elements, + target, offset); + } else { + UNREACHABLE(); + } +} + + +void HOptimizedGraphBuilder::BuildEmitFixedDoubleArray( + Handle<FixedArrayBase> elements, + ElementsKind kind, + HValue* object_elements) { + Zone* zone = this->zone(); + HInstruction* boilerplate_elements = AddInstruction(new(zone) HConstant( + elements, Representation::Tagged())); + int elements_length = elements->length(); + for (int i = 0; i < elements_length; i++) { + HValue* key_constant = AddInstruction(new(zone) HConstant(i)); + HInstruction* value_instruction = + AddInstruction(new(zone) HLoadKeyed( + boilerplate_elements, key_constant, NULL, kind, ALLOW_RETURN_HOLE)); + HInstruction* store = AddInstruction(new(zone) HStoreKeyed( + object_elements, key_constant, value_instruction, kind)); + store->SetFlag(HValue::kAllowUndefinedAsNaN); + } +} + + +void HOptimizedGraphBuilder::BuildEmitFixedArray( + Handle<FixedArrayBase> elements, + Handle<FixedArrayBase> original_elements, + ElementsKind kind, + HValue* object_elements, + HInstruction* target, + int* offset) { + Zone* zone = this->zone(); + HInstruction* boilerplate_elements = AddInstruction(new(zone) HConstant( + elements, Representation::Tagged())); + int elements_length = elements->length(); + Handle<FixedArray> fast_elements = Handle<FixedArray>::cast(elements); + Handle<FixedArray> original_fast_elements = + Handle<FixedArray>::cast(original_elements); + for (int i = 0; i < elements_length; i++) { + Handle<Object> value(fast_elements->get(i), isolate()); + HValue* key_constant = AddInstruction(new(zone) HConstant(i)); + if (value->IsJSObject()) { + Handle<JSObject> value_object = Handle<JSObject>::cast(value); + Handle<JSObject> original_value_object = Handle<JSObject>::cast( + Handle<Object>(original_fast_elements->get(i), isolate())); + HInstruction* value_instruction = + AddInstruction(new(zone) HInnerAllocatedObject(target, *offset)); + AddInstruction(new(zone) HStoreKeyed( + object_elements, key_constant, value_instruction, kind)); + BuildEmitDeepCopy(value_object, original_value_object, target, + offset, DONT_TRACK_ALLOCATION_SITE); + } else { + HInstruction* value_instruction = + AddInstruction(new(zone) HLoadKeyed( + boilerplate_elements, key_constant, NULL, kind, + ALLOW_RETURN_HOLE)); + AddInstruction(new(zone) HStoreKeyed( + object_elements, key_constant, value_instruction, kind)); + } + } +} + void HOptimizedGraphBuilder::VisitThisFunction(ThisFunction* expr) { ASSERT(!HasStackOverflow()); ASSERT(current_block() != NULL); @@ -11201,7 +10349,7 @@ void HOptimizedGraphBuilder::VisitFunctionDeclaration( case Variable::LOCAL: { CHECK_ALIVE(VisitForValue(declaration->fun())); HValue* value = Pop(); - environment()->Bind(variable, value); + BindIfLive(variable, value); break; } case Variable::CONTEXT: { @@ -11459,9 +10607,6 @@ void HOptimizedGraphBuilder::GenerateTwoByteSeqStringSetChar( HValue* value = Pop(); HValue* index = Pop(); HValue* string = Pop(); - HValue* context = environment()->LookupContext(); - HInstruction* char_code = BuildStringCharCodeAt(context, string, index); - AddInstruction(char_code); HSeqStringSetChar* result = new(zone()) HSeqStringSetChar( String::TWO_BYTE_ENCODING, string, index, value); return ast_context()->ReturnInstruction(result, call->id()); @@ -11497,13 +10642,8 @@ void HOptimizedGraphBuilder::GenerateSetValueOf(CallRuntime* call) { // Create in-object property store to kValueOffset. set_current_block(if_js_value); - Handle<String> name = isolate()->factory()->undefined_string(); - AddInstruction(new(zone()) HStoreNamedField(object, - name, - value, - true, // in-object store. - Representation::Tagged(), - JSValue::kValueOffset)); + AddStore(object, + HObjectAccess::ForJSObjectOffset(JSValue::kValueOffset), value); if_js_value->Goto(join); join->SetJoinId(call->id()); set_current_block(join); @@ -11790,8 +10930,8 @@ void HOptimizedGraphBuilder::GenerateFastAsciiArrayJoin(CallRuntime* call) { // Support for generators. -void HOptimizedGraphBuilder::GenerateGeneratorSend(CallRuntime* call) { - return Bailout("inlined runtime function: GeneratorSend"); +void HOptimizedGraphBuilder::GenerateGeneratorNext(CallRuntime* call) { + return Bailout("inlined runtime function: GeneratorNext"); } @@ -12120,14 +11260,16 @@ void HTracer::TraceCompilation(CompilationInfo* info) { void HTracer::TraceLithium(const char* name, LChunk* chunk) { ASSERT(!FLAG_parallel_recompilation); - ALLOW_HANDLE_DEREF(chunk->isolate(), "debug output"); + AllowHandleDereference allow_deref; + AllowDeferredHandleDereference allow_deferred_deref; Trace(name, chunk->graph(), chunk); } void HTracer::TraceHydrogen(const char* name, HGraph* graph) { ASSERT(!FLAG_parallel_recompilation); - ALLOW_HANDLE_DEREF(graph->isolate(), "debug output"); + AllowHandleDereference allow_deref; + AllowDeferredHandleDereference allow_deferred_deref; Trace(name, graph, NULL); } |