[wasm] Change the memory access offset to pointer size

TurboFan expects the offset input of a Load or Store node to be a
pointer-size input, i.e. an int32 input on 32-bit platforms, and int64
on 64-bit platforms. In WebAssembly we always provided 32-bit offset
though, which caused problems when the high word of the register which
contained the offset was not empty.

With this CL we change the offset input to int64 on 64-bit platforms.
In addition we also change the type of the memory_size_ node to int64,
so that that we do not have to adjust the type of the memory size at
every memory load.

This CL will cause performance regressions but is necessary for
correctness and to avoid crashes.

R=titzer@chromium.org

Bug: chromium:766666
Change-Id: I5301e108d05e125258d2a06d500c1b75e91697b8
Reviewed-on: https://chromium-review.googlesource.com/723379
Reviewed-by: Ben Titzer <titzer@chromium.org>
Commit-Queue: Andreas Haas <ahaas@chromium.org>
Cr-Commit-Position: refs/heads/master@{#48689}
This commit is contained in:
Andreas Haas 2017-10-18 14:51:49 +02:00 committed by Commit Bot
parent e737b4ce0d
commit 50ecc42cc7
2 changed files with 86 additions and 12 deletions

View File

@ -3215,6 +3215,10 @@ Node* WasmGraphBuilder::LoadMemSize() {
static_cast<int32_t>(offsetof(WasmContext, mem_size))), static_cast<int32_t>(offsetof(WasmContext, mem_size))),
*effect_, *control_); *effect_, *control_);
*effect_ = mem_size; *effect_ = mem_size;
if (jsgraph()->machine()->Is64()) {
mem_size = graph()->NewNode(jsgraph()->machine()->ChangeUint32ToUint64(),
mem_size);
}
return mem_size; return mem_size;
} }
@ -3262,8 +3266,13 @@ Node* WasmGraphBuilder::CurrentMemoryPages() {
// CurrentMemoryPages can not be called from asm.js. // CurrentMemoryPages can not be called from asm.js.
DCHECK_EQ(wasm::kWasmOrigin, env_->module->origin()); DCHECK_EQ(wasm::kWasmOrigin, env_->module->origin());
DCHECK_NOT_NULL(*mem_size_); DCHECK_NOT_NULL(*mem_size_);
Node* mem_size = *mem_size_;
if (jsgraph()->machine()->Is64()) {
mem_size = graph()->NewNode(jsgraph()->machine()->TruncateInt64ToInt32(),
mem_size);
}
return graph()->NewNode( return graph()->NewNode(
jsgraph()->machine()->Word32Shr(), *mem_size_, jsgraph()->machine()->Word32Shr(), mem_size,
jsgraph()->Int32Constant(WhichPowerOf2(wasm::WasmModule::kPageSize))); jsgraph()->Int32Constant(WhichPowerOf2(wasm::WasmModule::kPageSize)));
} }
@ -3405,15 +3414,21 @@ void WasmGraphBuilder::BoundsCheckMem(MachineType memtype, Node* index,
// The end offset is larger than the smallest memory. // The end offset is larger than the smallest memory.
// Dynamically check the end offset against the actual memory size, which // Dynamically check the end offset against the actual memory size, which
// is not known at compile time. // is not known at compile time.
Node* cond = graph()->NewNode( Node* cond;
jsgraph()->machine()->Uint32LessThanOrEqual(), if (jsgraph()->machine()->Is32()) {
jsgraph()->IntPtrConstant(static_cast<uintptr_t>(end_offset)), cond = graph()->NewNode(jsgraph()->machine()->Uint32LessThanOrEqual(),
*mem_size_); jsgraph()->Int32Constant(end_offset), *mem_size_);
} else {
cond = graph()->NewNode(
jsgraph()->machine()->Uint64LessThanOrEqual(),
jsgraph()->Int64Constant(static_cast<int64_t>(end_offset)),
*mem_size_);
}
TrapIfFalse(wasm::kTrapMemOutOfBounds, cond, position); TrapIfFalse(wasm::kTrapMemOutOfBounds, cond, position);
} else { } else {
// The end offset is within the bounds of the smallest memory, so only // The end offset is within the bounds of the smallest memory, so only
// one check is required. Check to see if the index is also a constant. // one check is required. Check to see if the index is also a constant.
Uint32Matcher m(index); UintPtrMatcher m(index);
if (m.HasValue()) { if (m.HasValue()) {
uint64_t index_val = m.Value(); uint64_t index_val = m.Value();
if ((index_val + offset + access_size) <= min_size) { if ((index_val + offset + access_size) <= min_size) {
@ -3424,12 +3439,22 @@ void WasmGraphBuilder::BoundsCheckMem(MachineType memtype, Node* index,
} }
} }
Node* effective_size = Node* effective_size;
graph()->NewNode(jsgraph()->machine()->Int32Sub(), *mem_size_, if (jsgraph()->machine()->Is32()) {
jsgraph()->Int32Constant(end_offset - 1)); effective_size =
graph()->NewNode(jsgraph()->machine()->Int32Sub(), *mem_size_,
jsgraph()->Int32Constant(end_offset - 1));
} else {
effective_size = graph()->NewNode(
jsgraph()->machine()->Int64Sub(), *mem_size_,
jsgraph()->Int64Constant(static_cast<int64_t>(end_offset - 1)));
}
Node* cond = graph()->NewNode(jsgraph()->machine()->Uint32LessThan(), index, const Operator* less = jsgraph()->machine()->Is32()
effective_size); ? jsgraph()->machine()->Uint32LessThan()
: jsgraph()->machine()->Uint64LessThan();
Node* cond = graph()->NewNode(less, index, effective_size);
TrapIfFalse(wasm::kTrapMemOutOfBounds, cond, position); TrapIfFalse(wasm::kTrapMemOutOfBounds, cond, position);
} }
@ -3483,6 +3508,10 @@ Node* WasmGraphBuilder::LoadMem(wasm::ValueType type, MachineType memtype,
wasm::WasmCodePosition position) { wasm::WasmCodePosition position) {
Node* load; Node* load;
if (jsgraph()->machine()->Is64()) {
index =
graph()->NewNode(jsgraph()->machine()->ChangeUint32ToUint64(), index);
}
// Wasm semantics throw on OOB. Introduce explicit bounds check. // Wasm semantics throw on OOB. Introduce explicit bounds check.
if (!FLAG_wasm_trap_handler || !V8_TRAP_HANDLER_SUPPORTED) { if (!FLAG_wasm_trap_handler || !V8_TRAP_HANDLER_SUPPORTED) {
BoundsCheckMem(memtype, index, offset, position); BoundsCheckMem(memtype, index, offset, position);
@ -3538,6 +3567,10 @@ Node* WasmGraphBuilder::StoreMem(MachineType memtype, Node* index,
wasm::ValueType type) { wasm::ValueType type) {
Node* store; Node* store;
if (jsgraph()->machine()->Is64()) {
index =
graph()->NewNode(jsgraph()->machine()->ChangeUint32ToUint64(), index);
}
// Wasm semantics throw on OOB. Introduce explicit bounds check. // Wasm semantics throw on OOB. Introduce explicit bounds check.
if (!FLAG_wasm_trap_handler || !V8_TRAP_HANDLER_SUPPORTED) { if (!FLAG_wasm_trap_handler || !V8_TRAP_HANDLER_SUPPORTED) {
BoundsCheckMem(memtype, index, offset, position); BoundsCheckMem(memtype, index, offset, position);
@ -3584,6 +3617,10 @@ Node* WasmGraphBuilder::BuildAsmjsLoadMem(MachineType type, Node* index) {
// asm.js semantics use CheckedLoad (i.e. OOB reads return 0ish). // asm.js semantics use CheckedLoad (i.e. OOB reads return 0ish).
DCHECK_NOT_NULL(*mem_size_); DCHECK_NOT_NULL(*mem_size_);
DCHECK_NOT_NULL(*mem_start_); DCHECK_NOT_NULL(*mem_start_);
if (jsgraph()->machine()->Is64()) {
index =
graph()->NewNode(jsgraph()->machine()->ChangeUint32ToUint64(), index);
}
const Operator* op = jsgraph()->machine()->CheckedLoad(type); const Operator* op = jsgraph()->machine()->CheckedLoad(type);
Node* load = Node* load =
graph()->NewNode(op, *mem_start_, index, *mem_size_, *effect_, *control_); graph()->NewNode(op, *mem_start_, index, *mem_size_, *effect_, *control_);
@ -3597,6 +3634,10 @@ Node* WasmGraphBuilder::BuildAsmjsStoreMem(MachineType type, Node* index,
// asm.js semantics use CheckedStore (i.e. ignore OOB writes). // asm.js semantics use CheckedStore (i.e. ignore OOB writes).
DCHECK_NOT_NULL(*mem_size_); DCHECK_NOT_NULL(*mem_size_);
DCHECK_NOT_NULL(*mem_start_); DCHECK_NOT_NULL(*mem_start_);
if (jsgraph()->machine()->Is64()) {
index =
graph()->NewNode(jsgraph()->machine()->ChangeUint32ToUint64(), index);
}
const Operator* op = const Operator* op =
jsgraph()->machine()->CheckedStore(type.representation()); jsgraph()->machine()->CheckedStore(type.representation());
Node* store = graph()->NewNode(op, *mem_start_, index, *mem_size_, val, Node* store = graph()->NewNode(op, *mem_start_, index, *mem_size_, val,
@ -3617,7 +3658,7 @@ Node* WasmGraphBuilder::String(const char* string) {
Graph* WasmGraphBuilder::graph() { return jsgraph()->graph(); } Graph* WasmGraphBuilder::graph() { return jsgraph()->graph(); }
void WasmGraphBuilder::LowerInt64() { void WasmGraphBuilder::LowerInt64() {
if (!jsgraph()->machine()->Is32()) return; if (jsgraph()->machine()->Is64()) return;
Int64Lowering r(jsgraph()->graph(), jsgraph()->machine(), jsgraph()->common(), Int64Lowering r(jsgraph()->graph(), jsgraph()->machine(), jsgraph()->common(),
jsgraph()->zone(), sig_); jsgraph()->zone(), sig_);
r.LowerGraph(); r.LowerGraph();

View File

@ -0,0 +1,33 @@
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
load("test/mjsunit/wasm/wasm-constants.js");
load("test/mjsunit/wasm/wasm-module-builder.js");
const builder = new WasmModuleBuilder();
builder.addMemory(1, kV8MaxPages, false);
builder.addFunction('load', kSig_i_ii)
.addBody([
kExprGetLocal, 0,
kExprI64SConvertI32,
kExprGetLocal, 1,
kExprI64SConvertI32,
kExprI64Shl,
kExprI32ConvertI64,
kExprI32LoadMem, 0, 0])
.exportFunc();
const module = builder.instantiate();
let start = 12;
let address = start;
for (i = 1; i < 64; i++) {
// This is the address which will be accessed in the code. We cannot use
// shifts to calculate the address because JS shifts work on 32-bit integers.
address = (address * 2) % 4294967296;
if (address < kPageSize) {
assertEquals(0, module.exports.load(start, i));
} else {
assertTraps(kTrapMemOutOfBounds, _ => { module.exports.load(start, i);});
}
}