Commit 9c0f98c6 authored by Deepti Gandluri's avatar Deepti Gandluri Committed by Commit Bot

[wasm] Implement atomics Exchange, CompareExchange

BUG=v8:6532

R=binji@chromium.org, bradnelson@chromium.org

Change-Id: I376dd8e4d27cac657d5a7c05a50a0477963da7b7
Reviewed-on: https://chromium-review.googlesource.com/627476
Commit-Queue: Brad Nelson <bradnelson@chromium.org>
Reviewed-by: 's avatarBrad Nelson <bradnelson@chromium.org>
Reviewed-by: 's avatarBen Smith <binji@chromium.org>
Cr-Commit-Position: refs/heads/master@{#47620}
parent 2809fdbd
......@@ -3752,22 +3752,30 @@ Node* WasmGraphBuilder::Simd8x16ShuffleOp(const uint8_t shuffle[16],
inputs[0], inputs[1]);
}
#define ATOMIC_BINOP_LIST(V) \
V(I32AtomicAdd, Add, Uint32) \
V(I32AtomicSub, Sub, Uint32) \
V(I32AtomicAnd, And, Uint32) \
V(I32AtomicOr, Or, Uint32) \
V(I32AtomicXor, Xor, Uint32) \
V(I32AtomicAdd8U, Add, Uint8) \
V(I32AtomicSub8U, Sub, Uint8) \
V(I32AtomicAnd8U, And, Uint8) \
V(I32AtomicOr8U, Or, Uint8) \
V(I32AtomicXor8U, Xor, Uint8) \
V(I32AtomicAdd16U, Add, Uint16) \
V(I32AtomicSub16U, Sub, Uint16) \
V(I32AtomicAnd16U, And, Uint16) \
V(I32AtomicOr16U, Or, Uint16) \
V(I32AtomicXor16U, Xor, Uint16)
#define ATOMIC_BINOP_LIST(V) \
V(I32AtomicAdd, Add, Uint32) \
V(I32AtomicSub, Sub, Uint32) \
V(I32AtomicAnd, And, Uint32) \
V(I32AtomicOr, Or, Uint32) \
V(I32AtomicXor, Xor, Uint32) \
V(I32AtomicExchange, Exchange, Uint32) \
V(I32AtomicAdd8U, Add, Uint8) \
V(I32AtomicSub8U, Sub, Uint8) \
V(I32AtomicAnd8U, And, Uint8) \
V(I32AtomicOr8U, Or, Uint8) \
V(I32AtomicXor8U, Xor, Uint8) \
V(I32AtomicExchange8U, Exchange, Uint8) \
V(I32AtomicAdd16U, Add, Uint16) \
V(I32AtomicSub16U, Sub, Uint16) \
V(I32AtomicAnd16U, And, Uint16) \
V(I32AtomicOr16U, Or, Uint16) \
V(I32AtomicXor16U, Xor, Uint16) \
V(I32AtomicExchange16U, Exchange, Uint16)
#define ATOMIC_TERNARY_LIST(V) \
V(I32AtomicCompareExchange, CompareExchange, Uint32) \
V(I32AtomicCompareExchange8U, CompareExchange, Uint8) \
V(I32AtomicCompareExchange16U, CompareExchange, Uint16)
Node* WasmGraphBuilder::AtomicOp(wasm::WasmOpcode opcode,
const NodeVector& inputs,
......@@ -3784,6 +3792,17 @@ Node* WasmGraphBuilder::AtomicOp(wasm::WasmOpcode opcode,
}
ATOMIC_BINOP_LIST(BUILD_ATOMIC_BINOP)
#undef BUILD_ATOMIC_BINOP
#define BUILD_ATOMIC_TERNARY_OP(Name, Operation, Type) \
case wasm::kExpr##Name: { \
BoundsCheckMem(MachineType::Type(), inputs[0], 0, position); \
node = graph()->NewNode( \
jsgraph()->machine()->Atomic##Operation(MachineType::Type()), \
MemBuffer(0), inputs[0], inputs[1], inputs[2], *effect_, *control_); \
break; \
}
ATOMIC_TERNARY_LIST(BUILD_ATOMIC_TERNARY_OP)
#undef BUILD_ATOMIC_TERNARY_OP
default:
FATAL_UNSUPPORTED_OPCODE(opcode);
}
......
......@@ -414,28 +414,28 @@ constexpr WasmCodePosition kNoCodePosition = -1;
V(S128LoadMem, 0xfd80, s_i) \
V(S128StoreMem, 0xfd81, s_is)
#define FOREACH_ATOMIC_OPCODE(V) \
V(I32AtomicAdd, 0xfe1e, i_ii) \
V(I32AtomicAdd8U, 0xfe20, i_ii) \
V(I32AtomicAdd16U, 0xfe21, i_ii) \
V(I32AtomicSub, 0xfe25, i_ii) \
V(I32AtomicSub8U, 0xfe27, i_ii) \
V(I32AtomicSub16U, 0xfe28, i_ii) \
V(I32AtomicAnd, 0xfe2c, i_ii) \
V(I32AtomicAnd8U, 0xfe2e, i_ii) \
V(I32AtomicAnd16U, 0xfe2f, i_ii) \
V(I32AtomicOr, 0xfe33, i_ii) \
V(I32AtomicOr8U, 0xfe35, i_ii) \
V(I32AtomicOr16U, 0xfe36, i_ii) \
V(I32AtomicXor, 0xfe3a, i_ii) \
V(I32AtomicXor8U, 0xfe3c, i_ii) \
V(I32AtomicXor16U, 0xfe3d, i_ii) \
V(I32AtomicExchange, 0xfe41, i_ii) \
V(I32AtomicExchange8U, 0xfe43, i_ii) \
V(I32AtomicExchange16U, 0xfe44, i_ii) \
V(I32AtomicCompareExchange, 0xfe48, i_ii) \
V(I32AtomicCompareExchange8U, 0xfe4a, i_ii) \
V(I32AtomicCompareExchange16U, 0xfe4b, i_ii)
#define FOREACH_ATOMIC_OPCODE(V) \
V(I32AtomicAdd, 0xfe1e, i_ii) \
V(I32AtomicAdd8U, 0xfe20, i_ii) \
V(I32AtomicAdd16U, 0xfe21, i_ii) \
V(I32AtomicSub, 0xfe25, i_ii) \
V(I32AtomicSub8U, 0xfe27, i_ii) \
V(I32AtomicSub16U, 0xfe28, i_ii) \
V(I32AtomicAnd, 0xfe2c, i_ii) \
V(I32AtomicAnd8U, 0xfe2e, i_ii) \
V(I32AtomicAnd16U, 0xfe2f, i_ii) \
V(I32AtomicOr, 0xfe33, i_ii) \
V(I32AtomicOr8U, 0xfe35, i_ii) \
V(I32AtomicOr16U, 0xfe36, i_ii) \
V(I32AtomicXor, 0xfe3a, i_ii) \
V(I32AtomicXor8U, 0xfe3c, i_ii) \
V(I32AtomicXor16U, 0xfe3d, i_ii) \
V(I32AtomicExchange, 0xfe41, i_ii) \
V(I32AtomicExchange8U, 0xfe43, i_ii) \
V(I32AtomicExchange16U, 0xfe44, i_ii) \
V(I32AtomicCompareExchange, 0xfe48, i_iii) \
V(I32AtomicCompareExchange8U, 0xfe4a, i_iii) \
V(I32AtomicCompareExchange16U, 0xfe4b, i_iii)
// All opcodes.
#define FOREACH_OPCODE(V) \
......@@ -481,7 +481,8 @@ constexpr WasmCodePosition kNoCodePosition = -1;
V(d_l, kWasmF64, kWasmI64) \
V(d_id, kWasmF64, kWasmI32, kWasmF64) \
V(f_if, kWasmF32, kWasmI32, kWasmF32) \
V(l_il, kWasmI64, kWasmI32, kWasmI64)
V(l_il, kWasmI64, kWasmI32, kWasmI64) \
V(i_iii, kWasmI32, kWasmI32, kWasmI32, kWasmI32)
#define FOREACH_SIMD_SIGNATURE(V) \
V(s_s, kWasmS128, kWasmS128) \
......
......@@ -9,6 +9,7 @@
#define WASM_ATOMICS_OP(op) kAtomicPrefix, static_cast<byte>(op)
#define WASM_ATOMICS_BINOP(op, x, y) x, y, WASM_ATOMICS_OP(op)
#define WASM_ATOMICS_TERNARY_OP(op, x, y, z) x, y, z, WASM_ATOMICS_OP(op)
typedef uint32_t (*Uint32BinOp)(uint32_t, uint32_t);
typedef uint16_t (*Uint16BinOp)(uint16_t, uint16_t);
......@@ -39,6 +40,17 @@ T Xor(T a, T b) {
return a ^ b;
}
template <typename T>
T Exchange(T a, T b) {
return b;
}
template <typename T>
T CompareExchange(T initial, T a, T b) {
if (initial == a) return b;
return a;
}
void RunU32BinOp(WasmOpcode wasm_op, Uint32BinOp expected_op) {
EXPERIMENTAL_FLAG_SCOPE(threads);
WasmRunner<uint32_t, uint32_t> r(kExecuteCompiled);
......@@ -62,6 +74,7 @@ WASM_EXEC_TEST(I32Sub) { RunU32BinOp(kExprI32AtomicSub, Sub); }
WASM_EXEC_TEST(I32And) { RunU32BinOp(kExprI32AtomicAnd, And); }
WASM_EXEC_TEST(I32Or) { RunU32BinOp(kExprI32AtomicOr, Or); }
WASM_EXEC_TEST(I32Xor) { RunU32BinOp(kExprI32AtomicXor, Xor); }
WASM_EXEC_TEST(I32Exchange) { RunU32BinOp(kExprI32AtomicExchange, Exchange); }
void RunU16BinOp(WasmOpcode wasm_op, Uint16BinOp expected_op) {
EXPERIMENTAL_FLAG_SCOPE(threads);
......@@ -86,6 +99,9 @@ WASM_EXEC_TEST(I32Sub16U) { RunU16BinOp(kExprI32AtomicSub16U, Sub); }
WASM_EXEC_TEST(I32And16U) { RunU16BinOp(kExprI32AtomicAnd16U, And); }
WASM_EXEC_TEST(I32Or16U) { RunU16BinOp(kExprI32AtomicOr16U, Or); }
WASM_EXEC_TEST(I32Xor16U) { RunU16BinOp(kExprI32AtomicXor16U, Xor); }
WASM_EXEC_TEST(I32Exchange16U) {
RunU16BinOp(kExprI32AtomicExchange16U, Exchange);
}
void RunU8BinOp(WasmOpcode wasm_op, Uint8BinOp expected_op) {
EXPERIMENTAL_FLAG_SCOPE(threads);
......@@ -110,3 +126,63 @@ WASM_EXEC_TEST(I32Sub8U) { RunU8BinOp(kExprI32AtomicSub8U, Sub); }
WASM_EXEC_TEST(I32And8U) { RunU8BinOp(kExprI32AtomicAnd8U, And); }
WASM_EXEC_TEST(I32Or8U) { RunU8BinOp(kExprI32AtomicOr8U, Or); }
WASM_EXEC_TEST(I32Xor8U) { RunU8BinOp(kExprI32AtomicXor8U, Xor); }
WASM_EXEC_TEST(I32Exchange8U) {
RunU8BinOp(kExprI32AtomicExchange8U, Exchange);
}
WASM_EXEC_TEST(I32CompareExchange) {
EXPERIMENTAL_FLAG_SCOPE(threads);
WasmRunner<uint32_t, uint32_t, uint32_t> r(kExecuteCompiled);
uint32_t* memory = r.builder().AddMemoryElems<uint32_t>(8);
BUILD(r,
WASM_ATOMICS_TERNARY_OP(kExprI32AtomicCompareExchange, WASM_I32V_1(0),
WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)));
FOR_UINT32_INPUTS(i) {
uint32_t initial = *i;
FOR_UINT32_INPUTS(j) {
r.builder().WriteMemory(&memory[0], initial);
CHECK_EQ(initial, r.Call(*i, *j));
uint32_t expected = CompareExchange(initial, *i, *j);
CHECK_EQ(expected, r.builder().ReadMemory(&memory[0]));
}
}
}
WASM_EXEC_TEST(I32CompareExchange16U) {
EXPERIMENTAL_FLAG_SCOPE(threads);
WasmRunner<uint32_t, uint32_t, uint32_t> r(kExecuteCompiled);
uint16_t* memory = r.builder().AddMemoryElems<uint16_t>(8);
BUILD(r, WASM_ATOMICS_TERNARY_OP(kExprI32AtomicCompareExchange16U,
WASM_I32V_1(0), WASM_GET_LOCAL(0),
WASM_GET_LOCAL(1)));
FOR_UINT16_INPUTS(i) {
uint16_t initial = *i;
FOR_UINT16_INPUTS(j) {
r.builder().WriteMemory(&memory[0], initial);
CHECK_EQ(initial, r.Call(*i, *j));
uint16_t expected = CompareExchange(initial, *i, *j);
CHECK_EQ(expected, r.builder().ReadMemory(&memory[0]));
}
}
}
WASM_EXEC_TEST(I32CompareExchange8U) {
EXPERIMENTAL_FLAG_SCOPE(threads);
WasmRunner<uint32_t, uint32_t, uint32_t> r(kExecuteCompiled);
uint8_t* memory = r.builder().AddMemoryElems<uint8_t>(8);
BUILD(r,
WASM_ATOMICS_TERNARY_OP(kExprI32AtomicCompareExchange8U, WASM_I32V_1(0),
WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)));
FOR_UINT8_INPUTS(i) {
uint8_t initial = *i;
FOR_UINT8_INPUTS(j) {
r.builder().WriteMemory(&memory[0], initial);
CHECK_EQ(initial, r.Call(*i, *j));
uint8_t expected = CompareExchange(initial, *i, *j);
CHECK_EQ(expected, r.builder().ReadMemory(&memory[0]));
}
}
}
......@@ -16,6 +16,7 @@ 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});
......@@ -38,15 +39,35 @@ function GetAtomicBinOpFunction(wasmExpression) {
return instance.exports.main;
}
function GetAtomicCmpExchangeFunction(wasmExpression) {
let builder = new WasmModuleBuilder();
builder.addImportedMemory("m", "imported_mem");
builder.addFunction("main", kSig_i_iii)
.addBody([
kExprGetLocal, 0,
kExprGetLocal, 1,
kExprGetLocal, 2,
kAtomicPrefix,
wasmExpression])
.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));
assertTraps(kTrapMemOutOfBounds, () => func(i, 5, 10));
}
// Test out of bounds at maximum + 1
assertTraps(kTrapMemOutOfBounds, () => func((maxSize + 1) * kPageSize, 5));
assertTraps(kTrapMemOutOfBounds, () => func((maxSize + 1) * kPageSize, 5, 1));
}
function Test32Op(operation, func) {
......@@ -174,3 +195,63 @@ function Test8Op(operation, func) {
let wasmXor = GetAtomicBinOpFunction(kExprI32AtomicXor8U);
Test8Op(Xor, wasmXor);
})();
(function TestAtomicExchange() {
print("TestAtomicExchange");
let wasmExchange = GetAtomicBinOpFunction(kExprI32AtomicExchange);
Test32Op(Exchange, wasmExchange);
})();
(function TestAtomicExchange16U() {
print("TestAtomicExchange16U");
let wasmExchange = GetAtomicBinOpFunction(kExprI32AtomicExchange16U);
Test16Op(Exchange, wasmExchange);
})();
(function TestAtomicExchange8U() {
print("TestAtomicExchange8U");
let wasmExchange = GetAtomicBinOpFunction(kExprI32AtomicExchange8U);
Test8Op(Exchange, wasmExchange);
})();
function TestCmpExchange(func, buffer, params, size) {
for (let i = 0; i < buffer.length; 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);
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);
let i16 = new Uint16Array(memory.buffer);
let params = [0x0001, 0x0555, 0x9999];
TestCmpExchange(wasmCmpExchange, i16, params, kMemtypeSize16);
})();
(function TestAtomicCompareExchange8U() {
print("TestAtomicCompareExchange8U");
let wasmCmpExchange =
GetAtomicCmpExchangeFunction(kExprI32AtomicCompareExchange8U);
let i8 = new Uint8Array(memory.buffer);
let params = [0x01, 0x0d, 0xf9];
TestCmpExchange(wasmCmpExchange, i8, params, kMemtypeSize8);
})();
......@@ -342,6 +342,12 @@ let kExprI32AtomicOr16U = 0x36;
let kExprI32AtomicXor = 0x3a;
let kExprI32AtomicXor8U = 0x3c;
let kExprI32AtomicXor16U = 0x3d;
let kExprI32AtomicExchange = 0x41;
let kExprI32AtomicExchange8U = 0x43;
let kExprI32AtomicExchange16U = 0x44;
let kExprI32AtomicCompareExchange = 0x48
let kExprI32AtomicCompareExchange8U = 0x4a
let kExprI32AtomicCompareExchange16U = 0x4b
let kTrapUnreachable = 0;
let kTrapMemOutOfBounds = 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