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