Commit f00efdae authored by titzer's avatar titzer Committed by Commit bot

[wasm] Introduce custom asm.js bytecodes for loads and stores.

R=ahaas@chromium.org,bradnelson@chromium.org
BUG=

Review-Url: https://codereview.chromium.org/1968943002
Cr-Commit-Position: refs/heads/master@{#36169}
parent ced492a6
......@@ -628,6 +628,16 @@ Node* WasmGraphBuilder::Binop(wasm::WasmOpcode opcode, Node* left, Node* right,
return BuildI32AsmjsRemS(left, right);
case wasm::kExprI32AsmjsRemU:
return BuildI32AsmjsRemU(left, right);
case wasm::kExprI32AsmjsStoreMem8:
return BuildAsmjsStoreMem(MachineType::Int8(), left, right);
case wasm::kExprI32AsmjsStoreMem16:
return BuildAsmjsStoreMem(MachineType::Int16(), left, right);
case wasm::kExprI32AsmjsStoreMem:
return BuildAsmjsStoreMem(MachineType::Int32(), left, right);
case wasm::kExprF32AsmjsStoreMem:
return BuildAsmjsStoreMem(MachineType::Float32(), left, right);
case wasm::kExprF64AsmjsStoreMem:
return BuildAsmjsStoreMem(MachineType::Float64(), left, right);
default:
op = UnsupportedOpcode(opcode);
}
......@@ -857,18 +867,28 @@ Node* WasmGraphBuilder::Unop(wasm::WasmOpcode opcode, Node* input,
}
op = m->RoundUint64ToFloat64();
break;
case wasm::kExprI64SConvertF32: {
case wasm::kExprI64SConvertF32:
return BuildI64SConvertF32(input, position);
}
case wasm::kExprI64SConvertF64: {
case wasm::kExprI64SConvertF64:
return BuildI64SConvertF64(input, position);
}
case wasm::kExprI64UConvertF32: {
case wasm::kExprI64UConvertF32:
return BuildI64UConvertF32(input, position);
}
case wasm::kExprI64UConvertF64: {
case wasm::kExprI64UConvertF64:
return BuildI64UConvertF64(input, position);
}
case wasm::kExprI32AsmjsLoadMem8S:
return BuildAsmjsLoadMem(MachineType::Int8(), input);
case wasm::kExprI32AsmjsLoadMem8U:
return BuildAsmjsLoadMem(MachineType::Uint8(), input);
case wasm::kExprI32AsmjsLoadMem16S:
return BuildAsmjsLoadMem(MachineType::Int16(), input);
case wasm::kExprI32AsmjsLoadMem16U:
return BuildAsmjsLoadMem(MachineType::Uint16(), input);
case wasm::kExprI32AsmjsLoadMem:
return BuildAsmjsLoadMem(MachineType::Int32(), input);
case wasm::kExprF32AsmjsLoadMem:
return BuildAsmjsLoadMem(MachineType::Float32(), input);
case wasm::kExprF64AsmjsLoadMem:
return BuildAsmjsLoadMem(MachineType::Float64(), input);
default:
op = UnsupportedOpcode(opcode);
}
......@@ -2631,19 +2651,10 @@ Node* WasmGraphBuilder::LoadMem(wasm::LocalType type, MachineType memtype,
Node* index, uint32_t offset,
wasm::WasmCodePosition position) {
Node* load;
if (module_ && module_->asm_js()) {
// asm.js semantics use CheckedLoad (i.e. OOB reads return 0ish).
DCHECK_EQ(0, offset);
const Operator* op = jsgraph()->machine()->CheckedLoad(memtype);
load = graph()->NewNode(op, MemBuffer(0), index, MemSize(0), *effect_,
*control_);
} else {
// WASM semantics throw on OOB. Introduce explicit bounds check.
BoundsCheckMem(memtype, index, offset, position);
load = graph()->NewNode(jsgraph()->machine()->Load(memtype),
MemBuffer(offset), index, *effect_, *control_);
}
// WASM semantics throw on OOB. Introduce explicit bounds check.
BoundsCheckMem(memtype, index, offset, position);
load = graph()->NewNode(jsgraph()->machine()->Load(memtype),
MemBuffer(offset), index, *effect_, *control_);
*effect_ = load;
......@@ -2667,25 +2678,36 @@ Node* WasmGraphBuilder::StoreMem(MachineType memtype, Node* index,
uint32_t offset, Node* val,
wasm::WasmCodePosition position) {
Node* store;
if (module_ && module_->asm_js()) {
// asm.js semantics use CheckedStore (i.e. ignore OOB writes).
DCHECK_EQ(0, offset);
const Operator* op =
jsgraph()->machine()->CheckedStore(memtype.representation());
store = graph()->NewNode(op, MemBuffer(0), index, MemSize(0), val, *effect_,
*control_);
} else {
// WASM semantics throw on OOB. Introduce explicit bounds check.
BoundsCheckMem(memtype, index, offset, position);
StoreRepresentation rep(memtype.representation(), kNoWriteBarrier);
store =
graph()->NewNode(jsgraph()->machine()->Store(rep), MemBuffer(offset),
index, val, *effect_, *control_);
}
// WASM semantics throw on OOB. Introduce explicit bounds check.
BoundsCheckMem(memtype, index, offset, position);
StoreRepresentation rep(memtype.representation(), kNoWriteBarrier);
store = graph()->NewNode(jsgraph()->machine()->Store(rep), MemBuffer(offset),
index, val, *effect_, *control_);
*effect_ = store;
return store;
}
Node* WasmGraphBuilder::BuildAsmjsLoadMem(MachineType type, Node* index) {
// TODO(turbofan): fold bounds checks for constant asm.js loads.
// asm.js semantics use CheckedLoad (i.e. OOB reads return 0ish).
const Operator* op = jsgraph()->machine()->CheckedLoad(type);
Node* load = graph()->NewNode(op, MemBuffer(0), index, MemSize(0), *effect_,
*control_);
*effect_ = load;
return load;
}
Node* WasmGraphBuilder::BuildAsmjsStoreMem(MachineType type, Node* index,
Node* val) {
// TODO(turbofan): fold bounds checks for constant asm.js stores.
// asm.js semantics use CheckedStore (i.e. ignore OOB writes).
const Operator* op =
jsgraph()->machine()->CheckedStore(type.representation());
Node* store = graph()->NewNode(op, MemBuffer(0), index, MemSize(0), val,
*effect_, *control_);
*effect_ = store;
return val;
}
void WasmGraphBuilder::PrintDebugName(Node* node) {
PrintF("#%d:%s", node->id(), node->op()->mnemonic());
......
......@@ -300,6 +300,8 @@ class WasmGraphBuilder {
Node* BuildI32AsmjsRemS(Node* left, Node* right);
Node* BuildI32AsmjsDivU(Node* left, Node* right);
Node* BuildI32AsmjsRemU(Node* left, Node* right);
Node* BuildAsmjsLoadMem(MachineType type, Node* index);
Node* BuildAsmjsStoreMem(MachineType type, Node* index, Node* val);
Node** Realloc(Node** buffer, size_t old_count, size_t new_count) {
Node** buf = Buffer(new_count);
......
......@@ -737,7 +737,7 @@ class AsmWasmBuilderImpl : public AstVisitor {
RECURSE(Visit(value));
}
void EmitAssignment(Assignment* expr, MachineType mtype) {
void EmitAssignment(Assignment* expr, MachineType type) {
// Match the left hand side of the assignment.
VariableProxy* target_var = expr->target()->AsVariableProxy();
if (target_var != nullptr) {
......@@ -762,8 +762,27 @@ class AsmWasmBuilderImpl : public AstVisitor {
cache_.kFloat32Array)) {
current_function_builder_->Emit(kExprF32ConvertF64);
}
current_function_builder_->EmitWithU8U8(
WasmOpcodes::LoadStoreOpcodeOf(mtype, true), 0, 0);
WasmOpcode opcode;
if (type == MachineType::Int8()) {
opcode = kExprI32AsmjsStoreMem8;
} else if (type == MachineType::Uint8()) {
opcode = kExprI32AsmjsStoreMem8;
} else if (type == MachineType::Int16()) {
opcode = kExprI32AsmjsStoreMem16;
} else if (type == MachineType::Uint16()) {
opcode = kExprI32AsmjsStoreMem16;
} else if (type == MachineType::Int32()) {
opcode = kExprI32AsmjsStoreMem;
} else if (type == MachineType::Uint32()) {
opcode = kExprI32AsmjsStoreMem;
} else if (type == MachineType::Float32()) {
opcode = kExprF32AsmjsStoreMem;
} else if (type == MachineType::Float64()) {
opcode = kExprF64AsmjsStoreMem;
} else {
UNREACHABLE();
}
current_function_builder_->Emit(opcode);
}
if (target_var == nullptr && target_prop == nullptr) {
......@@ -941,10 +960,30 @@ class AsmWasmBuilderImpl : public AstVisitor {
}
void VisitProperty(Property* expr) {
MachineType mtype;
VisitPropertyAndEmitIndex(expr, &mtype);
current_function_builder_->EmitWithU8U8(
WasmOpcodes::LoadStoreOpcodeOf(mtype, false), 0, 0);
MachineType type;
VisitPropertyAndEmitIndex(expr, &type);
WasmOpcode opcode;
if (type == MachineType::Int8()) {
opcode = kExprI32AsmjsLoadMem8S;
} else if (type == MachineType::Uint8()) {
opcode = kExprI32AsmjsLoadMem8U;
} else if (type == MachineType::Int16()) {
opcode = kExprI32AsmjsLoadMem16S;
} else if (type == MachineType::Uint16()) {
opcode = kExprI32AsmjsLoadMem16U;
} else if (type == MachineType::Int32()) {
opcode = kExprI32AsmjsLoadMem;
} else if (type == MachineType::Uint32()) {
opcode = kExprI32AsmjsLoadMem;
} else if (type == MachineType::Float32()) {
opcode = kExprF32AsmjsLoadMem;
} else if (type == MachineType::Float64()) {
opcode = kExprF64AsmjsLoadMem;
} else {
UNREACHABLE();
}
current_function_builder_->Emit(opcode);
}
bool VisitStdlibFunction(Call* call, VariableProxy* expr) {
......
......@@ -261,26 +261,23 @@ const WasmCodePosition kNoCodePosition = -1;
V(I32AsmjsDivU, 0xd1, i_ii) \
V(I32AsmjsRemS, 0xd2, i_ii) \
V(I32AsmjsRemU, 0xd3, i_ii) \
V(I32AsmjsLoadMem8S, 0xd4, i_i) \
V(I32AsmjsLoadMem8U, 0xd5, i_i) \
V(I32AsmjsLoadMem16S, 0xd6, i_i) \
V(I32AsmjsLoadMem16U, 0xd7, i_i) \
V(I32AsmjsLoadMem, 0xd8, i_i) \
V(F32AsmjsLoadMem, 0xd9, f_i) \
V(F64AsmjsLoadMem, 0xda, d_i) \
V(I32AsmjsStoreMem8, 0xdb, i_ii) \
V(I32AsmjsStoreMem16, 0xdc, i_ii) \
V(I32AsmjsStoreMem, 0xdd, i_ii) \
V(F32AsmjsStoreMem, 0xde, f_if) \
V(F64AsmjsStoreMem, 0xdf, d_id) \
V(I32AsmjsSConvertF32, 0xe0, i_f) \
V(I32AsmjsUConvertF32, 0xe1, i_f) \
V(I32AsmjsSConvertF64, 0xe2, i_d) \
V(I32AsmjsUConvertF64, 0xe3, i_d)
/* TODO(titzer): introduce compatibility opcodes for these asm.js ops \
V(I32AsmjsLoad8S, 0xd4, i_i) \ \
V(I32AsmjsLoad8U, 0xd5, i_i) \ \
V(I32AsmjsLoad16S, 0xd6, i_i) \ \
V(I32AsmjsLoad16U, 0xd7, i_i) \ \
V(I32AsmjsLoad, 0xd8, i_i) \ \
V(F32AsmjsLoad, 0xd9, f_i) \ \
V(F64AsmjsLoad, 0xda, d_i) \ \
V(I32AsmjsStore8, 0xdb, i_i) \ \
V(I32AsmjsStore16, 0xdc, i_i) \ \
V(I32AsmjsStore, 0xdd, i_ii) \ \
V(F32AsmjsStore, 0xde, i_if) \ \
V(F64AsmjsStore, 0xdf, i_id) \ \
*/
// All opcodes.
#define FOREACH_OPCODE(V) \
FOREACH_CONTROL_OPCODE(V) \
......
......@@ -117,7 +117,7 @@ TEST(Run_Wasm_LoadMemI32_oob_asm) {
WasmRunner<int32_t> r(&module, MachineType::Uint32());
module.RandomizeMemory(1112);
BUILD(r, WASM_LOAD_MEM(MachineType::Int32(), WASM_GET_LOCAL(0)));
BUILD(r, WASM_UNOP(kExprI32AsmjsLoadMem, WASM_GET_LOCAL(0)));
memory[0] = 999999;
CHECK_EQ(999999, r.Call(0u));
......@@ -130,3 +130,70 @@ TEST(Run_Wasm_LoadMemI32_oob_asm) {
CHECK_EQ(0, r.Call(offset));
}
}
TEST(Run_Wasm_LoadMemF32_oob_asm) {
TestingModule module;
module.origin = kAsmJsOrigin;
float* memory = module.AddMemoryElems<float>(8);
WasmRunner<float> r(&module, MachineType::Uint32());
module.RandomizeMemory(1112);
BUILD(r, WASM_UNOP(kExprF32AsmjsLoadMem, WASM_GET_LOCAL(0)));
memory[0] = 9999.5f;
CHECK_EQ(9999.5f, r.Call(0u));
// TODO(titzer): offset 29-31 should also be OOB.
for (uint32_t offset = 32; offset < 40; offset++) {
CHECK(std::isnan(r.Call(offset)));
}
for (uint32_t offset = 0x80000000; offset < 0x80000010; offset++) {
CHECK(std::isnan(r.Call(offset)));
}
}
TEST(Run_Wasm_LoadMemF64_oob_asm) {
TestingModule module;
module.origin = kAsmJsOrigin;
double* memory = module.AddMemoryElems<double>(8);
WasmRunner<double> r(&module, MachineType::Uint32());
module.RandomizeMemory(1112);
BUILD(r, WASM_UNOP(kExprF64AsmjsLoadMem, WASM_GET_LOCAL(0)));
memory[0] = 9799.5;
CHECK_EQ(9799.5, r.Call(0u));
memory[1] = 11799.25;
CHECK_EQ(11799.25, r.Call(8u));
// TODO(titzer): offset 57-63 should also be OOB.
for (uint32_t offset = 64; offset < 80; offset++) {
CHECK(std::isnan(r.Call(offset)));
}
for (uint32_t offset = 0x80000000; offset < 0x80000010; offset++) {
CHECK(std::isnan(r.Call(offset)));
}
}
TEST(Run_Wasm_StoreMemI32_oob_asm) {
TestingModule module;
module.origin = kAsmJsOrigin;
int32_t* memory = module.AddMemoryElems<int32_t>(8);
WasmRunner<int32_t> r(&module, MachineType::Uint32(), MachineType::Uint32());
module.RandomizeMemory(1112);
BUILD(r, WASM_BINOP(kExprI32AsmjsStoreMem, WASM_GET_LOCAL(0),
WASM_GET_LOCAL(1)));
memory[0] = 7777;
CHECK_EQ(999999, r.Call(0u, 999999));
CHECK_EQ(999999, memory[0]);
// TODO(titzer): offset 29-31 should also be OOB.
for (uint32_t offset = 32; offset < 40; offset++) {
CHECK_EQ(8888, r.Call(offset, 8888));
}
for (uint32_t offset = 0x10000000; offset < 0xF0000000; offset += 0x1000000) {
CHECK_EQ(7777, r.Call(offset, 7777));
}
}
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