From 46573e51d859fcc72489abc314caa197cace2594 Mon Sep 17 00:00:00 2001 From: Benedikt Meurer Date: Wed, 19 Sep 2018 14:41:25 +0200 Subject: [PATCH] [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 Reviewed-by: Benedikt Meurer Reviewed-by: Tobias Tebbi Reviewed-by: Yang Guo Reviewed-by: Michael Lippautz Cr-Commit-Position: refs/heads/master@{#56042} --- src/builtins/builtins-dataview.cc | 2 + src/builtins/data-view.tq | 93 ++++----- src/code-stub-assembler.cc | 6 + src/code-stub-assembler.h | 3 + src/compiler/access-builder.cc | 12 ++ src/compiler/access-builder.h | 3 + src/compiler/js-call-reducer.cc | 318 +++++++++-------------------- src/compiler/js-call-reducer.h | 3 + src/heap/concurrent-marking.cc | 6 +- src/heap/objects-visiting.h | 6 +- src/objects-body-descriptors-inl.h | 37 +++- src/objects.cc | 4 +- src/objects/js-array-buffer-inl.h | 12 +- src/objects/js-array-buffer.h | 21 +- src/objects/map.h | 3 +- src/snapshot/deserializer.cc | 7 + src/snapshot/serializer.cc | 9 + 17 files changed, 255 insertions(+), 290 deletions(-) diff --git a/src/builtins/builtins-dataview.cc b/src/builtins/builtins-dataview.cc index f40cd0f68e..21f02bbef6 100644 --- a/src/builtins/builtins-dataview.cc +++ b/src/builtins/builtins-dataview.cc @@ -101,6 +101,8 @@ BUILTIN(DataViewConstructor) { // 13. Set O's [[ByteOffset]] internal slot to offset. Handle::cast(result)->set_byte_offset(view_byte_offset); + Handle::cast(result)->set_external_pointer( + static_cast(array_buffer->backing_store()) + view_byte_offset); // 14. Return O. return *result; diff --git a/src/builtins/data-view.tq b/src/builtins/data-view.tq index 81a4d72ecb..58b1220fa0 100644 --- a/src/builtins/data-view.tq +++ b/src/builtins/data-view.tq @@ -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(LoadInt8(buffer.backing_store, offset)); + return Convert(LoadInt8(dataView.external_pointer, offset)); } else { - return Convert(LoadUint8(buffer.backing_store, offset)); + return Convert(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(getIndex); let getIndexWord: uintptr = Convert(getIndexFloat); - let viewOffsetWord: uintptr = dataView.byte_offset; let viewSizeFloat: float64 = Convert(dataView.byte_length); let elementSizeFloat: float64 = Convert(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(getIndex); let getIndexWord: uintptr = Convert(getIndexFloat); - let viewOffsetWord: uintptr = dataView.byte_offset; let viewSizeFloat: float64 = Convert(dataView.byte_length); let elementSizeFloat: float64 = Convert(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); } } diff --git a/src/code-stub-assembler.cc b/src/code-stub-assembler.cc index 55fc6dc8ff..d0e11028c2 100644 --- a/src/code-stub-assembler.cc +++ b/src/code-stub-assembler.cc @@ -12263,6 +12263,12 @@ TNode CodeStubAssembler::LoadJSArrayBufferViewByteOffset( JSArrayBufferView::kByteOffsetOffset); } +TNode CodeStubAssembler::LoadJSDataViewExternalPointer( + TNode data_view) { + return LoadObjectField(data_view, + JSDataView::kExternalPointerOffset); +} + TNode CodeStubAssembler::LoadJSTypedArrayLength( TNode typed_array) { return LoadObjectField(typed_array, JSTypedArray::kLengthOffset); diff --git a/src/code-stub-assembler.h b/src/code-stub-assembler.h index 5dbb8ca237..8626774c2e 100644 --- a/src/code-stub-assembler.h +++ b/src/code-stub-assembler.h @@ -2789,6 +2789,9 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler { SloppyTNode context, TNode array_buffer_view, const char* method_name); + // JSDataView helpers + TNode LoadJSDataViewExternalPointer(TNode data_view); + // JSTypedArray helpers TNode LoadJSTypedArrayLength(TNode typed_array); diff --git a/src/compiler/access-builder.cc b/src/compiler/access-builder.cc index 3c51f437eb..46fb3260ac 100644 --- a/src/compiler/access-builder.cc +++ b/src/compiler/access-builder.cc @@ -375,6 +375,18 @@ FieldAccess AccessBuilder::ForJSArrayBufferViewByteOffset() { return access; } +// static +FieldAccess AccessBuilder::ForJSDataViewExternalPointer() { + FieldAccess access = {kTaggedBase, + JSDataView::kExternalPointerOffset, + MaybeHandle(), + MaybeHandle(), + Type::ExternalPointer(), + MachineType::Pointer(), + kNoWriteBarrier}; + return access; +} + // static FieldAccess AccessBuilder::ForJSTypedArrayLength() { FieldAccess access = {kTaggedBase, diff --git a/src/compiler/access-builder.h b/src/compiler/access-builder.h index 945edf3014..68e1bf502d 100644 --- a/src/compiler/access-builder.h +++ b/src/compiler/access-builder.h @@ -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(); diff --git a/src/compiler/js-call-reducer.cc b/src/compiler/js-call-reducer.cc index bc047cc5f8..8fece1daa6 100644 --- a/src/compiler/js-call-reducer.cc +++ b/src/compiler/js-call-reducer.cc @@ -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 dataview = Handle::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(); diff --git a/src/compiler/js-call-reducer.h b/src/compiler/js-call-reducer.h index e04870ed2f..07a2745cf8 100644 --- a/src/compiler/js-call-reducer.h +++ b/src/compiler/js-call-reducer.h @@ -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, diff --git a/src/heap/concurrent-marking.cc b/src/heap/concurrent-marking.cc index 12e852a422..06c76f1118 100644 --- a/src/heap/concurrent-marking.cc +++ b/src/heap/concurrent-marking.cc @@ -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); } diff --git a/src/heap/objects-visiting.h b/src/heap/objects-visiting.h index 89b946faee..147af52c7e 100644 --- a/src/heap/objects-visiting.h +++ b/src/heap/objects-visiting.h @@ -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) \ diff --git a/src/objects-body-descriptors-inl.h b/src/objects-body-descriptors-inl.h index fed2a88cf2..40ebdffe4f 100644 --- a/src/objects-body-descriptors-inl.h +++ b/src/objects-body-descriptors-inl.h @@ -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 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 + 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) { diff --git a/src/objects.cc b/src/objects.cc index 264e97e631..04aa65dd0f 100644 --- a/src/objects.cc +++ b/src/objects.cc @@ -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; diff --git a/src/objects/js-array-buffer-inl.h b/src/objects/js-array-buffer-inl.h index 42c01352f5..5609466d27 100644 --- a/src/objects/js-array-buffer-inl.h +++ b/src/objects/js-array-buffer-inl.h @@ -33,7 +33,7 @@ void* JSArrayBuffer::backing_store() const { return reinterpret_cast(ptr); } -void JSArrayBuffer::set_backing_store(void* value, WriteBarrierMode mode) { +void JSArrayBuffer::set_backing_store(void* value) { intptr_t ptr = reinterpret_cast(value); WRITE_INTPTR_FIELD(this, kBackingStoreOffset, ptr); } @@ -184,6 +184,16 @@ MaybeHandle 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(ptr); +} + +void JSDataView::set_external_pointer(void* value) { + intptr_t ptr = reinterpret_cast(value); + WRITE_INTPTR_FIELD(this, kExternalPointerOffset, ptr); +} + } // namespace internal } // namespace v8 diff --git a/src/objects/js-array-buffer.h b/src/objects/js-array-buffer.h index b09447ddde..014f1dbbfb 100644 --- a/src/objects/js-array-buffer.h +++ b/src/objects/js-array-buffer.h @@ -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 MaterializeArrayBuffer( Handle 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); }; diff --git a/src/objects/map.h b/src/objects/map.h index 12545b513b..5f6b173cd3 100644 --- a/src/objects/map.h +++ b/src/objects/map.h @@ -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) \ diff --git a/src/snapshot/deserializer.cc b/src/snapshot/deserializer.cc index 7b63b0feea..d459350dba 100644 --- a/src/snapshot/deserializer.cc +++ b/src/snapshot/deserializer.cc @@ -241,6 +241,13 @@ HeapObject* Deserializer::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(buffer->backing_store()) + + data_view->byte_offset()); } else if (obj->IsFixedTypedArrayBase()) { FixedTypedArrayBase* fta = FixedTypedArrayBase::cast(obj); // Only fixup for the off-heap case. diff --git a/src/snapshot/serializer.cc b/src/snapshot/serializer.cc index 647721c24a..76829dd3ec 100644 --- a/src/snapshot/serializer.cc +++ b/src/snapshot/serializer.cc @@ -708,6 +708,15 @@ void Serializer::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);