Commit f733dc0f authored by Marja Hölttä's avatar Marja Hölttä Committed by V8 LUCI CQ

[rab / gsab] RAB / GSAB support for TA.p.set

Bug: v8:11111
Change-Id: I757e67cbcad98b6cacb3ad08b6a364194feead1f
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3427201Reviewed-by: 's avatarShu-yu Guo <syg@chromium.org>
Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Reviewed-by: 's avatarNico Hartmann <nicohartmann@chromium.org>
Commit-Queue: Marja Hölttä <marja@chromium.org>
Cr-Commit-Position: refs/heads/main@{#78937}
parent efd28c14
......@@ -186,8 +186,12 @@ TF_BUILTIN(TypedArrayPrototypeLength, TypedArrayBuiltinsAssembler) {
TNode<BoolT> TypedArrayBuiltinsAssembler::IsUint8ElementsKind(
TNode<Int32T> kind) {
return Word32Or(Word32Equal(kind, Int32Constant(UINT8_ELEMENTS)),
Word32Equal(kind, Int32Constant(UINT8_CLAMPED_ELEMENTS)));
return Word32Or(
Word32Or(Word32Equal(kind, Int32Constant(UINT8_ELEMENTS)),
Word32Equal(kind, Int32Constant(UINT8_CLAMPED_ELEMENTS))),
Word32Or(
Word32Equal(kind, Int32Constant(RAB_GSAB_UINT8_ELEMENTS)),
Word32Equal(kind, Int32Constant(RAB_GSAB_UINT8_CLAMPED_ELEMENTS))));
}
TNode<BoolT> TypedArrayBuiltinsAssembler::IsBigInt64ElementsKind(
......
......@@ -28,10 +28,12 @@ class TypedArrayBuiltinsAssembler : public CodeStubAssembler {
TNode<UintPtrT> CalculateExternalPointer(TNode<UintPtrT> backing_store,
TNode<UintPtrT> byte_offset);
// Returns true if kind is either UINT8_ELEMENTS or UINT8_CLAMPED_ELEMENTS.
// Returns true if kind is either UINT8_ELEMENTS, UINT8_CLAMPED_ELEMENTS,
// RAB_GSAB_UINT8_ELEMENTS, or RAB_GSAB_UINT8_CLAMPED_ELEMENTS.
TNode<BoolT> IsUint8ElementsKind(TNode<Int32T> kind);
// Returns true if kind is either BIGINT64_ELEMENTS or BIGUINT64_ELEMENTS.
// Returns true if kind is either BIGINT64_ELEMENTS, BIGUINT64_ELEMENTS,
// RAB_GSAB_BIGINT64_ELEMENTS, or RAB_GSAB_BIGUINT64_ELEMENTS.
TNode<BoolT> IsBigInt64ElementsKind(TNode<Int32T> kind);
// Returns the byte size of an element for a TypedArray elements kind.
......
......@@ -84,11 +84,6 @@ javascript builtin DataViewPrototypeGetBuffer(
return dataView.buffer;
}
extern macro IsJSArrayBufferViewDetachedOrOutOfBounds(JSArrayBufferView):
never labels DetachedOrOutOfBounds, NotDetachedNorOutOfBounds;
extern macro LoadVariableLengthJSArrayBufferViewByteLength(
JSArrayBufferView, JSArrayBuffer): uintptr labels DetachedOrOutOfBounds;
// ES6 section 24.2.4.2 get DataView.prototype.byteLength
javascript builtin DataViewPrototypeGetByteLength(
js-implicit context: NativeContext, receiver: JSAny)(...arguments): Number {
......@@ -118,7 +113,7 @@ javascript builtin DataViewPrototypeGetByteOffset(
const dataView: JSDataView =
ValidateDataView(context, receiver, 'get DataView.prototype.byte_offset');
try {
IsJSArrayBufferViewDetachedOrOutOfBounds(dataView)
typed_array::IsJSArrayBufferViewDetachedOrOutOfBounds(dataView)
otherwise DetachedOrOutOfBounds, NotDetachedNorOutOfBounds;
} label DetachedOrOutOfBounds {
ThrowTypeError(MessageTemplate::kDetachedOperation, kBuiltinNameByteOffset);
......@@ -399,7 +394,7 @@ transitioning macro DataViewGet(
// 7. If IsViewOutOfBounds(view, getBufferByteLength) is true, throw a
// TypeError exception.
try {
IsJSArrayBufferViewDetachedOrOutOfBounds(dataView)
typed_array::IsJSArrayBufferViewDetachedOrOutOfBounds(dataView)
otherwise DetachedOrOutOfBounds, NotDetachedNorOutOfBounds;
} label DetachedOrOutOfBounds {
ThrowTypeError(
......@@ -718,7 +713,7 @@ transitioning macro DataViewSet(
// 10. If IsViewOutOfBounds(view, getBufferByteLength) is true, throw a
// TypeError exception.
try {
IsJSArrayBufferViewDetachedOrOutOfBounds(dataView)
typed_array::IsJSArrayBufferViewDetachedOrOutOfBounds(dataView)
otherwise DetachedOrOutOfBounds, NotDetachedNorOutOfBounds;
} label DetachedOrOutOfBounds {
ThrowTypeError(
......
This diff is collapsed.
......@@ -3611,6 +3611,7 @@ class TypedElementsAccessor
}
}
// TODO(v8:11111): Update this once we have external RAB / GSAB array types.
static bool HasSimpleRepresentation(ExternalArrayType type) {
return !(type == kExternalFloat32Array || type == kExternalFloat64Array ||
type == kExternalUint8ClampedArray);
......@@ -3642,9 +3643,9 @@ class TypedElementsAccessor
CHECK(!source.WasDetached());
CHECK(!destination.WasDetached());
DCHECK_LE(offset, destination.length());
DCHECK_LE(length, destination.length() - offset);
DCHECK_LE(length, source.length());
DCHECK_LE(offset, destination.GetLength());
DCHECK_LE(length, destination.GetLength() - offset);
DCHECK_LE(length, source.GetLength());
ExternalArrayType source_type = source.type();
ExternalArrayType destination_type = destination.type();
......@@ -3705,6 +3706,7 @@ class TypedElementsAccessor
source_shared || destination_shared ? kShared : kUnshared); \
break;
TYPED_ARRAYS(TYPED_ARRAY_CASE)
RAB_GSAB_TYPED_ARRAYS(TYPED_ARRAY_CASE)
default:
UNREACHABLE();
break;
......@@ -3739,12 +3741,15 @@ class TypedElementsAccessor
static bool TryCopyElementsFastNumber(Context context, JSArray source,
JSTypedArray destination, size_t length,
size_t offset) {
if (Kind == BIGINT64_ELEMENTS || Kind == BIGUINT64_ELEMENTS) return false;
if (IsBigIntTypedArrayElementsKind(Kind)) return false;
Isolate* isolate = source.GetIsolate();
DisallowGarbageCollection no_gc;
DisallowJavascriptExecution no_js(isolate);
CHECK(!destination.WasDetached());
bool out_of_bounds = false;
CHECK(destination.GetLengthOrOutOfBounds(out_of_bounds) >= length);
CHECK(!out_of_bounds);
size_t current_length;
DCHECK(source.length().IsNumber() &&
......@@ -3752,7 +3757,7 @@ class TypedElementsAccessor
length <= current_length);
USE(current_length);
size_t dest_length = destination.length();
size_t dest_length = destination.GetLength();
DCHECK(length + offset <= dest_length);
USE(dest_length);
......@@ -3830,15 +3835,16 @@ class TypedElementsAccessor
LookupIterator it(isolate, source, i);
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, elem,
Object::GetProperty(&it));
if (Kind == BIGINT64_ELEMENTS || Kind == BIGUINT64_ELEMENTS) {
if (IsBigIntTypedArrayElementsKind(Kind)) {
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, elem,
BigInt::FromObject(isolate, elem));
} else {
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, elem,
Object::ToNumber(isolate, elem));
}
if (V8_UNLIKELY(destination->WasDetached())) {
bool out_of_bounds = false;
size_t new_length = destination->GetLengthOrOutOfBounds(out_of_bounds);
if (V8_UNLIKELY(out_of_bounds || destination->WasDetached())) {
const char* op = "set";
const MessageTemplate message = MessageTemplate::kDetachedOperation;
Handle<String> operation =
......@@ -3846,8 +3852,14 @@ class TypedElementsAccessor
THROW_NEW_ERROR_RETURN_FAILURE(isolate,
NewTypeError(message, operation));
}
// The spec says we store the length, then get each element, so we don't
// need to check changes to length.
if (V8_UNLIKELY(new_length <= offset + i)) {
// Proceed with the loop so that we call get getters for the source even
// though we don't set the values in the target.
// TODO(v8:11111): Maybe change this, depending on how
// https://github.com/tc39/proposal-resizablearraybuffer/issues/86 is
// resolved.
continue;
}
SetImpl(destination, InternalIndex(offset + i), *elem);
}
return *isolate->factory()->undefined_value();
......@@ -3860,15 +3872,24 @@ class TypedElementsAccessor
Handle<JSObject> destination,
size_t length, size_t offset) {
Isolate* isolate = destination->GetIsolate();
if (length == 0) return *isolate->factory()->undefined_value();
Handle<JSTypedArray> destination_ta =
Handle<JSTypedArray>::cast(destination);
DCHECK_LE(offset + length, destination_ta->length());
if (length == 0) return *isolate->factory()->undefined_value();
// All conversions from TypedArrays can be done without allocation.
if (source->IsJSTypedArray()) {
// TODO(v8:11111): Add RAB/GSAB support.
DCHECK(!destination_ta->is_length_tracking());
DCHECK(!destination_ta->is_backed_by_rab());
DCHECK(!Handle<JSTypedArray>::cast(source)->is_length_tracking());
DCHECK(!Handle<JSTypedArray>::cast(source)->is_backed_by_rab());
CHECK(!destination_ta->WasDetached());
bool out_of_bounds = false;
CHECK_LE(offset + length,
destination_ta->GetLengthOrOutOfBounds(out_of_bounds));
CHECK(!out_of_bounds);
Handle<JSTypedArray> source_ta = Handle<JSTypedArray>::cast(source);
ElementsKind source_kind = source_ta->GetElementsKind();
bool source_is_bigint =
......@@ -3884,6 +3905,10 @@ class TypedElementsAccessor
}
} else if (source->IsJSArray()) {
CHECK(!destination_ta->WasDetached());
bool out_of_bounds = false;
CHECK_LE(offset + length,
destination_ta->GetLengthOrOutOfBounds(out_of_bounds));
CHECK(!out_of_bounds);
// Fast cases for packed numbers kinds where we don't need to allocate.
Handle<JSArray> source_js_array = Handle<JSArray>::cast(source);
size_t current_length;
......@@ -3898,7 +3923,8 @@ class TypedElementsAccessor
}
}
// Final generic case that handles prototype chain lookups, getters, proxies
// and observable side effects via valueOf, etc.
// and observable side effects via valueOf, etc. In this case, it's possible
// that the length getter detached / resized the underlying buffer.
return CopyElementsHandleSlow(source, destination_ta, length, offset);
}
};
......@@ -5209,6 +5235,7 @@ void CopyFastNumberJSArrayElementsToTypedArray(Address raw_context,
context, source, destination, length, offset)); \
break;
TYPED_ARRAYS(TYPED_ARRAYS_CASE)
RAB_GSAB_TYPED_ARRAYS(TYPED_ARRAYS_CASE)
#undef TYPED_ARRAYS_CASE
default:
UNREACHABLE();
......@@ -5228,6 +5255,7 @@ void CopyTypedArrayElementsToTypedArray(Address raw_source,
length, offset); \
break;
TYPED_ARRAYS(TYPED_ARRAYS_CASE)
RAB_GSAB_TYPED_ARRAYS(TYPED_ARRAYS_CASE)
#undef TYPED_ARRAYS_CASE
default:
UNREACHABLE();
......
......@@ -73,6 +73,20 @@ macro IsLengthTrackingJSArrayBufferView(array: JSArrayBufferView): bool {
return array.bit_field.is_length_tracking;
}
extern macro LoadVariableLengthJSArrayBufferViewByteLength(
JSArrayBufferView, JSArrayBuffer): uintptr labels DetachedOrOutOfBounds;
@export
macro LoadJSArrayBufferViewByteLength(
view: JSArrayBufferView, buffer: JSArrayBuffer):
uintptr labels DetachedOrOutOfBounds {
if (IsVariableLengthJSArrayBufferView(view)) {
return LoadVariableLengthJSArrayBufferViewByteLength(view, buffer)
otherwise DetachedOrOutOfBounds;
}
return view.byte_length;
}
extern class JSTypedArray extends JSArrayBufferView {
length: uintptr;
// A SandboxedPtr if the sandbox is enabled
......
......@@ -67,6 +67,7 @@ class BuildFlags : public ContextualClass<BuildFlags> {
#else
build_flags_["V8_ENABLE_WEBASSEMBLY"] = false;
#endif
build_flags_["DEBUG"] = DEBUG_BOOL;
}
static bool GetFlag(const std::string& name, const char* production) {
auto it = Get().build_flags_.find(name);
......
......@@ -176,6 +176,20 @@ function LastIndexOfHelper(array, n, fromIndex) {
return array.lastIndexOf(n, fromIndex);
}
function SetHelper(target, source, offset) {
if (target instanceof BigInt64Array || target instanceof BigUint64Array) {
const bigIntSource = [];
for (s of source) {
bigIntSource.push(BigInt(s));
}
source = bigIntSource;
}
if (offset == undefined) {
return target.set(source);
}
return target.set(source, offset);
}
function testDataViewMethodsUpToSize(view, bufferSize) {
for (const [getter, setter, size, isBigInt] of dataViewAccessorsAndSizes) {
for (let i = 0; i <= bufferSize - size; ++i) {
......
......@@ -1125,3 +1125,156 @@ d8.file.execute('test/mjsunit/typedarray-helpers.js');
Helper(lengthTracking));
}
})();
(function SetSourceLengthGetterDetachesTarget() {
// Orig. array: [0, 2, 4, 6]
// [0, 2, 4, 6] << fixedLength
// [4, 6] << fixedLengthWithOffset
// [0, 2, 4, 6, ...] << lengthTracking
// [4, 6, ...] << lengthTrackingWithOffset
function CreateRabForTest(ctor) {
const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT,
8 * ctor.BYTES_PER_ELEMENT);
// Write some data into the array.
const taWrite = new ctor(rab);
for (let i = 0; i < 4; ++i) {
WriteToTypedArray(taWrite, i, 2 * i);
}
return rab;
}
let rab;
function CreateSourceProxy(length) {
return new Proxy({}, {
get(target, prop, receiver) {
if (prop == 'length') {
%ArrayBufferDetach(rab);
return length;
}
return true; // Can be converted to both BigInt and Number.
}
});
}
// Tests where the length getter returns a non-zero value -> these throw.
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const fixedLength = new ctor(rab, 0, 4);
assertThrows(() => { fixedLength.set(CreateSourceProxy(1)); }, TypeError);
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
assertThrows(() => { fixedLengthWithOffset.set(CreateSourceProxy(1)); },
TypeError);
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const lengthTracking = new ctor(rab, 0);
assertThrows(() => { lengthTracking.set(CreateSourceProxy(1)); },
TypeError);
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
assertThrows(() => { lengthTrackingWithOffset.set(CreateSourceProxy(1)); },
TypeError);
}
// Tests where the length getter returns a zero -> these don't throw.
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const fixedLength = new ctor(rab, 0, 4);
fixedLength.set(CreateSourceProxy(0));
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
fixedLengthWithOffset.set(CreateSourceProxy(0));
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const lengthTracking = new ctor(rab, 0);
lengthTracking.set(CreateSourceProxy(0));
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
lengthTrackingWithOffset.set(CreateSourceProxy(0));
}
})();
(function SetDetachTargetMidIteration() {
// Orig. array: [0, 2, 4, 6]
// [0, 2, 4, 6] << fixedLength
// [4, 6] << fixedLengthWithOffset
// [0, 2, 4, 6, ...] << lengthTracking
// [4, 6, ...] << lengthTrackingWithOffset
function CreateRabForTest(ctor) {
const rab = CreateResizableArrayBuffer(4 * ctor.BYTES_PER_ELEMENT,
8 * ctor.BYTES_PER_ELEMENT);
// Write some data into the array.
const taWrite = new ctor(rab);
for (let i = 0; i < 4; ++i) {
WriteToTypedArray(taWrite, i, 2 * i);
}
return rab;
}
let rab;
// Detaching will happen when we're calling Get for the `detachAt`:th data
// element, but we haven't yet written it to the target.
let detachAt;
function CreateSourceProxy(length) {
let requestedIndices = [];
return new Proxy({}, {
get(target, prop, receiver) {
if (prop == 'length') {
return length;
}
requestedIndices.push(prop);
if (requestedIndices.length == detachAt) {
%ArrayBufferDetach(rab);
}
return true; // Can be converted to both BigInt and Number.
}
});
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const fixedLength = new ctor(rab, 0, 4);
detachAt = 2;
assertThrows(() => { fixedLength.set(CreateSourceProxy(4)); }, TypeError);
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const fixedLengthWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT, 2);
detachAt = 2;
assertThrows(() => { fixedLengthWithOffset.set(CreateSourceProxy(2)); },
TypeError);
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const lengthTracking = new ctor(rab, 0);
detachAt = 2;
assertThrows(() => { lengthTracking.set(CreateSourceProxy(2)); },
TypeError);
}
for (let ctor of ctors) {
rab = CreateRabForTest(ctor);
const lengthTrackingWithOffset = new ctor(rab, 2 * ctor.BYTES_PER_ELEMENT);
detachAt = 2;
assertThrows(() => { lengthTrackingWithOffset.set(CreateSourceProxy(2)); },
TypeError);
}
})();
......@@ -283,9 +283,6 @@
# https://bugs.chromium.org/p/v8/issues/detail?id=11111
'built-ins/ArrayBuffer/prototype/transfer/*': [FAIL],
'built-ins/ArrayBuffer/prototype/transfer/this-is-sharedarraybuffer': [PASS],
'built-ins/TypedArray/prototype/set/BigInt/typedarray-arg-target-out-of-bounds': [SKIP],
'built-ins/TypedArray/prototype/set/BigInt/typedarray-arg-set-values-same-buffer-same-type-resized': [SKIP],
'built-ins/TypedArray/prototype/set/typedarray-arg-target-out-of-bounds': [SKIP],
'built-ins/TypedArray/prototype/sort/BigInt/return-abrupt-from-this-out-of-bounds': [FAIL],
'built-ins/TypedArray/prototype/sort/return-abrupt-from-this-out-of-bounds': [FAIL],
'built-ins/TypedArrayConstructors/ctors/typedarray-arg/out-of-bounds-when-species-retrieved-different-type': [FAIL],
......
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