Commit 94b4ad93 authored by Ben L. Titzer's avatar Ben L. Titzer Committed by Commit Bot

[wasm] Introduce WasmContextCacheNodes to simplify WasmGraphBuilder interface.

This CL introduces a small struct to hold the {mem_start} and {mem_size}
node pointers that are managed in the function body decoder's SSA environment.
This struct insulates the function body decoder from further changes in
how context-specific information is represented in the compiler.

R=clemensh@chromium.org
CC=​mstarzinger@chromium.org

Bug: 
Change-Id: If17bef9fd2490ac11e4f3b3614f91333bb0b9528
Reviewed-on: https://chromium-review.googlesource.com/817282Reviewed-by: 's avatarClemens Hammacher <clemensh@chromium.org>
Commit-Queue: Ben L. Titzer <titzer@google.com>
Cr-Commit-Position: refs/heads/master@{#50004}
parent e33a911a
......@@ -122,10 +122,6 @@ Node* WasmGraphBuilder::Terminate(Node* effect, Node* control) {
return terminate;
}
unsigned WasmGraphBuilder::InputCount(Node* node) {
return static_cast<unsigned>(node->InputCount());
}
bool WasmGraphBuilder::IsPhiWithMerge(Node* phi, Node* merge) {
return phi && IrOpcode::IsPhiOpcode(phi->opcode()) &&
NodeProperties::GetControlInput(phi) == merge;
......@@ -3230,28 +3226,21 @@ void WasmGraphBuilder::BuildCWasmEntry(Address wasm_context_address) {
}
}
// This function is used by WasmFullDecoder to create a node that loads the
// {mem_start} variable from the WasmContext. It should not be used directly by
// the WasmGraphBuilder. The WasmGraphBuilder should directly use {mem_start_},
// which will always contain the correct node (stored in the SsaEnv).
Node* WasmGraphBuilder::LoadMemStart() {
void WasmGraphBuilder::InitContextCache(WasmContextCacheNodes* context_cache) {
DCHECK_NOT_NULL(wasm_context_);
Node* mem_buffer = graph()->NewNode(
DCHECK_NOT_NULL(*control_);
DCHECK_NOT_NULL(*effect_);
// Load the memory start.
Node* mem_start = graph()->NewNode(
jsgraph()->machine()->Load(MachineType::UintPtr()), wasm_context_,
jsgraph()->Int32Constant(
static_cast<int32_t>(offsetof(WasmContext, mem_start))),
*effect_, *control_);
*effect_ = mem_buffer;
return mem_buffer;
}
*effect_ = mem_start;
// This function is used by WasmFullDecoder to create a node that loads the
// {mem_size} variable from the WasmContext. It should not be used directly by
// the WasmGraphBuilder. The WasmGraphBuilder should directly use {mem_size_},
// which will always contain the correct node (stored in the SsaEnv).
Node* WasmGraphBuilder::LoadMemSize() {
// Load mem_size from the WasmContext at runtime.
DCHECK_NOT_NULL(wasm_context_);
context_cache->mem_start = mem_start;
// Load the memory size.
Node* mem_size = graph()->NewNode(
jsgraph()->machine()->Load(MachineType::Uint32()), wasm_context_,
jsgraph()->Int32Constant(
......@@ -3262,7 +3251,69 @@ Node* WasmGraphBuilder::LoadMemSize() {
mem_size = graph()->NewNode(jsgraph()->machine()->ChangeUint32ToUint64(),
mem_size);
}
return mem_size;
context_cache->mem_size = mem_size;
}
void WasmGraphBuilder::PrepareContextCacheForLoop(
WasmContextCacheNodes* context_cache, Node* control) {
// Introduce phis for mem_size and mem_start.
context_cache->mem_size =
Phi(MachineRepresentation::kWord32, 1, &context_cache->mem_size, control);
context_cache->mem_start = Phi(MachineType::PointerRepresentation(), 1,
&context_cache->mem_start, control);
}
void WasmGraphBuilder::NewContextCacheMerge(WasmContextCacheNodes* to,
WasmContextCacheNodes* from,
Node* merge) {
if (to->mem_size != from->mem_size) {
Node* vals[] = {to->mem_size, from->mem_size};
to->mem_size = Phi(MachineRepresentation::kWord32, 2, vals, merge);
}
if (to->mem_start != from->mem_start) {
Node* vals[] = {to->mem_start, from->mem_start};
to->mem_start = Phi(MachineType::PointerRepresentation(), 2, vals, merge);
}
}
void WasmGraphBuilder::MergeContextCacheInto(WasmContextCacheNodes* to,
WasmContextCacheNodes* from,
Node* merge) {
to->mem_size = CreateOrMergeIntoPhi(MachineRepresentation::kWord32, merge,
to->mem_size, from->mem_size);
to->mem_start = CreateOrMergeIntoPhi(MachineType::PointerRepresentation(),
merge, to->mem_start, from->mem_start);
}
Node* WasmGraphBuilder::CreateOrMergeIntoPhi(wasm::ValueType type, Node* merge,
Node* tnode, Node* fnode) {
if (IsPhiWithMerge(tnode, merge)) {
AppendToPhi(tnode, fnode);
} else if (tnode != fnode) {
uint32_t count = merge->InputCount();
Node** vals = Buffer(count);
for (uint32_t j = 0; j < count - 1; j++) vals[j] = tnode;
vals[count - 1] = fnode;
return Phi(type, count, vals, merge);
}
return tnode;
}
Node* WasmGraphBuilder::CreateOrMergeIntoEffectPhi(Node* merge, Node* tnode,
Node* fnode) {
if (IsPhiWithMerge(tnode, merge)) {
AppendToPhi(tnode, fnode);
} else if (tnode != fnode) {
uint32_t count = merge->InputCount();
Node** effects = Buffer(count);
for (uint32_t j = 0; j < count - 1; j++) {
effects[j] = tnode;
}
effects[count - 1] = fnode;
tnode = EffectPhi(count, effects, merge);
}
return tnode;
}
void WasmGraphBuilder::GetGlobalBaseAndOffset(MachineType mem_type,
......@@ -3299,17 +3350,20 @@ void WasmGraphBuilder::GetGlobalBaseAndOffset(MachineType mem_type,
}
Node* WasmGraphBuilder::MemBuffer(uint32_t offset) {
DCHECK_NOT_NULL(*mem_start_);
if (offset == 0) return *mem_start_;
return graph()->NewNode(jsgraph()->machine()->IntAdd(), *mem_start_,
DCHECK_NOT_NULL(context_cache_);
Node* mem_start = context_cache_->mem_start;
DCHECK_NOT_NULL(mem_start);
if (offset == 0) return mem_start;
return graph()->NewNode(jsgraph()->machine()->IntAdd(), mem_start,
jsgraph()->IntPtrConstant(offset));
}
Node* WasmGraphBuilder::CurrentMemoryPages() {
// CurrentMemoryPages can not be called from asm.js.
DCHECK_EQ(wasm::kWasmOrigin, env_->module->origin());
DCHECK_NOT_NULL(*mem_size_);
Node* mem_size = *mem_size_;
DCHECK_NOT_NULL(context_cache_);
Node* mem_size = context_cache_->mem_size;
DCHECK_NOT_NULL(mem_size);
if (jsgraph()->machine()->Is64()) {
mem_size = graph()->NewNode(jsgraph()->machine()->TruncateInt64ToInt32(),
mem_size);
......@@ -3435,7 +3489,9 @@ void WasmGraphBuilder::BoundsCheckMem(MachineType memtype, Node* index,
uint32_t offset,
wasm::WasmCodePosition position) {
if (FLAG_wasm_no_bounds_checks) return;
DCHECK_NOT_NULL(*mem_size_);
DCHECK_NOT_NULL(context_cache_);
Node* mem_size = context_cache_->mem_size;
DCHECK_NOT_NULL(mem_size);
uint32_t min_size = env_->module->initial_pages * wasm::WasmModule::kPageSize;
uint32_t max_size =
......@@ -3460,12 +3516,11 @@ void WasmGraphBuilder::BoundsCheckMem(MachineType memtype, Node* index,
Node* cond;
if (jsgraph()->machine()->Is32()) {
cond = graph()->NewNode(jsgraph()->machine()->Uint32LessThanOrEqual(),
jsgraph()->Int32Constant(end_offset), *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_);
jsgraph()->Int64Constant(static_cast<int64_t>(end_offset)), mem_size);
}
TrapIfFalse(wasm::kTrapMemOutOfBounds, cond, position);
} else {
......@@ -3485,11 +3540,11 @@ void WasmGraphBuilder::BoundsCheckMem(MachineType memtype, Node* index,
Node* effective_size;
if (jsgraph()->machine()->Is32()) {
effective_size =
graph()->NewNode(jsgraph()->machine()->Int32Sub(), *mem_size_,
graph()->NewNode(jsgraph()->machine()->Int32Sub(), mem_size,
jsgraph()->Int32Constant(end_offset - 1));
} else {
effective_size = graph()->NewNode(
jsgraph()->machine()->Int64Sub(), *mem_size_,
jsgraph()->machine()->Int64Sub(), mem_size,
jsgraph()->Int64Constant(static_cast<int64_t>(end_offset - 1)));
}
......@@ -3658,15 +3713,18 @@ Node* WasmGraphBuilder::StoreMem(MachineType memtype, Node* index,
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).
DCHECK_NOT_NULL(*mem_size_);
DCHECK_NOT_NULL(*mem_start_);
DCHECK_NOT_NULL(context_cache_);
Node* mem_start = context_cache_->mem_start;
Node* mem_size = context_cache_->mem_size;
DCHECK_NOT_NULL(mem_start);
DCHECK_NOT_NULL(mem_size);
if (jsgraph()->machine()->Is64()) {
index =
graph()->NewNode(jsgraph()->machine()->ChangeUint32ToUint64(), index);
}
const Operator* op = jsgraph()->machine()->CheckedLoad(type);
Node* load =
graph()->NewNode(op, *mem_start_, index, *mem_size_, *effect_, *control_);
graph()->NewNode(op, mem_start, index, mem_size, *effect_, *control_);
*effect_ = load;
return load;
}
......@@ -3675,16 +3733,19 @@ 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).
DCHECK_NOT_NULL(*mem_size_);
DCHECK_NOT_NULL(*mem_start_);
DCHECK_NOT_NULL(context_cache_);
Node* mem_start = context_cache_->mem_start;
Node* mem_size = context_cache_->mem_size;
DCHECK_NOT_NULL(mem_start);
DCHECK_NOT_NULL(mem_size);
if (jsgraph()->machine()->Is64()) {
index =
graph()->NewNode(jsgraph()->machine()->ChangeUint32ToUint64(), index);
}
const Operator* op =
jsgraph()->machine()->CheckedStore(type.representation());
Node* store = graph()->NewNode(op, *mem_start_, index, *mem_size_, val,
*effect_, *control_);
Node* store = graph()->NewNode(op, mem_start, index, mem_size, val, *effect_,
*control_);
*effect_ = store;
return val;
}
......
......@@ -209,6 +209,16 @@ enum CWasmEntryParameters {
Handle<Code> CompileCWasmEntry(Isolate* isolate, wasm::FunctionSig* sig,
Address wasm_context_address);
// Values from the {WasmContext} are cached between WASM-level function calls.
// This struct allows the SSA environment handling this cache to be defined
// and manipulated in wasm-compiler.{h,cc} instead of inside the WASM decoder.
// (Note that currently, the globals base is immutable in a context, so not
// cached here.)
struct WasmContextCacheNodes {
Node* mem_start;
Node* mem_size;
};
// Abstracts details of building TurboFan graph nodes for wasm to separate
// the wasm decoder from the internal details of TurboFan.
typedef ZoneVector<Node*> NodeVector;
......@@ -239,6 +249,9 @@ class WasmGraphBuilder {
Node* Terminate(Node* effect, Node* control);
Node* Merge(unsigned count, Node** controls);
Node* Phi(wasm::ValueType type, unsigned count, Node** vals, Node* control);
Node* CreateOrMergeIntoPhi(wasm::ValueType type, Node* merge, Node* tnode,
Node* fnode);
Node* CreateOrMergeIntoEffectPhi(Node* merge, Node* tnode, Node* fnode);
Node* EffectPhi(unsigned count, Node** effects, Node* control);
Node* NumberConstant(int32_t value);
Node* Uint32Constant(uint32_t value);
......@@ -259,7 +272,6 @@ class WasmGraphBuilder {
Node* ConvertExceptionTagToRuntimeId(uint32_t tag);
Node* GetExceptionRuntimeId();
Node** GetExceptionValues(const wasm::WasmException* except_decl);
unsigned InputCount(Node* node);
bool IsPhiWithMerge(Node* phi, Node* merge);
bool ThrowsException(Node* node, Node** if_success, Node** if_exception);
void AppendToMerge(Node* merge, Node* from);
......@@ -362,9 +374,18 @@ class WasmGraphBuilder {
void GetGlobalBaseAndOffset(MachineType mem_type, uint32_t offset,
Node** base_node, Node** offset_node);
void set_mem_size(Node** mem_size) { this->mem_size_ = mem_size; }
void set_mem_start(Node** mem_start) { this->mem_start_ = mem_start; }
// Utilities to manipulate sets of context cache nodes.
void InitContextCache(WasmContextCacheNodes* context_cache);
void PrepareContextCacheForLoop(WasmContextCacheNodes* context_cache,
Node* control);
void NewContextCacheMerge(WasmContextCacheNodes* to,
WasmContextCacheNodes* from, Node* merge);
void MergeContextCacheInto(WasmContextCacheNodes* to,
WasmContextCacheNodes* from, Node* merge);
void set_context_cache(WasmContextCacheNodes* context_cache) {
this->context_cache_ = context_cache;
}
wasm::FunctionSig* GetFunctionSignature() { return sig_; }
......@@ -413,8 +434,7 @@ class WasmGraphBuilder {
NodeVector function_table_sizes_;
Node** control_ = nullptr;
Node** effect_ = nullptr;
Node** mem_size_ = nullptr;
Node** mem_start_ = nullptr;
WasmContextCacheNodes* context_cache_ = nullptr;
Node* globals_start_ = nullptr;
Node** cur_buffer_;
size_t cur_bufsize_;
......
......@@ -716,9 +716,9 @@ class WasmDecoder : public Decoder {
case kExprGrowMemory:
case kExprCallFunction:
case kExprCallIndirect:
// Add mem_size and mem_start to the assigned set.
assigned->Add(locals_count - 2); // mem_size
assigned->Add(locals_count - 1); // mem_start
// Add context cache nodes to the assigned set.
// TODO(titzer): make this more clear.
assigned->Add(locals_count - 1);
length = OpcodeLength(decoder, pc);
break;
case kExprEnd:
......
This diff is collapsed.
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