Commit b380525b authored by Deepti Gandluri's avatar Deepti Gandluri Committed by Commit Bot

[wasm] Implement I32AtomicLoad, I32AtomicStore ops

Bug: V8:6532
Change-Id: I6713e1c01ec669b7fa9a09bb75fbecff12f6cc22
Reviewed-on: https://chromium-review.googlesource.com/685949
Commit-Queue: Deepti Gandluri <gdeepti@chromium.org>
Reviewed-by: 's avatarBen Smith <binji@chromium.org>
Reviewed-by: 's avatarBill Budge <bbudge@chromium.org>
Cr-Commit-Position: refs/heads/master@{#48252}
parent 86fc2af9
......@@ -4046,32 +4046,66 @@ Node* WasmGraphBuilder::Simd8x16ShuffleOp(const uint8_t shuffle[16],
V(I32AtomicCompareExchange8U, CompareExchange, Uint8) \
V(I32AtomicCompareExchange16U, CompareExchange, Uint16)
#define ATOMIC_LOAD_LIST(V) \
V(I32AtomicLoad, Uint32) \
V(I32AtomicLoad8U, Uint8) \
V(I32AtomicLoad16U, Uint16)
#define ATOMIC_STORE_LIST(V) \
V(I32AtomicStore, Uint32, kWord32) \
V(I32AtomicStore8U, Uint8, kWord8) \
V(I32AtomicStore16U, Uint16, kWord16)
Node* WasmGraphBuilder::AtomicOp(wasm::WasmOpcode opcode, Node* const* inputs,
uint32_t alignment, uint32_t offset,
wasm::WasmCodePosition position) {
// TODO(gdeepti): Add alignment validation, traps on misalignment
Node* node;
switch (opcode) {
#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; \
#define BUILD_ATOMIC_BINOP(Name, Operation, Type) \
case wasm::kExpr##Name: { \
BoundsCheckMem(MachineType::Type(), inputs[0], offset, position); \
node = graph()->NewNode( \
jsgraph()->machine()->Atomic##Operation(MachineType::Type()), \
MemBuffer(offset), inputs[0], inputs[1], *effect_, *control_); \
break; \
}
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; \
#define BUILD_ATOMIC_TERNARY_OP(Name, Operation, Type) \
case wasm::kExpr##Name: { \
BoundsCheckMem(MachineType::Type(), inputs[0], offset, position); \
node = graph()->NewNode( \
jsgraph()->machine()->Atomic##Operation(MachineType::Type()), \
MemBuffer(offset), inputs[0], inputs[1], inputs[2], *effect_, \
*control_); \
break; \
}
ATOMIC_TERNARY_LIST(BUILD_ATOMIC_TERNARY_OP)
#undef BUILD_ATOMIC_TERNARY_OP
#define BUILD_ATOMIC_LOAD_OP(Name, Type) \
case wasm::kExpr##Name: { \
BoundsCheckMem(MachineType::Type(), inputs[0], offset, position); \
node = graph()->NewNode( \
jsgraph()->machine()->AtomicLoad(MachineType::Type()), \
MemBuffer(offset), inputs[0], *effect_, *control_); \
break; \
}
ATOMIC_LOAD_LIST(BUILD_ATOMIC_LOAD_OP)
#undef BUILD_ATOMIC_LOAD_OP
#define BUILD_ATOMIC_STORE_OP(Name, Type, Rep) \
case wasm::kExpr##Name: { \
BoundsCheckMem(MachineType::Type(), inputs[0], offset, position); \
node = graph()->NewNode( \
jsgraph()->machine()->AtomicStore(MachineRepresentation::Rep), \
MemBuffer(offset), inputs[0], inputs[1], *effect_, *control_); \
break; \
}
ATOMIC_STORE_LIST(BUILD_ATOMIC_STORE_OP)
#undef BUILD_ATOMIC_STORE_OP
default:
FATAL_UNSUPPORTED_OPCODE(opcode);
}
......@@ -4079,6 +4113,11 @@ Node* WasmGraphBuilder::AtomicOp(wasm::WasmOpcode opcode, Node* const* inputs,
return node;
}
#undef ATOMIC_BINOP_LIST
#undef ATOMIC_TERNARY_LIST
#undef ATOMIC_LOAD_LIST
#undef ATOMIC_STORE_LIST
namespace {
bool must_record_function_compilation(Isolate* isolate) {
return isolate->logger()->is_logging_code_events() || isolate->is_profiling();
......@@ -4738,8 +4777,6 @@ MaybeHandle<Code> WasmCompilationUnit::CompileWasmFunction(
#undef WASM_64
#undef FATAL_UNSUPPORTED_OPCODE
#undef ATOMIC_BINOP_LIST
#undef ATOMIC_TERNARY_LIST
} // namespace compiler
} // namespace internal
......
......@@ -51,18 +51,21 @@ struct WasmException;
(message)))
#define ATOMIC_OP_LIST(V) \
V(I32AtomicLoad, Uint32) \
V(I32AtomicAdd, Uint32) \
V(I32AtomicSub, Uint32) \
V(I32AtomicAnd, Uint32) \
V(I32AtomicOr, Uint32) \
V(I32AtomicXor, Uint32) \
V(I32AtomicExchange, Uint32) \
V(I32AtomicLoad8U, Uint8) \
V(I32AtomicAdd8U, Uint8) \
V(I32AtomicSub8U, Uint8) \
V(I32AtomicAnd8U, Uint8) \
V(I32AtomicOr8U, Uint8) \
V(I32AtomicXor8U, Uint8) \
V(I32AtomicExchange8U, Uint8) \
V(I32AtomicLoad16U, Uint16) \
V(I32AtomicAdd16U, Uint16) \
V(I32AtomicSub16U, Uint16) \
V(I32AtomicAnd16U, Uint16) \
......@@ -73,6 +76,11 @@ struct WasmException;
V(I32AtomicCompareExchange8U, Uint8) \
V(I32AtomicCompareExchange16U, Uint16)
#define ATOMIC_STORE_OP_LIST(V) \
V(I32AtomicStore, Uint32) \
V(I32AtomicStore8U, Uint8) \
V(I32AtomicStore16U, Uint16)
template <typename T>
Vector<T> vec2vec(std::vector<T>& vec) {
return Vector<T>(vec.data(), vec.size());
......@@ -1693,17 +1701,7 @@ class WasmFullDecoder : public WasmDecoder<validate> {
opcode = static_cast<WasmOpcode>(opcode << 8 | atomic_index);
TRACE(" @%-4d #%-20s|", startrel(this->pc_),
WasmOpcodes::OpcodeName(opcode));
switch (opcode) {
#define DECODE_ATOMIC_BINOP(Name, Type) \
case kExpr##Name: { \
len += DecodeAtomicOpcode(opcode, MachineType::Type()); \
break; \
}
ATOMIC_OP_LIST(DECODE_ATOMIC_BINOP)
#undef DECODE_ATOMIC_BINOP
default:
this->error("Invalid opcode");
}
len += DecodeAtomicOpcode(opcode);
break;
}
default: {
......@@ -1994,19 +1992,44 @@ class WasmFullDecoder : public WasmDecoder<validate> {
return len;
}
unsigned DecodeAtomicOpcode(WasmOpcode opcode, MachineType mem_type) {
unsigned DecodeAtomicOpcode(WasmOpcode opcode) {
unsigned len = 0;
ValueType ret_type;
FunctionSig* sig = WasmOpcodes::AtomicSignature(opcode);
if (sig != nullptr) {
MachineType memtype;
switch (opcode) {
#define CASE_ATOMIC_STORE_OP(Name, Type) \
case kExpr##Name: { \
memtype = MachineType::Type(); \
ret_type = MachineRepresentation::kNone; \
break; \
}
ATOMIC_STORE_OP_LIST(CASE_ATOMIC_STORE_OP)
#undef CASE_ATOMIC_OP
#define CASE_ATOMIC_OP(Name, Type) \
case kExpr##Name: { \
memtype = MachineType::Type(); \
ret_type = GetReturnType(sig); \
break; \
}
ATOMIC_OP_LIST(CASE_ATOMIC_OP)
#undef CASE_ATOMIC_OP
default:
this->error("invalid atomic opcode");
break;
}
// TODO(clemensh): Better memory management here.
std::vector<Value> args(sig->parameter_count());
MemoryAccessOperand<validate> operand(
this, this->pc_ + 1, ElementSizeLog2Of(mem_type.representation()));
this, this->pc_ + 1, ElementSizeLog2Of(memtype.representation()));
len += operand.length;
for (int i = static_cast<int>(sig->parameter_count() - 1); i >= 0; --i) {
args[i] = Pop(i, sig->GetParam(i));
}
auto* result = Push(GetReturnType(sig));
auto result = ret_type == MachineRepresentation::kNone
? nullptr
: Push(GetReturnType(sig));
interface_.AtomicOp(this, opcode, vec2vec(args), operand, result);
} else {
this->error("invalid atomic opcode");
......
......@@ -235,6 +235,8 @@ const char* WasmOpcodes::OpcodeName(WasmOpcode opcode) {
CASE_S1x16_OP(AllTrue, "all_true")
// Atomic operations.
CASE_U32_OP(AtomicLoad, "atomic_load")
CASE_U32_OP(AtomicStore, "atomic_store")
CASE_U32_OP(AtomicAdd, "atomic_add")
CASE_U32_OP(AtomicSub, "atomic_sub")
CASE_U32_OP(AtomicAnd, "atomic_and")
......
......@@ -419,6 +419,12 @@ constexpr WasmCodePosition kNoCodePosition = -1;
V(S128StoreMem, 0xfd81, s_is)
#define FOREACH_ATOMIC_OPCODE(V) \
V(I32AtomicLoad, 0xfe10, i_i) \
V(I32AtomicLoad8U, 0xfe12, i_i) \
V(I32AtomicLoad16U, 0xfe13, i_i) \
V(I32AtomicStore, 0xfe17, i_ii) \
V(I32AtomicStore8U, 0xfe19, i_ii) \
V(I32AtomicStore16U, 0xfe1a, i_ii) \
V(I32AtomicAdd, 0xfe1e, i_ii) \
V(I32AtomicAdd8U, 0xfe20, i_ii) \
V(I32AtomicAdd16U, 0xfe21, i_ii) \
......
......@@ -19,6 +19,12 @@ namespace wasm {
#define WASM_ATOMICS_TERNARY_OP(op, x, y, z, representation) \
x, y, z, WASM_ATOMICS_OP(op), \
static_cast<byte>(ElementSizeLog2Of(representation)), ZERO_OFFSET
#define WASM_ATOMICS_LOAD_OP(op, x, representation) \
x, WASM_ATOMICS_OP(op), \
static_cast<byte>(ElementSizeLog2Of(representation)), ZERO_OFFSET
#define WASM_ATOMICS_STORE_OP(op, x, y, representation) \
x, y, WASM_ATOMICS_OP(op), \
static_cast<byte>(ElementSizeLog2Of(representation)), ZERO_OFFSET
typedef uint32_t (*Uint32BinOp)(uint32_t, uint32_t);
typedef uint16_t (*Uint16BinOp)(uint16_t, uint16_t);
......@@ -203,9 +209,113 @@ TEST(I32AtomicCompareExchange8U) {
}
}
TEST(I32AtomicLoad) {
EXPERIMENTAL_FLAG_SCOPE(threads);
WasmRunner<uint32_t> r(kExecuteCompiled);
r.builder().SetHasSharedMemory();
uint32_t* memory = r.builder().AddMemoryElems<uint32_t>(8);
BUILD(r, WASM_ATOMICS_LOAD_OP(kExprI32AtomicLoad, WASM_ZERO,
MachineRepresentation::kWord32));
FOR_UINT32_INPUTS(i) {
uint32_t expected = *i;
r.builder().WriteMemory(&memory[0], expected);
CHECK_EQ(expected, r.Call());
}
}
TEST(I32AtomicLoad16U) {
EXPERIMENTAL_FLAG_SCOPE(threads);
WasmRunner<uint32_t> r(kExecuteCompiled);
r.builder().SetHasSharedMemory();
uint16_t* memory = r.builder().AddMemoryElems<uint16_t>(8);
BUILD(r, WASM_ATOMICS_LOAD_OP(kExprI32AtomicLoad16U, WASM_ZERO,
MachineRepresentation::kWord16));
FOR_UINT16_INPUTS(i) {
uint16_t expected = *i;
r.builder().WriteMemory(&memory[0], expected);
CHECK_EQ(expected, r.Call());
}
}
TEST(I32AtomicLoad8U) {
EXPERIMENTAL_FLAG_SCOPE(threads);
WasmRunner<uint32_t> r(kExecuteCompiled);
r.builder().SetHasSharedMemory();
uint8_t* memory = r.builder().AddMemoryElems<uint8_t>(8);
BUILD(r, WASM_ATOMICS_LOAD_OP(kExprI32AtomicLoad8U, WASM_ZERO,
MachineRepresentation::kWord8));
FOR_UINT8_INPUTS(i) {
uint8_t expected = *i;
r.builder().WriteMemory(&memory[0], expected);
CHECK_EQ(expected, r.Call());
}
}
TEST(I32AtomicStoreLoad) {
EXPERIMENTAL_FLAG_SCOPE(threads);
WasmRunner<uint32_t, uint32_t> r(kExecuteCompiled);
r.builder().SetHasSharedMemory();
uint32_t* memory = r.builder().AddMemoryElems<uint32_t>(8);
BUILD(r,
WASM_ATOMICS_STORE_OP(kExprI32AtomicStore, WASM_ZERO, WASM_GET_LOCAL(0),
MachineRepresentation::kWord32),
WASM_ATOMICS_LOAD_OP(kExprI32AtomicLoad, WASM_ZERO,
MachineRepresentation::kWord32));
FOR_UINT32_INPUTS(i) {
uint32_t expected = *i;
CHECK_EQ(expected, r.Call(*i));
CHECK_EQ(expected, r.builder().ReadMemory(&memory[0]));
}
}
TEST(I32AtomicStoreLoad16U) {
EXPERIMENTAL_FLAG_SCOPE(threads);
WasmRunner<uint32_t, uint32_t> r(kExecuteCompiled);
r.builder().SetHasSharedMemory();
uint16_t* memory = r.builder().AddMemoryElems<uint16_t>(8);
BUILD(
r,
WASM_ATOMICS_STORE_OP(kExprI32AtomicStore16U, WASM_ZERO,
WASM_GET_LOCAL(0), MachineRepresentation::kWord16),
WASM_ATOMICS_LOAD_OP(kExprI32AtomicLoad16U, WASM_ZERO,
MachineRepresentation::kWord16));
FOR_UINT16_INPUTS(i) {
uint16_t expected = *i;
CHECK_EQ(expected, r.Call(*i));
CHECK_EQ(expected, r.builder().ReadMemory(&memory[0]));
}
}
TEST(I32AtomicStoreLoad8U) {
EXPERIMENTAL_FLAG_SCOPE(threads);
WasmRunner<uint32_t, uint32_t> r(kExecuteCompiled);
r.builder().SetHasSharedMemory();
uint8_t* memory = r.builder().AddMemoryElems<uint8_t>(8);
BUILD(r,
WASM_ATOMICS_STORE_OP(kExprI32AtomicStore8U, WASM_ZERO,
WASM_GET_LOCAL(0), MachineRepresentation::kWord8),
WASM_ATOMICS_LOAD_OP(kExprI32AtomicLoad8U, WASM_ZERO,
MachineRepresentation::kWord8));
FOR_UINT8_INPUTS(i) {
uint8_t expected = *i;
CHECK_EQ(expected, r.Call(*i));
CHECK_EQ(*i, r.builder().ReadMemory(&memory[0]));
}
}
#undef WASM_ATOMICS_OP
#undef WASM_ATOMICS_BINOP
#undef WASM_ATOMICS_TERNARY_OP
#undef WASM_ATOMICS_LOAD_OP
#undef WASM_ATOMICS_STORE_OP
} // namespace wasm
} // namespace internal
......
......@@ -34,8 +34,8 @@ function GetAtomicBinOpFunction(wasmExpression, alignment, offset) {
// Instantiate module, get function exports
let module = new WebAssembly.Module(builder.toBuffer());
let instance = (new WebAssembly.Instance(module,
{m: {imported_mem: memory}}));
let instance = new WebAssembly.Instance(module,
{m: {imported_mem: memory}});
return instance.exports.main;
}
......@@ -53,11 +53,45 @@ function GetAtomicCmpExchangeFunction(wasmExpression, alignment, offset) {
// Instantiate module, get function exports
let module = new WebAssembly.Module(builder.toBuffer());
let instance = (new WebAssembly.Instance(module,
{m: {imported_mem: memory}}));
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;
......@@ -255,3 +289,98 @@ function TestCmpExchange(func, buffer, params, size) {
let params = [0x01, 0x0d, 0xf9];
TestCmpExchange(wasmCmpExchange, i8, params, kMemtypeSize8);
})();
function TestLoad(func, buffer, value, size) {
for (let i = 0; i < buffer.length; 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++) {
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);
})();
......@@ -332,6 +332,12 @@ let kExprF64ReinterpretI64 = 0xbf;
// Prefix opcodes
let kAtomicPrefix = 0xfe;
let kExprI32AtomicLoad = 0x10;
let kExprI32AtomicLoad8U = 0x12;
let kExprI32AtomicLoad16U = 0x13;
let kExprI32AtomicStore = 0x17;
let kExprI32AtomicStore8U = 0x19;
let kExprI32AtomicStore16U = 0x1a;
let kExprI32AtomicAdd = 0x1e;
let kExprI32AtomicAdd8U = 0x20;
let kExprI32AtomicAdd16U = 0x21;
......
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