Commit 46573e51 authored by Benedikt Meurer's avatar Benedikt Meurer Committed by Commit Bot

[es2015] Introduce JSDataView::external_pointer.

This adds a new external_pointer field to every JSDataView instance
which points directly into the backing store at the given view's
byte_offset. This was the DataView performance is now almost on
par with the TypedArray performance for accessing aligned memory
(with appropriate endianess). This also serves as prepatory work
to enable full 64-bit addressing of DataView backing stores in
optimized code (soonish).

This change optimizes the bounds checking sequence in TurboFan in
such a way that it further improves the DataView set/get performance
by around 10%, almost closing the remaining gap between DataViews
and TypedArrays.

Drive-by-fix: Get rid of the code duplication around DataView inlining
in the JSCallReducer and have only a single bottleneck method now.

Bug: chromium:225811, v8:4153, v8:7881, v8:8171
Change-Id: I9118efd4d19e93f0e51c931a9bec1a56a0f4593e
Reviewed-on: https://chromium-review.googlesource.com/1231994
Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
Reviewed-by: 's avatarBenedikt Meurer <bmeurer@chromium.org>
Reviewed-by: 's avatarTobias Tebbi <tebbi@chromium.org>
Reviewed-by: 's avatarYang Guo <yangguo@chromium.org>
Reviewed-by: 's avatarMichael Lippautz <mlippautz@chromium.org>
Cr-Commit-Position: refs/heads/master@{#56042}
parent 8f30ab32
......@@ -101,6 +101,8 @@ BUILTIN(DataViewConstructor) {
// 13. Set O's [[ByteOffset]] internal slot to offset.
Handle<JSDataView>::cast(result)->set_byte_offset(view_byte_offset);
Handle<JSDataView>::cast(result)->set_external_pointer(
static_cast<uint8_t*>(array_buffer->backing_store()) + view_byte_offset);
// 14. Return O.
return *result;
......
......@@ -10,8 +10,8 @@ module data_view {
macro LoadJSArrayBufferViewByteLength(JSArrayBufferView): uintptr;
extern operator '.byte_offset'
macro LoadJSArrayBufferViewByteOffset(JSArrayBufferView): uintptr;
extern operator '.backing_store'
macro LoadJSArrayBufferBackingStore(JSArrayBuffer): RawPtr;
extern operator '.external_pointer'
macro LoadJSDataViewExternalPointer(JSDataView): RawPtr;
macro MakeDataViewGetterNameString(kind: constexpr ElementsKind): String {
if constexpr (kind == UINT8_ELEMENTS) {
......@@ -123,20 +123,19 @@ module data_view {
extern macro LoadUint8(RawPtr, uintptr): uint32;
extern macro LoadInt8(RawPtr, uintptr): int32;
macro LoadDataView8(buffer: JSArrayBuffer, offset: uintptr,
macro LoadDataView8(dataView: JSDataView, offset: uintptr,
signed: constexpr bool): Smi {
if constexpr (signed) {
return Convert<Smi>(LoadInt8(buffer.backing_store, offset));
return Convert<Smi>(LoadInt8(dataView.external_pointer, offset));
} else {
return Convert<Smi>(LoadUint8(buffer.backing_store, offset));
return Convert<Smi>(LoadUint8(dataView.external_pointer, offset));
}
}
macro LoadDataView16(buffer: JSArrayBuffer, offset: uintptr,
macro LoadDataView16(dataView: JSDataView, offset: uintptr,
requestedLittleEndian: bool,
signed: constexpr bool): Number {
let dataPointer: RawPtr = buffer.backing_store;
let dataPointer: RawPtr = dataView.external_pointer;
let b0: int32;
let b1: int32;
let result: int32;
......@@ -159,11 +158,10 @@ module data_view {
}
}
macro LoadDataView32(buffer: JSArrayBuffer, offset: uintptr,
macro LoadDataView32(dataView: JSDataView, offset: uintptr,
requestedLittleEndian: bool,
kind: constexpr ElementsKind): Number {
let dataPointer: RawPtr = buffer.backing_store;
let dataPointer: RawPtr = dataView.external_pointer;
let b0: uint32 = LoadUint8(dataPointer, offset);
let b1: uint32 = LoadUint8(dataPointer, offset + 1);
let b2: uint32 = LoadUint8(dataPointer, offset + 2);
......@@ -188,10 +186,9 @@ module data_view {
}
}
macro LoadDataViewFloat64(buffer: JSArrayBuffer, offset: uintptr,
macro LoadDataViewFloat64(dataView: JSDataView, offset: uintptr,
requestedLittleEndian: bool): Number {
let dataPointer: RawPtr = buffer.backing_store;
let dataPointer: RawPtr = dataView.external_pointer;
let b0: uint32 = LoadUint8(dataPointer, offset);
let b1: uint32 = LoadUint8(dataPointer, offset + 1);
let b2: uint32 = LoadUint8(dataPointer, offset + 2);
......@@ -359,11 +356,10 @@ module data_view {
}
}
macro LoadDataViewBigInt(buffer: JSArrayBuffer, offset: uintptr,
macro LoadDataViewBigInt(dataView: JSDataView, offset: uintptr,
requestedLittleEndian: bool,
signed: constexpr bool): BigInt {
let dataPointer: RawPtr = buffer.backing_store;
let dataPointer: RawPtr = dataView.external_pointer;
let b0: uint32 = LoadUint8(dataPointer, offset);
let b1: uint32 = LoadUint8(dataPointer, offset + 1);
let b2: uint32 = LoadUint8(dataPointer, offset + 2);
......@@ -417,7 +413,6 @@ module data_view {
let getIndexFloat: float64 = Convert<float64>(getIndex);
let getIndexWord: uintptr = Convert<uintptr>(getIndexFloat);
let viewOffsetWord: uintptr = dataView.byte_offset;
let viewSizeFloat: float64 = Convert<float64>(dataView.byte_length);
let elementSizeFloat: float64 = Convert<float64>(DataViewElementSize(kind));
......@@ -425,28 +420,26 @@ module data_view {
ThrowRangeError(context, kInvalidDataViewAccessorOffset);
}
let bufferIndex: uintptr = getIndexWord + viewOffsetWord;
if constexpr (kind == UINT8_ELEMENTS) {
return LoadDataView8(buffer, bufferIndex, false);
return LoadDataView8(dataView, getIndexWord, false);
} else if constexpr (kind == INT8_ELEMENTS) {
return LoadDataView8(buffer, bufferIndex, true);
return LoadDataView8(dataView, getIndexWord, true);
} else if constexpr (kind == UINT16_ELEMENTS) {
return LoadDataView16(buffer, bufferIndex, littleEndian, false);
return LoadDataView16(dataView, getIndexWord, littleEndian, false);
} else if constexpr (kind == INT16_ELEMENTS) {
return LoadDataView16(buffer, bufferIndex, littleEndian, true);
return LoadDataView16(dataView, getIndexWord, littleEndian, true);
} else if constexpr (kind == UINT32_ELEMENTS) {
return LoadDataView32(buffer, bufferIndex, littleEndian, kind);
return LoadDataView32(dataView, getIndexWord, littleEndian, kind);
} else if constexpr (kind == INT32_ELEMENTS) {
return LoadDataView32(buffer, bufferIndex, littleEndian, kind);
return LoadDataView32(dataView, getIndexWord, littleEndian, kind);
} else if constexpr (kind == FLOAT32_ELEMENTS) {
return LoadDataView32(buffer, bufferIndex, littleEndian, kind);
return LoadDataView32(dataView, getIndexWord, littleEndian, kind);
} else if constexpr (kind == FLOAT64_ELEMENTS) {
return LoadDataViewFloat64(buffer, bufferIndex, littleEndian);
return LoadDataViewFloat64(dataView, getIndexWord, littleEndian);
} else if constexpr (kind == BIGUINT64_ELEMENTS) {
return LoadDataViewBigInt(buffer, bufferIndex, littleEndian, false);
return LoadDataViewBigInt(dataView, getIndexWord, littleEndian, false);
} else if constexpr (kind == BIGINT64_ELEMENTS) {
return LoadDataViewBigInt(buffer, bufferIndex, littleEndian, true);
return LoadDataViewBigInt(dataView, getIndexWord, littleEndian, true);
} else {
unreachable;
}
......@@ -571,15 +564,14 @@ module data_view {
extern macro StoreWord8(RawPtr, uintptr, uint32): void;
macro StoreDataView8(buffer: JSArrayBuffer, offset: uintptr,
macro StoreDataView8(dataView: JSDataView, offset: uintptr,
value: uint32) {
StoreWord8(buffer.backing_store, offset, value & 0xFF);
StoreWord8(dataView.external_pointer, offset, value & 0xFF);
}
macro StoreDataView16(buffer: JSArrayBuffer, offset: uintptr, value: uint32,
macro StoreDataView16(dataView: JSDataView, offset: uintptr, value: uint32,
requestedLittleEndian: bool) {
let dataPointer: RawPtr = buffer.backing_store;
let dataPointer: RawPtr = dataView.external_pointer;
let b0: uint32 = value & 0xFF;
let b1: uint32 = (value >>> 8) & 0xFF;
......@@ -592,10 +584,9 @@ module data_view {
}
}
macro StoreDataView32(buffer: JSArrayBuffer, offset: uintptr, value: uint32,
macro StoreDataView32(dataView: JSDataView, offset: uintptr, value: uint32,
requestedLittleEndian: bool) {
let dataPointer: RawPtr = buffer.backing_store;
let dataPointer: RawPtr = dataView.external_pointer;
let b0: uint32 = value & 0xFF;
let b1: uint32 = (value >>> 8) & 0xFF;
let b2: uint32 = (value >>> 16) & 0xFF;
......@@ -614,11 +605,10 @@ module data_view {
}
}
macro StoreDataView64(buffer: JSArrayBuffer, offset: uintptr,
macro StoreDataView64(dataView: JSDataView, offset: uintptr,
lowWord: uint32, highWord: uint32,
requestedLittleEndian: bool) {
let dataPointer: RawPtr = buffer.backing_store;
let dataPointer: RawPtr = dataView.external_pointer;
let b0: uint32 = lowWord & 0xFF;
let b1: uint32 = (lowWord >>> 8) & 0xFF;
let b2: uint32 = (lowWord >>> 16) & 0xFF;
......@@ -658,7 +648,7 @@ module data_view {
// We might get here a BigInt that is bigger than 64 bits, but we're only
// interested in the 64 lowest ones. This means the lowest BigInt digit
// on 64-bit platforms, and the 2 lowest BigInt digits on 32-bit ones.
macro StoreDataViewBigInt(buffer: JSArrayBuffer, offset: uintptr,
macro StoreDataViewBigInt(dataView: JSDataView, offset: uintptr,
bigIntValue: BigInt,
requestedLittleEndian: bool) {
......@@ -694,7 +684,7 @@ module data_view {
lowWord = Unsigned(0 - Signed(lowWord));
}
StoreDataView64(buffer, offset, lowWord, highWord,
StoreDataView64(dataView, offset, lowWord, highWord,
requestedLittleEndian);
}
......@@ -737,7 +727,6 @@ module data_view {
let getIndexFloat: float64 = Convert<float64>(getIndex);
let getIndexWord: uintptr = Convert<uintptr>(getIndexFloat);
let viewOffsetWord: uintptr = dataView.byte_offset;
let viewSizeFloat: float64 = Convert<float64>(dataView.byte_length);
let elementSizeFloat: float64 = Convert<float64>(DataViewElementSize(kind));
......@@ -745,36 +734,34 @@ module data_view {
ThrowRangeError(context, kInvalidDataViewAccessorOffset);
}
let bufferIndex: uintptr = getIndexWord + viewOffsetWord;
if constexpr (kind == BIGUINT64_ELEMENTS || kind == BIGINT64_ELEMENTS) {
StoreDataViewBigInt(buffer, bufferIndex, bigIntValue,
StoreDataViewBigInt(dataView, getIndexWord, bigIntValue,
littleEndian);
}
else {
let doubleValue: float64 = ChangeNumberToFloat64(numValue);
if constexpr (kind == UINT8_ELEMENTS || kind == INT8_ELEMENTS) {
StoreDataView8(buffer, bufferIndex,
StoreDataView8(dataView, getIndexWord,
TruncateFloat64ToWord32(doubleValue));
}
else if constexpr (kind == UINT16_ELEMENTS || kind == INT16_ELEMENTS) {
StoreDataView16(buffer, bufferIndex,
StoreDataView16(dataView, getIndexWord,
TruncateFloat64ToWord32(doubleValue), littleEndian);
}
else if constexpr (kind == UINT32_ELEMENTS || kind == INT32_ELEMENTS) {
StoreDataView32(buffer, bufferIndex,
StoreDataView32(dataView, getIndexWord,
TruncateFloat64ToWord32(doubleValue), littleEndian);
}
else if constexpr (kind == FLOAT32_ELEMENTS) {
let floatValue: float32 = TruncateFloat64ToFloat32(doubleValue);
StoreDataView32(buffer, bufferIndex,
StoreDataView32(dataView, getIndexWord,
BitcastFloat32ToInt32(floatValue), littleEndian);
}
else if constexpr (kind == FLOAT64_ELEMENTS) {
let lowWord: uint32 = Float64ExtractLowWord32(doubleValue);
let highWord: uint32 = Float64ExtractHighWord32(doubleValue);
StoreDataView64(buffer, bufferIndex, lowWord, highWord,
StoreDataView64(dataView, getIndexWord, lowWord, highWord,
littleEndian);
}
}
......
......@@ -12263,6 +12263,12 @@ TNode<UintPtrT> CodeStubAssembler::LoadJSArrayBufferViewByteOffset(
JSArrayBufferView::kByteOffsetOffset);
}
TNode<RawPtrT> CodeStubAssembler::LoadJSDataViewExternalPointer(
TNode<JSDataView> data_view) {
return LoadObjectField<RawPtrT>(data_view,
JSDataView::kExternalPointerOffset);
}
TNode<Smi> CodeStubAssembler::LoadJSTypedArrayLength(
TNode<JSTypedArray> typed_array) {
return LoadObjectField<Smi>(typed_array, JSTypedArray::kLengthOffset);
......
......@@ -2789,6 +2789,9 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
SloppyTNode<Context> context, TNode<JSArrayBufferView> array_buffer_view,
const char* method_name);
// JSDataView helpers
TNode<RawPtrT> LoadJSDataViewExternalPointer(TNode<JSDataView> data_view);
// JSTypedArray helpers
TNode<Smi> LoadJSTypedArrayLength(TNode<JSTypedArray> typed_array);
......
......@@ -375,6 +375,18 @@ FieldAccess AccessBuilder::ForJSArrayBufferViewByteOffset() {
return access;
}
// static
FieldAccess AccessBuilder::ForJSDataViewExternalPointer() {
FieldAccess access = {kTaggedBase,
JSDataView::kExternalPointerOffset,
MaybeHandle<Name>(),
MaybeHandle<Map>(),
Type::ExternalPointer(),
MachineType::Pointer(),
kNoWriteBarrier};
return access;
}
// static
FieldAccess AccessBuilder::ForJSTypedArrayLength() {
FieldAccess access = {kTaggedBase,
......
......@@ -130,6 +130,9 @@ class V8_EXPORT_PRIVATE AccessBuilder final
// Provides access to JSArrayBufferView::byteOffset() field.
static FieldAccess ForJSArrayBufferViewByteOffset();
// Provides access to JSDataView::external_pointer() field.
static FieldAccess ForJSDataViewExternalPointer();
// Provides access to JSTypedArray::length() field.
static FieldAccess ForJSTypedArrayLength();
......
......@@ -3470,53 +3470,53 @@ Reduction JSCallReducer::ReduceJSCall(Node* node,
node, JS_DATA_VIEW_TYPE,
AccessBuilder::ForJSArrayBufferViewByteOffset());
case Builtins::kDataViewPrototypeGetUint8:
return ReduceDataViewPrototypeGet(node,
return ReduceDataViewAccess(node, DataViewAccess::kGet,
ExternalArrayType::kExternalUint8Array);
case Builtins::kDataViewPrototypeGetInt8:
return ReduceDataViewPrototypeGet(node,
return ReduceDataViewAccess(node, DataViewAccess::kGet,
ExternalArrayType::kExternalInt8Array);
case Builtins::kDataViewPrototypeGetUint16:
return ReduceDataViewPrototypeGet(
node, ExternalArrayType::kExternalUint16Array);
return ReduceDataViewAccess(node, DataViewAccess::kGet,
ExternalArrayType::kExternalUint16Array);
case Builtins::kDataViewPrototypeGetInt16:
return ReduceDataViewPrototypeGet(node,
return ReduceDataViewAccess(node, DataViewAccess::kGet,
ExternalArrayType::kExternalInt16Array);
case Builtins::kDataViewPrototypeGetUint32:
return ReduceDataViewPrototypeGet(
node, ExternalArrayType::kExternalUint32Array);
return ReduceDataViewAccess(node, DataViewAccess::kGet,
ExternalArrayType::kExternalUint32Array);
case Builtins::kDataViewPrototypeGetInt32:
return ReduceDataViewPrototypeGet(node,
return ReduceDataViewAccess(node, DataViewAccess::kGet,
ExternalArrayType::kExternalInt32Array);
case Builtins::kDataViewPrototypeGetFloat32:
return ReduceDataViewPrototypeGet(
node, ExternalArrayType::kExternalFloat32Array);
return ReduceDataViewAccess(node, DataViewAccess::kGet,
ExternalArrayType::kExternalFloat32Array);
case Builtins::kDataViewPrototypeGetFloat64:
return ReduceDataViewPrototypeGet(
node, ExternalArrayType::kExternalFloat64Array);
return ReduceDataViewAccess(node, DataViewAccess::kGet,
ExternalArrayType::kExternalFloat64Array);
case Builtins::kDataViewPrototypeSetUint8:
return ReduceDataViewPrototypeSet(node,
return ReduceDataViewAccess(node, DataViewAccess::kSet,
ExternalArrayType::kExternalUint8Array);
case Builtins::kDataViewPrototypeSetInt8:
return ReduceDataViewPrototypeSet(node,
return ReduceDataViewAccess(node, DataViewAccess::kSet,
ExternalArrayType::kExternalInt8Array);
case Builtins::kDataViewPrototypeSetUint16:
return ReduceDataViewPrototypeSet(
node, ExternalArrayType::kExternalUint16Array);
return ReduceDataViewAccess(node, DataViewAccess::kSet,
ExternalArrayType::kExternalUint16Array);
case Builtins::kDataViewPrototypeSetInt16:
return ReduceDataViewPrototypeSet(node,
return ReduceDataViewAccess(node, DataViewAccess::kSet,
ExternalArrayType::kExternalInt16Array);
case Builtins::kDataViewPrototypeSetUint32:
return ReduceDataViewPrototypeSet(
node, ExternalArrayType::kExternalUint32Array);
return ReduceDataViewAccess(node, DataViewAccess::kSet,
ExternalArrayType::kExternalUint32Array);
case Builtins::kDataViewPrototypeSetInt32:
return ReduceDataViewPrototypeSet(node,
return ReduceDataViewAccess(node, DataViewAccess::kSet,
ExternalArrayType::kExternalInt32Array);
case Builtins::kDataViewPrototypeSetFloat32:
return ReduceDataViewPrototypeSet(
node, ExternalArrayType::kExternalFloat32Array);
return ReduceDataViewAccess(node, DataViewAccess::kSet,
ExternalArrayType::kExternalFloat32Array);
case Builtins::kDataViewPrototypeSetFloat64:
return ReduceDataViewPrototypeSet(
node, ExternalArrayType::kExternalFloat64Array);
return ReduceDataViewAccess(node, DataViewAccess::kSet,
ExternalArrayType::kExternalFloat64Array);
case Builtins::kTypedArrayPrototypeByteLength:
return ReduceArrayBufferViewAccessor(
node, JS_TYPED_ARRAY_TYPE,
......@@ -6770,6 +6770,7 @@ Reduction JSCallReducer::ReduceArrayBufferViewAccessor(
}
namespace {
uint32_t ExternalArrayElementSize(const ExternalArrayType element_type) {
switch (element_type) {
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) \
......@@ -6782,169 +6783,41 @@ uint32_t ExternalArrayElementSize(const ExternalArrayType element_type) {
#undef TYPED_ARRAY_CASE
}
}
} // namespace
Reduction JSCallReducer::ReduceDataViewPrototypeGet(
Node* node, ExternalArrayType element_type) {
Reduction JSCallReducer::ReduceDataViewAccess(Node* node, DataViewAccess access,
ExternalArrayType element_type) {
size_t const element_size = ExternalArrayElementSize(element_type);
CallParameters const& p = CallParametersOf(node->op());
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
Node* receiver = NodeProperties::GetValueInput(node, 1);
if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
return NoChange();
}
Node* offset = node->op()->ValueInputCount() > 2
? NodeProperties::GetValueInput(node, 2)
: jsgraph()->ZeroConstant();
Node* is_little_endian = node->op()->ValueInputCount() > 3
Node* value = (access == DataViewAccess::kGet)
? nullptr
: (node->op()->ValueInputCount() > 3
? NodeProperties::GetValueInput(node, 3)
: jsgraph()->FalseConstant();
// Only do stuff if the {receiver} is really a DataView.
if (NodeProperties::HasInstanceTypeWitness(isolate(), receiver, effect,
JS_DATA_VIEW_TYPE)) {
// Check that the {offset} is within range for the {receiver}.
HeapObjectMatcher m(receiver);
if (m.HasValue()) {
// We only deal with DataViews here whose [[ByteLength]] is at least
// {element_size} and less than 2^31-{element_size}.
Handle<JSDataView> dataview = Handle<JSDataView>::cast(m.Value());
if (dataview->byte_length() < element_size ||
dataview->byte_length() - element_size > kMaxInt) {
return NoChange();
}
// The {receiver}s [[ByteOffset]] must be within Unsigned31 range.
if (dataview->byte_offset() > kMaxInt) {
return NoChange();
}
// Check that the {offset} is within range of the {byte_length}.
Node* byte_length =
jsgraph()->Constant(dataview->byte_length() - (element_size - 1));
offset = effect =
graph()->NewNode(simplified()->CheckBounds(p.feedback()), offset,
byte_length, effect, control);
// Add the [[ByteOffset]] to compute the effective offset.
Node* byte_offset = jsgraph()->Constant(dataview->byte_offset());
offset = graph()->NewNode(simplified()->NumberAdd(), offset, byte_offset);
} else {
// We only deal with DataViews here that have Smi [[ByteLength]]s.
Node* byte_length = effect =
graph()->NewNode(simplified()->LoadField(
AccessBuilder::ForJSArrayBufferViewByteLength()),
receiver, effect, control);
byte_length = effect = graph()->NewNode(
simplified()->CheckSmi(p.feedback()), byte_length, effect, control);
// Check that the {offset} is within range of the {byte_length}.
offset = effect =
graph()->NewNode(simplified()->CheckBounds(p.feedback()), offset,
byte_length, effect, control);
if (element_size > 0) {
// For non-byte accesses we also need to check that the {offset}
// plus the {element_size}-1 fits within the given {byte_length}.
Node* end_offset =
graph()->NewNode(simplified()->NumberAdd(), offset,
jsgraph()->Constant(element_size - 1));
effect = graph()->NewNode(simplified()->CheckBounds(p.feedback()),
end_offset, byte_length, effect, control);
}
// The {receiver}s [[ByteOffset]] also needs to be a (positive) Smi.
Node* byte_offset = effect =
graph()->NewNode(simplified()->LoadField(
AccessBuilder::ForJSArrayBufferViewByteOffset()),
receiver, effect, control);
byte_offset = effect = graph()->NewNode(
simplified()->CheckSmi(p.feedback()), byte_offset, effect, control);
// Compute the buffer index at which we'll read.
offset = graph()->NewNode(simplified()->NumberAdd(), offset, byte_offset);
}
// Coerce {is_little_endian} to boolean.
is_little_endian =
graph()->NewNode(simplified()->ToBoolean(), is_little_endian);
// Get the underlying buffer and check that it has not been neutered.
Node* buffer = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForJSArrayBufferViewBuffer()),
receiver, effect, control);
if (isolate()->IsArrayBufferNeuteringIntact()) {
// Add a code dependency so we are deoptimized in case an ArrayBuffer
// gets neutered.
dependencies()->DependOnProtector(PropertyCellRef(
js_heap_broker(), factory()->array_buffer_neutering_protector()));
} else {
// Bail out if the {buffer} was neutered.
Node* buffer_bit_field = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForJSArrayBufferBitField()),
buffer, effect, control);
Node* check = graph()->NewNode(
simplified()->NumberEqual(),
graph()->NewNode(
simplified()->NumberBitwiseAnd(), buffer_bit_field,
jsgraph()->Constant(JSArrayBuffer::WasNeuteredBit::kMask)),
jsgraph()->ZeroConstant());
effect = graph()->NewNode(
simplified()->CheckIf(DeoptimizeReason::kArrayBufferWasNeutered,
p.feedback()),
check, effect, control);
}
// Get the buffer's backing store.
Node* backing_store = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForJSArrayBufferBackingStore()),
buffer, effect, control);
// Perform the load.
Node* value = effect = graph()->NewNode(
simplified()->LoadDataViewElement(element_type), buffer, backing_store,
offset, is_little_endian, effect, control);
// Continue on the regular path.
ReplaceWithValue(node, value, effect, control);
return Changed(value);
}
return NoChange();
}
Reduction JSCallReducer::ReduceDataViewPrototypeSet(
Node* node, ExternalArrayType element_type) {
size_t const element_size = ExternalArrayElementSize(element_type);
CallParameters const& p = CallParametersOf(node->op());
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
Node* receiver = NodeProperties::GetValueInput(node, 1);
: jsgraph()->ZeroConstant());
Node* is_little_endian = (access == DataViewAccess::kGet)
? (node->op()->ValueInputCount() > 3
? NodeProperties::GetValueInput(node, 3)
: jsgraph()->FalseConstant())
: (node->op()->ValueInputCount() > 4
? NodeProperties::GetValueInput(node, 4)
: jsgraph()->FalseConstant());
if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
return NoChange();
}
Node* offset = node->op()->ValueInputCount() > 2
? NodeProperties::GetValueInput(node, 2)
: jsgraph()->ZeroConstant();
Node* value = node->op()->ValueInputCount() > 3
? NodeProperties::GetValueInput(node, 3)
: jsgraph()->ZeroConstant();
Node* is_little_endian = node->op()->ValueInputCount() > 4
? NodeProperties::GetValueInput(node, 4)
: jsgraph()->FalseConstant();
// Only do stuff if the {receiver} is really a DataView.
if (NodeProperties::HasInstanceTypeWitness(isolate(), receiver, effect,
JS_DATA_VIEW_TYPE)) {
Node* external_pointer;
// Check that the {offset} is within range for the {receiver}.
HeapObjectMatcher m(receiver);
if (m.HasValue()) {
......@@ -6956,11 +6829,6 @@ Reduction JSCallReducer::ReduceDataViewPrototypeSet(
return NoChange();
}
// The {receiver}s [[ByteOffset]] must be within Unsigned31 range.
if (dataview->byte_offset() > kMaxInt) {
return NoChange();
}
// Check that the {offset} is within range of the {byte_length}.
Node* byte_length =
jsgraph()->Constant(dataview->byte_length() - (element_size - 1));
......@@ -6968,9 +6836,9 @@ Reduction JSCallReducer::ReduceDataViewPrototypeSet(
graph()->NewNode(simplified()->CheckBounds(p.feedback()), offset,
byte_length, effect, control);
// Add the [[ByteOffset]] to compute the effective offset.
Node* byte_offset = jsgraph()->Constant(dataview->byte_offset());
offset = graph()->NewNode(simplified()->NumberAdd(), offset, byte_offset);
// Determine the external pointer from the {dataview}.
external_pointer =
jsgraph()->PointerConstant(dataview->external_pointer());
} else {
// We only deal with DataViews here that have Smi [[ByteLength]]s.
Node* byte_length = effect =
......@@ -6981,41 +6849,48 @@ Reduction JSCallReducer::ReduceDataViewPrototypeSet(
simplified()->CheckSmi(p.feedback()), byte_length, effect, control);
// Check that the {offset} is within range of the {byte_length}.
offset = effect =
graph()->NewNode(simplified()->CheckBounds(p.feedback()), offset,
byte_length, effect, control);
if (element_size > 0) {
// For non-byte accesses we also need to check that the {offset}
// plus the {element_size}-1 fits within the given {byte_length}.
// For non-byte accesses we first check that it's safe to
// add the {element_size} (minus 1) to the {offset}...
offset = effect = graph()->NewNode(
simplified()->CheckBounds(p.feedback()), offset,
jsgraph()->Constant(kMaxInt - (element_size - 1)), effect, control);
Node* end_offset =
graph()->NewNode(simplified()->NumberAdd(), offset,
jsgraph()->Constant(element_size - 1));
// ...and then check that this {end_offset} still fits into
// the {byte_length} of the given {receiver}. This approach
// leads to better code generation, especially when TurboFan
// knows something about the {offset} already (i.e. from type
// feedback baked into the graph later).
effect = graph()->NewNode(simplified()->CheckBounds(p.feedback()),
end_offset, byte_length, effect, control);
} else {
// For byte accesses a single bounds check is enough.
offset = effect =
graph()->NewNode(simplified()->CheckBounds(p.feedback()), offset,
byte_length, effect, control);
}
// The {receiver}s [[ByteOffset]] also needs to be a (positive) Smi.
Node* byte_offset = effect =
// Determine the external pointer for the {receiver}.
external_pointer = effect =
graph()->NewNode(simplified()->LoadField(
AccessBuilder::ForJSArrayBufferViewByteOffset()),
AccessBuilder::ForJSDataViewExternalPointer()),
receiver, effect, control);
byte_offset = effect = graph()->NewNode(
simplified()->CheckSmi(p.feedback()), byte_offset, effect, control);
// Compute the buffer index at which we'll read.
offset = graph()->NewNode(simplified()->NumberAdd(), offset, byte_offset);
}
// Coerce {is_little_endian} to boolean.
is_little_endian =
graph()->NewNode(simplified()->ToBoolean(), is_little_endian);
// Coerce {value} to Number.
// Coerce {value} to Number first.
if (access == DataViewAccess::kSet) {
value = effect = graph()->NewNode(
simplified()->SpeculativeToNumber(NumberOperationHint::kNumberOrOddball,
p.feedback()),
simplified()->SpeculativeToNumber(
NumberOperationHint::kNumberOrOddball, p.feedback()),
value, effect, control);
}
// Get the underlying buffer and check that it has not been neutered.
Node* buffer = effect = graph()->NewNode(
......@@ -7044,21 +6919,24 @@ Reduction JSCallReducer::ReduceDataViewPrototypeSet(
check, effect, control);
}
// Get the buffer's backing store.
Node* backing_store = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForJSArrayBufferBackingStore()),
buffer, effect, control);
// Perform the store.
effect = graph()->NewNode(simplified()->StoreDataViewElement(element_type),
buffer, backing_store, offset, value,
is_little_endian, effect, control);
Node* value = jsgraph()->UndefinedConstant();
// Perform the actual memory access.
switch (access) {
case DataViewAccess::kGet:
value = effect = graph()->NewNode(
simplified()->LoadDataViewElement(element_type), buffer,
external_pointer, offset, is_little_endian, effect, control);
break;
case DataViewAccess::kSet:
effect = graph()->NewNode(
simplified()->StoreDataViewElement(element_type), buffer,
external_pointer, offset, value, is_little_endian, effect, control);
value = jsgraph()->UndefinedConstant();
break;
}
// Continue on the regular path.
ReplaceWithValue(node, value, effect, control);
return Changed(value);
return Replace(value);
}
return NoChange();
......
......@@ -182,6 +182,9 @@ class V8_EXPORT_PRIVATE JSCallReducer final : public AdvancedReducer {
InstanceType instance_type,
FieldAccess const& access);
enum class DataViewAccess { kGet, kSet };
Reduction ReduceDataViewAccess(Node* node, DataViewAccess access,
ExternalArrayType element_type);
Reduction ReduceDataViewPrototypeGet(Node* node,
ExternalArrayType element_type);
Reduction ReduceDataViewPrototypeSet(Node* node,
......
......@@ -184,7 +184,11 @@ class ConcurrentMarkingVisitor final
return VisitJSObjectSubclass(map, object);
}
int VisitJSArrayBufferView(Map* map, JSArrayBufferView* object) {
int VisitJSDataView(Map* map, JSDataView* object) {
return VisitJSObjectSubclass(map, object);
}
int VisitJSTypedArray(Map* map, JSTypedArray* object) {
return VisitJSObjectSubclass(map, object);
}
......
......@@ -21,8 +21,9 @@ class BigInt;
class BytecodeArray;
class DataHandler;
class JSArrayBuffer;
class JSArrayBufferView;
class JSDataView;
class JSRegExp;
class JSTypedArray;
class JSWeakCollection;
class UncompiledDataWithoutPreParsedScope;
class UncompiledDataWithPreParsedScope;
......@@ -45,8 +46,9 @@ class UncompiledDataWithPreParsedScope;
V(FixedFloat64Array) \
V(FixedTypedArrayBase) \
V(JSArrayBuffer) \
V(JSArrayBufferView) \
V(JSDataView) \
V(JSObject) \
V(JSTypedArray) \
V(JSWeakCollection) \
V(Map) \
V(Oddball) \
......
......@@ -228,24 +228,51 @@ class JSArrayBuffer::BodyDescriptor final : public BodyDescriptorBase {
}
};
class JSArrayBufferView::BodyDescriptor final : public BodyDescriptorBase {
class JSDataView::BodyDescriptor final : public BodyDescriptorBase {
public:
STATIC_ASSERT(kBufferOffset + kPointerSize == kByteOffsetOffset);
STATIC_ASSERT(kByteOffsetOffset + kUIntptrSize == kByteLengthOffset);
STATIC_ASSERT(kByteLengthOffset + kUIntptrSize == kHeaderSize);
STATIC_ASSERT(kByteLengthOffset + kUIntptrSize == kExternalPointerOffset);
STATIC_ASSERT(kExternalPointerOffset + kPointerSize == kSize);
static bool IsValidSlot(Map* map, HeapObject* obj, int offset) {
if (offset < kByteOffsetOffset) return true;
if (offset < kHeaderSize) return false;
if (offset < kSize) return false;
return IsValidSlotImpl(map, obj, offset);
}
template <typename ObjectVisitor>
static inline void IterateBody(Map* map, HeapObject* obj, int object_size,
ObjectVisitor* v) {
// JSDataView instances contain raw data that the GC does not know about.
IteratePointers(obj, kPropertiesOrHashOffset, kByteOffsetOffset, v);
IterateBodyImpl(map, obj, kSize, object_size, v);
}
static inline int SizeOf(Map* map, HeapObject* object) {
return map->instance_size();
}
};
class JSTypedArray::BodyDescriptor final : public BodyDescriptorBase {
public:
STATIC_ASSERT(kBufferOffset + kPointerSize == kByteOffsetOffset);
STATIC_ASSERT(kByteOffsetOffset + kUIntptrSize == kByteLengthOffset);
STATIC_ASSERT(kByteLengthOffset + kUIntptrSize == kLengthOffset);
STATIC_ASSERT(kLengthOffset + kPointerSize == kSize);
static bool IsValidSlot(Map* map, HeapObject* obj, int offset) {
if (offset < kByteOffsetOffset) return true;
if (offset < kSize) return false;
return IsValidSlotImpl(map, obj, offset);
}
template <typename ObjectVisitor>
static inline void IterateBody(Map* map, HeapObject* obj, int object_size,
ObjectVisitor* v) {
// JSArrayBufferView contains raw data that the GC does not know about.
// JSTypedArray instances contain raw data that the GC does not know about.
IteratePointers(obj, kPropertiesOrHashOffset, kByteOffsetOffset, v);
IterateBodyImpl(map, obj, kHeaderSize, object_size, v);
IterateBodyImpl(map, obj, kSize, object_size, v);
}
static inline int SizeOf(Map* map, HeapObject* object) {
......
......@@ -3161,8 +3161,10 @@ VisitorId Map::GetVisitorId(Map* map) {
return kVisitJSArrayBuffer;
case JS_DATA_VIEW_TYPE:
return kVisitJSDataView;
case JS_TYPED_ARRAY_TYPE:
return kVisitJSArrayBufferView;
return kVisitJSTypedArray;
case SMALL_ORDERED_HASH_MAP_TYPE:
return kVisitSmallOrderedHashMap;
......
......@@ -33,7 +33,7 @@ void* JSArrayBuffer::backing_store() const {
return reinterpret_cast<void*>(ptr);
}
void JSArrayBuffer::set_backing_store(void* value, WriteBarrierMode mode) {
void JSArrayBuffer::set_backing_store(void* value) {
intptr_t ptr = reinterpret_cast<intptr_t>(value);
WRITE_INTPTR_FIELD(this, kBackingStoreOffset, ptr);
}
......@@ -184,6 +184,16 @@ MaybeHandle<JSTypedArray> JSTypedArray::Validate(Isolate* isolate,
ACCESSORS(JSTypedArray, raw_length, Object, kLengthOffset)
#endif
void* JSDataView::external_pointer() const {
intptr_t ptr = READ_INTPTR_FIELD(this, kExternalPointerOffset);
return reinterpret_cast<void*>(ptr);
}
void JSDataView::set_external_pointer(void* value) {
intptr_t ptr = reinterpret_cast<intptr_t>(value);
WRITE_INTPTR_FIELD(this, kExternalPointerOffset, ptr);
}
} // namespace internal
} // namespace v8
......
......@@ -32,7 +32,7 @@ class JSArrayBuffer : public JSObject {
DECL_PRIMITIVE_ACCESSORS(byte_length, size_t)
// [backing_store]: backing memory for this array
DECL_ACCESSORS(backing_store, void)
DECL_PRIMITIVE_ACCESSORS(backing_store, void*)
// For non-wasm, allocation_length and allocation_base are byte_length and
// backing_store, respectively.
......@@ -156,10 +156,6 @@ class JSArrayBufferView : public JSObject {
static const int kByteLengthOffset = kByteOffsetOffset + kUIntptrSize;
static const int kHeaderSize = kByteLengthOffset + kUIntptrSize;
// Iterates all fields in the object including internal ones except
// kByteOffset and kByteLengthOffset.
class BodyDescriptor;
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(JSArrayBufferView);
};
......@@ -198,6 +194,10 @@ class JSTypedArray : public JSArrayBufferView {
static const int kSizeWithEmbedderFields =
kSize + v8::ArrayBufferView::kEmbedderFieldCount * kPointerSize;
// Iterates all fields in the object including internal ones except
// kByteOffsetOffset, kByteLengthOffset and kLengthOffset.
class BodyDescriptor;
private:
static Handle<JSArrayBuffer> MaterializeArrayBuffer(
Handle<JSTypedArray> typed_array);
......@@ -210,16 +210,25 @@ class JSTypedArray : public JSArrayBufferView {
class JSDataView : public JSArrayBufferView {
public:
// [external_pointer]: Points into the actual backing store at the
// byte offset of this data view.
DECL_PRIMITIVE_ACCESSORS(external_pointer, void*)
DECL_CAST(JSDataView)
// Dispatched behavior.
DECL_PRINTER(JSDataView)
DECL_VERIFIER(JSDataView)
static const int kSize = JSArrayBufferView::kHeaderSize;
static const int kExternalPointerOffset = JSArrayBufferView::kHeaderSize;
static const int kSize = kExternalPointerOffset + kPointerSize;
static const int kSizeWithEmbedderFields =
kSize + v8::ArrayBufferView::kEmbedderFieldCount * kPointerSize;
// Iterates all fields in the object including internal ones except
// kByteOffsetOffset, kByteLengthOffset and kExternalPointerOffset.
class BodyDescriptor;
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(JSDataView);
};
......
......@@ -37,9 +37,10 @@ namespace internal {
V(FreeSpace) \
V(JSApiObject) \
V(JSArrayBuffer) \
V(JSArrayBufferView) \
V(JSDataView) \
V(JSObject) \
V(JSObjectFast) \
V(JSTypedArray) \
V(JSWeakCollection) \
V(Map) \
V(NativeContext) \
......
......@@ -241,6 +241,13 @@ HeapObject* Deserializer<AllocatorT>::PostProcessNewObject(HeapObject* obj,
buffer->set_backing_store(backing_store);
isolate_->heap()->RegisterNewArrayBuffer(buffer);
}
} else if (obj->IsJSDataView()) {
// Fixup the JSDataView::external_pointer field.
JSDataView* data_view = JSDataView::cast(obj);
JSArrayBuffer* buffer = JSArrayBuffer::cast(data_view->buffer());
data_view->set_external_pointer(
reinterpret_cast<uint8_t*>(buffer->backing_store()) +
data_view->byte_offset());
} else if (obj->IsFixedTypedArrayBase()) {
FixedTypedArrayBase* fta = FixedTypedArrayBase::cast(obj);
// Only fixup for the off-heap case.
......
......@@ -708,6 +708,15 @@ void Serializer<AllocatorT>::ObjectSerializer::SerializeContent(Map* map,
object_->IterateBody(map, size, this);
// Finally skip to the end.
serializer_->FlushSkip(SkipTo(object_->address() + size));
} else if (object_->IsJSDataView()) {
// JSDataView::external_pointer() contains a raw pointer. Change it
// to nullptr for serialization to keep the snapshot predictable.
JSDataView* data_view = JSDataView::cast(object_);
void* external_pointer = data_view->external_pointer();
data_view->set_external_pointer(nullptr);
object_->IterateBody(map, size, this);
OutputRawData(object_->address() + size);
data_view->set_external_pointer(external_pointer);
} else {
// For other objects, iterate references first.
object_->IterateBody(map, size, this);
......
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