// Copyright 2017 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 kMemtypeSize32 = 4; const kMemtypeSize16 = 2; const kMemtypeSize8 = 1; function Add(a, b) { return a + b; } function Sub(a, b) { return a - b; } function And(a, b) { return a & b; } function Or(a, b) { return a | b; } function Xor(a, b) { return a ^ b; } function Exchange(a, b) { return b; } let maxSize = 10; let memory = new WebAssembly.Memory({initial: 1, maximum: maxSize, shared: true}); function GetAtomicBinOpFunction(wasmExpression, alignment, offset) { let builder = new WasmModuleBuilder(); builder.addImportedMemory("m", "imported_mem", 0, maxSize, "shared"); builder.addFunction("main", kSig_i_ii) .addBody([ kExprGetLocal, 0, kExprGetLocal, 1, kAtomicPrefix, wasmExpression, alignment, offset]) .exportAs("main"); // Instantiate module, get function exports let module = new WebAssembly.Module(builder.toBuffer()); let instance = new WebAssembly.Instance(module, {m: {imported_mem: memory}}); return instance.exports.main; } function GetAtomicCmpExchangeFunction(wasmExpression, alignment, offset) { let builder = new WasmModuleBuilder(); builder.addImportedMemory("m", "imported_mem", 0, maxSize, "shared"); builder.addFunction("main", kSig_i_iii) .addBody([ kExprGetLocal, 0, kExprGetLocal, 1, kExprGetLocal, 2, kAtomicPrefix, wasmExpression, alignment, offset]) .exportAs("main"); // Instantiate module, get function exports let module = new WebAssembly.Module(builder.toBuffer()); let instance = new WebAssembly.Instance(module, {m: {imported_mem: memory}}); return instance.exports.main; } function GetAtomicLoadFunction(wasmExpression, alignment, offset) { let builder = new WasmModuleBuilder(); builder.addImportedMemory("m", "imported_mem", 0, maxSize, "shared"); builder.addFunction("main", kSig_i_i) .addBody([ kExprGetLocal, 0, kAtomicPrefix, wasmExpression, alignment, offset]) .exportAs("main"); // Instantiate module, get function exports let module = new WebAssembly.Module(builder.toBuffer()); let instance = new WebAssembly.Instance(module, {m: {imported_mem: memory}}); return instance.exports.main; } function GetAtomicStoreFunction(wasmExpression, alignment, offset) { let builder = new WasmModuleBuilder(); builder.addImportedMemory("m", "imported_mem", 0, maxSize, "shared"); builder.addFunction("main", kSig_v_ii) .addBody([ kExprGetLocal, 0, kExprGetLocal, 1, kAtomicPrefix, wasmExpression, alignment, offset]) .exportAs("main"); // Instantiate module, get function exports let module = new WebAssembly.Module(builder.toBuffer()); let instance = new WebAssembly.Instance(module, {m: {imported_mem: memory}}); return instance.exports.main; } function VerifyBoundsCheck(func, memtype_size) { const kPageSize = 65536; // Test out of bounds at boundary for (let i = memory.buffer.byteLength - memtype_size + 1; i < memory.buffer.byteLength + memtype_size + 4; i++) { assertTraps(kTrapMemOutOfBounds, () => func(i, 5, 10)); } // Test out of bounds at maximum + 1 assertTraps(kTrapMemOutOfBounds, () => func((maxSize + 1) * kPageSize, 5, 1)); } // Test many elements in the small range, make bigger steps later. This is still // O(2^n), but takes 213 steps to reach 2^32. const inc = i => i + Math.floor(i/10) + 1; function Test32Op(operation, func) { let i32 = new Uint32Array(memory.buffer); for (let i = 0; i < i32.length; i = inc(i)) { let expected = 0x9cedf00d; let value = 0x11111111; i32[i] = expected; assertEquals(expected, func(i * kMemtypeSize32, value) >>> 0); assertEquals(operation(expected, value) >>> 0, i32[i]); } VerifyBoundsCheck(func, kMemtypeSize32); } function Test16Op(operation, func) { let i16 = new Uint16Array(memory.buffer); for (let i = 0; i < i16.length; i = inc(i)) { let expected = 0xd00d; let value = 0x1111; i16[i] = expected; assertEquals(expected, func(i * kMemtypeSize16, value)); assertEquals(operation(expected, value), i16[i]); } VerifyBoundsCheck(func, kMemtypeSize16); } function Test8Op(operation, func) { let i8 = new Uint8Array(memory.buffer); for (let i = 0; i < i8.length; i = inc(i)) { let expected = 0xbe; let value = 0x12; i8[i] = expected; assertEquals(expected, func(i * kMemtypeSize8, value)); assertEquals(operation(expected, value), i8[i]); } VerifyBoundsCheck(func, kMemtypeSize8, 10); } (function TestAtomicAdd() { print("TestAtomicAdd"); let wasmAdd = GetAtomicBinOpFunction(kExprI32AtomicAdd, 2, 0); Test32Op(Add, wasmAdd); })(); (function TestAtomicAdd16U() { print("TestAtomicAdd16U"); let wasmAdd = GetAtomicBinOpFunction(kExprI32AtomicAdd16U, 1, 0); Test16Op(Add, wasmAdd); })(); (function TestAtomicAdd8U() { print("TestAtomicAdd8U"); let wasmAdd = GetAtomicBinOpFunction(kExprI32AtomicAdd8U, 0, 0); Test8Op(Add, wasmAdd); })(); (function TestAtomicSub() { print("TestAtomicSub"); let wasmSub = GetAtomicBinOpFunction(kExprI32AtomicSub, 2, 0); Test32Op(Sub, wasmSub); })(); (function TestAtomicSub16U() { print("TestAtomicSub16U"); let wasmSub = GetAtomicBinOpFunction(kExprI32AtomicSub16U, 1, 0); Test16Op(Sub, wasmSub); })(); (function TestAtomicSub8U() { print("TestAtomicSub8U"); let wasmSub = GetAtomicBinOpFunction(kExprI32AtomicSub8U, 0, 0); Test8Op(Sub, wasmSub); })(); (function TestAtomicAnd() { print("TestAtomicAnd"); let wasmAnd = GetAtomicBinOpFunction(kExprI32AtomicAnd, 2, 0); Test32Op(And, wasmAnd); })(); (function TestAtomicAnd16U() { print("TestAtomicAnd16U"); let wasmAnd = GetAtomicBinOpFunction(kExprI32AtomicAnd16U, 1, 0); Test16Op(And, wasmAnd); })(); (function TestAtomicAnd8U() { print("TestAtomicAnd8U"); let wasmAnd = GetAtomicBinOpFunction(kExprI32AtomicAnd8U, 0, 0); Test8Op(And, wasmAnd); })(); (function TestAtomicOr() { print("TestAtomicOr"); let wasmOr = GetAtomicBinOpFunction(kExprI32AtomicOr, 2, 0); Test32Op(Or, wasmOr); })(); (function TestAtomicOr16U() { print("TestAtomicOr16U"); let wasmOr = GetAtomicBinOpFunction(kExprI32AtomicOr16U, 1, 0); Test16Op(Or, wasmOr); })(); (function TestAtomicOr8U() { print("TestAtomicOr8U"); let wasmOr = GetAtomicBinOpFunction(kExprI32AtomicOr8U, 0, 0); Test8Op(Or, wasmOr); })(); (function TestAtomicXor() { print("TestAtomicXor"); let wasmXor = GetAtomicBinOpFunction(kExprI32AtomicXor, 2, 0); Test32Op(Xor, wasmXor); })(); (function TestAtomicXor16U() { print("TestAtomicXor16U"); let wasmXor = GetAtomicBinOpFunction(kExprI32AtomicXor16U, 1, 0); Test16Op(Xor, wasmXor); })(); (function TestAtomicXor8U() { print("TestAtomicXor8U"); let wasmXor = GetAtomicBinOpFunction(kExprI32AtomicXor8U, 0, 0); Test8Op(Xor, wasmXor); })(); (function TestAtomicExchange() { print("TestAtomicExchange"); let wasmExchange = GetAtomicBinOpFunction(kExprI32AtomicExchange, 2, 0); Test32Op(Exchange, wasmExchange); })(); (function TestAtomicExchange16U() { print("TestAtomicExchange16U"); let wasmExchange = GetAtomicBinOpFunction(kExprI32AtomicExchange16U, 1, 0); Test16Op(Exchange, wasmExchange); })(); (function TestAtomicExchange8U() { print("TestAtomicExchange8U"); let wasmExchange = GetAtomicBinOpFunction(kExprI32AtomicExchange8U, 0, 0); Test8Op(Exchange, wasmExchange); })(); function TestCmpExchange(func, buffer, params, size) { for (let i = 0; i < buffer.length; i = inc(i)) { for (let j = 0; j < params.length; j++) { for (let k = 0; k < params.length; k++) { buffer[i] = params[j]; let loaded = func(i * size, params[k], params[j]) >>> 0; let expected = (params[k] == loaded) ? params[j] : loaded; assertEquals(loaded, params[j]); assertEquals(expected, buffer[i]); } } } VerifyBoundsCheck(func, size); } (function TestAtomicCompareExchange() { print("TestAtomicCompareExchange"); let wasmCmpExchange = GetAtomicCmpExchangeFunction(kExprI32AtomicCompareExchange, 2, 0); let i32 = new Uint32Array(memory.buffer); let params = [0x00000001, 0x00000555, 0x00099999, 0xffffffff]; TestCmpExchange(wasmCmpExchange, i32, params, kMemtypeSize32); })(); (function TestAtomicCompareExchange16U() { print("TestAtomicCompareExchange16U"); let wasmCmpExchange = GetAtomicCmpExchangeFunction(kExprI32AtomicCompareExchange16U, 1, 0); let i16 = new Uint16Array(memory.buffer); let params = [0x0001, 0x0555, 0x9999]; TestCmpExchange(wasmCmpExchange, i16, params, kMemtypeSize16); })(); (function TestAtomicCompareExchange8U() { print("TestAtomicCompareExchange8U"); let wasmCmpExchange = GetAtomicCmpExchangeFunction(kExprI32AtomicCompareExchange8U, 0, 0); let i8 = new Uint8Array(memory.buffer); let params = [0x01, 0x0d, 0xf9]; TestCmpExchange(wasmCmpExchange, i8, params, kMemtypeSize8); })(); function TestLoad(func, buffer, value, size) { for (let i = 0; i < buffer.length; i = inc(i)) { buffer[i] = value; assertEquals(value, func(i * size) >>> 0); } VerifyBoundsCheck(func, size); } (function TestAtomicLoad() { print("TestAtomicLoad"); let wasmLoad = GetAtomicLoadFunction(kExprI32AtomicLoad, 2, 0); let i32 = new Uint32Array(memory.buffer); let value = 0xacedaced; TestLoad(wasmLoad, i32, value, kMemtypeSize32); })(); (function TestAtomicLoad16U() { print("TestAtomicLoad16U"); let wasmLoad = GetAtomicLoadFunction(kExprI32AtomicLoad16U, 1, 0); let i16 = new Uint16Array(memory.buffer); let value = 0xaced; TestLoad(wasmLoad, i16, value, kMemtypeSize16); })(); (function TestAtomicLoad8U() { print("TestAtomicLoad8U"); let wasmLoad = GetAtomicLoadFunction(kExprI32AtomicLoad8U, 0, 0); let i8 = new Uint8Array(memory.buffer); let value = 0xac; TestLoad(wasmLoad, i8, value, kMemtypeSize8); })(); function TestStore(func, buffer, value, size) { for (let i = 0; i < buffer.length; i = inc(i)) { func(i * size, value) assertEquals(value, buffer[i]); } VerifyBoundsCheck(func, size); } (function TestAtomicStore() { print("TestAtomicStore"); let wasmStore = GetAtomicStoreFunction(kExprI32AtomicStore, 2, 0); let i32 = new Uint32Array(memory.buffer); let value = 0xacedaced; TestStore(wasmStore, i32, value, kMemtypeSize32); })(); (function TestAtomicStore16U() { print("TestAtomicStore16U"); let wasmStore = GetAtomicStoreFunction(kExprI32AtomicStore16U, 1, 0); let i16 = new Uint16Array(memory.buffer); let value = 0xaced; TestStore(wasmStore, i16, value, kMemtypeSize16); })(); (function TestAtomicStore8U() { print("TestAtomicStore8U"); let wasmStore = GetAtomicStoreFunction(kExprI32AtomicStore8U, 0, 0); let i8 = new Uint8Array(memory.buffer); let value = 0xac; TestCmpExchange(wasmStore, i8, value, kMemtypeSize8); })(); (function TestAtomicLoadStoreOffset() { print("TestAtomicLoadStoreOffset"); var builder = new WasmModuleBuilder(); let memory = new WebAssembly.Memory({ initial: 16, maximum: 128, shared: true}); builder.addImportedMemory("m", "imported_mem", 16, 128, "shared"); builder.addFunction("loadStore", kSig_i_v) .addBody([ kExprI32Const, 16, kExprI32Const, 20, kAtomicPrefix, kExprI32AtomicStore, 0, 0xFC, 0xFF, 0x3a, kExprI32Const, 16, kAtomicPrefix, kExprI32AtomicLoad, 0, 0xFC, 0xFF, 0x3a]) .exportAs("loadStore"); builder.addFunction("storeOob", kSig_v_v) .addBody([ kExprI32Const, 16, kExprI32Const, 20, kAtomicPrefix, kExprI32AtomicStore, 0, 0xFC, 0xFF, 0xFF, 0x3a]) .exportAs("storeOob"); let module = new WebAssembly.Module(builder.toBuffer()); let instance = (new WebAssembly.Instance(module, {m: {imported_mem: memory}})); let buf = memory.buffer; assertEquals(20, instance.exports.loadStore()); assertTraps(kTrapMemOutOfBounds, instance.exports.storeOob); })(); (function TestAtomicOpinLoop() { print("TestAtomicOpinLoop"); var builder = new WasmModuleBuilder(); let memory = new WebAssembly.Memory({ initial: 16, maximum: 128, shared: true}); builder.addImportedMemory("m", "imported_mem", 16, 128, "shared"); builder.addFunction("main", kSig_i_v) .addBody([ kExprLoop, kWasmStmt, kExprI32Const, 16, kExprI32Const, 20, kAtomicPrefix, kExprI32AtomicStore, 2, 0, kExprI32Const, 16, kAtomicPrefix, kExprI32AtomicLoad, 2, 0, kExprReturn, kExprEnd, kExprI32Const, 0 ]) .exportFunc(); let module = new WebAssembly.Module(builder.toBuffer()); let instance = (new WebAssembly.Instance(module, {m: {imported_mem: memory}})); assertEquals(20, instance.exports.main()); })();