Chromium Code Reviews| Index: src/hydrogen.cc |
| diff --git a/src/hydrogen.cc b/src/hydrogen.cc |
| index c2f3951494e647936183cdb1d25659c8d5ee187f..3f827b38bc0845093994f053a8cb600710ea4767 100644 |
| --- a/src/hydrogen.cc |
| +++ b/src/hydrogen.cc |
| @@ -114,6 +114,8 @@ void HBasicBlock::AddInstruction(HInstruction* instr) { |
| ASSERT(!instr->IsLinked()); |
| ASSERT(!IsFinished()); |
| if (first_ == NULL) { |
| + ASSERT(last_environment() != NULL); |
| + ASSERT(!last_environment()->ast_id().IsNone()); |
| HBlockEntry* entry = new(zone()) HBlockEntry(); |
| entry->InitializeAsFirst(this); |
| first_ = last_ = entry; |
| @@ -231,6 +233,7 @@ void HBasicBlock::SetJoinId(BailoutId ast_id) { |
| predecessor->last_environment()->closure()->shared() |
| ->VerifyBailoutId(ast_id))); |
| simulate->set_ast_id(ast_id); |
| + predecessor->last_environment()->set_ast_id(ast_id); |
| } |
| } |
| @@ -644,37 +647,51 @@ HGraphBuilder::CheckBuilder::CheckBuilder(HGraphBuilder* builder, BailoutId id) |
| finished_(false), |
| id_(id) { |
| HEnvironment* env = builder->environment(); |
| - failure_block_ = builder->CreateBasicBlock(env->Copy()); |
| - merge_block_ = builder->CreateBasicBlock(env->Copy()); |
| + failure_block_ = builder->CreateBasicBlock(env->CopyWithoutHistory()); |
| + merge_block_ = builder->CreateBasicBlock(env->CopyWithoutHistory()); |
| } |
| -void HGraphBuilder::CheckBuilder::CheckNotUndefined(HValue* value) { |
| +HValue* HGraphBuilder::CheckBuilder::CheckNotUndefined(HValue* value) { |
| HEnvironment* env = builder_->environment(); |
| HIsNilAndBranch* compare = |
| new(zone()) HIsNilAndBranch(value, kStrictEquality, kUndefinedValue); |
| - HBasicBlock* success_block = builder_->CreateBasicBlock(env->Copy()); |
| - HBasicBlock* failure_block = builder_->CreateBasicBlock(env->Copy()); |
| + HBasicBlock* success_block = |
| + builder_->CreateBasicBlock(env->CopyWithoutHistory()); |
| + HBasicBlock* failure_block = |
| + builder_->CreateBasicBlock(env->CopyWithoutHistory()); |
| compare->SetSuccessorAt(0, failure_block); |
| compare->SetSuccessorAt(1, success_block); |
| failure_block->Goto(failure_block_); |
| builder_->current_block()->Finish(compare); |
| builder_->set_current_block(success_block); |
| + return compare; |
| } |
| -void HGraphBuilder::CheckBuilder::CheckIntegerEq(HValue* left, HValue* right) { |
| +HValue* HGraphBuilder::CheckBuilder::CheckIntegerCompare(HValue* left, |
| + HValue* right, |
| + Token::Value op) { |
| HEnvironment* env = builder_->environment(); |
| HCompareIDAndBranch* compare = |
| - new(zone()) HCompareIDAndBranch(left, right, Token::EQ); |
| + new(zone()) HCompareIDAndBranch(left, right, op); |
| compare->AssumeRepresentation(Representation::Integer32()); |
| - HBasicBlock* success_block = builder_->CreateBasicBlock(env->Copy()); |
| - HBasicBlock* failure_block = builder_->CreateBasicBlock(env->Copy()); |
| + HBasicBlock* success_block = |
| + builder_->CreateBasicBlock(env->CopyWithoutHistory()); |
| + HBasicBlock* failure_block = |
| + builder_->CreateBasicBlock(env->CopyWithoutHistory()); |
| compare->SetSuccessorAt(0, success_block); |
| compare->SetSuccessorAt(1, failure_block); |
| failure_block->Goto(failure_block_); |
| builder_->current_block()->Finish(compare); |
| builder_->set_current_block(success_block); |
| + return compare; |
| +} |
| + |
| + |
| +HValue* HGraphBuilder::CheckBuilder::CheckIntegerEq(HValue* left, |
| + HValue* right) { |
| + return CheckIntegerCompare(left, right, Token::EQ); |
| } |
| @@ -692,6 +709,7 @@ void HGraphBuilder::CheckBuilder::End() { |
| HGraphBuilder::IfBuilder::IfBuilder(HGraphBuilder* builder, BailoutId id) |
| : builder_(builder), |
| finished_(false), |
| + did_else_(false), |
| id_(id) { |
| HEnvironment* env = builder->environment(); |
| first_true_block_ = builder->CreateBasicBlock(env->Copy()); |
| @@ -700,7 +718,7 @@ HGraphBuilder::IfBuilder::IfBuilder(HGraphBuilder* builder, BailoutId id) |
| } |
| -HInstruction* HGraphBuilder::IfBuilder::BeginTrue( |
| +HInstruction* HGraphBuilder::IfBuilder::BeginIf( |
| HValue* left, |
| HValue* right, |
| Token::Value token, |
| @@ -718,20 +736,45 @@ HInstruction* HGraphBuilder::IfBuilder::BeginTrue( |
| } |
| -void HGraphBuilder::IfBuilder::BeginFalse() { |
| +HInstruction* HGraphBuilder::IfBuilder::BeginIfObjectsEqual( |
| + HValue* left, |
| + HValue* right) { |
| + HCompareObjectEqAndBranch* compare = |
| + new(zone()) HCompareObjectEqAndBranch(left, right); |
| + compare->SetSuccessorAt(0, first_true_block_); |
| + compare->SetSuccessorAt(1, first_false_block_); |
| + builder_->current_block()->Finish(compare); |
| + builder_->set_current_block(first_true_block_); |
| + return compare; |
| +} |
| + |
| + |
| +HInstruction* HGraphBuilder::IfBuilder::BeginIfMapsEqual(HValue* value, |
| + Handle<Map> map) { |
| + HCompareMap* compare = new(zone()) |
| + HCompareMap(value, map, first_true_block_, first_false_block_); |
| + builder_->current_block()->Finish(compare); |
| + builder_->set_current_block(first_true_block_); |
| + return compare; |
| +} |
| + |
| + |
| +void HGraphBuilder::IfBuilder::BeginElse() { |
| last_true_block_ = builder_->current_block(); |
| ASSERT(!last_true_block_->IsFinished()); |
| builder_->set_current_block(first_false_block_); |
| + did_else_ = true; |
| } |
| void HGraphBuilder::IfBuilder::End() { |
| ASSERT(!finished_); |
| + if (!did_else_) BeginElse(); |
| ASSERT(!last_true_block_->IsFinished()); |
| HBasicBlock* last_false_block = builder_->current_block(); |
| ASSERT(!last_false_block->IsFinished()); |
| HEnvironment* merge_env = |
| - last_true_block_->last_environment()->Copy(); |
| + last_true_block_->last_environment()->CopyWithoutHistory(); |
| merge_block_ = builder_->CreateBasicBlock(merge_env); |
| last_true_block_->Goto(merge_block_); |
| last_false_block->Goto(merge_block_); |
| @@ -853,6 +896,7 @@ void HGraphBuilder::AddSimulate(BailoutId id, |
| RemovableSimulate removable) { |
| ASSERT(current_block() != NULL); |
| current_block()->AddSimulate(id, removable); |
| + environment()->set_ast_id(id); |
| } |
| @@ -950,7 +994,8 @@ HInstruction* HGraphBuilder::BuildFastElementAccess( |
| HValue* val, |
| HValue* load_dependency, |
| ElementsKind elements_kind, |
| - bool is_store) { |
| + bool is_store, |
| + KeyedAccessStoreMode store_mode) { |
| Zone* zone = this->zone(); |
| if (is_store) { |
| ASSERT(val != NULL); |
| @@ -978,6 +1023,100 @@ HInstruction* HGraphBuilder::BuildFastElementAccess( |
| } |
| +HValue* HGraphBuilder::BuildCheckForCapacityGrow(HValue* object, |
| + HValue* elements, |
| + ElementsKind kind, |
| + HValue* length, |
| + HValue* key, |
| + bool is_js_array) { |
| + BailoutId ast_id = environment()->ast_id(); |
| + Zone* zone = this->zone(); |
| + IfBuilder length_checker(this, ast_id); |
| + |
| + length_checker.BeginIf(length, key, Token::EQ); |
| + |
| + HValue* current_capacity = |
| + AddInstruction(new(zone) HFixedArrayBaseLength(elements)); |
| + |
| + IfBuilder capacity_checker(this, ast_id); |
| + |
| + capacity_checker.BeginIf(length, current_capacity, Token::EQ); |
| + |
| + HValue* context = environment()->LookupContext(); |
| + |
| + HValue* new_capacity = |
| + BuildNewElementsCapacity(context, current_capacity); |
| + |
| + HValue* new_elements = BuildGrowElementsCapacity(object, elements, |
| + kind, length, |
| + new_capacity, ast_id); |
| + |
| + environment()->Push(new_elements); |
| + capacity_checker.BeginElse(); |
| + |
| + environment()->Push(elements); |
| + capacity_checker.End(); |
| + |
| + if (is_js_array) { |
| + HValue* new_length = AddInstruction( |
| + HAdd::New(zone, context, length, graph_->GetConstant1())); |
| + new_length->ChangeRepresentation(Representation::Integer32()); |
| + new_length->ClearFlag(HValue::kCanOverflow); |
| + AddSimulate(ast_id, REMOVABLE_SIMULATE); |
| + |
| + Factory* factory = isolate()->factory(); |
| + HInstruction* length_store = AddInstruction(new(zone) HStoreNamedField( |
| + object, |
| + factory->length_field_string(), |
| + new_length, true, |
| + JSArray::kLengthOffset)); |
| + length_store->SetGVNFlag(kChangesArrayLengths); |
| + AddSimulate(ast_id, REMOVABLE_SIMULATE); |
| + } |
| + |
| + length_checker.BeginElse(); |
| + |
| + AddBoundsCheck(key, length, ALLOW_SMI_KEY); |
| + environment()->Push(elements); |
| + |
| + length_checker.End(); |
| + |
| + return environment()->Pop(); |
| +} |
| + |
| + |
| +HValue* HGraphBuilder::BuildCopyElementsOnWrite(HValue* object, |
| + HValue* elements, |
| + ElementsKind kind, |
| + HValue* length) { |
| + BailoutId ast_id = environment()->ast_id(); |
| + Zone* zone = this->zone(); |
| + Heap* heap = isolate()->heap(); |
| + |
| + IfBuilder cow_checker(this, ast_id); |
| + |
| + cow_checker.BeginIfMapsEqual(elements, |
| + Handle<Map>(heap->fixed_cow_array_map())); |
| + |
| + HValue* capacity = |
| + AddInstruction(new(zone) HFixedArrayBaseLength(elements)); |
| + |
| + HValue* new_elements = BuildGrowElementsCapacity(object, elements, |
| + kind, length, |
| + capacity, ast_id); |
| + |
| + environment()->Push(new_elements); |
| + |
| + cow_checker.BeginElse(); |
| + |
| + environment()->Push(elements); |
| + |
| + cow_checker.End(); |
| + |
| + return environment()->Pop(); |
| +} |
| + |
| + |
| HInstruction* HGraphBuilder::BuildUncheckedMonomorphicElementAccess( |
| HValue* object, |
| HValue* key, |
| @@ -986,7 +1125,9 @@ HInstruction* HGraphBuilder::BuildUncheckedMonomorphicElementAccess( |
| bool is_js_array, |
| ElementsKind elements_kind, |
| bool is_store, |
| + KeyedAccessStoreMode store_mode, |
| Representation checked_index_representation) { |
| + ASSERT(!IsExternalArrayElementsKind(elements_kind) || !is_js_array); |
| Zone* zone = this->zone(); |
| // No GVNFlag is necessary for ElementsKind if there is an explicit dependency |
| // on a HElementsTransition instruction. The flag can also be removed if the |
| @@ -1002,46 +1143,93 @@ HInstruction* HGraphBuilder::BuildUncheckedMonomorphicElementAccess( |
| } |
| bool fast_smi_only_elements = IsFastSmiElementsKind(elements_kind); |
| bool fast_elements = IsFastObjectElementsKind(elements_kind); |
| - HInstruction* elements = |
| + HValue* elements = |
| AddInstruction(new(zone) HLoadElements(object, mapcheck)); |
| - if (is_store && (fast_elements || fast_smi_only_elements)) { |
| + if (is_store && (fast_elements || fast_smi_only_elements) && |
| + store_mode != STORE_NO_TRANSITION_HANDLE_COW) { |
| HCheckMaps* check_cow_map = new(zone) HCheckMaps( |
| elements, isolate()->factory()->fixed_array_map(), zone); |
| check_cow_map->ClearGVNFlag(kDependsOnElementsKind); |
| AddInstruction(check_cow_map); |
| } |
| HInstruction* length = NULL; |
| - HInstruction* checked_key = NULL; |
| - if (IsExternalArrayElementsKind(elements_kind)) { |
| + if (is_js_array) { |
| + length = AddInstruction(new(zone) HJSArrayLength(object, mapcheck, |
| + HType::Smi())); |
| + } else { |
| length = AddInstruction(new(zone) HFixedArrayBaseLength(elements)); |
| - checked_key = AddBoundsCheck( |
| - key, length, ALLOW_SMI_KEY, checked_index_representation); |
| - HLoadExternalArrayPointer* external_elements = |
| - new(zone) HLoadExternalArrayPointer(elements); |
| - AddInstruction(external_elements); |
| - return BuildExternalArrayElementAccess( |
| - external_elements, checked_key, val, mapcheck, |
| - elements_kind, is_store); |
| + } |
| + HValue* checked_key = NULL; |
| + if (IsExternalArrayElementsKind(elements_kind)) { |
| + if (store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS) { |
| + HLoadExternalArrayPointer* external_elements = |
| + new(zone) HLoadExternalArrayPointer(elements); |
| + AddInstruction(external_elements); |
| + BailoutId previous_id = environment()->ast_id(); |
| + ASSERT(!previous_id.IsNone()); |
| + IfBuilder length_checker(this, previous_id); |
| + length_checker.BeginIf(key, length, Token::LT); |
| + CheckBuilder negative_checker(this, previous_id); |
| + HValue* bounds_check = negative_checker.CheckIntegerCompare( |
| + key, graph()->GetConstant0(), Token::GTE); |
| + negative_checker.End(); |
| + HInstruction* result = BuildExternalArrayElementAccess( |
| + external_elements, key, val, bounds_check, |
| + elements_kind, is_store); |
| + AddInstruction(result); |
| + length_checker.End(); |
| + return result; |
| + } else { |
| + ASSERT(store_mode == STANDARD_STORE); |
| + checked_key = AddBoundsCheck( |
| + key, length, ALLOW_SMI_KEY, checked_index_representation); |
| + HLoadExternalArrayPointer* external_elements = |
| + new(zone) HLoadExternalArrayPointer(elements); |
| + AddInstruction(external_elements); |
| + return AddInstruction(BuildExternalArrayElementAccess( |
| + external_elements, checked_key, val, mapcheck, |
| + elements_kind, is_store)); |
| + } |
| } |
| ASSERT(fast_smi_only_elements || |
| fast_elements || |
| IsFastDoubleElementsKind(elements_kind)); |
| - if (is_js_array) { |
| - length = AddInstruction(new(zone) HJSArrayLength(object, mapcheck, |
| - HType::Smi())); |
| + |
| + if (is_store && IsFastSmiElementsKind(elements_kind) && |
| + val->type().IsTagged() && !val->type().IsSmi()) { |
|
Jakob Kummerow
2013/03/14 15:00:20
I meant that you can remove the val->type().IsTagg
danno
2013/03/20 10:04:34
Done.
|
| + AddInstruction(new(zone) HCheckSmi(val)); |
| + } |
| + |
| + if (IsGrowStoreMode(store_mode)) { |
| + elements = BuildCheckForCapacityGrow(object, elements, elements_kind, |
| + length, key, is_js_array); |
| + checked_key = key; |
| } else { |
| - length = AddInstruction(new(zone) HFixedArrayBaseLength(elements)); |
| + checked_key = AddBoundsCheck( |
| + key, length, ALLOW_SMI_KEY, checked_index_representation); |
| + |
| + if (is_store && (fast_elements || fast_smi_only_elements)) { |
| + if (store_mode == STORE_NO_TRANSITION_HANDLE_COW) { |
| + elements = BuildCopyElementsOnWrite(object, elements, elements_kind, |
| + length); |
| + } else { |
| + HCheckMaps* check_cow_map = new(zone) HCheckMaps( |
| + elements, isolate()->factory()->fixed_array_map(), zone); |
| + check_cow_map->ClearGVNFlag(kDependsOnElementsKind); |
| + AddInstruction(check_cow_map); |
| + } |
| + } |
| } |
| - checked_key = AddBoundsCheck( |
| - key, length, ALLOW_SMI_KEY, checked_index_representation); |
| - return BuildFastElementAccess(elements, checked_key, val, mapcheck, |
| - elements_kind, is_store); |
| + return AddInstruction( |
| + BuildFastElementAccess(elements, checked_key, val, mapcheck, |
| + elements_kind, is_store, store_mode)); |
| } |
| -HValue* HGraphBuilder::BuildAllocateElements(HContext* context, |
| +HValue* HGraphBuilder::BuildAllocateElements(HValue* context, |
| ElementsKind kind, |
| - HValue* capacity) { |
| + HValue* capacity, |
| + BailoutId ast_id) { |
| Zone* zone = this->zone(); |
| int elements_size = IsFastDoubleElementsKind(kind) |
| @@ -1076,14 +1264,14 @@ HValue* HGraphBuilder::BuildAllocateElements(HContext* context, |
| Handle<Map> map = IsFastDoubleElementsKind(kind) |
| ? factory->fixed_double_array_map() |
| : factory->fixed_array_map(); |
| - BuildStoreMap(elements, map, BailoutId::StubEntry()); |
| + BuildStoreMap(elements, map, ast_id); |
| Handle<String> fixed_array_length_field_name = factory->length_field_string(); |
| HInstruction* store_length = |
| new(zone) HStoreNamedField(elements, fixed_array_length_field_name, |
| capacity, true, FixedArray::kLengthOffset); |
| AddInstruction(store_length); |
| - AddSimulate(BailoutId::StubEntry(), FIXED_SIMULATE); |
| + AddSimulate(ast_id, REMOVABLE_SIMULATE); |
| return elements; |
| } |
| @@ -1100,7 +1288,7 @@ HInstruction* HGraphBuilder::BuildStoreMap(HValue* object, |
| true, JSObject::kMapOffset); |
| store_map->SetGVNFlag(kChangesMaps); |
| AddInstruction(store_map); |
| - AddSimulate(id, FIXED_SIMULATE); |
| + AddSimulate(id, REMOVABLE_SIMULATE); |
| return store_map; |
| } |
| @@ -1115,17 +1303,132 @@ HInstruction* HGraphBuilder::BuildStoreMap(HValue* object, |
| } |
| -void HGraphBuilder::BuildCopyElements(HContext* context, |
| +HValue* HGraphBuilder::BuildNewElementsCapacity(HValue* context, |
| + HValue* old_capacity) { |
| + Zone* zone = this->zone(); |
| + HValue* half_old_capacity = |
| + AddInstruction(HShr::New(zone, context, old_capacity, |
| + graph_->GetConstant1())); |
| + half_old_capacity->ChangeRepresentation(Representation::Integer32()); |
| + half_old_capacity->ClearFlag(HValue::kCanOverflow); |
| + |
| + HValue* new_capacity = AddInstruction( |
| + HAdd::New(zone, context, half_old_capacity, old_capacity)); |
| + new_capacity->ChangeRepresentation(Representation::Integer32()); |
| + new_capacity->ClearFlag(HValue::kCanOverflow); |
| + |
| + HValue* min_growth = |
| + AddInstruction(new(zone) HConstant(16, Representation::Integer32())); |
| + |
| + new_capacity = AddInstruction( |
| + HAdd::New(zone, context, new_capacity, min_growth)); |
| + new_capacity->ChangeRepresentation(Representation::Integer32()); |
| + new_capacity->ClearFlag(HValue::kCanOverflow); |
| + |
| + return new_capacity; |
| +} |
| + |
| + |
| +void HGraphBuilder::BuildNewSpaceArrayCheck(HValue* length, ElementsKind kind) { |
| + Zone* zone = this->zone(); |
| + Heap* heap = isolate()->heap(); |
| + int element_size = IsFastDoubleElementsKind(kind) ? kDoubleSize |
| + : kPointerSize; |
| + int max_size = heap->MaxNewSpaceAllocationSize() / element_size; |
| + max_size -= JSArray::kSize / element_size; |
| + HConstant* max_size_constant = |
| + new(zone) HConstant(max_size, Representation::Integer32()); |
| + 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())); |
| +} |
| + |
| + |
| +HValue* HGraphBuilder::BuildGrowElementsCapacity(HValue* object, |
| + HValue* elements, |
| + ElementsKind kind, |
| + HValue* length, |
| + HValue* new_capacity, |
| + BailoutId ast_id) { |
| + Zone* zone = this->zone(); |
| + HValue* context = environment()->LookupContext(); |
| + |
| + BuildNewSpaceArrayCheck(new_capacity, kind); |
| + |
| + HValue* new_elements = |
| + BuildAllocateElements(context, kind, new_capacity, ast_id); |
| + |
| + BuildCopyElements(context, elements, kind, |
| + new_elements, kind, |
| + length, new_capacity, ast_id); |
| + |
| + Factory* factory = isolate()->factory(); |
| + HInstruction* elements_store = AddInstruction(new(zone) HStoreNamedField( |
| + object, |
| + factory->elements_field_string(), |
| + new_elements, true, |
| + JSArray::kElementsOffset)); |
| + elements_store->SetGVNFlag(kChangesElementsPointer); |
| + |
| + return new_elements; |
| +} |
| + |
| + |
| +void HGraphBuilder::BuildFillElementsWithHole(HValue* context, |
| + HValue* elements, |
| + ElementsKind elements_kind, |
| + HValue* from, |
| + HValue* to, |
| + BailoutId ast_id) { |
| + // Fast elements kinds need to be initialized in case statements below cause |
| + // a garbage collection. |
| + Factory* factory = isolate()->factory(); |
| + |
| + double nan_double = FixedDoubleArray::hole_nan_as_double(); |
| + Zone* zone = this->zone(); |
| + HValue* hole = IsFastSmiOrObjectElementsKind(elements_kind) |
| + ? AddInstruction(new(zone) HConstant(factory->the_hole_value(), |
| + Representation::Tagged())) |
| + : AddInstruction(new(zone) HConstant(nan_double, |
| + Representation::Double())); |
| + |
| + LoopBuilder builder(this, context, LoopBuilder::kPostIncrement, ast_id); |
| + |
| + HValue* key = builder.BeginBody(from, to, Token::LT); |
| + |
| + AddInstruction(new(zone) HStoreKeyed(elements, key, hole, elements_kind)); |
| + AddSimulate(ast_id, REMOVABLE_SIMULATE); |
| + |
| + builder.EndBody(); |
| +} |
| + |
| + |
| +void HGraphBuilder::BuildCopyElements(HValue* context, |
| HValue* from_elements, |
| ElementsKind from_elements_kind, |
| HValue* to_elements, |
| ElementsKind to_elements_kind, |
| - HValue* length) { |
| - LoopBuilder builder(this, context, LoopBuilder::kPostIncrement, |
| - BailoutId::StubEntry()); |
| + HValue* length, |
| + HValue* capacity, |
| + BailoutId ast_id) { |
| + bool pre_fill_with_holes = |
| + IsFastDoubleElementsKind(from_elements_kind) && |
| + IsFastObjectElementsKind(to_elements_kind); |
| + |
| + if (pre_fill_with_holes) { |
| + // If the copy might trigger a GC, make sure that the FixedArray is |
| + // pre-initialized with holes to make sure that it's always in a consistent |
| + // state. |
| + BuildFillElementsWithHole(context, to_elements, to_elements_kind, |
| + graph()->GetConstant0(), capacity, ast_id); |
| + } |
| - HValue* key = builder.BeginBody(graph()->GetConstant0(), |
| - length, Token::LT); |
| + LoopBuilder builder(this, context, LoopBuilder::kPostIncrement, ast_id); |
| + |
| + HValue* key = builder.BeginBody(graph()->GetConstant0(), length, Token::LT); |
| HValue* element = |
| AddInstruction(new(zone()) HLoadKeyed(from_elements, key, NULL, |
| @@ -1134,9 +1437,15 @@ void HGraphBuilder::BuildCopyElements(HContext* context, |
| AddInstruction(new(zone()) HStoreKeyed(to_elements, key, element, |
| to_elements_kind)); |
| - AddSimulate(BailoutId::StubEntry(), REMOVABLE_SIMULATE); |
| + AddSimulate(ast_id, REMOVABLE_SIMULATE); |
| builder.EndBody(); |
| + |
| + if (!pre_fill_with_holes && length != capacity) { |
| + // Fill unused capacity with the hole. |
| + BuildFillElementsWithHole(context, to_elements, to_elements_kind, |
| + key, capacity, ast_id); |
| + } |
| } |
| @@ -6781,7 +7090,8 @@ HInstruction* HOptimizedGraphBuilder::BuildMonomorphicElementAccess( |
| HValue* val, |
| HValue* dependency, |
| Handle<Map> map, |
| - bool is_store) { |
| + bool is_store, |
| + KeyedAccessStoreMode store_mode) { |
| HCheckMaps* mapcheck = new(zone()) HCheckMaps(object, map, |
| zone(), dependency); |
| AddInstruction(mapcheck); |
| @@ -6791,7 +7101,7 @@ HInstruction* HOptimizedGraphBuilder::BuildMonomorphicElementAccess( |
| return BuildUncheckedMonomorphicElementAccess( |
| object, key, val, |
| mapcheck, map->instance_type() == JS_ARRAY_TYPE, |
| - map->elements_kind(), is_store); |
| + map->elements_kind(), is_store, store_mode); |
| } |
| @@ -6846,7 +7156,7 @@ HInstruction* HOptimizedGraphBuilder::TryBuildConsolidatedElementLoad( |
| object, key, val, check_maps, |
| most_general_consolidated_map->instance_type() == JS_ARRAY_TYPE, |
| most_general_consolidated_map->elements_kind(), |
| - false); |
| + false, STANDARD_STORE); |
| return instr; |
| } |
| @@ -6859,6 +7169,7 @@ HValue* HOptimizedGraphBuilder::HandlePolymorphicElementAccess( |
| BailoutId ast_id, |
| int position, |
| bool is_store, |
| + KeyedAccessStoreMode store_mode, |
| bool* has_side_effects) { |
| *has_side_effects = false; |
| AddInstruction(new(zone()) HCheckNonSmi(object)); |
| @@ -6869,7 +7180,6 @@ HValue* HOptimizedGraphBuilder::HandlePolymorphicElementAccess( |
| HInstruction* consolidated_load = |
| TryBuildConsolidatedElementLoad(object, key, val, maps); |
| if (consolidated_load != NULL) { |
| - AddInstruction(consolidated_load); |
| *has_side_effects |= consolidated_load->HasObservableSideEffects(); |
| if (position != RelocInfo::kNoPosition) { |
| consolidated_load->set_position(position); |
| @@ -6936,8 +7246,9 @@ HValue* HOptimizedGraphBuilder::HandlePolymorphicElementAccess( |
| instr = AddInstruction(is_store ? BuildStoreKeyedGeneric(object, key, val) |
| : BuildLoadKeyedGeneric(object, key)); |
| } else { |
| - instr = AddInstruction(BuildMonomorphicElementAccess( |
| - object, key, val, transition, untransitionable_map, is_store)); |
| + instr = BuildMonomorphicElementAccess( |
| + object, key, val, transition, untransitionable_map, is_store, |
| + store_mode); |
| } |
| *has_side_effects |= instr->HasObservableSideEffects(); |
| if (position != RelocInfo::kNoPosition) instr->set_position(position); |
| @@ -7018,7 +7329,7 @@ HValue* HOptimizedGraphBuilder::HandlePolymorphicElementAccess( |
| checked_key = AddBoundsCheck(key, length, ALLOW_SMI_KEY); |
| access = AddInstruction(BuildFastElementAccess( |
| elements, checked_key, val, elements_kind_branch, |
| - elements_kind, is_store)); |
| + elements_kind, is_store, STANDARD_STORE)); |
| if (!is_store) { |
| Push(access); |
| } |
| @@ -7034,7 +7345,7 @@ HValue* HOptimizedGraphBuilder::HandlePolymorphicElementAccess( |
| checked_key = AddBoundsCheck(key, length, ALLOW_SMI_KEY); |
| access = AddInstruction(BuildFastElementAccess( |
| elements, checked_key, val, elements_kind_branch, |
| - elements_kind, is_store)); |
| + elements_kind, is_store, STANDARD_STORE)); |
| } else if (elements_kind == DICTIONARY_ELEMENTS) { |
| if (is_store) { |
| access = AddInstruction(BuildStoreKeyedGeneric(object, key, val)); |
| @@ -7080,23 +7391,26 @@ HValue* HOptimizedGraphBuilder::HandleKeyedElementAccess( |
| if (map->has_slow_elements_kind()) { |
| instr = is_store ? BuildStoreKeyedGeneric(obj, key, val) |
| : BuildLoadKeyedGeneric(obj, key); |
| + AddInstruction(instr); |
| } else { |
| AddInstruction(new(zone()) HCheckNonSmi(obj)); |
| - instr = BuildMonomorphicElementAccess(obj, key, val, NULL, map, is_store); |
| + instr = BuildMonomorphicElementAccess( |
| + obj, key, val, NULL, map, is_store, expr->GetStoreMode()); |
| } |
| } else if (expr->GetReceiverTypes() != NULL && |
| !expr->GetReceiverTypes()->is_empty()) { |
| return HandlePolymorphicElementAccess( |
| - obj, key, val, expr, ast_id, position, is_store, has_side_effects); |
| + obj, key, val, expr, ast_id, position, is_store, |
| + expr->GetStoreMode(), has_side_effects); |
| } else { |
| if (is_store) { |
| instr = BuildStoreKeyedGeneric(obj, key, val); |
| } else { |
| instr = BuildLoadKeyedGeneric(obj, key); |
| } |
| + AddInstruction(instr); |
| } |
| if (position != RelocInfo::kNoPosition) instr->set_position(position); |
| - AddInstruction(instr); |
| *has_side_effects = instr->HasObservableSideEffects(); |
| return instr; |
| } |