// |jit-test| skip-if: !largeArrayBufferSupported(); allow-oom // Test i32 index register is zero-extended. const rmw_ops = { add: (x, y) => (x + y), sub: (x, y) => (x - y), and: (x, y) => (x & y), or: (x, y) => (x | y), xor: (x, y) => (x ^ y), xchg: (x, y) => y, }; function rmw_name(type, name, width) { let prefix = width < type ? String(width) : ""; let suffix = width < type ? "_u" : ""; return `i${type}.atomic.rmw${prefix}.${name}${suffix}`; } let rmw_functions = []; for (let name of Object.keys(rmw_ops)) { for (let type of [64, 32]) { for (let width = type; width >= 8; width /= 2) { let op_name = rmw_name(type, name, width); rmw_functions.push(` (func (export "${op_name}") (param i32) (param i${type}) (result i${type}) (${op_name} (local.get 0) (local.get 1)) ) (func (export "${op_name}.effect") (param i32) (param i${type}) (${op_name} (local.get 0) (local.get 1)) drop ) `); } } } var ins = wasmEvalText(` (module ;; Larger than 2GB (memory (export "mem") 40000) ;; Load (i32) (func (export "i32.atomic.load") (param i32) (result i32) (i32.atomic.load (local.get 0)) ) (func (export "i32.atomic.load8_u") (param i32) (result i32) (i32.atomic.load8_u (local.get 0)) ) (func (export "i32.atomic.load16_u") (param i32) (result i32) (i32.atomic.load16_u (local.get 0)) ) ;; Load (i64) (func (export "i64.atomic.load") (param i32) (result i64) (i64.atomic.load (local.get 0)) ) (func (export "i64.atomic.load8_u") (param i32) (result i64) (i64.atomic.load8_u (local.get 0)) ) (func (export "i64.atomic.load16_u") (param i32) (result i64) (i64.atomic.load16_u (local.get 0)) ) (func (export "i64.atomic.load32_u") (param i32) (result i64) (i64.atomic.load32_u (local.get 0)) ) ;; Store (i32) (func (export "i32.atomic.store") (param i32) (param i32) (i32.atomic.store (local.get 0) (local.get 1)) ) (func (export "i32.atomic.store8") (param i32) (param i32) (i32.atomic.store8 (local.get 0) (local.get 1)) ) (func (export "i32.atomic.store16") (param i32) (param i32) (i32.atomic.store16 (local.get 0) (local.get 1)) ) ;; Store (i64) (func (export "i64.atomic.store") (param i32) (param i64) (i64.atomic.store (local.get 0) (local.get 1)) ) (func (export "i64.atomic.store8") (param i32) (param i64) (i64.atomic.store8 (local.get 0) (local.get 1)) ) (func (export "i64.atomic.store16") (param i32) (param i64) (i64.atomic.store16 (local.get 0) (local.get 1)) ) (func (export "i64.atomic.store32") (param i32) (param i64) (i64.atomic.store32 (local.get 0) (local.get 1)) ) ;; CompareExchange (i32) (func (export "i32.atomic.rmw.cmpxchg") (param i32) (param i32) (param i32) (result i32) (i32.atomic.rmw.cmpxchg (local.get 0) (local.get 1) (local.get 2)) ) (func (export "i32.atomic.rmw8.cmpxchg_u") (param i32) (param i32) (param i32) (result i32) (i32.atomic.rmw8.cmpxchg_u (local.get 0) (local.get 1) (local.get 2)) ) (func (export "i32.atomic.rmw16.cmpxchg_u") (param i32) (param i32) (param i32) (result i32) (i32.atomic.rmw16.cmpxchg_u (local.get 0) (local.get 1) (local.get 2)) ) ;; CompareExchange (i64) (func (export "i64.atomic.rmw.cmpxchg") (param i32) (param i64) (param i64) (result i64) (i64.atomic.rmw.cmpxchg (local.get 0) (local.get 1) (local.get 2)) ) (func (export "i64.atomic.rmw8.cmpxchg_u") (param i32) (param i64) (param i64) (result i64) (i64.atomic.rmw8.cmpxchg_u (local.get 0) (local.get 1) (local.get 2)) ) (func (export "i64.atomic.rmw16.cmpxchg_u") (param i32) (param i64) (param i64) (result i64) (i64.atomic.rmw16.cmpxchg_u (local.get 0) (local.get 1) (local.get 2)) ) (func (export "i64.atomic.rmw32.cmpxchg_u") (param i32) (param i64) (param i64) (result i64) (i64.atomic.rmw32.cmpxchg_u (local.get 0) (local.get 1) (local.get 2)) ) ;; RMW ${rmw_functions.join("\n")} )`); var ab = ins.exports.mem.buffer; var dv = new DataView(ab); var index = 0x80000000; assertEq(ab.byteLength > index, true); // Load (i32) dv.setInt32(index, 0, true); assertEq(ins.exports["i32.atomic.load"](index), 0); assertEq(ins.exports["i32.atomic.load8_u"](index), 0); assertEq(ins.exports["i32.atomic.load16_u"](index), 0); dv.setInt32(index, 0xaabbccdd, true); assertEq(ins.exports["i32.atomic.load"](index), 0xaabbccdd|0); assertEq(ins.exports["i32.atomic.load8_u"](index), 0xdd); assertEq(ins.exports["i32.atomic.load16_u"](index), 0xccdd); // Load (i64) dv.setBigInt64(index, 0n, true); assertEq(ins.exports["i64.atomic.load"](index), 0n); assertEq(ins.exports["i64.atomic.load8_u"](index), 0n); assertEq(ins.exports["i64.atomic.load16_u"](index), 0n); assertEq(ins.exports["i64.atomic.load32_u"](index), 0n); dv.setBigInt64(index, 0xaabbccdd_eeff1122n, true); assertEq(ins.exports["i64.atomic.load"](index), BigInt.asIntN(64, 0xaabbccdd_eeff1122n)); assertEq(ins.exports["i64.atomic.load8_u"](index), 0x22n); assertEq(ins.exports["i64.atomic.load16_u"](index), 0x1122n); assertEq(ins.exports["i64.atomic.load32_u"](index), 0xeeff1122n); // Store (i32) dv.setInt32(index, 0, true); ins.exports["i32.atomic.store"](index, 0xaabbccdd); assertEq(dv.getInt32(index, true), 0xaabbccdd|0); dv.setInt32(index, 0, true); ins.exports["i32.atomic.store8"](index, 0xaabbccdd); assertEq(dv.getInt32(index, true), 0xdd); dv.setInt32(index, 0, true); ins.exports["i32.atomic.store16"](index, 0xaabbccdd); assertEq(dv.getInt32(index, true), 0xccdd); // Store (i64) dv.setBigInt64(index, 0n, true); ins.exports["i64.atomic.store"](index, 0xaabbccdd_eeff1122n); assertEq(dv.getBigInt64(index, true), BigInt.asIntN(64, 0xaabbccdd_eeff1122n)); dv.setBigInt64(index, 0n, true); ins.exports["i64.atomic.store8"](index, 0xaabbccdd_eeff1122n); assertEq(dv.getBigInt64(index, true), 0x22n); dv.setBigInt64(index, 0n, true); ins.exports["i64.atomic.store16"](index, 0xaabbccdd_eeff1122n); assertEq(dv.getBigInt64(index, true), 0x1122n); dv.setBigInt64(index, 0n, true); ins.exports["i64.atomic.store32"](index, 0xaabbccdd_eeff1122n); assertEq(dv.getBigInt64(index, true), 0xeeff1122n); // CompareExchange (i32) dv.setInt32(index, 0x0f0f0f0f, true); assertEq(ins.exports["i32.atomic.rmw.cmpxchg"](index, 0, 0xaabbccdd), 0x0f0f0f0f); assertEq(dv.getInt32(index, true), 0x0f0f0f0f); assertEq(ins.exports["i32.atomic.rmw.cmpxchg"](index, 0x0f0f0f0f, 0xaabbccdd), 0x0f0f0f0f); assertEq(dv.getInt32(index, true), 0xaabbccdd|0); dv.setInt32(index, 0x0f0f0f0f, true); assertEq(ins.exports["i32.atomic.rmw8.cmpxchg_u"](index, 0, 0xaabbccdd), 0x0f); assertEq(dv.getInt32(index, true), 0x0f0f0f0f); assertEq(ins.exports["i32.atomic.rmw8.cmpxchg_u"](index, 0x0f0f0f0f, 0xaabbccdd), 0x0f); assertEq(dv.getInt32(index, true), 0x0f0f0fdd); dv.setInt32(index, 0x0f0f0f0f, true); assertEq(ins.exports["i32.atomic.rmw16.cmpxchg_u"](index, 0, 0xaabbccdd), 0x0f0f); assertEq(dv.getInt32(index, true), 0x0f0f0f0f); assertEq(ins.exports["i32.atomic.rmw16.cmpxchg_u"](index, 0x0f0f0f0f, 0xaabbccdd), 0x0f0f); assertEq(dv.getInt32(index, true), 0x0f0fccdd); // CompareExchange (i64) dv.setBigInt64(index, 0x0f0f0f0f_0f0f0f0fn, true); assertEq(ins.exports["i64.atomic.rmw.cmpxchg"](index, 0n, 0xaabbccdd_eeff1122n), 0x0f0f0f0f_0f0f0f0fn); assertEq(dv.getBigInt64(index, true), 0x0f0f0f0f_0f0f0f0fn); assertEq(ins.exports["i64.atomic.rmw.cmpxchg"](index, 0x0f0f0f0f_0f0f0f0fn, 0xaabbccdd_eeff1122n), 0x0f0f0f0f_0f0f0f0fn); assertEq(dv.getBigInt64(index, true), BigInt.asIntN(64, 0xaabbccdd_eeff1122n)); dv.setBigInt64(index, 0x0f0f0f0f_0f0f0f0fn, true); assertEq(ins.exports["i64.atomic.rmw8.cmpxchg_u"](index, 0n, 0xaabbccdd_eeff1122n), 0x0fn); assertEq(dv.getBigInt64(index, true), 0x0f0f0f0f_0f0f0f0fn); assertEq(ins.exports["i64.atomic.rmw8.cmpxchg_u"](index, 0x0f0f0f0f_0f0f0f0fn, 0xaabbccdd_eeff1122n), 0x0fn); assertEq(dv.getBigInt64(index, true), 0x0f0f0f0f_0f0f0f22n); dv.setBigInt64(index, 0x0f0f0f0f_0f0f0f0fn, true); assertEq(ins.exports["i64.atomic.rmw16.cmpxchg_u"](index, 0n, 0xaabbccdd_eeff1122n), 0x0f0fn); assertEq(dv.getBigInt64(index, true), 0x0f0f0f0f_0f0f0f0fn); assertEq(ins.exports["i64.atomic.rmw16.cmpxchg_u"](index, 0x0f0f0f0f_0f0f0f0fn, 0xaabbccdd_eeff1122n), 0x0f0fn); assertEq(dv.getBigInt64(index, true), 0x0f0f0f0f_0f0f1122n); dv.setBigInt64(index, 0x0f0f0f0f_0f0f0f0fn, true); assertEq(ins.exports["i64.atomic.rmw32.cmpxchg_u"](index, 0n, 0xaabbccdd_eeff1122n), 0x0f0f0f0fn); assertEq(dv.getBigInt64(index, true), 0x0f0f0f0f_0f0f0f0fn); assertEq(ins.exports["i64.atomic.rmw32.cmpxchg_u"](index, 0x0f0f0f0f_0f0f0f0fn, 0xaabbccdd_eeff1122n), 0x0f0f0f0fn); assertEq(dv.getBigInt64(index, true), 0x0f0f0f0f_eeff1122n); assertEq(ins.exports["i64.atomic.load"](index), 0x0f0f0f0f_eeff1122n); // RMW for (let [name, op] of Object.entries(rmw_ops)) { for (let width of [32, 16, 8]) { let mask = (2 ** width) - 1; let init = 0x0f0f0f0f; let value = 0xaabbccdd; let expected = (op(init, value) & mask) | (init & ~mask); let exp_name = rmw_name(32, name, width); dv.setInt32(index, init, true); assertEq(ins.exports[exp_name](index, value), init & mask); assertEq(dv.getInt32(index, true), expected); // for effect dv.setInt32(index, init, true); assertEq(ins.exports[`${exp_name}.effect`](index, value), undefined); assertEq(dv.getInt32(index, true), expected); } for (let width of [64, 32, 16, 8]) { let mask = (2n ** BigInt(width)) - 1n; let init = 0x0f0f0f0f_0f0f0f0fn; let value = 0xaabbccdd_eeff1122n; let expected = BigInt.asIntN(64, (op(init, value) & mask) | (init & ~mask)); let exp_name = rmw_name(64, name, width); dv.setBigInt64(index, init, true); assertEq(ins.exports[exp_name](index, value), init & mask); assertEq(dv.getBigInt64(index, true), expected); // for effect dv.setBigInt64(index, init, true); assertEq(ins.exports[`${exp_name}.effect`](index, value), undefined); assertEq(dv.getBigInt64(index, true), expected); } }