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