[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:
parent
335a8fc332
commit
17a651917a
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -744,6 +744,12 @@ void AllocateHeapNumberStub::InitializeDescriptor(
|
||||
}
|
||||
|
||||
|
||||
void AllocateMutableHeapNumberStub::InitializeDescriptor(
|
||||
CodeStubDescriptor* descriptor) {
|
||||
descriptor->Initialize();
|
||||
}
|
||||
|
||||
|
||||
void AllocateInNewSpaceStub::InitializeDescriptor(
|
||||
CodeStubDescriptor* descriptor) {
|
||||
descriptor->Initialize();
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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_; }
|
||||
|
@ -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();
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user