v8/test/mjsunit/wasm/compare-exchange-stress.js
Stephan Herhut 94364486c6 [wasm] Add test for I64AtomicCompareExchange
This adds a stress test for the I64 variants of the
AtomicCompareExchange opcodes.

Bug: v8:6532
Change-Id: Iaba4f31f944a71393e5c3222d364d214ff482b9e
Reviewed-on: https://chromium-review.googlesource.com/1235913
Commit-Queue: Stephan Herhut <herhut@chromium.org>
Reviewed-by: Andreas Haas <ahaas@chromium.org>
Cr-Commit-Position: refs/heads/master@{#56261}
2018-09-27 11:46:35 +00:00

208 lines
7.3 KiB
JavaScript

// 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.
// Flags: --experimental-wasm-threads
load("test/mjsunit/wasm/wasm-constants.js");
load("test/mjsunit/wasm/wasm-module-builder.js");
const kSequenceLength = 8192;
const kNumberOfWorkers = 4;
const kBitMask = kNumberOfWorkers - 1;
const kSequenceStartAddress = 32;
function makeWorkerCodeForOpcode(compareExchangeOpcode, size, functionName,
builder) {
let loadMemOpcode = kTrapUnreachable;
switch (size) {
case 32:
loadMemOpcode = kExprI32LoadMem;
break;
case 16:
loadMemOpcode = kExprI32LoadMem16U;
break;
case 8:
loadMemOpcode = kExprI32LoadMem8U;
break;
default:
throw "!";
}
const kArgMemoryCell = 0; // target for atomic ops
const kArgSequencePtr = 1; // address of sequence
const kArgSeqenceLength = 2; // lenght of sequence
const kArgWorkerId = 3; // id of this worker
const kArgBitMask = 4; // mask to extract worker id from value
const kLocalCurrentOffset = 5; // current position in sequence in bytes
const kLocalExpectedValue = 6; // the value we are waiting for
const kLocalNextValue = 7; // the value to write in the update
let body = [
// Turn sequence length to equivalent in bytes.
kExprGetLocal, kArgSeqenceLength,
kExprI32Const, size / 8,
kExprI32Mul,
kExprSetLocal, kArgSeqenceLength,
// Outer block so we have something to jump for return.
...[kExprBlock, kWasmStmt,
// Set counter to 0.
kExprI32Const, 0,
kExprSetLocal, kLocalCurrentOffset,
// Outer loop until maxcount.
...[kExprLoop, kWasmStmt,
// Find the next value to wait for.
...[kExprLoop, kWasmStmt,
// Check end of sequence.
kExprGetLocal, kLocalCurrentOffset,
kExprGetLocal, kArgSeqenceLength,
kExprI32Eq,
kExprBrIf, 2, // return
...[kExprBlock, kWasmStmt,
// Load next value.
kExprGetLocal, kArgSequencePtr,
kExprGetLocal, kLocalCurrentOffset,
kExprI32Add,
loadMemOpcode, 0, 0,
// Mask off bits.
kExprGetLocal, kArgBitMask,
kExprI32And,
// Compare with worker id.
kExprGetLocal, kArgWorkerId,
kExprI32Eq,
kExprBrIf, 0,
// Not found, increment position.
kExprGetLocal, kLocalCurrentOffset,
kExprI32Const, size / 8,
kExprI32Add,
kExprSetLocal, kLocalCurrentOffset,
kExprBr, 1,
kExprEnd
],
// Found, end loop.
kExprEnd
],
// Load expected value to local.
kExprGetLocal, kArgSequencePtr,
kExprGetLocal, kLocalCurrentOffset,
kExprI32Add,
loadMemOpcode, 0, 0,
kExprSetLocal, kLocalExpectedValue,
// Load value after expected one.
kExprGetLocal, kArgSequencePtr,
kExprGetLocal, kLocalCurrentOffset,
kExprI32Add,
kExprI32Const, size / 8,
kExprI32Add,
loadMemOpcode, 0, 0,
kExprSetLocal, kLocalNextValue,
// Hammer on memory until value found.
...[kExprLoop, kWasmStmt,
// Load address.
kExprGetLocal, kArgMemoryCell,
// Load expected value.
kExprGetLocal, kLocalExpectedValue,
// Load updated value.
kExprGetLocal, kLocalNextValue,
// Try update.
kAtomicPrefix, compareExchangeOpcode, 0, 0,
// Load expected value.
kExprGetLocal, kLocalExpectedValue,
// Spin if not what expected.
kExprI32Ne,
kExprBrIf, 0,
kExprEnd
],
// Next iteration of loop.
kExprGetLocal, kLocalCurrentOffset,
kExprI32Const, size / 8,
kExprI32Add,
kExprSetLocal, kLocalCurrentOffset,
kExprBr, 0,
kExprEnd
], // outer loop
kExprEnd
], // the block
kExprReturn
];
builder.addFunction(functionName, makeSig([kWasmI32, kWasmI32, kWasmI32,
kWasmI32, kWasmI32
], []))
.addLocals({
i32_count: 3
})
.addBody(body)
.exportAs(functionName);
}
function generateSequence(typedarray, start, count) {
let end = count + start;
for (let i = start; i < end; i++) {
typedarray[i] = Math.floor(Math.random() * 256);
}
}
function spawnWorker(module, memory, address, sequence) {
let workers = [];
for (let i = 0; i < kNumberOfWorkers; i++) {
let worker = new Worker(
`onmessage = function(msg) {
this.instance = new WebAssembly.Instance(msg.module,
{m: {imported_mem: msg.memory}});
instance.exports.worker(msg.address, msg.sequence, msg.sequenceLength, msg.workerId,
msg.bitMask);
postMessage({workerId: msg.workerId});
}`,
{type: 'string'}
);
workers.push(worker);
worker.postMessage({
module: module,
memory: memory,
address: address,
sequence: sequence,
sequenceLength: kSequenceLength,
workerId: i,
bitMask: kBitMask
});
}
return workers;
}
function waitForWorkers(workers) {
for (let worker of workers) {
worker.getMessage();
worker.terminate();
}
}
function testOpcode(opcode, opcodeSize) {
print("Testing I32AtomicCompareExchange" + opcodeSize);
let builder = new WasmModuleBuilder();
builder.addImportedMemory("m", "imported_mem", 0, 1, "shared");
makeWorkerCodeForOpcode(opcode, opcodeSize, "worker", builder);
let memory = new WebAssembly.Memory({
initial: 1,
maximum: 1,
shared: true
});
let memoryView = new Uint8Array(memory.buffer);
generateSequence(memoryView, kSequenceStartAddress, kSequenceLength * (opcodeSize / 8));
let module = new WebAssembly.Module(builder.toBuffer());
let workers = spawnWorker(module, memory, 0, kSequenceStartAddress);
// Fire the workers off
for (let i = opcodeSize / 8 - 1; i >= 0; i--) {
memoryView[i] = memoryView[kSequenceStartAddress + i];
}
waitForWorkers(workers);
print("DONE");
}
testOpcode(kExprI32AtomicCompareExchange, 32);
testOpcode(kExprI32AtomicCompareExchange16U, 16);
testOpcode(kExprI32AtomicCompareExchange8U, 8);