• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2015 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/arguments-inl.h"
6 #include "src/base/macros.h"
7 #include "src/base/platform/mutex.h"
8 #include "src/conversions-inl.h"
9 #include "src/heap/factory.h"
10 #include "src/objects/js-array-buffer-inl.h"
11 #include "src/runtime/runtime-utils.h"
12 
13 // Implement Atomic accesses to SharedArrayBuffers as defined in the
14 // SharedArrayBuffer draft spec, found here
15 // https://github.com/tc39/ecmascript_sharedmem
16 
17 namespace v8 {
18 namespace internal {
19 
20 namespace {
21 
22 #if V8_CC_GNU
23 
24 template <typename T>
ExchangeSeqCst(T * p,T value)25 inline T ExchangeSeqCst(T* p, T value) {
26   return __atomic_exchange_n(p, value, __ATOMIC_SEQ_CST);
27 }
28 
29 template <typename T>
CompareExchangeSeqCst(T * p,T oldval,T newval)30 inline T CompareExchangeSeqCst(T* p, T oldval, T newval) {
31   (void)__atomic_compare_exchange_n(p, &oldval, newval, 0, __ATOMIC_SEQ_CST,
32                                     __ATOMIC_SEQ_CST);
33   return oldval;
34 }
35 
36 template <typename T>
AddSeqCst(T * p,T value)37 inline T AddSeqCst(T* p, T value) {
38   return __atomic_fetch_add(p, value, __ATOMIC_SEQ_CST);
39 }
40 
41 template <typename T>
SubSeqCst(T * p,T value)42 inline T SubSeqCst(T* p, T value) {
43   return __atomic_fetch_sub(p, value, __ATOMIC_SEQ_CST);
44 }
45 
46 template <typename T>
AndSeqCst(T * p,T value)47 inline T AndSeqCst(T* p, T value) {
48   return __atomic_fetch_and(p, value, __ATOMIC_SEQ_CST);
49 }
50 
51 template <typename T>
OrSeqCst(T * p,T value)52 inline T OrSeqCst(T* p, T value) {
53   return __atomic_fetch_or(p, value, __ATOMIC_SEQ_CST);
54 }
55 
56 template <typename T>
XorSeqCst(T * p,T value)57 inline T XorSeqCst(T* p, T value) {
58   return __atomic_fetch_xor(p, value, __ATOMIC_SEQ_CST);
59 }
60 
61 #elif V8_CC_MSVC
62 
63 #define InterlockedExchange32 _InterlockedExchange
64 #define InterlockedCompareExchange32 _InterlockedCompareExchange
65 #define InterlockedCompareExchange8 _InterlockedCompareExchange8
66 #define InterlockedExchangeAdd32 _InterlockedExchangeAdd
67 #define InterlockedExchangeAdd16 _InterlockedExchangeAdd16
68 #define InterlockedExchangeAdd8 _InterlockedExchangeAdd8
69 #define InterlockedAnd32 _InterlockedAnd
70 #define InterlockedOr32 _InterlockedOr
71 #define InterlockedXor32 _InterlockedXor
72 
73 #define ATOMIC_OPS(type, suffix, vctype)                                    \
74   inline type ExchangeSeqCst(type* p, type value) {                         \
75     return InterlockedExchange##suffix(reinterpret_cast<vctype*>(p),        \
76                                        bit_cast<vctype>(value));            \
77   }                                                                         \
78   inline type CompareExchangeSeqCst(type* p, type oldval, type newval) {    \
79     return InterlockedCompareExchange##suffix(reinterpret_cast<vctype*>(p), \
80                                               bit_cast<vctype>(newval),     \
81                                               bit_cast<vctype>(oldval));    \
82   }                                                                         \
83   inline type AddSeqCst(type* p, type value) {                              \
84     return InterlockedExchangeAdd##suffix(reinterpret_cast<vctype*>(p),     \
85                                           bit_cast<vctype>(value));         \
86   }                                                                         \
87   inline type SubSeqCst(type* p, type value) {                              \
88     return InterlockedExchangeAdd##suffix(reinterpret_cast<vctype*>(p),     \
89                                           -bit_cast<vctype>(value));        \
90   }                                                                         \
91   inline type AndSeqCst(type* p, type value) {                              \
92     return InterlockedAnd##suffix(reinterpret_cast<vctype*>(p),             \
93                                   bit_cast<vctype>(value));                 \
94   }                                                                         \
95   inline type OrSeqCst(type* p, type value) {                               \
96     return InterlockedOr##suffix(reinterpret_cast<vctype*>(p),              \
97                                  bit_cast<vctype>(value));                  \
98   }                                                                         \
99   inline type XorSeqCst(type* p, type value) {                              \
100     return InterlockedXor##suffix(reinterpret_cast<vctype*>(p),             \
101                                   bit_cast<vctype>(value));                 \
102   }
103 
104 ATOMIC_OPS(int8_t, 8, char)
105 ATOMIC_OPS(uint8_t, 8, char)
106 ATOMIC_OPS(int16_t, 16, short)  /* NOLINT(runtime/int) */
107 ATOMIC_OPS(uint16_t, 16, short) /* NOLINT(runtime/int) */
108 ATOMIC_OPS(int32_t, 32, long)   /* NOLINT(runtime/int) */
109 ATOMIC_OPS(uint32_t, 32, long)  /* NOLINT(runtime/int) */
110 
111 #undef ATOMIC_OPS
112 
113 #undef InterlockedExchange32
114 #undef InterlockedCompareExchange32
115 #undef InterlockedCompareExchange8
116 #undef InterlockedExchangeAdd32
117 #undef InterlockedExchangeAdd16
118 #undef InterlockedExchangeAdd8
119 #undef InterlockedAnd32
120 #undef InterlockedOr32
121 #undef InterlockedXor32
122 
123 #else
124 
125 #error Unsupported platform!
126 
127 #endif
128 
129 template <typename T>
130 T FromObject(Handle<Object> number);
131 
132 template <>
FromObject(Handle<Object> number)133 inline uint8_t FromObject<uint8_t>(Handle<Object> number) {
134   return NumberToUint32(*number);
135 }
136 
137 template <>
FromObject(Handle<Object> number)138 inline int8_t FromObject<int8_t>(Handle<Object> number) {
139   return NumberToInt32(*number);
140 }
141 
142 template <>
FromObject(Handle<Object> number)143 inline uint16_t FromObject<uint16_t>(Handle<Object> number) {
144   return NumberToUint32(*number);
145 }
146 
147 template <>
FromObject(Handle<Object> number)148 inline int16_t FromObject<int16_t>(Handle<Object> number) {
149   return NumberToInt32(*number);
150 }
151 
152 template <>
FromObject(Handle<Object> number)153 inline uint32_t FromObject<uint32_t>(Handle<Object> number) {
154   return NumberToUint32(*number);
155 }
156 
157 template <>
FromObject(Handle<Object> number)158 inline int32_t FromObject<int32_t>(Handle<Object> number) {
159   return NumberToInt32(*number);
160 }
161 
162 
ToObject(Isolate * isolate,int8_t t)163 inline Object* ToObject(Isolate* isolate, int8_t t) { return Smi::FromInt(t); }
164 
ToObject(Isolate * isolate,uint8_t t)165 inline Object* ToObject(Isolate* isolate, uint8_t t) { return Smi::FromInt(t); }
166 
ToObject(Isolate * isolate,int16_t t)167 inline Object* ToObject(Isolate* isolate, int16_t t) { return Smi::FromInt(t); }
168 
ToObject(Isolate * isolate,uint16_t t)169 inline Object* ToObject(Isolate* isolate, uint16_t t) {
170   return Smi::FromInt(t);
171 }
172 
ToObject(Isolate * isolate,int32_t t)173 inline Object* ToObject(Isolate* isolate, int32_t t) {
174   return *isolate->factory()->NewNumber(t);
175 }
176 
ToObject(Isolate * isolate,uint32_t t)177 inline Object* ToObject(Isolate* isolate, uint32_t t) {
178   return *isolate->factory()->NewNumber(t);
179 }
180 
181 template <typename T>
DoExchange(Isolate * isolate,void * buffer,size_t index,Handle<Object> obj)182 inline Object* DoExchange(Isolate* isolate, void* buffer, size_t index,
183                           Handle<Object> obj) {
184   T value = FromObject<T>(obj);
185   T result = ExchangeSeqCst(static_cast<T*>(buffer) + index, value);
186   return ToObject(isolate, result);
187 }
188 
189 template <typename T>
DoCompareExchange(Isolate * isolate,void * buffer,size_t index,Handle<Object> oldobj,Handle<Object> newobj)190 inline Object* DoCompareExchange(Isolate* isolate, void* buffer, size_t index,
191                                  Handle<Object> oldobj, Handle<Object> newobj) {
192   T oldval = FromObject<T>(oldobj);
193   T newval = FromObject<T>(newobj);
194   T result =
195       CompareExchangeSeqCst(static_cast<T*>(buffer) + index, oldval, newval);
196   return ToObject(isolate, result);
197 }
198 
199 template <typename T>
DoAdd(Isolate * isolate,void * buffer,size_t index,Handle<Object> obj)200 inline Object* DoAdd(Isolate* isolate, void* buffer, size_t index,
201                      Handle<Object> obj) {
202   T value = FromObject<T>(obj);
203   T result = AddSeqCst(static_cast<T*>(buffer) + index, value);
204   return ToObject(isolate, result);
205 }
206 
207 template <typename T>
DoSub(Isolate * isolate,void * buffer,size_t index,Handle<Object> obj)208 inline Object* DoSub(Isolate* isolate, void* buffer, size_t index,
209                      Handle<Object> obj) {
210   T value = FromObject<T>(obj);
211   T result = SubSeqCst(static_cast<T*>(buffer) + index, value);
212   return ToObject(isolate, result);
213 }
214 
215 template <typename T>
DoAnd(Isolate * isolate,void * buffer,size_t index,Handle<Object> obj)216 inline Object* DoAnd(Isolate* isolate, void* buffer, size_t index,
217                      Handle<Object> obj) {
218   T value = FromObject<T>(obj);
219   T result = AndSeqCst(static_cast<T*>(buffer) + index, value);
220   return ToObject(isolate, result);
221 }
222 
223 template <typename T>
DoOr(Isolate * isolate,void * buffer,size_t index,Handle<Object> obj)224 inline Object* DoOr(Isolate* isolate, void* buffer, size_t index,
225                     Handle<Object> obj) {
226   T value = FromObject<T>(obj);
227   T result = OrSeqCst(static_cast<T*>(buffer) + index, value);
228   return ToObject(isolate, result);
229 }
230 
231 template <typename T>
DoXor(Isolate * isolate,void * buffer,size_t index,Handle<Object> obj)232 inline Object* DoXor(Isolate* isolate, void* buffer, size_t index,
233                      Handle<Object> obj) {
234   T value = FromObject<T>(obj);
235   T result = XorSeqCst(static_cast<T*>(buffer) + index, value);
236   return ToObject(isolate, result);
237 }
238 
239 }  // anonymous namespace
240 
241 // Duplicated from objects.h
242 // V has parameters (Type, type, TYPE, C type)
243 #define INTEGER_TYPED_ARRAYS(V)       \
244   V(Uint8, uint8, UINT8, uint8_t)     \
245   V(Int8, int8, INT8, int8_t)         \
246   V(Uint16, uint16, UINT16, uint16_t) \
247   V(Int16, int16, INT16, int16_t)     \
248   V(Uint32, uint32, UINT32, uint32_t) \
249   V(Int32, int32, INT32, int32_t)
250 
RUNTIME_FUNCTION(Runtime_AtomicsExchange)251 RUNTIME_FUNCTION(Runtime_AtomicsExchange) {
252   HandleScope scope(isolate);
253   DCHECK_EQ(3, args.length());
254   CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, sta, 0);
255   CONVERT_SIZE_ARG_CHECKED(index, 1);
256   CONVERT_NUMBER_ARG_HANDLE_CHECKED(value, 2);
257   CHECK(sta->GetBuffer()->is_shared());
258   CHECK_LT(index, NumberToSize(sta->length()));
259 
260   uint8_t* source = static_cast<uint8_t*>(sta->GetBuffer()->backing_store()) +
261                     NumberToSize(sta->byte_offset());
262 
263   switch (sta->type()) {
264 #define TYPED_ARRAY_CASE(Type, typeName, TYPE, ctype) \
265   case kExternal##Type##Array:                        \
266     return DoExchange<ctype>(isolate, source, index, value);
267 
268     INTEGER_TYPED_ARRAYS(TYPED_ARRAY_CASE)
269 #undef TYPED_ARRAY_CASE
270 
271     default:
272       break;
273   }
274 
275   UNREACHABLE();
276 }
277 
RUNTIME_FUNCTION(Runtime_AtomicsCompareExchange)278 RUNTIME_FUNCTION(Runtime_AtomicsCompareExchange) {
279   HandleScope scope(isolate);
280   DCHECK_EQ(4, args.length());
281   CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, sta, 0);
282   CONVERT_SIZE_ARG_CHECKED(index, 1);
283   CONVERT_NUMBER_ARG_HANDLE_CHECKED(oldobj, 2);
284   CONVERT_NUMBER_ARG_HANDLE_CHECKED(newobj, 3);
285   CHECK(sta->GetBuffer()->is_shared());
286   CHECK_LT(index, NumberToSize(sta->length()));
287 
288   uint8_t* source = static_cast<uint8_t*>(sta->GetBuffer()->backing_store()) +
289                     NumberToSize(sta->byte_offset());
290 
291   switch (sta->type()) {
292 #define TYPED_ARRAY_CASE(Type, typeName, TYPE, ctype) \
293   case kExternal##Type##Array:                        \
294     return DoCompareExchange<ctype>(isolate, source, index, oldobj, newobj);
295 
296     INTEGER_TYPED_ARRAYS(TYPED_ARRAY_CASE)
297 #undef TYPED_ARRAY_CASE
298 
299     default:
300       break;
301   }
302 
303   UNREACHABLE();
304 }
305 
306 // ES #sec-atomics.add
307 // Atomics.add( typedArray, index, value )
RUNTIME_FUNCTION(Runtime_AtomicsAdd)308 RUNTIME_FUNCTION(Runtime_AtomicsAdd) {
309   HandleScope scope(isolate);
310   DCHECK_EQ(3, args.length());
311   CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, sta, 0);
312   CONVERT_SIZE_ARG_CHECKED(index, 1);
313   CONVERT_NUMBER_ARG_HANDLE_CHECKED(value, 2);
314   CHECK(sta->GetBuffer()->is_shared());
315   CHECK_LT(index, NumberToSize(sta->length()));
316 
317   uint8_t* source = static_cast<uint8_t*>(sta->GetBuffer()->backing_store()) +
318                     NumberToSize(sta->byte_offset());
319 
320   switch (sta->type()) {
321 #define TYPED_ARRAY_CASE(Type, typeName, TYPE, ctype) \
322   case kExternal##Type##Array:                        \
323     return DoAdd<ctype>(isolate, source, index, value);
324 
325     INTEGER_TYPED_ARRAYS(TYPED_ARRAY_CASE)
326 #undef TYPED_ARRAY_CASE
327 
328     default:
329       break;
330   }
331 
332   UNREACHABLE();
333 }
334 
335 // ES #sec-atomics.sub
336 // Atomics.sub( typedArray, index, value )
RUNTIME_FUNCTION(Runtime_AtomicsSub)337 RUNTIME_FUNCTION(Runtime_AtomicsSub) {
338   HandleScope scope(isolate);
339   DCHECK_EQ(3, args.length());
340   CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, sta, 0);
341   CONVERT_SIZE_ARG_CHECKED(index, 1);
342   CONVERT_NUMBER_ARG_HANDLE_CHECKED(value, 2);
343   CHECK(sta->GetBuffer()->is_shared());
344   CHECK_LT(index, NumberToSize(sta->length()));
345 
346   uint8_t* source = static_cast<uint8_t*>(sta->GetBuffer()->backing_store()) +
347                     NumberToSize(sta->byte_offset());
348 
349   switch (sta->type()) {
350 #define TYPED_ARRAY_CASE(Type, typeName, TYPE, ctype) \
351   case kExternal##Type##Array:                        \
352     return DoSub<ctype>(isolate, source, index, value);
353 
354     INTEGER_TYPED_ARRAYS(TYPED_ARRAY_CASE)
355 #undef TYPED_ARRAY_CASE
356 
357     default:
358       break;
359   }
360 
361   UNREACHABLE();
362 }
363 
364 // ES #sec-atomics.and
365 // Atomics.and( typedArray, index, value )
RUNTIME_FUNCTION(Runtime_AtomicsAnd)366 RUNTIME_FUNCTION(Runtime_AtomicsAnd) {
367   HandleScope scope(isolate);
368   DCHECK_EQ(3, args.length());
369   CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, sta, 0);
370   CONVERT_SIZE_ARG_CHECKED(index, 1);
371   CONVERT_NUMBER_ARG_HANDLE_CHECKED(value, 2);
372   CHECK(sta->GetBuffer()->is_shared());
373   CHECK_LT(index, NumberToSize(sta->length()));
374 
375   uint8_t* source = static_cast<uint8_t*>(sta->GetBuffer()->backing_store()) +
376                     NumberToSize(sta->byte_offset());
377 
378   switch (sta->type()) {
379 #define TYPED_ARRAY_CASE(Type, typeName, TYPE, ctype) \
380   case kExternal##Type##Array:                        \
381     return DoAnd<ctype>(isolate, source, index, value);
382 
383     INTEGER_TYPED_ARRAYS(TYPED_ARRAY_CASE)
384 #undef TYPED_ARRAY_CASE
385 
386     default:
387       break;
388   }
389 
390   UNREACHABLE();
391 }
392 
393 // ES #sec-atomics.or
394 // Atomics.or( typedArray, index, value )
RUNTIME_FUNCTION(Runtime_AtomicsOr)395 RUNTIME_FUNCTION(Runtime_AtomicsOr) {
396   HandleScope scope(isolate);
397   DCHECK_EQ(3, args.length());
398   CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, sta, 0);
399   CONVERT_SIZE_ARG_CHECKED(index, 1);
400   CONVERT_NUMBER_ARG_HANDLE_CHECKED(value, 2);
401   CHECK(sta->GetBuffer()->is_shared());
402   CHECK_LT(index, NumberToSize(sta->length()));
403 
404   uint8_t* source = static_cast<uint8_t*>(sta->GetBuffer()->backing_store()) +
405                     NumberToSize(sta->byte_offset());
406 
407   switch (sta->type()) {
408 #define TYPED_ARRAY_CASE(Type, typeName, TYPE, ctype) \
409   case kExternal##Type##Array:                        \
410     return DoOr<ctype>(isolate, source, index, value);
411 
412     INTEGER_TYPED_ARRAYS(TYPED_ARRAY_CASE)
413 #undef TYPED_ARRAY_CASE
414 
415     default:
416       break;
417   }
418 
419   UNREACHABLE();
420 }
421 
422 // ES #sec-atomics.xor
423 // Atomics.xor( typedArray, index, value )
RUNTIME_FUNCTION(Runtime_AtomicsXor)424 RUNTIME_FUNCTION(Runtime_AtomicsXor) {
425   HandleScope scope(isolate);
426   DCHECK_EQ(3, args.length());
427   CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, sta, 0);
428   CONVERT_SIZE_ARG_CHECKED(index, 1);
429   CONVERT_NUMBER_ARG_HANDLE_CHECKED(value, 2);
430   CHECK(sta->GetBuffer()->is_shared());
431   CHECK_LT(index, NumberToSize(sta->length()));
432 
433   uint8_t* source = static_cast<uint8_t*>(sta->GetBuffer()->backing_store()) +
434                     NumberToSize(sta->byte_offset());
435 
436   switch (sta->type()) {
437 #define TYPED_ARRAY_CASE(Type, typeName, TYPE, ctype) \
438   case kExternal##Type##Array:                        \
439     return DoXor<ctype>(isolate, source, index, value);
440 
441     INTEGER_TYPED_ARRAYS(TYPED_ARRAY_CASE)
442 #undef TYPED_ARRAY_CASE
443 
444     default:
445       break;
446   }
447 
448   UNREACHABLE();
449 }
450 
451 #undef INTEGER_TYPED_ARRAYS
452 
453 }  // namespace internal
454 }  // namespace v8
455