test-run-wasm-atomics64.cc 32 KB
Newer Older
1 2 3 4 5 6 7 8 9 10
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "test/cctest/wasm/wasm-atomics-utils.h"
#include "test/common/wasm/wasm-macro-gen.h"

namespace v8 {
namespace internal {
namespace wasm {
11
namespace test_run_wasm_atomics_64 {
12

13
void RunU64BinOp(TestExecutionTier execution_tier, WasmOpcode wasm_op,
14 15
                 Uint64BinOp expected_op) {
  EXPERIMENTAL_FLAG_SCOPE(threads);
16
  WasmRunner<uint64_t, uint64_t> r(execution_tier);
17 18
  uint64_t* memory =
      r.builder().AddMemoryElems<uint64_t>(kWasmPageSize / sizeof(uint64_t));
19 20
  r.builder().SetHasSharedMemory();

21
  BUILD(r, WASM_ATOMICS_BINOP(wasm_op, WASM_I32V_1(0), WASM_LOCAL_GET(0),
22 23 24
                              MachineRepresentation::kWord64));

  FOR_UINT64_INPUTS(i) {
25
    uint64_t initial = i;
26 27
    FOR_UINT64_INPUTS(j) {
      r.builder().WriteMemory(&memory[0], initial);
28 29
      CHECK_EQ(initial, r.Call(j));
      uint64_t expected = expected_op(i, j);
30 31 32 33 34
      CHECK_EQ(expected, r.builder().ReadMemory(&memory[0]));
    }
  }
}

35 36 37 38
#define TEST_OPERATION(Name)                                 \
  WASM_EXEC_TEST(I64Atomic##Name) {                          \
    RunU64BinOp(execution_tier, kExprI64Atomic##Name, Name); \
  }
39
WASM_ATOMIC_OPERATION_LIST(TEST_OPERATION)
40
#undef TEST_OPERATION
41

42
void RunU32BinOp(TestExecutionTier execution_tier, WasmOpcode wasm_op,
43 44
                 Uint32BinOp expected_op) {
  EXPERIMENTAL_FLAG_SCOPE(threads);
45
  WasmRunner<uint64_t, uint64_t> r(execution_tier);
46 47
  uint32_t* memory =
      r.builder().AddMemoryElems<uint32_t>(kWasmPageSize / sizeof(uint32_t));
48 49
  r.builder().SetHasSharedMemory();

50
  BUILD(r, WASM_ATOMICS_BINOP(wasm_op, WASM_I32V_1(0), WASM_LOCAL_GET(0),
51 52 53
                              MachineRepresentation::kWord32));

  FOR_UINT32_INPUTS(i) {
54
    uint32_t initial = i;
55 56
    FOR_UINT32_INPUTS(j) {
      r.builder().WriteMemory(&memory[0], initial);
57 58
      CHECK_EQ(initial, r.Call(j));
      uint32_t expected = expected_op(i, j);
59 60 61 62 63
      CHECK_EQ(expected, r.builder().ReadMemory(&memory[0]));
    }
  }
}

64 65 66 67
#define TEST_OPERATION(Name)                                      \
  WASM_EXEC_TEST(I64Atomic##Name##32U) {                          \
    RunU32BinOp(execution_tier, kExprI64Atomic##Name##32U, Name); \
  }
68
WASM_ATOMIC_OPERATION_LIST(TEST_OPERATION)
69
#undef TEST_OPERATION
70

71
void RunU16BinOp(TestExecutionTier tier, WasmOpcode wasm_op,
72 73
                 Uint16BinOp expected_op) {
  EXPERIMENTAL_FLAG_SCOPE(threads);
74
  WasmRunner<uint64_t, uint64_t> r(tier);
75
  r.builder().SetHasSharedMemory();
76 77
  uint16_t* memory =
      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 81 82
                              MachineRepresentation::kWord16));

  FOR_UINT16_INPUTS(i) {
83
    uint16_t initial = i;
84 85
    FOR_UINT16_INPUTS(j) {
      r.builder().WriteMemory(&memory[0], initial);
86 87
      CHECK_EQ(initial, r.Call(j));
      uint16_t expected = expected_op(i, j);
88 89 90 91 92
      CHECK_EQ(expected, r.builder().ReadMemory(&memory[0]));
    }
  }
}

93 94 95 96
#define TEST_OPERATION(Name)                                      \
  WASM_EXEC_TEST(I64Atomic##Name##16U) {                          \
    RunU16BinOp(execution_tier, kExprI64Atomic##Name##16U, Name); \
  }
97
WASM_ATOMIC_OPERATION_LIST(TEST_OPERATION)
98
#undef TEST_OPERATION
99

100
void RunU8BinOp(TestExecutionTier execution_tier, WasmOpcode wasm_op,
101 102
                Uint8BinOp expected_op) {
  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 109 110
                              MachineRepresentation::kWord8));

  FOR_UINT8_INPUTS(i) {
111
    uint8_t initial = i;
112 113
    FOR_UINT8_INPUTS(j) {
      r.builder().WriteMemory(&memory[0], initial);
114 115
      CHECK_EQ(initial, r.Call(j));
      uint8_t expected = expected_op(i, j);
116 117 118 119 120
      CHECK_EQ(expected, r.builder().ReadMemory(&memory[0]));
    }
  }
}

121 122 123 124
#define TEST_OPERATION(Name)                                    \
  WASM_EXEC_TEST(I64Atomic##Name##8U) {                         \
    RunU8BinOp(execution_tier, kExprI64Atomic##Name##8U, Name); \
  }
125
WASM_ATOMIC_OPERATION_LIST(TEST_OPERATION)
126
#undef TEST_OPERATION
127

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 133
  uint64_t* memory =
      r.builder().AddMemoryElems<uint64_t>(kWasmPageSize / sizeof(uint64_t));
134
  BUILD(r, WASM_ATOMICS_TERNARY_OP(
135 136
               kExprI64AtomicCompareExchange, WASM_I32V_1(0), WASM_LOCAL_GET(0),
               WASM_LOCAL_GET(1), MachineRepresentation::kWord64));
137 138

  FOR_UINT64_INPUTS(i) {
139
    uint64_t initial = i;
140 141
    FOR_UINT64_INPUTS(j) {
      r.builder().WriteMemory(&memory[0], initial);
142 143
      CHECK_EQ(initial, r.Call(i, j));
      uint64_t expected = CompareExchange(initial, i, j);
144 145 146 147 148
      CHECK_EQ(expected, r.builder().ReadMemory(&memory[0]));
    }
  }
}

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 154
  uint32_t* memory =
      r.builder().AddMemoryElems<uint32_t>(kWasmPageSize / sizeof(uint32_t));
155
  BUILD(r, WASM_ATOMICS_TERNARY_OP(kExprI64AtomicCompareExchange32U,
156 157
                                   WASM_I32V_1(0), WASM_LOCAL_GET(0),
                                   WASM_LOCAL_GET(1),
158 159 160
                                   MachineRepresentation::kWord32));

  FOR_UINT32_INPUTS(i) {
161
    uint32_t initial = i;
162 163
    FOR_UINT32_INPUTS(j) {
      r.builder().WriteMemory(&memory[0], initial);
164 165
      CHECK_EQ(initial, r.Call(i, j));
      uint32_t expected = CompareExchange(initial, i, j);
166 167 168 169 170
      CHECK_EQ(expected, r.builder().ReadMemory(&memory[0]));
    }
  }
}

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 176
  uint16_t* memory =
      r.builder().AddMemoryElems<uint16_t>(kWasmPageSize / sizeof(uint16_t));
177
  BUILD(r, WASM_ATOMICS_TERNARY_OP(kExprI64AtomicCompareExchange16U,
178 179
                                   WASM_I32V_1(0), WASM_LOCAL_GET(0),
                                   WASM_LOCAL_GET(1),
180
                                   MachineRepresentation::kWord16));
181

182
  FOR_UINT16_INPUTS(i) {
183
    uint16_t initial = i;
184 185
    FOR_UINT16_INPUTS(j) {
      r.builder().WriteMemory(&memory[0], initial);
186 187
      CHECK_EQ(initial, r.Call(i, j));
      uint16_t expected = CompareExchange(initial, i, j);
188 189 190 191 192
      CHECK_EQ(expected, r.builder().ReadMemory(&memory[0]));
    }
  }
}

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 199
  BUILD(r,
        WASM_ATOMICS_TERNARY_OP(kExprI64AtomicCompareExchange8U, WASM_I32V_1(0),
200
                                WASM_LOCAL_GET(0), WASM_LOCAL_GET(1),
201 202
                                MachineRepresentation::kWord8));
  FOR_UINT8_INPUTS(i) {
203
    uint8_t initial = i;
204 205
    FOR_UINT8_INPUTS(j) {
      r.builder().WriteMemory(&memory[0], initial);
206 207
      CHECK_EQ(initial, r.Call(i, j));
      uint8_t expected = CompareExchange(initial, i, j);
208 209 210 211
      CHECK_EQ(expected, r.builder().ReadMemory(&memory[0]));
    }
  }
}
212

213
WASM_EXEC_TEST(I64AtomicLoad) {
214
  EXPERIMENTAL_FLAG_SCOPE(threads);
215
  WasmRunner<uint64_t> r(execution_tier);
216
  r.builder().SetHasSharedMemory();
217 218
  uint64_t* memory =
      r.builder().AddMemoryElems<uint64_t>(kWasmPageSize / sizeof(uint64_t));
219 220 221 222
  BUILD(r, WASM_ATOMICS_LOAD_OP(kExprI64AtomicLoad, WASM_ZERO,
                                MachineRepresentation::kWord64));

  FOR_UINT64_INPUTS(i) {
223
    uint64_t expected = i;
224 225 226 227 228
    r.builder().WriteMemory(&memory[0], expected);
    CHECK_EQ(expected, r.Call());
  }
}

229
WASM_EXEC_TEST(I64AtomicLoad32U) {
230
  EXPERIMENTAL_FLAG_SCOPE(threads);
231
  WasmRunner<uint64_t> r(execution_tier);
232
  r.builder().SetHasSharedMemory();
233 234
  uint32_t* memory =
      r.builder().AddMemoryElems<uint32_t>(kWasmPageSize / sizeof(uint32_t));
235 236 237 238
  BUILD(r, WASM_ATOMICS_LOAD_OP(kExprI64AtomicLoad32U, WASM_ZERO,
                                MachineRepresentation::kWord32));

  FOR_UINT32_INPUTS(i) {
239
    uint32_t expected = i;
240 241 242 243 244
    r.builder().WriteMemory(&memory[0], expected);
    CHECK_EQ(expected, r.Call());
  }
}

245
WASM_EXEC_TEST(I64AtomicLoad16U) {
246
  EXPERIMENTAL_FLAG_SCOPE(threads);
247
  WasmRunner<uint64_t> r(execution_tier);
248
  r.builder().SetHasSharedMemory();
249 250
  uint16_t* memory =
      r.builder().AddMemoryElems<uint16_t>(kWasmPageSize / sizeof(uint16_t));
251 252 253 254
  BUILD(r, WASM_ATOMICS_LOAD_OP(kExprI64AtomicLoad16U, WASM_ZERO,
                                MachineRepresentation::kWord16));

  FOR_UINT16_INPUTS(i) {
255
    uint16_t expected = i;
256 257 258 259 260
    r.builder().WriteMemory(&memory[0], expected);
    CHECK_EQ(expected, r.Call());
  }
}

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 267 268 269
  BUILD(r, WASM_ATOMICS_LOAD_OP(kExprI64AtomicLoad8U, WASM_ZERO,
                                MachineRepresentation::kWord8));

  FOR_UINT8_INPUTS(i) {
270
    uint8_t expected = i;
271 272 273 274 275
    r.builder().WriteMemory(&memory[0], expected);
    CHECK_EQ(expected, r.Call());
  }
}

276
WASM_EXEC_TEST(I64AtomicStoreLoad) {
277
  EXPERIMENTAL_FLAG_SCOPE(threads);
278
  WasmRunner<uint64_t, uint64_t> r(execution_tier);
279
  r.builder().SetHasSharedMemory();
280 281
  uint64_t* memory =
      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 286 287 288 289
                              MachineRepresentation::kWord64),
        WASM_ATOMICS_LOAD_OP(kExprI64AtomicLoad, WASM_ZERO,
                             MachineRepresentation::kWord64));

  FOR_UINT64_INPUTS(i) {
290 291
    uint64_t expected = i;
    CHECK_EQ(expected, r.Call(i));
292 293 294 295
    CHECK_EQ(expected, r.builder().ReadMemory(&memory[0]));
  }
}

296
WASM_EXEC_TEST(I64AtomicStoreLoad32U) {
297
  EXPERIMENTAL_FLAG_SCOPE(threads);
298
  WasmRunner<uint64_t, uint64_t> r(execution_tier);
299
  r.builder().SetHasSharedMemory();
300 301
  uint32_t* memory =
      r.builder().AddMemoryElems<uint32_t>(kWasmPageSize / sizeof(uint32_t));
302 303 304 305

  BUILD(
      r,
      WASM_ATOMICS_STORE_OP(kExprI64AtomicStore32U, WASM_ZERO,
306
                            WASM_LOCAL_GET(0), MachineRepresentation::kWord32),
307 308 309 310
      WASM_ATOMICS_LOAD_OP(kExprI64AtomicLoad32U, WASM_ZERO,
                           MachineRepresentation::kWord32));

  FOR_UINT32_INPUTS(i) {
311 312
    uint32_t expected = i;
    CHECK_EQ(expected, r.Call(i));
313 314 315 316
    CHECK_EQ(expected, r.builder().ReadMemory(&memory[0]));
  }
}

317
WASM_EXEC_TEST(I64AtomicStoreLoad16U) {
318
  EXPERIMENTAL_FLAG_SCOPE(threads);
319
  WasmRunner<uint64_t, uint64_t> r(execution_tier);
320
  r.builder().SetHasSharedMemory();
321 322
  uint16_t* memory =
      r.builder().AddMemoryElems<uint16_t>(kWasmPageSize / sizeof(uint16_t));
323 324 325 326

  BUILD(
      r,
      WASM_ATOMICS_STORE_OP(kExprI64AtomicStore16U, WASM_ZERO,
327
                            WASM_LOCAL_GET(0), MachineRepresentation::kWord16),
328 329 330 331
      WASM_ATOMICS_LOAD_OP(kExprI64AtomicLoad16U, WASM_ZERO,
                           MachineRepresentation::kWord16));

  FOR_UINT16_INPUTS(i) {
332 333
    uint16_t expected = i;
    CHECK_EQ(expected, r.Call(i));
334 335 336 337
    CHECK_EQ(expected, r.builder().ReadMemory(&memory[0]));
  }
}

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 345

  BUILD(r,
        WASM_ATOMICS_STORE_OP(kExprI64AtomicStore8U, WASM_ZERO,
346
                              WASM_LOCAL_GET(0), MachineRepresentation::kWord8),
347 348 349 350
        WASM_ATOMICS_LOAD_OP(kExprI64AtomicLoad8U, WASM_ZERO,
                             MachineRepresentation::kWord8));

  FOR_UINT8_INPUTS(i) {
351 352 353
    uint8_t expected = i;
    CHECK_EQ(expected, r.Call(i));
    CHECK_EQ(i, r.builder().ReadMemory(&memory[0]));
354 355
  }
}
356

357 358
// Drop tests verify atomic operations are run correctly when the
// entire 64-bit output is optimized out
359
void RunDropTest(TestExecutionTier execution_tier, WasmOpcode wasm_op,
360 361 362 363 364 365 366 367
                 Uint64BinOp op) {
  EXPERIMENTAL_FLAG_SCOPE(threads);
  WasmRunner<uint64_t, uint64_t> r(execution_tier);
  uint64_t* memory =
      r.builder().AddMemoryElems<uint64_t>(kWasmPageSize / sizeof(uint64_t));
  r.builder().SetHasSharedMemory();

  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 373 374 375 376 377 378 379 380 381 382

  uint64_t initial = 0x1111222233334444, local = 0x1111111111111111;
  r.builder().WriteMemory(&memory[0], initial);
  CHECK_EQ(local, r.Call(local));
  uint64_t expected = op(initial, local);
  CHECK_EQ(expected, r.builder().ReadMemory(&memory[0]));
}

#define TEST_OPERATION(Name)                                 \
  WASM_EXEC_TEST(I64Atomic##Name##Drop) {                    \
    RunDropTest(execution_tier, kExprI64Atomic##Name, Name); \
  }
383
WASM_ATOMIC_OPERATION_LIST(TEST_OPERATION)
384 385 386 387 388 389 390 391 392 393 394
#undef TEST_OPERATION

WASM_EXEC_TEST(I64AtomicSub16UDrop) {
  EXPERIMENTAL_FLAG_SCOPE(threads);
  WasmRunner<uint64_t, uint64_t> r(execution_tier);
  uint16_t* memory =
      r.builder().AddMemoryElems<uint16_t>(kWasmPageSize / sizeof(uint16_t));
  r.builder().SetHasSharedMemory();

  BUILD(r,
        WASM_ATOMICS_BINOP(kExprI64AtomicSub16U, WASM_I32V_1(0),
395 396
                           WASM_LOCAL_GET(0), MachineRepresentation::kWord16),
        WASM_DROP, WASM_LOCAL_GET(0));
397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412

  uint16_t initial = 0x7, local = 0xffe0;
  r.builder().WriteMemory(&memory[0], initial);
  CHECK_EQ(local, r.Call(local));
  uint16_t expected = Sub(initial, local);
  CHECK_EQ(expected, r.builder().ReadMemory(&memory[0]));
}

WASM_EXEC_TEST(I64AtomicCompareExchangeDrop) {
  EXPERIMENTAL_FLAG_SCOPE(threads);
  WasmRunner<uint64_t, uint64_t, uint64_t> r(execution_tier);
  r.builder().SetHasSharedMemory();
  uint64_t* memory =
      r.builder().AddMemoryElems<uint64_t>(kWasmPageSize / sizeof(uint64_t));
  BUILD(r,
        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 418 419 420 421 422 423 424 425 426 427 428 429 430 431

  uint64_t initial = 0x1111222233334444, local = 0x1111111111111111;
  r.builder().WriteMemory(&memory[0], initial);
  CHECK_EQ(local, r.Call(initial, local));
  uint64_t expected = CompareExchange(initial, initial, local);
  CHECK_EQ(expected, r.builder().ReadMemory(&memory[0]));
}

WASM_EXEC_TEST(I64AtomicStoreLoadDrop) {
  EXPERIMENTAL_FLAG_SCOPE(threads);
  WasmRunner<uint64_t, uint64_t, uint64_t> r(execution_tier);
  r.builder().SetHasSharedMemory();
  uint64_t* memory =
      r.builder().AddMemoryElems<uint64_t>(kWasmPageSize / sizeof(uint64_t));

  BUILD(r,
432
        WASM_ATOMICS_STORE_OP(kExprI64AtomicStore, WASM_ZERO, WASM_LOCAL_GET(0),
433 434 435
                              MachineRepresentation::kWord64),
        WASM_ATOMICS_LOAD_OP(kExprI64AtomicLoad, WASM_ZERO,
                             MachineRepresentation::kWord64),
436
        WASM_DROP, WASM_LOCAL_GET(1));
437 438 439 440 441 442 443 444 445 446 447 448 449 450

  uint64_t store_value = 0x1111111111111111, expected = 0xC0DE;
  CHECK_EQ(expected, r.Call(store_value, expected));
  CHECK_EQ(store_value, r.builder().ReadMemory(&memory[0]));
}

WASM_EXEC_TEST(I64AtomicAddConvertDrop) {
  EXPERIMENTAL_FLAG_SCOPE(threads);
  WasmRunner<uint64_t, uint64_t> r(execution_tier);
  uint64_t* memory =
      r.builder().AddMemoryElems<uint64_t>(kWasmPageSize / sizeof(uint64_t));
  r.builder().SetHasSharedMemory();

  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 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478

  uint64_t initial = 0x1111222233334444, local = 0x1111111111111111;
  r.builder().WriteMemory(&memory[0], initial);
  CHECK_EQ(local, r.Call(local));
  uint64_t expected = Add(initial, local);
  CHECK_EQ(expected, r.builder().ReadMemory(&memory[0]));
}

WASM_EXEC_TEST(I64AtomicLoadConvertDrop) {
  EXPERIMENTAL_FLAG_SCOPE(threads);
  WasmRunner<uint32_t, uint64_t> r(execution_tier);
  uint64_t* memory =
      r.builder().AddMemoryElems<uint64_t>(kWasmPageSize / sizeof(uint64_t));
  r.builder().SetHasSharedMemory();

  BUILD(r, WASM_I32_CONVERT_I64(WASM_ATOMICS_LOAD_OP(
               kExprI64AtomicLoad, WASM_ZERO, MachineRepresentation::kWord64)));

  uint64_t initial = 0x1111222233334444;
  r.builder().WriteMemory(&memory[0], initial);
  CHECK_EQ(static_cast<uint32_t>(initial), r.Call(initial));
}

// Convert tests verify atomic operations are run correctly when the
// upper half of the 64-bit output is optimized out
479
void RunConvertTest(TestExecutionTier execution_tier, WasmOpcode wasm_op,
480 481 482 483 484 485 486
                    Uint64BinOp op) {
  EXPERIMENTAL_FLAG_SCOPE(threads);
  WasmRunner<uint32_t, uint64_t> r(execution_tier);
  uint64_t* memory =
      r.builder().AddMemoryElems<uint64_t>(kWasmPageSize / sizeof(uint64_t));
  r.builder().SetHasSharedMemory();

487
  BUILD(r, WASM_I32_CONVERT_I64(
488
               WASM_ATOMICS_BINOP(wasm_op, WASM_ZERO, WASM_LOCAL_GET(0),
489
                                  MachineRepresentation::kWord64)));
490 491 492 493

  uint64_t initial = 0x1111222233334444, local = 0x1111111111111111;
  r.builder().WriteMemory(&memory[0], initial);
  CHECK_EQ(static_cast<uint32_t>(initial), r.Call(local));
494
  uint64_t expected = op(initial, local);
495 496 497 498 499 500 501
  CHECK_EQ(expected, r.builder().ReadMemory(&memory[0]));
}

#define TEST_OPERATION(Name)                                    \
  WASM_EXEC_TEST(I64AtomicConvert##Name) {                      \
    RunConvertTest(execution_tier, kExprI64Atomic##Name, Name); \
  }
502
WASM_ATOMIC_OPERATION_LIST(TEST_OPERATION)
503 504 505 506 507 508 509 510 511 512
#undef TEST_OPERATION

WASM_EXEC_TEST(I64AtomicConvertCompareExchange) {
  EXPERIMENTAL_FLAG_SCOPE(threads);
  WasmRunner<uint32_t, uint64_t, uint64_t> r(execution_tier);
  uint64_t* memory =
      r.builder().AddMemoryElems<uint64_t>(kWasmPageSize / sizeof(uint64_t));
  r.builder().SetHasSharedMemory();

  BUILD(r, WASM_I32_CONVERT_I64(WASM_ATOMICS_TERNARY_OP(
513 514
               kExprI64AtomicCompareExchange, WASM_I32V_1(0), WASM_LOCAL_GET(0),
               WASM_LOCAL_GET(1), MachineRepresentation::kWord64)));
515 516 517 518 519 520 521 522

  uint64_t initial = 0x1111222233334444, local = 0x1111111111111111;
  r.builder().WriteMemory(&memory[0], initial);
  CHECK_EQ(static_cast<uint32_t>(initial), r.Call(initial, local));
  uint64_t expected = CompareExchange(initial, initial, local);
  CHECK_EQ(expected, r.builder().ReadMemory(&memory[0]));
}

523 524
// The WASM_I64_EQ operation is used here to test that the index node
// is lowered correctly.
525
void RunNonConstIndexTest(TestExecutionTier execution_tier, WasmOpcode wasm_op,
526 527 528 529 530 531 532 533 534
                          Uint64BinOp op) {
  EXPERIMENTAL_FLAG_SCOPE(threads);
  WasmRunner<uint32_t, uint64_t> r(execution_tier);
  uint64_t* memory =
      r.builder().AddMemoryElems<uint64_t>(kWasmPageSize / sizeof(uint64_t));
  r.builder().SetHasSharedMemory();

  BUILD(r, WASM_I32_CONVERT_I64(WASM_ATOMICS_BINOP(
               wasm_op, WASM_I64_EQ(WASM_I64V(1), WASM_I64V(0)),
535
               WASM_LOCAL_GET(0), MachineRepresentation::kWord32)));
536 537 538 539 540 541 542 543

  uint64_t initial = 0x1111222233334444, local = 0x5555666677778888;
  r.builder().WriteMemory(&memory[0], initial);
  CHECK_EQ(static_cast<uint32_t>(initial), r.Call(local));
  CHECK_EQ(static_cast<uint32_t>(op(initial, local)),
           static_cast<uint32_t>(r.builder().ReadMemory(&memory[0])));
}

544
// Test a set of Narrow operations
545
#define TEST_OPERATION(Name)                                               \
546
  WASM_EXEC_TEST(I64AtomicConstIndex##Name##Narrow) {                      \
547 548
    RunNonConstIndexTest(execution_tier, kExprI64Atomic##Name##32U, Name); \
  }
549
WASM_ATOMIC_OPERATION_LIST(TEST_OPERATION)
550 551
#undef TEST_OPERATION

552 553 554 555 556
// Test a set of Regular operations
#define TEST_OPERATION(Name)                                          \
  WASM_EXEC_TEST(I64AtomicConstIndex##Name) {                         \
    RunNonConstIndexTest(execution_tier, kExprI64Atomic##Name, Name); \
  }
557
WASM_ATOMIC_OPERATION_LIST(TEST_OPERATION)
558 559 560
#undef TEST_OPERATION

WASM_EXEC_TEST(I64AtomicNonConstIndexCompareExchangeNarrow) {
561 562 563 564 565 566 567 568
  EXPERIMENTAL_FLAG_SCOPE(threads);
  WasmRunner<uint32_t, uint64_t, uint64_t> r(execution_tier);
  uint64_t* memory =
      r.builder().AddMemoryElems<uint64_t>(kWasmPageSize / sizeof(uint64_t));
  r.builder().SetHasSharedMemory();

  BUILD(r, WASM_I32_CONVERT_I64(WASM_ATOMICS_TERNARY_OP(
               kExprI64AtomicCompareExchange16U,
569 570
               WASM_I64_EQ(WASM_I64V(1), WASM_I64V(0)), WASM_LOCAL_GET(0),
               WASM_LOCAL_GET(1), MachineRepresentation::kWord16)));
571

572
  uint64_t initial = 0x4444333322221111, local = 0x9999888877776666;
573 574 575 576 577 578
  r.builder().WriteMemory(&memory[0], initial);
  CHECK_EQ(static_cast<uint16_t>(initial), r.Call(initial, local));
  CHECK_EQ(static_cast<uint16_t>(CompareExchange(initial, initial, local)),
           static_cast<uint16_t>(r.builder().ReadMemory(&memory[0])));
}

579 580 581 582 583 584 585 586 587
WASM_EXEC_TEST(I64AtomicNonConstIndexCompareExchange) {
  EXPERIMENTAL_FLAG_SCOPE(threads);
  WasmRunner<uint32_t, uint64_t, uint64_t> r(execution_tier);
  uint64_t* memory =
      r.builder().AddMemoryElems<uint64_t>(kWasmPageSize / sizeof(uint64_t));
  r.builder().SetHasSharedMemory();

  BUILD(r, WASM_I32_CONVERT_I64(WASM_ATOMICS_TERNARY_OP(
               kExprI64AtomicCompareExchange,
588 589
               WASM_I64_EQ(WASM_I64V(1), WASM_I64V(0)), WASM_LOCAL_GET(0),
               WASM_LOCAL_GET(1), MachineRepresentation::kWord16)));
590 591 592 593 594 595 596 597

  uint64_t initial = 4444333322221111, local = 0x9999888877776666;
  r.builder().WriteMemory(&memory[0], initial);
  CHECK_EQ(static_cast<uint32_t>(initial), r.Call(initial, local));
  CHECK_EQ(CompareExchange(initial, initial, local),
           r.builder().ReadMemory(&memory[0]));
}

598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619
WASM_EXEC_TEST(I64AtomicNonConstIndexLoad8U) {
  EXPERIMENTAL_FLAG_SCOPE(threads);
  WasmRunner<uint32_t> r(execution_tier);
  r.builder().SetHasSharedMemory();
  uint64_t* memory =
      r.builder().AddMemoryElems<uint64_t>(kWasmPageSize / sizeof(uint64_t));
  BUILD(r, WASM_I32_CONVERT_I64(WASM_ATOMICS_LOAD_OP(
               kExprI64AtomicLoad8U, WASM_I64_EQ(WASM_I64V(1), WASM_I64V(0)),
               MachineRepresentation::kWord8)));

  uint64_t expected = 0xffffeeeeddddcccc;
  r.builder().WriteMemory(&memory[0], expected);
  CHECK_EQ(static_cast<uint8_t>(expected), r.Call());
}

WASM_EXEC_TEST(I64AtomicCompareExchangeFail) {
  EXPERIMENTAL_FLAG_SCOPE(threads);
  WasmRunner<uint64_t, uint64_t, uint64_t> r(execution_tier);
  r.builder().SetHasSharedMemory();
  uint64_t* memory =
      r.builder().AddMemoryElems<uint64_t>(kWasmPageSize / sizeof(uint64_t));
  BUILD(r, WASM_ATOMICS_TERNARY_OP(
620 621
               kExprI64AtomicCompareExchange, WASM_I32V_1(0), WASM_LOCAL_GET(0),
               WASM_LOCAL_GET(1), MachineRepresentation::kWord64));
622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637

  uint64_t initial = 0x1111222233334444, local = 0x1111111111111111,
           test = 0x2222222222222222;
  r.builder().WriteMemory(&memory[0], initial);
  CHECK_EQ(initial, r.Call(test, local));
  // No memory change on failed compare exchange
  CHECK_EQ(initial, r.builder().ReadMemory(&memory[0]));
}

WASM_EXEC_TEST(I64AtomicCompareExchange32UFail) {
  EXPERIMENTAL_FLAG_SCOPE(threads);
  WasmRunner<uint64_t, uint64_t, uint64_t> r(execution_tier);
  r.builder().SetHasSharedMemory();
  uint64_t* memory =
      r.builder().AddMemoryElems<uint64_t>(kWasmPageSize / sizeof(uint64_t));
  BUILD(r, WASM_ATOMICS_TERNARY_OP(kExprI64AtomicCompareExchange32U,
638 639
                                   WASM_I32V_1(0), WASM_LOCAL_GET(0),
                                   WASM_LOCAL_GET(1),
640 641 642 643 644 645 646 647 648
                                   MachineRepresentation::kWord32));

  uint64_t initial = 0x1111222233334444, test = 0xffffffff, local = 0xeeeeeeee;
  r.builder().WriteMemory(&memory[0], initial);
  CHECK_EQ(static_cast<uint32_t>(initial), r.Call(test, local));
  // No memory change on failed compare exchange
  CHECK_EQ(initial, r.builder().ReadMemory(&memory[0]));
}

649 650
WASM_EXEC_TEST(AtomicStoreNoConsideredEffectful) {
  EXPERIMENTAL_FLAG_SCOPE(threads);
651 652
  // Use {Load} instead of {ProtectedLoad}.
  FLAG_SCOPE(wasm_enforce_bounds_checks);
653 654 655 656 657 658 659 660 661 662
  WasmRunner<uint32_t> r(execution_tier);
  r.builder().AddMemoryElems<int64_t>(kWasmPageSize / sizeof(int64_t));
  r.builder().SetHasSharedMemory();
  BUILD(r, WASM_LOAD_MEM(MachineType::Int64(), WASM_ZERO),
        WASM_ATOMICS_STORE_OP(kExprI64AtomicStore, WASM_ZERO, WASM_I64V(20),
                              MachineRepresentation::kWord64),
        kExprI64Eqz);
  CHECK_EQ(1, r.Call());
}

663
void RunNoEffectTest(TestExecutionTier execution_tier, WasmOpcode wasm_op) {
664
  EXPERIMENTAL_FLAG_SCOPE(threads);
665 666
  // Use {Load} instead of {ProtectedLoad}.
  FLAG_SCOPE(wasm_enforce_bounds_checks);
667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686
  WasmRunner<uint32_t> r(execution_tier);
  r.builder().AddMemoryElems<int64_t>(kWasmPageSize / sizeof(int64_t));
  r.builder().SetHasSharedMemory();
  BUILD(r, WASM_LOAD_MEM(MachineType::Int64(), WASM_ZERO),
        WASM_ATOMICS_BINOP(wasm_op, WASM_ZERO, WASM_I64V(20),
                           MachineRepresentation::kWord64),
        WASM_DROP, kExprI64Eqz);
  CHECK_EQ(1, r.Call());
}

WASM_EXEC_TEST(AtomicAddNoConsideredEffectful) {
  RunNoEffectTest(execution_tier, kExprI64AtomicAdd);
}

WASM_EXEC_TEST(AtomicExchangeNoConsideredEffectful) {
  RunNoEffectTest(execution_tier, kExprI64AtomicExchange);
}

WASM_EXEC_TEST(AtomicCompareExchangeNoConsideredEffectful) {
  EXPERIMENTAL_FLAG_SCOPE(threads);
687 688
  // Use {Load} instead of {ProtectedLoad}.
  FLAG_SCOPE(wasm_enforce_bounds_checks);
689 690 691 692 693 694 695 696 697 698 699
  WasmRunner<uint32_t> r(execution_tier);
  r.builder().AddMemoryElems<uint64_t>(kWasmPageSize / sizeof(uint64_t));
  r.builder().SetHasSharedMemory();
  BUILD(r, WASM_LOAD_MEM(MachineType::Int64(), WASM_ZERO),
        WASM_ATOMICS_TERNARY_OP(kExprI64AtomicCompareExchange, WASM_ZERO,
                                WASM_I64V(0), WASM_I64V(30),
                                MachineRepresentation::kWord64),
        WASM_DROP, kExprI64Eqz);
  CHECK_EQ(1, r.Call());
}

700 701 702 703 704
WASM_EXEC_TEST(I64AtomicLoadUseOnlyLowWord) {
  EXPERIMENTAL_FLAG_SCOPE(threads);
  WasmRunner<uint32_t> r(execution_tier);
  uint64_t* memory =
      r.builder().AddMemoryElems<uint64_t>(kWasmPageSize / sizeof(uint64_t));
705 706
  uint64_t initial = 0x1234567890abcdef;
  r.builder().WriteMemory(&memory[1], initial);
707 708 709 710 711 712 713 714 715 716 717 718 719
  r.builder().SetHasSharedMemory();
  // Test that we can use just the low word of an I64AtomicLoad.
  BUILD(r,
        WASM_I32_CONVERT_I64(WASM_ATOMICS_LOAD_OP(
            kExprI64AtomicLoad, WASM_I32V(8), MachineRepresentation::kWord64)));
  CHECK_EQ(0x90abcdef, r.Call());
}

WASM_EXEC_TEST(I64AtomicLoadUseOnlyHighWord) {
  EXPERIMENTAL_FLAG_SCOPE(threads);
  WasmRunner<uint32_t> r(execution_tier);
  uint64_t* memory =
      r.builder().AddMemoryElems<uint64_t>(kWasmPageSize / sizeof(uint64_t));
720 721
  uint64_t initial = 0x1234567890abcdef;
  r.builder().WriteMemory(&memory[1], initial);
722 723 724 725 726 727 728 729 730
  r.builder().SetHasSharedMemory();
  // Test that we can use just the high word of an I64AtomicLoad.
  BUILD(r, WASM_I32_CONVERT_I64(WASM_I64_ROR(
               WASM_ATOMICS_LOAD_OP(kExprI64AtomicLoad, WASM_I32V(8),
                                    MachineRepresentation::kWord64),
               WASM_I64V(32))));
  CHECK_EQ(0x12345678, r.Call());
}

731 732 733 734 735
WASM_EXEC_TEST(I64AtomicAddUseOnlyLowWord) {
  EXPERIMENTAL_FLAG_SCOPE(threads);
  WasmRunner<uint32_t> r(execution_tier);
  uint64_t* memory =
      r.builder().AddMemoryElems<uint64_t>(kWasmPageSize / sizeof(uint64_t));
736 737
  uint64_t initial = 0x1234567890abcdef;
  r.builder().WriteMemory(&memory[1], initial);
738 739 740 741 742 743 744 745 746 747 748 749 750
  r.builder().SetHasSharedMemory();
  // Test that we can use just the low word of an I64AtomicLoad.
  BUILD(r, WASM_I32_CONVERT_I64(
               WASM_ATOMICS_BINOP(kExprI64AtomicAdd, WASM_I32V(8), WASM_I64V(1),
                                  MachineRepresentation::kWord64)));
  CHECK_EQ(0x90abcdef, r.Call());
}

WASM_EXEC_TEST(I64AtomicAddUseOnlyHighWord) {
  EXPERIMENTAL_FLAG_SCOPE(threads);
  WasmRunner<uint32_t> r(execution_tier);
  uint64_t* memory =
      r.builder().AddMemoryElems<uint64_t>(kWasmPageSize / sizeof(uint64_t));
751 752
  uint64_t initial = 0x1234567890abcdef;
  r.builder().WriteMemory(&memory[1], initial);
753 754 755 756 757 758 759 760 761
  r.builder().SetHasSharedMemory();
  // Test that we can use just the high word of an I64AtomicLoad.
  BUILD(r, WASM_I32_CONVERT_I64(WASM_I64_ROR(
               WASM_ATOMICS_BINOP(kExprI64AtomicAdd, WASM_I32V(8), WASM_I64V(1),
                                  MachineRepresentation::kWord64),
               WASM_I64V(32))));
  CHECK_EQ(0x12345678, r.Call());
}

762 763 764 765 766
WASM_EXEC_TEST(I64AtomicCompareExchangeUseOnlyLowWord) {
  EXPERIMENTAL_FLAG_SCOPE(threads);
  WasmRunner<uint32_t> r(execution_tier);
  uint64_t* memory =
      r.builder().AddMemoryElems<uint64_t>(kWasmPageSize / sizeof(uint64_t));
767 768
  uint64_t initial = 0x1234567890abcdef;
  r.builder().WriteMemory(&memory[1], initial);
769 770 771 772 773 774 775 776 777 778 779 780 781
  r.builder().SetHasSharedMemory();
  // Test that we can use just the low word of an I64AtomicLoad.
  BUILD(r, WASM_I32_CONVERT_I64(WASM_ATOMICS_TERNARY_OP(
               kExprI64AtomicCompareExchange, WASM_I32V(8), WASM_I64V(1),
               WASM_I64V(memory[1]), MachineRepresentation::kWord64)));
  CHECK_EQ(0x90abcdef, r.Call());
}

WASM_EXEC_TEST(I64AtomicCompareExchangeUseOnlyHighWord) {
  EXPERIMENTAL_FLAG_SCOPE(threads);
  WasmRunner<uint32_t> r(execution_tier);
  uint64_t* memory =
      r.builder().AddMemoryElems<uint64_t>(kWasmPageSize / sizeof(uint64_t));
782 783
  uint64_t initial = 0x1234567890abcdef;
  r.builder().WriteMemory(&memory[1], initial);
784 785 786 787 788 789 790 791 792
  r.builder().SetHasSharedMemory();
  // Test that we can use just the high word of an I64AtomicLoad.
  BUILD(r, WASM_I32_CONVERT_I64(WASM_I64_ROR(
               WASM_ATOMICS_TERNARY_OP(
                   kExprI64AtomicCompareExchange, WASM_I32V(8), WASM_I64V(1),
                   WASM_I64V(memory[1]), MachineRepresentation::kWord64),
               WASM_I64V(32))));
  CHECK_EQ(0x12345678, r.Call());
}
793

794 795 796 797 798
WASM_EXEC_TEST(I64AtomicExchangeUseOnlyLowWord) {
  EXPERIMENTAL_FLAG_SCOPE(threads);
  WasmRunner<uint32_t> r(execution_tier);
  uint64_t* memory =
      r.builder().AddMemoryElems<uint64_t>(kWasmPageSize / sizeof(uint64_t));
799 800
  uint64_t initial = 0x1234567890abcdef;
  r.builder().WriteMemory(&memory[1], initial);
801 802 803 804 805 806 807 808 809 810 811 812 813
  r.builder().SetHasSharedMemory();
  // Test that we can use just the low word of an I64AtomicLoad.
  BUILD(r, WASM_I32_CONVERT_I64(WASM_ATOMICS_BINOP(
               kExprI64AtomicExchange, WASM_I32V(8), WASM_I64V(1),
               MachineRepresentation::kWord64)));
  CHECK_EQ(0x90abcdef, r.Call());
}

WASM_EXEC_TEST(I64AtomicExchangeUseOnlyHighWord) {
  EXPERIMENTAL_FLAG_SCOPE(threads);
  WasmRunner<uint32_t> r(execution_tier);
  uint64_t* memory =
      r.builder().AddMemoryElems<uint64_t>(kWasmPageSize / sizeof(uint64_t));
814 815
  uint64_t initial = 0x1234567890abcdef;
  r.builder().WriteMemory(&memory[1], initial);
816 817 818 819 820 821 822 823 824
  r.builder().SetHasSharedMemory();
  // Test that we can use just the high word of an I64AtomicLoad.
  BUILD(r, WASM_I32_CONVERT_I64(WASM_I64_ROR(
               WASM_ATOMICS_BINOP(kExprI64AtomicExchange, WASM_I32V(8),
                                  WASM_I64V(1), MachineRepresentation::kWord64),
               WASM_I64V(32))));
  CHECK_EQ(0x12345678, r.Call());
}

825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840
WASM_EXEC_TEST(I64AtomicCompareExchange32UZeroExtended) {
  EXPERIMENTAL_FLAG_SCOPE(threads);
  WasmRunner<uint32_t> r(execution_tier);
  uint64_t* memory =
      r.builder().AddMemoryElems<uint64_t>(kWasmPageSize / sizeof(uint64_t));
  memory[1] = 0;
  r.builder().SetHasSharedMemory();
  // Test that the high word of the expected value is cleared in the return
  // value.
  BUILD(r, WASM_I64_EQZ(WASM_ATOMICS_TERNARY_OP(
               kExprI64AtomicCompareExchange32U, WASM_I32V(8),
               WASM_I64V(0x1234567800000000), WASM_I64V(0),
               MachineRepresentation::kWord32)));
  CHECK_EQ(1, r.Call());
}

841
}  // namespace test_run_wasm_atomics_64
842 843 844
}  // namespace wasm
}  // namespace internal
}  // namespace v8