Commit 7768b245 authored by Clemens Hammacher's avatar Clemens Hammacher Committed by Commit Bot

Reland "[wasm][liftoff] Optimize one-armed ifs"

This is a reland of c2aaf0a6

Original change's description:
> [wasm][liftoff] Optimize one-armed ifs
> 
> Do not implement one-armed ifs by emulating an empty else branch. In
> Liftoff, we can generate better code and save compile time by handling
> this specially. If the merge point at the end of the if is not reached
> by the if-branch, we do not need to generate any merge code.
> 
> R=titzer@chromium.org
> 
> Bug: v8:6600, v8:8423
> Change-Id: Ie8ea69dd7491f225605a8e1b986d275d869aa90b
> Reviewed-on: https://chromium-review.googlesource.com/c/1356508
> Commit-Queue: Clemens Hammacher <clemensh@chromium.org>
> Reviewed-by: Ben Titzer <titzer@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#57968}

Bug: v8:6600, v8:8423
Change-Id: I6d5eea9f860486768779a33bf6bd7b87cbfc2af0
Reviewed-on: https://chromium-review.googlesource.com/c/1361040Reviewed-by: 's avatarBen Titzer <titzer@chromium.org>
Commit-Queue: Clemens Hammacher <clemensh@chromium.org>
Cr-Commit-Position: refs/heads/master@{#58024}
parent 1fba4b7c
...@@ -498,8 +498,10 @@ class LiftoffCompiler { ...@@ -498,8 +498,10 @@ class LiftoffCompiler {
if (c->end_merge.reached) { if (c->end_merge.reached) {
__ MergeFullStackWith(c->label_state); __ MergeFullStackWith(c->label_state);
} else if (c->is_onearmed_if()) { } else if (c->is_onearmed_if()) {
c->label_state.InitMerge(*__ cache_state(), __ num_locals(), // Init the merge point from the else state, then merge the if state into
c->br_merge()->arity); // that.
DCHECK_EQ(0, c->end_merge.arity);
c->label_state.InitMerge(c->else_state->state, __ num_locals(), 0);
__ MergeFullStackWith(c->label_state); __ MergeFullStackWith(c->label_state);
} else { } else {
c->label_state.Split(*__ cache_state()); c->label_state.Split(*__ cache_state());
...@@ -508,7 +510,22 @@ class LiftoffCompiler { ...@@ -508,7 +510,22 @@ class LiftoffCompiler {
} }
void PopControl(FullDecoder* decoder, Control* c) { void PopControl(FullDecoder* decoder, Control* c) {
if (!c->is_loop() && c->end_merge.reached) { if (c->is_onearmed_if()) {
if (c->end_merge.reached) {
// Generate the code to merge the else state into the end state.
// TODO(clemensh): Do this without switching to the else state first.
__ emit_jump(c->label.get());
__ bind(c->else_state->label.get());
__ cache_state()->Steal(c->else_state->state);
__ MergeFullStackWith(c->label_state);
__ cache_state()->Steal(c->label_state);
} else {
// There is no merge at the end of the if, so just continue with the
// else state.
__ bind(c->else_state->label.get());
__ cache_state()->Steal(c->else_state->state);
}
} else if (!c->is_loop() && c->end_merge.reached) {
__ cache_state()->Steal(c->label_state); __ cache_state()->Steal(c->label_state);
} }
if (!c->label.get()->is_bound()) { if (!c->label.get()->is_bound()) {
...@@ -1310,10 +1327,17 @@ class LiftoffCompiler { ...@@ -1310,10 +1327,17 @@ class LiftoffCompiler {
DCHECK(!table_iterator.has_next()); DCHECK(!table_iterator.has_next());
} }
void Else(FullDecoder* decoder, Control* if_block) { void Else(FullDecoder* decoder, Control* c) {
if (if_block->reachable()) __ emit_jump(if_block->label.get()); if (c->reachable()) {
__ bind(if_block->else_state->label.get()); if (!c->end_merge.reached) {
__ cache_state()->Steal(if_block->else_state->state); c->label_state.InitMerge(*__ cache_state(), __ num_locals(),
c->end_merge.arity);
}
__ MergeFullStackWith(c->label_state);
__ emit_jump(c->label.get());
}
__ bind(c->else_state->label.get());
__ cache_state()->Steal(c->else_state->state);
} }
Label* AddOutOfLineTrap(WasmCodePosition position, Label* AddOutOfLineTrap(WasmCodePosition position,
......
...@@ -1748,9 +1748,10 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -1748,9 +1748,10 @@ class WasmFullDecoder : public WasmDecoder<validate> {
this->error(this->pc_, "else already present for if"); this->error(this->pc_, "else already present for if");
break; break;
} }
FallThruTo(c); if (!TypeCheckFallThru(c)) break;
c->kind = kControlIfElse; c->kind = kControlIfElse;
CALL_INTERFACE_IF_PARENT_REACHABLE(Else, c); CALL_INTERFACE_IF_PARENT_REACHABLE(Else, c);
if (c->reachable()) c->end_merge.reached = true;
PushMergeValues(c, &c->start_merge); PushMergeValues(c, &c->start_merge);
c->reachability = control_at(1)->innerReachability(); c->reachability = control_at(1)->innerReachability();
break; break;
...@@ -1758,7 +1759,7 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -1758,7 +1759,7 @@ class WasmFullDecoder : public WasmDecoder<validate> {
case kExprEnd: { case kExprEnd: {
if (!VALIDATE(!control_.empty())) { if (!VALIDATE(!control_.empty())) {
this->error("end does not match any if, try, or block"); this->error("end does not match any if, try, or block");
return; break;
} }
Control* c = &control_.back(); Control* c = &control_.back();
if (!VALIDATE(!c->is_incomplete_try())) { if (!VALIDATE(!c->is_incomplete_try())) {
...@@ -1766,12 +1767,12 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -1766,12 +1767,12 @@ class WasmFullDecoder : public WasmDecoder<validate> {
break; break;
} }
if (c->is_onearmed_if()) { if (c->is_onearmed_if()) {
// Emulate empty else arm. if (!VALIDATE(c->end_merge.arity == c->start_merge.arity)) {
FallThruTo(c); this->error(
if (this->failed()) break; c->pc,
CALL_INTERFACE_IF_PARENT_REACHABLE(Else, c); "start-arity and end-arity of one-armed if must match");
PushMergeValues(c, &c->start_merge); break;
c->reachability = control_at(1)->innerReachability(); }
} }
if (c->is_try_catch()) { if (c->is_try_catch()) {
// Emulate catch-all + re-throw. // Emulate catch-all + re-throw.
...@@ -2301,7 +2302,7 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -2301,7 +2302,7 @@ class WasmFullDecoder : public WasmDecoder<validate> {
void PopControl(Control* c) { void PopControl(Control* c) {
DCHECK_EQ(c, &control_.back()); DCHECK_EQ(c, &control_.back());
CALL_INTERFACE_IF_PARENT_REACHABLE(PopControl, c); CALL_INTERFACE_IF_PARENT_REACHABLE(PopControl, c);
bool reached = c->end_merge.reached; bool reached = c->end_merge.reached || c->is_onearmed_if();
control_.pop_back(); control_.pop_back();
// If the parent block was reachable before, but the popped control does not // If the parent block was reachable before, but the popped control does not
// return to here, this block becomes indirectly unreachable. // return to here, this block becomes indirectly unreachable.
......
...@@ -200,6 +200,11 @@ class WasmGraphBuildingInterface { ...@@ -200,6 +200,11 @@ class WasmGraphBuildingInterface {
} }
void PopControl(FullDecoder* decoder, Control* block) { void PopControl(FullDecoder* decoder, Control* block) {
if (block->is_onearmed_if()) {
// Merge the else branch into the end merge.
SetEnv(block->false_env);
MergeValuesInto(decoder, block, &block->end_merge);
}
if (!block->is_loop()) SetEnv(block->end_env); if (!block->is_loop()) SetEnv(block->end_env);
} }
...@@ -338,6 +343,10 @@ class WasmGraphBuildingInterface { ...@@ -338,6 +343,10 @@ class WasmGraphBuildingInterface {
} }
void Else(FullDecoder* decoder, Control* if_block) { void Else(FullDecoder* decoder, Control* if_block) {
if (if_block->reachable()) {
// Merge the if branch into the end merge.
MergeValuesInto(decoder, if_block, &if_block->end_merge);
}
SetEnv(if_block->false_env); SetEnv(if_block->false_env);
} }
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include "test/common/wasm/flag-utils.h" #include "test/common/wasm/flag-utils.h"
#include "test/common/wasm/test-signatures.h" #include "test/common/wasm/test-signatures.h"
#include "test/common/wasm/wasm-macro-gen.h" #include "test/common/wasm/wasm-macro-gen.h"
#include "testing/gmock-support.h"
namespace v8 { namespace v8 {
namespace internal { namespace internal {
...@@ -146,6 +147,9 @@ class FunctionBodyDecoderTest : public TestWithZone { ...@@ -146,6 +147,9 @@ class FunctionBodyDecoderTest : public TestWithZone {
str << "Verification successed, expected failure; pc = +" << pc; str << "Verification successed, expected failure; pc = +" << pc;
} }
EXPECT_EQ(result.ok(), expected_success) << str.str(); EXPECT_EQ(result.ok(), expected_success) << str.str();
if (!expected_success && message) {
EXPECT_THAT(result.error_msg(), ::testing::HasSubstr(message));
}
} }
void TestBinop(WasmOpcode opcode, FunctionSig* success) { void TestBinop(WasmOpcode opcode, FunctionSig* success) {
...@@ -786,6 +790,13 @@ TEST_F(FunctionBodyDecoderTest, IfElseUnreachable2) { ...@@ -786,6 +790,13 @@ TEST_F(FunctionBodyDecoderTest, IfElseUnreachable2) {
} }
} }
TEST_F(FunctionBodyDecoderTest, OneArmedIfWithArity) {
static const byte code[] = {WASM_ZERO, kExprIf, kLocalI32, WASM_ONE,
kExprEnd};
EXPECT_FAILURE_C(i_v, code,
"start-arity and end-arity of one-armed if must match");
}
TEST_F(FunctionBodyDecoderTest, IfBreak) { TEST_F(FunctionBodyDecoderTest, IfBreak) {
EXPECT_VERIFIES(v_i, WASM_IF(WASM_GET_LOCAL(0), WASM_BR(0))); EXPECT_VERIFIES(v_i, WASM_IF(WASM_GET_LOCAL(0), WASM_BR(0)));
EXPECT_VERIFIES(v_i, WASM_IF(WASM_GET_LOCAL(0), WASM_BR(1))); EXPECT_VERIFIES(v_i, WASM_IF(WASM_GET_LOCAL(0), WASM_BR(1)));
......
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