atomics.js 12.8 KB
Newer Older
1 2 3 4 5 6 7 8 9
// 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-constants.js");
load("test/mjsunit/wasm/wasm-module-builder.js");

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

14 15 16 17 18
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; }
19
function Exchange(a, b) { return b; }
20 21 22 23

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

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

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

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

  // Instantiate module, get function exports
  let module = new WebAssembly.Module(builder.toBuffer());
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
  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([
      kExprGetLocal, 0,
      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}});
75 76 77
  return instance.exports.main;
}

78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
function GetAtomicStoreFunction(wasmExpression, alignment, offset) {
  let builder = new WasmModuleBuilder();
  builder.addImportedMemory("m", "imported_mem", 0, maxSize, "shared");
  builder.addFunction("main", kSig_v_ii)
    .addBody([
      kExprGetLocal, 0,
      kExprGetLocal, 1,
      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;
}
95

96 97 98 99 100
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++) {
101
    assertTraps(kTrapMemOutOfBounds, () => func(i, 5, 10));
102
  }
103
  // Test out of bounds at maximum + 1
104
  assertTraps(kTrapMemOutOfBounds, () => func((maxSize + 1) * kPageSize, 5, 1));
105
}
106

107 108 109 110
// 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;

111 112
function Test32Op(operation, func) {
  let i32 = new Uint32Array(memory.buffer);
113
  for (let i = 0; i < i32.length; i = inc(i)) {
114 115 116 117 118 119 120 121 122 123
    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) {
124
  let i16 = new Uint16Array(memory.buffer);
125
  for (let i = 0; i < i16.length; i = inc(i)) {
126 127 128 129 130 131 132 133
    let expected = 0xd00d;
    let value = 0x1111;
    i16[i] = expected;
    assertEquals(expected, func(i * kMemtypeSize16, value));
    assertEquals(operation(expected, value), i16[i]);
  }
  VerifyBoundsCheck(func, kMemtypeSize16);
}
134

135
function Test8Op(operation, func) {
136
  let i8 = new Uint8Array(memory.buffer);
137
  for (let i = 0; i < i8.length; i = inc(i)) {
138 139 140 141 142 143 144 145
    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);
}
146

147 148
(function TestAtomicAdd() {
  print("TestAtomicAdd");
149
  let wasmAdd = GetAtomicBinOpFunction(kExprI32AtomicAdd, 2, 0);
150 151
  Test32Op(Add, wasmAdd);
})();
152

153 154
(function TestAtomicAdd16U() {
  print("TestAtomicAdd16U");
155
  let wasmAdd = GetAtomicBinOpFunction(kExprI32AtomicAdd16U, 1, 0);
156 157 158 159 160
  Test16Op(Add, wasmAdd);
})();

(function TestAtomicAdd8U() {
  print("TestAtomicAdd8U");
161
  let wasmAdd = GetAtomicBinOpFunction(kExprI32AtomicAdd8U, 0, 0);
162
  Test8Op(Add, wasmAdd);
163 164 165 166
})();

(function TestAtomicSub() {
  print("TestAtomicSub");
167
  let wasmSub = GetAtomicBinOpFunction(kExprI32AtomicSub, 2, 0);
168 169
  Test32Op(Sub, wasmSub);
})();
170

171 172
(function TestAtomicSub16U() {
  print("TestAtomicSub16U");
173
  let wasmSub = GetAtomicBinOpFunction(kExprI32AtomicSub16U, 1, 0);
174 175
  Test16Op(Sub, wasmSub);
})();
176

177 178
(function TestAtomicSub8U() {
  print("TestAtomicSub8U");
179
  let wasmSub = GetAtomicBinOpFunction(kExprI32AtomicSub8U, 0, 0);
180
  Test8Op(Sub, wasmSub);
181
})();
182 183 184

(function TestAtomicAnd() {
  print("TestAtomicAnd");
185
  let wasmAnd = GetAtomicBinOpFunction(kExprI32AtomicAnd, 2, 0);
186 187
  Test32Op(And, wasmAnd);
})();
188

189 190
(function TestAtomicAnd16U() {
  print("TestAtomicAnd16U");
191
  let wasmAnd = GetAtomicBinOpFunction(kExprI32AtomicAnd16U, 1, 0);
192 193
  Test16Op(And, wasmAnd);
})();
194

195 196
(function TestAtomicAnd8U() {
  print("TestAtomicAnd8U");
197
  let wasmAnd = GetAtomicBinOpFunction(kExprI32AtomicAnd8U, 0, 0);
198
  Test8Op(And, wasmAnd);
199 200 201 202
})();

(function TestAtomicOr() {
  print("TestAtomicOr");
203
  let wasmOr = GetAtomicBinOpFunction(kExprI32AtomicOr, 2, 0);
204 205
  Test32Op(Or, wasmOr);
})();
206

207 208
(function TestAtomicOr16U() {
  print("TestAtomicOr16U");
209
  let wasmOr = GetAtomicBinOpFunction(kExprI32AtomicOr16U, 1, 0);
210 211
  Test16Op(Or, wasmOr);
})();
212

213 214
(function TestAtomicOr8U() {
  print("TestAtomicOr8U");
215
  let wasmOr = GetAtomicBinOpFunction(kExprI32AtomicOr8U, 0, 0);
216
  Test8Op(Or, wasmOr);
217 218 219 220
})();

(function TestAtomicXor() {
  print("TestAtomicXor");
221
  let wasmXor = GetAtomicBinOpFunction(kExprI32AtomicXor, 2, 0);
222 223
  Test32Op(Xor, wasmXor);
})();
224

225 226
(function TestAtomicXor16U() {
  print("TestAtomicXor16U");
227
  let wasmXor = GetAtomicBinOpFunction(kExprI32AtomicXor16U, 1, 0);
228 229
  Test16Op(Xor, wasmXor);
})();
230

231 232
(function TestAtomicXor8U() {
  print("TestAtomicXor8U");
233
  let wasmXor = GetAtomicBinOpFunction(kExprI32AtomicXor8U, 0, 0);
234
  Test8Op(Xor, wasmXor);
235
})();
236 237 238

(function TestAtomicExchange() {
  print("TestAtomicExchange");
239
  let wasmExchange = GetAtomicBinOpFunction(kExprI32AtomicExchange, 2, 0);
240 241 242 243 244
  Test32Op(Exchange, wasmExchange);
})();

(function TestAtomicExchange16U() {
  print("TestAtomicExchange16U");
245
  let wasmExchange = GetAtomicBinOpFunction(kExprI32AtomicExchange16U, 1, 0);
246 247 248 249 250
  Test16Op(Exchange, wasmExchange);
})();

(function TestAtomicExchange8U() {
  print("TestAtomicExchange8U");
251
  let wasmExchange = GetAtomicBinOpFunction(kExprI32AtomicExchange8U, 0, 0);
252 253 254 255
  Test8Op(Exchange, wasmExchange);
})();

function TestCmpExchange(func, buffer, params, size) {
256
  for (let i = 0; i < buffer.length; i = inc(i)) {
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272
    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() {
  print("TestAtomicCompareExchange");
  let wasmCmpExchange =
273
      GetAtomicCmpExchangeFunction(kExprI32AtomicCompareExchange, 2, 0);
274 275 276 277 278 279 280 281
  let i32 = new Uint32Array(memory.buffer);
  let params = [0x00000001, 0x00000555, 0x00099999, 0xffffffff];
  TestCmpExchange(wasmCmpExchange, i32, params, kMemtypeSize32);
})();

(function TestAtomicCompareExchange16U() {
  print("TestAtomicCompareExchange16U");
  let wasmCmpExchange =
282
      GetAtomicCmpExchangeFunction(kExprI32AtomicCompareExchange16U, 1, 0);
283 284 285 286 287 288 289 290
  let i16 = new Uint16Array(memory.buffer);
  let params = [0x0001, 0x0555, 0x9999];
  TestCmpExchange(wasmCmpExchange, i16, params, kMemtypeSize16);
})();

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

function TestLoad(func, buffer, value, size) {
298
  for (let i = 0; i < buffer.length; i = inc(i)) {
299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329
    buffer[i] = value;
    assertEquals(value, func(i * size) >>> 0);
  }
  VerifyBoundsCheck(func, size);
}

(function TestAtomicLoad() {
  print("TestAtomicLoad");
  let wasmLoad = GetAtomicLoadFunction(kExprI32AtomicLoad, 2, 0);
  let i32 = new Uint32Array(memory.buffer);
  let value = 0xacedaced;
  TestLoad(wasmLoad, i32, value, kMemtypeSize32);
})();

(function TestAtomicLoad16U() {
  print("TestAtomicLoad16U");
  let wasmLoad = GetAtomicLoadFunction(kExprI32AtomicLoad16U, 1, 0);
  let i16 = new Uint16Array(memory.buffer);
  let value = 0xaced;
  TestLoad(wasmLoad, i16, value, kMemtypeSize16);
})();

(function TestAtomicLoad8U() {
  print("TestAtomicLoad8U");
  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) {
330
  for (let i = 0; i < buffer.length; i = inc(i)) {
331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 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 390
    func(i * size, value)
    assertEquals(value, buffer[i]);
  }
  VerifyBoundsCheck(func, size);
}

(function TestAtomicStore() {
  print("TestAtomicStore");
  let wasmStore = GetAtomicStoreFunction(kExprI32AtomicStore, 2, 0);
  let i32 = new Uint32Array(memory.buffer);
  let value = 0xacedaced;
  TestStore(wasmStore, i32, value, kMemtypeSize32);
})();

(function TestAtomicStore16U() {
  print("TestAtomicStore16U");
  let wasmStore = GetAtomicStoreFunction(kExprI32AtomicStore16U, 1, 0);
  let i16 = new Uint16Array(memory.buffer);
  let value = 0xaced;
  TestStore(wasmStore, i16, value, kMemtypeSize16);
})();

(function TestAtomicStore8U() {
  print("TestAtomicStore8U");
  let wasmStore = GetAtomicStoreFunction(kExprI32AtomicStore8U, 0, 0);
  let i8 = new Uint8Array(memory.buffer);
  let value = 0xac;
  TestCmpExchange(wasmStore, i8, value, kMemtypeSize8);
})();

(function TestAtomicLoadStoreOffset() {
  print("TestAtomicLoadStoreOffset");
  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);
})();
391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417

(function TestAtomicOpinLoop() {
  print("TestAtomicOpinLoop");
  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());
})();