1 // Copyright 2018 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 "test/cctest/wasm/wasm-atomics-utils.h"
6 #include "test/common/wasm/wasm-macro-gen.h"
7
8 namespace v8 {
9 namespace internal {
10 namespace wasm {
11 namespace test_run_wasm_atomics_64 {
12
RunU64BinOp(TestExecutionTier execution_tier,WasmOpcode wasm_op,Uint64BinOp expected_op)13 void RunU64BinOp(TestExecutionTier execution_tier, WasmOpcode wasm_op,
14 Uint64BinOp expected_op) {
15 EXPERIMENTAL_FLAG_SCOPE(threads);
16 WasmRunner<uint64_t, uint64_t> r(execution_tier);
17 uint64_t* memory =
18 r.builder().AddMemoryElems<uint64_t>(kWasmPageSize / sizeof(uint64_t));
19 r.builder().SetHasSharedMemory();
20
21 BUILD(r, WASM_ATOMICS_BINOP(wasm_op, WASM_I32V_1(0), WASM_LOCAL_GET(0),
22 MachineRepresentation::kWord64));
23
24 FOR_UINT64_INPUTS(i) {
25 uint64_t initial = i;
26 FOR_UINT64_INPUTS(j) {
27 r.builder().WriteMemory(&memory[0], initial);
28 CHECK_EQ(initial, r.Call(j));
29 uint64_t expected = expected_op(i, j);
30 CHECK_EQ(expected, r.builder().ReadMemory(&memory[0]));
31 }
32 }
33 }
34
35 #define TEST_OPERATION(Name) \
36 WASM_EXEC_TEST(I64Atomic##Name) { \
37 RunU64BinOp(execution_tier, kExprI64Atomic##Name, Name); \
38 }
OPERATION_LIST(TEST_OPERATION)39 OPERATION_LIST(TEST_OPERATION)
40 #undef TEST_OPERATION
41
42 void RunU32BinOp(TestExecutionTier execution_tier, WasmOpcode wasm_op,
43 Uint32BinOp expected_op) {
44 EXPERIMENTAL_FLAG_SCOPE(threads);
45 WasmRunner<uint64_t, uint64_t> r(execution_tier);
46 uint32_t* memory =
47 r.builder().AddMemoryElems<uint32_t>(kWasmPageSize / sizeof(uint32_t));
48 r.builder().SetHasSharedMemory();
49
50 BUILD(r, WASM_ATOMICS_BINOP(wasm_op, WASM_I32V_1(0), WASM_LOCAL_GET(0),
51 MachineRepresentation::kWord32));
52
53 FOR_UINT32_INPUTS(i) {
54 uint32_t initial = i;
55 FOR_UINT32_INPUTS(j) {
56 r.builder().WriteMemory(&memory[0], initial);
57 CHECK_EQ(initial, r.Call(j));
58 uint32_t expected = expected_op(i, j);
59 CHECK_EQ(expected, r.builder().ReadMemory(&memory[0]));
60 }
61 }
62 }
63
64 #define TEST_OPERATION(Name) \
65 WASM_EXEC_TEST(I64Atomic##Name##32U) { \
66 RunU32BinOp(execution_tier, kExprI64Atomic##Name##32U, Name); \
67 }
OPERATION_LIST(TEST_OPERATION)68 OPERATION_LIST(TEST_OPERATION)
69 #undef TEST_OPERATION
70
71 void RunU16BinOp(TestExecutionTier tier, WasmOpcode wasm_op,
72 Uint16BinOp expected_op) {
73 EXPERIMENTAL_FLAG_SCOPE(threads);
74 WasmRunner<uint64_t, uint64_t> r(tier);
75 r.builder().SetHasSharedMemory();
76 uint16_t* memory =
77 r.builder().AddMemoryElems<uint16_t>(kWasmPageSize / sizeof(uint16_t));
78
79 BUILD(r, WASM_ATOMICS_BINOP(wasm_op, WASM_I32V_1(0), WASM_LOCAL_GET(0),
80 MachineRepresentation::kWord16));
81
82 FOR_UINT16_INPUTS(i) {
83 uint16_t initial = i;
84 FOR_UINT16_INPUTS(j) {
85 r.builder().WriteMemory(&memory[0], initial);
86 CHECK_EQ(initial, r.Call(j));
87 uint16_t expected = expected_op(i, j);
88 CHECK_EQ(expected, r.builder().ReadMemory(&memory[0]));
89 }
90 }
91 }
92
93 #define TEST_OPERATION(Name) \
94 WASM_EXEC_TEST(I64Atomic##Name##16U) { \
95 RunU16BinOp(execution_tier, kExprI64Atomic##Name##16U, Name); \
96 }
OPERATION_LIST(TEST_OPERATION)97 OPERATION_LIST(TEST_OPERATION)
98 #undef TEST_OPERATION
99
100 void RunU8BinOp(TestExecutionTier execution_tier, WasmOpcode wasm_op,
101 Uint8BinOp expected_op) {
102 EXPERIMENTAL_FLAG_SCOPE(threads);
103 WasmRunner<uint64_t, uint64_t> r(execution_tier);
104 r.builder().SetHasSharedMemory();
105 uint8_t* memory = r.builder().AddMemoryElems<uint8_t>(kWasmPageSize);
106
107 BUILD(r, WASM_ATOMICS_BINOP(wasm_op, WASM_I32V_1(0), WASM_LOCAL_GET(0),
108 MachineRepresentation::kWord8));
109
110 FOR_UINT8_INPUTS(i) {
111 uint8_t initial = i;
112 FOR_UINT8_INPUTS(j) {
113 r.builder().WriteMemory(&memory[0], initial);
114 CHECK_EQ(initial, r.Call(j));
115 uint8_t expected = expected_op(i, j);
116 CHECK_EQ(expected, r.builder().ReadMemory(&memory[0]));
117 }
118 }
119 }
120
121 #define TEST_OPERATION(Name) \
122 WASM_EXEC_TEST(I64Atomic##Name##8U) { \
123 RunU8BinOp(execution_tier, kExprI64Atomic##Name##8U, Name); \
124 }
125 OPERATION_LIST(TEST_OPERATION)
126 #undef TEST_OPERATION
127
WASM_EXEC_TEST(I64AtomicCompareExchange)128 WASM_EXEC_TEST(I64AtomicCompareExchange) {
129 EXPERIMENTAL_FLAG_SCOPE(threads);
130 WasmRunner<uint64_t, uint64_t, uint64_t> r(execution_tier);
131 r.builder().SetHasSharedMemory();
132 uint64_t* memory =
133 r.builder().AddMemoryElems<uint64_t>(kWasmPageSize / sizeof(uint64_t));
134 BUILD(r, WASM_ATOMICS_TERNARY_OP(
135 kExprI64AtomicCompareExchange, WASM_I32V_1(0), WASM_LOCAL_GET(0),
136 WASM_LOCAL_GET(1), MachineRepresentation::kWord64));
137
138 FOR_UINT64_INPUTS(i) {
139 uint64_t initial = i;
140 FOR_UINT64_INPUTS(j) {
141 r.builder().WriteMemory(&memory[0], initial);
142 CHECK_EQ(initial, r.Call(i, j));
143 uint64_t expected = CompareExchange(initial, i, j);
144 CHECK_EQ(expected, r.builder().ReadMemory(&memory[0]));
145 }
146 }
147 }
148
WASM_EXEC_TEST(I64AtomicCompareExchange32U)149 WASM_EXEC_TEST(I64AtomicCompareExchange32U) {
150 EXPERIMENTAL_FLAG_SCOPE(threads);
151 WasmRunner<uint64_t, uint64_t, uint64_t> r(execution_tier);
152 r.builder().SetHasSharedMemory();
153 uint32_t* memory =
154 r.builder().AddMemoryElems<uint32_t>(kWasmPageSize / sizeof(uint32_t));
155 BUILD(r, WASM_ATOMICS_TERNARY_OP(kExprI64AtomicCompareExchange32U,
156 WASM_I32V_1(0), WASM_LOCAL_GET(0),
157 WASM_LOCAL_GET(1),
158 MachineRepresentation::kWord32));
159
160 FOR_UINT32_INPUTS(i) {
161 uint32_t initial = i;
162 FOR_UINT32_INPUTS(j) {
163 r.builder().WriteMemory(&memory[0], initial);
164 CHECK_EQ(initial, r.Call(i, j));
165 uint32_t expected = CompareExchange(initial, i, j);
166 CHECK_EQ(expected, r.builder().ReadMemory(&memory[0]));
167 }
168 }
169 }
170
WASM_EXEC_TEST(I64AtomicCompareExchange16U)171 WASM_EXEC_TEST(I64AtomicCompareExchange16U) {
172 EXPERIMENTAL_FLAG_SCOPE(threads);
173 WasmRunner<uint64_t, uint64_t, uint64_t> r(execution_tier);
174 r.builder().SetHasSharedMemory();
175 uint16_t* memory =
176 r.builder().AddMemoryElems<uint16_t>(kWasmPageSize / sizeof(uint16_t));
177 BUILD(r, WASM_ATOMICS_TERNARY_OP(kExprI64AtomicCompareExchange16U,
178 WASM_I32V_1(0), WASM_LOCAL_GET(0),
179 WASM_LOCAL_GET(1),
180 MachineRepresentation::kWord16));
181
182 FOR_UINT16_INPUTS(i) {
183 uint16_t initial = i;
184 FOR_UINT16_INPUTS(j) {
185 r.builder().WriteMemory(&memory[0], initial);
186 CHECK_EQ(initial, r.Call(i, j));
187 uint16_t expected = CompareExchange(initial, i, j);
188 CHECK_EQ(expected, r.builder().ReadMemory(&memory[0]));
189 }
190 }
191 }
192
WASM_EXEC_TEST(I32AtomicCompareExchange8U)193 WASM_EXEC_TEST(I32AtomicCompareExchange8U) {
194 EXPERIMENTAL_FLAG_SCOPE(threads);
195 WasmRunner<uint64_t, uint64_t, uint64_t> r(execution_tier);
196 r.builder().SetHasSharedMemory();
197 uint8_t* memory = r.builder().AddMemoryElems<uint8_t>(kWasmPageSize);
198 BUILD(r,
199 WASM_ATOMICS_TERNARY_OP(kExprI64AtomicCompareExchange8U, WASM_I32V_1(0),
200 WASM_LOCAL_GET(0), WASM_LOCAL_GET(1),
201 MachineRepresentation::kWord8));
202 FOR_UINT8_INPUTS(i) {
203 uint8_t initial = i;
204 FOR_UINT8_INPUTS(j) {
205 r.builder().WriteMemory(&memory[0], initial);
206 CHECK_EQ(initial, r.Call(i, j));
207 uint8_t expected = CompareExchange(initial, i, j);
208 CHECK_EQ(expected, r.builder().ReadMemory(&memory[0]));
209 }
210 }
211 }
212
WASM_EXEC_TEST(I64AtomicLoad)213 WASM_EXEC_TEST(I64AtomicLoad) {
214 EXPERIMENTAL_FLAG_SCOPE(threads);
215 WasmRunner<uint64_t> r(execution_tier);
216 r.builder().SetHasSharedMemory();
217 uint64_t* memory =
218 r.builder().AddMemoryElems<uint64_t>(kWasmPageSize / sizeof(uint64_t));
219 BUILD(r, WASM_ATOMICS_LOAD_OP(kExprI64AtomicLoad, WASM_ZERO,
220 MachineRepresentation::kWord64));
221
222 FOR_UINT64_INPUTS(i) {
223 uint64_t expected = i;
224 r.builder().WriteMemory(&memory[0], expected);
225 CHECK_EQ(expected, r.Call());
226 }
227 }
228
WASM_EXEC_TEST(I64AtomicLoad32U)229 WASM_EXEC_TEST(I64AtomicLoad32U) {
230 EXPERIMENTAL_FLAG_SCOPE(threads);
231 WasmRunner<uint64_t> r(execution_tier);
232 r.builder().SetHasSharedMemory();
233 uint32_t* memory =
234 r.builder().AddMemoryElems<uint32_t>(kWasmPageSize / sizeof(uint32_t));
235 BUILD(r, WASM_ATOMICS_LOAD_OP(kExprI64AtomicLoad32U, WASM_ZERO,
236 MachineRepresentation::kWord32));
237
238 FOR_UINT32_INPUTS(i) {
239 uint32_t expected = i;
240 r.builder().WriteMemory(&memory[0], expected);
241 CHECK_EQ(expected, r.Call());
242 }
243 }
244
WASM_EXEC_TEST(I64AtomicLoad16U)245 WASM_EXEC_TEST(I64AtomicLoad16U) {
246 EXPERIMENTAL_FLAG_SCOPE(threads);
247 WasmRunner<uint64_t> r(execution_tier);
248 r.builder().SetHasSharedMemory();
249 uint16_t* memory =
250 r.builder().AddMemoryElems<uint16_t>(kWasmPageSize / sizeof(uint16_t));
251 BUILD(r, WASM_ATOMICS_LOAD_OP(kExprI64AtomicLoad16U, WASM_ZERO,
252 MachineRepresentation::kWord16));
253
254 FOR_UINT16_INPUTS(i) {
255 uint16_t expected = i;
256 r.builder().WriteMemory(&memory[0], expected);
257 CHECK_EQ(expected, r.Call());
258 }
259 }
260
WASM_EXEC_TEST(I64AtomicLoad8U)261 WASM_EXEC_TEST(I64AtomicLoad8U) {
262 EXPERIMENTAL_FLAG_SCOPE(threads);
263 WasmRunner<uint64_t> r(execution_tier);
264 r.builder().SetHasSharedMemory();
265 uint8_t* memory = r.builder().AddMemoryElems<uint8_t>(kWasmPageSize);
266 BUILD(r, WASM_ATOMICS_LOAD_OP(kExprI64AtomicLoad8U, WASM_ZERO,
267 MachineRepresentation::kWord8));
268
269 FOR_UINT8_INPUTS(i) {
270 uint8_t expected = i;
271 r.builder().WriteMemory(&memory[0], expected);
272 CHECK_EQ(expected, r.Call());
273 }
274 }
275
WASM_EXEC_TEST(I64AtomicStoreLoad)276 WASM_EXEC_TEST(I64AtomicStoreLoad) {
277 EXPERIMENTAL_FLAG_SCOPE(threads);
278 WasmRunner<uint64_t, uint64_t> r(execution_tier);
279 r.builder().SetHasSharedMemory();
280 uint64_t* memory =
281 r.builder().AddMemoryElems<uint64_t>(kWasmPageSize / sizeof(uint64_t));
282
283 BUILD(r,
284 WASM_ATOMICS_STORE_OP(kExprI64AtomicStore, WASM_ZERO, WASM_LOCAL_GET(0),
285 MachineRepresentation::kWord64),
286 WASM_ATOMICS_LOAD_OP(kExprI64AtomicLoad, WASM_ZERO,
287 MachineRepresentation::kWord64));
288
289 FOR_UINT64_INPUTS(i) {
290 uint64_t expected = i;
291 CHECK_EQ(expected, r.Call(i));
292 CHECK_EQ(expected, r.builder().ReadMemory(&memory[0]));
293 }
294 }
295
WASM_EXEC_TEST(I64AtomicStoreLoad32U)296 WASM_EXEC_TEST(I64AtomicStoreLoad32U) {
297 EXPERIMENTAL_FLAG_SCOPE(threads);
298 WasmRunner<uint64_t, uint64_t> r(execution_tier);
299 r.builder().SetHasSharedMemory();
300 uint32_t* memory =
301 r.builder().AddMemoryElems<uint32_t>(kWasmPageSize / sizeof(uint32_t));
302
303 BUILD(
304 r,
305 WASM_ATOMICS_STORE_OP(kExprI64AtomicStore32U, WASM_ZERO,
306 WASM_LOCAL_GET(0), MachineRepresentation::kWord32),
307 WASM_ATOMICS_LOAD_OP(kExprI64AtomicLoad32U, WASM_ZERO,
308 MachineRepresentation::kWord32));
309
310 FOR_UINT32_INPUTS(i) {
311 uint32_t expected = i;
312 CHECK_EQ(expected, r.Call(i));
313 CHECK_EQ(expected, r.builder().ReadMemory(&memory[0]));
314 }
315 }
316
WASM_EXEC_TEST(I64AtomicStoreLoad16U)317 WASM_EXEC_TEST(I64AtomicStoreLoad16U) {
318 EXPERIMENTAL_FLAG_SCOPE(threads);
319 WasmRunner<uint64_t, uint64_t> r(execution_tier);
320 r.builder().SetHasSharedMemory();
321 uint16_t* memory =
322 r.builder().AddMemoryElems<uint16_t>(kWasmPageSize / sizeof(uint16_t));
323
324 BUILD(
325 r,
326 WASM_ATOMICS_STORE_OP(kExprI64AtomicStore16U, WASM_ZERO,
327 WASM_LOCAL_GET(0), MachineRepresentation::kWord16),
328 WASM_ATOMICS_LOAD_OP(kExprI64AtomicLoad16U, WASM_ZERO,
329 MachineRepresentation::kWord16));
330
331 FOR_UINT16_INPUTS(i) {
332 uint16_t expected = i;
333 CHECK_EQ(expected, r.Call(i));
334 CHECK_EQ(expected, r.builder().ReadMemory(&memory[0]));
335 }
336 }
337
WASM_EXEC_TEST(I64AtomicStoreLoad8U)338 WASM_EXEC_TEST(I64AtomicStoreLoad8U) {
339 EXPERIMENTAL_FLAG_SCOPE(threads);
340 WasmRunner<uint64_t, uint64_t> r(execution_tier);
341 r.builder().SetHasSharedMemory();
342 uint8_t* memory = r.builder().AddMemoryElems<uint8_t>(kWasmPageSize);
343
344 BUILD(r,
345 WASM_ATOMICS_STORE_OP(kExprI64AtomicStore8U, WASM_ZERO,
346 WASM_LOCAL_GET(0), MachineRepresentation::kWord8),
347 WASM_ATOMICS_LOAD_OP(kExprI64AtomicLoad8U, WASM_ZERO,
348 MachineRepresentation::kWord8));
349
350 FOR_UINT8_INPUTS(i) {
351 uint8_t expected = i;
352 CHECK_EQ(expected, r.Call(i));
353 CHECK_EQ(i, r.builder().ReadMemory(&memory[0]));
354 }
355 }
356
357 // Drop tests verify atomic operations are run correctly when the
358 // entire 64-bit output is optimized out
RunDropTest(TestExecutionTier execution_tier,WasmOpcode wasm_op,Uint64BinOp op)359 void RunDropTest(TestExecutionTier execution_tier, WasmOpcode wasm_op,
360 Uint64BinOp op) {
361 EXPERIMENTAL_FLAG_SCOPE(threads);
362 WasmRunner<uint64_t, uint64_t> r(execution_tier);
363 uint64_t* memory =
364 r.builder().AddMemoryElems<uint64_t>(kWasmPageSize / sizeof(uint64_t));
365 r.builder().SetHasSharedMemory();
366
367 BUILD(r,
368 WASM_ATOMICS_BINOP(wasm_op, WASM_I32V_1(0), WASM_LOCAL_GET(0),
369 MachineRepresentation::kWord64),
370 WASM_DROP, WASM_LOCAL_GET(0));
371
372 uint64_t initial = 0x1111222233334444, local = 0x1111111111111111;
373 r.builder().WriteMemory(&memory[0], initial);
374 CHECK_EQ(local, r.Call(local));
375 uint64_t expected = op(initial, local);
376 CHECK_EQ(expected, r.builder().ReadMemory(&memory[0]));
377 }
378
379 #define TEST_OPERATION(Name) \
380 WASM_EXEC_TEST(I64Atomic##Name##Drop) { \
381 RunDropTest(execution_tier, kExprI64Atomic##Name, Name); \
382 }
383 OPERATION_LIST(TEST_OPERATION)
384 #undef TEST_OPERATION
385
WASM_EXEC_TEST(I64AtomicSub16UDrop)386 WASM_EXEC_TEST(I64AtomicSub16UDrop) {
387 EXPERIMENTAL_FLAG_SCOPE(threads);
388 WasmRunner<uint64_t, uint64_t> r(execution_tier);
389 uint16_t* memory =
390 r.builder().AddMemoryElems<uint16_t>(kWasmPageSize / sizeof(uint16_t));
391 r.builder().SetHasSharedMemory();
392
393 BUILD(r,
394 WASM_ATOMICS_BINOP(kExprI64AtomicSub16U, WASM_I32V_1(0),
395 WASM_LOCAL_GET(0), MachineRepresentation::kWord16),
396 WASM_DROP, WASM_LOCAL_GET(0));
397
398 uint16_t initial = 0x7, local = 0xffe0;
399 r.builder().WriteMemory(&memory[0], initial);
400 CHECK_EQ(local, r.Call(local));
401 uint16_t expected = Sub(initial, local);
402 CHECK_EQ(expected, r.builder().ReadMemory(&memory[0]));
403 }
404
WASM_EXEC_TEST(I64AtomicCompareExchangeDrop)405 WASM_EXEC_TEST(I64AtomicCompareExchangeDrop) {
406 EXPERIMENTAL_FLAG_SCOPE(threads);
407 WasmRunner<uint64_t, uint64_t, uint64_t> r(execution_tier);
408 r.builder().SetHasSharedMemory();
409 uint64_t* memory =
410 r.builder().AddMemoryElems<uint64_t>(kWasmPageSize / sizeof(uint64_t));
411 BUILD(r,
412 WASM_ATOMICS_TERNARY_OP(kExprI64AtomicCompareExchange, WASM_I32V_1(0),
413 WASM_LOCAL_GET(0), WASM_LOCAL_GET(1),
414 MachineRepresentation::kWord64),
415 WASM_DROP, WASM_LOCAL_GET(1));
416
417 uint64_t initial = 0x1111222233334444, local = 0x1111111111111111;
418 r.builder().WriteMemory(&memory[0], initial);
419 CHECK_EQ(local, r.Call(initial, local));
420 uint64_t expected = CompareExchange(initial, initial, local);
421 CHECK_EQ(expected, r.builder().ReadMemory(&memory[0]));
422 }
423
WASM_EXEC_TEST(I64AtomicStoreLoadDrop)424 WASM_EXEC_TEST(I64AtomicStoreLoadDrop) {
425 EXPERIMENTAL_FLAG_SCOPE(threads);
426 WasmRunner<uint64_t, uint64_t, uint64_t> r(execution_tier);
427 r.builder().SetHasSharedMemory();
428 uint64_t* memory =
429 r.builder().AddMemoryElems<uint64_t>(kWasmPageSize / sizeof(uint64_t));
430
431 BUILD(r,
432 WASM_ATOMICS_STORE_OP(kExprI64AtomicStore, WASM_ZERO, WASM_LOCAL_GET(0),
433 MachineRepresentation::kWord64),
434 WASM_ATOMICS_LOAD_OP(kExprI64AtomicLoad, WASM_ZERO,
435 MachineRepresentation::kWord64),
436 WASM_DROP, WASM_LOCAL_GET(1));
437
438 uint64_t store_value = 0x1111111111111111, expected = 0xC0DE;
439 CHECK_EQ(expected, r.Call(store_value, expected));
440 CHECK_EQ(store_value, r.builder().ReadMemory(&memory[0]));
441 }
442
WASM_EXEC_TEST(I64AtomicAddConvertDrop)443 WASM_EXEC_TEST(I64AtomicAddConvertDrop) {
444 EXPERIMENTAL_FLAG_SCOPE(threads);
445 WasmRunner<uint64_t, uint64_t> r(execution_tier);
446 uint64_t* memory =
447 r.builder().AddMemoryElems<uint64_t>(kWasmPageSize / sizeof(uint64_t));
448 r.builder().SetHasSharedMemory();
449
450 BUILD(r,
451 WASM_ATOMICS_BINOP(kExprI64AtomicAdd, WASM_I32V_1(0), WASM_LOCAL_GET(0),
452 MachineRepresentation::kWord64),
453 kExprI32ConvertI64, WASM_DROP, WASM_LOCAL_GET(0));
454
455 uint64_t initial = 0x1111222233334444, local = 0x1111111111111111;
456 r.builder().WriteMemory(&memory[0], initial);
457 CHECK_EQ(local, r.Call(local));
458 uint64_t expected = Add(initial, local);
459 CHECK_EQ(expected, r.builder().ReadMemory(&memory[0]));
460 }
461
WASM_EXEC_TEST(I64AtomicLoadConvertDrop)462 WASM_EXEC_TEST(I64AtomicLoadConvertDrop) {
463 EXPERIMENTAL_FLAG_SCOPE(threads);
464 WasmRunner<uint32_t, uint64_t> r(execution_tier);
465 uint64_t* memory =
466 r.builder().AddMemoryElems<uint64_t>(kWasmPageSize / sizeof(uint64_t));
467 r.builder().SetHasSharedMemory();
468
469 BUILD(r, WASM_I32_CONVERT_I64(WASM_ATOMICS_LOAD_OP(
470 kExprI64AtomicLoad, WASM_ZERO, MachineRepresentation::kWord64)));
471
472 uint64_t initial = 0x1111222233334444;
473 r.builder().WriteMemory(&memory[0], initial);
474 CHECK_EQ(static_cast<uint32_t>(initial), r.Call(initial));
475 }
476
477 // Convert tests verify atomic operations are run correctly when the
478 // upper half of the 64-bit output is optimized out
RunConvertTest(TestExecutionTier execution_tier,WasmOpcode wasm_op,Uint64BinOp op)479 void RunConvertTest(TestExecutionTier execution_tier, WasmOpcode wasm_op,
480 Uint64BinOp op) {
481 EXPERIMENTAL_FLAG_SCOPE(threads);
482 WasmRunner<uint32_t, uint64_t> r(execution_tier);
483 uint64_t* memory =
484 r.builder().AddMemoryElems<uint64_t>(kWasmPageSize / sizeof(uint64_t));
485 r.builder().SetHasSharedMemory();
486
487 BUILD(r, WASM_I32_CONVERT_I64(
488 WASM_ATOMICS_BINOP(wasm_op, WASM_ZERO, WASM_LOCAL_GET(0),
489 MachineRepresentation::kWord64)));
490
491 uint64_t initial = 0x1111222233334444, local = 0x1111111111111111;
492 r.builder().WriteMemory(&memory[0], initial);
493 CHECK_EQ(static_cast<uint32_t>(initial), r.Call(local));
494 uint64_t expected = op(initial, local);
495 CHECK_EQ(expected, r.builder().ReadMemory(&memory[0]));
496 }
497
498 #define TEST_OPERATION(Name) \
499 WASM_EXEC_TEST(I64AtomicConvert##Name) { \
500 RunConvertTest(execution_tier, kExprI64Atomic##Name, Name); \
501 }
502 OPERATION_LIST(TEST_OPERATION)
503 #undef TEST_OPERATION
504
WASM_EXEC_TEST(I64AtomicConvertCompareExchange)505 WASM_EXEC_TEST(I64AtomicConvertCompareExchange) {
506 EXPERIMENTAL_FLAG_SCOPE(threads);
507 WasmRunner<uint32_t, uint64_t, uint64_t> r(execution_tier);
508 uint64_t* memory =
509 r.builder().AddMemoryElems<uint64_t>(kWasmPageSize / sizeof(uint64_t));
510 r.builder().SetHasSharedMemory();
511
512 BUILD(r, WASM_I32_CONVERT_I64(WASM_ATOMICS_TERNARY_OP(
513 kExprI64AtomicCompareExchange, WASM_I32V_1(0), WASM_LOCAL_GET(0),
514 WASM_LOCAL_GET(1), MachineRepresentation::kWord64)));
515
516 uint64_t initial = 0x1111222233334444, local = 0x1111111111111111;
517 r.builder().WriteMemory(&memory[0], initial);
518 CHECK_EQ(static_cast<uint32_t>(initial), r.Call(initial, local));
519 uint64_t expected = CompareExchange(initial, initial, local);
520 CHECK_EQ(expected, r.builder().ReadMemory(&memory[0]));
521 }
522
523 // The WASM_I64_EQ operation is used here to test that the index node
524 // is lowered correctly.
RunNonConstIndexTest(TestExecutionTier execution_tier,WasmOpcode wasm_op,Uint64BinOp op)525 void RunNonConstIndexTest(TestExecutionTier execution_tier, WasmOpcode wasm_op,
526 Uint64BinOp op) {
527 EXPERIMENTAL_FLAG_SCOPE(threads);
528 WasmRunner<uint32_t, uint64_t> r(execution_tier);
529 uint64_t* memory =
530 r.builder().AddMemoryElems<uint64_t>(kWasmPageSize / sizeof(uint64_t));
531 r.builder().SetHasSharedMemory();
532
533 BUILD(r, WASM_I32_CONVERT_I64(WASM_ATOMICS_BINOP(
534 wasm_op, WASM_I64_EQ(WASM_I64V(1), WASM_I64V(0)),
535 WASM_LOCAL_GET(0), MachineRepresentation::kWord32)));
536
537 uint64_t initial = 0x1111222233334444, local = 0x5555666677778888;
538 r.builder().WriteMemory(&memory[0], initial);
539 CHECK_EQ(static_cast<uint32_t>(initial), r.Call(local));
540 CHECK_EQ(static_cast<uint32_t>(op(initial, local)),
541 static_cast<uint32_t>(r.builder().ReadMemory(&memory[0])));
542 }
543
544 // Test a set of Narrow operations
545 #define TEST_OPERATION(Name) \
546 WASM_EXEC_TEST(I64AtomicConstIndex##Name##Narrow) { \
547 RunNonConstIndexTest(execution_tier, kExprI64Atomic##Name##32U, Name); \
548 }
549 OPERATION_LIST(TEST_OPERATION)
550 #undef TEST_OPERATION
551
552 // Test a set of Regular operations
553 #define TEST_OPERATION(Name) \
554 WASM_EXEC_TEST(I64AtomicConstIndex##Name) { \
555 RunNonConstIndexTest(execution_tier, kExprI64Atomic##Name, Name); \
556 }
OPERATION_LIST(TEST_OPERATION)557 OPERATION_LIST(TEST_OPERATION)
558 #undef TEST_OPERATION
559
560 WASM_EXEC_TEST(I64AtomicNonConstIndexCompareExchangeNarrow) {
561 EXPERIMENTAL_FLAG_SCOPE(threads);
562 WasmRunner<uint32_t, uint64_t, uint64_t> r(execution_tier);
563 uint64_t* memory =
564 r.builder().AddMemoryElems<uint64_t>(kWasmPageSize / sizeof(uint64_t));
565 r.builder().SetHasSharedMemory();
566
567 BUILD(r, WASM_I32_CONVERT_I64(WASM_ATOMICS_TERNARY_OP(
568 kExprI64AtomicCompareExchange16U,
569 WASM_I64_EQ(WASM_I64V(1), WASM_I64V(0)), WASM_LOCAL_GET(0),
570 WASM_LOCAL_GET(1), MachineRepresentation::kWord16)));
571
572 uint64_t initial = 0x4444333322221111, local = 0x9999888877776666;
573 r.builder().WriteMemory(&memory[0], initial);
574 CHECK_EQ(static_cast<uint16_t>(initial), r.Call(initial, local));
575 CHECK_EQ(static_cast<uint16_t>(CompareExchange(initial, initial, local)),
576 static_cast<uint16_t>(r.builder().ReadMemory(&memory[0])));
577 }
578
WASM_EXEC_TEST(I64AtomicNonConstIndexCompareExchange)579 WASM_EXEC_TEST(I64AtomicNonConstIndexCompareExchange) {
580 EXPERIMENTAL_FLAG_SCOPE(threads);
581 WasmRunner<uint32_t, uint64_t, uint64_t> r(execution_tier);
582 uint64_t* memory =
583 r.builder().AddMemoryElems<uint64_t>(kWasmPageSize / sizeof(uint64_t));
584 r.builder().SetHasSharedMemory();
585
586 BUILD(r, WASM_I32_CONVERT_I64(WASM_ATOMICS_TERNARY_OP(
587 kExprI64AtomicCompareExchange,
588 WASM_I64_EQ(WASM_I64V(1), WASM_I64V(0)), WASM_LOCAL_GET(0),
589 WASM_LOCAL_GET(1), MachineRepresentation::kWord16)));
590
591 uint64_t initial = 4444333322221111, local = 0x9999888877776666;
592 r.builder().WriteMemory(&memory[0], initial);
593 CHECK_EQ(static_cast<uint32_t>(initial), r.Call(initial, local));
594 CHECK_EQ(CompareExchange(initial, initial, local),
595 r.builder().ReadMemory(&memory[0]));
596 }
597
WASM_EXEC_TEST(I64AtomicNonConstIndexLoad8U)598 WASM_EXEC_TEST(I64AtomicNonConstIndexLoad8U) {
599 EXPERIMENTAL_FLAG_SCOPE(threads);
600 WasmRunner<uint32_t> r(execution_tier);
601 r.builder().SetHasSharedMemory();
602 uint64_t* memory =
603 r.builder().AddMemoryElems<uint64_t>(kWasmPageSize / sizeof(uint64_t));
604 BUILD(r, WASM_I32_CONVERT_I64(WASM_ATOMICS_LOAD_OP(
605 kExprI64AtomicLoad8U, WASM_I64_EQ(WASM_I64V(1), WASM_I64V(0)),
606 MachineRepresentation::kWord8)));
607
608 uint64_t expected = 0xffffeeeeddddcccc;
609 r.builder().WriteMemory(&memory[0], expected);
610 CHECK_EQ(static_cast<uint8_t>(expected), r.Call());
611 }
612
WASM_EXEC_TEST(I64AtomicCompareExchangeFail)613 WASM_EXEC_TEST(I64AtomicCompareExchangeFail) {
614 EXPERIMENTAL_FLAG_SCOPE(threads);
615 WasmRunner<uint64_t, uint64_t, uint64_t> r(execution_tier);
616 r.builder().SetHasSharedMemory();
617 uint64_t* memory =
618 r.builder().AddMemoryElems<uint64_t>(kWasmPageSize / sizeof(uint64_t));
619 BUILD(r, WASM_ATOMICS_TERNARY_OP(
620 kExprI64AtomicCompareExchange, WASM_I32V_1(0), WASM_LOCAL_GET(0),
621 WASM_LOCAL_GET(1), MachineRepresentation::kWord64));
622
623 uint64_t initial = 0x1111222233334444, local = 0x1111111111111111,
624 test = 0x2222222222222222;
625 r.builder().WriteMemory(&memory[0], initial);
626 CHECK_EQ(initial, r.Call(test, local));
627 // No memory change on failed compare exchange
628 CHECK_EQ(initial, r.builder().ReadMemory(&memory[0]));
629 }
630
WASM_EXEC_TEST(I64AtomicCompareExchange32UFail)631 WASM_EXEC_TEST(I64AtomicCompareExchange32UFail) {
632 EXPERIMENTAL_FLAG_SCOPE(threads);
633 WasmRunner<uint64_t, uint64_t, uint64_t> r(execution_tier);
634 r.builder().SetHasSharedMemory();
635 uint64_t* memory =
636 r.builder().AddMemoryElems<uint64_t>(kWasmPageSize / sizeof(uint64_t));
637 BUILD(r, WASM_ATOMICS_TERNARY_OP(kExprI64AtomicCompareExchange32U,
638 WASM_I32V_1(0), WASM_LOCAL_GET(0),
639 WASM_LOCAL_GET(1),
640 MachineRepresentation::kWord32));
641
642 uint64_t initial = 0x1111222233334444, test = 0xffffffff, local = 0xeeeeeeee;
643 r.builder().WriteMemory(&memory[0], initial);
644 CHECK_EQ(static_cast<uint32_t>(initial), r.Call(test, local));
645 // No memory change on failed compare exchange
646 CHECK_EQ(initial, r.builder().ReadMemory(&memory[0]));
647 }
648
WASM_EXEC_TEST(AtomicStoreNoConsideredEffectful)649 WASM_EXEC_TEST(AtomicStoreNoConsideredEffectful) {
650 EXPERIMENTAL_FLAG_SCOPE(threads);
651 // Use {Load} instead of {ProtectedLoad}.
652 FLAG_SCOPE(wasm_enforce_bounds_checks);
653 WasmRunner<uint32_t> r(execution_tier);
654 r.builder().AddMemoryElems<int64_t>(kWasmPageSize / sizeof(int64_t));
655 r.builder().SetHasSharedMemory();
656 BUILD(r, WASM_LOAD_MEM(MachineType::Int64(), WASM_ZERO),
657 WASM_ATOMICS_STORE_OP(kExprI64AtomicStore, WASM_ZERO, WASM_I64V(20),
658 MachineRepresentation::kWord64),
659 kExprI64Eqz);
660 CHECK_EQ(1, r.Call());
661 }
662
RunNoEffectTest(TestExecutionTier execution_tier,WasmOpcode wasm_op)663 void RunNoEffectTest(TestExecutionTier execution_tier, WasmOpcode wasm_op) {
664 EXPERIMENTAL_FLAG_SCOPE(threads);
665 // Use {Load} instead of {ProtectedLoad}.
666 FLAG_SCOPE(wasm_enforce_bounds_checks);
667 WasmRunner<uint32_t> r(execution_tier);
668 r.builder().AddMemoryElems<int64_t>(kWasmPageSize / sizeof(int64_t));
669 r.builder().SetHasSharedMemory();
670 BUILD(r, WASM_LOAD_MEM(MachineType::Int64(), WASM_ZERO),
671 WASM_ATOMICS_BINOP(wasm_op, WASM_ZERO, WASM_I64V(20),
672 MachineRepresentation::kWord64),
673 WASM_DROP, kExprI64Eqz);
674 CHECK_EQ(1, r.Call());
675 }
676
WASM_EXEC_TEST(AtomicAddNoConsideredEffectful)677 WASM_EXEC_TEST(AtomicAddNoConsideredEffectful) {
678 RunNoEffectTest(execution_tier, kExprI64AtomicAdd);
679 }
680
WASM_EXEC_TEST(AtomicExchangeNoConsideredEffectful)681 WASM_EXEC_TEST(AtomicExchangeNoConsideredEffectful) {
682 RunNoEffectTest(execution_tier, kExprI64AtomicExchange);
683 }
684
WASM_EXEC_TEST(AtomicCompareExchangeNoConsideredEffectful)685 WASM_EXEC_TEST(AtomicCompareExchangeNoConsideredEffectful) {
686 EXPERIMENTAL_FLAG_SCOPE(threads);
687 // Use {Load} instead of {ProtectedLoad}.
688 FLAG_SCOPE(wasm_enforce_bounds_checks);
689 WasmRunner<uint32_t> r(execution_tier);
690 r.builder().AddMemoryElems<uint64_t>(kWasmPageSize / sizeof(uint64_t));
691 r.builder().SetHasSharedMemory();
692 BUILD(r, WASM_LOAD_MEM(MachineType::Int64(), WASM_ZERO),
693 WASM_ATOMICS_TERNARY_OP(kExprI64AtomicCompareExchange, WASM_ZERO,
694 WASM_I64V(0), WASM_I64V(30),
695 MachineRepresentation::kWord64),
696 WASM_DROP, kExprI64Eqz);
697 CHECK_EQ(1, r.Call());
698 }
699
WASM_EXEC_TEST(I64AtomicLoadUseOnlyLowWord)700 WASM_EXEC_TEST(I64AtomicLoadUseOnlyLowWord) {
701 EXPERIMENTAL_FLAG_SCOPE(threads);
702 WasmRunner<uint32_t> r(execution_tier);
703 uint64_t* memory =
704 r.builder().AddMemoryElems<uint64_t>(kWasmPageSize / sizeof(uint64_t));
705 memory[1] = 0x1234567890abcdeful;
706 r.builder().SetHasSharedMemory();
707 // Test that we can use just the low word of an I64AtomicLoad.
708 BUILD(r,
709 WASM_I32_CONVERT_I64(WASM_ATOMICS_LOAD_OP(
710 kExprI64AtomicLoad, WASM_I32V(8), MachineRepresentation::kWord64)));
711 CHECK_EQ(0x90abcdef, r.Call());
712 }
713
WASM_EXEC_TEST(I64AtomicLoadUseOnlyHighWord)714 WASM_EXEC_TEST(I64AtomicLoadUseOnlyHighWord) {
715 EXPERIMENTAL_FLAG_SCOPE(threads);
716 WasmRunner<uint32_t> r(execution_tier);
717 uint64_t* memory =
718 r.builder().AddMemoryElems<uint64_t>(kWasmPageSize / sizeof(uint64_t));
719 memory[1] = 0x1234567890abcdeful;
720 r.builder().SetHasSharedMemory();
721 // Test that we can use just the high word of an I64AtomicLoad.
722 BUILD(r, WASM_I32_CONVERT_I64(WASM_I64_ROR(
723 WASM_ATOMICS_LOAD_OP(kExprI64AtomicLoad, WASM_I32V(8),
724 MachineRepresentation::kWord64),
725 WASM_I64V(32))));
726 CHECK_EQ(0x12345678, r.Call());
727 }
728
WASM_EXEC_TEST(I64AtomicAddUseOnlyLowWord)729 WASM_EXEC_TEST(I64AtomicAddUseOnlyLowWord) {
730 EXPERIMENTAL_FLAG_SCOPE(threads);
731 WasmRunner<uint32_t> r(execution_tier);
732 uint64_t* memory =
733 r.builder().AddMemoryElems<uint64_t>(kWasmPageSize / sizeof(uint64_t));
734 memory[1] = 0x1234567890abcdeful;
735 r.builder().SetHasSharedMemory();
736 // Test that we can use just the low word of an I64AtomicLoad.
737 BUILD(r, WASM_I32_CONVERT_I64(
738 WASM_ATOMICS_BINOP(kExprI64AtomicAdd, WASM_I32V(8), WASM_I64V(1),
739 MachineRepresentation::kWord64)));
740 CHECK_EQ(0x90abcdef, r.Call());
741 }
742
WASM_EXEC_TEST(I64AtomicAddUseOnlyHighWord)743 WASM_EXEC_TEST(I64AtomicAddUseOnlyHighWord) {
744 EXPERIMENTAL_FLAG_SCOPE(threads);
745 WasmRunner<uint32_t> r(execution_tier);
746 uint64_t* memory =
747 r.builder().AddMemoryElems<uint64_t>(kWasmPageSize / sizeof(uint64_t));
748 memory[1] = 0x1234567890abcdeful;
749 r.builder().SetHasSharedMemory();
750 // Test that we can use just the high word of an I64AtomicLoad.
751 BUILD(r, WASM_I32_CONVERT_I64(WASM_I64_ROR(
752 WASM_ATOMICS_BINOP(kExprI64AtomicAdd, WASM_I32V(8), WASM_I64V(1),
753 MachineRepresentation::kWord64),
754 WASM_I64V(32))));
755 CHECK_EQ(0x12345678, r.Call());
756 }
757
WASM_EXEC_TEST(I64AtomicCompareExchangeUseOnlyLowWord)758 WASM_EXEC_TEST(I64AtomicCompareExchangeUseOnlyLowWord) {
759 EXPERIMENTAL_FLAG_SCOPE(threads);
760 WasmRunner<uint32_t> r(execution_tier);
761 uint64_t* memory =
762 r.builder().AddMemoryElems<uint64_t>(kWasmPageSize / sizeof(uint64_t));
763 memory[1] = 0x1234567890abcdeful;
764 r.builder().SetHasSharedMemory();
765 // Test that we can use just the low word of an I64AtomicLoad.
766 BUILD(r, WASM_I32_CONVERT_I64(WASM_ATOMICS_TERNARY_OP(
767 kExprI64AtomicCompareExchange, WASM_I32V(8), WASM_I64V(1),
768 WASM_I64V(memory[1]), MachineRepresentation::kWord64)));
769 CHECK_EQ(0x90abcdef, r.Call());
770 }
771
WASM_EXEC_TEST(I64AtomicCompareExchangeUseOnlyHighWord)772 WASM_EXEC_TEST(I64AtomicCompareExchangeUseOnlyHighWord) {
773 EXPERIMENTAL_FLAG_SCOPE(threads);
774 WasmRunner<uint32_t> r(execution_tier);
775 uint64_t* memory =
776 r.builder().AddMemoryElems<uint64_t>(kWasmPageSize / sizeof(uint64_t));
777 memory[1] = 0x1234567890abcdeful;
778 r.builder().SetHasSharedMemory();
779 // Test that we can use just the high word of an I64AtomicLoad.
780 BUILD(r, WASM_I32_CONVERT_I64(WASM_I64_ROR(
781 WASM_ATOMICS_TERNARY_OP(
782 kExprI64AtomicCompareExchange, WASM_I32V(8), WASM_I64V(1),
783 WASM_I64V(memory[1]), MachineRepresentation::kWord64),
784 WASM_I64V(32))));
785 CHECK_EQ(0x12345678, r.Call());
786 }
787
WASM_EXEC_TEST(I64AtomicExchangeUseOnlyLowWord)788 WASM_EXEC_TEST(I64AtomicExchangeUseOnlyLowWord) {
789 EXPERIMENTAL_FLAG_SCOPE(threads);
790 WasmRunner<uint32_t> r(execution_tier);
791 uint64_t* memory =
792 r.builder().AddMemoryElems<uint64_t>(kWasmPageSize / sizeof(uint64_t));
793 memory[1] = 0x1234567890abcdeful;
794 r.builder().SetHasSharedMemory();
795 // Test that we can use just the low word of an I64AtomicLoad.
796 BUILD(r, WASM_I32_CONVERT_I64(WASM_ATOMICS_BINOP(
797 kExprI64AtomicExchange, WASM_I32V(8), WASM_I64V(1),
798 MachineRepresentation::kWord64)));
799 CHECK_EQ(0x90abcdef, r.Call());
800 }
801
WASM_EXEC_TEST(I64AtomicExchangeUseOnlyHighWord)802 WASM_EXEC_TEST(I64AtomicExchangeUseOnlyHighWord) {
803 EXPERIMENTAL_FLAG_SCOPE(threads);
804 WasmRunner<uint32_t> r(execution_tier);
805 uint64_t* memory =
806 r.builder().AddMemoryElems<uint64_t>(kWasmPageSize / sizeof(uint64_t));
807 memory[1] = 0x1234567890abcdeful;
808 r.builder().SetHasSharedMemory();
809 // Test that we can use just the high word of an I64AtomicLoad.
810 BUILD(r, WASM_I32_CONVERT_I64(WASM_I64_ROR(
811 WASM_ATOMICS_BINOP(kExprI64AtomicExchange, WASM_I32V(8),
812 WASM_I64V(1), MachineRepresentation::kWord64),
813 WASM_I64V(32))));
814 CHECK_EQ(0x12345678, r.Call());
815 }
816
WASM_EXEC_TEST(I64AtomicCompareExchange32UZeroExtended)817 WASM_EXEC_TEST(I64AtomicCompareExchange32UZeroExtended) {
818 EXPERIMENTAL_FLAG_SCOPE(threads);
819 WasmRunner<uint32_t> r(execution_tier);
820 uint64_t* memory =
821 r.builder().AddMemoryElems<uint64_t>(kWasmPageSize / sizeof(uint64_t));
822 memory[1] = 0;
823 r.builder().SetHasSharedMemory();
824 // Test that the high word of the expected value is cleared in the return
825 // value.
826 BUILD(r, WASM_I64_EQZ(WASM_ATOMICS_TERNARY_OP(
827 kExprI64AtomicCompareExchange32U, WASM_I32V(8),
828 WASM_I64V(0x1234567800000000), WASM_I64V(0),
829 MachineRepresentation::kWord32)));
830 CHECK_EQ(1, r.Call());
831 }
832
833 } // namespace test_run_wasm_atomics_64
834 } // namespace wasm
835 } // namespace internal
836 } // namespace v8
837