[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}
This commit is contained in:
Stephan Herhut 2018-09-27 11:55:04 +02:00 committed by Commit Bot
parent bcbb6d9eb4
commit 94364486c6
3 changed files with 257 additions and 12 deletions

View File

@ -133,10 +133,10 @@ function makeWorkerCodeForOpcode(compareExchangeOpcode, size, functionName,
.exportAs(functionName);
}
function generateSequence(typedarray, start, count, size) {
function generateSequence(typedarray, start, count) {
let end = count + start;
for (let i = start; i < end; i++) {
typedarray[i] = Math.floor(Math.random() * (1 << (size - 1)) * 2);
typedarray[i] = Math.floor(Math.random() * 256);
}
}
@ -174,8 +174,7 @@ function waitForWorkers(workers) {
}
}
function testOpcode(opcode, ta_constructor) {
let opcodeSize = 8 * ta_constructor.BYTES_PER_ELEMENT;
function testOpcode(opcode, opcodeSize) {
print("Testing I32AtomicCompareExchange" + opcodeSize);
let builder = new WasmModuleBuilder();
builder.addImportedMemory("m", "imported_mem", 0, 1, "shared");
@ -187,22 +186,22 @@ function testOpcode(opcode, ta_constructor) {
maximum: 1,
shared: true
});
let memoryView = new ta_constructor(memory.buffer);
let sequenceStartInView = kSequenceStartAddress / ta_constructor.BYTES_PER_ELEMENT;
generateSequence(memoryView, sequenceStartInView, kSequenceLength,
opcodeSize);
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
memoryView[0] = memoryView[sequenceStartInView];
for (let i = opcodeSize / 8 - 1; i >= 0; i--) {
memoryView[i] = memoryView[kSequenceStartAddress + i];
}
waitForWorkers(workers);
print("DONE");
}
testOpcode(kExprI32AtomicCompareExchange, Int32Array);
testOpcode(kExprI32AtomicCompareExchange16U, Int16Array);
testOpcode(kExprI32AtomicCompareExchange8U, Int8Array);
testOpcode(kExprI32AtomicCompareExchange, 32);
testOpcode(kExprI32AtomicCompareExchange16U, 16);
testOpcode(kExprI32AtomicCompareExchange8U, 8);

View File

@ -0,0 +1,213 @@
// 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 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.
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,
kExprI64UConvertI32,
kExprI64And,
// Compare with worker id.
kExprGetLocal, kArgWorkerId,
kExprI64UConvertI32,
kExprI64Eq,
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.
kExprI64Ne,
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: 1, i64_count: 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);
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(kExprI64AtomicCompareExchange, 64);
testOpcode(kExprI64AtomicCompareExchange32U, 32);
testOpcode(kExprI64AtomicCompareExchange16U, 16);
testOpcode(kExprI64AtomicCompareExchange8U, 8);

View File

@ -371,6 +371,39 @@ let kExprI32AtomicExchange16U = 0x44;
let kExprI32AtomicCompareExchange = 0x48
let kExprI32AtomicCompareExchange8U = 0x4a
let kExprI32AtomicCompareExchange16U = 0x4b
let kExprI64AtomicLoad = 0x11;
let kExprI64AtomicLoad8U = 0x14;
let kExprI64AtomicLoad16U = 0x15;
let kExprI64AtomicLoad32U = 0x16;
let kExprI64AtomicStore = 0x18;
let kExprI64AtomicStore8U = 0x1b;
let kExprI64AtomicStore16U = 0x1c;
let kExprI64AtomicStore32U = 0x1d;
let kExprI64AtomicAdd = 0x1f;
let kExprI64AtomicAdd8U = 0x22;
let kExprI64AtomicAdd16U = 0x23;
let kExprI64AtomicAdd32U = 0x24;
let kExprI64AtomicSub = 0x26;
let kExprI64AtomicSub8U = 0x29;
let kExprI64AtomicSub16U = 0x2a;
let kExprI64AtomicSub32U = 0x2b;
let kExprI64AtomicAnd = 0x2d;
let kExprI64AtomicAnd8U = 0x30;
let kExprI64AtomicAnd16U = 0x31;
let kExprI64AtomicAnd32U = 0x32;
let kExprI64AtomicOr = 0x34;
let kExprI64AtomicOr8U = 0x37;
let kExprI64AtomicOr16U = 0x38;
let kExprI64AtomicOr32U = 0x39;
let kExprI64AtomicXor = 0x3b;
let kExprI64AtomicXor8U = 0x3e;
let kExprI64AtomicXor16U = 0x3f;
let kExprI64AtomicXor32U = 0x40;
let kExprI64AtomicExchange = 0x42;
let kExprI64AtomicExchange8U = 0x45;
let kExprI64AtomicExchange16U = 0x46;
let kExprI64AtomicExchange32U = 0x47;
let kExprI64AtomicCompareExchange = 0x49
let kExprI64AtomicCompareExchange8U = 0x4c;
let kExprI64AtomicCompareExchange16U = 0x4d;