atomics.js 14.6 KB
Newer Older
1 2 3 4 5 6 7 8
// Copyright 2017 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.

// Flags: --experimental-wasm-threads

load("test/mjsunit/wasm/wasm-module-builder.js");

9 10 11
const kMemtypeSize32 = 4;
const kMemtypeSize16 = 2;
const kMemtypeSize8 = 1;
12

13 14 15 16 17
function Add(a, b) { return a + b; }
function Sub(a, b) { return a - b; }
function And(a, b) { return a & b; }
function Or(a, b) { return a | b; }
function Xor(a, b) { return a ^ b; }
18
function Exchange(a, b) { return b; }
19 20 21 22

let maxSize = 10;
let memory = new WebAssembly.Memory({initial: 1, maximum: maxSize, shared: true});

23
function GetAtomicBinOpFunction(wasmExpression, alignment, offset) {
24
  let builder = new WasmModuleBuilder();
25
  builder.addImportedMemory("m", "imported_mem", 0, maxSize, "shared");
26
  builder.addFunction("main", kSig_i_ii)
27
    .addBody([
28 29
      kExprLocalGet, 0,
      kExprLocalGet, 1,
30
      kAtomicPrefix,
31
      wasmExpression, alignment, offset])
32
    .exportAs("main");
33 34 35

  // Instantiate module, get function exports
  let module = new WebAssembly.Module(builder.toBuffer());
36 37
  let instance = new WebAssembly.Instance(module,
        {m: {imported_mem: memory}});
38 39
  return instance.exports.main;
}
40

41
function GetAtomicCmpExchangeFunction(wasmExpression, alignment, offset) {
42
  let builder = new WasmModuleBuilder();
43
  builder.addImportedMemory("m", "imported_mem", 0, maxSize, "shared");
44 45
  builder.addFunction("main", kSig_i_iii)
    .addBody([
46 47 48
      kExprLocalGet, 0,
      kExprLocalGet, 1,
      kExprLocalGet, 2,
49
      kAtomicPrefix,
50
      wasmExpression, alignment, offset])
51 52 53 54
    .exportAs("main");

  // Instantiate module, get function exports
  let module = new WebAssembly.Module(builder.toBuffer());
55 56 57 58 59 60 61 62 63 64
  let instance = new WebAssembly.Instance(module,
        {m: {imported_mem: memory}});
  return instance.exports.main;
}

function GetAtomicLoadFunction(wasmExpression, alignment, offset) {
  let builder = new WasmModuleBuilder();
  builder.addImportedMemory("m", "imported_mem", 0, maxSize, "shared");
  builder.addFunction("main", kSig_i_i)
    .addBody([
65
      kExprLocalGet, 0,
66 67 68 69 70 71 72 73
      kAtomicPrefix,
      wasmExpression, alignment, offset])
    .exportAs("main");

  // Instantiate module, get function exports
  let module = new WebAssembly.Module(builder.toBuffer());
  let instance = new WebAssembly.Instance(module,
        {m: {imported_mem: memory}});
74 75 76
  return instance.exports.main;
}

77 78 79 80 81
function GetAtomicStoreFunction(wasmExpression, alignment, offset) {
  let builder = new WasmModuleBuilder();
  builder.addImportedMemory("m", "imported_mem", 0, maxSize, "shared");
  builder.addFunction("main", kSig_v_ii)
    .addBody([
82 83
      kExprLocalGet, 0,
      kExprLocalGet, 1,
84 85 86 87 88 89 90 91 92 93
      kAtomicPrefix,
      wasmExpression, alignment, offset])
    .exportAs("main");

  // Instantiate module, get function exports
  let module = new WebAssembly.Module(builder.toBuffer());
  let instance = new WebAssembly.Instance(module,
        {m: {imported_mem: memory}});
  return instance.exports.main;
}
94

95 96 97 98 99
function VerifyBoundsCheck(func, memtype_size) {
  const kPageSize = 65536;
  // Test out of bounds at boundary
  for (let i = memory.buffer.byteLength - memtype_size + 1;
       i < memory.buffer.byteLength + memtype_size + 4; i++) {
100
    assertTraps(kTrapMemOutOfBounds, () => func(i, 5, 10));
101
  }
102
  // Test out of bounds at maximum + 1
103
  assertTraps(kTrapMemOutOfBounds, () => func((maxSize + 1) * kPageSize, 5, 1));
104
}
105

106 107 108 109
// Test many elements in the small range, make bigger steps later. This is still
// O(2^n), but takes 213 steps to reach 2^32.
const inc = i => i + Math.floor(i/10) + 1;

110 111
function Test32Op(operation, func) {
  let i32 = new Uint32Array(memory.buffer);
112
  for (let i = 0; i < i32.length; i = inc(i)) {
113 114 115 116 117 118 119 120 121 122
    let expected = 0x9cedf00d;
    let value = 0x11111111;
    i32[i] = expected;
    assertEquals(expected, func(i * kMemtypeSize32, value) >>> 0);
    assertEquals(operation(expected, value) >>> 0, i32[i]);
  }
  VerifyBoundsCheck(func, kMemtypeSize32);
}

function Test16Op(operation, func) {
123
  let i16 = new Uint16Array(memory.buffer);
124
  for (let i = 0; i < i16.length; i = inc(i)) {
125 126 127 128 129 130 131 132
    let expected = 0xd00d;
    let value = 0x1111;
    i16[i] = expected;
    assertEquals(expected, func(i * kMemtypeSize16, value));
    assertEquals(operation(expected, value), i16[i]);
  }
  VerifyBoundsCheck(func, kMemtypeSize16);
}
133

134
function Test8Op(operation, func) {
135
  let i8 = new Uint8Array(memory.buffer);
136
  for (let i = 0; i < i8.length; i = inc(i)) {
137 138 139 140 141 142 143 144
    let expected = 0xbe;
    let value = 0x12;
    i8[i] = expected;
    assertEquals(expected, func(i * kMemtypeSize8, value));
    assertEquals(operation(expected, value), i8[i]);
  }
  VerifyBoundsCheck(func, kMemtypeSize8, 10);
}
145

146
(function TestAtomicAdd() {
147
  print(arguments.callee.name);
148
  let wasmAdd = GetAtomicBinOpFunction(kExprI32AtomicAdd, 2, 0);
149 150
  Test32Op(Add, wasmAdd);
})();
151

152
(function TestAtomicAdd16U() {
153
  print(arguments.callee.name);
154
  let wasmAdd = GetAtomicBinOpFunction(kExprI32AtomicAdd16U, 1, 0);
155 156 157 158
  Test16Op(Add, wasmAdd);
})();

(function TestAtomicAdd8U() {
159
  print(arguments.callee.name);
160
  let wasmAdd = GetAtomicBinOpFunction(kExprI32AtomicAdd8U, 0, 0);
161
  Test8Op(Add, wasmAdd);
162 163 164
})();

(function TestAtomicSub() {
165
  print(arguments.callee.name);
166
  let wasmSub = GetAtomicBinOpFunction(kExprI32AtomicSub, 2, 0);
167 168
  Test32Op(Sub, wasmSub);
})();
169

170
(function TestAtomicSub16U() {
171
  print(arguments.callee.name);
172
  let wasmSub = GetAtomicBinOpFunction(kExprI32AtomicSub16U, 1, 0);
173 174
  Test16Op(Sub, wasmSub);
})();
175

176
(function TestAtomicSub8U() {
177
  print(arguments.callee.name);
178
  let wasmSub = GetAtomicBinOpFunction(kExprI32AtomicSub8U, 0, 0);
179
  Test8Op(Sub, wasmSub);
180
})();
181 182

(function TestAtomicAnd() {
183
  print(arguments.callee.name);
184
  let wasmAnd = GetAtomicBinOpFunction(kExprI32AtomicAnd, 2, 0);
185 186
  Test32Op(And, wasmAnd);
})();
187

188
(function TestAtomicAnd16U() {
189
  print(arguments.callee.name);
190
  let wasmAnd = GetAtomicBinOpFunction(kExprI32AtomicAnd16U, 1, 0);
191 192
  Test16Op(And, wasmAnd);
})();
193

194
(function TestAtomicAnd8U() {
195
  print(arguments.callee.name);
196
  let wasmAnd = GetAtomicBinOpFunction(kExprI32AtomicAnd8U, 0, 0);
197
  Test8Op(And, wasmAnd);
198 199 200
})();

(function TestAtomicOr() {
201
  print(arguments.callee.name);
202
  let wasmOr = GetAtomicBinOpFunction(kExprI32AtomicOr, 2, 0);
203 204
  Test32Op(Or, wasmOr);
})();
205

206
(function TestAtomicOr16U() {
207
  print(arguments.callee.name);
208
  let wasmOr = GetAtomicBinOpFunction(kExprI32AtomicOr16U, 1, 0);
209 210
  Test16Op(Or, wasmOr);
})();
211

212
(function TestAtomicOr8U() {
213
  print(arguments.callee.name);
214
  let wasmOr = GetAtomicBinOpFunction(kExprI32AtomicOr8U, 0, 0);
215
  Test8Op(Or, wasmOr);
216 217 218
})();

(function TestAtomicXor() {
219
  print(arguments.callee.name);
220
  let wasmXor = GetAtomicBinOpFunction(kExprI32AtomicXor, 2, 0);
221 222
  Test32Op(Xor, wasmXor);
})();
223

224
(function TestAtomicXor16U() {
225
  print(arguments.callee.name);
226
  let wasmXor = GetAtomicBinOpFunction(kExprI32AtomicXor16U, 1, 0);
227 228
  Test16Op(Xor, wasmXor);
})();
229

230
(function TestAtomicXor8U() {
231
  print(arguments.callee.name);
232
  let wasmXor = GetAtomicBinOpFunction(kExprI32AtomicXor8U, 0, 0);
233
  Test8Op(Xor, wasmXor);
234
})();
235 236

(function TestAtomicExchange() {
237
  print(arguments.callee.name);
238
  let wasmExchange = GetAtomicBinOpFunction(kExprI32AtomicExchange, 2, 0);
239 240 241 242
  Test32Op(Exchange, wasmExchange);
})();

(function TestAtomicExchange16U() {
243
  print(arguments.callee.name);
244
  let wasmExchange = GetAtomicBinOpFunction(kExprI32AtomicExchange16U, 1, 0);
245 246 247 248
  Test16Op(Exchange, wasmExchange);
})();

(function TestAtomicExchange8U() {
249
  print(arguments.callee.name);
250
  let wasmExchange = GetAtomicBinOpFunction(kExprI32AtomicExchange8U, 0, 0);
251 252 253 254
  Test8Op(Exchange, wasmExchange);
})();

function TestCmpExchange(func, buffer, params, size) {
255
  for (let i = 0; i < buffer.length; i = inc(i)) {
256 257 258 259 260 261 262 263 264 265 266 267 268 269
    for (let j = 0; j < params.length; j++) {
      for (let k = 0; k < params.length; k++) {
        buffer[i] = params[j];
        let loaded = func(i * size, params[k], params[j]) >>> 0;
        let expected = (params[k] == loaded) ? params[j] : loaded;
        assertEquals(loaded, params[j]);
        assertEquals(expected, buffer[i]);
      }
    }
  }
  VerifyBoundsCheck(func, size);
}

(function TestAtomicCompareExchange() {
270
  print(arguments.callee.name);
271
  let wasmCmpExchange =
272
      GetAtomicCmpExchangeFunction(kExprI32AtomicCompareExchange, 2, 0);
273 274 275 276 277 278
  let i32 = new Uint32Array(memory.buffer);
  let params = [0x00000001, 0x00000555, 0x00099999, 0xffffffff];
  TestCmpExchange(wasmCmpExchange, i32, params, kMemtypeSize32);
})();

(function TestAtomicCompareExchange16U() {
279
  print(arguments.callee.name);
280
  let wasmCmpExchange =
281
      GetAtomicCmpExchangeFunction(kExprI32AtomicCompareExchange16U, 1, 0);
282 283 284 285 286 287
  let i16 = new Uint16Array(memory.buffer);
  let params = [0x0001, 0x0555, 0x9999];
  TestCmpExchange(wasmCmpExchange, i16, params, kMemtypeSize16);
})();

(function TestAtomicCompareExchange8U() {
288
  print(arguments.callee.name);
289
  let wasmCmpExchange =
290
      GetAtomicCmpExchangeFunction(kExprI32AtomicCompareExchange8U, 0, 0);
291 292 293 294
  let i8 = new Uint8Array(memory.buffer);
  let params = [0x01, 0x0d, 0xf9];
  TestCmpExchange(wasmCmpExchange, i8, params, kMemtypeSize8);
})();
295 296

function TestLoad(func, buffer, value, size) {
297
  for (let i = 0; i < buffer.length; i = inc(i)) {
298 299 300 301 302 303 304
    buffer[i] = value;
    assertEquals(value, func(i * size) >>> 0);
  }
  VerifyBoundsCheck(func, size);
}

(function TestAtomicLoad() {
305
  print(arguments.callee.name);
306 307 308 309 310 311 312
  let wasmLoad = GetAtomicLoadFunction(kExprI32AtomicLoad, 2, 0);
  let i32 = new Uint32Array(memory.buffer);
  let value = 0xacedaced;
  TestLoad(wasmLoad, i32, value, kMemtypeSize32);
})();

(function TestAtomicLoad16U() {
313
  print(arguments.callee.name);
314 315 316 317 318 319 320
  let wasmLoad = GetAtomicLoadFunction(kExprI32AtomicLoad16U, 1, 0);
  let i16 = new Uint16Array(memory.buffer);
  let value = 0xaced;
  TestLoad(wasmLoad, i16, value, kMemtypeSize16);
})();

(function TestAtomicLoad8U() {
321
  print(arguments.callee.name);
322 323 324 325 326 327 328
  let wasmLoad = GetAtomicLoadFunction(kExprI32AtomicLoad8U, 0, 0);
  let i8 = new Uint8Array(memory.buffer);
  let value = 0xac;
  TestLoad(wasmLoad, i8, value, kMemtypeSize8);
})();

function TestStore(func, buffer, value, size) {
329
  for (let i = 0; i < buffer.length; i = inc(i)) {
330 331 332 333 334 335 336
    func(i * size, value)
    assertEquals(value, buffer[i]);
  }
  VerifyBoundsCheck(func, size);
}

(function TestAtomicStore() {
337
  print(arguments.callee.name);
338 339 340 341 342 343 344
  let wasmStore = GetAtomicStoreFunction(kExprI32AtomicStore, 2, 0);
  let i32 = new Uint32Array(memory.buffer);
  let value = 0xacedaced;
  TestStore(wasmStore, i32, value, kMemtypeSize32);
})();

(function TestAtomicStore16U() {
345
  print(arguments.callee.name);
346 347 348 349 350 351 352
  let wasmStore = GetAtomicStoreFunction(kExprI32AtomicStore16U, 1, 0);
  let i16 = new Uint16Array(memory.buffer);
  let value = 0xaced;
  TestStore(wasmStore, i16, value, kMemtypeSize16);
})();

(function TestAtomicStore8U() {
353
  print(arguments.callee.name);
354 355 356 357 358 359 360
  let wasmStore = GetAtomicStoreFunction(kExprI32AtomicStore8U, 0, 0);
  let i8 = new Uint8Array(memory.buffer);
  let value = 0xac;
  TestCmpExchange(wasmStore, i8, value, kMemtypeSize8);
})();

(function TestAtomicLoadStoreOffset() {
361
  print(arguments.callee.name);
362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389
  var builder = new WasmModuleBuilder();
  let memory = new WebAssembly.Memory({
    initial: 16, maximum: 128, shared: true});
  builder.addImportedMemory("m", "imported_mem", 16, 128, "shared");
  builder.addFunction("loadStore", kSig_i_v)
    .addBody([
      kExprI32Const, 16,
      kExprI32Const, 20,
      kAtomicPrefix,
      kExprI32AtomicStore, 0, 0xFC, 0xFF, 0x3a,
      kExprI32Const, 16,
      kAtomicPrefix,
      kExprI32AtomicLoad, 0, 0xFC, 0xFF, 0x3a])
    .exportAs("loadStore");
  builder.addFunction("storeOob", kSig_v_v)
    .addBody([
      kExprI32Const, 16,
      kExprI32Const, 20,
      kAtomicPrefix,
      kExprI32AtomicStore, 0, 0xFC, 0xFF, 0xFF, 0x3a])
    .exportAs("storeOob");
  let module = new WebAssembly.Module(builder.toBuffer());
  let instance = (new WebAssembly.Instance(module,
        {m: {imported_mem: memory}}));
  let buf = memory.buffer;
  assertEquals(20, instance.exports.loadStore());
  assertTraps(kTrapMemOutOfBounds, instance.exports.storeOob);
})();
390 391

(function TestAtomicOpinLoop() {
392
  print(arguments.callee.name);
393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416
  var builder = new WasmModuleBuilder();
  let memory = new WebAssembly.Memory({
    initial: 16, maximum: 128, shared: true});
  builder.addImportedMemory("m", "imported_mem", 16, 128, "shared");
  builder.addFunction("main", kSig_i_v)
    .addBody([
      kExprLoop, kWasmStmt,
        kExprI32Const, 16,
        kExprI32Const, 20,
        kAtomicPrefix,
        kExprI32AtomicStore, 2, 0,
        kExprI32Const, 16,
        kAtomicPrefix,
        kExprI32AtomicLoad, 2, 0,
        kExprReturn,
      kExprEnd,
      kExprI32Const, 0
    ])
    .exportFunc();
  let module = new WebAssembly.Module(builder.toBuffer());
  let instance = (new WebAssembly.Instance(module,
        {m: {imported_mem: memory}}));
  assertEquals(20, instance.exports.main());
})();
417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433

(function TestUnalignedAtomicAccesses() {
  print(arguments.callee.name);
  let wasmAdd = GetAtomicBinOpFunction(kExprI32AtomicAdd, 2, 17);
  assertTraps(kTrapUnalignedAccess, () => wasmAdd(4, 1001));
  let wasmLoad = GetAtomicLoadFunction(kExprI32AtomicLoad16U, 1, 0);
  assertTraps(kTrapUnalignedAccess, () => wasmLoad(15));
  let wasmStore = GetAtomicStoreFunction(kExprI32AtomicStore, 2, 0);
  assertTraps(kTrapUnalignedAccess, () => wasmStore(22, 5));
  let wasmCmpExchange =
      GetAtomicCmpExchangeFunction(kExprI32AtomicCompareExchange, 2, 0x16);
  assertTraps(kTrapUnalignedAccess, () => wasmCmpExchange(11, 6, 5));

  // Building functions with bad alignment should fail to compile
  assertThrows(() => GetAtomicBinOpFunction(kExprI32AtomicSub16U, 3, 0),
      WebAssembly.CompileError);
})();
434 435 436 437 438 439 440 441 442

function CmpExchgLoop(opcode, alignment) {
  print("TestI64AtomicCompareExchangeLoop" + alignment);
  let builder = new WasmModuleBuilder();
  builder.addImportedMemory("m", "imported_mem", 0, 2, "shared");
  builder.addFunction("main", makeSig([kWasmI32], []))
      .addLocals({i64_count: 2})
      .addBody([
        kExprLoop, kWasmStmt,
443 444 445
          kExprLocalGet, 0,
          kExprLocalGet, 1,
          kExprLocalGet, 2,
446
          kAtomicPrefix, opcode, alignment, 0,
447
          kExprLocalGet, 1,
448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463
          kExprI64Ne,
          kExprBrIf, 0,
          kExprEnd
      ])
      .exportFunc();
  let mem = new WebAssembly.Memory({initial: 2, maximum: 2, shared: true});
  let module = new WebAssembly.Module(builder.toBuffer());
  let instance = new WebAssembly.Instance(module, {m: {imported_mem: mem}});
}

(function TestAtomicCompareExchgLoop() {
  CmpExchgLoop(kExprI64AtomicCompareExchange, 3);
  CmpExchgLoop(kExprI64AtomicCompareExchange32U, 2);
  CmpExchgLoop(kExprI64AtomicCompareExchange16U, 1);
  CmpExchgLoop(kExprI64AtomicCompareExchange8U, 0);
})();