[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: Benedikt Meurer <bmeurer@chromium.org>
Reviewed-by: Tobias Tebbi <tebbi@chromium.org>
Reviewed-by: Yang Guo <yangguo@chromium.org>
Reviewed-by: Michael Lippautz <mlippautz@chromium.org>
Cr-Commit-Position: refs/heads/master@{#56042}
This commit is contained in:
Benedikt Meurer 2018-09-19 14:41:25 +02:00 committed by Commit Bot
parent 8f30ab323c
commit 46573e51d8
17 changed files with 255 additions and 290 deletions

View File

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

View File

@ -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,
signed: constexpr bool): Smi {
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);
}
}

View File

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

View File

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

View File

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

View File

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

View File

@ -3470,53 +3470,53 @@ Reduction JSCallReducer::ReduceJSCall(Node* node,
node, JS_DATA_VIEW_TYPE,
AccessBuilder::ForJSArrayBufferViewByteOffset());
case Builtins::kDataViewPrototypeGetUint8:
return ReduceDataViewPrototypeGet(node,
ExternalArrayType::kExternalUint8Array);
return ReduceDataViewAccess(node, DataViewAccess::kGet,
ExternalArrayType::kExternalUint8Array);
case Builtins::kDataViewPrototypeGetInt8:
return ReduceDataViewPrototypeGet(node,
ExternalArrayType::kExternalInt8Array);
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,
ExternalArrayType::kExternalInt16Array);
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,
ExternalArrayType::kExternalInt32Array);
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,
ExternalArrayType::kExternalUint8Array);
return ReduceDataViewAccess(node, DataViewAccess::kSet,
ExternalArrayType::kExternalUint8Array);
case Builtins::kDataViewPrototypeSetInt8:
return ReduceDataViewPrototypeSet(node,
ExternalArrayType::kExternalInt8Array);
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,
ExternalArrayType::kExternalInt16Array);
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,
ExternalArrayType::kExternalInt32Array);
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,31 +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);
Node* offset = node->op()->ValueInputCount() > 2
? NodeProperties::GetValueInput(node, 2)
: jsgraph()->ZeroConstant();
Node* value = (access == DataViewAccess::kGet)
? nullptr
: (node->op()->ValueInputCount() > 3
? NodeProperties::GetValueInput(node, 3)
: 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* is_little_endian = 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)) {
Node* external_pointer;
// Check that the {offset} is within range for the {receiver}.
HeapObjectMatcher m(receiver);
if (m.HasValue()) {
@ -6818,11 +6829,6 @@ Reduction JSCallReducer::ReduceDataViewPrototypeGet(
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));
@ -6830,9 +6836,9 @@ Reduction JSCallReducer::ReduceDataViewPrototypeGet(
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 =
@ -6843,36 +6849,49 @@ Reduction JSCallReducer::ReduceDataViewPrototypeGet(
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 first.
if (access == DataViewAccess::kSet) {
value = effect = graph()->NewNode(
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(
simplified()->LoadField(AccessBuilder::ForJSArrayBufferViewBuffer()),
@ -6900,165 +6919,24 @@ Reduction JSCallReducer::ReduceDataViewPrototypeGet(
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);
// 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 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);
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)) {
// 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);
// Coerce {value} to Number.
value = effect = graph()->NewNode(
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(
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 store.
effect = graph()->NewNode(simplified()->StoreDataViewElement(element_type),
buffer, backing_store, offset, value,
is_little_endian, effect, control);
Node* value = jsgraph()->UndefinedConstant();
// Continue on the regular path.
ReplaceWithValue(node, value, effect, control);
return Changed(value);
return Replace(value);
}
return NoChange();

View File

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

View File

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

View File

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

View File

@ -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) {
// JSArrayBufferView contains raw data that the GC does not know about.
// JSDataView 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) {
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) {
// JSTypedArray 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) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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