[wasm] Speed up reachability checks

Most interface calls are preceded by a reachability check, such that we
only generate code if the current instruction is actually reachable.
This is particularly important for Liftoff (TurboFan would throw out
dead parts of the graph anyway).

In order to speed up this check, this CL introduces a boolean flag
directly on the {WasmFullDecoder}. This avoids checking whether an error
has been set *plus* checking the reachability of the top-most control
block.

This provides 5-6% speedup on Liftoff compilation locally.

R=thibaudm@chromium.org

Bug: v8:10576
Change-Id: Idcff623fb9c23473b06ebf91b3caee65cc6ca28b
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2230521
Reviewed-by: Thibaud Michaud <thibaudm@chromium.org>
Commit-Queue: Clemens Backes <clemensb@chromium.org>
Cr-Commit-Position: refs/heads/master@{#68200}
This commit is contained in:
Clemens Backes 2020-06-04 19:34:42 +02:00 committed by Commit Bot
parent d891c59a79
commit 2954b407d6
3 changed files with 32 additions and 17 deletions

View File

@ -1943,10 +1943,7 @@ class LiftoffCompiler {
if (statically_oob) {
__ emit_jump(trap_label);
Control* current_block = decoder->control_at(0);
if (current_block->reachable()) {
current_block->reachability = kSpecOnlyReachable;
}
decoder->SetSucceedingCodeDynamicallyUnreachable();
return true;
}

View File

@ -1794,12 +1794,14 @@ class WasmDecoder : public Decoder {
};
#define CALL_INTERFACE(name, ...) interface_.name(this, ##__VA_ARGS__)
#define CALL_INTERFACE_IF_REACHABLE(name, ...) \
do { \
DCHECK(!control_.empty()); \
if (VALIDATE(this->ok()) && control_.back().reachable()) { \
interface_.name(this, ##__VA_ARGS__); \
} \
#define CALL_INTERFACE_IF_REACHABLE(name, ...) \
do { \
DCHECK(!control_.empty()); \
DCHECK_EQ(current_code_reachable_, \
this->ok() && control_.back().reachable()); \
if (current_code_reachable_) { \
interface_.name(this, ##__VA_ARGS__); \
} \
} while (false)
#define CALL_INTERFACE_IF_PARENT_REACHABLE(name, ...) \
do { \
@ -1925,6 +1927,14 @@ class WasmFullDecoder : public WasmDecoder<validate> {
return &*(stack_.end() - depth);
}
void SetSucceedingCodeDynamicallyUnreachable() {
Control* current = &control_.back();
if (current->reachable()) {
current->reachability = kSpecOnlyReachable;
current_code_reachable_ = false;
}
}
private:
Zone* zone_;
@ -1933,6 +1943,9 @@ class WasmFullDecoder : public WasmDecoder<validate> {
ZoneVector<ValueType> local_type_vec_; // types of local variables.
ZoneVector<Value> stack_; // stack of values.
ZoneVector<Control> control_; // stack of blocks, loops, and ifs.
// Controls whether code should be generated for the current block (basically
// a cache for {ok() && control_.back().reachable()}).
bool current_code_reachable_ = true;
static Value UnreachableValue(const uint8_t* pc) {
return Value{pc, kWasmBottom};
@ -2084,6 +2097,7 @@ class WasmFullDecoder : public WasmDecoder<validate> {
FallThruTo(c);
stack_.erase(stack_.begin() + c->stack_depth, stack_.end());
c->reachability = control_at(1)->innerReachability();
current_code_reachable_ = this->ok() && c->reachable();
Value* exception = Push(kWasmExnRef);
CALL_INTERFACE_IF_PARENT_REACHABLE(Catch, c, exception);
break;
@ -2235,6 +2249,7 @@ class WasmFullDecoder : public WasmDecoder<validate> {
if (c->reachable()) c->end_merge.reached = true;
PushMergeValues(c, &c->start_merge);
c->reachability = control_at(1)->innerReachability();
current_code_reachable_ = this->ok() && c->reachable();
break;
}
case kExprEnd: {
@ -2380,7 +2395,7 @@ class WasmFullDecoder : public WasmDecoder<validate> {
DCHECK(this->ok());
if (control_.back().reachable()) {
if (current_code_reachable_) {
CALL_INTERFACE(BrTable, imm, key);
for (int i = 0, e = control_depth(); i < e; ++i) {
@ -2394,7 +2409,7 @@ class WasmFullDecoder : public WasmDecoder<validate> {
break;
}
case kExprReturn: {
if (V8_LIKELY(control_.back().reachable())) {
if (V8_LIKELY(current_code_reachable_)) {
if (!VALIDATE(TypeCheckReturn())) break;
DoReturn();
} else {
@ -2865,6 +2880,7 @@ class WasmFullDecoder : public WasmDecoder<validate> {
stack_.erase(stack_.begin() + current->stack_depth, stack_.end());
CALL_INTERFACE_IF_REACHABLE(EndControl, current);
current->reachability = kUnreachable;
current_code_reachable_ = false;
}
template<typename func>
@ -2928,6 +2944,7 @@ class WasmFullDecoder : public WasmDecoder<validate> {
control_.empty() ? kReachable : control_.back().innerReachability();
control_.emplace_back(kind, locals_count, stack_size(), this->pc_,
reachability);
current_code_reachable_ = this->ok() && reachability == kReachable;
return &control_.back();
}
@ -2943,9 +2960,8 @@ class WasmFullDecoder : public WasmDecoder<validate> {
control_.pop_back();
// If the parent block was reachable before, but the popped control does not
// return to here, this block becomes "spec only reachable".
if (!parent_reached && control_.back().reachable()) {
control_.back().reachability = kSpecOnlyReachable;
}
if (!parent_reached) SetSucceedingCodeDynamicallyUnreachable();
current_code_reachable_ = control_.back().reachable();
}
int DecodeLoadMem(LoadType type, int prefix_len = 0) {
@ -3637,7 +3653,7 @@ class WasmFullDecoder : public WasmDecoder<validate> {
}
bool TypeCheckFallThru() {
static_assert(validate, "Call this function only whithin VALIDATE");
static_assert(validate, "Call this function only within VALIDATE");
Control& c = control_.back();
if (V8_LIKELY(c.reachable())) {
uint32_t expected = c.end_merge.arity;
@ -3742,6 +3758,7 @@ class WasmFullDecoder : public WasmDecoder<validate> {
void onFirstError() override {
this->end_ = this->pc_; // Terminate decoding loop.
this->current_code_reachable_ = false;
TRACE(" !%s\n", this->error_.message().c_str());
CALL_INTERFACE(OnFirstError);
}

View File

@ -554,6 +554,7 @@ class WasmGraphBuildingInterface {
void Catch(FullDecoder* decoder, Control* block, Value* exception) {
DCHECK(block->is_try_catch());
DCHECK_EQ(decoder->control_at(0), block);
current_catch_ = block->previous_catch; // Pop try scope.
@ -561,7 +562,7 @@ class WasmGraphBuildingInterface {
// exist. We only build a landing pad if some node in the try block can
// (possibly) throw. Otherwise the catch environments remain empty.
if (!block->try_info->might_throw()) {
block->reachability = kSpecOnlyReachable;
decoder->SetSucceedingCodeDynamicallyUnreachable();
return;
}