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, ...@@ -628,6 +628,16 @@ Node* WasmGraphBuilder::Binop(wasm::WasmOpcode opcode, Node* left, Node* right,
return BuildI32AsmjsRemS(left, right); return BuildI32AsmjsRemS(left, right);
case wasm::kExprI32AsmjsRemU: case wasm::kExprI32AsmjsRemU:
return BuildI32AsmjsRemU(left, right); 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: default:
op = UnsupportedOpcode(opcode); op = UnsupportedOpcode(opcode);
} }
...@@ -857,18 +867,28 @@ Node* WasmGraphBuilder::Unop(wasm::WasmOpcode opcode, Node* input, ...@@ -857,18 +867,28 @@ Node* WasmGraphBuilder::Unop(wasm::WasmOpcode opcode, Node* input,
} }
op = m->RoundUint64ToFloat64(); op = m->RoundUint64ToFloat64();
break; break;
case wasm::kExprI64SConvertF32: { case wasm::kExprI64SConvertF32:
return BuildI64SConvertF32(input, position); return BuildI64SConvertF32(input, position);
} case wasm::kExprI64SConvertF64:
case wasm::kExprI64SConvertF64: {
return BuildI64SConvertF64(input, position); return BuildI64SConvertF64(input, position);
} case wasm::kExprI64UConvertF32:
case wasm::kExprI64UConvertF32: {
return BuildI64UConvertF32(input, position); return BuildI64UConvertF32(input, position);
} case wasm::kExprI64UConvertF64:
case wasm::kExprI64UConvertF64: {
return BuildI64UConvertF64(input, position); 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: default:
op = UnsupportedOpcode(opcode); op = UnsupportedOpcode(opcode);
} }
...@@ -2631,19 +2651,10 @@ Node* WasmGraphBuilder::LoadMem(wasm::LocalType type, MachineType memtype, ...@@ -2631,19 +2651,10 @@ Node* WasmGraphBuilder::LoadMem(wasm::LocalType type, MachineType memtype,
Node* index, uint32_t offset, Node* index, uint32_t offset,
wasm::WasmCodePosition position) { wasm::WasmCodePosition position) {
Node* load; Node* load;
// WASM semantics throw on OOB. Introduce explicit bounds check.
if (module_ && module_->asm_js()) { BoundsCheckMem(memtype, index, offset, position);
// asm.js semantics use CheckedLoad (i.e. OOB reads return 0ish). load = graph()->NewNode(jsgraph()->machine()->Load(memtype),
DCHECK_EQ(0, offset); MemBuffer(offset), index, *effect_, *control_);
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_);
}
*effect_ = load; *effect_ = load;
...@@ -2667,25 +2678,36 @@ Node* WasmGraphBuilder::StoreMem(MachineType memtype, Node* index, ...@@ -2667,25 +2678,36 @@ Node* WasmGraphBuilder::StoreMem(MachineType memtype, Node* index,
uint32_t offset, Node* val, uint32_t offset, Node* val,
wasm::WasmCodePosition position) { wasm::WasmCodePosition position) {
Node* store; Node* store;
if (module_ && module_->asm_js()) { // WASM semantics throw on OOB. Introduce explicit bounds check.
// asm.js semantics use CheckedStore (i.e. ignore OOB writes). BoundsCheckMem(memtype, index, offset, position);
DCHECK_EQ(0, offset); StoreRepresentation rep(memtype.representation(), kNoWriteBarrier);
const Operator* op = store = graph()->NewNode(jsgraph()->machine()->Store(rep), MemBuffer(offset),
jsgraph()->machine()->CheckedStore(memtype.representation()); index, val, *effect_, *control_);
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_);
}
*effect_ = store; *effect_ = store;
return 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) { void WasmGraphBuilder::PrintDebugName(Node* node) {
PrintF("#%d:%s", node->id(), node->op()->mnemonic()); PrintF("#%d:%s", node->id(), node->op()->mnemonic());
......
...@@ -300,6 +300,8 @@ class WasmGraphBuilder { ...@@ -300,6 +300,8 @@ class WasmGraphBuilder {
Node* BuildI32AsmjsRemS(Node* left, Node* right); Node* BuildI32AsmjsRemS(Node* left, Node* right);
Node* BuildI32AsmjsDivU(Node* left, Node* right); Node* BuildI32AsmjsDivU(Node* left, Node* right);
Node* BuildI32AsmjsRemU(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** Realloc(Node** buffer, size_t old_count, size_t new_count) {
Node** buf = Buffer(new_count); Node** buf = Buffer(new_count);
......
...@@ -737,7 +737,7 @@ class AsmWasmBuilderImpl : public AstVisitor { ...@@ -737,7 +737,7 @@ class AsmWasmBuilderImpl : public AstVisitor {
RECURSE(Visit(value)); RECURSE(Visit(value));
} }
void EmitAssignment(Assignment* expr, MachineType mtype) { void EmitAssignment(Assignment* expr, MachineType type) {
// Match the left hand side of the assignment. // Match the left hand side of the assignment.
VariableProxy* target_var = expr->target()->AsVariableProxy(); VariableProxy* target_var = expr->target()->AsVariableProxy();
if (target_var != nullptr) { if (target_var != nullptr) {
...@@ -762,8 +762,27 @@ class AsmWasmBuilderImpl : public AstVisitor { ...@@ -762,8 +762,27 @@ class AsmWasmBuilderImpl : public AstVisitor {
cache_.kFloat32Array)) { cache_.kFloat32Array)) {
current_function_builder_->Emit(kExprF32ConvertF64); current_function_builder_->Emit(kExprF32ConvertF64);
} }
current_function_builder_->EmitWithU8U8( WasmOpcode opcode;
WasmOpcodes::LoadStoreOpcodeOf(mtype, true), 0, 0); 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) { if (target_var == nullptr && target_prop == nullptr) {
...@@ -941,10 +960,30 @@ class AsmWasmBuilderImpl : public AstVisitor { ...@@ -941,10 +960,30 @@ class AsmWasmBuilderImpl : public AstVisitor {
} }
void VisitProperty(Property* expr) { void VisitProperty(Property* expr) {
MachineType mtype; MachineType type;
VisitPropertyAndEmitIndex(expr, &mtype); VisitPropertyAndEmitIndex(expr, &type);
current_function_builder_->EmitWithU8U8( WasmOpcode opcode;
WasmOpcodes::LoadStoreOpcodeOf(mtype, false), 0, 0); 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) { bool VisitStdlibFunction(Call* call, VariableProxy* expr) {
......
...@@ -261,26 +261,23 @@ const WasmCodePosition kNoCodePosition = -1; ...@@ -261,26 +261,23 @@ const WasmCodePosition kNoCodePosition = -1;
V(I32AsmjsDivU, 0xd1, i_ii) \ V(I32AsmjsDivU, 0xd1, i_ii) \
V(I32AsmjsRemS, 0xd2, i_ii) \ V(I32AsmjsRemS, 0xd2, i_ii) \
V(I32AsmjsRemU, 0xd3, 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(I32AsmjsSConvertF32, 0xe0, i_f) \
V(I32AsmjsUConvertF32, 0xe1, i_f) \ V(I32AsmjsUConvertF32, 0xe1, i_f) \
V(I32AsmjsSConvertF64, 0xe2, i_d) \ V(I32AsmjsSConvertF64, 0xe2, i_d) \
V(I32AsmjsUConvertF64, 0xe3, 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. // All opcodes.
#define FOREACH_OPCODE(V) \ #define FOREACH_OPCODE(V) \
FOREACH_CONTROL_OPCODE(V) \ FOREACH_CONTROL_OPCODE(V) \
......
...@@ -117,7 +117,7 @@ TEST(Run_Wasm_LoadMemI32_oob_asm) { ...@@ -117,7 +117,7 @@ TEST(Run_Wasm_LoadMemI32_oob_asm) {
WasmRunner<int32_t> r(&module, MachineType::Uint32()); WasmRunner<int32_t> r(&module, MachineType::Uint32());
module.RandomizeMemory(1112); 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; memory[0] = 999999;
CHECK_EQ(999999, r.Call(0u)); CHECK_EQ(999999, r.Call(0u));
...@@ -130,3 +130,70 @@ TEST(Run_Wasm_LoadMemI32_oob_asm) { ...@@ -130,3 +130,70 @@ TEST(Run_Wasm_LoadMemI32_oob_asm) {
CHECK_EQ(0, r.Call(offset)); 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