Commit 575ec863 authored by Deepti Gandluri's avatar Deepti Gandluri Committed by Commit Bot

[wasm] Implement atomic logical BinOps

 - Implemented ops: I32AtomicAnd, I32AtomicAnd8U, I32AtomicAnd16U, I32AtomicOr,
 I32AtomicOr8U, I32AtomicOr16U, I32AtomicXor, I32AtomicXor8U, I32AtomicXor16U
 - Refactor wasm-compiler AtomicOp to use macros
 - Tests

Bug:V8:6532

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

Change-Id: I7e4dc8ad8cf3e211c3aef721a02778f2a4621322
Reviewed-on: https://chromium-review.googlesource.com/600539Reviewed-by: 's avatarBill Budge <bbudge@chromium.org>
Commit-Queue: Deepti Gandluri <gdeepti@chromium.org>
Cr-Commit-Position: refs/heads/master@{#47172}
parent 946f78a0
......@@ -3718,53 +3718,38 @@ 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)
Node* WasmGraphBuilder::AtomicOp(wasm::WasmOpcode opcode,
const NodeVector& inputs,
wasm::WasmCodePosition position) {
Node* node;
switch (opcode) {
case wasm::kExprI32AtomicAdd: {
BoundsCheckMem(MachineType::Uint32(), inputs[0], 0, position);
node = graph()->NewNode(
jsgraph()->machine()->AtomicAdd(MachineType::Uint32()), MemBuffer(0),
inputs[0], inputs[1], *effect_, *control_);
break;
}
case wasm::kExprI32AtomicSub: {
BoundsCheckMem(MachineType::Uint32(), inputs[0], 0, position);
node = graph()->NewNode(
jsgraph()->machine()->AtomicSub(MachineType::Uint32()), MemBuffer(0),
inputs[0], inputs[1], *effect_, *control_);
break;
}
case wasm::kExprI32AtomicAdd16U: {
BoundsCheckMem(MachineType::Uint16(), inputs[0], 0, position);
node = graph()->NewNode(
jsgraph()->machine()->AtomicAdd(MachineType::Uint16()), MemBuffer(0),
inputs[0], inputs[1], *effect_, *control_);
break;
}
case wasm::kExprI32AtomicSub16U: {
BoundsCheckMem(MachineType::Uint16(), inputs[0], 0, position);
node = graph()->NewNode(
jsgraph()->machine()->AtomicSub(MachineType::Uint16()), MemBuffer(0),
inputs[0], inputs[1], *effect_, *control_);
break;
}
case wasm::kExprI32AtomicAdd8U: {
BoundsCheckMem(MachineType::Uint8(), inputs[0], 0, position);
node = graph()->NewNode(
jsgraph()->machine()->AtomicAdd(MachineType::Uint8()), MemBuffer(0),
inputs[0], inputs[1], *effect_, *control_);
break;
}
case wasm::kExprI32AtomicSub8U: {
BoundsCheckMem(MachineType::Uint8(), inputs[0], 0, position);
node = graph()->NewNode(
jsgraph()->machine()->AtomicSub(MachineType::Uint8()), MemBuffer(0),
inputs[0], inputs[1], *effect_, *control_);
break;
}
#define BUILD_ATOMIC_BINOP(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], *effect_, *control_); \
break; \
}
ATOMIC_BINOP_LIST(BUILD_ATOMIC_BINOP)
#undef BUILD_ATOMIC_BINOP
default:
FATAL_UNSUPPORTED_OPCODE(opcode);
}
......
......@@ -24,6 +24,21 @@ T Sub(T a, T b) {
return a - b;
}
template <typename T>
T And(T a, T b) {
return a & b;
}
template <typename T>
T Or(T a, T b) {
return a | b;
}
template <typename T>
T Xor(T a, T b) {
return a ^ b;
}
void RunU32BinOp(WasmOpcode wasm_op, Uint32BinOp expected_op) {
EXPERIMENTAL_FLAG_SCOPE(threads);
WasmRunner<uint32_t, uint32_t> r(kExecuteCompiled);
......@@ -44,6 +59,9 @@ void RunU32BinOp(WasmOpcode wasm_op, Uint32BinOp expected_op) {
WASM_EXEC_TEST(I32Add) { RunU32BinOp(kExprI32AtomicAdd, Add); }
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); }
void RunU16BinOp(WasmOpcode wasm_op, Uint16BinOp expected_op) {
EXPERIMENTAL_FLAG_SCOPE(threads);
......@@ -65,6 +83,9 @@ void RunU16BinOp(WasmOpcode wasm_op, Uint16BinOp expected_op) {
WASM_EXEC_TEST(I32Add16U) { RunU16BinOp(kExprI32AtomicAdd16U, Add); }
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); }
void RunU8BinOp(WasmOpcode wasm_op, Uint8BinOp expected_op) {
EXPERIMENTAL_FLAG_SCOPE(threads);
......@@ -86,3 +107,6 @@ void RunU8BinOp(WasmOpcode wasm_op, Uint8BinOp expected_op) {
WASM_EXEC_TEST(I32Add8U) { RunU8BinOp(kExprI32AtomicAdd8U, Add); }
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); }
......@@ -146,3 +146,191 @@ function VerifyBoundsCheck(buffer, funcs, max) {
VerifyBoundsCheck(memory.buffer, subs, 20);
})();
(function TestAtomicAnd() {
print("TestAtomicAnd");
let memory = new WebAssembly.Memory({initial: 5, maximum: 20, shared: true});
let builder = new WasmModuleBuilder();
builder.addImportedMemory("m", "imported_mem");
builder.addFunction("atomic_and", kSig_i_ii)
.addBody([
kExprGetLocal, 0,
kExprGetLocal, 1,
kAtomicPrefix,
kExprI32AtomicAnd])
.exportAs("atomic_and");
builder.addFunction("atomic_and16", kSig_i_ii)
.addBody([
kExprGetLocal, 0,
kExprGetLocal, 1,
kAtomicPrefix,
kExprI32AtomicAnd16U])
.exportAs("atomic_and16");
builder.addFunction("atomic_and8", kSig_i_ii)
.addBody([
kExprGetLocal, 0,
kExprGetLocal, 1,
kAtomicPrefix,
kExprI32AtomicAnd8U])
.exportAs("atomic_and8");
let module = new WebAssembly.Module(builder.toBuffer());
let instance = (new WebAssembly.Instance(module,
{m: {imported_mem: memory}}));
// 32-bit And
let i32 = new Uint32Array(memory.buffer);
for (i = 0; i < i32.length; i++) {
i32[i] = i32.length;
assertEquals(i32.length, instance.exports.atomic_and(i * 4, i));
assertEquals(i32.length & i, i32[i]);
}
// 16-bit And
let i16 = new Uint16Array(memory.buffer);
for (i = 0; i < i16.length; i++) {
i16[i] = 0xffff;
assertEquals(0xffff, instance.exports.atomic_and16(i * 2, 0x1234));
assertEquals(0xffff & 0x1234, i16[i]);
}
// 8-bit And
let i8 = new Uint8Array(memory.buffer);
for (i = 0; i < i8.length; i++) {
i8[i] = 0xff;
assertEquals(0xff, instance.exports.atomic_and8(i, i % 0xff));
assertEquals(0xff & i % 0xff, i8[i]);
}
var ands = [{func: instance.exports.atomic_and, memtype_size: 4},
{func: instance.exports.atomic_and16, memtype_size: 2},
{func: instance.exports.atomic_and8, memtype_size: 1}];
VerifyBoundsCheck(memory.buffer, ands, 20);
})();
(function TestAtomicOr() {
print("TestAtomicOr");
let memory = new WebAssembly.Memory({initial: 5, maximum: 20, shared: true});
let builder = new WasmModuleBuilder();
builder.addImportedMemory("m", "imported_mem");
builder.addFunction("atomic_or", kSig_i_ii)
.addBody([
kExprGetLocal, 0,
kExprGetLocal, 1,
kAtomicPrefix,
kExprI32AtomicOr])
.exportAs("atomic_or");
builder.addFunction("atomic_or16", kSig_i_ii)
.addBody([
kExprGetLocal, 0,
kExprGetLocal, 1,
kAtomicPrefix,
kExprI32AtomicOr16U])
.exportAs("atomic_or16");
builder.addFunction("atomic_or8", kSig_i_ii)
.addBody([
kExprGetLocal, 0,
kExprGetLocal, 1,
kAtomicPrefix,
kExprI32AtomicOr8U])
.exportAs("atomic_or8");
let module = new WebAssembly.Module(builder.toBuffer());
let instance = (new WebAssembly.Instance(module,
{m: {imported_mem: memory}}));
// 32-bit Or
let i32 = new Uint32Array(memory.buffer);
for (i = 0; i < i32.length; i++) {
i32[i] = i32.length;
assertEquals(i32.length, instance.exports.atomic_or(i * 4, i));
assertEquals(i32.length | i, i32[i]);
}
// 16-bit Or
let i16 = new Uint16Array(memory.buffer);
for (i = 0; i < i16.length; i++) {
i16[i] = 0xffff;
assertEquals(0xffff, instance.exports.atomic_or16(i * 2, 0x1234));
assertEquals(0xffff | 0x1234, i16[i]);
}
// 8-bit Or
let i8 = new Uint8Array(memory.buffer);
for (i = 0; i < i8.length; i++) {
i8[i] = 0xff;
assertEquals(0xff, instance.exports.atomic_or8(i, i % 0xff));
assertEquals(0xff | i % 0xff, i8[i]);
}
var ors = [{func: instance.exports.atomic_or, memtype_size: 4},
{func: instance.exports.atomic_or16, memtype_size: 2},
{func: instance.exports.atomic_or8, memtype_size: 1}];
VerifyBoundsCheck(memory.buffer, ors, 20);
})();
(function TestAtomicXor() {
print("TestAtomicXor");
let memory = new WebAssembly.Memory({initial: 5, maximum: 20, shared: true});
let builder = new WasmModuleBuilder();
builder.addImportedMemory("m", "imported_mem");
builder.addFunction("atomic_xor", kSig_i_ii)
.addBody([
kExprGetLocal, 0,
kExprGetLocal, 1,
kAtomicPrefix,
kExprI32AtomicXor])
.exportAs("atomic_xor");
builder.addFunction("atomic_xor16", kSig_i_ii)
.addBody([
kExprGetLocal, 0,
kExprGetLocal, 1,
kAtomicPrefix,
kExprI32AtomicXor16U])
.exportAs("atomic_xor16");
builder.addFunction("atomic_xor8", kSig_i_ii)
.addBody([
kExprGetLocal, 0,
kExprGetLocal, 1,
kAtomicPrefix,
kExprI32AtomicXor8U])
.exportAs("atomic_xor8");
let module = new WebAssembly.Module(builder.toBuffer());
let instance = (new WebAssembly.Instance(module,
{m: {imported_mem: memory}}));
// 32-bit Xor
let i32 = new Uint32Array(memory.buffer);
for (i = 0; i < i32.length; i++) {
i32[i] = i32.length;
assertEquals(i32.length, instance.exports.atomic_xor(i * 4, i));
assertEquals(i32.length ^ i, i32[i]);
}
// 16-bit Xor
let i16 = new Uint16Array(memory.buffer);
for (i = 0; i < i16.length; i++) {
i16[i] = 0xffff;
assertEquals(0xffff, instance.exports.atomic_xor16(i * 2, 0x5678));
assertEquals(0xffff ^ 0x5678, i16[i]);
}
// 8-bit Xor
let i8 = new Uint8Array(memory.buffer);
for (i = 0; i < i8.length; i++) {
i8[i] = 0xff;
assertEquals(0xff, instance.exports.atomic_xor8(i, i % 0xff));
assertEquals(0xff ^ i % 0xff, i8[i]);
}
var xors = [{func: instance.exports.atomic_xor, memtype_size: 4},
{func: instance.exports.atomic_xor16, memtype_size: 2},
{func: instance.exports.atomic_xor8, memtype_size: 1}];
VerifyBoundsCheck(memory.buffer, xors, 20);
})();
......@@ -329,6 +329,15 @@ let kExprI32AtomicAdd16U = 0x21;
let kExprI32AtomicSub = 0x25;
let kExprI32AtomicSub8U = 0x27;
let kExprI32AtomicSub16U = 0x28;
let kExprI32AtomicAnd = 0x2c;
let kExprI32AtomicAnd8U = 0x2e;
let kExprI32AtomicAnd16U = 0x2f;
let kExprI32AtomicOr = 0x33;
let kExprI32AtomicOr8U = 0x35;
let kExprI32AtomicOr16U = 0x36;
let kExprI32AtomicXor = 0x3a;
let kExprI32AtomicXor8U = 0x3c;
let kExprI32AtomicXor16U = 0x3d;
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