[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/600539 Reviewed-by: Bill Budge <bbudge@chromium.org> Commit-Queue: Deepti Gandluri <gdeepti@chromium.org> Cr-Commit-Position: refs/heads/master@{#47172}
This commit is contained in:
parent
946f78a0ad
commit
575ec86335
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user