[crankshaft][turbofan] Compilers' part of constant field tracking.
The constant field tracking is still disabled. BUG=v8:5495 Change-Id: I543fe50b82e2255bbf200ea785ec53e3623e30cb Reviewed-on: https://chromium-review.googlesource.com/440924 Commit-Queue: Igor Sheludko <ishell@chromium.org> Reviewed-by: Benedikt Meurer <bmeurer@chromium.org> Cr-Commit-Position: refs/heads/master@{#43304}
This commit is contained in:
parent
1bbbfb42d5
commit
c6b57edc04
@ -80,11 +80,12 @@ PropertyAccessInfo PropertyAccessInfo::DataConstant(
|
||||
|
||||
// static
|
||||
PropertyAccessInfo PropertyAccessInfo::DataField(
|
||||
MapList const& receiver_maps, FieldIndex field_index,
|
||||
MachineRepresentation field_representation, Type* field_type,
|
||||
MaybeHandle<Map> field_map, MaybeHandle<JSObject> holder,
|
||||
PropertyConstness constness, MapList const& receiver_maps,
|
||||
FieldIndex field_index, MachineRepresentation field_representation,
|
||||
Type* field_type, MaybeHandle<Map> field_map, MaybeHandle<JSObject> holder,
|
||||
MaybeHandle<Map> transition_map) {
|
||||
return PropertyAccessInfo(holder, transition_map, field_index,
|
||||
Kind kind = constness == kConst ? kDataConstantField : kDataField;
|
||||
return PropertyAccessInfo(kind, holder, transition_map, field_index,
|
||||
field_representation, field_type, field_map,
|
||||
receiver_maps);
|
||||
}
|
||||
@ -126,10 +127,10 @@ PropertyAccessInfo::PropertyAccessInfo(Kind kind, MaybeHandle<JSObject> holder,
|
||||
field_type_(Type::Any()) {}
|
||||
|
||||
PropertyAccessInfo::PropertyAccessInfo(
|
||||
MaybeHandle<JSObject> holder, MaybeHandle<Map> transition_map,
|
||||
Kind kind, MaybeHandle<JSObject> holder, MaybeHandle<Map> transition_map,
|
||||
FieldIndex field_index, MachineRepresentation field_representation,
|
||||
Type* field_type, MaybeHandle<Map> field_map, MapList const& receiver_maps)
|
||||
: kind_(kDataField),
|
||||
: kind_(kind),
|
||||
receiver_maps_(receiver_maps),
|
||||
transition_map_(transition_map),
|
||||
holder_(holder),
|
||||
@ -146,9 +147,11 @@ bool PropertyAccessInfo::Merge(PropertyAccessInfo const* that) {
|
||||
case kInvalid:
|
||||
break;
|
||||
|
||||
case kDataField: {
|
||||
case kDataField:
|
||||
case kDataConstantField: {
|
||||
// Check if we actually access the same field.
|
||||
if (this->transition_map_.address() == that->transition_map_.address() &&
|
||||
if (this->kind_ == that->kind_ &&
|
||||
this->transition_map_.address() == that->transition_map_.address() &&
|
||||
this->field_index_ == that->field_index_ &&
|
||||
this->field_map_.address() == that->field_map_.address() &&
|
||||
this->field_type_->Is(that->field_type_) &&
|
||||
@ -338,8 +341,8 @@ bool AccessInfoFactory::ComputePropertyAccessInfo(
|
||||
}
|
||||
}
|
||||
*access_info = PropertyAccessInfo::DataField(
|
||||
MapList{receiver_map}, field_index, field_representation,
|
||||
field_type, field_map, holder);
|
||||
details.constness(), MapList{receiver_map}, field_index,
|
||||
field_representation, field_type, field_map, holder);
|
||||
return true;
|
||||
} else {
|
||||
DCHECK_EQ(kAccessor, details.kind());
|
||||
@ -502,8 +505,9 @@ bool AccessInfoFactory::LookupSpecialFieldAccessor(
|
||||
field_type = type_cache_.kJSArrayLengthType;
|
||||
}
|
||||
}
|
||||
// Special fields are always mutable.
|
||||
*access_info = PropertyAccessInfo::DataField(
|
||||
MapList{map}, field_index, field_representation, field_type);
|
||||
kMutable, MapList{map}, field_index, field_representation, field_type);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -563,9 +567,10 @@ bool AccessInfoFactory::LookupTransition(Handle<Map> map, Handle<Name> name,
|
||||
}
|
||||
}
|
||||
dependencies()->AssumeMapNotDeprecated(transition_map);
|
||||
// Transitioning stores are never stores to constant fields.
|
||||
*access_info = PropertyAccessInfo::DataField(
|
||||
MapList{map}, field_index, field_representation, field_type, field_map,
|
||||
holder, transition_map);
|
||||
kMutable, MapList{map}, field_index, field_representation, field_type,
|
||||
field_map, holder, transition_map);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -62,6 +62,7 @@ class PropertyAccessInfo final {
|
||||
kNotFound,
|
||||
kDataConstant,
|
||||
kDataField,
|
||||
kDataConstantField,
|
||||
kAccessorConstant,
|
||||
kGeneric
|
||||
};
|
||||
@ -72,9 +73,9 @@ class PropertyAccessInfo final {
|
||||
Handle<Object> constant,
|
||||
MaybeHandle<JSObject> holder);
|
||||
static PropertyAccessInfo DataField(
|
||||
MapList const& receiver_maps, FieldIndex field_index,
|
||||
MachineRepresentation field_representation, Type* field_type,
|
||||
MaybeHandle<Map> field_map = MaybeHandle<Map>(),
|
||||
PropertyConstness constness, MapList const& receiver_maps,
|
||||
FieldIndex field_index, MachineRepresentation field_representation,
|
||||
Type* field_type, MaybeHandle<Map> field_map = MaybeHandle<Map>(),
|
||||
MaybeHandle<JSObject> holder = MaybeHandle<JSObject>(),
|
||||
MaybeHandle<Map> transition_map = MaybeHandle<Map>());
|
||||
static PropertyAccessInfo AccessorConstant(MapList const& receiver_maps,
|
||||
@ -89,6 +90,9 @@ class PropertyAccessInfo final {
|
||||
bool IsNotFound() const { return kind() == kNotFound; }
|
||||
bool IsDataConstant() const { return kind() == kDataConstant; }
|
||||
bool IsDataField() const { return kind() == kDataField; }
|
||||
// TODO(ishell): rename to IsDataConstant() once constant field tracking
|
||||
// is done.
|
||||
bool IsDataConstantField() const { return kind() == kDataConstantField; }
|
||||
bool IsAccessorConstant() const { return kind() == kAccessorConstant; }
|
||||
bool IsGeneric() const { return kind() == kGeneric; }
|
||||
|
||||
@ -111,7 +115,7 @@ class PropertyAccessInfo final {
|
||||
MapList const& receiver_maps);
|
||||
PropertyAccessInfo(Kind kind, MaybeHandle<JSObject> holder,
|
||||
Handle<Object> constant, MapList const& receiver_maps);
|
||||
PropertyAccessInfo(MaybeHandle<JSObject> holder,
|
||||
PropertyAccessInfo(Kind kind, MaybeHandle<JSObject> holder,
|
||||
MaybeHandle<Map> transition_map, FieldIndex field_index,
|
||||
MachineRepresentation field_representation,
|
||||
Type* field_type, MaybeHandle<Map> field_map,
|
||||
|
@ -1386,7 +1386,7 @@ JSNativeContextSpecialization::BuildPropertyAccess(
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (access_info.IsDataField()) {
|
||||
} else if (access_info.IsDataField() || access_info.IsDataConstantField()) {
|
||||
FieldIndex const field_index = access_info.field_index();
|
||||
Type* const field_type = access_info.field_type();
|
||||
MachineRepresentation const field_representation =
|
||||
@ -1411,10 +1411,23 @@ JSNativeContextSpecialization::BuildPropertyAccess(
|
||||
// but for now let's just do what Crankshaft does.
|
||||
LookupIterator it(m.Value(), name,
|
||||
LookupIterator::OWN_SKIP_INTERCEPTOR);
|
||||
if (it.state() == LookupIterator::DATA && it.IsReadOnly() &&
|
||||
!it.IsConfigurable()) {
|
||||
Node* value = jsgraph()->Constant(JSReceiver::GetDataProperty(&it));
|
||||
return ValueEffectControl(value, effect, control);
|
||||
if (it.state() == LookupIterator::DATA) {
|
||||
bool is_reaonly_non_configurable =
|
||||
it.IsReadOnly() && !it.IsConfigurable();
|
||||
if (is_reaonly_non_configurable ||
|
||||
(FLAG_track_constant_fields &&
|
||||
access_info.IsDataConstantField())) {
|
||||
Node* value = jsgraph()->Constant(JSReceiver::GetDataProperty(&it));
|
||||
if (!is_reaonly_non_configurable) {
|
||||
// It's necessary to add dependency on the map that introduced
|
||||
// the field.
|
||||
DCHECK(access_info.IsDataConstantField());
|
||||
DCHECK(!it.is_dictionary_holder());
|
||||
Handle<Map> field_owner_map = it.GetFieldOwnerMap();
|
||||
dependencies()->AssumeFieldOwner(field_owner_map);
|
||||
}
|
||||
return ValueEffectControl(value, effect, control);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1464,6 +1477,10 @@ JSNativeContextSpecialization::BuildPropertyAccess(
|
||||
value = effect = graph()->NewNode(simplified()->LoadField(field_access),
|
||||
storage, effect, control);
|
||||
} else {
|
||||
bool store_to_constant_field = FLAG_track_constant_fields &&
|
||||
(access_mode == AccessMode::kStore) &&
|
||||
access_info.IsDataConstantField();
|
||||
|
||||
DCHECK(access_mode == AccessMode::kStore ||
|
||||
access_mode == AccessMode::kStoreInLiteral);
|
||||
switch (field_representation) {
|
||||
@ -1510,29 +1527,62 @@ JSNativeContextSpecialization::BuildPropertyAccess(
|
||||
field_access.machine_type = MachineType::Float64();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MachineRepresentation::kTaggedSigned: {
|
||||
value = effect = graph()->NewNode(simplified()->CheckSmi(), value,
|
||||
effect, control);
|
||||
field_access.write_barrier_kind = kNoWriteBarrier;
|
||||
break;
|
||||
}
|
||||
case MachineRepresentation::kTaggedPointer: {
|
||||
// Ensure that {value} is a HeapObject.
|
||||
value = BuildCheckHeapObject(value, &effect, control);
|
||||
Handle<Map> field_map;
|
||||
if (access_info.field_map().ToHandle(&field_map)) {
|
||||
// Emit a map check for the value.
|
||||
effect = graph()->NewNode(
|
||||
simplified()->CheckMaps(CheckMapsFlag::kNone,
|
||||
ZoneHandleSet<Map>(field_map)),
|
||||
value, effect, control);
|
||||
if (store_to_constant_field) {
|
||||
DCHECK(!access_info.HasTransitionMap());
|
||||
// If the field is constant check that the value we are going
|
||||
// to store matches current value.
|
||||
Node* current_value = effect =
|
||||
graph()->NewNode(simplified()->LoadField(field_access), storage,
|
||||
effect, control);
|
||||
|
||||
Node* check = graph()->NewNode(simplified()->NumberEqual(),
|
||||
current_value, value);
|
||||
effect = graph()->NewNode(simplified()->CheckIf(), check, effect,
|
||||
control);
|
||||
return ValueEffectControl(value, effect, control);
|
||||
}
|
||||
field_access.write_barrier_kind = kPointerWriteBarrier;
|
||||
break;
|
||||
}
|
||||
case MachineRepresentation::kTaggedSigned:
|
||||
case MachineRepresentation::kTaggedPointer:
|
||||
case MachineRepresentation::kTagged:
|
||||
if (store_to_constant_field) {
|
||||
DCHECK(!access_info.HasTransitionMap());
|
||||
// If the field is constant check that the value we are going
|
||||
// to store matches current value.
|
||||
Node* current_value = effect =
|
||||
graph()->NewNode(simplified()->LoadField(field_access), storage,
|
||||
effect, control);
|
||||
|
||||
Node* check = graph()->NewNode(simplified()->ReferenceEqual(),
|
||||
current_value, value);
|
||||
effect = graph()->NewNode(simplified()->CheckIf(), check, effect,
|
||||
control);
|
||||
return ValueEffectControl(value, effect, control);
|
||||
}
|
||||
|
||||
if (field_representation == MachineRepresentation::kTaggedSigned) {
|
||||
value = effect = graph()->NewNode(simplified()->CheckSmi(), value,
|
||||
effect, control);
|
||||
field_access.write_barrier_kind = kNoWriteBarrier;
|
||||
|
||||
} else if (field_representation ==
|
||||
MachineRepresentation::kTaggedPointer) {
|
||||
// Ensure that {value} is a HeapObject.
|
||||
value = BuildCheckHeapObject(value, &effect, control);
|
||||
Handle<Map> field_map;
|
||||
if (access_info.field_map().ToHandle(&field_map)) {
|
||||
// Emit a map check for the value.
|
||||
effect = graph()->NewNode(
|
||||
simplified()->CheckMaps(CheckMapsFlag::kNone,
|
||||
ZoneHandleSet<Map>(field_map)),
|
||||
value, effect, control);
|
||||
}
|
||||
field_access.write_barrier_kind = kPointerWriteBarrier;
|
||||
|
||||
} else {
|
||||
DCHECK_EQ(MachineRepresentation::kTagged, field_representation);
|
||||
}
|
||||
break;
|
||||
case MachineRepresentation::kNone:
|
||||
case MachineRepresentation::kBit:
|
||||
|
@ -5529,6 +5529,7 @@ void HOptimizedGraphBuilder::VisitObjectLiteral(ObjectLiteral* expr) {
|
||||
store = BuildMonomorphicAccess(
|
||||
&info, literal, checked_literal, value,
|
||||
BailoutId::None(), BailoutId::None());
|
||||
DCHECK_NOT_NULL(store);
|
||||
} else {
|
||||
CHECK_ALIVE(store = BuildNamedGeneric(STORE, NULL, slot,
|
||||
literal, name, value));
|
||||
@ -5679,7 +5680,7 @@ HCheckMaps* HOptimizedGraphBuilder::AddCheckMap(HValue* object,
|
||||
HInstruction* HOptimizedGraphBuilder::BuildLoadNamedField(
|
||||
PropertyAccessInfo* info,
|
||||
HValue* checked_object) {
|
||||
// See if this is a load for an immutable property
|
||||
// Check if this is a load of an immutable or constant property.
|
||||
if (checked_object->ActualValue()->IsConstant()) {
|
||||
Handle<Object> object(
|
||||
HConstant::cast(checked_object->ActualValue())->handle(isolate()));
|
||||
@ -5687,9 +5688,20 @@ HInstruction* HOptimizedGraphBuilder::BuildLoadNamedField(
|
||||
if (object->IsJSObject()) {
|
||||
LookupIterator it(object, info->name(),
|
||||
LookupIterator::OWN_SKIP_INTERCEPTOR);
|
||||
Handle<Object> value = JSReceiver::GetDataProperty(&it);
|
||||
if (it.IsFound() && it.IsReadOnly() && !it.IsConfigurable()) {
|
||||
return New<HConstant>(value);
|
||||
if (it.IsFound()) {
|
||||
bool is_reaonly_non_configurable =
|
||||
it.IsReadOnly() && !it.IsConfigurable();
|
||||
if (is_reaonly_non_configurable ||
|
||||
(FLAG_track_constant_fields && info->IsDataConstantField())) {
|
||||
Handle<Object> value = JSReceiver::GetDataProperty(&it);
|
||||
if (!is_reaonly_non_configurable) {
|
||||
DCHECK(!it.is_dictionary_holder());
|
||||
// Add dependency on the map that introduced the field.
|
||||
Handle<Map> field_owner_map = it.GetFieldOwnerMap();
|
||||
top_info()->dependencies()->AssumeFieldOwner(field_owner_map);
|
||||
}
|
||||
return New<HConstant>(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -5718,15 +5730,17 @@ HInstruction* HOptimizedGraphBuilder::BuildLoadNamedField(
|
||||
checked_object, checked_object, access, maps, info->field_type());
|
||||
}
|
||||
|
||||
|
||||
HInstruction* HOptimizedGraphBuilder::BuildStoreNamedField(
|
||||
PropertyAccessInfo* info,
|
||||
HValue* checked_object,
|
||||
HValue* value) {
|
||||
HValue* HOptimizedGraphBuilder::BuildStoreNamedField(PropertyAccessInfo* info,
|
||||
HValue* checked_object,
|
||||
HValue* value) {
|
||||
bool transition_to_field = info->IsTransition();
|
||||
// TODO(verwaest): Move this logic into PropertyAccessInfo.
|
||||
HObjectAccess field_access = info->access();
|
||||
|
||||
bool store_to_constant_field = FLAG_track_constant_fields &&
|
||||
info->StoreMode() != INITIALIZING_STORE &&
|
||||
info->IsDataConstantField();
|
||||
|
||||
HStoreNamedField *instr;
|
||||
if (field_access.representation().IsDouble() &&
|
||||
(!FLAG_unbox_double_fields || !field_access.IsInobject())) {
|
||||
@ -5752,23 +5766,57 @@ HInstruction* HOptimizedGraphBuilder::BuildStoreNamedField(
|
||||
// Already holds a HeapNumber; load the box and write its value field.
|
||||
HInstruction* heap_number =
|
||||
Add<HLoadNamedField>(checked_object, nullptr, heap_number_access);
|
||||
instr = New<HStoreNamedField>(heap_number,
|
||||
HObjectAccess::ForHeapNumberValue(),
|
||||
value, STORE_TO_INITIALIZED_ENTRY);
|
||||
|
||||
if (store_to_constant_field) {
|
||||
// If the field is constant check that the value we are going to store
|
||||
// matches current value.
|
||||
HInstruction* current_value = Add<HLoadNamedField>(
|
||||
heap_number, nullptr, HObjectAccess::ForHeapNumberValue());
|
||||
IfBuilder value_checker(this);
|
||||
value_checker.IfNot<HCompareNumericAndBranch>(current_value, value,
|
||||
Token::EQ);
|
||||
value_checker.ThenDeopt(DeoptimizeReason::kValueMismatch);
|
||||
value_checker.End();
|
||||
return nullptr;
|
||||
|
||||
} else {
|
||||
instr = New<HStoreNamedField>(heap_number,
|
||||
HObjectAccess::ForHeapNumberValue(),
|
||||
value, STORE_TO_INITIALIZED_ENTRY);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (field_access.representation().IsHeapObject()) {
|
||||
BuildCheckHeapObject(value);
|
||||
}
|
||||
if (store_to_constant_field) {
|
||||
// If the field is constant check that the value we are going to store
|
||||
// matches current value.
|
||||
HInstruction* current_value = Add<HLoadNamedField>(
|
||||
checked_object->ActualValue(), checked_object, field_access);
|
||||
|
||||
if (!info->field_maps()->is_empty()) {
|
||||
DCHECK(field_access.representation().IsHeapObject());
|
||||
value = Add<HCheckMaps>(value, info->field_maps());
|
||||
}
|
||||
IfBuilder value_checker(this);
|
||||
if (field_access.representation().IsDouble()) {
|
||||
value_checker.IfNot<HCompareNumericAndBranch>(current_value, value,
|
||||
Token::EQ);
|
||||
} else {
|
||||
value_checker.IfNot<HCompareObjectEqAndBranch>(current_value, value);
|
||||
}
|
||||
value_checker.ThenDeopt(DeoptimizeReason::kValueMismatch);
|
||||
value_checker.End();
|
||||
return nullptr;
|
||||
|
||||
// This is a normal store.
|
||||
instr = New<HStoreNamedField>(checked_object->ActualValue(), field_access,
|
||||
value, info->StoreMode());
|
||||
} else {
|
||||
if (field_access.representation().IsHeapObject()) {
|
||||
BuildCheckHeapObject(value);
|
||||
}
|
||||
|
||||
if (!info->field_maps()->is_empty()) {
|
||||
DCHECK(field_access.representation().IsHeapObject());
|
||||
value = Add<HCheckMaps>(value, info->field_maps());
|
||||
}
|
||||
|
||||
// This is a normal store.
|
||||
instr = New<HStoreNamedField>(checked_object->ActualValue(), field_access,
|
||||
value, info->StoreMode());
|
||||
}
|
||||
}
|
||||
|
||||
if (transition_to_field) {
|
||||
|
@ -2523,6 +2523,12 @@ class HOptimizedGraphBuilder : public HGraphBuilder,
|
||||
bool IsFound() const { return lookup_type_ != NOT_FOUND; }
|
||||
bool IsProperty() const { return IsFound() && !IsTransition(); }
|
||||
bool IsTransition() const { return lookup_type_ == TRANSITION_TYPE; }
|
||||
// TODO(ishell): rename to IsDataConstant() once constant field tracking
|
||||
// is done.
|
||||
bool IsDataConstantField() const {
|
||||
return lookup_type_ == DESCRIPTOR_TYPE && details_.kind() == kData &&
|
||||
details_.location() == kField && details_.constness() == kConst;
|
||||
}
|
||||
bool IsData() const {
|
||||
return lookup_type_ == DESCRIPTOR_TYPE && details_.kind() == kData &&
|
||||
details_.location() == kField;
|
||||
@ -2729,9 +2735,8 @@ class HOptimizedGraphBuilder : public HGraphBuilder,
|
||||
|
||||
HInstruction* BuildLoadNamedField(PropertyAccessInfo* info,
|
||||
HValue* checked_object);
|
||||
HInstruction* BuildStoreNamedField(PropertyAccessInfo* info,
|
||||
HValue* checked_object,
|
||||
HValue* value);
|
||||
HValue* BuildStoreNamedField(PropertyAccessInfo* info, HValue* checked_object,
|
||||
HValue* value);
|
||||
|
||||
HValue* BuildContextChainWalk(Variable* var);
|
||||
|
||||
|
@ -675,6 +675,14 @@ int LookupIterator::GetConstantIndex() const {
|
||||
return descriptor_number();
|
||||
}
|
||||
|
||||
Handle<Map> LookupIterator::GetFieldOwnerMap() const {
|
||||
DCHECK(has_property_);
|
||||
DCHECK(holder_->HasFastProperties());
|
||||
DCHECK_EQ(kField, property_details_.location());
|
||||
DCHECK(!IsElement());
|
||||
Map* holder_map = holder_->map();
|
||||
return handle(holder_map->FindFieldOwner(descriptor_number()), isolate_);
|
||||
}
|
||||
|
||||
FieldIndex LookupIterator::GetFieldIndex() const {
|
||||
DCHECK(has_property_);
|
||||
|
@ -236,7 +236,9 @@ class V8_EXPORT_PRIVATE LookupIterator final BASE_EMBEDDED {
|
||||
Representation representation() const {
|
||||
return property_details().representation();
|
||||
}
|
||||
PropertyLocation location() const { return property_details().location(); }
|
||||
PropertyConstness constness() const { return property_details().constness(); }
|
||||
Handle<Map> GetFieldOwnerMap() const;
|
||||
FieldIndex GetFieldIndex() const;
|
||||
Handle<FieldType> GetFieldType() const;
|
||||
int GetFieldDescriptorIndex() const;
|
||||
|
268
test/mjsunit/const-field-tracking.js
Normal file
268
test/mjsunit/const-field-tracking.js
Normal file
@ -0,0 +1,268 @@
|
||||
// Copyright 2017 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Flags: --allow-natives-syntax --crankshaft --no-always-opt
|
||||
|
||||
var global = this;
|
||||
|
||||
// TODO(ishell): update the test once const->mutable migration does not
|
||||
// create a new map.
|
||||
var IS_INPLACE_MAP_MODIFICATION_SUPPORTED = false;
|
||||
|
||||
var unique_id = 0;
|
||||
// Creates a function with unique SharedFunctionInfo to ensure the feedback
|
||||
// vector is unique for each test case.
|
||||
function MakeFunctionWithUniqueSFI(...args) {
|
||||
assertTrue(args.length > 0);
|
||||
var body = `/* Unique comment: ${unique_id++} */ ` + args.pop();
|
||||
return new Function(...args, body);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Load constant field from constant object directly.
|
||||
//
|
||||
function TestLoadFromConstantFieldOfAConstantObject(the_value, other_value) {
|
||||
function A(v) { this.v = v; }
|
||||
function O() { this.a = new A(the_value); }
|
||||
var the_object = new O();
|
||||
|
||||
// Ensure that {the_object.a}'s map is not stable to complicate compiler's
|
||||
// life.
|
||||
new A(the_value).blah = 0;
|
||||
|
||||
// Ensure that constant tracking is enabled for {contant_object}.
|
||||
delete global.constant_object;
|
||||
global.constant_object = the_object;
|
||||
assertEquals(the_object, constant_object);
|
||||
|
||||
assertTrue(%HasFastProperties(the_object));
|
||||
|
||||
// {constant_object} is known to the compiler via global property cell
|
||||
// tracking.
|
||||
var load = MakeFunctionWithUniqueSFI("return constant_object.a.v;");
|
||||
load();
|
||||
load();
|
||||
%OptimizeFunctionOnNextCall(load);
|
||||
assertEquals(the_value, load());
|
||||
assertOptimized(load);
|
||||
if (IS_INPLACE_MAP_MODIFICATION_SUPPORTED) {
|
||||
var a = new A(other_value);
|
||||
assertTrue(%HaveSameMap(a, the_object.a));
|
||||
// Make constant field mutable by assigning another value
|
||||
// to some other instance of A.
|
||||
new A(the_value).v = other_value;
|
||||
assertTrue(%HaveSameMap(a, new A(the_value)));
|
||||
assertTrue(%HaveSameMap(a, the_object.a));
|
||||
assertUnoptimized(load);
|
||||
assertEquals(the_value, load());
|
||||
} else {
|
||||
var a = new A(other_value);
|
||||
assertTrue(%HaveSameMap(a, the_object.a));
|
||||
// Make constant field mutable by assigning another value
|
||||
// to some other instance of A.
|
||||
new A(the_value).v = other_value;
|
||||
assertOptimized(load);
|
||||
assertTrue(!%HaveSameMap(a, new A(the_value)));
|
||||
|
||||
assertTrue(%HaveSameMap(a, the_object.a));
|
||||
// Ensure the {the_object.a} migrated to an up-to date version of a map
|
||||
// by loading a property through IC.
|
||||
assertEquals(the_value, the_object.a.v);
|
||||
assertTrue(!%HaveSameMap(a, the_object.a));
|
||||
assertOptimized(load);
|
||||
|
||||
// Now attempt to call load should deoptimize because of failed map check.
|
||||
assertEquals(the_value, load());
|
||||
}
|
||||
assertUnoptimized(load);
|
||||
assertEquals(the_value, load());
|
||||
}
|
||||
|
||||
// Test constant tracking with Smi value.
|
||||
(function() {
|
||||
var the_value = 42;
|
||||
var other_value = 153;
|
||||
TestLoadFromConstantFieldOfAConstantObject(the_value, other_value);
|
||||
})();
|
||||
|
||||
// Test constant tracking with double value.
|
||||
(function() {
|
||||
var the_value = 0.9;
|
||||
var other_value = 0.42;
|
||||
TestLoadFromConstantFieldOfAConstantObject(the_value, other_value);
|
||||
})();
|
||||
|
||||
// Test constant tracking with function value.
|
||||
(function() {
|
||||
var the_value = function V() {};
|
||||
var other_value = function W() {};
|
||||
TestLoadFromConstantFieldOfAConstantObject(the_value, other_value);
|
||||
})();
|
||||
|
||||
// Test constant tracking with heap object value.
|
||||
(function() {
|
||||
function V() {}
|
||||
var the_value = new V();
|
||||
var other_value = new V();
|
||||
TestLoadFromConstantFieldOfAConstantObject(the_value, other_value);
|
||||
})();
|
||||
|
||||
|
||||
//
|
||||
// Load constant field from a prototype.
|
||||
//
|
||||
function TestLoadFromConstantFieldOfAPrototype(the_value, other_value) {
|
||||
function Proto() { this.v = the_value; }
|
||||
var the_prototype = new Proto();
|
||||
|
||||
function O() {}
|
||||
O.prototype = the_prototype;
|
||||
var the_object = new O();
|
||||
|
||||
// Ensure O.prototype is in fast mode by loading from its field.
|
||||
function warmup() { return new O().v; }
|
||||
warmup(); warmup(); warmup();
|
||||
assertTrue(%HasFastProperties(O.prototype));
|
||||
|
||||
// The parameter object is not constant but all the values have the same
|
||||
// map and therefore the compiler knows the prototype object and can
|
||||
// optimize load of "v".
|
||||
var load = MakeFunctionWithUniqueSFI("o", "return o.v;");
|
||||
load(new O());
|
||||
load(new O());
|
||||
%OptimizeFunctionOnNextCall(load);
|
||||
assertEquals(the_value, load(new O()));
|
||||
assertOptimized(load);
|
||||
if (IS_INPLACE_MAP_MODIFICATION_SUPPORTED) {
|
||||
// Invalidation of mutability should trigger deoptimization with a
|
||||
// "field-owner" reason.
|
||||
the_prototype.v = other_value;
|
||||
} else {
|
||||
// Invalidation of mutability should trigger deoptimization with a
|
||||
// "prototype-check" (stability) reason.
|
||||
the_prototype.v = other_value;
|
||||
}
|
||||
assertUnoptimized(load);
|
||||
}
|
||||
|
||||
// Test constant tracking with Smi value.
|
||||
(function() {
|
||||
var the_value = 42;
|
||||
var other_value = 153;
|
||||
TestLoadFromConstantFieldOfAPrototype(the_value, other_value);
|
||||
})();
|
||||
|
||||
// Test constant tracking with double value.
|
||||
(function() {
|
||||
var the_value = 0.9;
|
||||
var other_value = 0.42;
|
||||
TestLoadFromConstantFieldOfAPrototype(the_value, other_value);
|
||||
})();
|
||||
|
||||
// Test constant tracking with function value.
|
||||
(function() {
|
||||
var the_value = function V() {};
|
||||
var other_value = function W() {};
|
||||
TestLoadFromConstantFieldOfAPrototype(the_value, other_value);
|
||||
})();
|
||||
|
||||
// Test constant tracking with heap object value.
|
||||
(function() {
|
||||
function V() {}
|
||||
var the_value = new V();
|
||||
var other_value = new V();
|
||||
TestLoadFromConstantFieldOfAPrototype(the_value, other_value);
|
||||
})();
|
||||
|
||||
|
||||
//
|
||||
// Store to constant field of a constant object.
|
||||
//
|
||||
function TestStoreToConstantFieldOfConstantObject(the_value, other_value) {
|
||||
function A(v) { this.v = v; }
|
||||
function O() { this.a = new A(the_value); }
|
||||
var the_object = new O();
|
||||
|
||||
// Ensure that {the_object.a}'s map is not stable to complicate compiler's
|
||||
// life.
|
||||
new A(the_value).blah = 0;
|
||||
|
||||
// Ensure that constant tracking is enabled for {contant_object}.
|
||||
delete global.constant_object;
|
||||
global.constant_object = the_object;
|
||||
assertEquals(the_object, constant_object);
|
||||
|
||||
assertTrue(%HasFastProperties(the_object));
|
||||
|
||||
// {constant_object} is known to the compiler via global property cell
|
||||
// tracking.
|
||||
var store = MakeFunctionWithUniqueSFI("v", "constant_object.a.v = v;");
|
||||
store(the_value);
|
||||
store(the_value);
|
||||
%OptimizeFunctionOnNextCall(store);
|
||||
store(the_value);
|
||||
assertEquals(the_value, constant_object.a.v);
|
||||
assertOptimized(store);
|
||||
// Storing of the same value does not deoptimize.
|
||||
store(the_value);
|
||||
assertEquals(the_value, constant_object.a.v);
|
||||
assertOptimized(store);
|
||||
|
||||
if (IS_INPLACE_MAP_MODIFICATION_SUPPORTED) {
|
||||
var a = new A(other_value);
|
||||
|
||||
if (typeof the_value == "function" || typeof the_value == "object") {
|
||||
// For heap object fields "field-owner" dependency is installed for
|
||||
// any access of the field, therefore making constant field mutable by
|
||||
// assigning other value to some other instance of A should already
|
||||
// trigger deoptimization.
|
||||
assertTrue(%HaveSameMap(a, the_object.a));
|
||||
new A(the_value).v = other_value;
|
||||
assertTrue(%HaveSameMap(a, new A(the_value)));
|
||||
assertTrue(%HaveSameMap(a, the_object.a));
|
||||
assertUnoptimized(store);
|
||||
} else {
|
||||
assertOptimized(store);
|
||||
}
|
||||
// Storing other value deoptimizes because of failed value check.
|
||||
store(other_value);
|
||||
assertUnoptimized(store);
|
||||
assertEquals(other_value, constant_object.a.v);
|
||||
} else {
|
||||
// Storing other value deoptimizes because of failed value check.
|
||||
store(other_value);
|
||||
assertUnoptimized(store);
|
||||
assertEquals(other_value, constant_object.a.v);
|
||||
}
|
||||
}
|
||||
|
||||
// Test constant tracking with Smi values.
|
||||
(function() {
|
||||
var the_value = 42;
|
||||
var other_value = 153;
|
||||
TestStoreToConstantFieldOfConstantObject(the_value, other_value);
|
||||
})();
|
||||
|
||||
// Test constant tracking with double values.
|
||||
(function() {
|
||||
var the_value = 0.9;
|
||||
var other_value = 0.42
|
||||
TestStoreToConstantFieldOfConstantObject(the_value, other_value);
|
||||
})();
|
||||
|
||||
// Test constant tracking with function values.
|
||||
(function() {
|
||||
var the_value = function V() {};
|
||||
var other_value = function W() {};
|
||||
TestStoreToConstantFieldOfConstantObject(the_value, other_value);
|
||||
})();
|
||||
|
||||
// Test constant tracking with heap object values.
|
||||
(function() {
|
||||
function V() {}
|
||||
var the_value = new V();
|
||||
var other_value = new V();
|
||||
TestStoreToConstantFieldOfConstantObject(the_value, other_value);
|
||||
})();
|
@ -53,6 +53,9 @@
|
||||
# Issue 3784: setters-on-elements is flaky
|
||||
'setters-on-elements': [PASS, FAIL],
|
||||
|
||||
# Issue 5495: enable the test when the constant field tracking in enabled.
|
||||
'const-field-tracking': [SKIP],
|
||||
|
||||
##############################################################################
|
||||
# Too slow in debug mode with --stress-opt mode.
|
||||
'regress/regress-create-exception': [PASS, ['mode == debug', SKIP]],
|
||||
|
Loading…
Reference in New Issue
Block a user