| // Copyright 2018 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include 'src/builtins/builtins-typed-array-gen.h' |
| |
| namespace typed_array { |
| // Naming convention from elements.cc. We have a similar intent but implement |
| // fastpaths using generics instead of using a class hierarchy for elements |
| // kinds specific implementations. |
| type Uint8Elements extends ElementsKind; |
| type Int8Elements extends ElementsKind; |
| type Uint16Elements extends ElementsKind; |
| type Int16Elements extends ElementsKind; |
| type Uint32Elements extends ElementsKind; |
| type Int32Elements extends ElementsKind; |
| type Float16Elements extends ElementsKind; |
| type Float32Elements extends ElementsKind; |
| type Float64Elements extends ElementsKind; |
| type Uint8ClampedElements extends ElementsKind; |
| type BigUint64Elements extends ElementsKind; |
| type BigInt64Elements extends ElementsKind; |
| type RabGsabUint8Elements extends ElementsKind; |
| |
| @export |
| struct TypedArrayElementsInfo { |
| // Calculates the number of bytes required for specified number of elements. |
| macro CalculateByteLength(length: uintptr): uintptr labels IfInvalid { |
| const maxArrayLength = ArrayBufferMaxByteLength() >>> this.sizeLog2; |
| if (length > maxArrayLength) goto IfInvalid; |
| const byteLength = length << this.sizeLog2; |
| return byteLength; |
| } |
| |
| // Calculates the maximum number of elements supported by a specified number |
| // of bytes. |
| macro CalculateLength(byteLength: uintptr): uintptr { |
| return byteLength >>> this.sizeLog2; |
| } |
| |
| // Determines if `bytes` (byte offset or length) cannot be evenly divided by |
| // element size. |
| macro IsUnaligned(bytes: uintptr): bool { |
| // Exploits the fact the element size is a power of 2. Determining whether |
| // there is remainder (not aligned) can be achieved efficiently with bit |
| // masking. Shift is safe as sizeLog2 can be 3 at most (see |
| // ElementsKindToShiftSize). |
| return (bytes & ((1 << this.sizeLog2) - 1)) != 0; |
| } |
| |
| sizeLog2: uintptr; |
| kind: ElementsKind; |
| } |
| extern runtime TypedArrayCopyElements(Context, JSTypedArray, Object, Number): |
| void; |
| extern macro TypedArrayBuiltinsAssembler::ValidateTypedArray( |
| Context, JSAny, constexpr string): JSTypedArray; |
| extern macro TypedArrayBuiltinsAssembler::ValidateTypedArrayAndGetLength( |
| Context, JSAny, constexpr string): uintptr; |
| |
| extern macro TypedArrayBuiltinsAssembler::CallCMemcpy(RawPtr, RawPtr, uintptr): |
| void; |
| extern macro TypedArrayBuiltinsAssembler::CallCMemmove( |
| RawPtr, RawPtr, uintptr): void; |
| extern macro TypedArrayBuiltinsAssembler::CallCMemset(RawPtr, intptr, uintptr): |
| void; |
| extern macro TypedArrayBuiltinsAssembler::CallCRelaxedMemcpy( |
| RawPtr, RawPtr, uintptr): void; |
| extern macro TypedArrayBuiltinsAssembler::CallCRelaxedMemmove( |
| RawPtr, RawPtr, uintptr): void; |
| extern macro GetTypedArrayBuffer(implicit context: Context)( |
| JSTypedArray): JSArrayBuffer; |
| extern macro TypedArrayBuiltinsAssembler::GetTypedArrayElementsInfo( |
| JSTypedArray): TypedArrayElementsInfo; |
| extern macro TypedArrayBuiltinsAssembler::GetTypedArrayElementsInfo(Map): |
| TypedArrayElementsInfo; |
| extern macro TypedArrayBuiltinsAssembler::IsUint8ElementsKind(ElementsKind): |
| bool; |
| extern macro TypedArrayBuiltinsAssembler::IsBigInt64ElementsKind(ElementsKind): |
| bool; |
| extern macro LoadFixedTypedArrayElementAsTagged( |
| RawPtr, uintptr, constexpr ElementsKind): Numeric; |
| extern macro TypedArrayBuiltinsAssembler::StoreJSTypedArrayElementFromNumeric( |
| Context, JSTypedArray, uintptr, Numeric, constexpr ElementsKind): void; |
| extern macro TypedArrayBuiltinsAssembler::StoreJSTypedArrayElementFromTagged( |
| Context, JSTypedArray, uintptr, JSAny, |
| constexpr ElementsKind): void labels IfDetached; |
| |
| extern macro LoadJSTypedArrayLengthAndCheckDetached(JSTypedArray): uintptr |
| labels IfDetached; |
| |
| type LoadNumericFn = builtin(JSTypedArray, uintptr) => Numeric; |
| type StoreNumericFn = builtin(Context, JSTypedArray, uintptr, Numeric) => Smi; |
| type StoreJSAnyFn = builtin(Context, JSTypedArray, uintptr, JSAny) => Smi; |
| |
| // The result codes returned by StoreNumericFn and StoreJSAnyFn builtins. |
| const kStoreSucceded: Smi = 0; |
| const kStoreFailureArrayDetachedOrOutOfBounds: Smi = 1; |
| |
| struct TypedArrayAccessor { |
| macro LoadNumeric(array: JSTypedArray, index: uintptr): Numeric { |
| const loadfn: LoadNumericFn = this.loadNumericFn; |
| return loadfn(array, index); |
| } |
| |
| macro StoreNumeric( |
| context: Context, array: JSTypedArray, index: uintptr, |
| value: Numeric): void { |
| const storefn: StoreNumericFn = this.storeNumericFn; |
| const result = storefn(context, array, index, value); |
| dcheck(result == kStoreSucceded); |
| } |
| |
| macro StoreJSAnyInBounds( |
| context: Context, array: JSTypedArray, index: uintptr, |
| value: JSAny): void { |
| const storefn: StoreJSAnyFn = this.storeJSAnyFn; |
| const result = storefn(context, array, index, value); |
| check(result == kStoreSucceded); |
| } |
| |
| macro StoreJSAny( |
| context: Context, array: JSTypedArray, index: uintptr, |
| value: JSAny): void { |
| const storefn: StoreJSAnyFn = this.storeJSAnyFn; |
| const result = storefn(context, array, index, value); |
| // ES#sec-typedarray-set |
| // |
| // A [[Set]] on a TypedArray with a detached or out-of-bounds |
| // underlying ArrayBuffer is a no-op. |
| dcheck( |
| result == kStoreSucceded || |
| result == kStoreFailureArrayDetachedOrOutOfBounds); |
| } |
| |
| loadNumericFn: LoadNumericFn; |
| storeNumericFn: StoreNumericFn; |
| storeJSAnyFn: StoreJSAnyFn; |
| } |
| |
| macro GetTypedArrayAccessor<T : type extends ElementsKind>(): |
| TypedArrayAccessor { |
| const loadNumericFn = LoadTypedElement<T>; |
| const storeNumericFn = StoreTypedElementNumeric<T>; |
| const storeJSAnyFn = StoreTypedElementJSAny<T>; |
| return TypedArrayAccessor{loadNumericFn, storeNumericFn, storeJSAnyFn}; |
| } |
| |
| macro GetTypedArrayAccessor(elementsKindParam: ElementsKind): |
| TypedArrayAccessor { |
| let elementsKind = elementsKindParam; |
| if (IsElementsKindGreaterThanOrEqual( |
| elementsKind, kFirstRabGsabFixedTypedArrayElementsKind)) { |
| elementsKind = %RawDownCast<ElementsKind>( |
| elementsKind - kFirstRabGsabFixedTypedArrayElementsKind + |
| kFirstFixedTypedArrayElementsKind); |
| } |
| if (IsElementsKindGreaterThan(elementsKind, ElementsKind::UINT32_ELEMENTS)) { |
| if (elementsKind == ElementsKind::INT32_ELEMENTS) { |
| return GetTypedArrayAccessor<Int32Elements>(); |
| } else if (elementsKind == ElementsKind::FLOAT16_ELEMENTS) { |
| return GetTypedArrayAccessor<Float16Elements>(); |
| } else if (elementsKind == ElementsKind::FLOAT32_ELEMENTS) { |
| return GetTypedArrayAccessor<Float32Elements>(); |
| } else if (elementsKind == ElementsKind::FLOAT64_ELEMENTS) { |
| return GetTypedArrayAccessor<Float64Elements>(); |
| } else if (elementsKind == ElementsKind::UINT8_CLAMPED_ELEMENTS) { |
| return GetTypedArrayAccessor<Uint8ClampedElements>(); |
| } else if (elementsKind == ElementsKind::BIGUINT64_ELEMENTS) { |
| return GetTypedArrayAccessor<BigUint64Elements>(); |
| } else if (elementsKind == ElementsKind::BIGINT64_ELEMENTS) { |
| return GetTypedArrayAccessor<BigInt64Elements>(); |
| } |
| } else { |
| if (elementsKind == ElementsKind::UINT8_ELEMENTS) { |
| return GetTypedArrayAccessor<Uint8Elements>(); |
| } else if (elementsKind == ElementsKind::INT8_ELEMENTS) { |
| return GetTypedArrayAccessor<Int8Elements>(); |
| } else if (elementsKind == ElementsKind::UINT16_ELEMENTS) { |
| return GetTypedArrayAccessor<Uint16Elements>(); |
| } else if (elementsKind == ElementsKind::INT16_ELEMENTS) { |
| return GetTypedArrayAccessor<Int16Elements>(); |
| } else if (elementsKind == ElementsKind::UINT32_ELEMENTS) { |
| return GetTypedArrayAccessor<Uint32Elements>(); |
| } |
| } |
| unreachable; |
| } |
| |
| extern macro TypedArrayBuiltinsAssembler::SetJSTypedArrayOnHeapDataPtr( |
| JSTypedArray, ByteArray, uintptr): void; |
| extern macro TypedArrayBuiltinsAssembler::SetJSTypedArrayOffHeapDataPtr( |
| JSTypedArray, RawPtr, uintptr): void; |
| extern macro IsJSArrayBufferViewDetachedOrOutOfBounds(JSArrayBufferView): |
| never labels DetachedOrOutOfBounds, NotDetachedNorOutOfBounds; |
| extern macro IsJSArrayBufferViewDetachedOrOutOfBoundsBoolean( |
| JSArrayBufferView): bool; |
| |
| // AttachedJSTypedArray guards that the array's buffer is not detached. |
| transient type AttachedJSTypedArray extends JSTypedArray; |
| |
| macro EnsureAttached(array: JSTypedArray): AttachedJSTypedArray |
| labels DetachedOrOutOfBounds { |
| try { |
| IsJSArrayBufferViewDetachedOrOutOfBounds(array) |
| otherwise DetachedOrOutOfBounds, NotDetachedNorOutOfBounds; |
| } label NotDetachedNorOutOfBounds { |
| return %RawDownCast<AttachedJSTypedArray>(array); |
| } |
| } |
| |
| struct AttachedJSTypedArrayAndLength { |
| array: AttachedJSTypedArray; |
| length: uintptr; |
| } |
| |
| macro EnsureAttachedAndReadLength(array: JSTypedArray): |
| AttachedJSTypedArrayAndLength |
| labels DetachedOrOutOfBounds { |
| const length = LoadJSTypedArrayLengthAndCheckDetached(array) |
| otherwise DetachedOrOutOfBounds; |
| return AttachedJSTypedArrayAndLength{ |
| array: %RawDownCast<AttachedJSTypedArray>(array), |
| length: length |
| }; |
| } |
| |
| struct AttachedJSTypedArrayWitness { |
| macro GetStable(): JSTypedArray { |
| return this.stable; |
| } |
| |
| macro RecheckIndex(index: uintptr): void labels DetachedOrOutOfBounds { |
| const length = LoadJSTypedArrayLengthAndCheckDetached(this.stable) |
| otherwise DetachedOrOutOfBounds; |
| if (index >= length) { |
| goto DetachedOrOutOfBounds; |
| } |
| this.unstable = %RawDownCast<AttachedJSTypedArray>(this.stable); |
| } |
| |
| macro Load(implicit context: Context)(k: uintptr): JSAny { |
| const lf: LoadNumericFn = this.loadfn; |
| return lf(this.unstable, k); |
| } |
| |
| stable: JSTypedArray; |
| unstable: AttachedJSTypedArray; |
| loadfn: LoadNumericFn; |
| } |
| |
| macro NewAttachedJSTypedArrayWitness(array: AttachedJSTypedArray): |
| AttachedJSTypedArrayWitness { |
| const kind = array.elements_kind; |
| const accessor: TypedArrayAccessor = GetTypedArrayAccessor(kind); |
| return AttachedJSTypedArrayWitness{ |
| stable: array, |
| unstable: array, |
| loadfn: accessor.loadNumericFn |
| }; |
| } |
| |
| macro KindForArrayType<T : type extends ElementsKind>(): |
| constexpr ElementsKind; |
| KindForArrayType<Uint8Elements>(): constexpr ElementsKind { |
| return ElementsKind::UINT8_ELEMENTS; |
| } |
| KindForArrayType<Int8Elements>(): constexpr ElementsKind { |
| return ElementsKind::INT8_ELEMENTS; |
| } |
| KindForArrayType<Uint16Elements>(): constexpr ElementsKind { |
| return ElementsKind::UINT16_ELEMENTS; |
| } |
| KindForArrayType<Int16Elements>(): constexpr ElementsKind { |
| return ElementsKind::INT16_ELEMENTS; |
| } |
| KindForArrayType<Uint32Elements>(): constexpr ElementsKind { |
| return ElementsKind::UINT32_ELEMENTS; |
| } |
| KindForArrayType<Int32Elements>(): constexpr ElementsKind { |
| return ElementsKind::INT32_ELEMENTS; |
| } |
| KindForArrayType<Float16Elements>(): constexpr ElementsKind { |
| return ElementsKind::FLOAT16_ELEMENTS; |
| } |
| KindForArrayType<Float32Elements>(): constexpr ElementsKind { |
| return ElementsKind::FLOAT32_ELEMENTS; |
| } |
| KindForArrayType<Float64Elements>(): constexpr ElementsKind { |
| return ElementsKind::FLOAT64_ELEMENTS; |
| } |
| KindForArrayType<Uint8ClampedElements>(): constexpr ElementsKind { |
| return ElementsKind::UINT8_CLAMPED_ELEMENTS; |
| } |
| KindForArrayType<BigUint64Elements>(): constexpr ElementsKind { |
| return ElementsKind::BIGUINT64_ELEMENTS; |
| } |
| KindForArrayType<BigInt64Elements>(): constexpr ElementsKind { |
| return ElementsKind::BIGINT64_ELEMENTS; |
| } |
| |
| builtin LoadTypedElement<T : type extends ElementsKind>( |
| array: JSTypedArray, index: uintptr): Numeric { |
| return LoadFixedTypedArrayElementAsTagged( |
| array.data_ptr, index, KindForArrayType<T>()); |
| } |
| |
| builtin StoreTypedElementNumeric<T : type extends ElementsKind>( |
| context: Context, typedArray: JSTypedArray, index: uintptr, |
| value: Numeric): Smi { |
| StoreJSTypedArrayElementFromNumeric( |
| context, typedArray, index, value, KindForArrayType<T>()); |
| return kStoreSucceded; |
| } |
| |
| // Returns True on success or False if the typedArrays was detached. |
| builtin StoreTypedElementJSAny<T : type extends ElementsKind>( |
| context: Context, typedArray: JSTypedArray, index: uintptr, |
| value: JSAny): Smi { |
| try { |
| StoreJSTypedArrayElementFromTagged( |
| context, typedArray, index, value, KindForArrayType<T>()) |
| otherwise IfDetachedOrOutOfBounds; |
| } label IfDetachedOrOutOfBounds { |
| return kStoreFailureArrayDetachedOrOutOfBounds; |
| } |
| return kStoreSucceded; |
| } |
| } |