зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1619196 - allow atomics on non-shared memory part 2: test cases. r=rhunt
Basically generalize almost all the tests to run with both shared and unshared memory, removing a number of validation failures. Test run-time failure in the case of wait() on unshared memory, and that notify() on unshared memory returns 0. Differential Revision: https://phabricator.services.mozilla.com/D81320
This commit is contained in:
Родитель
cd41e1a357
Коммит
724b5a1d44
|
@ -4,85 +4,92 @@ const oob = /index out of bounds/;
|
|||
const unaligned = /unaligned memory access/;
|
||||
const RuntimeError = WebAssembly.RuntimeError;
|
||||
|
||||
function valText(text) {
|
||||
return WebAssembly.validate(wasmTextToBinary(text));
|
||||
}
|
||||
|
||||
function assertNum(a, b) {
|
||||
if (typeof a == "number" && typeof b == "number")
|
||||
assertEq(a, b);
|
||||
else if (typeof a == "number") {
|
||||
assertEq(a, b.low);
|
||||
assertEq(0, b.high);
|
||||
} else if (typeof b == "number") {
|
||||
assertEq(a.low, b);
|
||||
assertEq(a.high, 0);
|
||||
} else {
|
||||
assertEq(a.high, b.high);
|
||||
assertEq(a.low, b.low);
|
||||
}
|
||||
}
|
||||
|
||||
// Check that the output of wasmTextToBinary verifies correctly.
|
||||
|
||||
let SHARED = 'shared';
|
||||
let UNSHARED = '';
|
||||
let sharedError = 'memory with atomic operations without shared memory';
|
||||
|
||||
for (let [type,width,view] of [['i32','8', '_u'],['i32','16','_u'],['i32','',''],['i64','8','_u'],['i64','16','_u'],['i64','32','_u'],['i64','','']]) {
|
||||
{
|
||||
let text = (shared) => `(module (memory 1 1 ${shared})
|
||||
for ( let shared of ['shared', ''] ) {
|
||||
for (let [type,width,view] of [['i32','8', '_u'],['i32','16','_u'],['i32','',''],['i64','8','_u'],['i64','16','_u'],['i64','32','_u'],['i64','','']]) {
|
||||
{
|
||||
let text = (shared) => `(module (memory 1 1 ${shared})
|
||||
(func (result ${type}) (${type}.atomic.load${width}${view} (i32.const 0)))
|
||||
(export "" (func 0)))`;
|
||||
assertEq(valText(text(SHARED)), true);
|
||||
assertEq(valText(text(UNSHARED)), false);
|
||||
}
|
||||
assertEq(valText(text(shared)), true);
|
||||
}
|
||||
|
||||
{
|
||||
let text = (shared) => `(module (memory 1 1 ${shared})
|
||||
{
|
||||
let text = (shared) => `(module (memory 1 1 ${shared})
|
||||
(func (${type}.atomic.store${width} (i32.const 0) (${type}.const 1)))
|
||||
(export "" (func 0)))`;
|
||||
assertEq(valText(text(SHARED)), true);
|
||||
assertEq(valText(text(UNSHARED)), false);
|
||||
}
|
||||
assertEq(valText(text(shared)), true);
|
||||
}
|
||||
|
||||
{
|
||||
let text = (shared) => `(module (memory 1 1 ${shared})
|
||||
{
|
||||
let text = (shared) => `(module (memory 1 1 ${shared})
|
||||
(func (result ${type})
|
||||
(${type}.atomic.rmw${width}.cmpxchg${view} (i32.const 0) (${type}.const 1) (${type}.const 2)))
|
||||
(export "" (func 0)))`;
|
||||
assertEq(valText(text(SHARED)), true);
|
||||
assertEq(valText(text(UNSHARED)), false);
|
||||
}
|
||||
assertEq(valText(text(shared)), true);
|
||||
}
|
||||
|
||||
for (let op of ['add','and','or','sub','xor','xchg']) {
|
||||
// Operate with appropriately-typed value 1 on address 0
|
||||
let text = (shared) => `(module (memory 1 1 ${shared})
|
||||
for (let op of ['add','and','or','sub','xor','xchg']) {
|
||||
// Operate with appropriately-typed value 1 on address 0
|
||||
let text = (shared) => `(module (memory 1 1 ${shared})
|
||||
(func (result ${type}) (${type}.atomic.rmw${width}.${op}${view} (i32.const 0) (${type}.const 1)))
|
||||
(export "" (func 0)))`;
|
||||
|
||||
assertEq(valText(text(SHARED)), true);
|
||||
assertEq(valText(text(UNSHARED)), false);
|
||||
assertEq(valText(text(shared)), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (let type of ['i32', 'i64']) {
|
||||
let text = (shared) => `(module (memory 1 1 ${shared})
|
||||
for (let type of ['i32', 'i64']) {
|
||||
let text = (shared) => `(module (memory 1 1 ${shared})
|
||||
(func (result i32) (${type}.atomic.wait (i32.const 0) (${type}.const 1) (i64.const -1)))
|
||||
(export "" (func 0)))`;
|
||||
assertEq(valText(text(SHARED)), true);
|
||||
assertEq(valText(text(UNSHARED)), false);
|
||||
}
|
||||
assertEq(valText(text(shared)), true);
|
||||
}
|
||||
|
||||
let text = (shared) => `(module (memory 1 1 ${shared})
|
||||
let text = (shared) => `(module (memory 1 1 ${shared})
|
||||
(func (result i32) (atomic.notify (i32.const 0) (i32.const 1)))
|
||||
(export "" (func 0)))`;
|
||||
assertEq(valText(text(SHARED)), true);
|
||||
assertEq(valText(text(UNSHARED)), false);
|
||||
assertEq(valText(text(shared)), true);
|
||||
|
||||
// Required explicit alignment for WAIT is the size of the datum
|
||||
// Required explicit alignment for WAIT is the size of the datum
|
||||
|
||||
for (let [type,align,good] of [['i32',1,false],['i32',2,false],['i32',4,true],['i32',8,false],
|
||||
['i64',1,false],['i64',2,false],['i64',4,false],['i64',8,true]])
|
||||
{
|
||||
let text = `(module (memory 1 1 shared)
|
||||
for (let [type,align,good] of [['i32',1,false],['i32',2,false],['i32',4,true],['i32',8,false],
|
||||
['i64',1,false],['i64',2,false],['i64',4,false],['i64',8,true]])
|
||||
{
|
||||
let text = `(module (memory 1 1 shared)
|
||||
(func (result i32) (${type}.atomic.wait align=${align} (i32.const 0) (${type}.const 1) (i64.const -1)))
|
||||
(export "" (func 0)))`;
|
||||
assertEq(valText(text), good);
|
||||
}
|
||||
assertEq(valText(text), good);
|
||||
}
|
||||
|
||||
// Required explicit alignment for NOTIFY is 4
|
||||
// Required explicit alignment for NOTIFY is 4
|
||||
|
||||
for (let align of [1, 2, 4, 8]) {
|
||||
let text = `(module (memory 1 1 shared)
|
||||
for (let align of [1, 2, 4, 8]) {
|
||||
let text = `(module (memory 1 1 shared)
|
||||
(func (result i32) (atomic.notify align=${align} (i32.const 0) (i32.const 1)))
|
||||
(export "" (func 0)))`;
|
||||
assertEq(valText(text), align == 4);
|
||||
}
|
||||
|
||||
function valText(text) {
|
||||
return WebAssembly.validate(wasmTextToBinary(text));
|
||||
assertEq(valText(text), align == 4);
|
||||
}
|
||||
}
|
||||
|
||||
// Test that atomic operations work.
|
||||
|
@ -163,309 +170,299 @@ function widen(TA, value, complement = true) {
|
|||
// both. Also, there may be different paths for constant addresses/operands and
|
||||
// variable ditto, so test as many combinations as possible.
|
||||
|
||||
var RMWOperation =
|
||||
{
|
||||
loadStoreModule(type, width, view, address, operand) {
|
||||
let bin = wasmTextToBinary(
|
||||
`(module
|
||||
(memory (import "" "memory") 1 1 shared)
|
||||
for ( let shared of ['shared',''] ) {
|
||||
let RMWOperation = {
|
||||
loadStoreModule(type, width, view, address, operand) {
|
||||
let bin = wasmTextToBinary(
|
||||
`(module
|
||||
(memory (import "" "memory") 1 1 ${shared})
|
||||
(func (export "st") (param i32)
|
||||
(${type}.atomic.store${width} ${address} ${operand}))
|
||||
(func $ld (param i32) (result ${type})
|
||||
(${type}.atomic.load${width}${view} ${address}))
|
||||
(func (export "ld") (param i32) (result i32)
|
||||
(${type}.eq (call $ld (local.get 0)) ${operand})))`);
|
||||
let mod = new WebAssembly.Module(bin);
|
||||
let mem = new WebAssembly.Memory({initial: 1, maximum: 1, shared: true});
|
||||
let ins = new WebAssembly.Instance(mod, {"": {memory: mem}});
|
||||
return [mem, ins.exports.ld, ins.exports.st];
|
||||
},
|
||||
let mod = new WebAssembly.Module(bin);
|
||||
let mem = new WebAssembly.Memory({initial: 1, maximum: 1, shared});
|
||||
let ins = new WebAssembly.Instance(mod, {"": {memory: mem}});
|
||||
return [mem, ins.exports.ld, ins.exports.st];
|
||||
},
|
||||
|
||||
opModuleEffect(type, width, view, address, op, operand, ignored) {
|
||||
let bin = wasmTextToBinary(
|
||||
`(module
|
||||
(memory (import "" "memory") 1 1 shared)
|
||||
opModuleEffect(type, width, view, address, op, operand, ignored) {
|
||||
let bin = wasmTextToBinary(
|
||||
`(module
|
||||
(memory (import "" "memory") 1 1 ${shared})
|
||||
(func (export "f") (param i32) (result i32)
|
||||
(drop (${type}.atomic.rmw${width}.${op}${view} ${address} ${operand}))
|
||||
(i32.const 1)))`);
|
||||
let mod = new WebAssembly.Module(bin);
|
||||
let mem = new WebAssembly.Memory({initial: 1, maximum: 1, shared: true});
|
||||
let ins = new WebAssembly.Instance(mod, {"": {memory: mem}});
|
||||
return [mem, ins.exports.f];
|
||||
},
|
||||
let mod = new WebAssembly.Module(bin);
|
||||
let mem = new WebAssembly.Memory({initial: 1, maximum: 1, shared});
|
||||
let ins = new WebAssembly.Instance(mod, {"": {memory: mem}});
|
||||
return [mem, ins.exports.f];
|
||||
},
|
||||
|
||||
opModuleReturned(type, width, view, address, op, operand, expected) {
|
||||
let bin = wasmTextToBinary(
|
||||
`(module
|
||||
(memory (import "" "memory") 1 1 shared)
|
||||
opModuleReturned(type, width, view, address, op, operand, expected) {
|
||||
let bin = wasmTextToBinary(
|
||||
`(module
|
||||
(memory (import "" "memory") 1 1 ${shared})
|
||||
(func $_f (param i32) (result ${type})
|
||||
(${type}.atomic.rmw${width}.${op}${view} ${address} ${operand}))
|
||||
(func (export "f") (param i32) (result i32)
|
||||
(${type}.eq (call $_f (local.get 0)) (${type}.const ${expected}))))`);
|
||||
let mod = new WebAssembly.Module(bin);
|
||||
let mem = new WebAssembly.Memory({initial: 1, maximum: 1, shared: true});
|
||||
let ins = new WebAssembly.Instance(mod, {"": {memory: mem}});
|
||||
return [mem, ins.exports.f];
|
||||
},
|
||||
let mod = new WebAssembly.Module(bin);
|
||||
let mem = new WebAssembly.Memory({initial: 1, maximum: 1, shared});
|
||||
let ins = new WebAssembly.Instance(mod, {"": {memory: mem}});
|
||||
return [mem, ins.exports.f];
|
||||
},
|
||||
|
||||
cmpxchgModuleEffect(type, width, view, address, operand1, operand2, ignored) {
|
||||
let bin = wasmTextToBinary(
|
||||
`(module
|
||||
(memory (import "" "memory") 1 1 shared)
|
||||
cmpxchgModuleEffect(type, width, view, address, operand1, operand2, ignored) {
|
||||
let bin = wasmTextToBinary(
|
||||
`(module
|
||||
(memory (import "" "memory") 1 1 ${shared})
|
||||
(func (export "f") (param i32)
|
||||
(drop (${type}.atomic.rmw${width}.cmpxchg${view} ${address} ${operand1} ${operand2}))))`);
|
||||
let mod = new WebAssembly.Module(bin);
|
||||
let mem = new WebAssembly.Memory({initial: 1, maximum: 1, shared: true});
|
||||
let ins = new WebAssembly.Instance(mod, {"": {memory: mem}});
|
||||
return [mem, ins.exports.f];
|
||||
},
|
||||
let mod = new WebAssembly.Module(bin);
|
||||
let mem = new WebAssembly.Memory({initial: 1, maximum: 1, shared});
|
||||
let ins = new WebAssembly.Instance(mod, {"": {memory: mem}});
|
||||
return [mem, ins.exports.f];
|
||||
},
|
||||
|
||||
cmpxchgModuleReturned(type, width, view, address, operand1, operand2, expected) {
|
||||
let bin = wasmTextToBinary(
|
||||
`(module
|
||||
(memory (import "" "memory") 1 1 shared)
|
||||
cmpxchgModuleReturned(type, width, view, address, operand1, operand2, expected) {
|
||||
let bin = wasmTextToBinary(
|
||||
`(module
|
||||
(memory (import "" "memory") 1 1 ${shared})
|
||||
(func $_f (param i32) (result ${type})
|
||||
(${type}.atomic.rmw${width}.cmpxchg${view} ${address} ${operand1} ${operand2}))
|
||||
(func (export "f") (param i32) (result i32)
|
||||
(${type}.eq (call $_f (local.get 0)) (${type}.const ${expected}))))`);
|
||||
let mod = new WebAssembly.Module(bin);
|
||||
let mem = new WebAssembly.Memory({initial: 1, maximum: 1, shared: true});
|
||||
let ins = new WebAssembly.Instance(mod, {"": {memory: mem}});
|
||||
return [mem, ins.exports.f];
|
||||
},
|
||||
let mod = new WebAssembly.Module(bin);
|
||||
let mem = new WebAssembly.Memory({initial: 1, maximum: 1, shared});
|
||||
let ins = new WebAssembly.Instance(mod, {"": {memory: mem}});
|
||||
return [mem, ins.exports.f];
|
||||
},
|
||||
|
||||
assertZero(array, LOC) {
|
||||
for ( let i=0 ; i < 100 ; i++ ) {
|
||||
if (i != LOC)
|
||||
assertNum(array.read(i), 0);
|
||||
}
|
||||
},
|
||||
assertZero(array, LOC) {
|
||||
for ( let i=0 ; i < 100 ; i++ ) {
|
||||
if (i != LOC)
|
||||
assertNum(array.read(i), 0);
|
||||
}
|
||||
},
|
||||
|
||||
run() {
|
||||
const LOC = 13; // The cell we operate on
|
||||
const OPD1 = 37; // Sometimes we'll put an operand here
|
||||
const OPD2 = 42; // Sometimes we'll put another operand here
|
||||
run() {
|
||||
const LOC = 13; // The cell we operate on
|
||||
const OPD1 = 37; // Sometimes we'll put an operand here
|
||||
const OPD2 = 42; // Sometimes we'll put another operand here
|
||||
|
||||
for ( let [type, variations] of
|
||||
for ( let [type, variations] of
|
||||
[["i32", [[Uint8Array,"8", "_u"], [Uint16Array,"16", "_u"], [Uint32Array,"",""]]],
|
||||
["i64", [[Uint8Array,"8","_u"], [Uint16Array,"16","_u"], [Uint32Array,"32","_u"], [Uint64Array,"",""]]]] )
|
||||
{
|
||||
for ( let [TA, width, view] of variations )
|
||||
{
|
||||
for ( let addr of [`(i32.const ${LOC * TA.BYTES_PER_ELEMENT})`,
|
||||
`(local.get 0)`] )
|
||||
{
|
||||
for ( let [initial, operand] of [[0x12, 0x37]] )
|
||||
for ( let [TA, width, view] of variations )
|
||||
{
|
||||
for ( let addr of [`(i32.const ${LOC * TA.BYTES_PER_ELEMENT})`,
|
||||
`(local.get 0)`] )
|
||||
{
|
||||
let [opd_str, opd_num] = widen(TA, operand);
|
||||
for ( let rhs of [`(${type}.const ${opd_str})`,
|
||||
`(${type}.load${width}${view} (i32.const ${OPD1 * TA.BYTES_PER_ELEMENT}))`] )
|
||||
{
|
||||
let [mem, ld, st] = this.loadStoreModule(type, width, view, addr, rhs);
|
||||
let array = new TA(mem.buffer);
|
||||
array.write(OPD1, opd_num);
|
||||
array.write(LOC, initial);
|
||||
st(LOC * TA.BYTES_PER_ELEMENT);
|
||||
let res = ld(LOC * TA.BYTES_PER_ELEMENT);
|
||||
assertEq(res, 1);
|
||||
assertNum(array.read(LOC), opd_num);
|
||||
array.write(OPD1, 0);
|
||||
this.assertZero(array, LOC);
|
||||
}
|
||||
}
|
||||
|
||||
for ( let [op, initial, operand, expected] of [["add", 37, 5, 42],
|
||||
["sub", 42, 5, 37],
|
||||
["and", 0x45, 0x13, 0x01],
|
||||
["or", 0x45, 0x13, 0x57],
|
||||
["xor", 0x45, 0x13, 0x56],
|
||||
["xchg", 0x45, 0x13, 0x13]] )
|
||||
{
|
||||
let complement = op == "xchg";
|
||||
let [ini_str, ini_num] = widen(TA, initial, complement);
|
||||
let [opd_str, opd_num] = widen(TA, operand, complement);
|
||||
let [exp_str, exp_num] = widen(TA, expected, complement);
|
||||
for ( let rhs of [`(${type}.const ${opd_str})`,
|
||||
`(${type}.load${width}${view} (i32.const ${OPD1 * TA.BYTES_PER_ELEMENT}))`] )
|
||||
{
|
||||
for ( let [generateIt, checkIt] of [["opModuleEffect", false], ["opModuleReturned", true]] )
|
||||
for ( let [initial, operand] of [[0x12, 0x37]] )
|
||||
{
|
||||
let [opd_str, opd_num] = widen(TA, operand);
|
||||
for ( let rhs of [`(${type}.const ${opd_str})`,
|
||||
`(${type}.load${width}${view} (i32.const ${OPD1 * TA.BYTES_PER_ELEMENT}))`] )
|
||||
{
|
||||
let [mem, f] = this[generateIt](type, width, view, addr, op, rhs, ini_str);
|
||||
let array = new TA(mem.buffer);
|
||||
array.write(OPD1, opd_num);
|
||||
array.write(LOC, ini_num);
|
||||
let res = f(LOC * TA.BYTES_PER_ELEMENT);
|
||||
if (checkIt)
|
||||
assertEq(res, 1);
|
||||
assertNum(array.read(LOC), exp_num);
|
||||
array.write(OPD1, 0);
|
||||
this.assertZero(array, LOC);
|
||||
let [mem, ld, st] = this.loadStoreModule(type, width, view, addr, rhs);
|
||||
let array = new TA(mem.buffer);
|
||||
array.write(OPD1, opd_num);
|
||||
array.write(LOC, initial);
|
||||
st(LOC * TA.BYTES_PER_ELEMENT);
|
||||
let res = ld(LOC * TA.BYTES_PER_ELEMENT);
|
||||
assertEq(res, 1);
|
||||
assertNum(array.read(LOC), opd_num);
|
||||
array.write(OPD1, 0);
|
||||
this.assertZero(array, LOC);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for ( let [initial, operand1, operand2, expected] of [[33, 33, 44, 44], [33, 44, 55, 33]] )
|
||||
{
|
||||
let [ini_str, ini_num] = widen(TA, initial);
|
||||
let [opd1_str, opd1_num] = widen(TA, operand1);
|
||||
let [opd2_str, opd2_num] = widen(TA, operand2);
|
||||
let [exp_str, exp_num] = widen(TA, expected);
|
||||
for ( let op1 of [`(${type}.const ${opd1_str})`,
|
||||
`(${type}.load${width}${view} (i32.const ${OPD1 * TA.BYTES_PER_ELEMENT}))`] )
|
||||
{
|
||||
for ( let op2 of [`(${type}.const ${opd2_str})`,
|
||||
`(${type}.load${width}${view} (i32.const ${OPD2 * TA.BYTES_PER_ELEMENT}))`] )
|
||||
for ( let [op, initial, operand, expected] of [["add", 37, 5, 42],
|
||||
["sub", 42, 5, 37],
|
||||
["and", 0x45, 0x13, 0x01],
|
||||
["or", 0x45, 0x13, 0x57],
|
||||
["xor", 0x45, 0x13, 0x56],
|
||||
["xchg", 0x45, 0x13, 0x13]] )
|
||||
{
|
||||
let complement = op == "xchg";
|
||||
let [ini_str, ini_num] = widen(TA, initial, complement);
|
||||
let [opd_str, opd_num] = widen(TA, operand, complement);
|
||||
let [exp_str, exp_num] = widen(TA, expected, complement);
|
||||
for ( let rhs of [`(${type}.const ${opd_str})`,
|
||||
`(${type}.load${width}${view} (i32.const ${OPD1 * TA.BYTES_PER_ELEMENT}))`] )
|
||||
{
|
||||
for ( let [generateIt, checkIt] of [["cmpxchgModuleEffect", false], ["cmpxchgModuleReturned", true]] )
|
||||
{
|
||||
let [mem, f] = this[generateIt](type, width, view, addr, op1, op2, ini_str);
|
||||
for ( let [generateIt, checkIt] of [["opModuleEffect", false], ["opModuleReturned", true]] )
|
||||
{
|
||||
let [mem, f] = this[generateIt](type, width, view, addr, op, rhs, ini_str);
|
||||
let array = new TA(mem.buffer);
|
||||
array.write(OPD1, opd1_num);
|
||||
array.write(OPD2, opd2_num);
|
||||
array.write(OPD1, opd_num);
|
||||
array.write(LOC, ini_num);
|
||||
let res = f(LOC * TA.BYTES_PER_ELEMENT);
|
||||
if (checkIt)
|
||||
assertEq(res, 1);
|
||||
assertNum(array.read(13), exp_num);
|
||||
assertEq(res, 1);
|
||||
assertNum(array.read(LOC), exp_num);
|
||||
array.write(OPD1, 0);
|
||||
array.write(OPD2, 0);
|
||||
this.assertZero(array, LOC);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for ( let [initial, operand1, operand2, expected] of [[33, 33, 44, 44], [33, 44, 55, 33]] )
|
||||
{
|
||||
let [ini_str, ini_num] = widen(TA, initial);
|
||||
let [opd1_str, opd1_num] = widen(TA, operand1);
|
||||
let [opd2_str, opd2_num] = widen(TA, operand2);
|
||||
let [exp_str, exp_num] = widen(TA, expected);
|
||||
for ( let op1 of [`(${type}.const ${opd1_str})`,
|
||||
`(${type}.load${width}${view} (i32.const ${OPD1 * TA.BYTES_PER_ELEMENT}))`] )
|
||||
{
|
||||
for ( let op2 of [`(${type}.const ${opd2_str})`,
|
||||
`(${type}.load${width}${view} (i32.const ${OPD2 * TA.BYTES_PER_ELEMENT}))`] )
|
||||
{
|
||||
for ( let [generateIt, checkIt] of [["cmpxchgModuleEffect", false], ["cmpxchgModuleReturned", true]] )
|
||||
{
|
||||
let [mem, f] = this[generateIt](type, width, view, addr, op1, op2, ini_str);
|
||||
let array = new TA(mem.buffer);
|
||||
array.write(OPD1, opd1_num);
|
||||
array.write(OPD2, opd2_num);
|
||||
array.write(LOC, ini_num);
|
||||
let res = f(LOC * TA.BYTES_PER_ELEMENT);
|
||||
if (checkIt)
|
||||
assertEq(res, 1);
|
||||
assertNum(array.read(13), exp_num);
|
||||
array.write(OPD1, 0);
|
||||
array.write(OPD2, 0);
|
||||
this.assertZero(array, LOC);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
RMWOperation.run();
|
||||
|
||||
function assertNum(a, b) {
|
||||
if (typeof a == "number" && typeof b == "number")
|
||||
assertEq(a, b);
|
||||
else if (typeof a == "number") {
|
||||
assertEq(a, b.low);
|
||||
assertEq(0, b.high);
|
||||
} else if (typeof b == "number") {
|
||||
assertEq(a.low, b);
|
||||
assertEq(a.high, 0);
|
||||
} else {
|
||||
assertEq(a.high, b.high);
|
||||
assertEq(a.low, b.low);
|
||||
}
|
||||
RMWOperation.run();
|
||||
}
|
||||
|
||||
// Test bounds and alignment checking on atomic ops
|
||||
|
||||
var BoundsAndAlignment =
|
||||
{
|
||||
loadModule(type, view, width, offset) {
|
||||
return wasmEvalText(
|
||||
`(module
|
||||
(memory 1 1 shared)
|
||||
for ( let shared of ['shared',''] ) {
|
||||
var BoundsAndAlignment = {
|
||||
loadModule(type, view, width, offset) {
|
||||
return wasmEvalText(
|
||||
`(module
|
||||
(memory 1 1 ${shared})
|
||||
(func $0 (param i32) (result ${type})
|
||||
(${type}.atomic.load${width}${view} offset=${offset} (local.get 0)))
|
||||
(func (export "f") (param i32)
|
||||
(drop (call $0 (local.get 0)))))
|
||||
`).exports.f;
|
||||
},
|
||||
},
|
||||
|
||||
loadModuleIgnored(type, view, width, offset) {
|
||||
return wasmEvalText(
|
||||
`(module
|
||||
(memory 1 1 shared)
|
||||
loadModuleIgnored(type, view, width, offset) {
|
||||
return wasmEvalText(
|
||||
`(module
|
||||
(memory 1 1 ${shared})
|
||||
(func (export "f") (param i32)
|
||||
(drop (${type}.atomic.load${width}${view} offset=${offset} (local.get 0)))))
|
||||
`).exports.f;
|
||||
},
|
||||
},
|
||||
|
||||
storeModule(type, view, width, offset) {
|
||||
return wasmEvalText(
|
||||
`(module
|
||||
(memory 1 1 shared)
|
||||
storeModule(type, view, width, offset) {
|
||||
return wasmEvalText(
|
||||
`(module
|
||||
(memory 1 1 ${shared})
|
||||
(func (export "f") (param i32)
|
||||
(${type}.atomic.store${width} offset=${offset} (local.get 0) (${type}.const 37))))
|
||||
`).exports.f;
|
||||
},
|
||||
},
|
||||
|
||||
opModule(type, view, width, offset, op) {
|
||||
return wasmEvalText(
|
||||
`(module
|
||||
(memory 1 1 shared)
|
||||
opModule(type, view, width, offset, op) {
|
||||
return wasmEvalText(
|
||||
`(module
|
||||
(memory 1 1 ${shared})
|
||||
(func $0 (param i32) (result ${type})
|
||||
(${type}.atomic.rmw${width}.${op}${view} offset=${offset} (local.get 0) (${type}.const 37)))
|
||||
(func (export "f") (param i32)
|
||||
(drop (call $0 (local.get 0)))))
|
||||
`).exports.f;
|
||||
},
|
||||
},
|
||||
|
||||
opModuleForEffect(type, view, width, offset, op) {
|
||||
return wasmEvalText(
|
||||
`(module
|
||||
(memory 1 1 shared)
|
||||
opModuleForEffect(type, view, width, offset, op) {
|
||||
return wasmEvalText(
|
||||
`(module
|
||||
(memory 1 1 ${shared})
|
||||
(func (export "f") (param i32)
|
||||
(drop (${type}.atomic.rmw${width}.${op}${view} offset=${offset} (local.get 0) (${type}.const 37)))))
|
||||
`).exports.f;
|
||||
},
|
||||
},
|
||||
|
||||
cmpxchgModule(type, view, width, offset) {
|
||||
return wasmEvalText(
|
||||
`(module
|
||||
(memory 1 1 shared)
|
||||
cmpxchgModule(type, view, width, offset) {
|
||||
return wasmEvalText(
|
||||
`(module
|
||||
(memory 1 1 ${shared})
|
||||
(func $0 (param i32) (result ${type})
|
||||
(${type}.atomic.rmw${width}.cmpxchg${view} offset=${offset} (local.get 0) (${type}.const 37) (${type}.const 42)))
|
||||
(func (export "f") (param i32)
|
||||
(drop (call $0 (local.get 0)))))
|
||||
`).exports.f;
|
||||
},
|
||||
},
|
||||
|
||||
run() {
|
||||
for ( let [type, variations] of [["i32", [["8","_u", 1], ["16","_u", 2], ["","", 4]]],
|
||||
["i64", [["8","_u",1], ["16","_u",2], ["32","_u",4], ["","",8]]]] )
|
||||
{
|
||||
for ( let [width,view,size] of variations )
|
||||
run() {
|
||||
for ( let [type, variations] of [["i32", [["8","_u", 1], ["16","_u", 2], ["","", 4]]],
|
||||
["i64", [["8","_u",1], ["16","_u",2], ["32","_u",4], ["","",8]]]] )
|
||||
{
|
||||
// Aligned but out-of-bounds
|
||||
let addrs = [[65536, 0, oob], [65536*2, 0, oob], [65532, 4, oob],
|
||||
[65533, 3, oob], [65534, 2, oob], [65535, 1, oob]];
|
||||
if (type == "i64")
|
||||
addrs.push([65536-8, 8, oob]);
|
||||
for ( let [width,view,size] of variations )
|
||||
{
|
||||
// Aligned but out-of-bounds
|
||||
let addrs = [[65536, 0, oob], [65536*2, 0, oob], [65532, 4, oob],
|
||||
[65533, 3, oob], [65534, 2, oob], [65535, 1, oob]];
|
||||
if (type == "i64")
|
||||
addrs.push([65536-8, 8, oob]);
|
||||
|
||||
// In-bounds but unaligned
|
||||
for ( let i=1 ; i < size ; i++ )
|
||||
addrs.push([65520, i, unaligned]);
|
||||
// In-bounds but unaligned
|
||||
for ( let i=1 ; i < size ; i++ )
|
||||
addrs.push([65520, i, unaligned]);
|
||||
|
||||
// Both out-of-bounds and unaligned. The spec leaves it unspecified
|
||||
// whether we see the OOB message or the unaligned message (they are
|
||||
// both "traps"). In Firefox, the unaligned check comes first.
|
||||
for ( let i=1 ; i < size ; i++ )
|
||||
addrs.push([65536, i, unaligned]);
|
||||
// Both out-of-bounds and unaligned. The spec leaves it unspecified
|
||||
// whether we see the OOB message or the unaligned message (they are
|
||||
// both "traps"). In Firefox, the unaligned check comes first.
|
||||
for ( let i=1 ; i < size ; i++ )
|
||||
addrs.push([65536, i, unaligned]);
|
||||
|
||||
// GC to prevent TSan builds from running out of memory.
|
||||
gc();
|
||||
// GC to prevent TSan builds from running out of memory.
|
||||
gc();
|
||||
|
||||
for ( let [ base, offset, re ] of addrs )
|
||||
{
|
||||
assertErrorMessage(() => this.loadModule(type, view, width, offset)(base), RuntimeError, re);
|
||||
assertErrorMessage(() => this.loadModuleIgnored(type, view, width, offset)(base), RuntimeError, re);
|
||||
assertErrorMessage(() => this.storeModule(type, view, width, offset)(base), RuntimeError, re);
|
||||
for ( let op of [ "add", "sub", "and", "or", "xor", "xchg" ]) {
|
||||
assertErrorMessage(() => this.opModule(type, view, width, offset, op)(base), RuntimeError, re);
|
||||
assertErrorMessage(() => this.opModuleForEffect(type, view, width, offset, op)(base), RuntimeError, re);
|
||||
for ( let [ base, offset, re ] of addrs )
|
||||
{
|
||||
assertErrorMessage(() => this.loadModule(type, view, width, offset)(base), RuntimeError, re);
|
||||
assertErrorMessage(() => this.loadModuleIgnored(type, view, width, offset)(base), RuntimeError, re);
|
||||
assertErrorMessage(() => this.storeModule(type, view, width, offset)(base), RuntimeError, re);
|
||||
for ( let op of [ "add", "sub", "and", "or", "xor", "xchg" ]) {
|
||||
assertErrorMessage(() => this.opModule(type, view, width, offset, op)(base), RuntimeError, re);
|
||||
assertErrorMessage(() => this.opModuleForEffect(type, view, width, offset, op)(base), RuntimeError, re);
|
||||
}
|
||||
assertErrorMessage(() => this.cmpxchgModule(type, view, width, offset)(base), RuntimeError, re);
|
||||
}
|
||||
assertErrorMessage(() => this.cmpxchgModule(type, view, width, offset)(base), RuntimeError, re);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BoundsAndAlignment.run();
|
||||
BoundsAndAlignment.run();
|
||||
}
|
||||
|
||||
// Bounds and alignment checks on wait and notify
|
||||
|
||||
// For 'wait', we check bounds and alignment after sharedness, so the memory
|
||||
// must be shared always.
|
||||
|
||||
assertErrorMessage(() => wasmEvalText(`(module (memory 1 1 shared)
|
||||
(func (param i32) (result i32)
|
||||
(i32.atomic.wait (local.get 0) (i32.const 1) (i64.const -1)))
|
||||
|
@ -490,20 +487,41 @@ assertErrorMessage(() => wasmEvalText(`(module (memory 1 1 shared)
|
|||
(export "" (func 0)))`).exports[""](65501),
|
||||
RuntimeError, unaligned);
|
||||
|
||||
assertErrorMessage(() => wasmEvalText(`(module (memory 1 1 shared)
|
||||
// For 'notify', we check bounds and alignment before returning 0 in the case of
|
||||
// non-shared memory, so both shared and non-shared memories must be checked.
|
||||
|
||||
for ( let shared of ['shared',''] ) {
|
||||
assertErrorMessage(() => wasmEvalText(`(module (memory 1 1 ${shared})
|
||||
(func (param i32) (result i32)
|
||||
(atomic.notify (local.get 0) (i32.const 1)))
|
||||
(export "" (func 0)))`).exports[""](65536),
|
||||
RuntimeError, oob);
|
||||
|
||||
// Minimum run-time alignment for NOTIFY is 4
|
||||
for (let addr of [1,2,3,5,6,7]) {
|
||||
assertErrorMessage(() => wasmEvalText(`(module (memory 1 1 shared)
|
||||
// Minimum run-time alignment for NOTIFY is 4
|
||||
for (let addr of [1,2,3,5,6,7]) {
|
||||
assertErrorMessage(() => wasmEvalText(`(module (memory 1 1 ${shared})
|
||||
(func (export "f") (param i32) (result i32)
|
||||
(atomic.notify (local.get 0) (i32.const 1))))`).exports.f(addr),
|
||||
RuntimeError, unaligned);
|
||||
RuntimeError, unaligned);
|
||||
}
|
||||
}
|
||||
|
||||
// Sharedness check for wait
|
||||
|
||||
assertErrorMessage(() => wasmEvalText(`(module (memory 1 1)
|
||||
(func (param i32) (result i32)
|
||||
(i32.atomic.wait (local.get 0) (i32.const 1) (i64.const -1)))
|
||||
(export "" (func 0)))`).exports[""](0),
|
||||
RuntimeError, /atomic wait on non-shared memory/);
|
||||
|
||||
// Ensure that notify works on non-shared memories and returns zero.
|
||||
|
||||
assertEq(wasmEvalText(`
|
||||
(module (memory 1 1)
|
||||
(func (export "f") (param i32) (result i32)
|
||||
(atomic.notify (local.get 0) (i32.const 1))))
|
||||
`).exports.f(256), 0);
|
||||
|
||||
// Ensure alias analysis works even if atomic and non-atomic accesses are
|
||||
// mixed.
|
||||
assertErrorMessage(() => wasmEvalText(`(module
|
||||
|
|
Загрузка…
Ссылка в новой задаче