1// Copyright 2017 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-utils-gen.h"
6#include "src/builtins/builtins.h"
7#include "src/codegen/code-stub-assembler.h"
8#include "src/objects/objects.h"
9
10namespace v8 {
11namespace internal {
12
13class SharedArrayBufferBuiltinsAssembler : public CodeStubAssembler {
14 public:
15  explicit SharedArrayBufferBuiltinsAssembler(
16      compiler::CodeAssemblerState* state)
17      : CodeStubAssembler(state) {}
18
19 protected:
20  using AssemblerFunction = TNode<Word32T> (CodeAssembler::*)(
21      MachineType type, TNode<RawPtrT> base, TNode<UintPtrT> offset,
22      TNode<Word32T> value);
23  template <class Type>
24  using AssemblerFunction64 = TNode<Type> (CodeAssembler::*)(
25      TNode<RawPtrT> base, TNode<UintPtrT> offset, TNode<UintPtrT> value,
26      TNode<UintPtrT> value_high);
27  TNode<JSArrayBuffer> ValidateIntegerTypedArray(
28      TNode<Object> maybe_array, TNode<Context> context,
29      TNode<Int32T>* out_elements_kind, TNode<RawPtrT>* out_backing_store,
30      Label* detached);
31
32  TNode<UintPtrT> ValidateAtomicAccess(TNode<JSTypedArray> array,
33                                       TNode<Object> index,
34                                       TNode<Context> context);
35
36  inline void DebugCheckAtomicIndex(TNode<JSTypedArray> array,
37                                    TNode<UintPtrT> index);
38
39  void AtomicBinopBuiltinCommon(
40      TNode<Object> maybe_array, TNode<Object> index, TNode<Object> value,
41      TNode<Context> context, AssemblerFunction function,
42      AssemblerFunction64<AtomicInt64> function_int_64,
43      AssemblerFunction64<AtomicUint64> function_uint_64,
44      Runtime::FunctionId runtime_function, const char* method_name);
45
46  // Create a BigInt from the result of a 64-bit atomic operation, using
47  // projections on 32-bit platforms.
48  TNode<BigInt> BigIntFromSigned64(TNode<AtomicInt64> signed64);
49  TNode<BigInt> BigIntFromUnsigned64(TNode<AtomicUint64> unsigned64);
50};
51
52// https://tc39.es/ecma262/#sec-validateintegertypedarray
53TNode<JSArrayBuffer>
54SharedArrayBufferBuiltinsAssembler::ValidateIntegerTypedArray(
55    TNode<Object> maybe_array, TNode<Context> context,
56    TNode<Int32T>* out_elements_kind, TNode<RawPtrT>* out_backing_store,
57    Label* detached) {
58  Label not_float_or_clamped(this), invalid(this);
59
60  // The logic of TypedArrayBuiltinsAssembler::ValidateTypedArrayBuffer is
61  // inlined to avoid duplicate error branches.
62
63  // Fail if it is not a heap object.
64  GotoIf(TaggedIsSmi(maybe_array), &invalid);
65
66  // Fail if the array's instance type is not JSTypedArray.
67  TNode<Map> map = LoadMap(CAST(maybe_array));
68  GotoIfNot(IsJSTypedArrayMap(map), &invalid);
69  TNode<JSTypedArray> array = CAST(maybe_array);
70
71  // Fail if the array's JSArrayBuffer is detached.
72  TNode<JSArrayBuffer> array_buffer = GetTypedArrayBuffer(context, array);
73  GotoIf(IsDetachedBuffer(array_buffer), detached);
74
75  // Fail if the array's element type is float32, float64 or clamped.
76  STATIC_ASSERT(INT8_ELEMENTS < FLOAT32_ELEMENTS);
77  STATIC_ASSERT(INT16_ELEMENTS < FLOAT32_ELEMENTS);
78  STATIC_ASSERT(INT32_ELEMENTS < FLOAT32_ELEMENTS);
79  STATIC_ASSERT(UINT8_ELEMENTS < FLOAT32_ELEMENTS);
80  STATIC_ASSERT(UINT16_ELEMENTS < FLOAT32_ELEMENTS);
81  STATIC_ASSERT(UINT32_ELEMENTS < FLOAT32_ELEMENTS);
82  TNode<Int32T> elements_kind = LoadMapElementsKind(map);
83  GotoIf(Int32LessThan(elements_kind, Int32Constant(FLOAT32_ELEMENTS)),
84         &not_float_or_clamped);
85  STATIC_ASSERT(BIGINT64_ELEMENTS > UINT8_CLAMPED_ELEMENTS);
86  STATIC_ASSERT(BIGUINT64_ELEMENTS > UINT8_CLAMPED_ELEMENTS);
87  Branch(Int32GreaterThan(elements_kind, Int32Constant(UINT8_CLAMPED_ELEMENTS)),
88         &not_float_or_clamped, &invalid);
89
90  BIND(&invalid);
91  {
92    ThrowTypeError(context, MessageTemplate::kNotIntegerTypedArray,
93                   maybe_array);
94  }
95
96  BIND(&not_float_or_clamped);
97  *out_elements_kind = elements_kind;
98
99  TNode<RawPtrT> backing_store = LoadJSArrayBufferBackingStorePtr(array_buffer);
100  TNode<UintPtrT> byte_offset = LoadJSArrayBufferViewByteOffset(array);
101  *out_backing_store = RawPtrAdd(backing_store, Signed(byte_offset));
102
103  return array_buffer;
104}
105
106// https://tc39.github.io/ecma262/#sec-validateatomicaccess
107// ValidateAtomicAccess( typedArray, requestIndex )
108TNode<UintPtrT> SharedArrayBufferBuiltinsAssembler::ValidateAtomicAccess(
109    TNode<JSTypedArray> array, TNode<Object> index, TNode<Context> context) {
110  Label done(this), range_error(this);
111  // TODO(v8:11111): Support RAB / GSAB.
112
113  // 1. Assert: typedArray is an Object that has a [[ViewedArrayBuffer]]
114  // internal slot.
115  // 2. Let length be typedArray.[[ArrayLength]].
116  TNode<UintPtrT> array_length = LoadJSTypedArrayLength(array);
117
118  // 3. Let accessIndex be ? ToIndex(requestIndex).
119  TNode<UintPtrT> index_uintptr = ToIndex(context, index, &range_error);
120
121  // 4. Assert: accessIndex ≥ 0.
122  // 5. If accessIndex ≥ length, throw a RangeError exception.
123  Branch(UintPtrLessThan(index_uintptr, array_length), &done, &range_error);
124
125  BIND(&range_error);
126  ThrowRangeError(context, MessageTemplate::kInvalidAtomicAccessIndex);
127
128  // 6. Return accessIndex.
129  BIND(&done);
130  return index_uintptr;
131}
132
133void SharedArrayBufferBuiltinsAssembler::DebugCheckAtomicIndex(
134    TNode<JSTypedArray> array, TNode<UintPtrT> index) {
135  // In Debug mode, we re-validate the index as a sanity check because ToInteger
136  // above calls out to JavaScript. Atomics work on ArrayBuffers, which may be
137  // detached, and detachment state must be checked and throw before this
138  // check. The length cannot change.
139  //
140  // This function must always be called after ValidateIntegerTypedArray, which
141  // will ensure that LoadJSArrayBufferViewBuffer will not be null.
142  CSA_DCHECK(this, Word32BinaryNot(
143                       IsDetachedBuffer(LoadJSArrayBufferViewBuffer(array))));
144  CSA_DCHECK(this, UintPtrLessThan(index, LoadJSTypedArrayLength(array)));
145}
146
147TNode<BigInt> SharedArrayBufferBuiltinsAssembler::BigIntFromSigned64(
148    TNode<AtomicInt64> signed64) {
149#if defined(V8_HOST_ARCH_32_BIT)
150  TNode<IntPtrT> low = Projection<0>(signed64);
151  TNode<IntPtrT> high = Projection<1>(signed64);
152  return BigIntFromInt32Pair(low, high);
153#else
154  return BigIntFromInt64(signed64);
155#endif
156}
157
158TNode<BigInt> SharedArrayBufferBuiltinsAssembler::BigIntFromUnsigned64(
159    TNode<AtomicUint64> unsigned64) {
160#if defined(V8_HOST_ARCH_32_BIT)
161  TNode<UintPtrT> low = Projection<0>(unsigned64);
162  TNode<UintPtrT> high = Projection<1>(unsigned64);
163  return BigIntFromUint32Pair(low, high);
164#else
165  return BigIntFromUint64(unsigned64);
166#endif
167}
168
169// https://tc39.es/ecma262/#sec-atomicload
170TF_BUILTIN(AtomicsLoad, SharedArrayBufferBuiltinsAssembler) {
171  auto maybe_array_or_shared_struct =
172      Parameter<Object>(Descriptor::kArrayOrSharedStruct);
173  auto index_or_field_name = Parameter<Object>(Descriptor::kIndexOrFieldName);
174  auto context = Parameter<Context>(Descriptor::kContext);
175
176  Label shared_struct(this);
177  GotoIf(IsJSSharedStruct(maybe_array_or_shared_struct), &shared_struct);
178
179  // 1. Let buffer be ? ValidateIntegerTypedArray(typedArray).
180  Label detached(this);
181  TNode<Int32T> elements_kind;
182  TNode<RawPtrT> backing_store;
183  TNode<JSArrayBuffer> array_buffer =
184      ValidateIntegerTypedArray(maybe_array_or_shared_struct, context,
185                                &elements_kind, &backing_store, &detached);
186  TNode<JSTypedArray> array = CAST(maybe_array_or_shared_struct);
187
188  // 2. Let i be ? ValidateAtomicAccess(typedArray, index).
189  TNode<UintPtrT> index_word =
190      ValidateAtomicAccess(array, index_or_field_name, context);
191
192  // 3. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
193  // 4. NOTE: The above check is not redundant with the check in
194  // ValidateIntegerTypedArray because the call to ValidateAtomicAccess on the
195  // preceding line can have arbitrary side effects, which could cause the
196  // buffer to become detached.
197  GotoIf(IsDetachedBuffer(array_buffer), &detached);
198
199  // Steps 5-10.
200  //
201  // (Not copied from ecma262 due to the axiomatic nature of the memory model.)
202  Label i8(this), u8(this), i16(this), u16(this), i32(this), u32(this),
203      i64(this), u64(this), other(this);
204  int32_t case_values[] = {
205      INT8_ELEMENTS,  UINT8_ELEMENTS,  INT16_ELEMENTS,    UINT16_ELEMENTS,
206      INT32_ELEMENTS, UINT32_ELEMENTS, BIGINT64_ELEMENTS, BIGUINT64_ELEMENTS,
207  };
208  Label* case_labels[] = {&i8, &u8, &i16, &u16, &i32, &u32, &i64, &u64};
209  Switch(elements_kind, &other, case_values, case_labels,
210         arraysize(case_labels));
211
212  BIND(&i8);
213  Return(SmiFromInt32(AtomicLoad<Int8T>(AtomicMemoryOrder::kSeqCst,
214                                        backing_store, index_word)));
215
216  BIND(&u8);
217  Return(SmiFromInt32(AtomicLoad<Uint8T>(AtomicMemoryOrder::kSeqCst,
218                                         backing_store, index_word)));
219
220  BIND(&i16);
221  Return(SmiFromInt32(AtomicLoad<Int16T>(
222      AtomicMemoryOrder::kSeqCst, backing_store, WordShl(index_word, 1))));
223
224  BIND(&u16);
225  Return(SmiFromInt32(AtomicLoad<Uint16T>(
226      AtomicMemoryOrder::kSeqCst, backing_store, WordShl(index_word, 1))));
227
228  BIND(&i32);
229  Return(ChangeInt32ToTagged(AtomicLoad<Int32T>(
230      AtomicMemoryOrder::kSeqCst, backing_store, WordShl(index_word, 2))));
231
232  BIND(&u32);
233  Return(ChangeUint32ToTagged(AtomicLoad<Uint32T>(
234      AtomicMemoryOrder::kSeqCst, backing_store, WordShl(index_word, 2))));
235#if V8_TARGET_ARCH_MIPS && !_MIPS_ARCH_MIPS32R6
236  BIND(&i64);
237  Goto(&u64);
238
239  BIND(&u64);
240  {
241    TNode<Number> index_number = ChangeUintPtrToTagged(index_word);
242    Return(CallRuntime(Runtime::kAtomicsLoad64, context, array, index_number));
243  }
244#else
245  BIND(&i64);
246  Return(BigIntFromSigned64(AtomicLoad64<AtomicInt64>(
247      AtomicMemoryOrder::kSeqCst, backing_store, WordShl(index_word, 3))));
248
249  BIND(&u64);
250  Return(BigIntFromUnsigned64(AtomicLoad64<AtomicUint64>(
251      AtomicMemoryOrder::kSeqCst, backing_store, WordShl(index_word, 3))));
252#endif
253
254  // This shouldn't happen, we've already validated the type.
255  BIND(&other);
256  Unreachable();
257
258  BIND(&detached);
259  {
260    ThrowTypeError(context, MessageTemplate::kDetachedOperation,
261                   "Atomics.load");
262  }
263
264  BIND(&shared_struct);
265  {
266    Return(CallRuntime(Runtime::kAtomicsLoadSharedStructField, context,
267                       maybe_array_or_shared_struct, index_or_field_name));
268  }
269}
270
271// https://tc39.es/ecma262/#sec-atomics.store
272TF_BUILTIN(AtomicsStore, SharedArrayBufferBuiltinsAssembler) {
273  auto maybe_array_or_shared_struct =
274      Parameter<Object>(Descriptor::kArrayOrSharedStruct);
275  auto index_or_field_name = Parameter<Object>(Descriptor::kIndexOrFieldName);
276  auto value = Parameter<Object>(Descriptor::kValue);
277  auto context = Parameter<Context>(Descriptor::kContext);
278
279  Label shared_struct(this);
280  GotoIf(IsJSSharedStruct(maybe_array_or_shared_struct), &shared_struct);
281
282  // 1. Let buffer be ? ValidateIntegerTypedArray(typedArray).
283  Label detached(this);
284  TNode<Int32T> elements_kind;
285  TNode<RawPtrT> backing_store;
286  TNode<JSArrayBuffer> array_buffer =
287      ValidateIntegerTypedArray(maybe_array_or_shared_struct, context,
288                                &elements_kind, &backing_store, &detached);
289  TNode<JSTypedArray> array = CAST(maybe_array_or_shared_struct);
290
291  // 2. Let i be ? ValidateAtomicAccess(typedArray, index).
292  TNode<UintPtrT> index_word =
293      ValidateAtomicAccess(array, index_or_field_name, context);
294
295  Label u8(this), u16(this), u32(this), u64(this), other(this);
296
297  // 3. Let arrayTypeName be typedArray.[[TypedArrayName]].
298  // 4. If arrayTypeName is "BigUint64Array" or "BigInt64Array",
299  //    let v be ? ToBigInt(value).
300  STATIC_ASSERT(BIGINT64_ELEMENTS > INT32_ELEMENTS);
301  STATIC_ASSERT(BIGUINT64_ELEMENTS > INT32_ELEMENTS);
302  GotoIf(Int32GreaterThan(elements_kind, Int32Constant(INT32_ELEMENTS)), &u64);
303
304  // 5. Otherwise, let v be ? ToInteger(value).
305  TNode<Number> value_integer = ToInteger_Inline(context, value);
306
307  // 6. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
308  // 7. NOTE: The above check is not redundant with the check in
309  // ValidateIntegerTypedArray because the call to ToBigInt or ToInteger on the
310  // preceding lines can have arbitrary side effects, which could cause the
311  // buffer to become detached.
312  GotoIf(IsDetachedBuffer(array_buffer), &detached);
313
314  TNode<Word32T> value_word32 = TruncateTaggedToWord32(context, value_integer);
315
316  DebugCheckAtomicIndex(array, index_word);
317
318  // Steps 8-13.
319  //
320  // (Not copied from ecma262 due to the axiomatic nature of the memory model.)
321  int32_t case_values[] = {
322      INT8_ELEMENTS,   UINT8_ELEMENTS, INT16_ELEMENTS,
323      UINT16_ELEMENTS, INT32_ELEMENTS, UINT32_ELEMENTS,
324  };
325  Label* case_labels[] = {&u8, &u8, &u16, &u16, &u32, &u32};
326  Switch(elements_kind, &other, case_values, case_labels,
327         arraysize(case_labels));
328
329  BIND(&u8);
330  AtomicStore(MachineRepresentation::kWord8, AtomicMemoryOrder::kSeqCst,
331              backing_store, index_word, value_word32);
332  Return(value_integer);
333
334  BIND(&u16);
335  AtomicStore(MachineRepresentation::kWord16, AtomicMemoryOrder::kSeqCst,
336              backing_store, WordShl(index_word, 1), value_word32);
337  Return(value_integer);
338
339  BIND(&u32);
340  AtomicStore(MachineRepresentation::kWord32, AtomicMemoryOrder::kSeqCst,
341              backing_store, WordShl(index_word, 2), value_word32);
342  Return(value_integer);
343
344  BIND(&u64);
345#if V8_TARGET_ARCH_MIPS && !_MIPS_ARCH_MIPS32R6
346  TNode<Number> index_number = ChangeUintPtrToTagged(index_word);
347  Return(CallRuntime(Runtime::kAtomicsStore64, context, array, index_number,
348                     value));
349#else
350  // 4. If arrayTypeName is "BigUint64Array" or "BigInt64Array",
351  //    let v be ? ToBigInt(value).
352  TNode<BigInt> value_bigint = ToBigInt(context, value);
353
354  // 6. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
355  GotoIf(IsDetachedBuffer(array_buffer), &detached);
356
357  DebugCheckAtomicIndex(array, index_word);
358
359  TVARIABLE(UintPtrT, var_low);
360  TVARIABLE(UintPtrT, var_high);
361  BigIntToRawBytes(value_bigint, &var_low, &var_high);
362  TNode<UintPtrT> high = Is64() ? TNode<UintPtrT>() : var_high.value();
363  AtomicStore64(AtomicMemoryOrder::kSeqCst, backing_store,
364                WordShl(index_word, 3), var_low.value(), high);
365  Return(value_bigint);
366#endif
367
368  // This shouldn't happen, we've already validated the type.
369  BIND(&other);
370  Unreachable();
371
372  BIND(&detached);
373  {
374    ThrowTypeError(context, MessageTemplate::kDetachedOperation,
375                   "Atomics.store");
376  }
377
378  BIND(&shared_struct);
379  {
380    Return(CallRuntime(Runtime::kAtomicsStoreSharedStructField, context,
381                       maybe_array_or_shared_struct, index_or_field_name,
382                       value));
383  }
384}
385
386// https://tc39.es/ecma262/#sec-atomics.exchange
387TF_BUILTIN(AtomicsExchange, SharedArrayBufferBuiltinsAssembler) {
388  auto maybe_array_or_shared_struct =
389      Parameter<Object>(Descriptor::kArrayOrSharedStruct);
390  auto index_or_field_name = Parameter<Object>(Descriptor::kIndexOrFieldName);
391  auto value = Parameter<Object>(Descriptor::kValue);
392  auto context = Parameter<Context>(Descriptor::kContext);
393
394  Label shared_struct(this);
395  GotoIf(IsJSSharedStruct(maybe_array_or_shared_struct), &shared_struct);
396
397  // Inlines AtomicReadModifyWrite
398  // https://tc39.es/ecma262/#sec-atomicreadmodifywrite
399
400  // 1. Let buffer be ? ValidateIntegerTypedArray(typedArray).
401  Label detached(this);
402  TNode<Int32T> elements_kind;
403  TNode<RawPtrT> backing_store;
404  TNode<JSArrayBuffer> array_buffer =
405      ValidateIntegerTypedArray(maybe_array_or_shared_struct, context,
406                                &elements_kind, &backing_store, &detached);
407  TNode<JSTypedArray> array = CAST(maybe_array_or_shared_struct);
408
409  // 2. Let i be ? ValidateAtomicAccess(typedArray, index).
410  TNode<UintPtrT> index_word =
411      ValidateAtomicAccess(array, index_or_field_name, context);
412
413#if V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64 || V8_TARGET_ARCH_RISCV64
414  USE(array_buffer);
415  TNode<Number> index_number = ChangeUintPtrToTagged(index_word);
416  Return(CallRuntime(Runtime::kAtomicsExchange, context, array, index_number,
417                     value));
418#else
419
420  Label i8(this), u8(this), i16(this), u16(this), i32(this), u32(this),
421      i64(this), u64(this), big(this), other(this);
422
423  // 3. Let arrayTypeName be typedArray.[[TypedArrayName]].
424  // 4. If typedArray.[[ContentType]] is BigInt, let v be ? ToBigInt(value).
425  STATIC_ASSERT(BIGINT64_ELEMENTS > INT32_ELEMENTS);
426  STATIC_ASSERT(BIGUINT64_ELEMENTS > INT32_ELEMENTS);
427  GotoIf(Int32GreaterThan(elements_kind, Int32Constant(INT32_ELEMENTS)), &big);
428
429  // 5. Otherwise, let v be ? ToInteger(value).
430  TNode<Number> value_integer = ToInteger_Inline(context, value);
431
432  // 6. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
433  // 7. NOTE: The above check is not redundant with the check in
434  // ValidateIntegerTypedArray because the call to ToBigInt or ToInteger on the
435  // preceding lines can have arbitrary side effects, which could cause the
436  // buffer to become detached.
437  GotoIf(IsDetachedBuffer(array_buffer), &detached);
438
439  DebugCheckAtomicIndex(array, index_word);
440
441  TNode<Word32T> value_word32 = TruncateTaggedToWord32(context, value_integer);
442
443  // Steps 8-12.
444  //
445  // (Not copied from ecma262 due to the axiomatic nature of the memory model.)
446  int32_t case_values[] = {
447      INT8_ELEMENTS,   UINT8_ELEMENTS, INT16_ELEMENTS,
448      UINT16_ELEMENTS, INT32_ELEMENTS, UINT32_ELEMENTS,
449  };
450  Label* case_labels[] = {
451      &i8, &u8, &i16, &u16, &i32, &u32,
452  };
453  Switch(elements_kind, &other, case_values, case_labels,
454         arraysize(case_labels));
455
456  BIND(&i8);
457  Return(SmiFromInt32(Signed(AtomicExchange(MachineType::Int8(), backing_store,
458                                            index_word, value_word32))));
459
460  BIND(&u8);
461  Return(SmiFromInt32(Signed(AtomicExchange(MachineType::Uint8(), backing_store,
462                                            index_word, value_word32))));
463
464  BIND(&i16);
465  Return(SmiFromInt32(Signed(
466      AtomicExchange(MachineType::Int16(), backing_store,
467                     WordShl(index_word, UintPtrConstant(1)), value_word32))));
468
469  BIND(&u16);
470  Return(SmiFromInt32(Signed(
471      AtomicExchange(MachineType::Uint16(), backing_store,
472                     WordShl(index_word, UintPtrConstant(1)), value_word32))));
473
474  BIND(&i32);
475  Return(ChangeInt32ToTagged(Signed(
476      AtomicExchange(MachineType::Int32(), backing_store,
477                     WordShl(index_word, UintPtrConstant(2)), value_word32))));
478
479  BIND(&u32);
480  Return(ChangeUint32ToTagged(Unsigned(
481      AtomicExchange(MachineType::Uint32(), backing_store,
482                     WordShl(index_word, UintPtrConstant(2)), value_word32))));
483
484  BIND(&big);
485  // 4. If typedArray.[[ContentType]] is BigInt, let v be ? ToBigInt(value).
486  TNode<BigInt> value_bigint = ToBigInt(context, value);
487
488  // 6. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
489  GotoIf(IsDetachedBuffer(array_buffer), &detached);
490
491  DebugCheckAtomicIndex(array, index_word);
492
493  TVARIABLE(UintPtrT, var_low);
494  TVARIABLE(UintPtrT, var_high);
495  BigIntToRawBytes(value_bigint, &var_low, &var_high);
496  TNode<UintPtrT> high = Is64() ? TNode<UintPtrT>() : var_high.value();
497  GotoIf(Word32Equal(elements_kind, Int32Constant(BIGINT64_ELEMENTS)), &i64);
498  GotoIf(Word32Equal(elements_kind, Int32Constant(BIGUINT64_ELEMENTS)), &u64);
499  Unreachable();
500
501  BIND(&i64);
502  Return(BigIntFromSigned64(AtomicExchange64<AtomicInt64>(
503      backing_store, WordShl(index_word, UintPtrConstant(3)), var_low.value(),
504      high)));
505
506  BIND(&u64);
507  Return(BigIntFromUnsigned64(AtomicExchange64<AtomicUint64>(
508      backing_store, WordShl(index_word, UintPtrConstant(3)), var_low.value(),
509      high)));
510
511  // This shouldn't happen, we've already validated the type.
512  BIND(&other);
513  Unreachable();
514#endif  // V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64 ||
515        // V8_TARGET_ARCH_RISCV64
516
517  BIND(&detached);
518  {
519    ThrowTypeError(context, MessageTemplate::kDetachedOperation,
520                   "Atomics.exchange");
521  }
522
523  BIND(&shared_struct);
524  {
525    Return(CallRuntime(Runtime::kAtomicsExchangeSharedStructField, context,
526                       maybe_array_or_shared_struct, index_or_field_name,
527                       value));
528  }
529}
530
531// https://tc39.es/ecma262/#sec-atomics.compareexchange
532TF_BUILTIN(AtomicsCompareExchange, SharedArrayBufferBuiltinsAssembler) {
533  auto maybe_array = Parameter<Object>(Descriptor::kArray);
534  auto index = Parameter<Object>(Descriptor::kIndex);
535  auto old_value = Parameter<Object>(Descriptor::kOldValue);
536  auto new_value = Parameter<Object>(Descriptor::kNewValue);
537  auto context = Parameter<Context>(Descriptor::kContext);
538
539  // 1. Let buffer be ? ValidateIntegerTypedArray(typedArray).
540  Label detached(this);
541  TNode<Int32T> elements_kind;
542  TNode<RawPtrT> backing_store;
543  TNode<JSArrayBuffer> array_buffer = ValidateIntegerTypedArray(
544      maybe_array, context, &elements_kind, &backing_store, &detached);
545  TNode<JSTypedArray> array = CAST(maybe_array);
546
547  // 2. Let i be ? ValidateAtomicAccess(typedArray, index).
548  TNode<UintPtrT> index_word = ValidateAtomicAccess(array, index, context);
549
550#if V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64 || V8_TARGET_ARCH_PPC64 || \
551    V8_TARGET_ARCH_PPC || V8_TARGET_ARCH_S390 || V8_TARGET_ARCH_S390X ||    \
552    V8_TARGET_ARCH_RISCV64
553  USE(array_buffer);
554  TNode<Number> index_number = ChangeUintPtrToTagged(index_word);
555  Return(CallRuntime(Runtime::kAtomicsCompareExchange, context, array,
556                     index_number, old_value, new_value));
557#else
558  Label i8(this), u8(this), i16(this), u16(this), i32(this), u32(this),
559      i64(this), u64(this), big(this), other(this);
560
561  // 3. Let arrayTypeName be typedArray.[[TypedArrayName]].
562  // 4. If typedArray.[[ContentType]] is BigInt, then
563  //   a. Let expected be ? ToBigInt(expectedValue).
564  //   b. Let replacement be ? ToBigInt(replacementValue).
565  STATIC_ASSERT(BIGINT64_ELEMENTS > INT32_ELEMENTS);
566  STATIC_ASSERT(BIGUINT64_ELEMENTS > INT32_ELEMENTS);
567  GotoIf(Int32GreaterThan(elements_kind, Int32Constant(INT32_ELEMENTS)), &big);
568
569  // 5. Else,
570  //   a. Let expected be ? ToInteger(expectedValue).
571  //   b. Let replacement be ? ToInteger(replacementValue).
572  TNode<Number> old_value_integer = ToInteger_Inline(context, old_value);
573  TNode<Number> new_value_integer = ToInteger_Inline(context, new_value);
574
575  // 6. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
576  // 7. NOTE: The above check is not redundant with the check in
577  // ValidateIntegerTypedArray because the call to ToBigInt or ToInteger on the
578  // preceding lines can have arbitrary side effects, which could cause the
579  // buffer to become detached.
580  GotoIf(IsDetachedBuffer(array_buffer), &detached);
581
582  DebugCheckAtomicIndex(array, index_word);
583
584  TNode<Word32T> old_value_word32 =
585      TruncateTaggedToWord32(context, old_value_integer);
586  TNode<Word32T> new_value_word32 =
587      TruncateTaggedToWord32(context, new_value_integer);
588
589  // Steps 8-14.
590  //
591  // (Not copied from ecma262 due to the axiomatic nature of the memory model.)
592  int32_t case_values[] = {
593      INT8_ELEMENTS,   UINT8_ELEMENTS, INT16_ELEMENTS,
594      UINT16_ELEMENTS, INT32_ELEMENTS, UINT32_ELEMENTS,
595  };
596  Label* case_labels[] = {
597      &i8, &u8, &i16, &u16, &i32, &u32,
598  };
599  Switch(elements_kind, &other, case_values, case_labels,
600         arraysize(case_labels));
601
602  BIND(&i8);
603  Return(SmiFromInt32(Signed(
604      AtomicCompareExchange(MachineType::Int8(), backing_store, index_word,
605                            old_value_word32, new_value_word32))));
606
607  BIND(&u8);
608  Return(SmiFromInt32(Signed(
609      AtomicCompareExchange(MachineType::Uint8(), backing_store, index_word,
610                            old_value_word32, new_value_word32))));
611
612  BIND(&i16);
613  Return(SmiFromInt32(Signed(AtomicCompareExchange(
614      MachineType::Int16(), backing_store, WordShl(index_word, 1),
615      old_value_word32, new_value_word32))));
616
617  BIND(&u16);
618  Return(SmiFromInt32(Signed(AtomicCompareExchange(
619      MachineType::Uint16(), backing_store, WordShl(index_word, 1),
620      old_value_word32, new_value_word32))));
621
622  BIND(&i32);
623  Return(ChangeInt32ToTagged(Signed(AtomicCompareExchange(
624      MachineType::Int32(), backing_store, WordShl(index_word, 2),
625      old_value_word32, new_value_word32))));
626
627  BIND(&u32);
628  Return(ChangeUint32ToTagged(Unsigned(AtomicCompareExchange(
629      MachineType::Uint32(), backing_store, WordShl(index_word, 2),
630      old_value_word32, new_value_word32))));
631
632  BIND(&big);
633  // 4. If typedArray.[[ContentType]] is BigInt, then
634  //   a. Let expected be ? ToBigInt(expectedValue).
635  //   b. Let replacement be ? ToBigInt(replacementValue).
636  TNode<BigInt> old_value_bigint = ToBigInt(context, old_value);
637  TNode<BigInt> new_value_bigint = ToBigInt(context, new_value);
638
639  // 6. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
640  GotoIf(IsDetachedBuffer(array_buffer), &detached);
641
642  DebugCheckAtomicIndex(array, index_word);
643
644  TVARIABLE(UintPtrT, var_old_low);
645  TVARIABLE(UintPtrT, var_old_high);
646  TVARIABLE(UintPtrT, var_new_low);
647  TVARIABLE(UintPtrT, var_new_high);
648  BigIntToRawBytes(old_value_bigint, &var_old_low, &var_old_high);
649  BigIntToRawBytes(new_value_bigint, &var_new_low, &var_new_high);
650  TNode<UintPtrT> old_high = Is64() ? TNode<UintPtrT>() : var_old_high.value();
651  TNode<UintPtrT> new_high = Is64() ? TNode<UintPtrT>() : var_new_high.value();
652  GotoIf(Word32Equal(elements_kind, Int32Constant(BIGINT64_ELEMENTS)), &i64);
653  GotoIf(Word32Equal(elements_kind, Int32Constant(BIGUINT64_ELEMENTS)), &u64);
654  Unreachable();
655
656  BIND(&i64);
657  // This uses Uint64() intentionally: AtomicCompareExchange is not implemented
658  // for Int64(), which is fine because the machine instruction only cares
659  // about words.
660  Return(BigIntFromSigned64(AtomicCompareExchange64<AtomicInt64>(
661      backing_store, WordShl(index_word, 3), var_old_low.value(),
662      var_new_low.value(), old_high, new_high)));
663
664  BIND(&u64);
665  Return(BigIntFromUnsigned64(AtomicCompareExchange64<AtomicUint64>(
666      backing_store, WordShl(index_word, 3), var_old_low.value(),
667      var_new_low.value(), old_high, new_high)));
668
669  // This shouldn't happen, we've already validated the type.
670  BIND(&other);
671  Unreachable();
672#endif  // V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64 || V8_TARGET_ARCH_PPC64
673        // || V8_TARGET_ARCH_PPC || V8_TARGET_ARCH_S390 || V8_TARGET_ARCH_S390X
674        // || V8_TARGET_ARCH_RISCV64
675
676  BIND(&detached);
677  {
678    ThrowTypeError(context, MessageTemplate::kDetachedOperation,
679                   "Atomics.store");
680  }
681}
682
683#define BINOP_BUILTIN(op, method_name)                                        \
684  TF_BUILTIN(Atomics##op, SharedArrayBufferBuiltinsAssembler) {               \
685    auto array = Parameter<Object>(Descriptor::kArray);                       \
686    auto index = Parameter<Object>(Descriptor::kIndex);                       \
687    auto value = Parameter<Object>(Descriptor::kValue);                       \
688    auto context = Parameter<Context>(Descriptor::kContext);                  \
689    AtomicBinopBuiltinCommon(array, index, value, context,                    \
690                             &CodeAssembler::Atomic##op,                      \
691                             &CodeAssembler::Atomic##op##64 < AtomicInt64 >,  \
692                             &CodeAssembler::Atomic##op##64 < AtomicUint64 >, \
693                             Runtime::kAtomics##op, method_name);             \
694  }
695// https://tc39.es/ecma262/#sec-atomics.add
696BINOP_BUILTIN(Add, "Atomics.add")
697// https://tc39.es/ecma262/#sec-atomics.sub
698BINOP_BUILTIN(Sub, "Atomics.sub")
699// https://tc39.es/ecma262/#sec-atomics.and
700BINOP_BUILTIN(And, "Atomics.and")
701// https://tc39.es/ecma262/#sec-atomics.or
702BINOP_BUILTIN(Or, "Atomics.or")
703// https://tc39.es/ecma262/#sec-atomics.xor
704BINOP_BUILTIN(Xor, "Atomics.xor")
705#undef BINOP_BUILTIN
706
707// https://tc39.es/ecma262/#sec-atomicreadmodifywrite
708void SharedArrayBufferBuiltinsAssembler::AtomicBinopBuiltinCommon(
709    TNode<Object> maybe_array, TNode<Object> index, TNode<Object> value,
710    TNode<Context> context, AssemblerFunction function,
711    AssemblerFunction64<AtomicInt64> function_int_64,
712    AssemblerFunction64<AtomicUint64> function_uint_64,
713    Runtime::FunctionId runtime_function, const char* method_name) {
714  // 1. Let buffer be ? ValidateIntegerTypedArray(typedArray).
715  Label detached(this);
716  TNode<Int32T> elements_kind;
717  TNode<RawPtrT> backing_store;
718  TNode<JSArrayBuffer> array_buffer = ValidateIntegerTypedArray(
719      maybe_array, context, &elements_kind, &backing_store, &detached);
720  TNode<JSTypedArray> array = CAST(maybe_array);
721
722  // 2. Let i be ? ValidateAtomicAccess(typedArray, index).
723  TNode<UintPtrT> index_word = ValidateAtomicAccess(array, index, context);
724
725#if V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64 || V8_TARGET_ARCH_PPC64 || \
726    V8_TARGET_ARCH_PPC || V8_TARGET_ARCH_S390 || V8_TARGET_ARCH_S390X ||    \
727    V8_TARGET_ARCH_RISCV64
728  USE(array_buffer);
729  TNode<Number> index_number = ChangeUintPtrToTagged(index_word);
730  Return(CallRuntime(runtime_function, context, array, index_number, value));
731#else
732  Label i8(this), u8(this), i16(this), u16(this), i32(this), u32(this),
733      i64(this), u64(this), big(this), other(this);
734
735  // 3. Let arrayTypeName be typedArray.[[TypedArrayName]].
736  // 4. If typedArray.[[ContentType]] is BigInt, let v be ? ToBigInt(value).
737  STATIC_ASSERT(BIGINT64_ELEMENTS > INT32_ELEMENTS);
738  STATIC_ASSERT(BIGUINT64_ELEMENTS > INT32_ELEMENTS);
739  GotoIf(Int32GreaterThan(elements_kind, Int32Constant(INT32_ELEMENTS)), &big);
740
741  // 5. Otherwise, let v be ? ToInteger(value).
742  TNode<Number> value_integer = ToInteger_Inline(context, value);
743
744  // 6. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
745  // 7. NOTE: The above check is not redundant with the check in
746  // ValidateIntegerTypedArray because the call to ToBigInt or ToInteger on the
747  // preceding lines can have arbitrary side effects, which could cause the
748  // buffer to become detached.
749  GotoIf(IsDetachedBuffer(array_buffer), &detached);
750
751  DebugCheckAtomicIndex(array, index_word);
752
753  TNode<Word32T> value_word32 = TruncateTaggedToWord32(context, value_integer);
754
755  // Steps 8-12.
756  //
757  // (Not copied from ecma262 due to the axiomatic nature of the memory model.)
758  int32_t case_values[] = {
759      INT8_ELEMENTS,   UINT8_ELEMENTS, INT16_ELEMENTS,
760      UINT16_ELEMENTS, INT32_ELEMENTS, UINT32_ELEMENTS,
761  };
762  Label* case_labels[] = {
763      &i8, &u8, &i16, &u16, &i32, &u32,
764  };
765  Switch(elements_kind, &other, case_values, case_labels,
766         arraysize(case_labels));
767
768  BIND(&i8);
769  Return(SmiFromInt32(Signed((this->*function)(
770      MachineType::Int8(), backing_store, index_word, value_word32))));
771  BIND(&u8);
772  Return(SmiFromInt32(Signed((this->*function)(
773      MachineType::Uint8(), backing_store, index_word, value_word32))));
774  BIND(&i16);
775  Return(SmiFromInt32(Signed((this->*function)(
776      MachineType::Int16(), backing_store,
777      WordShl(index_word, UintPtrConstant(1)), value_word32))));
778  BIND(&u16);
779  Return(SmiFromInt32(Signed((this->*function)(
780      MachineType::Uint16(), backing_store,
781      WordShl(index_word, UintPtrConstant(1)), value_word32))));
782  BIND(&i32);
783  Return(ChangeInt32ToTagged(Signed((this->*function)(
784      MachineType::Int32(), backing_store,
785      WordShl(index_word, UintPtrConstant(2)), value_word32))));
786  BIND(&u32);
787  Return(ChangeUint32ToTagged(Unsigned((this->*function)(
788      MachineType::Uint32(), backing_store,
789      WordShl(index_word, UintPtrConstant(2)), value_word32))));
790  BIND(&big);
791  // 4. If typedArray.[[ContentType]] is BigInt, let v be ? ToBigInt(value).
792  TNode<BigInt> value_bigint = ToBigInt(context, value);
793
794  // 6. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
795  GotoIf(IsDetachedBuffer(array_buffer), &detached);
796
797  DebugCheckAtomicIndex(array, index_word);
798
799  TVARIABLE(UintPtrT, var_low);
800  TVARIABLE(UintPtrT, var_high);
801  BigIntToRawBytes(value_bigint, &var_low, &var_high);
802  TNode<UintPtrT> high = Is64() ? TNode<UintPtrT>() : var_high.value();
803  GotoIf(Word32Equal(elements_kind, Int32Constant(BIGINT64_ELEMENTS)), &i64);
804  GotoIf(Word32Equal(elements_kind, Int32Constant(BIGUINT64_ELEMENTS)), &u64);
805  Unreachable();
806
807  BIND(&i64);
808  Return(BigIntFromSigned64((this->*function_int_64)(
809      backing_store, WordShl(index_word, UintPtrConstant(3)), var_low.value(),
810      high)));
811  BIND(&u64);
812  Return(BigIntFromUnsigned64((this->*function_uint_64)(
813      backing_store, WordShl(index_word, UintPtrConstant(3)), var_low.value(),
814      high)));
815  // // This shouldn't happen, we've already validated the type.
816  BIND(&other);
817  Unreachable();
818#endif  // V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64 || V8_TARGET_ARCH_PPC64
819        // || V8_TARGET_ARCH_PPC || V8_TARGET_ARCH_S390 || V8_TARGET_ARCH_S390X
820        // || V8_TARGET_ARCH_RISCV64
821
822  BIND(&detached);
823  ThrowTypeError(context, MessageTemplate::kDetachedOperation, method_name);
824}
825
826}  // namespace internal
827}  // namespace v8
828