[turbofan] Add support for transitioning stores to double fields.

This introduces an AllocateMutableHeapNumberStub for the boxed double
field case, where we need to allocate a box in case of a transitioning
store first. We cannot use our inline allocations for this currently,
because mutable HeapNumber objects have certain alignment constraints,
and I don't want to mess up Allocate/AllocateInNewSpace eagerly.

Also refactor the PropertyAccessInfoFactory slightly to split the long
methods into simpler parts.

R=jarin@chromium.org
BUG=v8:4470
LOG=n

Review URL: https://codereview.chromium.org/1419173007

Cr-Commit-Position: refs/heads/master@{#31695}
This commit is contained in:
bmeurer 2015-10-30 09:11:54 -07:00 committed by Commit bot
parent 335a8fc332
commit 17a651917a
13 changed files with 191 additions and 61 deletions

View File

@ -273,6 +273,13 @@ Callable CodeFactory::AllocateHeapNumber(Isolate* isolate) {
}
// static
Callable CodeFactory::AllocateMutableHeapNumber(Isolate* isolate) {
AllocateMutableHeapNumberStub stub(isolate);
return Callable(stub.GetCode(), stub.GetCallInterfaceDescriptor());
}
// static
Callable CodeFactory::AllocateInNewSpace(Isolate* isolate) {
AllocateInNewSpaceStub stub(isolate);

View File

@ -94,6 +94,7 @@ class CodeFactory final {
bool has_duplicate_parameters);
static Callable AllocateHeapNumber(Isolate* isolate);
static Callable AllocateMutableHeapNumber(Isolate* isolate);
static Callable AllocateInNewSpace(Isolate* isolate);
static Callable CallFunction(Isolate* isolate, int argc,

View File

@ -1140,6 +1140,21 @@ Handle<Code> AllocateHeapNumberStub::GenerateCode() {
}
template <>
HValue* CodeStubGraphBuilder<AllocateMutableHeapNumberStub>::BuildCodeStub() {
HValue* result =
Add<HAllocate>(Add<HConstant>(HeapNumber::kSize), HType::HeapObject(),
NOT_TENURED, MUTABLE_HEAP_NUMBER_TYPE);
AddStoreMapConstant(result, isolate()->factory()->mutable_heap_number_map());
return result;
}
Handle<Code> AllocateMutableHeapNumberStub::GenerateCode() {
return DoGenerateCode(this);
}
template <>
HValue* CodeStubGraphBuilder<AllocateInNewSpaceStub>::BuildCodeStub() {
HValue* result = Add<HAllocate>(GetParameter(0), HType::Tagged(), NOT_TENURED,

View File

@ -744,6 +744,12 @@ void AllocateHeapNumberStub::InitializeDescriptor(
}
void AllocateMutableHeapNumberStub::InitializeDescriptor(
CodeStubDescriptor* descriptor) {
descriptor->Initialize();
}
void AllocateInNewSpaceStub::InitializeDescriptor(
CodeStubDescriptor* descriptor) {
descriptor->Initialize();

View File

@ -61,6 +61,7 @@ namespace internal {
V(VectorKeyedStoreIC) \
/* HydrogenCodeStubs */ \
V(AllocateHeapNumber) \
V(AllocateMutableHeapNumber) \
V(AllocateInNewSpace) \
V(ArrayNArgumentsConstructor) \
V(ArrayNoArgumentConstructor) \
@ -2658,6 +2659,17 @@ class AllocateHeapNumberStub final : public HydrogenCodeStub {
};
class AllocateMutableHeapNumberStub final : public HydrogenCodeStub {
public:
explicit AllocateMutableHeapNumberStub(Isolate* isolate)
: HydrogenCodeStub(isolate) {}
private:
DEFINE_CALL_INTERFACE_DESCRIPTOR(AllocateMutableHeapNumber);
DEFINE_HYDROGEN_CODE_STUB(AllocateMutableHeapNumber, HydrogenCodeStub);
};
class AllocateInNewSpaceStub final : public HydrogenCodeStub {
public:
explicit AllocateInNewSpaceStub(Isolate* isolate)

View File

@ -22,6 +22,15 @@ FieldAccess AccessBuilder::ForMap() {
}
// static
FieldAccess AccessBuilder::ForHeapNumberValue() {
FieldAccess access = {kTaggedBase, HeapNumber::kValueOffset,
MaybeHandle<Name>(), TypeCache().Get().kFloat64,
kMachFloat64};
return access;
}
// static
FieldAccess AccessBuilder::ForJSObjectProperties() {
FieldAccess access = {kTaggedBase, JSObject::kPropertiesOffset,

View File

@ -22,6 +22,9 @@ class AccessBuilder final : public AllStatic {
// Provides access to HeapObject::map() field.
static FieldAccess ForMap();
// Provides access to HeapNumber::value() field.
static FieldAccess ForHeapNumberValue();
// Provides access to JSObject::properties() field.
static FieldAccess ForJSObjectProperties();

View File

@ -5,10 +5,12 @@
#include "src/compiler/js-native-context-specialization.h"
#include "src/accessors.h"
#include "src/code-factory.h"
#include "src/compilation-dependencies.h"
#include "src/compiler/access-builder.h"
#include "src/compiler/js-graph.h"
#include "src/compiler/js-operator.h"
#include "src/compiler/linkage.h"
#include "src/compiler/node-matchers.h"
#include "src/contexts.h"
#include "src/field-index-inl.h"
@ -423,18 +425,18 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess(
}
FieldAccess field_access = {kTaggedBase, field_index.offset(), name,
field_type, kMachAnyTagged};
if (field_type->Is(Type::UntaggedFloat64())) {
if (!field_index.is_inobject() || field_index.is_hidden_field() ||
!FLAG_unbox_double_fields) {
this_storage = this_effect =
graph()->NewNode(simplified()->LoadField(field_access),
this_storage, this_effect, this_control);
field_access.offset = HeapNumber::kValueOffset;
field_access.name = MaybeHandle<Name>();
}
field_access.machine_type = kMachFloat64;
}
if (access_mode == PropertyAccessMode::kLoad) {
if (field_type->Is(Type::UntaggedFloat64())) {
if (!field_index.is_inobject() || field_index.is_hidden_field() ||
!FLAG_unbox_double_fields) {
this_storage = this_effect =
graph()->NewNode(simplified()->LoadField(field_access),
this_storage, this_effect, this_control);
field_access.offset = HeapNumber::kValueOffset;
field_access.name = MaybeHandle<Name>();
}
field_access.machine_type = kMachFloat64;
}
this_value = this_effect =
graph()->NewNode(simplified()->LoadField(field_access),
this_storage, this_effect, this_control);
@ -450,6 +452,39 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess(
this_control = graph()->NewNode(common()->IfTrue(), branch);
this_value = graph()->NewNode(common()->Guard(Type::Number()),
this_value, this_control);
if (!field_index.is_inobject() || field_index.is_hidden_field() ||
!FLAG_unbox_double_fields) {
if (access_info.HasTransitionMap()) {
// Allocate a MutableHeapNumber for the new property.
Callable callable =
CodeFactory::AllocateMutableHeapNumber(isolate());
CallDescriptor* desc = Linkage::GetStubCallDescriptor(
isolate(), jsgraph()->zone(), callable.descriptor(), 0,
CallDescriptor::kNoFlags, Operator::kNoThrow);
Node* this_box = this_effect = graph()->NewNode(
common()->Call(desc),
jsgraph()->HeapConstant(callable.code()),
jsgraph()->NoContextConstant(), this_effect, this_control);
this_effect = graph()->NewNode(
simplified()->StoreField(AccessBuilder::ForHeapNumberValue()),
this_box, this_value, this_effect, this_control);
this_value = this_box;
field_access.type = Type::TaggedPointer();
} else {
// We just store directly to the MutableHeapNumber.
this_storage = this_effect =
graph()->NewNode(simplified()->LoadField(field_access),
this_storage, this_effect, this_control);
field_access.offset = HeapNumber::kValueOffset;
field_access.name = MaybeHandle<Name>();
field_access.machine_type = kMachFloat64;
}
} else {
// Unboxed double field, we store directly to the field.
field_access.machine_type = kMachFloat64;
}
} else if (field_type->Is(Type::TaggedSigned())) {
Node* check =
graph()->NewNode(simplified()->ObjectIsSmi(), this_value);
@ -505,7 +540,7 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess(
this_effect = graph()->NewNode(simplified()->StoreField(field_access),
this_storage, this_value, this_effect,
this_control);
if (!access_info.transition_map().is_null()) {
if (access_info.HasTransitionMap()) {
this_effect =
graph()->NewNode(common()->FinishRegion(),
jsgraph()->UndefinedConstant(), this_effect);

View File

@ -87,39 +87,11 @@ bool PropertyAccessInfoFactory::ComputePropertyAccessInfo(
// Compute the receiver type.
Handle<Map> receiver_map = map;
Type* receiver_type = Type::Class(receiver_map, zone());
// We support fast inline cases for certain JSObject getters.
if (access_mode == PropertyAccessMode::kLoad) {
// Check for special JSObject field accessors.
int offset;
if (Accessors::IsJSObjectFieldAccessor(map, name, &offset)) {
FieldIndex field_index = FieldIndex::ForInObjectOffset(offset);
Type* field_type = Type::Tagged();
if (map->IsStringMap()) {
DCHECK(Name::Equals(factory()->length_string(), name));
// The String::length property is always a smi in the range
// [0, String::kMaxLength].
field_type = type_cache_.kStringLengthType;
} else if (map->IsJSArrayMap()) {
DCHECK(Name::Equals(factory()->length_string(), name));
// The JSArray::length property is a smi in the range
// [0, FixedDoubleArray::kMaxLength] in case of fast double
// elements, a smi in the range [0, FixedArray::kMaxLength]
// in case of other fast elements, and [0, kMaxUInt32] in
// case of other arrays.
if (IsFastDoubleElementsKind(map->elements_kind())) {
field_type = type_cache_.kFixedDoubleArrayLengthType;
} else if (IsFastElementsKind(map->elements_kind())) {
field_type = type_cache_.kFixedArrayLengthType;
} else {
field_type = type_cache_.kJSArrayLengthType;
}
}
*access_info =
PropertyAccessInfo::DataField(receiver_type, field_index, field_type);
return true;
}
if (access_mode == PropertyAccessMode::kLoad &&
LookupSpecialFieldAccessor(map, name, access_info)) {
return true;
}
MaybeHandle<JSObject> holder;
@ -136,16 +108,16 @@ bool PropertyAccessInfoFactory::ComputePropertyAccessInfo(
}
// Check for store to data property on a prototype.
if (details.kind() == kData && !holder.is_null()) {
// We need to add the data field to the receiver. Leave the loop
// and check whether we already have a transition for this field.
// Store to property not found on the receiver but on a prototype, we
// need to transition to a new data property.
// Implemented according to ES6 section 9.1.9 [[Set]] (P, V, Receiver)
break;
return LookupTransition(receiver_map, name, holder, access_info);
}
}
if (details.type() == DATA_CONSTANT) {
*access_info = PropertyAccessInfo::DataConstant(
receiver_type, handle(descriptors->GetValue(number), isolate()),
holder);
Type::Class(receiver_map, zone()),
handle(descriptors->GetValue(number), isolate()), holder);
return true;
} else if (details.type() == DATA) {
int index = descriptors->GetFieldIndex(number);
@ -180,8 +152,8 @@ bool PropertyAccessInfoFactory::ComputePropertyAccessInfo(
}
DCHECK(field_type->Is(Type::TaggedPointer()));
}
*access_info = PropertyAccessInfo::DataField(receiver_type, field_index,
field_type, holder);
*access_info = PropertyAccessInfo::DataField(
Type::Class(receiver_map, zone()), field_index, field_type, holder);
return true;
} else {
// TODO(bmeurer): Add support for accessors.
@ -213,7 +185,7 @@ bool PropertyAccessInfoFactory::ComputePropertyAccessInfo(
// to transition to a new data property.
// Implemented according to ES6 section 9.1.9 [[Set]] (P, V, Receiver)
if (access_mode == PropertyAccessMode::kStore) {
break;
return LookupTransition(receiver_map, name, holder, access_info);
}
// TODO(bmeurer): Handle the not found case if the prototype is null.
return false;
@ -233,13 +205,53 @@ bool PropertyAccessInfoFactory::ComputePropertyAccessInfo(
// Check if it is safe to inline property access for the {map}.
if (!CanInlinePropertyAccess(map)) return false;
}
DCHECK_EQ(PropertyAccessMode::kStore, access_mode);
return false;
}
// Check if the {receiver_map} has a data transition with the given {name}.
if (receiver_map->unused_property_fields() == 0) return false;
if (Map* transition = TransitionArray::SearchTransition(*receiver_map, kData,
*name, NONE)) {
Handle<Map> transition_map(transition, isolate());
bool PropertyAccessInfoFactory::LookupSpecialFieldAccessor(
Handle<Map> map, Handle<Name> name, PropertyAccessInfo* access_info) {
// Check for special JSObject field accessors.
int offset;
if (Accessors::IsJSObjectFieldAccessor(map, name, &offset)) {
FieldIndex field_index = FieldIndex::ForInObjectOffset(offset);
Type* field_type = Type::Tagged();
if (map->IsStringMap()) {
DCHECK(Name::Equals(factory()->length_string(), name));
// The String::length property is always a smi in the range
// [0, String::kMaxLength].
field_type = type_cache_.kStringLengthType;
} else if (map->IsJSArrayMap()) {
DCHECK(Name::Equals(factory()->length_string(), name));
// The JSArray::length property is a smi in the range
// [0, FixedDoubleArray::kMaxLength] in case of fast double
// elements, a smi in the range [0, FixedArray::kMaxLength]
// in case of other fast elements, and [0, kMaxUInt32] in
// case of other arrays.
if (IsFastDoubleElementsKind(map->elements_kind())) {
field_type = type_cache_.kFixedDoubleArrayLengthType;
} else if (IsFastElementsKind(map->elements_kind())) {
field_type = type_cache_.kFixedArrayLengthType;
} else {
field_type = type_cache_.kJSArrayLengthType;
}
}
*access_info = PropertyAccessInfo::DataField(Type::Class(map, zone()),
field_index, field_type);
return true;
}
return false;
}
bool PropertyAccessInfoFactory::LookupTransition(
Handle<Map> map, Handle<Name> name, MaybeHandle<JSObject> holder,
PropertyAccessInfo* access_info) {
// Check if the {map} has a data transition with the given {name}.
if (map->unused_property_fields() == 0) return false;
Handle<Map> transition_map;
if (TransitionArray::SearchTransition(map, kData, name, NONE)
.ToHandle(&transition_map)) {
int const number = transition_map->LastAdded();
PropertyDetails const details =
transition_map->instance_descriptors()->GetDetails(number);
@ -255,8 +267,7 @@ bool PropertyAccessInfoFactory::ComputePropertyAccessInfo(
if (field_representation.IsSmi()) {
field_type = type_cache_.kSmi;
} else if (field_representation.IsDouble()) {
// TODO(bmeurer): Add support for storing to double fields.
return false;
field_type = type_cache_.kFloat64;
} else if (field_representation.IsHeapObject()) {
// Extract the field type from the property details (make sure its
// representation is TaggedPointer to reflect the heap object case).
@ -279,8 +290,9 @@ bool PropertyAccessInfoFactory::ComputePropertyAccessInfo(
DCHECK(field_type->Is(Type::TaggedPointer()));
}
dependencies()->AssumeMapNotDeprecated(transition_map);
*access_info = PropertyAccessInfo::DataField(
receiver_type, field_index, field_type, holder, transition_map);
*access_info =
PropertyAccessInfo::DataField(Type::Class(map, zone()), field_index,
field_type, holder, transition_map);
return true;
}
return false;

View File

@ -60,6 +60,8 @@ class PropertyAccessInfo final {
Type* field_type() const { return field_type_; }
Type* receiver_type() const { return receiver_type_; }
bool HasTransitionMap() const { return !transition_map().is_null(); }
private:
PropertyAccessInfo(MaybeHandle<JSObject> holder, Handle<Object> constant,
Type* receiver_type);
@ -91,6 +93,12 @@ class PropertyAccessInfoFactory final {
ZoneVector<PropertyAccessInfo>* access_infos);
private:
bool LookupSpecialFieldAccessor(Handle<Map> map, Handle<Name> name,
PropertyAccessInfo* access_info);
bool LookupTransition(Handle<Map> map, Handle<Name> name,
MaybeHandle<JSObject> holder,
PropertyAccessInfo* access_info);
CompilationDependencies* dependencies() const { return dependencies_; }
Factory* factory() const;
Isolate* isolate() const { return isolate_; }

View File

@ -75,6 +75,12 @@ const char* CallInterfaceDescriptor::DebugName(Isolate* isolate) const {
}
void AllocateMutableHeapNumberDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
data->InitializePlatformSpecific(0, nullptr, nullptr);
}
Type::FunctionType* LoadDescriptor::BuildCallInterfaceDescriptorFunctionType(
Isolate* isolate, int paramater_count) {
Zone* zone = isolate->interface_descriptor_zone();

View File

@ -42,6 +42,7 @@ class PlatformInterfaceDescriptor;
V(RegExpConstructResult) \
V(TransitionElementsKind) \
V(AllocateHeapNumber) \
V(AllocateMutableHeapNumber) \
V(AllocateInNewSpace) \
V(ArrayConstructorConstantArgCount) \
V(ArrayConstructor) \
@ -521,6 +522,13 @@ class AllocateHeapNumberDescriptor : public CallInterfaceDescriptor {
};
class AllocateMutableHeapNumberDescriptor : public CallInterfaceDescriptor {
public:
DECLARE_DESCRIPTOR(AllocateMutableHeapNumberDescriptor,
CallInterfaceDescriptor)
};
class AllocateInNewSpaceDescriptor : public CallInterfaceDescriptor {
public:
DECLARE_DESCRIPTOR(AllocateInNewSpaceDescriptor, CallInterfaceDescriptor)

View File

@ -41,6 +41,14 @@ class TransitionArray: public FixedArray {
static Map* SearchTransition(Map* map, PropertyKind kind, Name* name,
PropertyAttributes attributes);
static MaybeHandle<Map> SearchTransition(Handle<Map> map, PropertyKind kind,
Handle<Name> name,
PropertyAttributes attributes) {
if (Map* transition = SearchTransition(*map, kind, *name, attributes)) {
return handle(transition);
}
return MaybeHandle<Map>();
}
static Map* SearchSpecial(Map* map, Symbol* name);