builtins-sharedarraybuffer-gen.cc 24.9 KB
Newer Older
1 2 3 4 5 6
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "src/builtins/builtins-utils-gen.h"
#include "src/builtins/builtins.h"
7
#include "src/codegen/code-stub-assembler.h"
8
#include "src/objects/objects.h"
9 10 11 12 13 14 15 16 17 18 19 20 21

namespace v8 {
namespace internal {

using compiler::Node;

class SharedArrayBufferBuiltinsAssembler : public CodeStubAssembler {
 public:
  explicit SharedArrayBufferBuiltinsAssembler(
      compiler::CodeAssemblerState* state)
      : CodeStubAssembler(state) {}

 protected:
22 23 24 25
  using AssemblerFunction = Node* (CodeAssembler::*)(MachineType type,
                                                     Node* base, Node* offset,
                                                     Node* value,
                                                     Node* value_high);
26 27
  void ValidateSharedTypedArray(TNode<Object> maybe_array,
                                TNode<Context> context,
28
                                TNode<Int32T>* out_elements_kind,
29
                                TNode<RawPtrT>* out_backing_store);
30 31 32 33

  TNode<UintPtrT> ValidateAtomicAccess(TNode<JSTypedArray> array,
                                       TNode<Object> index,
                                       TNode<Context> context);
34 35

  inline void DebugSanityCheckAtomicIndex(TNode<JSTypedArray> array,
36
                                          TNode<UintPtrT> index);
37 38 39 40

  void AtomicBinopBuiltinCommon(TNode<Object> maybe_array, TNode<Object> index,
                                TNode<Object> value, TNode<Context> context,
                                AssemblerFunction function,
41
                                Runtime::FunctionId runtime_function);
42 43 44 45 46

  // Create a BigInt from the result of a 64-bit atomic operation, using
  // projections on 32-bit platforms.
  TNode<BigInt> BigIntFromSigned64(Node* signed64);
  TNode<BigInt> BigIntFromUnsigned64(Node* unsigned64);
47 48
};

49
// https://tc39.es/ecma262/#sec-validatesharedintegertypedarray
50
void SharedArrayBufferBuiltinsAssembler::ValidateSharedTypedArray(
51 52
    TNode<Object> maybe_array, TNode<Context> context,
    TNode<Int32T>* out_elements_kind, TNode<RawPtrT>* out_backing_store) {
53 54 55
  Label not_float_or_clamped(this), invalid(this);

  // Fail if it is not a heap object.
56
  GotoIf(TaggedIsSmi(maybe_array), &invalid);
57 58

  // Fail if the array's instance type is not JSTypedArray.
59 60 61
  TNode<Map> map = LoadMap(CAST(maybe_array));
  GotoIfNot(IsJSTypedArrayMap(map), &invalid);
  TNode<JSTypedArray> array = CAST(maybe_array);
62 63

  // Fail if the array's JSArrayBuffer is not shared.
64
  TNode<JSArrayBuffer> array_buffer = LoadJSArrayBufferViewBuffer(array);
65
  TNode<Uint32T> bitfield = LoadJSArrayBufferBitField(array_buffer);
66
  GotoIfNot(IsSetWord32<JSArrayBuffer::IsSharedBit>(bitfield), &invalid);
67 68

  // Fail if the array's element type is float32, float64 or clamped.
69 70 71 72 73 74
  STATIC_ASSERT(INT8_ELEMENTS < FLOAT32_ELEMENTS);
  STATIC_ASSERT(INT16_ELEMENTS < FLOAT32_ELEMENTS);
  STATIC_ASSERT(INT32_ELEMENTS < FLOAT32_ELEMENTS);
  STATIC_ASSERT(UINT8_ELEMENTS < FLOAT32_ELEMENTS);
  STATIC_ASSERT(UINT16_ELEMENTS < FLOAT32_ELEMENTS);
  STATIC_ASSERT(UINT32_ELEMENTS < FLOAT32_ELEMENTS);
75
  TNode<Int32T> elements_kind = LoadMapElementsKind(map);
76
  GotoIf(Int32LessThan(elements_kind, Int32Constant(FLOAT32_ELEMENTS)),
77
         &not_float_or_clamped);
78 79 80
  STATIC_ASSERT(BIGINT64_ELEMENTS > UINT8_CLAMPED_ELEMENTS);
  STATIC_ASSERT(BIGUINT64_ELEMENTS > UINT8_CLAMPED_ELEMENTS);
  Branch(Int32GreaterThan(elements_kind, Int32Constant(UINT8_CLAMPED_ELEMENTS)),
81 82
         &not_float_or_clamped, &invalid);

83
  BIND(&invalid);
84
  {
85
    ThrowTypeError(context, MessageTemplate::kNotIntegerSharedTypedArray,
86
                   maybe_array);
87 88
  }

89
  BIND(&not_float_or_clamped);
90
  *out_elements_kind = elements_kind;
91

92
  TNode<RawPtrT> backing_store = LoadJSArrayBufferBackingStorePtr(array_buffer);
93 94
  TNode<UintPtrT> byte_offset = LoadJSArrayBufferViewByteOffset(array);
  *out_backing_store = RawPtrAdd(backing_store, Signed(byte_offset));
95 96
}

97
// https://tc39.github.io/ecma262/#sec-validateatomicaccess
98 99 100
// ValidateAtomicAccess( typedArray, requestIndex )
TNode<UintPtrT> SharedArrayBufferBuiltinsAssembler::ValidateAtomicAccess(
    TNode<JSTypedArray> array, TNode<Object> index, TNode<Context> context) {
101 102
  Label done(this), range_error(this);

103
  TNode<UintPtrT> index_uintptr = ToIndex(context, index, &range_error);
104

105
  TNode<UintPtrT> array_length = LoadJSTypedArrayLength(array);
106
  Branch(UintPtrLessThan(index_uintptr, array_length), &done, &range_error);
107

108
  BIND(&range_error);
109
  ThrowRangeError(context, MessageTemplate::kInvalidAtomicAccessIndex);
110

111 112
  BIND(&done);
  return index_uintptr;
113 114
}

115
void SharedArrayBufferBuiltinsAssembler::DebugSanityCheckAtomicIndex(
116
    TNode<JSTypedArray> array, TNode<UintPtrT> index) {
117 118
  // In Debug mode, we re-validate the index as a sanity check because
  // ToInteger above calls out to JavaScript. A SharedArrayBuffer can't be
119
  // detached and the TypedArray length can't change either, so skipping this
120
  // check in Release mode is safe.
121 122 123
  CSA_ASSERT(this, Word32BinaryNot(
                       IsDetachedBuffer(LoadJSArrayBufferViewBuffer(array))));
  CSA_ASSERT(this, UintPtrLessThan(index, LoadJSTypedArrayLength(array)));
124 125
}

126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
TNode<BigInt> SharedArrayBufferBuiltinsAssembler::BigIntFromSigned64(
    Node* signed64) {
  if (Is64()) {
    return BigIntFromInt64(UncheckedCast<IntPtrT>(signed64));
  } else {
    TNode<IntPtrT> low = UncheckedCast<IntPtrT>(Projection(0, signed64));
    TNode<IntPtrT> high = UncheckedCast<IntPtrT>(Projection(1, signed64));
    return BigIntFromInt32Pair(low, high);
  }
}

TNode<BigInt> SharedArrayBufferBuiltinsAssembler::BigIntFromUnsigned64(
    Node* unsigned64) {
  if (Is64()) {
    return BigIntFromUint64(UncheckedCast<UintPtrT>(unsigned64));
  } else {
    TNode<UintPtrT> low = UncheckedCast<UintPtrT>(Projection(0, unsigned64));
    TNode<UintPtrT> high = UncheckedCast<UintPtrT>(Projection(1, unsigned64));
    return BigIntFromUint32Pair(low, high);
  }
}

148
// https://tc39.es/ecma262/#sec-atomicload
149
TF_BUILTIN(AtomicsLoad, SharedArrayBufferBuiltinsAssembler) {
150 151 152
  TNode<Object> maybe_array = CAST(Parameter(Descriptor::kArray));
  TNode<Object> index = CAST(Parameter(Descriptor::kIndex));
  TNode<Context> context = CAST(Parameter(Descriptor::kContext));
153

154
  TNode<Int32T> elements_kind;
155 156 157 158
  TNode<RawPtrT> backing_store;
  ValidateSharedTypedArray(maybe_array, context, &elements_kind,
                           &backing_store);
  TNode<JSTypedArray> array = CAST(maybe_array);
159

160
  TNode<UintPtrT> index_word = ValidateAtomicAccess(array, index, context);
161 162

  Label i8(this), u8(this), i16(this), u16(this), i32(this), u32(this),
163
      i64(this), u64(this), other(this);
164
  int32_t case_values[] = {
165 166
      INT8_ELEMENTS,  UINT8_ELEMENTS,  INT16_ELEMENTS,    UINT16_ELEMENTS,
      INT32_ELEMENTS, UINT32_ELEMENTS, BIGINT64_ELEMENTS, BIGUINT64_ELEMENTS,
167
  };
168
  Label* case_labels[] = {&i8, &u8, &i16, &u16, &i32, &u32, &i64, &u64};
169
  Switch(elements_kind, &other, case_values, case_labels,
170 171
         arraysize(case_labels));

172
  BIND(&i8);
173 174
  Return(
      SmiFromInt32(AtomicLoad(MachineType::Int8(), backing_store, index_word)));
175

176
  BIND(&u8);
177
  Return(SmiFromInt32(
178 179
      AtomicLoad(MachineType::Uint8(), backing_store, index_word)));

180
  BIND(&i16);
181
  Return(SmiFromInt32(
182 183
      AtomicLoad(MachineType::Int16(), backing_store, WordShl(index_word, 1))));

184
  BIND(&u16);
185 186
  Return(SmiFromInt32(AtomicLoad(MachineType::Uint16(), backing_store,
                                 WordShl(index_word, 1))));
187

188
  BIND(&i32);
189 190 191
  Return(ChangeInt32ToTagged(
      AtomicLoad(MachineType::Int32(), backing_store, WordShl(index_word, 2))));

192
  BIND(&u32);
193 194
  Return(ChangeUint32ToTagged(AtomicLoad(MachineType::Uint32(), backing_store,
                                         WordShl(index_word, 2))));
195
#if V8_TARGET_ARCH_MIPS && !_MIPS_ARCH_MIPS32R6
196
  BIND(&i64);
197
  Goto(&u64);
198

199
  BIND(&u64);
200 201 202 203
  {
    TNode<Number> index_number = ChangeUintPtrToTagged(index_word);
    Return(CallRuntime(Runtime::kAtomicsLoad64, context, array, index_number));
  }
204
#else
205 206 207 208 209 210 211 212 213 214
  BIND(&i64);
  // This uses Uint64() intentionally: AtomicLoad is not implemented for
  // Int64(), which is fine because the machine instruction only cares
  // about words.
  Return(BigIntFromSigned64(AtomicLoad(MachineType::Uint64(), backing_store,
                                       WordShl(index_word, 3))));

  BIND(&u64);
  Return(BigIntFromUnsigned64(AtomicLoad(MachineType::Uint64(), backing_store,
                                         WordShl(index_word, 3))));
215
#endif
216
  // This shouldn't happen, we've already validated the type.
217
  BIND(&other);
218 219 220
  Unreachable();
}

221
// https://tc39.es/ecma262/#sec-atomics.store
222
TF_BUILTIN(AtomicsStore, SharedArrayBufferBuiltinsAssembler) {
223 224 225 226
  TNode<Object> maybe_array = CAST(Parameter(Descriptor::kArray));
  TNode<Object> index = CAST(Parameter(Descriptor::kIndex));
  TNode<Object> value = CAST(Parameter(Descriptor::kValue));
  TNode<Context> context = CAST(Parameter(Descriptor::kContext));
227

228
  TNode<Int32T> elements_kind;
229 230 231 232
  TNode<RawPtrT> backing_store;
  ValidateSharedTypedArray(maybe_array, context, &elements_kind,
                           &backing_store);
  TNode<JSTypedArray> array = CAST(maybe_array);
233

234
  TNode<UintPtrT> index_word = ValidateAtomicAccess(array, index, context);
235

236
  Label u8(this), u16(this), u32(this), u64(this), other(this);
237 238 239
  STATIC_ASSERT(BIGINT64_ELEMENTS > INT32_ELEMENTS);
  STATIC_ASSERT(BIGUINT64_ELEMENTS > INT32_ELEMENTS);
  GotoIf(Int32GreaterThan(elements_kind, Int32Constant(INT32_ELEMENTS)), &u64);
240

241
  TNode<Number> value_integer = ToInteger_Inline(context, value);
242
  TNode<Word32T> value_word32 = TruncateTaggedToWord32(context, value_integer);
243

244
  DebugSanityCheckAtomicIndex(array, index_word);
245 246

  int32_t case_values[] = {
247 248
      INT8_ELEMENTS,   UINT8_ELEMENTS, INT16_ELEMENTS,
      UINT16_ELEMENTS, INT32_ELEMENTS, UINT32_ELEMENTS,
249
  };
250
  Label* case_labels[] = {&u8, &u8, &u16, &u16, &u32, &u32};
251
  Switch(elements_kind, &other, case_values, case_labels,
252 253
         arraysize(case_labels));

254
  BIND(&u8);
255 256 257 258
  AtomicStore(MachineRepresentation::kWord8, backing_store, index_word,
              value_word32);
  Return(value_integer);

259
  BIND(&u16);
260 261 262 263
  AtomicStore(MachineRepresentation::kWord16, backing_store,
              WordShl(index_word, 1), value_word32);
  Return(value_integer);

264
  BIND(&u32);
265 266 267 268
  AtomicStore(MachineRepresentation::kWord32, backing_store,
              WordShl(index_word, 2), value_word32);
  Return(value_integer);

269
  BIND(&u64);
270
#if V8_TARGET_ARCH_MIPS && !_MIPS_ARCH_MIPS32R6
271 272
  TNode<Number> index_number = ChangeUintPtrToTagged(index_word);
  Return(CallRuntime(Runtime::kAtomicsStore64, context, array, index_number,
273 274
                     value));
#else
275 276
  TNode<BigInt> value_bigint = ToBigInt(context, value);

277
  DebugSanityCheckAtomicIndex(array, index_word);
278

279 280 281
  TVARIABLE(UintPtrT, var_low);
  TVARIABLE(UintPtrT, var_high);
  BigIntToRawBytes(value_bigint, &var_low, &var_high);
282
  TNode<UintPtrT> high = Is64() ? TNode<UintPtrT>() : var_high.value();
283 284 285
  AtomicStore(MachineRepresentation::kWord64, backing_store,
              WordShl(index_word, 3), var_low.value(), high);
  Return(value_bigint);
286
#endif
287

288
  // This shouldn't happen, we've already validated the type.
289
  BIND(&other);
290 291 292
  Unreachable();
}

293
// https://tc39.es/ecma262/#sec-atomics.exchange
294
TF_BUILTIN(AtomicsExchange, SharedArrayBufferBuiltinsAssembler) {
295 296 297 298
  TNode<Object> maybe_array = CAST(Parameter(Descriptor::kArray));
  TNode<Object> index = CAST(Parameter(Descriptor::kIndex));
  TNode<Object> value = CAST(Parameter(Descriptor::kValue));
  TNode<Context> context = CAST(Parameter(Descriptor::kContext));
299

300
  TNode<Int32T> elements_kind;
301 302 303 304
  TNode<RawPtrT> backing_store;
  ValidateSharedTypedArray(maybe_array, context, &elements_kind,
                           &backing_store);
  TNode<JSTypedArray> array = CAST(maybe_array);
305

306
  TNode<UintPtrT> index_word = ValidateAtomicAccess(array, index, context);
307

308
#if V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64
309 310
  TNode<Number> index_number = ChangeUintPtrToTagged(index_word);
  Return(CallRuntime(Runtime::kAtomicsExchange, context, array, index_number,
311
                     value));
312 313
#else

314 315
  Label i8(this), u8(this), i16(this), u16(this), i32(this), u32(this),
      i64(this), u64(this), big(this), other(this);
316 317 318
  STATIC_ASSERT(BIGINT64_ELEMENTS > INT32_ELEMENTS);
  STATIC_ASSERT(BIGUINT64_ELEMENTS > INT32_ELEMENTS);
  GotoIf(Int32GreaterThan(elements_kind, Int32Constant(INT32_ELEMENTS)), &big);
319

320 321
  TNode<Number> value_integer = ToInteger_Inline(context, value);

322
  DebugSanityCheckAtomicIndex(array, index_word);
323

324
  TNode<Word32T> value_word32 = TruncateTaggedToWord32(context, value_integer);
325 326

  int32_t case_values[] = {
327 328
      INT8_ELEMENTS,   UINT8_ELEMENTS, INT16_ELEMENTS,
      UINT16_ELEMENTS, INT32_ELEMENTS, UINT32_ELEMENTS,
329 330 331 332
  };
  Label* case_labels[] = {
      &i8, &u8, &i16, &u16, &i32, &u32,
  };
333
  Switch(elements_kind, &other, case_values, case_labels,
334 335
         arraysize(case_labels));

336
  BIND(&i8);
337 338
  Return(SmiFromInt32(AtomicExchange(MachineType::Int8(), backing_store,
                                     index_word, value_word32)));
339

340
  BIND(&u8);
341 342
  Return(SmiFromInt32(AtomicExchange(MachineType::Uint8(), backing_store,
                                     index_word, value_word32)));
343

344
  BIND(&i16);
345 346
  Return(SmiFromInt32(AtomicExchange(MachineType::Int16(), backing_store,
                                     WordShl(index_word, 1), value_word32)));
347

348
  BIND(&u16);
349 350
  Return(SmiFromInt32(AtomicExchange(MachineType::Uint16(), backing_store,
                                     WordShl(index_word, 1), value_word32)));
351

352
  BIND(&i32);
353 354 355 356
  Return(ChangeInt32ToTagged(AtomicExchange(MachineType::Int32(), backing_store,
                                            WordShl(index_word, 2),
                                            value_word32)));

357
  BIND(&u32);
358 359 360 361
  Return(ChangeUint32ToTagged(
      AtomicExchange(MachineType::Uint32(), backing_store,
                     WordShl(index_word, 2), value_word32)));

362
  BIND(&big);
363 364
  TNode<BigInt> value_bigint = ToBigInt(context, value);

365
  DebugSanityCheckAtomicIndex(array, index_word);
366

367 368 369
  TVARIABLE(UintPtrT, var_low);
  TVARIABLE(UintPtrT, var_high);
  BigIntToRawBytes(value_bigint, &var_low, &var_high);
370
  TNode<UintPtrT> high = Is64() ? TNode<UintPtrT>() : var_high.value();
371 372
  GotoIf(Word32Equal(elements_kind, Int32Constant(BIGINT64_ELEMENTS)), &i64);
  GotoIf(Word32Equal(elements_kind, Int32Constant(BIGUINT64_ELEMENTS)), &u64);
373 374 375 376 377 378 379 380 381 382 383 384 385 386 387
  Unreachable();

  BIND(&i64);
  // This uses Uint64() intentionally: AtomicExchange is not implemented for
  // Int64(), which is fine because the machine instruction only cares
  // about words.
  Return(BigIntFromSigned64(AtomicExchange(MachineType::Uint64(), backing_store,
                                           WordShl(index_word, 3),
                                           var_low.value(), high)));

  BIND(&u64);
  Return(BigIntFromUnsigned64(
      AtomicExchange(MachineType::Uint64(), backing_store,
                     WordShl(index_word, 3), var_low.value(), high)));

388
  // This shouldn't happen, we've already validated the type.
389
  BIND(&other);
390
  Unreachable();
391
#endif  // V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64
392 393
}

394
// https://tc39.es/ecma262/#sec-atomics.compareexchange
395
TF_BUILTIN(AtomicsCompareExchange, SharedArrayBufferBuiltinsAssembler) {
396 397 398 399 400
  TNode<Object> maybe_array = CAST(Parameter(Descriptor::kArray));
  TNode<Object> index = CAST(Parameter(Descriptor::kIndex));
  TNode<Object> old_value = CAST(Parameter(Descriptor::kOldValue));
  TNode<Object> new_value = CAST(Parameter(Descriptor::kNewValue));
  TNode<Context> context = CAST(Parameter(Descriptor::kContext));
401

402
  TNode<Int32T> elements_kind;
403 404 405 406
  TNode<RawPtrT> backing_store;
  ValidateSharedTypedArray(maybe_array, context, &elements_kind,
                           &backing_store);
  TNode<JSTypedArray> array = CAST(maybe_array);
407

408
  TNode<UintPtrT> index_word = ValidateAtomicAccess(array, index, context);
409

410 411
#if V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64 || V8_TARGET_ARCH_PPC64 || \
    V8_TARGET_ARCH_PPC || V8_TARGET_ARCH_S390 || V8_TARGET_ARCH_S390X
412
  TNode<Number> index_number = ChangeUintPtrToTagged(index_word);
413
  Return(CallRuntime(Runtime::kAtomicsCompareExchange, context, array,
414
                     index_number, old_value, new_value));
415
#else
416 417
  Label i8(this), u8(this), i16(this), u16(this), i32(this), u32(this),
      i64(this), u64(this), big(this), other(this);
418 419 420
  STATIC_ASSERT(BIGINT64_ELEMENTS > INT32_ELEMENTS);
  STATIC_ASSERT(BIGUINT64_ELEMENTS > INT32_ELEMENTS);
  GotoIf(Int32GreaterThan(elements_kind, Int32Constant(INT32_ELEMENTS)), &big);
421

422 423 424
  TNode<Number> old_value_integer = ToInteger_Inline(context, old_value);
  TNode<Number> new_value_integer = ToInteger_Inline(context, new_value);

425
  DebugSanityCheckAtomicIndex(array, index_word);
426

427 428 429 430
  TNode<Word32T> old_value_word32 =
      TruncateTaggedToWord32(context, old_value_integer);
  TNode<Word32T> new_value_word32 =
      TruncateTaggedToWord32(context, new_value_integer);
431 432

  int32_t case_values[] = {
433 434
      INT8_ELEMENTS,   UINT8_ELEMENTS, INT16_ELEMENTS,
      UINT16_ELEMENTS, INT32_ELEMENTS, UINT32_ELEMENTS,
435 436 437 438
  };
  Label* case_labels[] = {
      &i8, &u8, &i16, &u16, &i32, &u32,
  };
439
  Switch(elements_kind, &other, case_values, case_labels,
440 441
         arraysize(case_labels));

442
  BIND(&i8);
443 444 445
  Return(SmiFromInt32(AtomicCompareExchange(MachineType::Int8(), backing_store,
                                            index_word, old_value_word32,
                                            new_value_word32)));
446

447
  BIND(&u8);
448 449 450
  Return(SmiFromInt32(AtomicCompareExchange(MachineType::Uint8(), backing_store,
                                            index_word, old_value_word32,
                                            new_value_word32)));
451

452
  BIND(&i16);
453
  Return(SmiFromInt32(AtomicCompareExchange(
454 455 456
      MachineType::Int16(), backing_store, WordShl(index_word, 1),
      old_value_word32, new_value_word32)));

457
  BIND(&u16);
458
  Return(SmiFromInt32(AtomicCompareExchange(
459 460 461
      MachineType::Uint16(), backing_store, WordShl(index_word, 1),
      old_value_word32, new_value_word32)));

462
  BIND(&i32);
463 464 465 466
  Return(ChangeInt32ToTagged(AtomicCompareExchange(
      MachineType::Int32(), backing_store, WordShl(index_word, 2),
      old_value_word32, new_value_word32)));

467
  BIND(&u32);
468 469 470 471
  Return(ChangeUint32ToTagged(AtomicCompareExchange(
      MachineType::Uint32(), backing_store, WordShl(index_word, 2),
      old_value_word32, new_value_word32)));

472
  BIND(&big);
473 474 475
  TNode<BigInt> old_value_bigint = ToBigInt(context, old_value);
  TNode<BigInt> new_value_bigint = ToBigInt(context, new_value);

476
  DebugSanityCheckAtomicIndex(array, index_word);
477

478 479 480 481 482 483
  TVARIABLE(UintPtrT, var_old_low);
  TVARIABLE(UintPtrT, var_old_high);
  TVARIABLE(UintPtrT, var_new_low);
  TVARIABLE(UintPtrT, var_new_high);
  BigIntToRawBytes(old_value_bigint, &var_old_low, &var_old_high);
  BigIntToRawBytes(new_value_bigint, &var_new_low, &var_new_high);
484 485
  TNode<UintPtrT> old_high = Is64() ? TNode<UintPtrT>() : var_old_high.value();
  TNode<UintPtrT> new_high = Is64() ? TNode<UintPtrT>() : var_new_high.value();
486 487
  GotoIf(Word32Equal(elements_kind, Int32Constant(BIGINT64_ELEMENTS)), &i64);
  GotoIf(Word32Equal(elements_kind, Int32Constant(BIGUINT64_ELEMENTS)), &u64);
488 489 490 491 492 493 494 495 496 497 498 499 500 501 502
  Unreachable();

  BIND(&i64);
  // This uses Uint64() intentionally: AtomicCompareExchange is not implemented
  // for Int64(), which is fine because the machine instruction only cares
  // about words.
  Return(BigIntFromSigned64(AtomicCompareExchange(
      MachineType::Uint64(), backing_store, WordShl(index_word, 3),
      var_old_low.value(), var_new_low.value(), old_high, new_high)));

  BIND(&u64);
  Return(BigIntFromUnsigned64(AtomicCompareExchange(
      MachineType::Uint64(), backing_store, WordShl(index_word, 3),
      var_old_low.value(), var_new_low.value(), old_high, new_high)));

503
  // This shouldn't happen, we've already validated the type.
504
  BIND(&other);
505 506 507 508 509
  Unreachable();
#endif  // V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64 || V8_TARGET_ARCH_PPC64
        // || V8_TARGET_ARCH_PPC || V8_TARGET_ARCH_S390 || V8_TARGET_ARCH_S390X
}

510 511 512 513 514 515 516 517 518
#define BINOP_BUILTIN(op)                                           \
  TF_BUILTIN(Atomics##op, SharedArrayBufferBuiltinsAssembler) {     \
    TNode<Object> array = CAST(Parameter(Descriptor::kArray));      \
    TNode<Object> index = CAST(Parameter(Descriptor::kIndex));      \
    TNode<Object> value = CAST(Parameter(Descriptor::kValue));      \
    TNode<Context> context = CAST(Parameter(Descriptor::kContext)); \
    AtomicBinopBuiltinCommon(array, index, value, context,          \
                             &CodeAssembler::Atomic##op,            \
                             Runtime::kAtomics##op);                \
519
  }
520
// https://tc39.es/ecma262/#sec-atomics.add
521
BINOP_BUILTIN(Add)
522
// https://tc39.es/ecma262/#sec-atomics.sub
523
BINOP_BUILTIN(Sub)
524
// https://tc39.es/ecma262/#sec-atomics.and
525
BINOP_BUILTIN(And)
526
// https://tc39.es/ecma262/#sec-atomics.or
527
BINOP_BUILTIN(Or)
528
// https://tc39.es/ecma262/#sec-atomics.xor
529 530 531
BINOP_BUILTIN(Xor)
#undef BINOP_BUILTIN

532
// https://tc39.es/ecma262/#sec-atomicreadmodifywrite
533
void SharedArrayBufferBuiltinsAssembler::AtomicBinopBuiltinCommon(
534 535 536
    TNode<Object> maybe_array, TNode<Object> index, TNode<Object> value,
    TNode<Context> context, AssemblerFunction function,
    Runtime::FunctionId runtime_function) {
537
  TNode<Int32T> elements_kind;
538 539 540 541
  TNode<RawPtrT> backing_store;
  ValidateSharedTypedArray(maybe_array, context, &elements_kind,
                           &backing_store);
  TNode<JSTypedArray> array = CAST(maybe_array);
542

543
  TNode<UintPtrT> index_word = ValidateAtomicAccess(array, index, context);
544 545 546

#if V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64 || V8_TARGET_ARCH_PPC64 || \
    V8_TARGET_ARCH_PPC || V8_TARGET_ARCH_S390 || V8_TARGET_ARCH_S390X
547 548
  TNode<Number> index_number = ChangeUintPtrToTagged(index_word);
  Return(CallRuntime(runtime_function, context, array, index_number, value));
549
#else
550 551 552
  Label i8(this), u8(this), i16(this), u16(this), i32(this), u32(this),
      i64(this), u64(this), big(this), other(this);

553 554 555
  STATIC_ASSERT(BIGINT64_ELEMENTS > INT32_ELEMENTS);
  STATIC_ASSERT(BIGUINT64_ELEMENTS > INT32_ELEMENTS);
  GotoIf(Int32GreaterThan(elements_kind, Int32Constant(INT32_ELEMENTS)), &big);
556

557 558
  TNode<Number> value_integer = ToInteger_Inline(context, value);

559
  DebugSanityCheckAtomicIndex(array, index_word);
560

561
  TNode<Word32T> value_word32 = TruncateTaggedToWord32(context, value_integer);
562 563

  int32_t case_values[] = {
564 565
      INT8_ELEMENTS,   UINT8_ELEMENTS, INT16_ELEMENTS,
      UINT16_ELEMENTS, INT32_ELEMENTS, UINT32_ELEMENTS,
566 567 568 569
  };
  Label* case_labels[] = {
      &i8, &u8, &i16, &u16, &i32, &u32,
  };
570
  Switch(elements_kind, &other, case_values, case_labels,
571 572
         arraysize(case_labels));

573
  BIND(&i8);
574
  Return(SmiFromInt32((this->*function)(MachineType::Int8(), backing_store,
575
                                        index_word, value_word32, nullptr)));
576

577
  BIND(&u8);
578
  Return(SmiFromInt32((this->*function)(MachineType::Uint8(), backing_store,
579
                                        index_word, value_word32, nullptr)));
580

581
  BIND(&i16);
582
  Return(SmiFromInt32((this->*function)(MachineType::Int16(), backing_store,
583 584
                                        WordShl(index_word, 1), value_word32,
                                        nullptr)));
585

586
  BIND(&u16);
587
  Return(SmiFromInt32((this->*function)(MachineType::Uint16(), backing_store,
588 589
                                        WordShl(index_word, 1), value_word32,
                                        nullptr)));
590

591
  BIND(&i32);
592 593
  Return(ChangeInt32ToTagged(
      (this->*function)(MachineType::Int32(), backing_store,
594
                        WordShl(index_word, 2), value_word32, nullptr)));
595

596
  BIND(&u32);
597 598
  Return(ChangeUint32ToTagged(
      (this->*function)(MachineType::Uint32(), backing_store,
599 600 601
                        WordShl(index_word, 2), value_word32, nullptr)));

  BIND(&big);
602 603
  TNode<BigInt> value_bigint = ToBigInt(context, value);

604
  DebugSanityCheckAtomicIndex(array, index_word);
605

606 607 608
  TVARIABLE(UintPtrT, var_low);
  TVARIABLE(UintPtrT, var_high);
  BigIntToRawBytes(value_bigint, &var_low, &var_high);
609
  TNode<UintPtrT> high = Is64() ? TNode<UintPtrT>() : var_high.value();
610 611
  GotoIf(Word32Equal(elements_kind, Int32Constant(BIGINT64_ELEMENTS)), &i64);
  GotoIf(Word32Equal(elements_kind, Int32Constant(BIGUINT64_ELEMENTS)), &u64);
612 613 614 615 616 617 618 619 620 621 622 623 624 625
  Unreachable();

  BIND(&i64);
  // This uses Uint64() intentionally: Atomic* ops are not implemented for
  // Int64(), which is fine because the machine instructions only care
  // about words.
  Return(BigIntFromSigned64(
      (this->*function)(MachineType::Uint64(), backing_store,
                        WordShl(index_word, 3), var_low.value(), high)));

  BIND(&u64);
  Return(BigIntFromUnsigned64(
      (this->*function)(MachineType::Uint64(), backing_store,
                        WordShl(index_word, 3), var_low.value(), high)));
626 627

  // This shouldn't happen, we've already validated the type.
628
  BIND(&other);
629 630 631 632 633
  Unreachable();
#endif  // V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64 || V8_TARGET_ARCH_PPC64
        // || V8_TARGET_ARCH_PPC || V8_TARGET_ARCH_S390 || V8_TARGET_ARCH_S390X
}

634 635
}  // namespace internal
}  // namespace v8