1// Copyright 2018 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include 'src/builtins/builtins-typed-array-gen.h'
6
7namespace typed_array {
8// Naming convention from elements.cc. We have a similar intent but implement
9// fastpaths using generics instead of using a class hierarchy for elements
10// kinds specific implementations.
11type Uint8Elements extends ElementsKind;
12type Int8Elements extends ElementsKind;
13type Uint16Elements extends ElementsKind;
14type Int16Elements extends ElementsKind;
15type Uint32Elements extends ElementsKind;
16type Int32Elements extends ElementsKind;
17type Float32Elements extends ElementsKind;
18type Float64Elements extends ElementsKind;
19type Uint8ClampedElements extends ElementsKind;
20type BigUint64Elements extends ElementsKind;
21type BigInt64Elements extends ElementsKind;
22type RabGsabUint8Elements extends ElementsKind;
23
24@export
25struct TypedArrayElementsInfo {
26  // Calculates the number of bytes required for specified number of elements.
27  macro CalculateByteLength(length: uintptr): uintptr labels IfInvalid {
28    if (length > kTypedArrayMaxLength) goto IfInvalid;
29    const maxArrayLength = kArrayBufferMaxByteLength >>> this.sizeLog2;
30    if (length > maxArrayLength) goto IfInvalid;
31    const byteLength = length << this.sizeLog2;
32    return byteLength;
33  }
34
35  // Calculates the maximum number of elements supported by a specified number
36  // of bytes.
37  macro CalculateLength(byteLength: uintptr): uintptr labels IfInvalid {
38    const length = byteLength >>> this.sizeLog2;
39    if (length > kTypedArrayMaxLength) goto IfInvalid;
40    return length;
41  }
42
43  // Determines if `bytes` (byte offset or length) cannot be evenly divided by
44  // element size.
45  macro IsUnaligned(bytes: uintptr): bool {
46    // Exploits the fact the element size is a power of 2. Determining whether
47    // there is remainder (not aligned) can be achieved efficiently with bit
48    // masking. Shift is safe as sizeLog2 can be 3 at most (see
49    // ElementsKindToShiftSize).
50    return (bytes & ((1 << this.sizeLog2) - 1)) != 0;
51  }
52
53  sizeLog2: uintptr;
54  kind: ElementsKind;
55}
56extern runtime TypedArrayCopyElements(
57    Context, JSTypedArray, Object, Number): void;
58extern macro TypedArrayBuiltinsAssembler::ValidateTypedArray(
59    Context, JSAny, constexpr string): JSTypedArray;
60extern macro TypedArrayBuiltinsAssembler::ValidateTypedArrayAndGetLength(
61    Context, JSAny, constexpr string): uintptr;
62
63extern macro TypedArrayBuiltinsAssembler::CallCMemcpy(
64    RawPtr, RawPtr, uintptr): void;
65extern macro TypedArrayBuiltinsAssembler::CallCMemmove(
66    RawPtr, RawPtr, uintptr): void;
67extern macro TypedArrayBuiltinsAssembler::CallCMemset(
68    RawPtr, intptr, uintptr): void;
69extern macro TypedArrayBuiltinsAssembler::CallCRelaxedMemcpy(
70    RawPtr, RawPtr, uintptr): void;
71extern macro TypedArrayBuiltinsAssembler::CallCRelaxedMemmove(
72    RawPtr, RawPtr, uintptr): void;
73extern macro GetTypedArrayBuffer(implicit context: Context)(JSTypedArray):
74    JSArrayBuffer;
75extern macro TypedArrayBuiltinsAssembler::GetTypedArrayElementsInfo(
76    JSTypedArray): TypedArrayElementsInfo;
77extern macro TypedArrayBuiltinsAssembler::GetTypedArrayElementsInfo(Map):
78    TypedArrayElementsInfo;
79extern macro TypedArrayBuiltinsAssembler::IsUint8ElementsKind(ElementsKind):
80    bool;
81extern macro TypedArrayBuiltinsAssembler::IsBigInt64ElementsKind(ElementsKind):
82    bool;
83extern macro LoadFixedTypedArrayElementAsTagged(
84    RawPtr, uintptr, constexpr ElementsKind): Numeric;
85extern macro TypedArrayBuiltinsAssembler::StoreJSTypedArrayElementFromNumeric(
86    Context, JSTypedArray, uintptr, Numeric, constexpr ElementsKind): void;
87extern macro TypedArrayBuiltinsAssembler::StoreJSTypedArrayElementFromTagged(
88    Context, JSTypedArray, uintptr, JSAny,
89    constexpr ElementsKind): void labels IfDetached;
90
91extern macro LoadJSTypedArrayLengthAndCheckDetached(JSTypedArray): uintptr
92    labels IfDetached;
93
94type LoadNumericFn = builtin(JSTypedArray, uintptr) => Numeric;
95type StoreNumericFn = builtin(Context, JSTypedArray, uintptr, Numeric) => Smi;
96type StoreJSAnyFn = builtin(Context, JSTypedArray, uintptr, JSAny) => Smi;
97
98// The result codes returned by StoreNumericFn and StoreJSAnyFn builtins.
99const kStoreSucceded: Smi = 0;
100const kStoreFailureArrayDetached: Smi = 1;
101
102struct TypedArrayAccessor {
103  macro LoadNumeric(array: JSTypedArray, index: uintptr): Numeric {
104    const loadfn: LoadNumericFn = this.loadNumericFn;
105    return loadfn(array, index);
106  }
107
108  macro StoreNumeric(
109      context: Context, array: JSTypedArray, index: uintptr,
110      value: Numeric): void {
111    const storefn: StoreNumericFn = this.storeNumericFn;
112    const result = storefn(context, array, index, value);
113    dcheck(result == kStoreSucceded);
114  }
115
116  macro StoreJSAny(
117      context: Context, array: JSTypedArray, index: uintptr,
118      value: JSAny): void labels IfDetached {
119    const storefn: StoreJSAnyFn = this.storeJSAnyFn;
120    const result = storefn(context, array, index, value);
121    if (result == kStoreFailureArrayDetached) {
122      goto IfDetached;
123    }
124    dcheck(result == kStoreSucceded);
125  }
126
127  loadNumericFn: LoadNumericFn;
128  storeNumericFn: StoreNumericFn;
129  storeJSAnyFn: StoreJSAnyFn;
130}
131
132macro GetTypedArrayAccessor<T : type extends ElementsKind>():
133    TypedArrayAccessor {
134  const loadNumericFn = LoadTypedElement<T>;
135  const storeNumericFn = StoreTypedElementNumeric<T>;
136  const storeJSAnyFn = StoreTypedElementJSAny<T>;
137  return TypedArrayAccessor{loadNumericFn, storeNumericFn, storeJSAnyFn};
138}
139
140macro GetTypedArrayAccessor(elementsKindParam: ElementsKind):
141    TypedArrayAccessor {
142  let elementsKind = elementsKindParam;
143  if (IsElementsKindGreaterThanOrEqual(
144          elementsKind, kFirstRabGsabFixedTypedArrayElementsKind)) {
145    elementsKind = %RawDownCast<ElementsKind>(
146        elementsKind - kFirstRabGsabFixedTypedArrayElementsKind +
147        kFirstFixedTypedArrayElementsKind);
148  }
149  if (IsElementsKindGreaterThan(elementsKind, ElementsKind::UINT32_ELEMENTS)) {
150    if (elementsKind == ElementsKind::INT32_ELEMENTS) {
151      return GetTypedArrayAccessor<Int32Elements>();
152    } else if (elementsKind == ElementsKind::FLOAT32_ELEMENTS) {
153      return GetTypedArrayAccessor<Float32Elements>();
154    } else if (elementsKind == ElementsKind::FLOAT64_ELEMENTS) {
155      return GetTypedArrayAccessor<Float64Elements>();
156    } else if (elementsKind == ElementsKind::UINT8_CLAMPED_ELEMENTS) {
157      return GetTypedArrayAccessor<Uint8ClampedElements>();
158    } else if (elementsKind == ElementsKind::BIGUINT64_ELEMENTS) {
159      return GetTypedArrayAccessor<BigUint64Elements>();
160    } else if (elementsKind == ElementsKind::BIGINT64_ELEMENTS) {
161      return GetTypedArrayAccessor<BigInt64Elements>();
162    }
163  } else {
164    if (elementsKind == ElementsKind::UINT8_ELEMENTS) {
165      return GetTypedArrayAccessor<Uint8Elements>();
166    } else if (elementsKind == ElementsKind::INT8_ELEMENTS) {
167      return GetTypedArrayAccessor<Int8Elements>();
168    } else if (elementsKind == ElementsKind::UINT16_ELEMENTS) {
169      return GetTypedArrayAccessor<Uint16Elements>();
170    } else if (elementsKind == ElementsKind::INT16_ELEMENTS) {
171      return GetTypedArrayAccessor<Int16Elements>();
172    } else if (elementsKind == ElementsKind::UINT32_ELEMENTS) {
173      return GetTypedArrayAccessor<Uint32Elements>();
174    }
175  }
176  unreachable;
177}
178
179extern macro TypedArrayBuiltinsAssembler::SetJSTypedArrayOnHeapDataPtr(
180    JSTypedArray, ByteArray, uintptr): void;
181extern macro TypedArrayBuiltinsAssembler::SetJSTypedArrayOffHeapDataPtr(
182    JSTypedArray, RawPtr, uintptr): void;
183extern macro IsJSTypedArrayDetachedOrOutOfBounds(JSTypedArray):
184    never labels Detached, NotDetached;
185
186// AttachedJSTypedArray guards that the array's buffer is not detached.
187transient type AttachedJSTypedArray extends JSTypedArray;
188
189macro EnsureAttached(array: JSTypedArray): AttachedJSTypedArray
190    labels Detached {
191  try {
192    IsJSTypedArrayDetachedOrOutOfBounds(array) otherwise Detached, NotDetached;
193  } label NotDetached {
194    return %RawDownCast<AttachedJSTypedArray>(array);
195  }
196}
197
198struct AttachedJSTypedArrayWitness {
199  macro Get(): AttachedJSTypedArray {
200    return this.unstable;
201  }
202
203  macro GetStable(): JSTypedArray {
204    return this.stable;
205  }
206
207  // TODO(v8:11111): Migrate users to use RecheckIndex.
208  macro Recheck(): void labels Detached {
209    if (IsDetachedBuffer(this.stable.buffer)) goto Detached;
210    this.unstable = %RawDownCast<AttachedJSTypedArray>(this.stable);
211  }
212
213  macro RecheckIndex(index: uintptr): void labels DetachedOrOutOfBounds {
214    const length = LoadJSTypedArrayLengthAndCheckDetached(this.stable)
215        otherwise DetachedOrOutOfBounds;
216    if (index >= length) {
217      goto DetachedOrOutOfBounds;
218    }
219    this.unstable = %RawDownCast<AttachedJSTypedArray>(this.stable);
220  }
221
222  macro Load(implicit context: Context)(k: uintptr): JSAny {
223    const lf: LoadNumericFn = this.loadfn;
224    return lf(this.unstable, k);
225  }
226
227  stable: JSTypedArray;
228  unstable: AttachedJSTypedArray;
229  loadfn: LoadNumericFn;
230}
231
232macro NewAttachedJSTypedArrayWitness(array: AttachedJSTypedArray):
233    AttachedJSTypedArrayWitness {
234  const kind = array.elements_kind;
235  const accessor: TypedArrayAccessor = GetTypedArrayAccessor(kind);
236  return AttachedJSTypedArrayWitness{
237    stable: array,
238    unstable: array,
239    loadfn: accessor.loadNumericFn
240  };
241}
242
243macro KindForArrayType<T : type extends ElementsKind>(): constexpr ElementsKind;
244KindForArrayType<Uint8Elements>(): constexpr ElementsKind {
245  return ElementsKind::UINT8_ELEMENTS;
246}
247KindForArrayType<Int8Elements>(): constexpr ElementsKind {
248  return ElementsKind::INT8_ELEMENTS;
249}
250KindForArrayType<Uint16Elements>(): constexpr ElementsKind {
251  return ElementsKind::UINT16_ELEMENTS;
252}
253KindForArrayType<Int16Elements>(): constexpr ElementsKind {
254  return ElementsKind::INT16_ELEMENTS;
255}
256KindForArrayType<Uint32Elements>(): constexpr ElementsKind {
257  return ElementsKind::UINT32_ELEMENTS;
258}
259KindForArrayType<Int32Elements>(): constexpr ElementsKind {
260  return ElementsKind::INT32_ELEMENTS;
261}
262KindForArrayType<Float32Elements>(): constexpr ElementsKind {
263  return ElementsKind::FLOAT32_ELEMENTS;
264}
265KindForArrayType<Float64Elements>(): constexpr ElementsKind {
266  return ElementsKind::FLOAT64_ELEMENTS;
267}
268KindForArrayType<Uint8ClampedElements>(): constexpr ElementsKind {
269  return ElementsKind::UINT8_CLAMPED_ELEMENTS;
270}
271KindForArrayType<BigUint64Elements>(): constexpr ElementsKind {
272  return ElementsKind::BIGUINT64_ELEMENTS;
273}
274KindForArrayType<BigInt64Elements>(): constexpr ElementsKind {
275  return ElementsKind::BIGINT64_ELEMENTS;
276}
277
278builtin LoadTypedElement<T : type extends ElementsKind>(
279    array: JSTypedArray, index: uintptr): Numeric {
280  return LoadFixedTypedArrayElementAsTagged(
281      array.data_ptr, index, KindForArrayType<T>());
282}
283
284builtin StoreTypedElementNumeric<T : type extends ElementsKind>(
285    context: Context, typedArray: JSTypedArray, index: uintptr,
286    value: Numeric): Smi {
287  StoreJSTypedArrayElementFromNumeric(
288      context, typedArray, index, value, KindForArrayType<T>());
289  return kStoreSucceded;
290}
291
292// Returns True on sucess or False if the typedArrays was detached.
293builtin StoreTypedElementJSAny<T : type extends ElementsKind>(
294    context: Context, typedArray: JSTypedArray, index: uintptr,
295    value: JSAny): Smi {
296  try {
297    StoreJSTypedArrayElementFromTagged(
298        context, typedArray, index, value, KindForArrayType<T>())
299        otherwise IfDetached;
300  } label IfDetached {
301    return kStoreFailureArrayDetached;
302  }
303  return kStoreSucceded;
304}
305}
306