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

[rab/gsab] Add iteration support

Bug: v8:11111
Change-Id: Ieb9e8d1440b57f3840857c34debfece337bf4f51
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2979598
Commit-Queue: Marja Hölttä <marja@chromium.org>
Reviewed-by: 's avatarIgor Sheludko <ishell@chromium.org>
Reviewed-by: 's avatarShu-yu Guo <syg@chromium.org>
Cr-Commit-Position: refs/heads/master@{#75409}
parent 4b6b5761
......@@ -128,6 +128,7 @@ void ArrayBuiltinsAssembler::GenerateIteratingTypedArrayBuiltinBody(
TNode<JSTypedArray> typed_array = CAST(receiver_);
o_ = typed_array;
// TODO(v8:11111): Support RAB / GSAB.
TNode<JSArrayBuffer> array_buffer = LoadJSArrayBufferViewBuffer(typed_array);
ThrowIfArrayBufferIsDetached(context_, array_buffer, name_);
......@@ -1309,14 +1310,13 @@ TF_BUILTIN(ArrayIteratorPrototypeNext, CodeStubAssembler) {
TNode<UintPtrT> index_uintptr =
ChangeSafeIntegerNumberToUintPtr(index, &allocate_iterator_result);
// Check that the {array}s buffer wasn't detached.
ThrowIfArrayBufferViewBufferIsDetached(context, CAST(array), method_name);
// If we go outside of the {length}, we don't need to update the
// [[ArrayIteratorNextIndex]] anymore, since a JSTypedArray's
// length cannot change anymore, so this {iterator} will never
// produce values again anyways.
TNode<UintPtrT> length = LoadJSTypedArrayLength(CAST(array));
Label detached(this);
TNode<UintPtrT> length =
LoadJSTypedArrayLengthAndCheckDetached(CAST(array), &detached);
GotoIfNot(UintPtrLessThan(index_uintptr, length),
&allocate_iterator_result);
// TODO(v8:4153): Consider storing next index as uintptr. Update this and
......@@ -1338,6 +1338,9 @@ TF_BUILTIN(ArrayIteratorPrototypeNext, CodeStubAssembler) {
var_value = LoadFixedTypedArrayElementAsTagged(data_ptr, index_uintptr,
elements_kind);
Goto(&allocate_entry_if_needed);
BIND(&detached);
ThrowTypeError(context, MessageTemplate::kDetachedOperation, method_name);
}
BIND(&allocate_entry_if_needed);
......
......@@ -108,6 +108,7 @@ SharedArrayBufferBuiltinsAssembler::ValidateIntegerTypedArray(
TNode<UintPtrT> SharedArrayBufferBuiltinsAssembler::ValidateAtomicAccess(
TNode<JSTypedArray> array, TNode<Object> index, TNode<Context> context) {
Label done(this), range_error(this);
// TODO(v8:11111): Support RAB / GSAB.
// 1. Assert: typedArray is an Object that has a [[ViewedArrayBuffer]]
// internal slot.
......
......@@ -173,28 +173,12 @@ TF_BUILTIN(TypedArrayPrototypeLength, TypedArrayBuiltinsAssembler) {
ThrowIfNotInstanceType(context, receiver, JS_TYPED_ARRAY_TYPE, kMethodName);
TNode<JSTypedArray> receiver_array = CAST(receiver);
TNode<JSArrayBuffer> receiver_buffer =
LoadJSArrayBufferViewBuffer(receiver_array);
Label variable_length(this), normal(this);
Branch(IsVariableLengthTypedArray(receiver_array), &variable_length, &normal);
BIND(&variable_length);
{
Label miss(this);
Return(ChangeUintPtrToTagged(LoadVariableLengthJSTypedArrayLength(
receiver_array, receiver_buffer, &miss)));
BIND(&miss);
Return(ChangeUintPtrToTagged(UintPtrConstant(0)));
}
BIND(&normal);
{
// Default to zero if the {receiver}s buffer was detached.
TNode<UintPtrT> length = Select<UintPtrT>(
IsDetachedBuffer(receiver_buffer), [=] { return UintPtrConstant(0); },
[=] { return LoadJSTypedArrayLength(receiver_array); });
Return(ChangeUintPtrToTagged(length));
}
TVARIABLE(UintPtrT, length);
Label detached(this), end(this);
length = LoadJSTypedArrayLengthAndCheckDetached(receiver_array, &detached);
Return(ChangeUintPtrToTagged(length.value()));
BIND(&detached);
Return(ChangeUintPtrToTagged(UintPtrConstant(0)));
}
TNode<BoolT> TypedArrayBuiltinsAssembler::IsUint8ElementsKind(
......
......@@ -2554,7 +2554,7 @@ TNode<Numeric> CodeStubAssembler::LoadFixedTypedArrayElementAsTagged(
Label done(this), if_unknown_type(this, Label::kDeferred);
int32_t elements_kinds[] = {
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) TYPE##_ELEMENTS,
TYPED_ARRAYS(TYPED_ARRAY_CASE)
TYPED_ARRAYS(TYPED_ARRAY_CASE) RAB_GSAB_TYPED_ARRAYS(TYPED_ARRAY_CASE)
#undef TYPED_ARRAY_CASE
};
......@@ -2565,6 +2565,9 @@ TNode<Numeric> CodeStubAssembler::LoadFixedTypedArrayElementAsTagged(
Label* elements_kind_labels[] = {
#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) &if_##type##array,
TYPED_ARRAYS(TYPED_ARRAY_CASE)
// The same labels again for RAB / GSAB. We dispatch RAB / GSAB elements
// kinds to the corresponding non-RAB / GSAB elements kinds.
TYPED_ARRAYS(TYPED_ARRAY_CASE)
#undef TYPED_ARRAY_CASE
};
STATIC_ASSERT(arraysize(elements_kinds) == arraysize(elements_kind_labels));
......@@ -13801,9 +13804,31 @@ TNode<UintPtrT> CodeStubAssembler::LoadJSArrayBufferViewByteOffset(
JSArrayBufferView::kByteOffsetOffset);
}
TNode<UintPtrT> CodeStubAssembler::LoadJSTypedArrayLength(
TNode<JSTypedArray> typed_array) {
return LoadObjectField<UintPtrT>(typed_array, JSTypedArray::kLengthOffset);
TNode<UintPtrT> CodeStubAssembler::LoadJSTypedArrayLengthAndCheckDetached(
TNode<JSTypedArray> typed_array, Label* detached) {
TVARIABLE(UintPtrT, result);
TNode<JSArrayBuffer> buffer = LoadJSArrayBufferViewBuffer(typed_array);
Label variable_length(this), fixed_length(this), end(this);
Branch(IsVariableLengthTypedArray(typed_array), &variable_length,
&fixed_length);
BIND(&variable_length);
{
result =
LoadVariableLengthJSTypedArrayLength(typed_array, buffer, detached);
Goto(&end);
}
BIND(&fixed_length);
{
Label not_detached(this);
Branch(IsDetachedBuffer(buffer), detached, &not_detached);
BIND(&not_detached);
result = LoadJSTypedArrayLength(typed_array);
Goto(&end);
}
BIND(&end);
return result.value();
}
// ES #sec-integerindexedobjectlength
......
......@@ -3555,7 +3555,8 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
const char* method_name);
// JSTypedArray helpers
TNode<UintPtrT> LoadJSTypedArrayLength(TNode<JSTypedArray> typed_array);
TNode<UintPtrT> LoadJSTypedArrayLengthAndCheckDetached(
TNode<JSTypedArray> typed_array, Label* detached);
// Helper for length tracking JSTypedArrays and JSTypedArrays backed by
// ResizableArrayBuffer.
TNode<UintPtrT> LoadVariableLengthJSTypedArrayLength(
......
......@@ -352,3 +352,203 @@ const ctors = [
assertEquals('012', keys);
}
}());
(function IterateTypedArray() {
const no_elements = 10;
const offset = 2;
function TestIteration(ta, expected) {
let values = [];
for (const value of ta) {
values.push(value);
}
assertEquals(expected, values);
}
for (let ctor of ctors) {
if (ctor == BigInt64Array || ctor == BigUint64Array) {
// This test doesn't work for BigInts.
continue;
}
const buffer_byte_length = no_elements * ctor.BYTES_PER_ELEMENT;
// We can use the same GSAB for all the TAs below, since we won't modify it
// after writing the initial values.
const gsab = new GrowableSharedArrayBuffer(buffer_byte_length,
2 * buffer_byte_length);
const byte_offset = offset * ctor.BYTES_PER_ELEMENT;
// Write some data into the array.
let ta_write = new ctor(gsab);
for (let i = 0; i < no_elements; ++i) {
ta_write[i] = i % 128;
}
// Create various different styles of TypedArrays with the GSAB as the
// backing store and iterate them.
const ta = new ctor(gsab, 0, 3);
TestIteration(ta, [0, 1, 2]);
const empty_ta = new ctor(gsab, 0, 0);
TestIteration(empty_ta, []);
const ta_with_offset = new ctor(gsab, byte_offset, 3);
TestIteration(ta_with_offset, [2, 3, 4]);
const empty_ta_with_offset = new ctor(gsab, byte_offset, 0);
TestIteration(empty_ta_with_offset, []);
const length_tracking_ta = new ctor(gsab);
{
let expected = [];
for (let i = 0; i < no_elements; ++i) {
expected.push(i % 128);
}
TestIteration(length_tracking_ta, expected);
}
const length_tracking_ta_with_offset = new ctor(gsab, byte_offset);
{
let expected = [];
for (let i = offset; i < no_elements; ++i) {
expected.push(i % 128);
}
TestIteration(length_tracking_ta_with_offset, expected);
}
const empty_length_tracking_ta_with_offset = new ctor(gsab, buffer_byte_length);
TestIteration(empty_length_tracking_ta_with_offset, []);
}
}());
// Helpers for iteration tests.
function CreateGsab(buffer_byte_length, ctor) {
const gsab = new GrowableSharedArrayBuffer(buffer_byte_length,
2 * buffer_byte_length);
// Write some data into the array.
let ta_write = new ctor(gsab);
for (let i = 0; i < buffer_byte_length / ctor.BYTES_PER_ELEMENT; ++i) {
ta_write[i] = i % 128;
}
return gsab;
}
function TestIterationAndGrow(ta, expected, gsab, grow_after,
new_byte_length) {
let values = [];
let grown = false;
for (const value of ta) {
values.push(value);
if (!grown && values.length == grow_after) {
gsab.grow(new_byte_length);
grown = true;
}
}
assertEquals(expected, values);
assertTrue(grown);
}
(function IterateTypedArrayAndGrowMidIteration() {
const no_elements = 10;
const offset = 2;
for (let ctor of ctors) {
if (ctor == BigInt64Array || ctor == BigUint64Array) {
// This test doesn't work for BigInts.
continue;
}
const buffer_byte_length = no_elements * ctor.BYTES_PER_ELEMENT;
const byte_offset = offset * ctor.BYTES_PER_ELEMENT;
// Create various different styles of TypedArrays with the gsab as the
// backing store and iterate them.
// Fixed-length TAs aren't affected by resizing.
let gsab = CreateGsab(buffer_byte_length, ctor);
const ta = new ctor(gsab, 0, 3);
TestIterationAndGrow(ta, [0, 1, 2], gsab, 2, buffer_byte_length * 2);
gsab = CreateGsab(buffer_byte_length, ctor);
const ta_with_offset = new ctor(gsab, byte_offset, 3);
TestIterationAndGrow(ta_with_offset, [2, 3, 4], gsab, 2,
buffer_byte_length * 2);
gsab = CreateGsab(buffer_byte_length, ctor);
const length_tracking_ta = new ctor(gsab);
{
let expected = [];
for (let i = 0; i < no_elements; ++i) {
expected.push(i % 128);
}
// After resizing, the new memory contains zeros.
for (let i = 0; i < no_elements; ++i) {
expected.push(0);
}
TestIterationAndGrow(length_tracking_ta, expected, gsab, 2,
buffer_byte_length * 2);
}
gsab = CreateGsab(buffer_byte_length, ctor);
const length_tracking_ta_with_offset = new ctor(gsab, byte_offset);
{
let expected = [];
for (let i = offset; i < no_elements; ++i) {
expected.push(i % 128);
}
for (let i = 0; i < no_elements; ++i) {
expected.push(0);
}
TestIterationAndGrow(length_tracking_ta_with_offset, expected, gsab, 2,
buffer_byte_length * 2);
}
}
}());
(function IterateTypedArrayAndGrowJustBeforeIterationWouldEnd() {
const no_elements = 10;
const offset = 2;
// We need to recreate the gsab between all TA tests, since we grow it.
for (let ctor of ctors) {
if (ctor == BigInt64Array || ctor == BigUint64Array) {
// This test doesn't work for BigInts.
continue;
}
const buffer_byte_length = no_elements * ctor.BYTES_PER_ELEMENT;
const byte_offset = offset * ctor.BYTES_PER_ELEMENT;
// Create various different styles of TypedArrays with the gsab as the
// backing store and iterate them.
let gsab = CreateGsab(buffer_byte_length, ctor);
const length_tracking_ta = new ctor(gsab);
{
let expected = [];
for (let i = 0; i < no_elements; ++i) {
expected.push(i % 128);
}
// After resizing, the new memory contains zeros.
for (let i = 0; i < no_elements; ++i) {
expected.push(0);
}
TestIterationAndGrow(length_tracking_ta, expected, gsab, no_elements,
buffer_byte_length * 2);
}
gsab = CreateGsab(buffer_byte_length, ctor);
const length_tracking_ta_with_offset = new ctor(gsab, byte_offset);
{
let expected = [];
for (let i = offset; i < no_elements; ++i) {
expected.push(i % 128);
}
for (let i = 0; i < no_elements; ++i) {
expected.push(0);
}
TestIterationAndGrow(length_tracking_ta_with_offset, expected, gsab,
no_elements - offset, buffer_byte_length * 2);
}
}
}());
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