| OLD | NEW | 
|---|
| 1 // Copyright 2016 the V8 project authors. All rights reserved. | 1 // Copyright 2016 the V8 project authors. All rights reserved. | 
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be | 
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. | 
| 4 | 4 | 
| 5 #include "src/base/macros.h" | 5 #include "src/base/macros.h" | 
| 6 #include "src/base/platform/mutex.h" | 6 #include "src/base/platform/mutex.h" | 
| 7 #include "src/base/platform/time.h" | 7 #include "src/base/platform/time.h" | 
| 8 #include "src/builtins/builtins-utils.h" | 8 #include "src/builtins/builtins-utils.h" | 
| 9 #include "src/builtins/builtins.h" | 9 #include "src/builtins/builtins.h" | 
| 10 #include "src/code-factory.h" | 10 #include "src/code-factory.h" | 
| 11 #include "src/code-stub-assembler.h" |  | 
| 12 #include "src/conversions-inl.h" | 11 #include "src/conversions-inl.h" | 
| 13 #include "src/counters.h" | 12 #include "src/counters.h" | 
| 14 #include "src/factory.h" | 13 #include "src/factory.h" | 
| 15 #include "src/futex-emulation.h" | 14 #include "src/futex-emulation.h" | 
| 16 #include "src/globals.h" | 15 #include "src/globals.h" | 
| 17 #include "src/objects-inl.h" | 16 #include "src/objects-inl.h" | 
| 18 | 17 | 
| 19 namespace v8 { | 18 namespace v8 { | 
| 20 namespace internal { | 19 namespace internal { | 
| 21 | 20 | 
| 22 using compiler::Node; |  | 
| 23 |  | 
| 24 class SharedArrayBufferBuiltinsAssembler : public CodeStubAssembler { |  | 
| 25  public: |  | 
| 26   explicit SharedArrayBufferBuiltinsAssembler( |  | 
| 27       compiler::CodeAssemblerState* state) |  | 
| 28       : CodeStubAssembler(state) {} |  | 
| 29 |  | 
| 30  protected: |  | 
| 31   void ValidateSharedTypedArray(Node* tagged, Node* context, |  | 
| 32                                 Node** out_instance_type, |  | 
| 33                                 Node** out_backing_store); |  | 
| 34   Node* ConvertTaggedAtomicIndexToWord32(Node* tagged, Node* context, |  | 
| 35                                          Node** number_index); |  | 
| 36   void ValidateAtomicIndex(Node* index_word, Node* array_length_word, |  | 
| 37                            Node* context); |  | 
| 38 }; |  | 
| 39 |  | 
| 40 // ES7 sharedmem 6.3.4.1 get SharedArrayBuffer.prototype.byteLength | 21 // ES7 sharedmem 6.3.4.1 get SharedArrayBuffer.prototype.byteLength | 
| 41 BUILTIN(SharedArrayBufferPrototypeGetByteLength) { | 22 BUILTIN(SharedArrayBufferPrototypeGetByteLength) { | 
| 42   HandleScope scope(isolate); | 23   HandleScope scope(isolate); | 
| 43   CHECK_RECEIVER(JSArrayBuffer, array_buffer, | 24   CHECK_RECEIVER(JSArrayBuffer, array_buffer, | 
| 44                  "get SharedArrayBuffer.prototype.byteLength"); | 25                  "get SharedArrayBuffer.prototype.byteLength"); | 
| 45   if (!array_buffer->is_shared()) { | 26   if (!array_buffer->is_shared()) { | 
| 46     THROW_NEW_ERROR_RETURN_FAILURE( | 27     THROW_NEW_ERROR_RETURN_FAILURE( | 
| 47         isolate, NewTypeError(MessageTemplate::kIncompatibleMethodReceiver, | 28         isolate, NewTypeError(MessageTemplate::kIncompatibleMethodReceiver, | 
| 48                               isolate->factory()->NewStringFromAsciiChecked( | 29                               isolate->factory()->NewStringFromAsciiChecked( | 
| 49                                   "get SharedArrayBuffer.prototype.byteLength"), | 30                                   "get SharedArrayBuffer.prototype.byteLength"), | 
| 50                               args.receiver())); | 31                               args.receiver())); | 
| 51   } | 32   } | 
| 52   return array_buffer->byte_length(); | 33   return array_buffer->byte_length(); | 
| 53 } | 34 } | 
| 54 | 35 | 
| 55 void SharedArrayBufferBuiltinsAssembler::ValidateSharedTypedArray( |  | 
| 56     Node* tagged, Node* context, Node** out_instance_type, |  | 
| 57     Node** out_backing_store) { |  | 
| 58   Label not_float_or_clamped(this), invalid(this); |  | 
| 59 |  | 
| 60   // Fail if it is not a heap object. |  | 
| 61   GotoIf(TaggedIsSmi(tagged), &invalid); |  | 
| 62 |  | 
| 63   // Fail if the array's instance type is not JSTypedArray. |  | 
| 64   GotoIf(Word32NotEqual(LoadInstanceType(tagged), |  | 
| 65                         Int32Constant(JS_TYPED_ARRAY_TYPE)), |  | 
| 66          &invalid); |  | 
| 67 |  | 
| 68   // Fail if the array's JSArrayBuffer is not shared. |  | 
| 69   Node* array_buffer = LoadObjectField(tagged, JSTypedArray::kBufferOffset); |  | 
| 70   Node* bitfield = LoadObjectField(array_buffer, JSArrayBuffer::kBitFieldOffset, |  | 
| 71                                    MachineType::Uint32()); |  | 
| 72   GotoIfNot(IsSetWord32<JSArrayBuffer::IsShared>(bitfield), &invalid); |  | 
| 73 |  | 
| 74   // Fail if the array's element type is float32, float64 or clamped. |  | 
| 75   Node* elements_instance_type = |  | 
| 76       LoadInstanceType(LoadObjectField(tagged, JSObject::kElementsOffset)); |  | 
| 77   STATIC_ASSERT(FIXED_INT8_ARRAY_TYPE < FIXED_FLOAT32_ARRAY_TYPE); |  | 
| 78   STATIC_ASSERT(FIXED_INT16_ARRAY_TYPE < FIXED_FLOAT32_ARRAY_TYPE); |  | 
| 79   STATIC_ASSERT(FIXED_INT32_ARRAY_TYPE < FIXED_FLOAT32_ARRAY_TYPE); |  | 
| 80   STATIC_ASSERT(FIXED_UINT8_ARRAY_TYPE < FIXED_FLOAT32_ARRAY_TYPE); |  | 
| 81   STATIC_ASSERT(FIXED_UINT16_ARRAY_TYPE < FIXED_FLOAT32_ARRAY_TYPE); |  | 
| 82   STATIC_ASSERT(FIXED_UINT32_ARRAY_TYPE < FIXED_FLOAT32_ARRAY_TYPE); |  | 
| 83   Branch(Int32LessThan(elements_instance_type, |  | 
| 84                        Int32Constant(FIXED_FLOAT32_ARRAY_TYPE)), |  | 
| 85          ¬_float_or_clamped, &invalid); |  | 
| 86 |  | 
| 87   Bind(&invalid); |  | 
| 88   { |  | 
| 89     CallRuntime(Runtime::kThrowNotIntegerSharedTypedArrayError, context, |  | 
| 90                 tagged); |  | 
| 91     Unreachable(); |  | 
| 92   } |  | 
| 93 |  | 
| 94   Bind(¬_float_or_clamped); |  | 
| 95   *out_instance_type = elements_instance_type; |  | 
| 96 |  | 
| 97   Node* backing_store = |  | 
| 98       LoadObjectField(array_buffer, JSArrayBuffer::kBackingStoreOffset); |  | 
| 99   Node* byte_offset = ChangeUint32ToWord(TruncateTaggedToWord32( |  | 
| 100       context, LoadObjectField(tagged, JSArrayBufferView::kByteOffsetOffset))); |  | 
| 101   *out_backing_store = |  | 
| 102       IntPtrAdd(BitcastTaggedToWord(backing_store), byte_offset); |  | 
| 103 } |  | 
| 104 |  | 
| 105 // https://tc39.github.io/ecmascript_sharedmem/shmem.html#Atomics.ValidateAtomic
     Access |  | 
| 106 Node* SharedArrayBufferBuiltinsAssembler::ConvertTaggedAtomicIndexToWord32( |  | 
| 107     Node* tagged, Node* context, Node** number_index) { |  | 
| 108   Variable var_result(this, MachineRepresentation::kWord32); |  | 
| 109 |  | 
| 110   // TODO(jkummerow): Skip ToNumber call when |tagged| is a number already. |  | 
| 111   // Maybe this can be unified with other tagged-to-index conversions? |  | 
| 112   // Why does this return an int32, and not an intptr? |  | 
| 113   // Why is there the additional |number_index| output parameter? |  | 
| 114   Callable to_number = CodeFactory::ToNumber(isolate()); |  | 
| 115   *number_index = CallStub(to_number, context, tagged); |  | 
| 116   Label done(this, &var_result); |  | 
| 117 |  | 
| 118   Label if_numberissmi(this), if_numberisnotsmi(this); |  | 
| 119   Branch(TaggedIsSmi(*number_index), &if_numberissmi, &if_numberisnotsmi); |  | 
| 120 |  | 
| 121   Bind(&if_numberissmi); |  | 
| 122   { |  | 
| 123     var_result.Bind(SmiToWord32(*number_index)); |  | 
| 124     Goto(&done); |  | 
| 125   } |  | 
| 126 |  | 
| 127   Bind(&if_numberisnotsmi); |  | 
| 128   { |  | 
| 129     Node* number_index_value = LoadHeapNumberValue(*number_index); |  | 
| 130     Node* access_index = TruncateFloat64ToWord32(number_index_value); |  | 
| 131     Node* test_index = ChangeInt32ToFloat64(access_index); |  | 
| 132 |  | 
| 133     Label if_indexesareequal(this), if_indexesarenotequal(this); |  | 
| 134     Branch(Float64Equal(number_index_value, test_index), &if_indexesareequal, |  | 
| 135            &if_indexesarenotequal); |  | 
| 136 |  | 
| 137     Bind(&if_indexesareequal); |  | 
| 138     { |  | 
| 139       var_result.Bind(access_index); |  | 
| 140       Goto(&done); |  | 
| 141     } |  | 
| 142 |  | 
| 143     Bind(&if_indexesarenotequal); |  | 
| 144     { |  | 
| 145       CallRuntime(Runtime::kThrowInvalidAtomicAccessIndexError, context); |  | 
| 146       Unreachable(); |  | 
| 147     } |  | 
| 148   } |  | 
| 149 |  | 
| 150   Bind(&done); |  | 
| 151   return var_result.value(); |  | 
| 152 } |  | 
| 153 |  | 
| 154 void SharedArrayBufferBuiltinsAssembler::ValidateAtomicIndex( |  | 
| 155     Node* index_word, Node* array_length_word, Node* context) { |  | 
| 156   // Check if the index is in bounds. If not, throw RangeError. |  | 
| 157   Label check_passed(this); |  | 
| 158   GotoIf(Uint32LessThan(index_word, array_length_word), &check_passed); |  | 
| 159 |  | 
| 160   CallRuntime(Runtime::kThrowInvalidAtomicAccessIndexError, context); |  | 
| 161   Unreachable(); |  | 
| 162 |  | 
| 163   Bind(&check_passed); |  | 
| 164 } |  | 
| 165 |  | 
| 166 TF_BUILTIN(AtomicsLoad, SharedArrayBufferBuiltinsAssembler) { |  | 
| 167   Node* array = Parameter(1); |  | 
| 168   Node* index = Parameter(2); |  | 
| 169   Node* context = Parameter(3 + 2); |  | 
| 170 |  | 
| 171   Node* instance_type; |  | 
| 172   Node* backing_store; |  | 
| 173   ValidateSharedTypedArray(array, context, &instance_type, &backing_store); |  | 
| 174 |  | 
| 175   Node* index_integer; |  | 
| 176   Node* index_word32 = |  | 
| 177       ConvertTaggedAtomicIndexToWord32(index, context, &index_integer); |  | 
| 178   Node* array_length_word32 = TruncateTaggedToWord32( |  | 
| 179       context, LoadObjectField(array, JSTypedArray::kLengthOffset)); |  | 
| 180   ValidateAtomicIndex(index_word32, array_length_word32, context); |  | 
| 181   Node* index_word = ChangeUint32ToWord(index_word32); |  | 
| 182 |  | 
| 183   Label i8(this), u8(this), i16(this), u16(this), i32(this), u32(this), |  | 
| 184       other(this); |  | 
| 185   int32_t case_values[] = { |  | 
| 186       FIXED_INT8_ARRAY_TYPE,   FIXED_UINT8_ARRAY_TYPE, FIXED_INT16_ARRAY_TYPE, |  | 
| 187       FIXED_UINT16_ARRAY_TYPE, FIXED_INT32_ARRAY_TYPE, FIXED_UINT32_ARRAY_TYPE, |  | 
| 188   }; |  | 
| 189   Label* case_labels[] = { |  | 
| 190       &i8, &u8, &i16, &u16, &i32, &u32, |  | 
| 191   }; |  | 
| 192   Switch(instance_type, &other, case_values, case_labels, |  | 
| 193          arraysize(case_labels)); |  | 
| 194 |  | 
| 195   Bind(&i8); |  | 
| 196   Return(SmiFromWord32( |  | 
| 197       AtomicLoad(MachineType::Int8(), backing_store, index_word))); |  | 
| 198 |  | 
| 199   Bind(&u8); |  | 
| 200   Return(SmiFromWord32( |  | 
| 201       AtomicLoad(MachineType::Uint8(), backing_store, index_word))); |  | 
| 202 |  | 
| 203   Bind(&i16); |  | 
| 204   Return(SmiFromWord32( |  | 
| 205       AtomicLoad(MachineType::Int16(), backing_store, WordShl(index_word, 1)))); |  | 
| 206 |  | 
| 207   Bind(&u16); |  | 
| 208   Return(SmiFromWord32(AtomicLoad(MachineType::Uint16(), backing_store, |  | 
| 209                                   WordShl(index_word, 1)))); |  | 
| 210 |  | 
| 211   Bind(&i32); |  | 
| 212   Return(ChangeInt32ToTagged( |  | 
| 213       AtomicLoad(MachineType::Int32(), backing_store, WordShl(index_word, 2)))); |  | 
| 214 |  | 
| 215   Bind(&u32); |  | 
| 216   Return(ChangeUint32ToTagged(AtomicLoad(MachineType::Uint32(), backing_store, |  | 
| 217                                          WordShl(index_word, 2)))); |  | 
| 218 |  | 
| 219   // This shouldn't happen, we've already validated the type. |  | 
| 220   Bind(&other); |  | 
| 221   Unreachable(); |  | 
| 222 } |  | 
| 223 |  | 
| 224 TF_BUILTIN(AtomicsStore, SharedArrayBufferBuiltinsAssembler) { |  | 
| 225   Node* array = Parameter(1); |  | 
| 226   Node* index = Parameter(2); |  | 
| 227   Node* value = Parameter(3); |  | 
| 228   Node* context = Parameter(4 + 2); |  | 
| 229 |  | 
| 230   Node* instance_type; |  | 
| 231   Node* backing_store; |  | 
| 232   ValidateSharedTypedArray(array, context, &instance_type, &backing_store); |  | 
| 233 |  | 
| 234   Node* index_integer; |  | 
| 235   Node* index_word32 = |  | 
| 236       ConvertTaggedAtomicIndexToWord32(index, context, &index_integer); |  | 
| 237   Node* array_length_word32 = TruncateTaggedToWord32( |  | 
| 238       context, LoadObjectField(array, JSTypedArray::kLengthOffset)); |  | 
| 239   ValidateAtomicIndex(index_word32, array_length_word32, context); |  | 
| 240   Node* index_word = ChangeUint32ToWord(index_word32); |  | 
| 241 |  | 
| 242   Node* value_integer = ToInteger(context, value); |  | 
| 243   Node* value_word32 = TruncateTaggedToWord32(context, value_integer); |  | 
| 244 |  | 
| 245   Label u8(this), u16(this), u32(this), other(this); |  | 
| 246   int32_t case_values[] = { |  | 
| 247       FIXED_INT8_ARRAY_TYPE,   FIXED_UINT8_ARRAY_TYPE, FIXED_INT16_ARRAY_TYPE, |  | 
| 248       FIXED_UINT16_ARRAY_TYPE, FIXED_INT32_ARRAY_TYPE, FIXED_UINT32_ARRAY_TYPE, |  | 
| 249   }; |  | 
| 250   Label* case_labels[] = { |  | 
| 251       &u8, &u8, &u16, &u16, &u32, &u32, |  | 
| 252   }; |  | 
| 253   Switch(instance_type, &other, case_values, case_labels, |  | 
| 254          arraysize(case_labels)); |  | 
| 255 |  | 
| 256   Bind(&u8); |  | 
| 257   AtomicStore(MachineRepresentation::kWord8, backing_store, index_word, |  | 
| 258               value_word32); |  | 
| 259   Return(value_integer); |  | 
| 260 |  | 
| 261   Bind(&u16); |  | 
| 262   AtomicStore(MachineRepresentation::kWord16, backing_store, |  | 
| 263               WordShl(index_word, 1), value_word32); |  | 
| 264   Return(value_integer); |  | 
| 265 |  | 
| 266   Bind(&u32); |  | 
| 267   AtomicStore(MachineRepresentation::kWord32, backing_store, |  | 
| 268               WordShl(index_word, 2), value_word32); |  | 
| 269   Return(value_integer); |  | 
| 270 |  | 
| 271   // This shouldn't happen, we've already validated the type. |  | 
| 272   Bind(&other); |  | 
| 273   Unreachable(); |  | 
| 274 } |  | 
| 275 |  | 
| 276 TF_BUILTIN(AtomicsExchange, SharedArrayBufferBuiltinsAssembler) { |  | 
| 277   Node* array = Parameter(1); |  | 
| 278   Node* index = Parameter(2); |  | 
| 279   Node* value = Parameter(3); |  | 
| 280   Node* context = Parameter(4 + 2); |  | 
| 281 |  | 
| 282   Node* instance_type; |  | 
| 283   Node* backing_store; |  | 
| 284   ValidateSharedTypedArray(array, context, &instance_type, &backing_store); |  | 
| 285 |  | 
| 286   Node* index_integer; |  | 
| 287   Node* index_word32 = |  | 
| 288       ConvertTaggedAtomicIndexToWord32(index, context, &index_integer); |  | 
| 289   Node* array_length_word32 = TruncateTaggedToWord32( |  | 
| 290       context, LoadObjectField(array, JSTypedArray::kLengthOffset)); |  | 
| 291   ValidateAtomicIndex(index_word32, array_length_word32, context); |  | 
| 292 |  | 
| 293   Node* value_integer = ToInteger(context, value); |  | 
| 294 |  | 
| 295 #if V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64 || V8_TARGET_ARCH_PPC64 || \ |  | 
| 296     V8_TARGET_ARCH_PPC |  | 
| 297   Return(CallRuntime(Runtime::kAtomicsExchange, context, array, index_integer, |  | 
| 298                      value_integer)); |  | 
| 299 #else |  | 
| 300   Node* index_word = ChangeUint32ToWord(index_word32); |  | 
| 301 |  | 
| 302   Node* value_word32 = TruncateTaggedToWord32(context, value_integer); |  | 
| 303 |  | 
| 304   Label i8(this), u8(this), i16(this), u16(this), i32(this), u32(this), |  | 
| 305       other(this); |  | 
| 306   int32_t case_values[] = { |  | 
| 307       FIXED_INT8_ARRAY_TYPE,   FIXED_UINT8_ARRAY_TYPE, FIXED_INT16_ARRAY_TYPE, |  | 
| 308       FIXED_UINT16_ARRAY_TYPE, FIXED_INT32_ARRAY_TYPE, FIXED_UINT32_ARRAY_TYPE, |  | 
| 309   }; |  | 
| 310   Label* case_labels[] = { |  | 
| 311       &i8, &u8, &i16, &u16, &i32, &u32, |  | 
| 312   }; |  | 
| 313   Switch(instance_type, &other, case_values, case_labels, |  | 
| 314          arraysize(case_labels)); |  | 
| 315 |  | 
| 316   Bind(&i8); |  | 
| 317   Return(SmiFromWord32(AtomicExchange(MachineType::Int8(), backing_store, |  | 
| 318                                       index_word, value_word32))); |  | 
| 319 |  | 
| 320   Bind(&u8); |  | 
| 321   Return(SmiFromWord32(AtomicExchange(MachineType::Uint8(), backing_store, |  | 
| 322                                       index_word, value_word32))); |  | 
| 323 |  | 
| 324   Bind(&i16); |  | 
| 325   Return(SmiFromWord32(AtomicExchange(MachineType::Int16(), backing_store, |  | 
| 326                                       WordShl(index_word, 1), value_word32))); |  | 
| 327 |  | 
| 328   Bind(&u16); |  | 
| 329   Return(SmiFromWord32(AtomicExchange(MachineType::Uint16(), backing_store, |  | 
| 330                                       WordShl(index_word, 1), value_word32))); |  | 
| 331 |  | 
| 332   Bind(&i32); |  | 
| 333   Return(ChangeInt32ToTagged(AtomicExchange(MachineType::Int32(), backing_store, |  | 
| 334                                             WordShl(index_word, 2), |  | 
| 335                                             value_word32))); |  | 
| 336 |  | 
| 337   Bind(&u32); |  | 
| 338   Return(ChangeUint32ToTagged( |  | 
| 339       AtomicExchange(MachineType::Uint32(), backing_store, |  | 
| 340                      WordShl(index_word, 2), value_word32))); |  | 
| 341 |  | 
| 342   // This shouldn't happen, we've already validated the type. |  | 
| 343   Bind(&other); |  | 
| 344   Unreachable(); |  | 
| 345 #endif  // V8_TARGET_ARCH_MIPS || V8_TARGET_ARCH_MIPS64 || V8_TARGET_ARCH_PPC64 |  | 
| 346         // || V8_TARGET_ARCH_PPC || V8_TARGET_ARCH_S390 || V8_TARGET_ARCH_S390X |  | 
| 347 } |  | 
| 348 |  | 
| 349 inline bool AtomicIsLockFree(uint32_t size) { | 36 inline bool AtomicIsLockFree(uint32_t size) { | 
| 350   return size == 1 || size == 2 || size == 4; | 37   return size == 1 || size == 2 || size == 4; | 
| 351 } | 38 } | 
| 352 | 39 | 
| 353 // ES #sec-atomics.islockfree | 40 // ES #sec-atomics.islockfree | 
| 354 BUILTIN(AtomicsIsLockFree) { | 41 BUILTIN(AtomicsIsLockFree) { | 
| 355   HandleScope scope(isolate); | 42   HandleScope scope(isolate); | 
| 356   Handle<Object> size = args.atOrUndefined(isolate, 1); | 43   Handle<Object> size = args.atOrUndefined(isolate, 1); | 
| 357   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, size, Object::ToNumber(size)); | 44   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, size, Object::ToNumber(size)); | 
| 358   return *isolate->factory()->ToBoolean(AtomicIsLockFree(size->Number())); | 45   return *isolate->factory()->ToBoolean(AtomicIsLockFree(size->Number())); | 
| (...skipping 578 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 937     default: | 624     default: | 
| 938       break; | 625       break; | 
| 939   } | 626   } | 
| 940 | 627 | 
| 941   UNREACHABLE(); | 628   UNREACHABLE(); | 
| 942   return isolate->heap()->undefined_value(); | 629   return isolate->heap()->undefined_value(); | 
| 943 } | 630 } | 
| 944 | 631 | 
| 945 }  // namespace internal | 632 }  // namespace internal | 
| 946 }  // namespace v8 | 633 }  // namespace v8 | 
| OLD | NEW | 
|---|