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