Commit ee8ae932 authored by jpp's avatar jpp Committed by Commit bot

[V8][Wasm] Removes references to finally in wasm.

The initial support for low level exception handling in Wasm will not
support finally blocks. This decision is taken for both simplicity (
handling finallys is not straightforward if we want try blocks to yield
values), and lack of good use case (clang++ does not need them.) They
may be added in the future once we understand the implications of
having them.

BUG=

Review-Url: https://codereview.chromium.org/2336303002
Cr-Commit-Position: refs/heads/master@{#39393}
parent 36e58db6
...@@ -68,54 +68,43 @@ struct Value { ...@@ -68,54 +68,43 @@ struct Value {
LocalType type; LocalType type;
}; };
// An entry on the control stack (i.e. if, block, loop). struct Control;
// An entry on the control stack (i.e. if, block, loop, try).
struct Control { struct Control {
const byte* pc; const byte* pc;
int stack_depth; // stack height at the beginning of the construct. int stack_depth; // stack height at the beginning of the construct.
SsaEnv* end_env; // end environment for the construct. SsaEnv* end_env; // end environment for the construct.
SsaEnv* false_env; // false environment (only for if). SsaEnv* false_env; // false environment (only for if).
SsaEnv* catch_env; // catch environment (only for try with catch). SsaEnv* catch_env; // catch environment (only for try).
SsaEnv* finish_try_env; // the environment where a try with finally lives.
TFNode* node; // result node for the construct. TFNode* node; // result node for the construct.
LocalType type; // result type for the construct. LocalType type; // result type for the construct.
bool is_loop; // true if this is the inner label of a loop. bool is_loop; // true if this is the inner label of a loop.
bool is_if() const { return *pc == kExprIf; } bool is_if() const { return *pc == kExprIf; }
bool is_try() const { bool is_try() const { return *pc == kExprTry; }
return *pc == kExprTryCatch || *pc == kExprTryCatchFinally ||
*pc == kExprTryFinally;
}
bool has_catch() const {
return *pc == kExprTryCatch || *pc == kExprTryCatchFinally;
}
bool has_finally() const {
return *pc == kExprTryCatchFinally || *pc == kExprTryFinally;
}
// Named constructors. // Named constructors.
static Control Block(const byte* pc, int stack_depth, SsaEnv* end_env) { static Control Block(const byte* pc, int stack_depth, SsaEnv* end_env) {
return {pc, stack_depth, end_env, nullptr, nullptr, return {pc, stack_depth, end_env, nullptr,
nullptr, nullptr, kAstEnd, false}; nullptr, nullptr, kAstEnd, false};
} }
static Control If(const byte* pc, int stack_depth, SsaEnv* end_env, static Control If(const byte* pc, int stack_depth, SsaEnv* end_env,
SsaEnv* false_env) { SsaEnv* false_env) {
return {pc, stack_depth, end_env, false_env, nullptr, return {pc, stack_depth, end_env, false_env,
nullptr, nullptr, kAstStmt, false}; nullptr, nullptr, kAstStmt, false};
} }
static Control Loop(const byte* pc, int stack_depth, SsaEnv* end_env) { static Control Loop(const byte* pc, int stack_depth, SsaEnv* end_env) {
return {pc, stack_depth, end_env, nullptr, nullptr, return {pc, stack_depth, end_env, nullptr, nullptr, nullptr, kAstEnd, true};
nullptr, nullptr, kAstEnd, true};
} }
static Control Try(const byte* pc, int stack_depth, SsaEnv* end_env, static Control Try(const byte* pc, int stack_depth, SsaEnv* end_env,
SsaEnv* catch_env, SsaEnv* finish_try_env) { SsaEnv* catch_env) {
return {pc, stack_depth, end_env, nullptr, catch_env, finish_try_env, return {pc, stack_depth, end_env, nullptr,
nullptr, kAstEnd, false}; catch_env, nullptr, kAstEnd, false};
} }
}; };
...@@ -288,10 +277,7 @@ class WasmDecoder : public Decoder { ...@@ -288,10 +277,7 @@ class WasmDecoder : public Decoder {
case kExprEnd: case kExprEnd:
case kExprBlock: case kExprBlock:
case kExprThrow: case kExprThrow:
case kExprTryCatch: case kExprTry:
case kExprTryCatchFinally:
case kExprTryFinally:
case kExprFinally:
case kExprLoop: case kExprLoop:
return 0; return 0;
...@@ -686,32 +672,13 @@ class WasmFullDecoder : public WasmDecoder { ...@@ -686,32 +672,13 @@ class WasmFullDecoder : public WasmDecoder {
// TODO(jpp): start exception propagation. // TODO(jpp): start exception propagation.
break; break;
} }
case kExprTryCatch: { case kExprTry: {
CHECK_PROTOTYPE_OPCODE(wasm_eh_prototype);
SsaEnv* outer_env = ssa_env_;
SsaEnv* try_env = Steal(outer_env);
SsaEnv* catch_env = Split(try_env);
PushTry(outer_env, catch_env, nullptr);
SetEnv("try_catch:start", try_env);
break;
}
case kExprTryCatchFinally: {
CHECK_PROTOTYPE_OPCODE(wasm_eh_prototype); CHECK_PROTOTYPE_OPCODE(wasm_eh_prototype);
SsaEnv* outer_env = ssa_env_; SsaEnv* outer_env = ssa_env_;
SsaEnv* try_env = Steal(outer_env); SsaEnv* try_env = Steal(outer_env);
SsaEnv* catch_env = Split(try_env); SsaEnv* catch_env = Split(try_env);
SsaEnv* finally_env = Split(try_env); PushTry(outer_env, catch_env);
PushTry(finally_env, catch_env, outer_env); SetEnv("try:start", try_env);
SetEnv("try_catch_finally:start", try_env);
break;
}
case kExprTryFinally: {
CHECK_PROTOTYPE_OPCODE(wasm_eh_prototype);
SsaEnv* outer_env = ssa_env_;
SsaEnv* try_env = Steal(outer_env);
SsaEnv* finally_env = Split(outer_env);
PushTry(finally_env, nullptr, outer_env);
SetEnv("try_finally:start", try_env);
break; break;
} }
case kExprCatch: { case kExprCatch: {
...@@ -725,8 +692,8 @@ class WasmFullDecoder : public WasmDecoder { ...@@ -725,8 +692,8 @@ class WasmFullDecoder : public WasmDecoder {
} }
Control* c = &control_.back(); Control* c = &control_.back();
if (!c->has_catch()) { if (!c->is_try()) {
error(pc_, "catch does not match a try with catch"); error(pc_, "catch does not match a try");
break; break;
} }
...@@ -753,50 +720,6 @@ class WasmFullDecoder : public WasmDecoder { ...@@ -753,50 +720,6 @@ class WasmFullDecoder : public WasmDecoder {
break; break;
} }
case kExprFinally: {
CHECK_PROTOTYPE_OPCODE(wasm_eh_prototype);
if (control_.empty()) {
error(pc_, "finally does not match a any try");
break;
}
Control* c = &control_.back();
if (c->has_catch() && c->catch_env != nullptr) {
error(pc_, "missing catch for try with catch and finally");
break;
}
if (!c->has_finally()) {
error(pc_, "finally does not match a try with finally");
break;
}
if (c->finish_try_env == nullptr) {
error(pc_, "finally already present for try with finally");
break;
}
// ssa_env_ is either the env for either the try or the catch, but
// it does not matter: either way we need to direct the control flow
// to the end_env, which is the env for the finally.
// c->finish_try_env is the the environment enclosing the try block.
Goto(ssa_env_, c->end_env);
PopUpTo(c->stack_depth);
// The current environment becomes end_env, and finish_try_env
// becomes the new end_env. This ensures that any control flow
// leaving a try block up to now will do so by branching to the
// finally block. Setting the end_env to be finish_try_env ensures
// that kExprEnd below can handle the try block as it would any
// other block construct.
SsaEnv* finally_env = c->end_env;
c->end_env = c->finish_try_env;
SetEnv("finally:begin", finally_env);
c->finish_try_env = nullptr;
break;
}
case kExprLoop: { case kExprLoop: {
// The break environment is the outer environment. // The break environment is the outer environment.
SsaEnv* break_env = ssa_env_; SsaEnv* break_env = ssa_env_;
...@@ -847,7 +770,7 @@ class WasmFullDecoder : public WasmDecoder { ...@@ -847,7 +770,7 @@ class WasmFullDecoder : public WasmDecoder {
} }
case kExprEnd: { case kExprEnd: {
if (control_.empty()) { if (control_.empty()) {
error(pc_, "end does not match any if or block"); error(pc_, "end does not match any if, try, or block");
break; break;
} }
const char* name = "block:end"; const char* name = "block:end";
...@@ -855,7 +778,7 @@ class WasmFullDecoder : public WasmDecoder { ...@@ -855,7 +778,7 @@ class WasmFullDecoder : public WasmDecoder {
Value val = PopUpTo(c->stack_depth); Value val = PopUpTo(c->stack_depth);
if (c->is_loop) { if (c->is_loop) {
// Loops always push control in pairs. // Loops always push control in pairs.
control_.pop_back(); PopControl();
c = &control_.back(); c = &control_.back();
name = "loop:end"; name = "loop:end";
} else if (c->is_if()) { } else if (c->is_if()) {
...@@ -871,28 +794,21 @@ class WasmFullDecoder : public WasmDecoder { ...@@ -871,28 +794,21 @@ class WasmFullDecoder : public WasmDecoder {
} else if (c->is_try()) { } else if (c->is_try()) {
name = "try:end"; name = "try:end";
// try blocks do not yield a value. // validate that catch was seen.
val = {val.pc, nullptr, kAstStmt};
// validate that catch/finally were seen.
if (c->catch_env != nullptr) { if (c->catch_env != nullptr) {
error(pc_, "missing catch in try with catch"); error(pc_, "missing catch in try");
break;
}
if (c->finish_try_env != nullptr) {
error(pc_, "missing finally in try with finally");
break; break;
} }
} }
if (ssa_env_->go()) { if (ssa_env_->go()) {
// Adds a fallthrough edge to the next control block.
MergeInto(c->end_env, &c->node, &c->type, val); MergeInto(c->end_env, &c->node, &c->type, val);
} }
SetEnv(name, c->end_env); SetEnv(name, c->end_env);
stack_.resize(c->stack_depth); stack_.resize(c->stack_depth);
Push(c->type, c->node); Push(c->type, c->node);
control_.pop_back(); PopControl();
break; break;
} }
case kExprSelect: { case kExprSelect: {
...@@ -1293,12 +1209,13 @@ class WasmFullDecoder : public WasmDecoder { ...@@ -1293,12 +1209,13 @@ class WasmFullDecoder : public WasmDecoder {
control_.emplace_back(Control::If(pc_, stack_depth, end_env, false_env)); control_.emplace_back(Control::If(pc_, stack_depth, end_env, false_env));
} }
void PushTry(SsaEnv* end_env, SsaEnv* catch_env, SsaEnv* finish_try_env) { void PushTry(SsaEnv* end_env, SsaEnv* catch_env) {
const int stack_depth = static_cast<int>(stack_.size()); const int stack_depth = static_cast<int>(stack_.size());
control_.emplace_back( control_.emplace_back(Control::Try(pc_, stack_depth, end_env, catch_env));
Control::Try(pc_, stack_depth, end_env, catch_env, finish_try_env));
} }
void PopControl() { control_.pop_back(); }
int DecodeLoadMem(LocalType type, MachineType mem_type) { int DecodeLoadMem(LocalType type, MachineType mem_type) {
MemoryAccessOperand operand(this, pc_, MemoryAccessOperand operand(this, pc_,
ElementSizeLog2Of(mem_type.representation())); ElementSizeLog2Of(mem_type.representation()));
...@@ -1419,7 +1336,7 @@ class WasmFullDecoder : public WasmDecoder { ...@@ -1419,7 +1336,7 @@ class WasmFullDecoder : public WasmDecoder {
int startrel(const byte* ptr) { return static_cast<int>(ptr - start_); } int startrel(const byte* ptr) { return static_cast<int>(ptr - start_); }
void BreakTo(Control* block, Value& val) { void BreakTo(Control* block, const Value& val) {
if (block->is_loop) { if (block->is_loop) {
// This is the inner loop block, which does not have a value. // This is the inner loop block, which does not have a value.
Goto(ssa_env_, block->end_env); Goto(ssa_env_, block->end_env);
...@@ -1429,7 +1346,8 @@ class WasmFullDecoder : public WasmDecoder { ...@@ -1429,7 +1346,8 @@ class WasmFullDecoder : public WasmDecoder {
} }
} }
void MergeInto(SsaEnv* target, TFNode** node, LocalType* type, Value& val) { void MergeInto(SsaEnv* target, TFNode** node, LocalType* type,
const Value& val) {
if (!ssa_env_->go()) return; if (!ssa_env_->go()) return;
DCHECK_NE(kAstEnd, val.type); DCHECK_NE(kAstEnd, val.type);
...@@ -1674,9 +1592,7 @@ class WasmFullDecoder : public WasmDecoder { ...@@ -1674,9 +1592,7 @@ class WasmFullDecoder : public WasmDecoder {
case kExprLoop: case kExprLoop:
case kExprIf: case kExprIf:
case kExprBlock: case kExprBlock:
case kExprTryCatch: case kExprTry:
case kExprTryCatchFinally:
case kExprTryFinally:
depth++; depth++;
DCHECK_EQ(1, OpcodeLength(pc)); DCHECK_EQ(1, OpcodeLength(pc));
break; break;
...@@ -1847,9 +1763,7 @@ bool PrintAst(base::AccountingAllocator* allocator, const FunctionBody& body, ...@@ -1847,9 +1763,7 @@ bool PrintAst(base::AccountingAllocator* allocator, const FunctionBody& body,
case kExprElse: case kExprElse:
case kExprLoop: case kExprLoop:
case kExprBlock: case kExprBlock:
case kExprTryCatch: case kExprTry:
case kExprTryCatchFinally:
case kExprTryFinally:
os << " // @" << i.pc_offset(); os << " // @" << i.pc_offset();
control_depth++; control_depth++;
break; break;
......
...@@ -56,11 +56,8 @@ const WasmCodePosition kNoCodePosition = -1; ...@@ -56,11 +56,8 @@ const WasmCodePosition kNoCodePosition = -1;
V(Return, 0x09, _) \ V(Return, 0x09, _) \
V(Unreachable, 0x0a, _) \ V(Unreachable, 0x0a, _) \
V(Throw, 0xfa, _) \ V(Throw, 0xfa, _) \
V(TryCatch, 0xfb, _) \ V(Try, 0xfb, _) \
V(TryCatchFinally, 0xfc, _) \
V(TryFinally, 0xfd, _) \
V(Catch, 0xfe, _) \ V(Catch, 0xfe, _) \
V(Finally, 0xff, _) \
V(End, 0x0F, _) V(End, 0x0F, _)
// Constants, locals, globals, and calls. // Constants, locals, globals, and calls.
......
...@@ -2003,73 +2003,17 @@ TEST_F(AstDecoderTest, Throw) { ...@@ -2003,73 +2003,17 @@ TEST_F(AstDecoderTest, Throw) {
#define WASM_CATCH(local) kExprCatch, static_cast<byte>(local) #define WASM_CATCH(local) kExprCatch, static_cast<byte>(local)
TEST_F(AstDecoderTest, TryCatch) { TEST_F(AstDecoderTest, TryCatch) {
FLAG_wasm_eh_prototype = true; FLAG_wasm_eh_prototype = true;
EXPECT_VERIFIES_INLINE(sigs.v_i(), kExprTryCatch, WASM_CATCH(0), kExprEnd); EXPECT_VERIFIES_INLINE(sigs.v_i(), kExprTry, WASM_CATCH(0), kExprEnd);
// Missing catch. // Missing catch.
EXPECT_FAILURE_INLINE(sigs.v_v(), kExprTryCatch, kExprEnd); EXPECT_FAILURE_INLINE(sigs.v_v(), kExprTry, kExprEnd);
// Missing end. // Missing end.
EXPECT_FAILURE_INLINE(sigs.v_i(), kExprTryCatch, WASM_CATCH(0)); EXPECT_FAILURE_INLINE(sigs.v_i(), kExprTry, WASM_CATCH(0));
// Double catch. // Double catch.
EXPECT_FAILURE_INLINE(sigs.v_i(), kExprTryCatch, WASM_CATCH(0), WASM_CATCH(0), EXPECT_FAILURE_INLINE(sigs.v_i(), kExprTry, WASM_CATCH(0), WASM_CATCH(0),
kExprEnd); kExprEnd);
// Unexpected finally.
EXPECT_FAILURE_INLINE(sigs.v_i(), kExprTryCatch, WASM_CATCH(0), kExprFinally,
kExprEnd);
}
TEST_F(AstDecoderTest, TryFinally) {
FLAG_wasm_eh_prototype = true;
EXPECT_VERIFIES_INLINE(sigs.v_v(), kExprTryFinally, kExprFinally, kExprEnd);
// Mising finally.
EXPECT_FAILURE_INLINE(sigs.v_v(), kExprTryFinally, kExprEnd);
// Missing end.
EXPECT_FAILURE_INLINE(sigs.v_v(), kExprTryFinally, kExprFinally);
// Double finally.
EXPECT_FAILURE_INLINE(sigs.v_v(), kExprTryFinally, kExprFinally, kExprFinally,
kExprEnd);
// Unexpected catch.
EXPECT_FAILURE_INLINE(sigs.v_i(), kExprTryCatch, WASM_CATCH(0), kExprFinally,
kExprEnd);
}
TEST_F(AstDecoderTest, TryCatchFinally) {
FLAG_wasm_eh_prototype = true;
EXPECT_VERIFIES_INLINE(sigs.v_i(), kExprTryCatchFinally, WASM_CATCH(0),
kExprFinally, kExprEnd);
// Missing catch.
EXPECT_FAILURE_INLINE(sigs.v_i(), kExprTryCatchFinally, kExprFinally,
kExprEnd);
// Double catch.
EXPECT_FAILURE_INLINE(sigs.v_i(), kExprTryCatchFinally, WASM_CATCH(0),
WASM_CATCH(0), kExprFinally, kExprEnd);
// Missing finally.
EXPECT_FAILURE_INLINE(sigs.v_i(), kExprTryCatchFinally, WASM_CATCH(0),
kExprEnd);
// Double finally.
EXPECT_FAILURE_INLINE(sigs.v_i(), kExprTryCatchFinally, WASM_CATCH(0),
kExprFinally, kExprFinally, kExprEnd);
// Finally before catch.
EXPECT_FAILURE_INLINE(sigs.v_i(), kExprTryCatchFinally, kExprFinally,
WASM_CATCH(0), kExprEnd);
// Missing both try and finally.
EXPECT_FAILURE_INLINE(sigs.v_i(), kExprTryCatchFinally, kExprEnd);
// Missing end.
EXPECT_FAILURE_INLINE(sigs.v_i(), kExprTryCatchFinally, WASM_CATCH(0),
kExprFinally);
} }
class WasmOpcodeLengthTest : public TestWithZone { class WasmOpcodeLengthTest : public TestWithZone {
...@@ -2100,11 +2044,8 @@ TEST_F(WasmOpcodeLengthTest, Statements) { ...@@ -2100,11 +2044,8 @@ TEST_F(WasmOpcodeLengthTest, Statements) {
EXPECT_LENGTH(3, kExprBr); EXPECT_LENGTH(3, kExprBr);
EXPECT_LENGTH(3, kExprBrIf); EXPECT_LENGTH(3, kExprBrIf);
EXPECT_LENGTH(1, kExprThrow); EXPECT_LENGTH(1, kExprThrow);
EXPECT_LENGTH(1, kExprTryCatch); EXPECT_LENGTH(1, kExprTry);
EXPECT_LENGTH(1, kExprTryFinally);
EXPECT_LENGTH(1, kExprTryCatchFinally);
EXPECT_LENGTH(2, kExprCatch); EXPECT_LENGTH(2, kExprCatch);
EXPECT_LENGTH(1, kExprFinally);
} }
TEST_F(WasmOpcodeLengthTest, MiscExpressions) { TEST_F(WasmOpcodeLengthTest, MiscExpressions) {
...@@ -2348,11 +2289,8 @@ TEST_F(WasmOpcodeArityTest, Control) { ...@@ -2348,11 +2289,8 @@ TEST_F(WasmOpcodeArityTest, Control) {
} }
EXPECT_ARITY(0, kExprThrow); EXPECT_ARITY(0, kExprThrow);
EXPECT_ARITY(0, kExprTryCatch); EXPECT_ARITY(0, kExprTry);
EXPECT_ARITY(0, kExprTryFinally);
EXPECT_ARITY(0, kExprTryCatchFinally);
EXPECT_ARITY(1, kExprCatch, 2); EXPECT_ARITY(1, kExprCatch, 2);
EXPECT_ARITY(0, kExprFinally);
} }
TEST_F(WasmOpcodeArityTest, Misc) { TEST_F(WasmOpcodeArityTest, Misc) {
......
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