// 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 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 { if (length > kTypedArrayMaxLength) goto IfInvalid; const maxArrayLength = kArrayBufferMaxByteLength >>> 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 labels IfInvalid { const length = byteLength >>> this.sizeLog2; if (length > kTypedArrayMaxLength) goto IfInvalid; return length; } // 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 kStoreFailureArrayDetached: 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 StoreJSAny( context: Context, array: JSTypedArray, index: uintptr, value: JSAny): void labels IfDetached { const storefn: StoreJSAnyFn = this.storeJSAnyFn; const result = storefn(context, array, index, value); if (result == kStoreFailureArrayDetached) { goto IfDetached; } dcheck(result == kStoreSucceded); } 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::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<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 IfDetached; } label IfDetached { return kStoreFailureArrayDetached; } return kStoreSucceded; } }