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 
10 namespace v8 {
11 namespace internal {
12 
13 class SharedArrayBufferBuiltinsAssembler : public CodeStubAssembler {
14  public:
SharedArrayBufferBuiltinsAssembler( compiler::CodeAssemblerState* state)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
53 TNode<JSArrayBuffer>
ValidateIntegerTypedArray( TNode<Object> maybe_array, TNode<Context> context, TNode<Int32T>* out_elements_kind, TNode<RawPtrT>* out_backing_store, Label* detached)54 SharedArrayBufferBuiltinsAssembler::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 )
108 TNode<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 
133 void 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 
147 TNode<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 
158 TNode<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
170 TF_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
272 TF_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
387 TF_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
532 TF_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
696 BINOP_BUILTIN(Add, "Atomics.add")
697 // https://tc39.es/ecma262/#sec-atomics.sub
698 BINOP_BUILTIN(Sub, "Atomics.sub")
699 // https://tc39.es/ecma262/#sec-atomics.and
700 BINOP_BUILTIN(And, "Atomics.and")
701 // https://tc39.es/ecma262/#sec-atomics.or
702 BINOP_BUILTIN(Or, "Atomics.or")
703 // https://tc39.es/ecma262/#sec-atomics.xor
704 BINOP_BUILTIN(Xor, "Atomics.xor")
705 #undef BINOP_BUILTIN
706 
707 // https://tc39.es/ecma262/#sec-atomicreadmodifywrite
708 void 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