[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:
Igor Sheludko 2017-02-17 17:01:47 +01:00 committed by Commit Bot
parent 1bbbfb42d5
commit c6b57edc04
9 changed files with 459 additions and 66 deletions

View File

@ -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;

View File

@ -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,

View File

@ -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:

View File

@ -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) {

View File

@ -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);

View File

@ -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_);

View File

@ -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;

View 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);
})();

View File

@ -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]],