Index: src/hydrogen.cc |
diff --git a/src/hydrogen.cc b/src/hydrogen.cc |
index 20e1d0d3412d08f60c77fde1d03657f766b408db..40320b7d6bbd686693d47e38befccbf7ab0daa5c 100644 |
--- a/src/hydrogen.cc |
+++ b/src/hydrogen.cc |
@@ -1341,8 +1341,10 @@ HValue* HGraphBuilder::BuildAllocateElements(HValue* context, |
total_size->ChangeRepresentation(Representation::Integer32()); |
total_size->ClearFlag(HValue::kCanOverflow); |
- HAllocate::Flags flags = HAllocate::CAN_ALLOCATE_IN_NEW_SPACE; |
+ HAllocate::Flags flags = HAllocate::DefaultFlags(kind); |
if (FLAG_pretenure_literals) { |
+ // TODO(hpayer): When pretenuring can be internalized, flags can become |
+ // private to HAllocate. |
if (IsFastDoubleElementsKind(kind)) { |
flags = static_cast<HAllocate::Flags>( |
flags | HAllocate::CAN_ALLOCATE_IN_OLD_DATA_SPACE); |
@@ -1351,10 +1353,6 @@ HValue* HGraphBuilder::BuildAllocateElements(HValue* context, |
flags | HAllocate::CAN_ALLOCATE_IN_OLD_POINTER_SPACE); |
} |
} |
- if (IsFastDoubleElementsKind(kind)) { |
- flags = static_cast<HAllocate::Flags>( |
- flags | HAllocate::ALLOCATE_DOUBLE_ALIGNED); |
- } |
HValue* elements = |
AddInstruction(new(zone) HAllocate(context, total_size, |
@@ -1390,6 +1388,63 @@ HValue* HGraphBuilder::BuildAllocateAndInitializeElements(HValue* context, |
} |
+HInnerAllocatedObject* HGraphBuilder::BuildJSArrayHeader(HValue* array, |
+ HValue* array_map, |
+ AllocationSiteMode mode, |
+ HValue* allocation_site_payload, |
+ HValue* length_field) { |
+ |
+ BuildStoreMap(array, array_map); |
+ |
+ HConstant* empty_fixed_array = |
+ new(zone()) HConstant( |
+ Handle<FixedArray>(isolate()->heap()->empty_fixed_array()), |
+ Representation::Tagged()); |
+ AddInstruction(empty_fixed_array); |
+ |
+ AddInstruction(new(zone()) HStoreNamedField(array, |
+ isolate()->factory()->properties_field_symbol(), |
+ empty_fixed_array, |
+ true, |
+ JSArray::kPropertiesOffset)); |
+ |
+ HInstruction* length_store = AddInstruction( |
+ new(zone()) HStoreNamedField(array, |
+ isolate()->factory()->length_field_string(), |
+ length_field, |
+ true, |
+ JSArray::kLengthOffset)); |
+ length_store->SetGVNFlag(kChangesArrayLengths); |
+ |
+ if (mode == TRACK_ALLOCATION_SITE) { |
+ BuildCreateAllocationSiteInfo(array, |
+ JSArray::kSize, |
+ allocation_site_payload); |
+ } |
+ |
+ int elements_location = JSArray::kSize; |
+ if (mode == TRACK_ALLOCATION_SITE) { |
+ elements_location += AllocationSiteInfo::kSize; |
+ } |
+ |
+ HInnerAllocatedObject* elements = new(zone()) HInnerAllocatedObject( |
+ array, |
+ elements_location); |
+ AddInstruction(elements); |
+ |
+ HInstruction* elements_store = AddInstruction( |
+ new(zone()) HStoreNamedField( |
+ array, |
+ isolate()->factory()->elements_field_string(), |
+ elements, |
+ true, |
+ JSArray::kElementsOffset)); |
+ elements_store->SetGVNFlag(kChangesElementsPointer); |
+ |
+ return elements; |
+} |
+ |
+ |
HInstruction* HGraphBuilder::BuildStoreMap(HValue* object, |
HValue* map) { |
Zone* zone = this->zone(); |
@@ -1503,13 +1558,38 @@ void HGraphBuilder::BuildFillElementsWithHole(HValue* context, |
: AddInstruction(new(zone) HConstant(nan_double, |
Representation::Double())); |
- LoopBuilder builder(this, context, LoopBuilder::kPostIncrement); |
+ // Special loop unfolding case |
+ static const int kLoopUnfoldLimit = 4; |
+ bool unfold_loop = false; |
+ int initial_capacity = JSArray::kPreallocatedArrayElements; |
+ if (from->IsConstant() && to->IsConstant() && |
+ initial_capacity <= kLoopUnfoldLimit) { |
+ HConstant* constant_from = HConstant::cast(from); |
+ HConstant* constant_to = HConstant::cast(to); |
+ |
+ if (constant_from->HasInteger32Value() && |
+ constant_from->Integer32Value() == 0 && |
+ constant_to->HasInteger32Value() && |
+ constant_to->Integer32Value() == initial_capacity) { |
+ unfold_loop = true; |
+ } |
+ } |
+ |
+ if (unfold_loop) { |
+ for (int i = 0; i < initial_capacity; i++) { |
+ HInstruction* key = AddInstruction(new(zone) |
+ HConstant(i, Representation::Integer32())); |
+ AddInstruction(new(zone) HStoreKeyed(elements, key, hole, elements_kind)); |
+ } |
+ } else { |
+ LoopBuilder builder(this, context, LoopBuilder::kPostIncrement); |
- HValue* key = builder.BeginBody(from, to, Token::LT); |
+ HValue* key = builder.BeginBody(from, to, Token::LT); |
- AddInstruction(new(zone) HStoreKeyed(elements, key, hole, elements_kind)); |
+ AddInstruction(new(zone) HStoreKeyed(elements, key, hole, elements_kind)); |
- builder.EndBody(); |
+ builder.EndBody(); |
+ } |
} |
@@ -1576,12 +1656,7 @@ HValue* HGraphBuilder::BuildCloneShallowArray(HContext* context, |
: FixedArray::SizeFor(length); |
} |
- HAllocate::Flags allocate_flags = HAllocate::CAN_ALLOCATE_IN_NEW_SPACE; |
- if (IsFastDoubleElementsKind(kind)) { |
- allocate_flags = static_cast<HAllocate::Flags>( |
- allocate_flags | HAllocate::ALLOCATE_DOUBLE_ALIGNED); |
- } |
- |
+ HAllocate::Flags allocate_flags = HAllocate::DefaultFlags(kind); |
// Allocate both the JS array and the elements array in one big |
// allocation. This avoids multiple limit checks. |
HValue* size_in_bytes = |
@@ -1610,15 +1685,7 @@ HValue* HGraphBuilder::BuildCloneShallowArray(HContext* context, |
// Create an allocation site info if requested. |
if (mode == TRACK_ALLOCATION_SITE) { |
- HValue* alloc_site = |
- AddInstruction(new(zone) HInnerAllocatedObject(object, JSArray::kSize)); |
- Handle<Map> alloc_site_map(isolate()->heap()->allocation_site_info_map()); |
- BuildStoreMap(alloc_site, alloc_site_map); |
- int alloc_payload_offset = AllocationSiteInfo::kPayloadOffset; |
- AddInstruction(new(zone) HStoreNamedField(alloc_site, |
- factory->empty_string(), |
- boilerplate, |
- true, alloc_payload_offset)); |
+ BuildCreateAllocationSiteInfo(object, JSArray::kSize, boilerplate); |
} |
if (length > 0) { |
@@ -1667,6 +1734,158 @@ HValue* HGraphBuilder::BuildCloneShallowArray(HContext* context, |
} |
+HValue* HGraphBuilder::BuildCreateAllocationSiteInfo(HValue* previous_object, |
+ int previous_object_size, |
+ HValue* payload) { |
+ HInnerAllocatedObject* alloc_site = new(zone()) |
+ 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, |
+ AllocationSiteInfo::kPayloadOffset)); |
+ return alloc_site; |
+} |
+ |
+ |
+HGraphBuilder::JSArrayBuilder::JSArrayBuilder(HGraphBuilder* builder, |
+ ElementsKind kind, |
+ HValue* allocation_site_payload, |
+ AllocationSiteMode mode) : |
+ builder_(builder), |
+ kind_(kind), |
+ allocation_site_payload_(allocation_site_payload) { |
+ if (mode == DONT_TRACK_ALLOCATION_SITE) { |
+ mode_ = mode; |
+ } else { |
+ mode_ = AllocationSiteInfo::GetMode(kind); |
+ } |
+} |
+ |
+ |
+HValue* HGraphBuilder::JSArrayBuilder::EmitMapCode(HValue* context) { |
+ // Get the global context, the native context, the map array |
+ HInstruction* global_object = AddInstruction(new(zone()) |
+ HGlobalObject(context)); |
+ HInstruction* native_context = AddInstruction(new(zone()) |
+ HLoadNamedField(global_object, true, GlobalObject::kNativeContextOffset)); |
+ size_t offset = Context::kHeaderSize + |
+ kPointerSize * Context::JS_ARRAY_MAPS_INDEX; |
+ HInstruction* map_array = AddInstruction(new(zone()) |
+ HLoadNamedField(native_context, true, offset)); |
+ offset = kind_ * kPointerSize + FixedArrayBase::kHeaderSize; |
+ return AddInstruction(new(zone()) HLoadNamedField(map_array, true, offset)); |
+} |
+ |
+ |
+HValue* HGraphBuilder::JSArrayBuilder::EstablishAllocationSize( |
+ HValue* length_node) { |
+ HValue* context = builder()->environment()->LookupContext(); |
+ ASSERT(length_node != NULL); |
+ |
+ int base_size = JSArray::kSize; |
+ if (mode_ == TRACK_ALLOCATION_SITE) { |
+ base_size += AllocationSiteInfo::kSize; |
+ } |
+ |
+ if (IsFastDoubleElementsKind(kind_)) { |
+ base_size += FixedDoubleArray::kHeaderSize; |
+ } else { |
+ base_size += FixedArray::kHeaderSize; |
+ } |
+ |
+ HInstruction* elements_size_value = new(zone()) |
+ HConstant(elements_size(), Representation::Integer32()); |
+ AddInstruction(elements_size_value); |
+ HInstruction* mul = HMul::New(zone(), context, length_node, |
+ elements_size_value); |
+ mul->ChangeRepresentation(Representation::Integer32()); |
+ mul->ClearFlag(HValue::kCanOverflow); |
+ AddInstruction(mul); |
+ |
+ HInstruction* base = new(zone()) HConstant(base_size, |
+ Representation::Integer32()); |
+ AddInstruction(base); |
+ HInstruction* total_size = HAdd::New(zone(), context, base, mul); |
+ total_size->ChangeRepresentation(Representation::Integer32()); |
+ total_size->ClearFlag(HValue::kCanOverflow); |
+ AddInstruction(total_size); |
+ return total_size; |
+} |
+ |
+ |
+HValue* HGraphBuilder::JSArrayBuilder::EstablishEmptyArrayAllocationSize() { |
+ int base_size = JSArray::kSize; |
+ if (mode_ == TRACK_ALLOCATION_SITE) { |
+ base_size += AllocationSiteInfo::kSize; |
+ } |
+ |
+ base_size += IsFastDoubleElementsKind(kind_) |
+ ? FixedDoubleArray::SizeFor(initial_capacity()) |
+ : FixedArray::SizeFor(initial_capacity()); |
+ |
+ HConstant* array_size = |
+ new(zone()) HConstant(base_size, Representation::Integer32()); |
+ AddInstruction(array_size); |
+ return array_size; |
+} |
+ |
+ |
+HValue* HGraphBuilder::JSArrayBuilder::AllocateEmptyArray() { |
+ HValue* size_in_bytes = EstablishEmptyArrayAllocationSize(); |
+ HConstant* capacity = |
+ new(zone()) HConstant(initial_capacity(), Representation::Integer32()); |
+ AddInstruction(capacity); |
+ return AllocateArray(size_in_bytes, |
+ capacity, |
+ builder()->graph()->GetConstant0(), |
+ true); |
+} |
+ |
+ |
+HValue* HGraphBuilder::JSArrayBuilder::AllocateArray(HValue* capacity, |
+ HValue* length_field, |
+ bool fill_with_hole) { |
+ HValue* size_in_bytes = EstablishAllocationSize(capacity); |
+ return AllocateArray(size_in_bytes, capacity, length_field, fill_with_hole); |
+} |
+ |
+ |
+HValue* HGraphBuilder::JSArrayBuilder::AllocateArray(HValue* size_in_bytes, |
+ HValue* capacity, |
+ HValue* length_field, |
+ bool fill_with_hole) { |
+ HValue* context = builder()->environment()->LookupContext(); |
+ |
+ // Allocate (dealing with failure appropriately) |
+ HAllocate::Flags flags = HAllocate::DefaultFlags(kind_); |
+ HAllocate* new_object = new(zone()) HAllocate(context, size_in_bytes, |
+ HType::JSArray(), flags); |
+ AddInstruction(new_object); |
+ |
+ // Fill in the fields: map, properties, length |
+ HValue* map = EmitMapCode(context); |
+ elements_location_ = builder()->BuildJSArrayHeader(new_object, |
+ map, |
+ mode_, |
+ allocation_site_payload_, |
+ length_field); |
+ |
+ // Initialize the elements |
+ builder()->BuildInitializeElements(elements_location_, kind_, capacity); |
+ |
+ if (fill_with_hole) { |
+ builder()->BuildFillElementsWithHole(context, elements_location_, kind_, |
+ graph()->GetConstant0(), capacity); |
+ } |
+ |
+ return new_object; |
+} |
+ |
+ |
HOptimizedGraphBuilder::HOptimizedGraphBuilder(CompilationInfo* info, |
TypeFeedbackOracle* oracle) |
: HGraphBuilder(info), |
@@ -9215,19 +9434,31 @@ void HOptimizedGraphBuilder::VisitCallNew(CallNew* expr) { |
} else { |
// The constructor function is both an operand to the instruction and an |
// argument to the construct call. |
+ bool use_call_new_array = FLAG_optimize_constructed_arrays && |
+ !(expr->target().is_null()) && |
+ *(expr->target()) == isolate()->global_context()->array_function(); |
+ |
CHECK_ALIVE(VisitArgument(expr->expression())); |
HValue* constructor = HPushArgument::cast(Top())->argument(); |
CHECK_ALIVE(VisitArgumentList(expr->arguments())); |
HCallNew* call; |
- if (FLAG_optimize_constructed_arrays && |
- !(expr->target().is_null()) && |
- *(expr->target()) == isolate()->global_context()->array_function()) { |
+ if (use_call_new_array) { |
+ AddInstruction(new(zone()) HCheckFunction(constructor, |
+ Handle<JSFunction>(isolate()->global_context()->array_function()))); |
Handle<Object> feedback = oracle()->GetInfo(expr->CallNewFeedbackId()); |
ASSERT(feedback->IsSmi()); |
+ |
+ // 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<JSGlobalPropertyCell> cell = |
isolate()->factory()->NewJSGlobalPropertyCell(feedback); |
- AddInstruction(new(zone()) HCheckFunction(constructor, |
- Handle<JSFunction>(isolate()->global_context()->array_function()))); |
+ |
+ // 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. |
call = new(zone()) HCallNewArray(context, constructor, argument_count, |
cell); |
} else { |
@@ -10340,15 +10571,7 @@ void HOptimizedGraphBuilder::BuildEmitDeepCopy( |
// Build Allocation Site Info if desired |
if (create_allocation_site_info) { |
- HValue* alloc_site = |
- AddInstruction(new(zone) HInnerAllocatedObject(target, JSArray::kSize)); |
- Handle<Map> alloc_site_map(isolate()->heap()->allocation_site_info_map()); |
- BuildStoreMap(alloc_site, alloc_site_map); |
- int alloc_payload_offset = AllocationSiteInfo::kPayloadOffset; |
- AddInstruction(new(zone) HStoreNamedField(alloc_site, |
- factory->payload_string(), |
- original_boilerplate, |
- true, alloc_payload_offset)); |
+ BuildCreateAllocationSiteInfo(target, JSArray::kSize, original_boilerplate); |
} |
if (object_elements != NULL) { |