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 ¬_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 ¬_float_or_clamped, &invalid);
89
90 BIND(&invalid);
91 {
92 ThrowTypeError(context, MessageTemplate::kNotIntegerTypedArray,
93 maybe_array);
94 }
95
96 BIND(¬_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