Commit 14bfcf7c authored by Ben L. Titzer's avatar Ben L. Titzer Committed by Commit Bot

[mjsunit/wasm] Reuse WebAssembly.Memory objects in stress test

In the atomics stress, the search for sequential sequences creates
lots of new WebAssembly.Memory objects. This memory pressure is not
central to this test, so reuse the same memory to make them less
flaky.

R=mstarzinger@chromium.org

Change-Id: I8d135e7b82d572cb1df38f37a4e2f6393f6b2e05
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1697247Reviewed-by: 's avatarMichael Starzinger <mstarzinger@chromium.org>
Commit-Queue: Ben Titzer <titzer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#62644}
parent 98bc64d3
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
// Note that results of this test are flaky by design. While the test is // Note that results of this test are flaky by design. While the test is
// deterministic with a fixed seed, bugs may introduce non-determinism. // deterministic with a fixed seed, bugs may introduce non-determinism.
load("test/mjsunit/wasm/wasm-module-builder.js"); load('test/mjsunit/wasm/wasm-module-builder.js');
const kDebug = false; const kDebug = false;
...@@ -22,67 +22,47 @@ const kFirstOpcodeWithoutOutput = 3; ...@@ -22,67 +22,47 @@ const kFirstOpcodeWithoutOutput = 3;
const kLastOpcodeWithoutOutput = 5; const kLastOpcodeWithoutOutput = 5;
const opCodes = [ const opCodes = [
kExprI32AtomicLoad, kExprI32AtomicLoad, kExprI32AtomicLoad8U, kExprI32AtomicLoad16U,
kExprI32AtomicLoad8U, kExprI32AtomicStore, kExprI32AtomicStore8U, kExprI32AtomicStore16U,
kExprI32AtomicLoad16U, kExprI32AtomicAdd, kExprI32AtomicAdd8U, kExprI32AtomicAdd16U,
kExprI32AtomicStore, kExprI32AtomicSub, kExprI32AtomicSub8U, kExprI32AtomicSub16U,
kExprI32AtomicStore8U, kExprI32AtomicAnd, kExprI32AtomicAnd8U, kExprI32AtomicAnd16U,
kExprI32AtomicStore16U, kExprI32AtomicOr, kExprI32AtomicOr8U, kExprI32AtomicOr16U,
kExprI32AtomicAdd, kExprI32AtomicXor, kExprI32AtomicXor8U, kExprI32AtomicXor16U,
kExprI32AtomicAdd8U, kExprI32AtomicExchange, kExprI32AtomicExchange8U, kExprI32AtomicExchange16U
kExprI32AtomicAdd16U,
kExprI32AtomicSub,
kExprI32AtomicSub8U,
kExprI32AtomicSub16U,
kExprI32AtomicAnd,
kExprI32AtomicAnd8U,
kExprI32AtomicAnd16U,
kExprI32AtomicOr,
kExprI32AtomicOr8U,
kExprI32AtomicOr16U,
kExprI32AtomicXor,
kExprI32AtomicXor8U,
kExprI32AtomicXor16U,
kExprI32AtomicExchange,
kExprI32AtomicExchange8U,
kExprI32AtomicExchange16U
]; ];
const opCodeNames = [ const opCodeNames = [
"kExprI32AtomicLoad", 'kExprI32AtomicLoad', 'kExprI32AtomicLoad8U',
"kExprI32AtomicLoad8U", 'kExprI32AtomicLoad16U', 'kExprI32AtomicStore',
"kExprI32AtomicLoad16U", 'kExprI32AtomicStore8U', 'kExprI32AtomicStore16U',
"kExprI32AtomicStore", 'kExprI32AtomicAdd', 'kExprI32AtomicAdd8U',
"kExprI32AtomicStore8U", 'kExprI32AtomicAdd16U', 'kExprI32AtomicSub',
"kExprI32AtomicStore16U", 'kExprI32AtomicSub8U', 'kExprI32AtomicSub16U',
"kExprI32AtomicAdd", 'kExprI32AtomicAnd', 'kExprI32AtomicAnd8U',
"kExprI32AtomicAdd8U", 'kExprI32AtomicAnd16U', 'kExprI32AtomicOr',
"kExprI32AtomicAdd16U", 'kExprI32AtomicOr8U', 'kExprI32AtomicOr16U',
"kExprI32AtomicSub", 'kExprI32AtomicXor', 'kExprI32AtomicXor8U',
"kExprI32AtomicSub8U", 'kExprI32AtomicXor16U', 'kExprI32AtomicExchange',
"kExprI32AtomicSub16U", 'kExprI32AtomicExchange8U', 'kExprI32AtomicExchange16U'
"kExprI32AtomicAnd",
"kExprI32AtomicAnd8U",
"kExprI32AtomicAnd16U",
"kExprI32AtomicOr",
"kExprI32AtomicOr8U",
"kExprI32AtomicOr16U",
"kExprI32AtomicXor",
"kExprI32AtomicXor8U",
"kExprI32AtomicXor16U",
"kExprI32AtomicExchange",
"kExprI32AtomicExchange8U",
"kExprI32AtomicExchange16U"
]; ];
let kMaxMemPages = 10;
let gSharedMemory =
new WebAssembly.Memory({initial: 1, maximum: kMaxMemPages, shared: true});
let gSharedMemoryView = new Int32Array(gSharedMemory.buffer);
let gPrivateMemory =
new WebAssembly.Memory({initial: 1, maximum: kMaxMemPages, shared: true});
let gPrivateMemoryView = new Int32Array(gPrivateMemory.buffer);
class Operation { class Operation {
constructor(opcode, input, offset) { constructor(opcode, input, offset) {
this.opcode = opcode != undefined ? opcode : Operation.nextOpcode(); this.opcode = opcode != undefined ? opcode : Operation.nextOpcode();
this.size = Operation.opcodeToSize(this.opcode); this.size = Operation.opcodeToSize(this.opcode);
this.input = input != undefined ? input : Operation.inputForSize( this.input = input != undefined ? input : Operation.inputForSize(this.size);
this.size); this.offset =
this.offset = offset != undefined ? offset : Operation.offsetForSize( offset != undefined ? offset : Operation.offsetForSize(this.size);
this.size);
} }
static nextOpcode() { static nextOpcode() {
...@@ -109,7 +89,8 @@ class Operation { ...@@ -109,7 +89,8 @@ class Operation {
static offsetForSize(size) { static offsetForSize(size) {
// Pick an offset in bytes between 0 and 7. // Pick an offset in bytes between 0 and 7.
let offset = Math.floor(Math.random() * 8); let offset = Math.floor(Math.random() * 8);
// Make sure the offset matches the required alignment by masking out the lower bits. // Make sure the offset matches the required alignment by masking out the
// lower bits.
let size_in_bytes = size / 8; let size_in_bytes = size / 8;
let mask = ~(size_in_bytes - 1); let mask = ~(size_in_bytes - 1);
return offset & mask; return offset & mask;
...@@ -117,7 +98,10 @@ class Operation { ...@@ -117,7 +98,10 @@ class Operation {
get wasmOpcode() { get wasmOpcode() {
// [opcode, alignment, offset] // [opcode, alignment, offset]
return [opCodes[this.opcode], Operation.opcodeToAlignment(this.opcode), this.offset]; return [
opCodes[this.opcode], Operation.opcodeToAlignment(this.opcode),
this.offset
];
} }
get hasInput() { get hasInput() {
...@@ -125,18 +109,20 @@ class Operation { ...@@ -125,18 +109,20 @@ class Operation {
} }
get hasOutput() { get hasOutput() {
return this.opcode < kFirstOpcodeWithoutOutput || this.opcode > return this.opcode < kFirstOpcodeWithoutOutput ||
kLastOpcodeWithoutOutput; this.opcode > kLastOpcodeWithoutOutput;
} }
truncateResultBits(low, high) { truncateResultBits(low, high) {
// Shift the lower part. For offsets greater four it drops out of the visible window. // Shift the lower part. For offsets greater four it drops out of the
// visible window.
let shiftedL = this.offset >= 4 ? 0 : low >>> (this.offset * 8); let shiftedL = this.offset >= 4 ? 0 : low >>> (this.offset * 8);
// The higher part is zero for offset 0, left shifted for [1..3] and right shifted // The higher part is zero for offset 0, left shifted for [1..3] and right
// for [4..7]. // shifted for [4..7].
let shiftedH = this.offset == 0 ? 0 : let shiftedH = this.offset == 0 ?
this.offset >= 4 ? high >>> (this.offset - 4) * 8 : high << ((4 - 0 :
this.offset) * 8); this.offset >= 4 ? high >>> (this.offset - 4) * 8 :
high << ((4 - this.offset) * 8);
let value = shiftedL | shiftedH; let value = shiftedL | shiftedH;
switch (this.size) { switch (this.size) {
...@@ -147,15 +133,14 @@ class Operation { ...@@ -147,15 +133,14 @@ class Operation {
case 32: case 32:
return value; return value;
default: default:
throw "Unexpected size: " + this.size; throw 'Unexpected size: ' + this.size;
} }
} }
static get builder() { static get builder() {
if (!Operation.__builder) { if (!Operation.__builder) {
let builder = new WasmModuleBuilder(); let builder = new WasmModuleBuilder();
builder.addMemory(1, 1, 1, false); builder.addImportedMemory('m', 'imported_mem', 0, kMaxMemPages, 'shared');
builder.exportMemoryAs("mem");
Operation.__builder = builder; Operation.__builder = builder;
} }
return Operation.__builder; return Operation.__builder;
...@@ -168,10 +153,6 @@ class Operation { ...@@ -168,10 +153,6 @@ class Operation {
return Operation.__instance.exports; return Operation.__instance.exports;
} }
static get memory() {
return Operation.exports.mem;
}
static set instance(instance) { static set instance(instance) {
Operation.__instance = instance; Operation.__instance = instance;
} }
...@@ -184,13 +165,11 @@ class Operation { ...@@ -184,13 +165,11 @@ class Operation {
// Load address of low 32 bits. // Load address of low 32 bits.
kExprI32Const, 0, kExprI32Const, 0,
// Load expected value. // Load expected value.
kExprGetLocal, 0, kExprGetLocal, 0, kExprI32StoreMem, 2, 0,
kExprI32StoreMem, 2, 0,
// Load address of high 32 bits. // Load address of high 32 bits.
kExprI32Const, 4, kExprI32Const, 4,
// Load expected value. // Load expected value.
kExprGetLocal, 1, kExprGetLocal, 1, kExprI32StoreMem, 2, 0,
kExprI32StoreMem, 2, 0,
// Load address of where our window starts. // Load address of where our window starts.
kExprI32Const, 0, kExprI32Const, 0,
// Load input if there is one. // Load input if there is one.
...@@ -200,8 +179,7 @@ class Operation { ...@@ -200,8 +179,7 @@ class Operation {
// Drop output if it had any. // Drop output if it had any.
...(this.hasOutput ? [kExprDrop] : []), ...(this.hasOutput ? [kExprDrop] : []),
// Load resulting value. // Load resulting value.
kExprI32Const, 0, kExprI32Const, 0, kExprI32LoadMem, 2, 0,
kExprI32LoadMem, 2, 0,
// Return. // Return.
kExprReturn kExprReturn
] ]
...@@ -210,28 +188,27 @@ class Operation { ...@@ -210,28 +188,27 @@ class Operation {
.exportAs(this.key); .exportAs(this.key);
// Instantiate module, get function exports. // Instantiate module, get function exports.
let module = new WebAssembly.Module(builder.toBuffer()); let module = new WebAssembly.Module(builder.toBuffer());
Operation.instance = new WebAssembly.Instance(module); Operation.instance =
new WebAssembly.Instance(module, {m: {imported_mem: gPrivateMemory}});
evalFun = Operation.exports[this.key]; evalFun = Operation.exports[this.key];
} }
let result = evalFun(state.low, state.high, this.input); let result = evalFun(state.low, state.high, this.input);
let ta = new Int32Array(Operation.memory.buffer); let ta = gPrivateMemoryView;
if (kDebug) { if (kDebug) {
print(state.high + ":" + state.low + " " + this.toString() + print(
" -> " + ta[1] + ":" + ta[0]); state.high + ':' + state.low + ' ' + this.toString() + ' -> ' +
ta[1] + ':' + ta[0]);
} }
if (result != ta[0]) throw "!"; if (result != ta[0]) throw '!';
return { return {low: ta[0], high: ta[1]};
low: ta[0],
high: ta[1]
};
} }
toString() { toString() {
return opCodeNames[this.opcode] + "[+" + this.offset + "] " + this.input; return opCodeNames[this.opcode] + '[+' + this.offset + '] ' + this.input;
} }
get key() { get key() {
return this.opcode + "-" + this.offset; return this.opcode + '-' + this.offset;
} }
} }
...@@ -248,7 +225,7 @@ class State { ...@@ -248,7 +225,7 @@ class State {
} }
toString() { toString() {
return this.high + ":" + this.low + " @ " + this.indices; return this.high + ':' + this.low + ' @ ' + this.indices;
} }
} }
...@@ -284,18 +261,12 @@ function generateFunctionBodyForSequence(sequence) { ...@@ -284,18 +261,12 @@ function generateFunctionBodyForSequence(sequence) {
if (!kDebug) { if (!kDebug) {
body.push( body.push(
// Decrement the wait count. // Decrement the wait count.
kExprGetLocal, 2, kExprGetLocal, 2, kExprI32Const, 1, kAtomicPrefix, kExprI32AtomicSub, 2,
kExprI32Const, 1, 0,
kAtomicPrefix, kExprI32AtomicSub, 2, 0,
// Spin until zero. // Spin until zero.
kExprLoop, kWasmStmt, kExprLoop, kWasmStmt, kExprGetLocal, 2, kAtomicPrefix,
kExprGetLocal, 2, kExprI32AtomicLoad, 2, 0, kExprI32Const, 0, kExprI32GtU, kExprBrIf, 0,
kAtomicPrefix, kExprI32AtomicLoad, 2, 0, kExprEnd);
kExprI32Const, 0,
kExprI32GtU,
kExprBrIf, 0,
kExprEnd
);
} }
for (let operation of sequence) { for (let operation of sequence) {
body.push( body.push(
...@@ -304,8 +275,9 @@ function generateFunctionBodyForSequence(sequence) { ...@@ -304,8 +275,9 @@ function generateFunctionBodyForSequence(sequence) {
// Load address where atomic pointers are stored. // Load address where atomic pointers are stored.
kExprGetLocal, 0, kExprGetLocal, 0,
// Load the second argument if it had any. // Load the second argument if it had any.
...(operation.hasInput ? [kExprI32Const, ...toSLeb128(operation ...(operation.hasInput ?
.input)] : []), [kExprI32Const, ...toSLeb128(operation.input)] :
[]),
// Perform operation // Perform operation
kAtomicPrefix, ...operation.wasmOpcode, kAtomicPrefix, ...operation.wasmOpcode,
// Generate fake output in needed. // Generate fake output in needed.
...@@ -313,21 +285,17 @@ function generateFunctionBodyForSequence(sequence) { ...@@ -313,21 +285,17 @@ function generateFunctionBodyForSequence(sequence) {
// Store read intermediate to sequence. // Store read intermediate to sequence.
kExprI32StoreMem, 2, 0, kExprI32StoreMem, 2, 0,
// Increment result sequence pointer. // Increment result sequence pointer.
kExprGetLocal, 1, kExprGetLocal, 1, kExprI32Const, 4, kExprI32Add, kExprSetLocal, 1);
kExprI32Const, 4,
kExprI32Add,
kExprSetLocal, 1
);
} }
// Return end of sequence index. // Return end of sequence index.
body.push( body.push(kExprGetLocal, 1, kExprReturn);
kExprGetLocal, 1,
kExprReturn);
return body; return body;
} }
function getSequence(start, end) { function getSequence(start, end) {
return new Int32Array(memory.buffer, start, (end - start) / Int32Array.BYTES_PER_ELEMENT); return new Int32Array(
gSharedMemory.buffer, start,
(end - start) / Int32Array.BYTES_PER_ELEMENT);
} }
function spawnWorkers() { function spawnWorkers() {
...@@ -348,8 +316,8 @@ function spawnWorkers() { ...@@ -348,8 +316,8 @@ function spawnWorkers() {
let result = instance.exports["worker" + index](address, sequence, spin); let result = instance.exports["worker" + index](address, sequence, spin);
postMessage({index: index, sequence: sequence, result: result}); postMessage({index: index, sequence: sequence, result: result});
} }
}`, {type: 'string'} }`,
); {type: 'string'});
workers.push(worker); workers.push(worker);
} }
return workers; return workers;
...@@ -357,12 +325,9 @@ function spawnWorkers() { ...@@ -357,12 +325,9 @@ function spawnWorkers() {
function instantiateModuleInWorkers(workers) { function instantiateModuleInWorkers(workers) {
for (let worker of workers) { for (let worker of workers) {
worker.postMessage({ worker.postMessage({module: module, mem: gSharedMemory});
module: module,
mem: memory
});
let msg = worker.getMessage(); let msg = worker.getMessage();
if (!msg.instantiated) throw "Worker failed to instantiate"; if (!msg.instantiated) throw 'Worker failed to instantiate';
} }
} }
...@@ -391,7 +356,8 @@ function selectMatchingWorkers(state) { ...@@ -391,7 +356,8 @@ function selectMatchingWorkers(state) {
if (index >= kSequenceLength) continue; if (index >= kSequenceLength) continue;
// We need to project the expected value to the number of bits this // We need to project the expected value to the number of bits this
// operation will read at runtime. // operation will read at runtime.
let expected = sequences[i][index].truncateResultBits(state.low, state.high); let expected =
sequences[i][index].truncateResultBits(state.low, state.high);
let hasOutput = sequences[i][index].hasOutput; let hasOutput = sequences[i][index].hasOutput;
if (!hasOutput || (results[i][index] == expected)) { if (!hasOutput || (results[i][index] == expected)) {
matching.push(i); matching.push(i);
...@@ -405,10 +371,7 @@ function computeNextState(state, advanceIdx) { ...@@ -405,10 +371,7 @@ function computeNextState(state, advanceIdx) {
let sequence = sequences[advanceIdx]; let sequence = sequences[advanceIdx];
let operation = sequence[state.indices[advanceIdx]]; let operation = sequence[state.indices[advanceIdx]];
newIndices[advanceIdx]++; newIndices[advanceIdx]++;
let { let {low, high} = operation.compute(state);
low,
high
} = operation.compute(state);
return new State(low, high, newIndices, state.count + 1); return new State(low, high, newIndices, state.count + 1);
} }
...@@ -434,7 +397,7 @@ function findSequentialOrdering() { ...@@ -434,7 +397,7 @@ function findSequentialOrdering() {
matchingStates.push(newState); matchingStates.push(newState);
} }
if (steps++ > kNumberOfSteps) { if (steps++ > kNumberOfSteps) {
print("Search timed out, aborting..."); print('Search timed out, aborting...');
return true; return true;
} }
} }
...@@ -451,12 +414,12 @@ function loadSequencesFromStrings(inputs) { ...@@ -451,12 +414,12 @@ function loadSequencesFromStrings(inputs) {
let sequences = []; let sequences = [];
let parseRE = /([a-zA-Z0-9]*)\[\+([0-9])\] ([\-0-9]*)/; let parseRE = /([a-zA-Z0-9]*)\[\+([0-9])\] ([\-0-9]*)/;
for (let input of inputs) { for (let input of inputs) {
let parts = input.split(","); let parts = input.split(',');
let sequence = []; let sequence = [];
for (let part of parts) { for (let part of parts) {
let parsed = parseRE.exec(part); let parsed = parseRE.exec(part);
sequence.push(new Operation(reverseOpcodes[parsed[1]], parsed[3], sequence.push(
parsed[2] | 0)); new Operation(reverseOpcodes[parsed[1]], parsed[3], parsed[2] | 0));
} }
sequences.push(sequence); sequences.push(sequence);
} }
...@@ -467,7 +430,7 @@ function loadSequencesFromStrings(inputs) { ...@@ -467,7 +430,7 @@ function loadSequencesFromStrings(inputs) {
function loadResultsFromStrings(inputs) { function loadResultsFromStrings(inputs) {
let results = []; let results = [];
for (let input of inputs) { for (let input of inputs) {
let parts = input.split(","); let parts = input.split(',');
let result = []; let result = [];
for (let number of parts) { for (let number of parts) {
result.push(number | 0); result.push(number | 0);
...@@ -477,39 +440,28 @@ function loadResultsFromStrings(inputs) { ...@@ -477,39 +440,28 @@ function loadResultsFromStrings(inputs) {
return results; return results;
} }
let maxSize = 10;
let memory = new WebAssembly.Memory({
initial: 1,
maximum: maxSize,
shared: true
});
let memory_view = new Int32Array(memory.buffer);
let sequences = []; let sequences = [];
let results = []; let results = [];
let builder = new WasmModuleBuilder(); let builder = new WasmModuleBuilder();
builder.addImportedMemory("m", "imported_mem", 0, maxSize, "shared"); builder.addImportedMemory('m', 'imported_mem', 0, kMaxMemPages, 'shared');
for (let i = 0; i < kNumberOfWorker; i++) { for (let i = 0; i < kNumberOfWorker; i++) {
sequences[i] = makeSequenceOfOperations(kSequenceLength); sequences[i] = makeSequenceOfOperations(kSequenceLength);
builder.addFunction("worker" + i, kSig_i_iii) builder.addFunction('worker' + i, kSig_i_iii)
.addBody(generateFunctionBodyForSequence(sequences[i])) .addBody(generateFunctionBodyForSequence(sequences[i]))
.exportAs("worker" + i); .exportAs('worker' + i);
} }
// Instantiate module, get function exports. // Instantiate module, get function exports.
let module = new WebAssembly.Module(builder.toBuffer()); let module = new WebAssembly.Module(builder.toBuffer());
let instance = new WebAssembly.Instance(module, { let instance =
m: { new WebAssembly.Instance(module, {m: {imported_mem: gSharedMemory}});
imported_mem: memory
}
});
// Spawn off the workers and run the sequences. // Spawn off the workers and run the sequences.
let workers = spawnWorkers(); let workers = spawnWorkers();
// Set spin count. // Set spin count.
memory_view[4] = kNumberOfWorker; gSharedMemoryView[4] = kNumberOfWorker;
instantiateModuleInWorkers(workers); instantiateModuleInWorkers(workers);
executeSequenceInWorkers(workers); executeSequenceInWorkers(workers);
...@@ -541,13 +493,13 @@ if (kDebug) { ...@@ -541,13 +493,13 @@ if (kDebug) {
let passed = findSequentialOrdering(); let passed = findSequentialOrdering();
if (passed) { if (passed) {
print("PASS"); print('PASS');
} else { } else {
for (let i = 0; i < kNumberOfWorker; i++) { for (let i = 0; i < kNumberOfWorker; i++) {
print("Worker " + i); print('Worker ' + i);
print(sequences[i]); print(sequences[i]);
print(results[i]); print(results[i]);
} }
print("FAIL"); print('FAIL');
quit(-1); quit(-1);
} }
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
// Note that results of this test are flaky by design. While the test is // Note that results of this test are flaky by design. While the test is
// deterministic with a fixed seed, bugs may introduce non-determinism. // deterministic with a fixed seed, bugs may introduce non-determinism.
load("test/mjsunit/wasm/wasm-module-builder.js"); load('test/mjsunit/wasm/wasm-module-builder.js');
const kDebug = false; const kDebug = false;
...@@ -22,75 +22,47 @@ const kFirstOpcodeWithoutOutput = 4; ...@@ -22,75 +22,47 @@ const kFirstOpcodeWithoutOutput = 4;
const kLastOpcodeWithoutOutput = 7; const kLastOpcodeWithoutOutput = 7;
const opCodes = [ const opCodes = [
kExprI64AtomicLoad, kExprI64AtomicLoad, kExprI64AtomicLoad8U, kExprI64AtomicLoad16U,
kExprI64AtomicLoad8U, kExprI64AtomicLoad32U, kExprI64AtomicStore, kExprI64AtomicStore8U,
kExprI64AtomicLoad16U, kExprI64AtomicStore16U, kExprI64AtomicStore32U, kExprI64AtomicAdd,
kExprI64AtomicLoad32U, kExprI64AtomicAdd8U, kExprI64AtomicAdd16U, kExprI64AtomicAdd32U,
kExprI64AtomicStore, kExprI64AtomicSub, kExprI64AtomicSub8U, kExprI64AtomicSub16U,
kExprI64AtomicStore8U, kExprI64AtomicSub32U, kExprI64AtomicAnd, kExprI64AtomicAnd8U,
kExprI64AtomicStore16U, kExprI64AtomicAnd16U, kExprI64AtomicAnd32U, kExprI64AtomicOr,
kExprI64AtomicStore32U, kExprI64AtomicOr8U, kExprI64AtomicOr16U, kExprI64AtomicOr32U,
kExprI64AtomicAdd, kExprI64AtomicXor, kExprI64AtomicXor8U, kExprI64AtomicXor16U,
kExprI64AtomicAdd8U, kExprI64AtomicXor32U, kExprI64AtomicExchange, kExprI64AtomicExchange8U,
kExprI64AtomicAdd16U, kExprI64AtomicExchange16U, kExprI64AtomicExchange32U
kExprI64AtomicAdd32U,
kExprI64AtomicSub,
kExprI64AtomicSub8U,
kExprI64AtomicSub16U,
kExprI64AtomicSub32U,
kExprI64AtomicAnd,
kExprI64AtomicAnd8U,
kExprI64AtomicAnd16U,
kExprI64AtomicAnd32U,
kExprI64AtomicOr,
kExprI64AtomicOr8U,
kExprI64AtomicOr16U,
kExprI64AtomicOr32U,
kExprI64AtomicXor,
kExprI64AtomicXor8U,
kExprI64AtomicXor16U,
kExprI64AtomicXor32U,
kExprI64AtomicExchange,
kExprI64AtomicExchange8U,
kExprI64AtomicExchange16U,
kExprI64AtomicExchange32U
]; ];
const opCodeNames = [ const opCodeNames = [
"kExprI64AtomicLoad", 'kExprI64AtomicLoad', 'kExprI64AtomicLoad8U',
"kExprI64AtomicLoad8U", 'kExprI64AtomicLoad16U', 'kExprI64AtomicLoad32U',
"kExprI64AtomicLoad16U", 'kExprI64AtomicStore', 'kExprI64AtomicStore8U',
"kExprI64AtomicLoad32U", 'kExprI64AtomicStore16U', 'kExprI64AtomicStore32U',
"kExprI64AtomicStore", 'kExprI64AtomicAdd', 'kExprI64AtomicAdd8U',
"kExprI64AtomicStore8U", 'kExprI64AtomicAdd16U', 'kExprI64AtomicAdd32U',
"kExprI64AtomicStore16U", 'kExprI64AtomicSub', 'kExprI64AtomicSub8U',
"kExprI64AtomicStore32U", 'kExprI64AtomicSub16U', 'kExprI64AtomicSub32U',
"kExprI64AtomicAdd", 'kExprI64AtomicAnd', 'kExprI64AtomicAnd8U',
"kExprI64AtomicAdd8U", 'kExprI64AtomicAnd16U', 'kExprI64AtomicAnd32U',
"kExprI64AtomicAdd16U", 'kExprI64AtomicOr', 'kExprI64AtomicOr8U',
"kExprI64AtomicAdd32U", 'kExprI64AtomicOr16U', 'kExprI64AtomicOr32U',
"kExprI64AtomicSub", 'kExprI64AtomicXor', 'kExprI64AtomicXor8U',
"kExprI64AtomicSub8U", 'kExprI64AtomicXor16U', 'kExprI64AtomicXor32U',
"kExprI64AtomicSub16U", 'kExprI64AtomicExchange', 'kExprI64AtomicExchange8U',
"kExprI64AtomicSub32U", 'kExprI64AtomicExchange16U', 'kExprI64AtomicExchange32U'
"kExprI64AtomicAnd",
"kExprI64AtomicAnd8U",
"kExprI64AtomicAnd16U",
"kExprI64AtomicAnd32U",
"kExprI64AtomicOr",
"kExprI64AtomicOr8U",
"kExprI64AtomicOr16U",
"kExprI64AtomicOr32U",
"kExprI64AtomicXor",
"kExprI64AtomicXor8U",
"kExprI64AtomicXor16U",
"kExprI64AtomicXor32U",
"kExprI64AtomicExchange",
"kExprI64AtomicExchange8U",
"kExprI64AtomicExchange16U",
"kExprI64AtomicExchange32U"
]; ];
let kMaxMemPages = 10;
let gSharedMemory =
new WebAssembly.Memory({initial: 1, maximum: kMaxMemPages, shared: true});
let gSharedMemoryView = new Int32Array(gSharedMemory.buffer);
let gPrivateMemory =
new WebAssembly.Memory({initial: 1, maximum: kMaxMemPages, shared: true});
let gPrivateMemoryView = new Int32Array(gPrivateMemory.buffer);
const kMaxInt32 = (1 << 31) * 2; const kMaxInt32 = (1 << 31) * 2;
class Operation { class Operation {
...@@ -102,8 +74,8 @@ class Operation { ...@@ -102,8 +74,8 @@ class Operation {
} }
this.low_input = low_input; this.low_input = low_input;
this.high_input = high_input; this.high_input = high_input;
this.offset = offset != undefined ? offset : Operation.offsetForSize( this.offset =
this.size); offset != undefined ? offset : Operation.offsetForSize(this.size);
} }
static nextOpcode() { static nextOpcode() {
...@@ -127,14 +99,17 @@ class Operation { ...@@ -127,14 +99,17 @@ class Operation {
// Avoid 32 bit overflow for integer here :( // Avoid 32 bit overflow for integer here :(
return [Math.floor(random * (1 << (size - 1)) * 2), 0]; return [Math.floor(random * (1 << (size - 1)) * 2), 0];
} }
return [Math.floor(Math.random() * kMaxInt32), Math.floor(Math.random() * return [
kMaxInt32)]; Math.floor(Math.random() * kMaxInt32),
Math.floor(Math.random() * kMaxInt32)
];
} }
static offsetForSize(size) { static offsetForSize(size) {
// Pick an offset in bytes between 0 and 8. // Pick an offset in bytes between 0 and 8.
let offset = Math.floor(Math.random() * 8); let offset = Math.floor(Math.random() * 8);
// Make sure the offset matches the required alignment by masking out the lower bits. // Make sure the offset matches the required alignment by masking out the
// lower bits.
let size_in_bytes = size / 8; let size_in_bytes = size / 8;
let mask = ~(size_in_bytes - 1); let mask = ~(size_in_bytes - 1);
return offset & mask; return offset & mask;
...@@ -142,7 +117,10 @@ class Operation { ...@@ -142,7 +117,10 @@ class Operation {
get wasmOpcode() { get wasmOpcode() {
// [opcode, alignment, offset] // [opcode, alignment, offset]
return [opCodes[this.opcode], Operation.opcodeToAlignment(this.opcode), this.offset]; return [
opCodes[this.opcode], Operation.opcodeToAlignment(this.opcode),
this.offset
];
} }
get hasInput() { get hasInput() {
...@@ -150,20 +128,23 @@ class Operation { ...@@ -150,20 +128,23 @@ class Operation {
} }
get hasOutput() { get hasOutput() {
return this.opcode < kFirstOpcodeWithoutOutput || this.opcode > return this.opcode < kFirstOpcodeWithoutOutput ||
kLastOpcodeWithoutOutput; this.opcode > kLastOpcodeWithoutOutput;
} }
truncateResultBits(low, high) { truncateResultBits(low, high) {
if (this.size == 64) return [low, high] if (this.size == 64)
return [low, high]
// Shift the lower part. For offsets greater four it drops out of the visible window. // Shift the lower part. For offsets greater four it drops out of the
// visible window.
let shiftedL = this.offset >= 4 ? 0 : low >>> (this.offset * 8); let shiftedL = this.offset >= 4 ? 0 : low >>> (this.offset * 8);
// The higher part is zero for offset 0, left shifted for [1..3] and right shifted // The higher part is zero for offset 0, left shifted for [1..3] and right
// for [4..7]. // shifted for [4..7].
let shiftedH = this.offset == 0 ? 0 : let shiftedH = this.offset == 0 ?
this.offset >= 4 ? high >>> (this.offset - 4) * 8 : high << ((4 - 0 :
this.offset) * 8); this.offset >= 4 ? high >>> (this.offset - 4) * 8 :
high << ((4 - this.offset) * 8);
let value = shiftedL | shiftedH; let value = shiftedL | shiftedH;
switch (this.size) { switch (this.size) {
...@@ -174,15 +155,14 @@ class Operation { ...@@ -174,15 +155,14 @@ class Operation {
case 32: case 32:
return [value, 0]; return [value, 0];
default: default:
throw "Unexpected size: " + this.size; throw 'Unexpected size: ' + this.size;
} }
} }
static get builder() { static get builder() {
if (!Operation.__builder) { if (!Operation.__builder) {
let builder = new WasmModuleBuilder(); let builder = new WasmModuleBuilder();
builder.addMemory(1, 1, 1, false); builder.addImportedMemory('m', 'imported_mem', 0, kMaxMemPages, 'shared');
builder.exportMemoryAs("mem");
Operation.__builder = builder; Operation.__builder = builder;
} }
return Operation.__builder; return Operation.__builder;
...@@ -211,24 +191,21 @@ class Operation { ...@@ -211,24 +191,21 @@ class Operation {
// Load address of low 32 bits. // Load address of low 32 bits.
kExprI32Const, 0, kExprI32Const, 0,
// Load expected value. // Load expected value.
kExprGetLocal, 0, kExprGetLocal, 0, kExprI32StoreMem, 2, 0,
kExprI32StoreMem, 2, 0,
// Load address of high 32 bits. // Load address of high 32 bits.
kExprI32Const, 4, kExprI32Const, 4,
// Load expected value. // Load expected value.
kExprGetLocal, 1, kExprGetLocal, 1, kExprI32StoreMem, 2, 0,
kExprI32StoreMem, 2, 0,
// Load address of where our window starts. // Load address of where our window starts.
kExprI32Const, 0, kExprI32Const, 0,
// Load input if there is one. // Load input if there is one.
...(this.hasInput ? [kExprGetLocal, 3, ...(this.hasInput ?
kExprI64UConvertI32, [
kExprI64Const, 32, kExprGetLocal, 3, kExprI64UConvertI32, kExprI64Const, 32,
kExprI64Shl, kExprI64Shl, kExprGetLocal, 2, kExprI64UConvertI32,
kExprGetLocal, 2,
kExprI64UConvertI32,
kExprI64Ior kExprI64Ior
] : []), ] :
[]),
// Perform operation. // Perform operation.
kAtomicPrefix, ...this.wasmOpcode, kAtomicPrefix, ...this.wasmOpcode,
// Drop output if it had any. // Drop output if it had any.
...@@ -241,28 +218,27 @@ class Operation { ...@@ -241,28 +218,27 @@ class Operation {
.exportAs(this.key); .exportAs(this.key);
// Instantiate module, get function exports. // Instantiate module, get function exports.
let module = new WebAssembly.Module(builder.toBuffer()); let module = new WebAssembly.Module(builder.toBuffer());
Operation.instance = new WebAssembly.Instance(module); Operation.instance =
new WebAssembly.Instance(module, {m: {imported_mem: gPrivateMemory}});
evalFun = Operation.exports[this.key]; evalFun = Operation.exports[this.key];
} }
evalFun(state.low, state.high, this.low_input, this.high_input); evalFun(state.low, state.high, this.low_input, this.high_input);
let ta = new Int32Array(Operation.memory.buffer); let ta = gPrivateMemoryView;
if (kDebug) { if (kDebug) {
print(state.high + ":" + state.low + " " + this.toString() + print(
" -> " + ta[1] + ":" + ta[0]); state.high + ':' + state.low + ' ' + this.toString() + ' -> ' +
ta[1] + ':' + ta[0]);
} }
return { return {low: ta[0], high: ta[1]};
low: ta[0],
high: ta[1]
};
} }
toString() { toString() {
return opCodeNames[this.opcode] + "[+" + this.offset + "] " + this.high_input + return opCodeNames[this.opcode] + '[+' + this.offset + '] ' +
":" + this.low_input; this.high_input + ':' + this.low_input;
} }
get key() { get key() {
return this.opcode + "-" + this.offset; return this.opcode + '-' + this.offset;
} }
} }
...@@ -279,7 +255,7 @@ class State { ...@@ -279,7 +255,7 @@ class State {
} }
toString() { toString() {
return this.high + ":" + this.low + " @ " + this.indices; return this.high + ':' + this.low + ' @ ' + this.indices;
} }
} }
...@@ -304,8 +280,8 @@ function toSLeb128(low, high) { ...@@ -304,8 +280,8 @@ function toSLeb128(low, high) {
high = high >> 7; high = high >> 7;
} }
let msbIsSet = (v & 0x40) || false; let msbIsSet = (v & 0x40) || false;
if (((low == 0) && (high == 0) && !msbIsSet) || ((low == -1) && (high == if (((low == 0) && (high == 0) && !msbIsSet) ||
-1) && msbIsSet)) { ((low == -1) && (high == -1) && msbIsSet)) {
result.push(v); result.push(v);
break; break;
} }
...@@ -323,18 +299,12 @@ function generateFunctionBodyForSequence(sequence) { ...@@ -323,18 +299,12 @@ function generateFunctionBodyForSequence(sequence) {
if (!kDebug) { if (!kDebug) {
body.push( body.push(
// Decrement the wait count. // Decrement the wait count.
kExprGetLocal, 2, kExprGetLocal, 2, kExprI32Const, 1, kAtomicPrefix, kExprI32AtomicSub, 2,
kExprI32Const, 1, 0,
kAtomicPrefix, kExprI32AtomicSub, 2, 0,
// Spin until zero. // Spin until zero.
kExprLoop, kWasmStmt, kExprLoop, kWasmStmt, kExprGetLocal, 2, kAtomicPrefix,
kExprGetLocal, 2, kExprI32AtomicLoad, 2, 0, kExprI32Const, 0, kExprI32GtU, kExprBrIf, 0,
kAtomicPrefix, kExprI32AtomicLoad, 2, 0, kExprEnd);
kExprI32Const, 0,
kExprI32GtU,
kExprBrIf, 0,
kExprEnd
);
} }
for (let operation of sequence) { for (let operation of sequence) {
body.push( body.push(
...@@ -343,8 +313,12 @@ function generateFunctionBodyForSequence(sequence) { ...@@ -343,8 +313,12 @@ function generateFunctionBodyForSequence(sequence) {
// Load address where atomic pointers are stored. // Load address where atomic pointers are stored.
kExprGetLocal, 0, kExprGetLocal, 0,
// Load the second argument if it had any. // Load the second argument if it had any.
...(operation.hasInput ? [kExprI64Const, ...toSLeb128(operation ...(operation.hasInput ?
.low_input, operation.high_input)] : []), [
kExprI64Const,
...toSLeb128(operation.low_input, operation.high_input)
] :
[]),
// Perform operation // Perform operation
kAtomicPrefix, ...operation.wasmOpcode, kAtomicPrefix, ...operation.wasmOpcode,
// Generate fake output in needed. // Generate fake output in needed.
...@@ -352,21 +326,17 @@ function generateFunctionBodyForSequence(sequence) { ...@@ -352,21 +326,17 @@ function generateFunctionBodyForSequence(sequence) {
// Store read intermediate to sequence. // Store read intermediate to sequence.
kExprI64StoreMem, 3, 0, kExprI64StoreMem, 3, 0,
// Increment result sequence pointer. // Increment result sequence pointer.
kExprGetLocal, 1, kExprGetLocal, 1, kExprI32Const, 8, kExprI32Add, kExprSetLocal, 1);
kExprI32Const, 8,
kExprI32Add,
kExprSetLocal, 1
);
} }
// Return end of sequence index. // Return end of sequence index.
body.push( body.push(kExprGetLocal, 1, kExprReturn);
kExprGetLocal, 1,
kExprReturn);
return body; return body;
} }
function getSequence(start, end) { function getSequence(start, end) {
return new Int32Array(memory.buffer, start, (end - start) / Int32Array.BYTES_PER_ELEMENT); return new Int32Array(
gSharedMemory.buffer, start,
(end - start) / Int32Array.BYTES_PER_ELEMENT);
} }
function spawnWorkers() { function spawnWorkers() {
...@@ -387,8 +357,8 @@ function spawnWorkers() { ...@@ -387,8 +357,8 @@ function spawnWorkers() {
let result = instance.exports["worker" + index](address, sequence, spin); let result = instance.exports["worker" + index](address, sequence, spin);
postMessage({index: index, sequence: sequence, result: result}); postMessage({index: index, sequence: sequence, result: result});
} }
}`, {type: 'string'} }`,
); {type: 'string'});
workers.push(worker); workers.push(worker);
} }
return workers; return workers;
...@@ -396,12 +366,9 @@ function spawnWorkers() { ...@@ -396,12 +366,9 @@ function spawnWorkers() {
function instantiateModuleInWorkers(workers) { function instantiateModuleInWorkers(workers) {
for (let worker of workers) { for (let worker of workers) {
worker.postMessage({ worker.postMessage({module: module, mem: gSharedMemory});
module: module,
mem: memory
});
let msg = worker.getMessage(); let msg = worker.getMessage();
if (!msg.instantiated) throw "Worker failed to instantiate"; if (!msg.instantiated) throw 'Worker failed to instantiate';
} }
} }
...@@ -430,11 +397,12 @@ function selectMatchingWorkers(state) { ...@@ -430,11 +397,12 @@ function selectMatchingWorkers(state) {
if (index >= kSequenceLength) continue; if (index >= kSequenceLength) continue;
// We need to project the expected value to the number of bits this // We need to project the expected value to the number of bits this
// operation will read at runtime. // operation will read at runtime.
let [expected_low, expected_high] = sequences[i][index].truncateResultBits( let [expected_low, expected_high] =
state.low, state.high); sequences[i][index].truncateResultBits(state.low, state.high);
let hasOutput = sequences[i][index].hasOutput; let hasOutput = sequences[i][index].hasOutput;
if (!hasOutput || ((results[i][index * 2] == expected_low) && (results[ if (!hasOutput ||
i][index * 2 + 1] == expected_high))) { ((results[i][index * 2] == expected_low) &&
(results[i][index * 2 + 1] == expected_high))) {
matching.push(i); matching.push(i);
} }
} }
...@@ -446,10 +414,7 @@ function computeNextState(state, advanceIdx) { ...@@ -446,10 +414,7 @@ function computeNextState(state, advanceIdx) {
let sequence = sequences[advanceIdx]; let sequence = sequences[advanceIdx];
let operation = sequence[state.indices[advanceIdx]]; let operation = sequence[state.indices[advanceIdx]];
newIndices[advanceIdx]++; newIndices[advanceIdx]++;
let { let {low, high} = operation.compute(state);
low,
high
} = operation.compute(state);
return new State(low, high, newIndices, state.count + 1); return new State(low, high, newIndices, state.count + 1);
} }
...@@ -476,7 +441,7 @@ function findSequentialOrdering() { ...@@ -476,7 +441,7 @@ function findSequentialOrdering() {
matchingStates.push(newState); matchingStates.push(newState);
} }
if (steps++ > kNumberOfSteps) { if (steps++ > kNumberOfSteps) {
print("Search timed out, aborting..."); print('Search timed out, aborting...');
return true; return true;
} }
} }
...@@ -493,12 +458,12 @@ function loadSequencesFromStrings(inputs) { ...@@ -493,12 +458,12 @@ function loadSequencesFromStrings(inputs) {
let sequences = []; let sequences = [];
let parseRE = /([a-zA-Z0-9]*)\[\+([0-9])\] ([\-0-9]*)/; let parseRE = /([a-zA-Z0-9]*)\[\+([0-9])\] ([\-0-9]*)/;
for (let input of inputs) { for (let input of inputs) {
let parts = input.split(","); let parts = input.split(',');
let sequence = []; let sequence = [];
for (let part of parts) { for (let part of parts) {
let parsed = parseRE.exec(part); let parsed = parseRE.exec(part);
sequence.push(new Operation(reverseOpcodes[parsed[1]], parsed[3], sequence.push(
parsed[2] | 0)); new Operation(reverseOpcodes[parsed[1]], parsed[3], parsed[2] | 0));
} }
sequences.push(sequence); sequences.push(sequence);
} }
...@@ -509,7 +474,7 @@ function loadSequencesFromStrings(inputs) { ...@@ -509,7 +474,7 @@ function loadSequencesFromStrings(inputs) {
function loadResultsFromStrings(inputs) { function loadResultsFromStrings(inputs) {
let results = []; let results = [];
for (let input of inputs) { for (let input of inputs) {
let parts = input.split(","); let parts = input.split(',');
let result = []; let result = [];
for (let number of parts) { for (let number of parts) {
result.push(number | 0); result.push(number | 0);
...@@ -519,39 +484,28 @@ function loadResultsFromStrings(inputs) { ...@@ -519,39 +484,28 @@ function loadResultsFromStrings(inputs) {
return results; return results;
} }
let maxSize = 10;
let memory = new WebAssembly.Memory({
initial: 1,
maximum: maxSize,
shared: true
});
let memory_view = new Int32Array(memory.buffer);
let sequences = []; let sequences = [];
let results = []; let results = [];
let builder = new WasmModuleBuilder(); let builder = new WasmModuleBuilder();
builder.addImportedMemory("m", "imported_mem", 0, maxSize, "shared"); builder.addImportedMemory('m', 'imported_mem', 0, kMaxMemPages, 'shared');
for (let i = 0; i < kNumberOfWorker; i++) { for (let i = 0; i < kNumberOfWorker; i++) {
sequences[i] = makeSequenceOfOperations(kSequenceLength); sequences[i] = makeSequenceOfOperations(kSequenceLength);
builder.addFunction("worker" + i, kSig_i_iii) builder.addFunction('worker' + i, kSig_i_iii)
.addBody(generateFunctionBodyForSequence(sequences[i])) .addBody(generateFunctionBodyForSequence(sequences[i]))
.exportAs("worker" + i); .exportAs('worker' + i);
} }
// Instantiate module, get function exports. // Instantiate module, get function exports.
let module = new WebAssembly.Module(builder.toBuffer()); let module = new WebAssembly.Module(builder.toBuffer());
let instance = new WebAssembly.Instance(module, { let instance =
m: { new WebAssembly.Instance(module, {m: {imported_mem: gSharedMemory}});
imported_mem: memory
}
});
// Spawn off the workers and run the sequences. // Spawn off the workers and run the sequences.
let workers = spawnWorkers(); let workers = spawnWorkers();
// Set spin count. // Set spin count.
memory_view[4] = kNumberOfWorker; gSharedMemoryView[4] = kNumberOfWorker;
instantiateModuleInWorkers(workers); instantiateModuleInWorkers(workers);
executeSequenceInWorkers(workers); executeSequenceInWorkers(workers);
...@@ -583,13 +537,13 @@ if (kDebug) { ...@@ -583,13 +537,13 @@ if (kDebug) {
let passed = findSequentialOrdering(); let passed = findSequentialOrdering();
if (passed) { if (passed) {
print("PASS"); print('PASS');
} else { } else {
for (let i = 0; i < kNumberOfWorker; i++) { for (let i = 0; i < kNumberOfWorker; i++) {
print("Worker " + i); print('Worker ' + i);
print(sequences[i]); print(sequences[i]);
print(results[i]); print(results[i]);
} }
print("FAIL"); print('FAIL');
quit(-1); quit(-1);
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment