blob: be219f3a0708757c663f0d950f135c2c72950ee9 [file] [log] [blame]
// 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-module-builder.js");
const kSequenceLength = 8192;
const kNumberOfWorkers = 4;
const kBitMask = kNumberOfWorkers - 1;
const kMemoryAddress = 0;
const kSequenceStartAddress = 32;
function makeWorkerCodeForOpcode(compareExchangeOpcode, size, functionName,
builder) {
let loadMemOpcode = kTrapUnreachable;
switch (size) {
case 64:
loadMemOpcode = kExprI64LoadMem;
break;
case 32:
loadMemOpcode = kExprI64LoadMem32U;
break;
case 16:
loadMemOpcode = kExprI64LoadMem16U;
break;
case 8:
loadMemOpcode = kExprI64LoadMem8U;
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.
kExprLocalGet, kArgSeqenceLength,
kExprI32Const, size / 8,
kExprI32Mul,
kExprLocalSet, kArgSeqenceLength,
// Outer block so we have something to jump for return.
...[kExprBlock, kWasmStmt,
// Set counter to 0.
kExprI32Const, 0,
kExprLocalSet, kLocalCurrentOffset,
// Outer loop until maxcount.
...[kExprLoop, kWasmStmt,
// Find the next value to wait for.
...[kExprLoop, kWasmStmt,
// Check end of sequence.
kExprLocalGet, kLocalCurrentOffset,
kExprLocalGet, kArgSeqenceLength,
kExprI32Eq,
kExprBrIf, 2, // return
...[kExprBlock, kWasmStmt,
// Load next value.
kExprLocalGet, kArgSequencePtr,
kExprLocalGet, kLocalCurrentOffset,
kExprI32Add,
loadMemOpcode, 0, 0,
// Mask off bits.
kExprLocalGet, kArgBitMask,
kExprI64UConvertI32,
kExprI64And,
// Compare with worker id.
kExprLocalGet, kArgWorkerId,
kExprI64UConvertI32,
kExprI64Eq,
kExprBrIf, 0,
// Not found, increment position.
kExprLocalGet, kLocalCurrentOffset,
kExprI32Const, size / 8,
kExprI32Add,
kExprLocalSet, kLocalCurrentOffset,
kExprBr, 1,
kExprEnd
],
// Found, end loop.
kExprEnd
],
// Load expected value to local.
kExprLocalGet, kArgSequencePtr,
kExprLocalGet, kLocalCurrentOffset,
kExprI32Add,
loadMemOpcode, 0, 0,
kExprLocalSet, kLocalExpectedValue,
// Load value after expected one.
kExprLocalGet, kArgSequencePtr,
kExprLocalGet, kLocalCurrentOffset,
kExprI32Add,
kExprI32Const, size / 8,
kExprI32Add,
loadMemOpcode, 0, 0,
kExprLocalSet, kLocalNextValue,
// Hammer on memory until value found.
...[kExprLoop, kWasmStmt,
// Load address.
kExprLocalGet, kArgMemoryCell,
// Load expected value.
kExprLocalGet, kLocalExpectedValue,
// Load updated value.
kExprLocalGet, kLocalNextValue,
// Try update.
kAtomicPrefix, compareExchangeOpcode, 0, 0,
// Load expected value.
kExprLocalGet, kLocalExpectedValue,
// Spin if not what expected.
kExprI64Ne,
kExprBrIf, 0,
kExprEnd
],
// Next iteration of loop.
kExprLocalGet, kLocalCurrentOffset,
kExprI32Const, size / 8,
kExprI32Add,
kExprLocalSet, kLocalCurrentOffset,
kExprBr, 0,
kExprEnd
], // outer loop
kExprEnd
], // the block
kExprReturn
];
builder.addFunction(functionName, makeSig([kWasmI32, kWasmI32, kWasmI32,
kWasmI32, kWasmI32
], []))
.addLocals(kWasmI32, 1).addLocals(kWasmI64, 2)
.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 I64AtomicCompareExchange" + opcodeSize);
let builder = new WasmModuleBuilder();
builder.addImportedMemory("m", "imported_mem", 0, 2, "shared");
makeWorkerCodeForOpcode(opcode, opcodeSize, "worker", builder);
let memory = new WebAssembly.Memory({
initial: 2,
maximum: 2,
shared: true
});
let memoryView = new Uint8Array(memory.buffer);
let numBytes = opcodeSize / 8;
generateSequence(
memoryView, kSequenceStartAddress, kSequenceLength * numBytes);
// Write the first element of the sequence to memory, such that the workers
// can start running as soon as they are spawned.
memoryView.copyWithin(
kMemoryAddress, kSequenceStartAddress,
kSequenceStartAddress + numBytes);
let module = new WebAssembly.Module(builder.toBuffer());
let workers =
spawnWorker(module, memory, kMemoryAddress, kSequenceStartAddress);
waitForWorkers(workers);
print("DONE");
}
testOpcode(kExprI64AtomicCompareExchange, 64);
testOpcode(kExprI64AtomicCompareExchange32U, 32);
testOpcode(kExprI64AtomicCompareExchange16U, 16);
testOpcode(kExprI64AtomicCompareExchange8U, 8);