Commit fc36dfb7 authored by Georg Neis's avatar Georg Neis Committed by Commit Bot

[turbofan] Serialize for ReduceKeyedLoadFromHeapConstant

Drive-by fix: In ProcessFeedbackForGlobalAccess, we had forgotten to
return the feedback when it already existed.

Bug: v8:7790, v8:9094
Change-Id: Ie4be6cef5755bbdd9d8ed472caaa2e32d243893d
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1554680Reviewed-by: 's avatarJaroslav Sevcik <jarin@chromium.org>
Commit-Queue: Georg Neis <neis@chromium.org>
Cr-Commit-Position: refs/heads/master@{#60705}
parent 38ce72ae
This diff is collapsed.
...@@ -124,6 +124,11 @@ class V8_EXPORT_PRIVATE ObjectRef { ...@@ -124,6 +124,11 @@ class V8_EXPORT_PRIVATE ObjectRef {
bool BooleanValue() const; bool BooleanValue() const;
Maybe<double> OddballToNumber() const; Maybe<double> OddballToNumber() const;
// Return the element at key {index} if {index} is known to be an own data
// property of the object that is non-writable and non-configurable.
base::Optional<ObjectRef> GetOwnConstantElement(uint32_t index,
bool serialize = false) const;
Isolate* isolate() const; Isolate* isolate() const;
protected: protected:
...@@ -132,8 +137,12 @@ class V8_EXPORT_PRIVATE ObjectRef { ...@@ -132,8 +137,12 @@ class V8_EXPORT_PRIVATE ObjectRef {
ObjectData* data_; // Should be used only by object() getters. ObjectData* data_; // Should be used only by object() getters.
private: private:
friend class JSArrayData;
friend class JSGlobalProxyRef; friend class JSGlobalProxyRef;
friend class JSGlobalProxyData; friend class JSGlobalProxyData;
friend class JSObjectData;
friend class StringData;
JSHeapBroker* broker_; JSHeapBroker* broker_;
}; };
...@@ -521,6 +530,11 @@ class JSArrayRef : public JSObjectRef { ...@@ -521,6 +530,11 @@ class JSArrayRef : public JSObjectRef {
Handle<JSArray> object() const; Handle<JSArray> object() const;
ObjectRef length() const; ObjectRef length() const;
// Return the element at key {index} if the array has a copy-on-write elements
// storage and {index} is known to be an own data property.
base::Optional<ObjectRef> GetOwnCowElement(uint32_t index,
bool serialize = false) const;
}; };
class ScopeInfoRef : public HeapObjectRef { class ScopeInfoRef : public HeapObjectRef {
......
...@@ -1742,58 +1742,38 @@ Reduction JSNativeContextSpecialization::ReduceKeyedLoadFromHeapConstant( ...@@ -1742,58 +1742,38 @@ Reduction JSNativeContextSpecialization::ReduceKeyedLoadFromHeapConstant(
return NoChange(); return NoChange();
} }
// Check whether we're accessing a known element on the {receiver} // Check whether we're accessing a known element on the {receiver} and can
// that is non-configurable, non-writable (e.g. the {receiver} was // constant-fold the load.
// frozen using Object.freeze).
NumberMatcher mindex(index); NumberMatcher mindex(index);
if (mindex.IsInteger() && mindex.IsInRange(0.0, kMaxUInt32 - 1.0)) { if (mindex.IsInteger() && mindex.IsInRange(0.0, kMaxUInt32 - 1.0)) {
LookupIterator it(isolate(), receiver_ref.object(), uint32_t index_value = static_cast<uint32_t>(mindex.Value());
static_cast<uint32_t>(mindex.Value()), base::Optional<ObjectRef> element =
LookupIterator::OWN); receiver_ref.GetOwnConstantElement(index_value);
if (it.state() == LookupIterator::DATA) { if (!element.has_value() && receiver_ref.IsJSArray()) {
if (it.IsReadOnly() && !it.IsConfigurable()) { // We didn't find a constant element, but if the receiver is a cow-array
// We can safely constant-fold the {index} access to {receiver}, // we can exploit the fact that any future write to the element will
// since the element is non-configurable, non-writable and thus // replace the whole elements storage.
// cannot change anymore. element = receiver_ref.AsJSArray().GetOwnCowElement(index_value);
Node* value = access_mode == AccessMode::kHas if (element.has_value()) {
? jsgraph()->TrueConstant() Node* elements = effect = graph()->NewNode(
: jsgraph()->Constant(it.GetDataValue()); simplified()->LoadField(AccessBuilder::ForJSObjectElements()),
ReplaceWithValue(node, value, effect, control); receiver, effect, control);
return Replace(value); FixedArrayRef array_elements =
} receiver_ref.AsJSArray().elements().AsFixedArray();
Node* check = graph()->NewNode(simplified()->ReferenceEqual(), elements,
// Check if the {receiver} is a known constant with a copy-on-write jsgraph()->Constant(array_elements));
// backing store, and whether {index} is within the appropriate effect = graph()->NewNode(
// bounds. In that case we can constant-fold the access and only simplified()->CheckIf(DeoptimizeReason::kCowArrayElementsChanged),
// check that the {elements} didn't change. This is sufficient as check, effect, control);
// the backing store of a copy-on-write JSArray is defensively
// copied whenever the length or the elements (might) change.
//
// What's interesting here is that we don't need to map check the
// {receiver}, since JSArray's will always have their elements in
// the backing store.
if (receiver_ref.IsJSArray()) {
Handle<JSArray> array = receiver_ref.AsJSArray().object();
if (array->elements()->IsCowArray()) {
Node* elements = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForJSObjectElements()),
receiver, effect, control);
Handle<FixedArray> array_elements(FixedArray::cast(array->elements()),
isolate());
Node* check =
graph()->NewNode(simplified()->ReferenceEqual(), elements,
jsgraph()->HeapConstant(array_elements));
effect = graph()->NewNode(
simplified()->CheckIf(DeoptimizeReason::kCowArrayElementsChanged),
check, effect, control);
Node* value = access_mode == AccessMode::kHas
? jsgraph()->TrueConstant()
: jsgraph()->Constant(it.GetDataValue());
ReplaceWithValue(node, value, effect, control);
return Replace(value);
}
} }
} }
if (element.has_value()) {
Node* value = access_mode == AccessMode::kHas
? jsgraph()->TrueConstant()
: jsgraph()->Constant(*element);
ReplaceWithValue(node, value, effect, control);
return Replace(value);
}
} }
// For constant Strings we can eagerly strength-reduce the keyed // For constant Strings we can eagerly strength-reduce the keyed
...@@ -2272,7 +2252,6 @@ JSNativeContextSpecialization::BuildPropertyAccess( ...@@ -2272,7 +2252,6 @@ JSNativeContextSpecialization::BuildPropertyAccess(
return BuildPropertyTest(effect, control, access_info); return BuildPropertyTest(effect, control, access_info);
} }
UNREACHABLE(); UNREACHABLE();
return ValueEffectControl();
} }
JSNativeContextSpecialization::ValueEffectControl JSNativeContextSpecialization::ValueEffectControl
......
...@@ -717,13 +717,15 @@ SerializerForBackgroundCompilation::ProcessFeedbackForGlobalAccess( ...@@ -717,13 +717,15 @@ SerializerForBackgroundCompilation::ProcessFeedbackForGlobalAccess(
if (slot.IsInvalid()) return nullptr; if (slot.IsInvalid()) return nullptr;
if (environment()->function().feedback_vector.is_null()) return nullptr; if (environment()->function().feedback_vector.is_null()) return nullptr;
FeedbackSource source(environment()->function().feedback_vector, slot); FeedbackSource source(environment()->function().feedback_vector, slot);
if (!broker()->HasFeedback(source)) {
const GlobalAccessFeedback* feedback = if (broker()->HasFeedback(source)) {
broker()->ProcessFeedbackForGlobalAccess(source); return broker()->GetGlobalAccessFeedback(source);
broker()->SetFeedback(source, feedback);
return feedback;
} }
return nullptr;
const GlobalAccessFeedback* feedback =
broker()->ProcessFeedbackForGlobalAccess(source);
broker()->SetFeedback(source, feedback);
return feedback;
} }
void SerializerForBackgroundCompilation::VisitLdaGlobal( void SerializerForBackgroundCompilation::VisitLdaGlobal(
...@@ -818,6 +820,43 @@ void SerializerForBackgroundCompilation::ProcessFeedbackForKeyedPropertyAccess( ...@@ -818,6 +820,43 @@ void SerializerForBackgroundCompilation::ProcessFeedbackForKeyedPropertyAccess(
} }
} }
void SerializerForBackgroundCompilation::ProcessKeyedPropertyAccess(
Hints const& receiver, Hints const& key, FeedbackSlot slot,
AccessMode mode) {
ProcessFeedbackForKeyedPropertyAccess(slot, mode);
for (Handle<Object> hint : receiver.constants()) {
ObjectRef receiver_ref(broker(), hint);
// For JSNativeContextSpecialization::ReduceElementAccess.
if (mode == AccessMode::kStore) {
if (receiver_ref.IsJSTypedArray()) {
receiver_ref.AsJSTypedArray().Serialize();
}
}
// For JSNativeContextSpecialization::ReduceKeyedLoadFromHeapConstant.
if (mode == AccessMode::kLoad || mode == AccessMode::kHas) {
for (Handle<Object> hint : key.constants()) {
ObjectRef key_ref(broker(), hint);
// TODO(neis): Do this for integer-HeapNumbers too?
if (key_ref.IsSmi() && key_ref.AsSmi() >= 0) {
base::Optional<ObjectRef> element =
receiver_ref.GetOwnConstantElement(key_ref.AsSmi(), true);
if (!element.has_value() && receiver_ref.IsJSArray()) {
// We didn't find a constant element, but if the receiver is a
// cow-array we can exploit the fact that any future write to the
// element will replace the whole elements storage.
receiver_ref.AsJSArray().GetOwnCowElement(key_ref.AsSmi(), true);
}
}
}
}
}
environment()->accumulator_hints().Clear();
}
void SerializerForBackgroundCompilation::ProcessMapForNamedPropertyAccess( void SerializerForBackgroundCompilation::ProcessMapForNamedPropertyAccess(
MapRef const& map, NameRef const& name) { MapRef const& map, NameRef const& name) {
// For JSNativeContextSpecialization::ReduceNamedAccess. // For JSNativeContextSpecialization::ReduceNamedAccess.
...@@ -850,9 +889,11 @@ void SerializerForBackgroundCompilation::ProcessFeedbackForNamedPropertyAccess( ...@@ -850,9 +889,11 @@ void SerializerForBackgroundCompilation::ProcessFeedbackForNamedPropertyAccess(
void SerializerForBackgroundCompilation::VisitLdaKeyedProperty( void SerializerForBackgroundCompilation::VisitLdaKeyedProperty(
BytecodeArrayIterator* iterator) { BytecodeArrayIterator* iterator) {
Hints const& key = environment()->accumulator_hints();
Hints const& receiver =
environment()->register_hints(iterator->GetRegisterOperand(0));
FeedbackSlot slot = iterator->GetSlotOperand(1); FeedbackSlot slot = iterator->GetSlotOperand(1);
ProcessFeedbackForKeyedPropertyAccess(slot, AccessMode::kLoad); ProcessKeyedPropertyAccess(receiver, key, slot, AccessMode::kLoad);
environment()->accumulator_hints().Clear();
} }
void SerializerForBackgroundCompilation::ProcessNamedPropertyAccess( void SerializerForBackgroundCompilation::ProcessNamedPropertyAccess(
...@@ -907,32 +948,31 @@ void SerializerForBackgroundCompilation::VisitStaNamedProperty( ...@@ -907,32 +948,31 @@ void SerializerForBackgroundCompilation::VisitStaNamedProperty(
void SerializerForBackgroundCompilation::VisitTestIn( void SerializerForBackgroundCompilation::VisitTestIn(
BytecodeArrayIterator* iterator) { BytecodeArrayIterator* iterator) {
Hints const& receiver = environment()->accumulator_hints();
Hints const& key =
environment()->register_hints(iterator->GetRegisterOperand(0));
FeedbackSlot slot = iterator->GetSlotOperand(1); FeedbackSlot slot = iterator->GetSlotOperand(1);
ProcessFeedbackForKeyedPropertyAccess(slot, AccessMode::kHas); ProcessKeyedPropertyAccess(receiver, key, slot, AccessMode::kHas);
environment()->accumulator_hints().Clear();
} }
void SerializerForBackgroundCompilation::VisitStaKeyedProperty( void SerializerForBackgroundCompilation::VisitStaKeyedProperty(
BytecodeArrayIterator* iterator) { BytecodeArrayIterator* iterator) {
const Hints& receiver = Hints const& receiver =
environment()->register_hints(iterator->GetRegisterOperand(0)); environment()->register_hints(iterator->GetRegisterOperand(0));
Hints const& key =
environment()->register_hints(iterator->GetRegisterOperand(1));
FeedbackSlot slot = iterator->GetSlotOperand(2); FeedbackSlot slot = iterator->GetSlotOperand(2);
ProcessKeyedPropertyAccess(receiver, key, slot, AccessMode::kStore);
ProcessFeedbackForKeyedPropertyAccess(slot, AccessMode::kStore);
for (Handle<Object> object : receiver.constants()) {
// For JSNativeContextSpecialization::ReduceElementAccess.
if (object->IsJSTypedArray()) JSTypedArrayRef(broker(), object).Serialize();
}
environment()->accumulator_hints().Clear();
} }
void SerializerForBackgroundCompilation::VisitStaInArrayLiteral( void SerializerForBackgroundCompilation::VisitStaInArrayLiteral(
BytecodeArrayIterator* iterator) { BytecodeArrayIterator* iterator) {
Hints const& receiver =
environment()->register_hints(iterator->GetRegisterOperand(0));
Hints const& key =
environment()->register_hints(iterator->GetRegisterOperand(1));
FeedbackSlot slot = iterator->GetSlotOperand(2); FeedbackSlot slot = iterator->GetSlotOperand(2);
ProcessFeedbackForKeyedPropertyAccess(slot, AccessMode::kStoreInLiteral); ProcessKeyedPropertyAccess(receiver, key, slot, AccessMode::kStoreInLiteral);
environment()->accumulator_hints().Clear();
} }
#define DEFINE_CLEAR_ENVIRONMENT(name, ...) \ #define DEFINE_CLEAR_ENVIRONMENT(name, ...) \
......
...@@ -251,6 +251,8 @@ class SerializerForBackgroundCompilation { ...@@ -251,6 +251,8 @@ class SerializerForBackgroundCompilation {
void ProcessJump(interpreter::BytecodeArrayIterator* iterator); void ProcessJump(interpreter::BytecodeArrayIterator* iterator);
void MergeAfterJump(interpreter::BytecodeArrayIterator* iterator); void MergeAfterJump(interpreter::BytecodeArrayIterator* iterator);
void ProcessKeyedPropertyAccess(Hints const& receiver, Hints const& key,
FeedbackSlot slot, AccessMode mode);
void ProcessNamedPropertyAccess(interpreter::BytecodeArrayIterator* iterator, void ProcessNamedPropertyAccess(interpreter::BytecodeArrayIterator* iterator,
AccessMode mode); AccessMode mode);
void ProcessNamedPropertyAccess(Hints const& receiver, NameRef const& name, void ProcessNamedPropertyAccess(Hints const& receiver, NameRef const& name,
......
...@@ -303,16 +303,15 @@ class JSObject : public JSReceiver { ...@@ -303,16 +303,15 @@ class JSObject : public JSReceiver {
// corresponds to a set of object representations of elements that // corresponds to a set of object representations of elements that
// have something in common. // have something in common.
// //
// In the fast mode elements is a FixedArray and so each element can // In the fast mode elements is a FixedArray and so each element can be
// be quickly accessed. This fact is used in the generated code. The // quickly accessed. The elements array can have one of several maps in this
// elements array can have one of three maps in this mode: // mode: fixed_array_map, fixed_double_array_map,
// fixed_array_map, sloppy_arguments_elements_map or // sloppy_arguments_elements_map or fixed_cow_array_map (for copy-on-write
// fixed_cow_array_map (for copy-on-write arrays). In the latter case // arrays). In the latter case the elements array may be shared by a few
// the elements array may be shared by a few objects and so before // objects and so before writing to any element the array must be copied. Use
// writing to any element the array must be copied. Use
// EnsureWritableFastElements in this case. // EnsureWritableFastElements in this case.
// //
// In the slow mode the elements is either a NumberDictionary, a // In the slow mode the elements is either a NumberDictionary or a
// FixedArray parameter map for a (sloppy) arguments object. // FixedArray parameter map for a (sloppy) arguments object.
DECL_ACCESSORS(elements, FixedArrayBase) DECL_ACCESSORS(elements, FixedArrayBase)
inline void initialize_elements(); inline void initialize_elements();
......
// Copyright 2019 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
var s = "hello";
function foo() {
return s[4];
}
assertTrue("o" === foo());
assertTrue("o" === foo());
%OptimizeFunctionOnNextCall(foo);
assertTrue("o" === foo());
function bar() {
return s[5];
}
assertSame(undefined, bar());
assertSame(undefined, bar());
%OptimizeFunctionOnNextCall(bar);
assertSame(undefined, bar());
...@@ -1035,4 +1035,10 @@ ...@@ -1035,4 +1035,10 @@
'*': [SKIP], '*': [SKIP],
}], # variant == jitless and not embedded_builtins }], # variant == jitless and not embedded_builtins
##############################################################################
['variant == future', {
# crbug.com/v8/9094
'compiler/constant-fold-cow-array': [SKIP],
}], # variant == future
] ]
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment