Commit 36e3af32 authored by bmeurer's avatar bmeurer Committed by Commit bot

[turbofan] Don't check for neutered array buffers eagerly.

We don't need to check for neutered array buffers unless at least one
JSArrayBuffer has been neutered (i.e. detached in TC39 speak). For this
we introduce a protector cell that get's invalidated on first call to
the JSArrayBuffer::Neuter() method.

R=jarin@chromium.org,ulan@chromium.org
BUG=v8:5267

Review-Url: https://codereview.chromium.org/2504163002
Cr-Commit-Position: refs/heads/master@{#41021}
parent 5716db5d
......@@ -235,17 +235,27 @@ Reduction JSBuiltinReducer::ReduceArrayIterator(Handle<Map> receiver_map,
Node* control = NodeProperties::GetControlInput(node);
if (iter_kind == ArrayIteratorKind::kTypedArray) {
// For JSTypedArray iterator methods, deopt if the buffer is neutered. This
// is potentially a deopt loop, but should be extremely unlikely.
DCHECK_EQ(JS_TYPED_ARRAY_TYPE, receiver_map->instance_type());
Node* buffer = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForJSArrayBufferViewBuffer()),
receiver, effect, control);
Node* check = effect = graph()->NewNode(
simplified()->ArrayBufferWasNeutered(), buffer, effect, control);
check = graph()->NewNode(simplified()->BooleanNot(), check);
effect = graph()->NewNode(simplified()->CheckIf(), check, effect, control);
// See if we can skip the neutering check.
if (isolate()->IsArrayBufferNeuteringIntact()) {
// Add a code dependency so we are deoptimized in case an ArrayBuffer
// gets neutered.
dependencies()->AssumePropertyCell(
factory()->array_buffer_neutering_protector());
} else {
// For JSTypedArray iterator methods, deopt if the buffer is neutered.
// This is potentially a deopt loop, but should be extremely unlikely.
DCHECK_EQ(JS_TYPED_ARRAY_TYPE, receiver_map->instance_type());
Node* buffer = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForJSArrayBufferViewBuffer()),
receiver, effect, control);
// Deoptimize if the {buffer} has been neutered.
Node* check = effect = graph()->NewNode(
simplified()->ArrayBufferWasNeutered(), buffer, effect, control);
check = graph()->NewNode(simplified()->BooleanNot(), check);
effect =
graph()->NewNode(simplified()->CheckIf(), check, effect, control);
}
}
int map_index = -1;
......@@ -535,11 +545,20 @@ Reduction JSBuiltinReducer::ReduceTypedArrayIteratorNext(
simplified()->LoadField(AccessBuilder::ForJSArrayBufferViewBuffer()),
array, efalse0, if_false0);
Node* check1 = efalse0 = graph()->NewNode(
simplified()->ArrayBufferWasNeutered(), buffer, efalse0, if_false0);
check1 = graph()->NewNode(simplified()->BooleanNot(), check1);
efalse0 =
graph()->NewNode(simplified()->CheckIf(), check1, efalse0, if_false0);
// See if we can skip the neutering check.
if (isolate()->IsArrayBufferNeuteringIntact()) {
// Add a code dependency so we are deoptimized in case an ArrayBuffer
// gets neutered.
dependencies()->AssumePropertyCell(
factory()->array_buffer_neutering_protector());
} else {
// Deoptimize if the array byuffer was neutered.
Node* check1 = efalse0 = graph()->NewNode(
simplified()->ArrayBufferWasNeutered(), buffer, efalse0, if_false0);
check1 = graph()->NewNode(simplified()->BooleanNot(), check1);
efalse0 =
graph()->NewNode(simplified()->CheckIf(), check1, efalse0, if_false0);
}
Node* length = efalse0 = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForJSTypedArrayLength()), array,
......@@ -1775,21 +1794,29 @@ Reduction JSBuiltinReducer::ReduceArrayBufferViewAccessor(
Node* control = NodeProperties::GetControlInput(node);
if (HasInstanceTypeWitness(receiver, effect, instance_type)) {
// Load the {receiver}s field.
Node* receiver_value = effect = graph()->NewNode(
simplified()->LoadField(access), receiver, effect, control);
// Check if the {receiver}s buffer was neutered.
Node* receiver_buffer = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForJSArrayBufferViewBuffer()),
receiver, effect, control);
Node* check = effect =
graph()->NewNode(simplified()->ArrayBufferWasNeutered(),
receiver_buffer, effect, control);
// Default to zero if the {receiver}s buffer was neutered.
Node* value = graph()->NewNode(
common()->Select(MachineRepresentation::kTagged, BranchHint::kFalse),
check, jsgraph()->ZeroConstant(), receiver_value);
Node* value = effect = graph()->NewNode(simplified()->LoadField(access),
receiver, effect, control);
// See if we can skip the neutering check.
if (isolate()->IsArrayBufferNeuteringIntact()) {
// Add a code dependency so we are deoptimized in case an ArrayBuffer
// gets neutered.
dependencies()->AssumePropertyCell(
factory()->array_buffer_neutering_protector());
} else {
// Check if the {receiver}s buffer was neutered.
Node* receiver_buffer = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForJSArrayBufferViewBuffer()),
receiver, effect, control);
Node* check = effect =
graph()->NewNode(simplified()->ArrayBufferWasNeutered(),
receiver_buffer, effect, control);
// Default to zero if the {receiver}s buffer was neutered.
value = graph()->NewNode(
common()->Select(MachineRepresentation::kTagged, BranchHint::kFalse),
check, jsgraph()->ZeroConstant(), value);
}
ReplaceWithValue(node, value, effect, control);
return Replace(value);
......
......@@ -1213,12 +1213,20 @@ JSNativeContextSpecialization::BuildElementAccess(
elements, effect, control);
}
// Default to zero if the {receiver}s buffer was neutered.
Node* check = effect = graph()->NewNode(
simplified()->ArrayBufferWasNeutered(), buffer, effect, control);
length = graph()->NewNode(
common()->Select(MachineRepresentation::kTagged, BranchHint::kFalse),
check, jsgraph()->ZeroConstant(), length);
// See if we can skip the neutering check.
if (isolate()->IsArrayBufferNeuteringIntact()) {
// Add a code dependency so we are deoptimized in case an ArrayBuffer
// gets neutered.
dependencies()->AssumePropertyCell(
factory()->array_buffer_neutering_protector());
} else {
// Default to zero if the {receiver}s buffer was neutered.
Node* check = effect = graph()->NewNode(
simplified()->ArrayBufferWasNeutered(), buffer, effect, control);
length = graph()->NewNode(
common()->Select(MachineRepresentation::kTagged, BranchHint::kFalse),
check, jsgraph()->ZeroConstant(), length);
}
if (store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS) {
// Check that the {index} is a valid array index, we do the actual
......
......@@ -2854,6 +2854,10 @@ void Heap::CreateInitialObjects() {
handle(Smi::FromInt(Isolate::kProtectorValid), isolate()));
set_array_iterator_protector(*array_iterator_cell);
cell = factory->NewPropertyCell();
cell->set_value(Smi::FromInt(Isolate::kProtectorValid));
set_array_buffer_neutering_protector(*cell);
set_serialized_templates(empty_fixed_array());
set_weak_stack_trace_list(Smi::kZero);
......
......@@ -168,6 +168,8 @@ using v8::MemoryPressureLevel;
V(PropertyCell, string_length_protector, StringLengthProtector) \
V(Cell, fast_array_iteration_protector, FastArrayIterationProtector) \
V(Cell, array_iterator_protector, ArrayIteratorProtector) \
V(PropertyCell, array_buffer_neutering_protector, \
ArrayBufferNeuteringProtector) \
/* Special numbers */ \
V(HeapNumber, nan_value, NanValue) \
V(HeapNumber, hole_nan_value, HoleNanValue) \
......
......@@ -148,6 +148,11 @@ bool Isolate::IsFastArrayIterationIntact() {
return fast_iteration->value() == Smi::FromInt(kProtectorValid);
}
bool Isolate::IsArrayBufferNeuteringIntact() {
PropertyCell* fast_iteration = heap()->array_buffer_neutering_protector();
return fast_iteration->value() == Smi::FromInt(kProtectorValid);
}
bool Isolate::IsArrayIteratorLookupChainIntact() {
Cell* array_iterator_cell = heap()->array_iterator_protector();
return array_iterator_cell->value() == Smi::FromInt(kProtectorValid);
......
......@@ -2960,6 +2960,15 @@ void Isolate::InvalidateArrayIteratorProtector() {
DCHECK(!IsArrayIteratorLookupChainIntact());
}
void Isolate::InvalidateArrayBufferNeuteringProtector() {
DCHECK(factory()->array_buffer_neutering_protector()->value()->IsSmi());
DCHECK(IsArrayBufferNeuteringIntact());
PropertyCell::SetValueWithInvalidation(
factory()->array_buffer_neutering_protector(),
handle(Smi::FromInt(kProtectorInvalid), this));
DCHECK(!IsArrayBufferNeuteringIntact());
}
bool Isolate::IsAnyInitialArrayPrototype(Handle<JSArray> array) {
DisallowHeapAllocation no_gc;
return IsInAnyContext(*array, Context::INITIAL_ARRAY_PROTOTYPE_INDEX);
......
......@@ -1001,6 +1001,9 @@ class Isolate {
// Avoid deopt loops if fast Array Iterators migrate to slow Array Iterators.
inline bool IsFastArrayIterationIntact();
// Make sure we do check for neutered array buffers.
inline bool IsArrayBufferNeuteringIntact();
// On intent to set an element in object, make sure that appropriate
// notifications occur if the set is on the elements of the array or
// object prototype. Also ensure that changes to prototype chain between
......@@ -1020,6 +1023,7 @@ class Isolate {
void InvalidateIsConcatSpreadableProtector();
void InvalidateStringLengthOverflowProtector();
void InvalidateArrayIteratorProtector();
void InvalidateArrayBufferNeuteringProtector();
// Returns true if array is the initial array prototype in any native context.
bool IsAnyInitialArrayPrototype(Handle<JSArray> array);
......
......@@ -19488,6 +19488,11 @@ void JSArrayBuffer::Neuter() {
set_backing_store(NULL);
set_byte_length(Smi::kZero);
set_was_neutered(true);
// Invalidate the neutering protector.
Isolate* const isolate = GetIsolate();
if (isolate->IsArrayBufferNeuteringIntact()) {
isolate->InvalidateArrayBufferNeuteringProtector();
}
}
......
This diff is collapsed.
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