2018-09-07 08:44:53 +00:00
|
|
|
// 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;
|
2018-09-14 07:37:00 +00:00
|
|
|
const kNumberOfWorkers = 4;
|
2018-09-07 08:44:53 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2018-09-27 09:55:04 +00:00
|
|
|
function generateSequence(typedarray, start, count) {
|
2018-09-07 08:44:53 +00:00
|
|
|
let end = count + start;
|
|
|
|
for (let i = start; i < end; i++) {
|
2018-09-27 09:55:04 +00:00
|
|
|
typedarray[i] = Math.floor(Math.random() * 256);
|
2018-09-07 08:44:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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});
|
2018-09-19 23:30:04 +00:00
|
|
|
}`,
|
|
|
|
{type: 'string'}
|
2018-09-07 08:44:53 +00:00
|
|
|
);
|
|
|
|
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();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-27 09:55:04 +00:00
|
|
|
function testOpcode(opcode, opcodeSize) {
|
2018-09-07 08:44:53 +00:00
|
|
|
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
|
|
|
|
});
|
2018-09-27 09:55:04 +00:00
|
|
|
let memoryView = new Uint8Array(memory.buffer);
|
|
|
|
generateSequence(memoryView, kSequenceStartAddress, kSequenceLength * (opcodeSize / 8));
|
2018-09-07 08:44:53 +00:00
|
|
|
|
|
|
|
let module = new WebAssembly.Module(builder.toBuffer());
|
|
|
|
let workers = spawnWorker(module, memory, 0, kSequenceStartAddress);
|
|
|
|
|
|
|
|
// Fire the workers off
|
2018-09-27 09:55:04 +00:00
|
|
|
for (let i = opcodeSize / 8 - 1; i >= 0; i--) {
|
|
|
|
memoryView[i] = memoryView[kSequenceStartAddress + i];
|
|
|
|
}
|
2018-09-07 08:44:53 +00:00
|
|
|
|
|
|
|
waitForWorkers(workers);
|
|
|
|
|
|
|
|
print("DONE");
|
|
|
|
}
|
|
|
|
|
2018-09-27 09:55:04 +00:00
|
|
|
testOpcode(kExprI32AtomicCompareExchange, 32);
|
|
|
|
testOpcode(kExprI32AtomicCompareExchange16U, 16);
|
|
|
|
testOpcode(kExprI32AtomicCompareExchange8U, 8);
|