diff --git a/src/compiler/js-native-context-specialization.cc b/src/compiler/js-native-context-specialization.cc index c5cc3d597f..95f8f5e663 100644 --- a/src/compiler/js-native-context-specialization.cc +++ b/src/compiler/js-native-context-specialization.cc @@ -1745,6 +1745,7 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess( // trimming. return NoChange(); } + values.push_back(continuation->value()); effects.push_back(continuation->effect()); controls.push_back(continuation->control()); @@ -2898,9 +2899,15 @@ JSNativeContextSpecialization::BuildPropertyStore( AccessBuilder::ForJSObjectPropertiesOrHashKnownPointer()), storage, effect, control); } - bool store_to_existing_constant_field = access_info.IsFastDataConstant() && - access_mode == AccessMode::kStore && - !access_info.HasTransitionMap(); + if (access_info.IsFastDataConstant() && access_mode == AccessMode::kStore && + !access_info.HasTransitionMap()) { + Node* deoptimize = graph()->NewNode( + common()->DeoptimizeIf(DeoptimizeReason::kStoreToConstant, + FeedbackSource()), + jsgraph()->TrueConstant(), frame_state, effect, control); + return ValueEffectControl(jsgraph()->UndefinedConstant(), deoptimize, + deoptimize); + } FieldAccess field_access = { kTaggedBase, field_index.offset(), @@ -2953,40 +2960,11 @@ JSNativeContextSpecialization::BuildPropertyStore( field_access.name = MaybeHandle(); field_access.machine_type = MachineType::Float64(); } - if (store_to_existing_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()->SameValue(), current_value, value); - effect = graph()->NewNode( - simplified()->CheckIf(DeoptimizeReason::kWrongValue), check, - effect, control); - return ValueEffectControl(value, effect, control); - } break; } case MachineRepresentation::kTaggedSigned: case MachineRepresentation::kTaggedPointer: case MachineRepresentation::kTagged: - if (store_to_existing_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()->SameValueNumbersOnly(), - current_value, value); - effect = graph()->NewNode( - simplified()->CheckIf(DeoptimizeReason::kWrongValue), check, - effect, control); - return ValueEffectControl(value, effect, control); - } - if (field_representation == MachineRepresentation::kTaggedSigned) { value = effect = graph()->NewNode( simplified()->CheckSmi(FeedbackSource()), value, effect, control); diff --git a/src/deoptimizer/deoptimize-reason.h b/src/deoptimizer/deoptimize-reason.h index 75042f33a4..5e686659e0 100644 --- a/src/deoptimizer/deoptimize-reason.h +++ b/src/deoptimizer/deoptimize-reason.h @@ -57,6 +57,7 @@ namespace internal { V(OutOfBounds, "out of bounds") \ V(Overflow, "overflow") \ V(Smi, "Smi") \ + V(StoreToConstant, "Storing to a constant field") \ V(SuspendGeneratorIsDead, "SuspendGenerator is in a dead branch") \ V(TransitionedToMonomorphicIC, "IC transitioned to monomorphic") \ V(TransitionedToMegamorphicIC, "IC transitioned to megamorphic") \ diff --git a/src/ic/accessor-assembler.cc b/src/ic/accessor-assembler.cc index 1ab98745bb..ed8745fa73 100644 --- a/src/ic/accessor-assembler.cc +++ b/src/ic/accessor-assembler.cc @@ -1304,24 +1304,12 @@ void AccessorAssembler::HandleStoreICHandlerCase( GotoIf(IsSetWord32(details, kTypeAndReadOnlyMask), miss); if (V8_DICT_PROPERTY_CONST_TRACKING_BOOL) { - GotoIf(IsPropertyDetailsConst(details), &if_constant); + GotoIf(IsPropertyDetailsConst(details), miss); } StoreValueByKeyIndex( properties, var_name_index.value(), p->value()); Return(p->value()); - - if (V8_DICT_PROPERTY_CONST_TRACKING_BOOL) { - BIND(&if_constant); - { - TNode prev_value = - LoadValueByKeyIndex(properties, var_name_index.value()); - Branch(TaggedEqual(prev_value, p->value()), &done, miss); - } - - BIND(&done); - Return(p->value()); - } } } BIND(&if_fast_smi); @@ -1612,14 +1600,9 @@ void AccessorAssembler::OverwriteExistingFastDataProperty( StoreMap(object, object_map); StoreObjectField(object, field_offset, heap_number); } else { + GotoIf(IsPropertyDetailsConst(details), slow); TNode heap_number = CAST(LoadObjectField(object, field_offset)); - Label store_value(this); - GotoIfNot(IsPropertyDetailsConst(details), &store_value); - TNode current_value = LoadHeapNumberValue(heap_number); - GotoIfNotSameNumberBitPattern(current_value, double_value, slow); - Goto(&done); - BIND(&store_value); StoreHeapNumberValue(heap_number, double_value); } Goto(&done); @@ -1630,11 +1613,7 @@ void AccessorAssembler::OverwriteExistingFastDataProperty( if (do_transitioning_store) { StoreMap(object, object_map); } else { - Label if_mutable(this); - GotoIfNot(IsPropertyDetailsConst(details), &if_mutable); - TNode current_value = LoadObjectField(object, field_offset); - Branch(TaggedEqual(current_value, value), &done, slow); - BIND(&if_mutable); + GotoIf(IsPropertyDetailsConst(details), slow); } StoreObjectField(object, field_offset, value); Goto(&done); @@ -1682,29 +1661,16 @@ void AccessorAssembler::OverwriteExistingFastDataProperty( &double_rep, &tagged_rep); BIND(&double_rep); { + GotoIf(IsPropertyDetailsConst(details), slow); TNode heap_number = CAST(LoadPropertyArrayElement(properties, backing_store_index)); TNode double_value = ChangeNumberToFloat64(CAST(value)); - - Label if_mutable(this); - GotoIfNot(IsPropertyDetailsConst(details), &if_mutable); - TNode current_value = LoadHeapNumberValue(heap_number); - GotoIfNotSameNumberBitPattern(current_value, double_value, slow); - Goto(&done); - - BIND(&if_mutable); StoreHeapNumberValue(heap_number, double_value); Goto(&done); } BIND(&tagged_rep); { - Label if_mutable(this); - GotoIfNot(IsPropertyDetailsConst(details), &if_mutable); - TNode current_value = - LoadPropertyArrayElement(properties, backing_store_index); - Branch(TaggedEqual(current_value, value), &done, slow); - - BIND(&if_mutable); + GotoIf(IsPropertyDetailsConst(details), slow); StorePropertyArrayElement(properties, backing_store_index, value); Goto(&done); } diff --git a/src/maglev/maglev-graph-builder.cc b/src/maglev/maglev-graph-builder.cc index 8f4a7a3196..ea0ed9d68e 100644 --- a/src/maglev/maglev-graph-builder.cc +++ b/src/maglev/maglev-graph-builder.cc @@ -1833,22 +1833,9 @@ bool MaglevGraphBuilder::TryBuildStoreField( if (original_map.UnusedPropertyFields() == 0) { return false; } - } else if (access_info.IsFastDataConstant() && - access_mode != compiler::AccessMode::kStoreInLiteral) { - // For object literals we want to just store, similar to StoreInLiteral, but - // Define is also used for classes so we can't. Bail out for now. - if (access_mode == compiler::AccessMode::kDefine) return false; - // TODO(verwaest): Support doubles. - if (field_representation.IsDouble()) return false; - ValueNode* value = GetAccumulatorTagged(); - ValueNode* expected = BuildLoadField(access_info, receiver); - if (Constant* constant = expected->TryCast()) { - BuildCheckValue(value, constant->ref()); - } else { - AddNewNode({value, expected}); - } - - return true; + } else if (access_info.IsFastDataConstant()) { + // TODO(verwaest): Deoptimize instead. + return false; } ValueNode* store_target; diff --git a/src/objects/js-objects.cc b/src/objects/js-objects.cc index 6ab660fa4e..39bd69bcbc 100644 --- a/src/objects/js-objects.cc +++ b/src/objects/js-objects.cc @@ -1532,7 +1532,7 @@ Maybe JSReceiver::ValidateAndApplyPropertyDescriptor( desc->enumerable() == current->enumerable()) && (!desc->has_configurable() || desc->configurable() == current->configurable()) && - (!desc->has_value() || current->value().is_identical_to(desc->value())) && + !desc->has_value() && (!desc->has_writable() || (current->has_writable() && current->writable() == desc->writable())) && (!desc->has_get() || diff --git a/src/objects/lookup.cc b/src/objects/lookup.cc index 5f82d9d267..7c6f6c4a35 100644 --- a/src/objects/lookup.cc +++ b/src/objects/lookup.cc @@ -375,11 +375,9 @@ void LookupIterator::PrepareForDataProperty(Handle value) { // Check that current value matches new value otherwise we should make // the property mutable. if (holder->HasFastProperties(isolate_)) { - if (!IsConstFieldValueEqualTo(*value)) { - new_constness = PropertyConstness::kMutable; - } + if (!CanStayConst(*value)) new_constness = PropertyConstness::kMutable; } else if (V8_DICT_PROPERTY_CONST_TRACKING_BOOL) { - if (!IsConstDictValueEqualTo(*value)) { + if (!DictCanStayConst(*value)) { property_details_ = property_details_.CopyWithConstness(PropertyConstness::kMutable); @@ -902,7 +900,7 @@ Handle LookupIterator::FetchValue( return handle(result, isolate_); } -bool LookupIterator::IsConstFieldValueEqualTo(Object value) const { +bool LookupIterator::CanStayConst(Object value) const { DCHECK(!IsElement(*holder_)); DCHECK(holder_->HasFastProperties(isolate_)); DCHECK_EQ(PropertyLocation::kField, property_details_.location()); @@ -932,12 +930,10 @@ bool LookupIterator::IsConstFieldValueEqualTo(Object value) const { } Object current_value = holder->RawFastPropertyAt(isolate_, field_index); - // Only allow exact same objects to ensure we don't need expensive - // validation in optimized code. - return current_value.IsUninitialized(isolate()) || current_value == value; + return current_value.IsUninitialized(isolate()); } -bool LookupIterator::IsConstDictValueEqualTo(Object value) const { +bool LookupIterator::DictCanStayConst(Object value) const { DCHECK(!IsElement(*holder_)); DCHECK(!holder_->HasFastProperties(isolate_)); DCHECK(!holder_->IsJSGlobalObject()); @@ -962,11 +958,7 @@ bool LookupIterator::IsConstDictValueEqualTo(Object value) const { current_value = dict.ValueAt(dictionary_entry()); } - if (current_value.IsUninitialized(isolate()) || current_value == value) { - return true; - } - return current_value.IsNumber(isolate_) && value.IsNumber(isolate_) && - Object::SameNumberValue(current_value.Number(), value.Number()); + return current_value.IsUninitialized(isolate()); } int LookupIterator::GetFieldDescriptorIndex() const { @@ -1053,7 +1045,7 @@ void LookupIterator::WriteDataValue(Handle value, // equal to |value|. DCHECK_IMPLIES(!initializing_store && property_details_.constness() == PropertyConstness::kConst, - IsConstFieldValueEqualTo(*value)); + CanStayConst(*value)); JSObject::cast(*holder).WriteToField(descriptor_number(), property_details_, *value); } else { @@ -1077,7 +1069,7 @@ void LookupIterator::WriteDataValue(Handle value, DCHECK_IMPLIES( V8_DICT_PROPERTY_CONST_TRACKING_BOOL && !initializing_store && property_details_.constness() == PropertyConstness::kConst, - holder->IsJSProxy(isolate_) || IsConstDictValueEqualTo(*value)); + holder->IsJSProxy(isolate_) || DictCanStayConst(*value)); if (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) { SwissNameDictionary dictionary = diff --git a/src/objects/lookup.h b/src/objects/lookup.h index 782a09225c..9adee79b30 100644 --- a/src/objects/lookup.h +++ b/src/objects/lookup.h @@ -257,8 +257,8 @@ class V8_EXPORT_PRIVATE LookupIterator final { void RestartInternal(InterceptorState interceptor_state); Handle FetchValue(AllocationPolicy allocation_policy = AllocationPolicy::kAllocationAllowed) const; - bool IsConstFieldValueEqualTo(Object value) const; - bool IsConstDictValueEqualTo(Object value) const; + bool CanStayConst(Object value) const; + bool DictCanStayConst(Object value) const; template void ReloadPropertyInformation(); diff --git a/test/mjsunit/const-dict-tracking.js b/test/mjsunit/const-dict-tracking.js deleted file mode 100644 index 63b6160ddd..0000000000 --- a/test/mjsunit/const-dict-tracking.js +++ /dev/null @@ -1,734 +0,0 @@ -// Copyright 2020 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 --turbofan --no-always-turbofan -// Flags: --no-stress-flush-code --concurrent-recompilation -// -// Tests tracking of constness of properties stored in dictionary -// mode prototypes. - - -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); -} - -// Invalidation by store handler. -(function() { - var proto = Object.create(null); - proto.z = 1; - assertFalse(%HasFastProperties(proto)); - - var o = Object.create(proto); - - function read_z() { - return o.z; - } - function update_z(new_value) { - proto.z = new_value; - } - - // Allocate feedback vector, but we don't want to optimize the function. - %PrepareFunctionForOptimization(read_z); - for (var i = 0; i < 4; i++) { - read_z(); - } - assertTrue(%HasOwnConstDataProperty(proto, "z")); - - // Allocate feedback vector, but we don't want to optimize the function. - %PrepareFunctionForOptimization(update_z); - for (var i = 0; i < 4; i++) { - // Overwriting with same value maintains const-ness. - update_z(1); - } - - - assertTrue(%HasOwnConstDataProperty(proto, "z")); - - update_z(2); - - assertFalse(%HasOwnConstDataProperty(proto, "z")); - assertEquals(2, read_z()); -})(); - -// Properties become const when dict mode object becomes prototype. -(function() { - var proto = Object.create(null); - var proto_shadow = Object.create(null); - - proto.z = 1; - proto_shadow.z = 1; - - // Make sure that z is marked as mutable. - proto.z = 2; - proto_shadow.z = 2; - - assertFalse(%HasFastProperties(proto)); - assertTrue(%HaveSameMap(proto, proto_shadow)); - - var o = Object.create(proto); - - assertFalse(%HasFastProperties(proto)); - // proto must have received new map. - assertFalse(%HaveSameMap(proto, proto_shadow)); - assertEquals(%IsDictPropertyConstTrackingEnabled(), - %HasOwnConstDataProperty(proto, "z")); -})(); - -// Properties become const when fast mode object becomes prototype. -(function() { - var proto = {} - var proto_shadow = {}; - - proto.z = 1; - proto_shadow.z = 1; - - // Make sure that z is marked as mutable. - proto.z = 2; - proto_shadow.z = 2; - - assertTrue(%HasFastProperties(proto)); - assertTrue(%HaveSameMap(proto, proto_shadow)); - - var o = Object.create(proto); - - assertFalse(%HasFastProperties(proto)); - // proto must have received new map. - assertFalse(%HaveSameMap(proto, proto_shadow)); - assertEquals(%IsDictPropertyConstTrackingEnabled(), - %HasOwnConstDataProperty(proto, "z")); -})(); - -function testbench(o, proto, update_proto, check_constness) { - var check_z = MakeFunctionWithUniqueSFI("obj", "return obj.z;"); - - if (check_constness && %IsDictPropertyConstTrackingEnabled()) - assertTrue(%HasOwnConstDataProperty(proto, "z")); - - // Allocate feedback vector, but we don't want to optimize the function. - %PrepareFunctionForOptimization(check_z); - for (var i = 0; i < 4; i++) { - check_z(o); - } - - update_proto(); - - if (%IsDictPropertyConstTrackingEnabled()) { - if (check_constness) - assertFalse(%HasOwnConstDataProperty(proto, "z")); - assertFalse(%HasFastProperties(proto)); - } - - assertEquals("2", check_z(o)); -} - -// Simple update. -(function() { - var proto = Object.create(null); - proto.z = "1"; - assertFalse(%HasFastProperties(proto)); - - var o = Object.create(proto); - - function update_z() { - proto.z = "2"; - } - - testbench(o, proto, update_z, true); -})(); - -// Update using Object.assign. -(function() { - var proto = Object.create(null); - proto.z = "1"; - assertFalse(%HasFastProperties(proto)); - - var o = Object.create(proto); - - function update_z() { - Object.assign(proto, {z: "2"}); - } - - testbench(o, proto, update_z, true); -})(); - -// Update using Object.defineProperty -(function() { - var proto = Object.create(null); - proto.z = "1"; - assertFalse(%HasFastProperties(proto)); - - var o = Object.create(proto); - - function update_z() { - Object.defineProperty(proto, 'z', { - value: "2", - configurable: true, - enumerable: true, - writable: true - }); - } - - testbench(o, proto, update_z, true); -})(); - - -// Update using setter -(function() { - var proto = Object.create(null); - Object.defineProperty(proto, "z", { - get : function () {return this.z_val;}, - set : function (new_z) {this.z_val = new_z;} - }); - - proto.z = "1"; - assertFalse(%HasFastProperties(proto)); - - var o = Object.create(proto); - - function update_z() { - proto.z = "2"; - } - - testbench(o, proto, update_z, false); -})(); - -// Proxy test 1: Update via proxy. -(function() { - var proto = Object.create(null); - - var proxy = new Proxy(proto, {}); - - proxy.z = "1"; - assertFalse(%HasFastProperties(proto)); - - var o = Object.create(proxy); - - function update_z() { - proxy.z = "2"; - } - - testbench(o, proto, update_z, false); -})(); - -// Proxy test 2: Update on proto. -(function() { - var proto = Object.create(null); - - var proxy = new Proxy(proto, {}); - - proto.z = "1"; - assertFalse(%HasFastProperties(proto)); - - var o = Object.create(proxy); - - function update_z() { - proto.z = "2"; - } - - testbench(o, proto, update_z, false); -})(); - -// Proxy test 3: Update intercepted. -(function() { - var proto = Object.create(null); - - var handler = { - get: function(target, prop) { - return target.the_value; - }, - set: function(target, prop, value) { - return target.the_value = value; - } - }; - - var proxy = new Proxy(proto, handler); - - proxy.z = "1"; - assertFalse(%HasFastProperties(proto)); - - var o = Object.create(proxy); - - function update_z() { - proxy.z = "2"; - } - - testbench(o, proto, update_z, false); - -})(); - -// -// Below: Testing TF optimization of accessing constants in dictionary mode -// protoypes. -// - -// Test inlining with fast mode receiver. -(function() { - var proto = Object.create(null); - proto.x = 1; - var o = Object.create(proto); - assertTrue(%HasFastProperties(o)); - assertFalse(%HasFastProperties(proto)); - - function read_x(arg_o) { - return arg_o.x; - } - - %PrepareFunctionForOptimization(read_x); - assertEquals(1, read_x(o)); - %OptimizeFunctionOnNextCall(read_x); - assertEquals(1, read_x(o)); - assertOptimized(read_x); - - - // Test that we inlined the access: - var dummy = {x : 123}; - read_x(dummy); - - if (%IsDictPropertyConstTrackingEnabled()) { - assertTrue(%HasFastProperties(o)); - assertFalse(%HasFastProperties(proto)); - assertUnoptimized(read_x); - } - -})(); - -// Test inlining with dictionary mode receiver that is a prototype. - -(function() { - - var proto1 = Object.create(null); - proto1.x = 1; - var proto2 = Object.create(null); - var o = Object.create(proto1); - Object.setPrototypeOf(proto1, proto2); - - assertTrue(%HasFastProperties(o)); - assertFalse(%HasFastProperties(proto1)); - assertFalse(%HasFastProperties(proto2)); - - function read_x(arg_o) { - return arg_o.x; - } - - %PrepareFunctionForOptimization(read_x); - assertEquals(1, read_x(proto1)); - %OptimizeFunctionOnNextCall(read_x); - assertEquals(1, read_x(proto1)); - assertOptimized(read_x); - - // Test that we inlined the access: - var dummy = {x : 123}; - read_x(dummy); - - // TODO(v8:11457) This test doesn't work yet, see TODO in - // AccessInfoFactory::TryLoadPropertyDetails. Currently, we can't inline - // accesses with dictionary mode receivers. - // if (%IsDictPropertyConstTrackingEnabled()) { - // assertTrue(%HasFastProperties(o)); - // assertFalse(%HasFastProperties(proto1)); - // assertFalse(%HasFastProperties(proto2)); - // assertUnoptimized(read_x); - // } -})(); - -// The machinery we use for detecting the invalidation of constants held by -// dictionary mode objects (related to the prototype validity cell mechanism) is -// specific to prototypes. This means that for non-prototype dictionary mode -// objects, we have no way of detecting changes invalidating folded -// constants. Therefore, we must not fold constants held by non-prototype -// dictionary mode objects. This is tested here. -(function() { - var proto = Object.create(null); - proto.x = 1; - var o = Object.create(null); - Object.setPrototypeOf(o, proto); - assertFalse(%HasFastProperties(o)); - assertFalse(%HasFastProperties(proto)); - - function read_x(arg_o) { - return arg_o.x; - } - - %PrepareFunctionForOptimization(read_x); - assertEquals(1, read_x(o)); - %OptimizeFunctionOnNextCall(read_x); - assertEquals(1, read_x(o)); - assertOptimized(read_x); - - var dummy = {x : 123}; - read_x(dummy); - - if (%IsDictPropertyConstTrackingEnabled()) { - assertFalse(%HasFastProperties(o)); - assertFalse(%HasFastProperties(proto)); - - // We never inlined the acceess, so it's still optimized. - assertOptimized(read_x); - } -})(); - -// Test inlining of accessor. -(function() { - var proto = Object.create(null); - proto.x_val = 1; - Object.defineProperty(proto, "x", { - get : function () {return this.x_val;} - }); - - var o = Object.create(proto); - assertFalse(%HasFastProperties(proto)) - - function read_x(arg_o) { - return arg_o.x; - } - - %PrepareFunctionForOptimization(read_x); - assertEquals(1, read_x(o)); - %OptimizeFunctionOnNextCall(read_x); - assertEquals(1, read_x(o)); - assertOptimized(read_x); - - // Test that we inlined the access: - var dummy = {x : 123}; - read_x(dummy); - - if (%IsDictPropertyConstTrackingEnabled()) { - assertTrue(%HasFastProperties(o)); - assertFalse(%HasFastProperties(proto)); - assertUnoptimized(read_x); - } -})(); - -// Invalidation by adding same property to receiver. -(function() { - var proto = Object.create(null); - proto.x = 1; - var o = Object.create(proto); - assertTrue(%HasFastProperties(o)); - assertFalse(%HasFastProperties(proto)); - - function read_x(arg_o) { - return arg_o.x; - } - - %PrepareFunctionForOptimization(read_x); - assertEquals(1, read_x(o)); - %OptimizeFunctionOnNextCall(read_x); - assertEquals(1, read_x(o)); - assertOptimized(read_x); - - o.x = 2; - - assertEquals(2, read_x(o)); - - if (%IsDictPropertyConstTrackingEnabled()) { - assertTrue(%HasFastProperties(o)); - assertFalse(%HasFastProperties(proto)); - assertUnoptimized(read_x); - } - -})(); - -// Invalidation by adding property to intermediate prototype. -(function() { - var proto = Object.create(null); - proto.x = 1; - var in_between = Object.create(null); - Object.setPrototypeOf(in_between, proto); - - var o = Object.create(in_between); - assertTrue(%HasFastProperties(o)); - assertFalse(%HasFastProperties(in_between)); - assertFalse(%HasFastProperties(proto)); - - function read_x(arg_o) { - return arg_o.x; - } - - %PrepareFunctionForOptimization(read_x); - assertEquals(1, read_x(o)); - %OptimizeFunctionOnNextCall(read_x); - assertEquals(1, read_x(o)); - assertOptimized(read_x); - - in_between.x = 2; - - if (%IsDictPropertyConstTrackingEnabled()) { - assertFalse(%HasFastProperties(in_between)); - assertFalse(%HasFastProperties(proto)); - assertUnoptimized(read_x); - } - - assertEquals(2, read_x(o)); -})(); - -// Invalidation by changing prototype of receiver. -(function() { - var proto = Object.create(null); - proto.x = 1; - var other_proto = Object.create(null); - other_proto.x = 2; - - var o = Object.create(proto); - assertTrue(%HasFastProperties(o)); - assertFalse(%HasFastProperties(proto)); - - function read_x(arg_o) { - return arg_o.x; - } - - %PrepareFunctionForOptimization(read_x); - assertEquals(1, read_x(o)); - %OptimizeFunctionOnNextCall(read_x); - assertEquals(1, read_x(o)); - - Object.setPrototypeOf(o, other_proto); - assertEquals(2, read_x(o)); - - if (%IsDictPropertyConstTrackingEnabled()) { - assertFalse(%HasFastProperties(proto)); - assertFalse(%HasFastProperties(other_proto)); - assertUnoptimized(read_x); - } -})(); - -// Invalidation by changing [[Prototype]] of a prototype on the chain from the -// receiver to the holder. -(function() { - var proto = Object.create(null); - proto.x = 1; - var other_proto = Object.create(null); - other_proto.x = 2; - var in_between = Object.create(null); - Object.setPrototypeOf(in_between, proto); - - var o = Object.create(in_between); - assertTrue(%HasFastProperties(o)); - assertFalse(%HasFastProperties(in_between)); - assertFalse(%HasFastProperties(proto)); - - function read_x(arg_o) { - return arg_o.x; - } - - %PrepareFunctionForOptimization(read_x); - assertEquals(1, read_x(o)); - %OptimizeFunctionOnNextCall(read_x); - assertEquals(1, read_x(o)); - assertOptimized(read_x); - - Object.setPrototypeOf(in_between, other_proto); - - if (%IsDictPropertyConstTrackingEnabled()) { - assertFalse(%HasFastProperties(in_between)); - assertFalse(%HasFastProperties(proto)); - assertFalse(%HasFastProperties(other_proto)); - assertUnoptimized(read_x); - } - - assertEquals(2, read_x(o)); -})(); - -// Invalidation by changing property on prototype itself. -(function() { - var proto = Object.create(null); - proto.x = 1; - var o = Object.create(proto); - assertTrue(%HasFastProperties(o)); - assertFalse(%HasFastProperties(proto)); - - function read_x(arg_o) { - return arg_o.x; - } - - %PrepareFunctionForOptimization(read_x); - assertEquals(1, read_x(o)); - %OptimizeFunctionOnNextCall(read_x); - assertEquals(1, read_x(o)); - assertOptimized(read_x); - - proto.x = 2; - - if (%IsDictPropertyConstTrackingEnabled()) { - assertFalse(%HasFastProperties(proto)); - assertUnoptimized(read_x); - } - - assertEquals(2, read_x(o)); -})(); - -// Invalidation by deleting property on prototype. -(function() { - var proto = Object.create(null); - proto.x = 1; - var o = Object.create(proto); - assertTrue(%HasFastProperties(o)); - assertFalse(%HasFastProperties(proto)); - - function read_x(arg_o) { - return arg_o.x; - } - - %PrepareFunctionForOptimization(read_x); - read_x(o); - %OptimizeFunctionOnNextCall(read_x); - read_x(o); - - delete proto.x; - - if (%IsDictPropertyConstTrackingEnabled()) { - assertFalse(%HasFastProperties(proto)); - assertUnoptimized(read_x); - } - - assertEquals(undefined, read_x(o)); -})(); - -// Storing the same value does not invalidate const-ness. Store done from -// runtime/without feedback. -(function() { - var proto = Object.create(null); - var some_object = {bla: 123}; - proto.x = 1; - proto.y = some_object - - var o = Object.create(proto); - assertTrue(%HasFastProperties(o)); - assertFalse(%HasFastProperties(proto)); - - function read_xy(arg_o) { - return [arg_o.x, arg_o.y]; - } - - %PrepareFunctionForOptimization(read_xy); - assertEquals([1, some_object], read_xy(o)); - %OptimizeFunctionOnNextCall(read_xy); - assertEquals([1, some_object], read_xy(o)); - assertOptimized(read_xy); - - // Build value 1 without re-using proto.x. - var x2 = 0; - for(var i = 0; i < 5; ++i) { - x2 += 0.2; - } - - // Storing the same values for x and y again: - proto.x = x2; - proto.y = some_object; - assertEquals(x2, proto.x); - - if (%IsDictPropertyConstTrackingEnabled()) { - assertFalse(%HasFastProperties(proto)); - assertTrue(%HasOwnConstDataProperty(proto, "x")); - assertOptimized(read_xy); - } - - proto.x = 2; - if (%IsDictPropertyConstTrackingEnabled()) { - assertFalse(%HasFastProperties(proto)); - assertFalse(%HasOwnConstDataProperty(proto, "x")); - assertUnoptimized(read_xy); - } - - assertEquals(2, read_xy(o)[0]); -})(); - -// Storing the same value does not invalidate const-ness. Store done by IC -// handler. -(function() { - var proto = Object.create(null); - var some_object = {bla: 123}; - proto.x = 1; - proto.y = some_object - - var o = Object.create(proto); - assertTrue(%HasFastProperties(o)); - assertFalse(%HasFastProperties(proto)); - - function read_xy(arg_o) { - return [arg_o.x, arg_o.y]; - } - - %PrepareFunctionForOptimization(read_xy); - assertEquals([1, some_object], read_xy(o)); - %OptimizeFunctionOnNextCall(read_xy); - assertEquals([1, some_object], read_xy(o)); - assertOptimized(read_xy); - - // Build value 1 without re-using proto.x. - var x2 = 0; - for(var i = 0; i < 5; ++i) { - x2 += 0.2; - } - - function change_xy(obj, x, y) { - obj.x = x; - obj.y = y; - } - - %PrepareFunctionForOptimization(change_xy); - // Storing the same values for x and y again: - change_xy(proto, 1, some_object); - change_xy(proto, 1, some_object); - - if (%IsDictPropertyConstTrackingEnabled()) { - assertFalse(%HasFastProperties(proto)); - assertTrue(%HasOwnConstDataProperty(proto, "x")); - assertOptimized(read_xy); - } - - change_xy(proto, 2, some_object); - - if (%IsDictPropertyConstTrackingEnabled()) { - assertFalse(%HasFastProperties(proto)); - assertFalse(%HasOwnConstDataProperty(proto, "x")); - assertUnoptimized(read_xy); - } - - assertEquals(2, read_xy(o)[0]); -})(); - -// Invalidation by replacing a prototype. Just like the old prototype, the new -// prototype owns the property as an accessor, but in the form of an -// AccessorInfo rather than an AccessorPair. -(function() { - var proto1 = Object.create(null); - Object.defineProperty(proto1, 'length', {get() {return 1}}); - var proto2 = Object.create(proto1); - var o = Object.create(proto2); - assertTrue(%HasFastProperties(o)); - assertFalse(%HasFastProperties(proto1)); - assertFalse(%HasFastProperties(proto2)); - - function read_length(arg_o) { - return arg_o.length; - } - - %PrepareFunctionForOptimization(read_length); - assertEquals(1, read_length(o)); - %DisableOptimizationFinalization(); - %OptimizeFunctionOnNextCall(read_length, "concurrent"); - assertEquals(1, read_length(o)); - assertUnoptimized(read_length); - - %WaitForBackgroundOptimization(); - var other_proto1 = []; - Object.setPrototypeOf(proto2, other_proto1); - %FinalizeOptimization(); - - assertUnoptimized(read_length); - assertEquals(0, read_length(o)); - - if (%IsDictPropertyConstTrackingEnabled()) { - assertFalse(%HasFastProperties(proto1)); - assertFalse(%HasFastProperties(proto2)); - assertFalse(%HasFastProperties(other_proto1)); - assertUnoptimized(read_length); - } -})(); diff --git a/test/mjsunit/const-field-tracking.js b/test/mjsunit/const-field-tracking.js deleted file mode 100644 index 96bce0ac13..0000000000 --- a/test/mjsunit/const-field-tracking.js +++ /dev/null @@ -1,237 +0,0 @@ -// 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 --turbofan --no-always-turbofan - -var global = this; -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;"); - %PrepareFunctionForOptimization(load); - load(); - load(); - %OptimizeFunctionOnNextCall(load); - assertEquals(the_value, load()); - assertOptimized(load); - 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()); - 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; } - %EnsureFeedbackVectorForFunction(warmup); - warmup(); warmup(); warmup(); - if (!%IsDictPropertyConstTrackingEnabled()) - 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;"); - %PrepareFunctionForOptimization(load); - load(new O()); - load(new O()); - %OptimizeFunctionOnNextCall(load); - assertEquals(the_value, load(new O())); - assertOptimized(load); - // Invalidation of mutability should trigger deoptimization with a - // "field-owner" 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;"); - %PrepareFunctionForOptimization(store); - 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); - - 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); -} - -// 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); -})(); diff --git a/test/mjsunit/mjsunit.status b/test/mjsunit/mjsunit.status index 8e33b1c9b9..6b5db317f4 100644 --- a/test/mjsunit/mjsunit.status +++ b/test/mjsunit/mjsunit.status @@ -1588,7 +1588,6 @@ 'regress/regress-11519': [SKIP], 'regress/regress-4121': [SKIP], 'packed-elements': [SKIP], - 'const-dict-tracking': [SKIP], 'compiler/native-context-specialization-hole-check': [SKIP], 'compiler/test-literal-map-migration': [SKIP], 'compiler/deopt-pretenure': [SKIP], @@ -1612,8 +1611,6 @@ ################################################################################ ['third_party_heap', { - # Requires local heaps - 'const-field-tracking': [SKIP], # Requires --concurrent_inlining / --finalize_streaming_on_background: 'regress/regress-1220974': [SKIP], 'regress-1146106': [SKIP], @@ -1775,7 +1772,6 @@ # TODO(b/201757247): 'array-constructor-feedback': [FAIL], - 'const-dict-tracking': [FAIL], 'compiler/deopt-pretenure': [FAIL], 'compiler/fast-api-sequences-x64': [FAIL], 'compiler/native-context-specialization-hole-check': [FAIL],