| // 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()); | 
 | })(); |