1/*
2 * Copyright (C) 2016-2019 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "AtomicsObject.h"
28
29#include "FrameTracers.h"
30#include "JSCInlines.h"
31#include "JSTypedArrays.h"
32#include "ObjectPrototype.h"
33#include "ReleaseHeapAccessScope.h"
34#include "TypedArrayController.h"
35
36namespace JSC {
37
38STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(AtomicsObject);
39
40#define FOR_EACH_ATOMICS_FUNC(macro) \
41 macro(add, Add, 3) \
42 macro(and, And, 3) \
43 macro(compareExchange, CompareExchange, 4) \
44 macro(exchange, Exchange, 3) \
45 macro(isLockFree, IsLockFree, 1) \
46 macro(load, Load, 2) \
47 macro(or, Or, 3) \
48 macro(store, Store, 3) \
49 macro(sub, Sub, 3) \
50 macro(wait, Wait, 4) \
51 macro(wake, Wake, 3) \
52 macro(xor, Xor, 3)
53
54#define DECLARE_FUNC_PROTO(lowerName, upperName, count) \
55 EncodedJSValue JSC_HOST_CALL atomicsFunc ## upperName(JSGlobalObject*, CallFrame*);
56FOR_EACH_ATOMICS_FUNC(DECLARE_FUNC_PROTO)
57#undef DECLARE_FUNC_PROTO
58
59const ClassInfo AtomicsObject::s_info = { "Atomics", &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(AtomicsObject) };
60
61AtomicsObject::AtomicsObject(VM& vm, Structure* structure)
62 : JSNonFinalObject(vm, structure)
63{
64}
65
66AtomicsObject* AtomicsObject::create(VM& vm, JSGlobalObject* globalObject, Structure* structure)
67{
68 AtomicsObject* object = new (NotNull, allocateCell<AtomicsObject>(vm.heap)) AtomicsObject(vm, structure);
69 object->finishCreation(vm, globalObject);
70 return object;
71}
72
73Structure* AtomicsObject::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
74{
75 return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info());
76}
77
78void AtomicsObject::finishCreation(VM& vm, JSGlobalObject* globalObject)
79{
80 Base::finishCreation(vm);
81 ASSERT(inherits(vm, info()));
82
83#define PUT_DIRECT_NATIVE_FUNC(lowerName, upperName, count) \
84 putDirectNativeFunctionWithoutTransition(vm, globalObject, Identifier::fromString(vm, #lowerName), count, atomicsFunc ## upperName, Atomics ## upperName ## Intrinsic, static_cast<unsigned>(PropertyAttribute::DontEnum));
85 FOR_EACH_ATOMICS_FUNC(PUT_DIRECT_NATIVE_FUNC)
86#undef PUT_DIRECT_NATIVE_FUNC
87}
88
89namespace {
90
91template<typename Adaptor, typename Func>
92EncodedJSValue atomicOperationWithArgsCase(JSGlobalObject* globalObject, const JSValue* args, ThrowScope& scope, JSArrayBufferView* typedArrayView, unsigned accessIndex, const Func& func)
93{
94 JSGenericTypedArrayView<Adaptor>* typedArray = jsCast<JSGenericTypedArrayView<Adaptor>*>(typedArrayView);
95
96 double extraArgs[Func::numExtraArgs + 1]; // Add 1 to avoid 0 size array error in VS.
97 for (unsigned i = 0; i < Func::numExtraArgs; ++i) {
98 double value = args[2 + i].toInteger(globalObject);
99 RETURN_IF_EXCEPTION(scope, JSValue::encode(jsUndefined()));
100 extraArgs[i] = value;
101 }
102
103 return JSValue::encode(func(typedArray->typedVector() + accessIndex, extraArgs));
104}
105
106unsigned validatedAccessIndex(VM& vm, JSGlobalObject* globalObject, JSValue accessIndexValue, JSArrayBufferView* typedArrayView)
107{
108 auto scope = DECLARE_THROW_SCOPE(vm);
109 if (UNLIKELY(!accessIndexValue.isInt32())) {
110 double accessIndexDouble = accessIndexValue.toNumber(globalObject);
111 RETURN_IF_EXCEPTION(scope, 0);
112 if (accessIndexDouble == 0)
113 accessIndexValue = jsNumber(0);
114 else {
115 accessIndexValue = jsNumber(accessIndexDouble);
116 if (!accessIndexValue.isInt32()) {
117 throwRangeError(globalObject, scope, "Access index is not an integer."_s);
118 return 0;
119 }
120 }
121 }
122 int32_t accessIndex = accessIndexValue.asInt32();
123
124 ASSERT(typedArrayView->length() <= static_cast<unsigned>(INT_MAX));
125 if (static_cast<unsigned>(accessIndex) >= typedArrayView->length()) {
126 throwRangeError(globalObject, scope, "Access index out of bounds for atomic access."_s);
127 return 0;
128 }
129
130 return accessIndex;
131}
132
133template<typename Func>
134EncodedJSValue atomicOperationWithArgs(VM& vm, JSGlobalObject* globalObject, const JSValue* args, const Func& func)
135{
136 auto scope = DECLARE_THROW_SCOPE(vm);
137
138 JSValue typedArrayValue = args[0];
139 if (!typedArrayValue.isCell()) {
140 throwTypeError(globalObject, scope, "Typed array argument must be a cell."_s);
141 return JSValue::encode(jsUndefined());
142 }
143
144 JSCell* typedArrayCell = typedArrayValue.asCell();
145
146 JSType type = typedArrayCell->type();
147 switch (type) {
148 case Int8ArrayType:
149 case Int16ArrayType:
150 case Int32ArrayType:
151 case Uint8ArrayType:
152 case Uint16ArrayType:
153 case Uint32ArrayType:
154 break;
155 default:
156 throwTypeError(globalObject, scope, "Typed array argument must be an Int8Array, Int16Array, Int32Array, Uint8Array, Uint16Array, or Uint32Array."_s);
157 return JSValue::encode(jsUndefined());
158 }
159
160 JSArrayBufferView* typedArrayView = jsCast<JSArrayBufferView*>(typedArrayCell);
161 if (!typedArrayView->isShared()) {
162 throwTypeError(globalObject, scope, "Typed array argument must wrap a SharedArrayBuffer."_s);
163 return JSValue::encode(jsUndefined());
164 }
165
166 unsigned accessIndex = validatedAccessIndex(vm, globalObject, args[1], typedArrayView);
167 RETURN_IF_EXCEPTION(scope, JSValue::encode(jsUndefined()));
168
169 switch (type) {
170 case Int8ArrayType:
171 return atomicOperationWithArgsCase<Int8Adaptor>(globalObject, args, scope, typedArrayView, accessIndex, func);
172 case Int16ArrayType:
173 return atomicOperationWithArgsCase<Int16Adaptor>(globalObject, args, scope, typedArrayView, accessIndex, func);
174 case Int32ArrayType:
175 return atomicOperationWithArgsCase<Int32Adaptor>(globalObject, args, scope, typedArrayView, accessIndex, func);
176 case Uint8ArrayType:
177 return atomicOperationWithArgsCase<Uint8Adaptor>(globalObject, args, scope, typedArrayView, accessIndex, func);
178 case Uint16ArrayType:
179 return atomicOperationWithArgsCase<Uint16Adaptor>(globalObject, args, scope, typedArrayView, accessIndex, func);
180 case Uint32ArrayType:
181 return atomicOperationWithArgsCase<Uint32Adaptor>(globalObject, args, scope, typedArrayView, accessIndex, func);
182 default:
183 RELEASE_ASSERT_NOT_REACHED();
184 return JSValue::encode(jsUndefined());
185 }
186}
187
188template<typename Func>
189EncodedJSValue atomicOperationWithArgs(JSGlobalObject* globalObject, CallFrame* callFrame, const Func& func)
190{
191 JSValue args[2 + Func::numExtraArgs];
192 for (unsigned i = 2 + Func::numExtraArgs; i--;)
193 args[i] = callFrame->argument(i);
194 return atomicOperationWithArgs(globalObject->vm(), globalObject, args, func);
195}
196
197struct AddFunc {
198 static constexpr unsigned numExtraArgs = 1;
199
200 template<typename T>
201 JSValue operator()(T* ptr, const double* args) const
202 {
203 return jsNumber(WTF::atomicExchangeAdd(ptr, toInt32(args[0])));
204 }
205};
206
207struct AndFunc {
208 static constexpr unsigned numExtraArgs = 1;
209
210 template<typename T>
211 JSValue operator()(T* ptr, const double* args) const
212 {
213 return jsNumber(WTF::atomicExchangeAnd(ptr, toInt32(args[0])));
214 }
215};
216
217struct CompareExchangeFunc {
218 static constexpr unsigned numExtraArgs = 2;
219
220 template<typename T>
221 JSValue operator()(T* ptr, const double* args) const
222 {
223 T expected = static_cast<T>(toInt32(args[0]));
224 T newValue = static_cast<T>(toInt32(args[1]));
225 return jsNumber(WTF::atomicCompareExchangeStrong(ptr, expected, newValue));
226 }
227};
228
229struct ExchangeFunc {
230 static constexpr unsigned numExtraArgs = 1;
231
232 template<typename T>
233 JSValue operator()(T* ptr, const double* args) const
234 {
235 return jsNumber(WTF::atomicExchange(ptr, static_cast<T>(toInt32(args[0]))));
236 }
237};
238
239struct LoadFunc {
240 static constexpr unsigned numExtraArgs = 0;
241
242 template<typename T>
243 JSValue operator()(T* ptr, const double*) const
244 {
245 return jsNumber(WTF::atomicLoadFullyFenced(ptr));
246 }
247};
248
249struct OrFunc {
250 static constexpr unsigned numExtraArgs = 1;
251
252 template<typename T>
253 JSValue operator()(T* ptr, const double* args) const
254 {
255 return jsNumber(WTF::atomicExchangeOr(ptr, toInt32(args[0])));
256 }
257};
258
259struct StoreFunc {
260 static constexpr unsigned numExtraArgs = 1;
261
262 template<typename T>
263 JSValue operator()(T* ptr, const double* args) const
264 {
265 double valueAsInt = args[0];
266 T valueAsT = static_cast<T>(toInt32(valueAsInt));
267 WTF::atomicStoreFullyFenced(ptr, valueAsT);
268 return jsNumber(valueAsInt);
269 }
270};
271
272struct SubFunc {
273 static constexpr unsigned numExtraArgs = 1;
274
275 template<typename T>
276 JSValue operator()(T* ptr, const double* args) const
277 {
278 return jsNumber(WTF::atomicExchangeSub(ptr, toInt32(args[0])));
279 }
280};
281
282struct XorFunc {
283 static constexpr unsigned numExtraArgs = 1;
284
285 template<typename T>
286 JSValue operator()(T* ptr, const double* args) const
287 {
288 return jsNumber(WTF::atomicExchangeXor(ptr, toInt32(args[0])));
289 }
290};
291
292EncodedJSValue isLockFree(JSGlobalObject* globalObject, JSValue arg)
293{
294 VM& vm = globalObject->vm();
295 auto scope = DECLARE_THROW_SCOPE(vm);
296
297 int32_t size = arg.toInt32(globalObject);
298 RETURN_IF_EXCEPTION(scope, JSValue::encode(jsUndefined()));
299
300 bool result;
301 switch (size) {
302 case 1:
303 case 2:
304 case 4:
305 result = true;
306 break;
307 default:
308 result = false;
309 break;
310 }
311 return JSValue::encode(jsBoolean(result));
312}
313
314} // anonymous namespace
315
316EncodedJSValue JSC_HOST_CALL atomicsFuncAdd(JSGlobalObject* globalObject, CallFrame* callFrame)
317{
318 return atomicOperationWithArgs(globalObject, callFrame, AddFunc());
319}
320
321EncodedJSValue JSC_HOST_CALL atomicsFuncAnd(JSGlobalObject* globalObject, CallFrame* callFrame)
322{
323 return atomicOperationWithArgs(globalObject, callFrame, AndFunc());
324}
325
326EncodedJSValue JSC_HOST_CALL atomicsFuncCompareExchange(JSGlobalObject* globalObject, CallFrame* callFrame)
327{
328 return atomicOperationWithArgs(globalObject, callFrame, CompareExchangeFunc());
329}
330
331EncodedJSValue JSC_HOST_CALL atomicsFuncExchange(JSGlobalObject* globalObject, CallFrame* callFrame)
332{
333 return atomicOperationWithArgs(globalObject, callFrame, ExchangeFunc());
334}
335
336EncodedJSValue JSC_HOST_CALL atomicsFuncIsLockFree(JSGlobalObject* globalObject, CallFrame* callFrame)
337{
338 return isLockFree(globalObject, callFrame->argument(0));
339}
340
341EncodedJSValue JSC_HOST_CALL atomicsFuncLoad(JSGlobalObject* globalObject, CallFrame* callFrame)
342{
343 return atomicOperationWithArgs(globalObject, callFrame, LoadFunc());
344}
345
346EncodedJSValue JSC_HOST_CALL atomicsFuncOr(JSGlobalObject* globalObject, CallFrame* callFrame)
347{
348 return atomicOperationWithArgs(globalObject, callFrame, OrFunc());
349}
350
351EncodedJSValue JSC_HOST_CALL atomicsFuncStore(JSGlobalObject* globalObject, CallFrame* callFrame)
352{
353 return atomicOperationWithArgs(globalObject, callFrame, StoreFunc());
354}
355
356EncodedJSValue JSC_HOST_CALL atomicsFuncSub(JSGlobalObject* globalObject, CallFrame* callFrame)
357{
358 return atomicOperationWithArgs(globalObject, callFrame, SubFunc());
359}
360
361EncodedJSValue JSC_HOST_CALL atomicsFuncWait(JSGlobalObject* globalObject, CallFrame* callFrame)
362{
363 VM& vm = globalObject->vm();
364 auto scope = DECLARE_THROW_SCOPE(vm);
365
366 JSInt32Array* typedArray = jsDynamicCast<JSInt32Array*>(vm, callFrame->argument(0));
367 if (!typedArray) {
368 throwTypeError(globalObject, scope, "Typed array for wait/wake must be an Int32Array."_s);
369 return JSValue::encode(jsUndefined());
370 }
371
372 if (!typedArray->isShared()) {
373 throwTypeError(globalObject, scope, "Typed array for wait/wake must wrap a SharedArrayBuffer."_s);
374 return JSValue::encode(jsUndefined());
375 }
376
377 unsigned accessIndex = validatedAccessIndex(vm, globalObject, callFrame->argument(1), typedArray);
378 RETURN_IF_EXCEPTION(scope, JSValue::encode(jsUndefined()));
379
380 int32_t* ptr = typedArray->typedVector() + accessIndex;
381
382 int32_t expectedValue = callFrame->argument(2).toInt32(globalObject);
383 RETURN_IF_EXCEPTION(scope, JSValue::encode(jsUndefined()));
384
385 double timeoutInMilliseconds = callFrame->argument(3).toNumber(globalObject);
386 RETURN_IF_EXCEPTION(scope, JSValue::encode(jsUndefined()));
387
388 if (!vm.m_typedArrayController->isAtomicsWaitAllowedOnCurrentThread()) {
389 throwTypeError(globalObject, scope, "Atomics.wait cannot be called from the current thread."_s);
390 return JSValue::encode(jsUndefined());
391 }
392
393 Seconds timeout = Seconds::fromMilliseconds(timeoutInMilliseconds);
394
395 // This covers the proposed rule:
396 //
397 // 4. If timeout is not provided or is undefined then let t be +inf. Otherwise:
398 // a. Let q be ? ToNumber(timeout).
399 // b. If q is NaN then let t be +inf, otherwise let t be max(0, q).
400 //
401 // callFrame->argument(3) returns undefined if it's not provided and ToNumber(undefined) returns NaN,
402 // so NaN is the only special case.
403 if (!std::isnan(timeout))
404 timeout = std::max(0_s, timeout);
405 else
406 timeout = Seconds::infinity();
407
408 bool didPassValidation = false;
409 ParkingLot::ParkResult result;
410 {
411 ReleaseHeapAccessScope releaseHeapAccessScope(vm.heap);
412 result = ParkingLot::parkConditionally(
413 ptr,
414 [&] () -> bool {
415 didPassValidation = WTF::atomicLoad(ptr) == expectedValue;
416 return didPassValidation;
417 },
418 [] () { },
419 MonotonicTime::now() + timeout);
420 }
421 const char* resultString;
422 if (!didPassValidation)
423 resultString = "not-equal";
424 else if (!result.wasUnparked)
425 resultString = "timed-out";
426 else
427 resultString = "ok";
428 return JSValue::encode(jsString(vm, resultString));
429}
430
431EncodedJSValue JSC_HOST_CALL atomicsFuncWake(JSGlobalObject* globalObject, CallFrame* callFrame)
432{
433 VM& vm = globalObject->vm();
434 auto scope = DECLARE_THROW_SCOPE(vm);
435
436 JSInt32Array* typedArray = jsDynamicCast<JSInt32Array*>(vm, callFrame->argument(0));
437 if (!typedArray) {
438 throwTypeError(globalObject, scope, "Typed array for wait/wake must be an Int32Array."_s);
439 return JSValue::encode(jsUndefined());
440 }
441
442 if (!typedArray->isShared()) {
443 throwTypeError(globalObject, scope, "Typed array for wait/wake must wrap a SharedArrayBuffer."_s);
444 return JSValue::encode(jsUndefined());
445 }
446
447 unsigned accessIndex = validatedAccessIndex(vm, globalObject, callFrame->argument(1), typedArray);
448 RETURN_IF_EXCEPTION(scope, JSValue::encode(jsUndefined()));
449
450 int32_t* ptr = typedArray->typedVector() + accessIndex;
451
452 JSValue countValue = callFrame->argument(2);
453 unsigned count = UINT_MAX;
454 if (!countValue.isUndefined()) {
455 int32_t countInt = countValue.toInt32(globalObject);
456 RETURN_IF_EXCEPTION(scope, JSValue::encode(jsUndefined()));
457 count = std::max(0, countInt);
458 }
459
460 return JSValue::encode(jsNumber(ParkingLot::unparkCount(ptr, count)));
461}
462
463EncodedJSValue JSC_HOST_CALL atomicsFuncXor(JSGlobalObject* globalObject, CallFrame* callFrame)
464{
465 return atomicOperationWithArgs(globalObject, callFrame, XorFunc());
466}
467
468IGNORE_WARNINGS_BEGIN("frame-address")
469
470EncodedJSValue JIT_OPERATION operationAtomicsAdd(JSGlobalObject* globalObject, EncodedJSValue base, EncodedJSValue index, EncodedJSValue operand)
471{
472 VM& vm = globalObject->vm();
473 CallFrame* callFrame = DECLARE_CALL_FRAME(vm);
474 JITOperationPrologueCallFrameTracer tracer(vm, callFrame);
475 JSValue args[] = {JSValue::decode(base), JSValue::decode(index), JSValue::decode(operand)};
476 return atomicOperationWithArgs(vm, globalObject, args, AddFunc());
477}
478
479EncodedJSValue JIT_OPERATION operationAtomicsAnd(JSGlobalObject* globalObject, EncodedJSValue base, EncodedJSValue index, EncodedJSValue operand)
480{
481 VM& vm = globalObject->vm();
482 CallFrame* callFrame = DECLARE_CALL_FRAME(vm);
483 JITOperationPrologueCallFrameTracer tracer(vm, callFrame);
484 JSValue args[] = {JSValue::decode(base), JSValue::decode(index), JSValue::decode(operand)};
485 return atomicOperationWithArgs(vm, globalObject, args, AndFunc());
486}
487
488EncodedJSValue JIT_OPERATION operationAtomicsCompareExchange(JSGlobalObject* globalObject, EncodedJSValue base, EncodedJSValue index, EncodedJSValue expected, EncodedJSValue newValue)
489{
490 VM& vm = globalObject->vm();
491 CallFrame* callFrame = DECLARE_CALL_FRAME(vm);
492 JITOperationPrologueCallFrameTracer tracer(vm, callFrame);
493 JSValue args[] = {JSValue::decode(base), JSValue::decode(index), JSValue::decode(expected), JSValue::decode(newValue)};
494 return atomicOperationWithArgs(vm, globalObject, args, CompareExchangeFunc());
495}
496
497EncodedJSValue JIT_OPERATION operationAtomicsExchange(JSGlobalObject* globalObject, EncodedJSValue base, EncodedJSValue index, EncodedJSValue operand)
498{
499 VM& vm = globalObject->vm();
500 CallFrame* callFrame = DECLARE_CALL_FRAME(vm);
501 JITOperationPrologueCallFrameTracer tracer(vm, callFrame);
502 JSValue args[] = {JSValue::decode(base), JSValue::decode(index), JSValue::decode(operand)};
503 return atomicOperationWithArgs(vm, globalObject, args, ExchangeFunc());
504}
505
506EncodedJSValue JIT_OPERATION operationAtomicsIsLockFree(JSGlobalObject* globalObject, EncodedJSValue size)
507{
508 VM& vm = globalObject->vm();
509 CallFrame* callFrame = DECLARE_CALL_FRAME(vm);
510 JITOperationPrologueCallFrameTracer tracer(vm, callFrame);
511 return isLockFree(globalObject, JSValue::decode(size));
512}
513
514EncodedJSValue JIT_OPERATION operationAtomicsLoad(JSGlobalObject* globalObject, EncodedJSValue base, EncodedJSValue index)
515{
516 VM& vm = globalObject->vm();
517 CallFrame* callFrame = DECLARE_CALL_FRAME(vm);
518 JITOperationPrologueCallFrameTracer tracer(vm, callFrame);
519 JSValue args[] = {JSValue::decode(base), JSValue::decode(index)};
520 return atomicOperationWithArgs(vm, globalObject, args, LoadFunc());
521}
522
523EncodedJSValue JIT_OPERATION operationAtomicsOr(JSGlobalObject* globalObject, EncodedJSValue base, EncodedJSValue index, EncodedJSValue operand)
524{
525 VM& vm = globalObject->vm();
526 CallFrame* callFrame = DECLARE_CALL_FRAME(vm);
527 JITOperationPrologueCallFrameTracer tracer(vm, callFrame);
528 JSValue args[] = {JSValue::decode(base), JSValue::decode(index), JSValue::decode(operand)};
529 return atomicOperationWithArgs(vm, globalObject, args, OrFunc());
530}
531
532EncodedJSValue JIT_OPERATION operationAtomicsStore(JSGlobalObject* globalObject, EncodedJSValue base, EncodedJSValue index, EncodedJSValue operand)
533{
534 VM& vm = globalObject->vm();
535 CallFrame* callFrame = DECLARE_CALL_FRAME(vm);
536 JITOperationPrologueCallFrameTracer tracer(vm, callFrame);
537 JSValue args[] = {JSValue::decode(base), JSValue::decode(index), JSValue::decode(operand)};
538 return atomicOperationWithArgs(vm, globalObject, args, StoreFunc());
539}
540
541EncodedJSValue JIT_OPERATION operationAtomicsSub(JSGlobalObject* globalObject, EncodedJSValue base, EncodedJSValue index, EncodedJSValue operand)
542{
543 VM& vm = globalObject->vm();
544 CallFrame* callFrame = DECLARE_CALL_FRAME(vm);
545 JITOperationPrologueCallFrameTracer tracer(vm, callFrame);
546 JSValue args[] = {JSValue::decode(base), JSValue::decode(index), JSValue::decode(operand)};
547 return atomicOperationWithArgs(vm, globalObject, args, SubFunc());
548}
549
550EncodedJSValue JIT_OPERATION operationAtomicsXor(JSGlobalObject* globalObject, EncodedJSValue base, EncodedJSValue index, EncodedJSValue operand)
551{
552 VM& vm = globalObject->vm();
553 CallFrame* callFrame = DECLARE_CALL_FRAME(vm);
554 JITOperationPrologueCallFrameTracer tracer(vm, callFrame);
555 JSValue args[] = {JSValue::decode(base), JSValue::decode(index), JSValue::decode(operand)};
556 return atomicOperationWithArgs(vm, globalObject, args, XorFunc());
557}
558
559IGNORE_WARNINGS_END
560
561} // namespace JSC
562
563