Index: src/wasm/ast-decoder.cc |
diff --git a/src/wasm/ast-decoder.cc b/src/wasm/ast-decoder.cc |
index 28723c5fa6ae3410bdaf66d14ef684fe188c5c63..edbdcfe1d96ed98076884d2d4f0667ae4ae74d05 100644 |
--- a/src/wasm/ast-decoder.cc |
+++ b/src/wasm/ast-decoder.cc |
@@ -65,15 +65,52 @@ struct Value { |
// An entry on the control stack (i.e. if, block, loop). |
struct Control { |
const byte* pc; |
- int stack_depth; // stack height at the beginning of the construct. |
- SsaEnv* end_env; // end environment for the construct. |
- SsaEnv* false_env; // false environment (only for if). |
- TFNode* node; // result node 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_if() { return *pc == kExprIf; } |
- bool is_block() { return *pc == kExprBlock; } |
+ int stack_depth; // stack height at the beginning of the construct. |
+ SsaEnv* end_env; // end environment for the construct. |
+ SsaEnv* false_env; // false environment (only for if). |
+ SsaEnv* catch_env; // catch environment (only for try with catch). |
+ SsaEnv* finish_try_env; // the environment where a try with finally lives. |
+ TFNode* node; // result node 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_if() const { return *pc == kExprIf; } |
+ |
+ bool is_try() const { |
+ 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. |
+ static Control Block(const byte* pc, int stack_depth, SsaEnv* end_env) { |
+ return {pc, stack_depth, end_env, nullptr, nullptr, |
+ nullptr, nullptr, kAstEnd, false}; |
+ } |
+ |
+ static Control If(const byte* pc, int stack_depth, SsaEnv* end_env, |
+ SsaEnv* false_env) { |
+ return {pc, stack_depth, end_env, false_env, nullptr, |
+ nullptr, nullptr, kAstStmt, false}; |
+ } |
+ |
+ static Control Loop(const byte* pc, int stack_depth, SsaEnv* end_env) { |
+ return {pc, stack_depth, end_env, nullptr, nullptr, |
+ nullptr, nullptr, kAstEnd, true}; |
+ } |
+ |
+ static Control Try(const byte* pc, int stack_depth, SsaEnv* end_env, |
+ SsaEnv* catch_env, SsaEnv* finish_try_env) { |
+ return {pc, stack_depth, end_env, nullptr, catch_env, finish_try_env, |
+ nullptr, kAstEnd, false}; |
+ } |
}; |
// Macros that build nodes only if there is a graph and the current SSA |
@@ -244,12 +281,18 @@ class WasmDecoder : public Decoder { |
case kExprUnreachable: |
case kExprEnd: |
case kExprBlock: |
+ case kExprThrow: |
+ case kExprTryCatch: |
+ case kExprTryCatchFinally: |
+ case kExprTryFinally: |
+ case kExprFinally: |
case kExprLoop: |
return 0; |
case kExprSetGlobal: |
case kExprSetLocal: |
case kExprElse: |
+ case kExprCatch: |
return 1; |
case kExprBr: { |
@@ -340,7 +383,8 @@ class WasmDecoder : public Decoder { |
} |
case kExprSetLocal: |
- case kExprGetLocal: { |
+ case kExprGetLocal: |
+ case kExprCatch: { |
LocalIndexOperand operand(this, pc); |
return 1 + operand.length; |
} |
@@ -626,16 +670,159 @@ class WasmFullDecoder : public WasmDecoder { |
SetEnv("block:start", Steal(break_env)); |
break; |
} |
+ case kExprThrow: { |
+ if (!FLAG_wasm_eh_prototype) { |
+ error("Invalid opcode"); |
+ return; |
+ } |
+ |
+ // TODO(jpp): validate the poped value. |
+ Pop(); |
+ |
+ // TODO(jpp): start exception propagation. |
+ break; |
+ } |
+ case kExprTryCatch: { |
+ if (!FLAG_wasm_eh_prototype) { |
+ error("Invalid opcode"); |
+ return; |
+ } |
+ |
+ 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: { |
+ if (!FLAG_wasm_eh_prototype) { |
+ error("Invalid opcode"); |
+ return; |
+ } |
+ |
+ SsaEnv* outer_env = ssa_env_; |
+ SsaEnv* try_env = Steal(outer_env); |
+ SsaEnv* catch_env = Split(try_env); |
+ SsaEnv* finally_env = Split(try_env); |
+ PushTry(finally_env, catch_env, outer_env); |
+ SetEnv("try_catch_finally:start", try_env); |
+ break; |
+ } |
+ case kExprTryFinally: { |
+ if (!FLAG_wasm_eh_prototype) { |
+ error("Invalid opcode"); |
+ return; |
+ } |
+ |
+ 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; |
+ } |
+ case kExprCatch: { |
+ if (!FLAG_wasm_eh_prototype) { |
+ error("Invalid opcode"); |
+ return; |
+ } |
+ |
+ LocalIndexOperand operand(this, pc_); |
+ len = 1 + operand.length; |
+ |
+ if (control_.empty()) { |
+ error(pc_, "catch does not match a any try"); |
+ break; |
+ } |
+ |
+ Control* c = &control_.back(); |
+ if (!c->has_catch()) { |
+ error(pc_, "catch does not match a try with catch"); |
+ break; |
+ } |
+ |
+ if (c->catch_env == nullptr) { |
+ error(pc_, "catch already present for try with catch"); |
+ break; |
+ } |
+ |
+ Goto(ssa_env_, c->end_env); |
+ |
+ SsaEnv* catch_env = c->catch_env; |
+ c->catch_env = nullptr; |
+ SetEnv("catch:begin", catch_env); |
+ |
+ if (Validate(pc_, operand)) { |
+ // TODO(jpp): figure out how thrown value is propagated. It is |
+ // unlikely to be a value on the stack. |
+ if (ssa_env_->locals) { |
+ ssa_env_->locals[operand.index] = nullptr; |
+ } |
+ } |
+ |
+ PopUpTo(c->stack_depth); |
+ |
+ break; |
+ } |
+ case kExprFinally: { |
+ if (!FLAG_wasm_eh_prototype) { |
+ error("Invalid opcode"); |
+ return; |
+ } |
+ |
+ 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: { |
// The break environment is the outer environment. |
SsaEnv* break_env = ssa_env_; |
PushBlock(break_env); |
- SsaEnv* cont_env = Steal(break_env); |
+ SsaEnv* finish_try_env = Steal(break_env); |
// The continue environment is the inner environment. |
- PrepareForLoop(pc_, cont_env); |
- SetEnv("loop:start", Split(cont_env)); |
+ PrepareForLoop(pc_, finish_try_env); |
+ SetEnv("loop:start", Split(finish_try_env)); |
ssa_env_->SetNotMerged(); |
- PushLoop(cont_env); |
+ PushLoop(finish_try_env); |
break; |
} |
case kExprIf: { |
@@ -681,14 +868,13 @@ class WasmFullDecoder : public WasmDecoder { |
} |
const char* name = "block:end"; |
Control* c = &control_.back(); |
+ Value val = PopUpTo(c->stack_depth); |
if (c->is_loop) { |
// Loops always push control in pairs. |
control_.pop_back(); |
c = &control_.back(); |
name = "loop:end"; |
- } |
- Value val = PopUpTo(c->stack_depth); |
- if (c->is_if()) { |
+ } else if (c->is_if()) { |
if (c->false_env != nullptr) { |
// End the true branch of a one-armed if. |
Goto(c->false_env, c->end_env); |
@@ -698,7 +884,26 @@ class WasmFullDecoder : public WasmDecoder { |
// End the false branch of a two-armed if. |
name = "if_else:merge"; |
} |
+ } else if (c->is_try()) { |
+ DCHECK(FLAG_wasm_eh_prototype); |
+ |
+ name = "try:end"; |
+ |
+ // try blocks do not yield a value. |
+ val = {val.pc, nullptr, kAstStmt}; |
+ |
+ // validate that catch/finally were seen. |
+ if (c->catch_env != nullptr) { |
+ error(pc_, "missing catch in try with catch"); |
+ break; |
+ } |
+ |
+ if (c->finish_try_env != nullptr) { |
+ error(pc_, "missing finally in try with finally"); |
+ break; |
+ } |
} |
+ |
if (ssa_env_->go()) { |
MergeInto(c->end_env, &c->node, &c->type, val); |
} |
@@ -996,13 +1201,15 @@ class WasmFullDecoder : public WasmDecoder { |
break; |
} |
case kSimdPrefix: { |
- if (FLAG_wasm_simd_prototype) { |
- len++; |
- byte simd_index = *(pc_ + 1); |
- opcode = static_cast<WasmOpcode>(opcode << 8 | simd_index); |
- DecodeSimdOpcode(opcode); |
- break; |
+ if (!FLAG_wasm_simd_prototype) { |
+ error("Invalid opcode"); |
+ return; |
} |
+ len++; |
+ byte simd_index = *(pc_ + 1); |
+ opcode = static_cast<WasmOpcode>(opcode << 8 | simd_index); |
+ DecodeSimdOpcode(opcode); |
+ break; |
} |
default: |
error("Invalid opcode"); |
@@ -1073,21 +1280,24 @@ class WasmFullDecoder : public WasmDecoder { |
} |
void PushBlock(SsaEnv* end_env) { |
- int stack_depth = static_cast<int>(stack_.size()); |
- control_.push_back( |
- {pc_, stack_depth, end_env, nullptr, nullptr, kAstEnd, false}); |
+ const int stack_depth = static_cast<int>(stack_.size()); |
+ control_.emplace_back(Control::Block(pc_, stack_depth, end_env)); |
} |
void PushLoop(SsaEnv* end_env) { |
- int stack_depth = static_cast<int>(stack_.size()); |
- control_.push_back( |
- {pc_, stack_depth, end_env, nullptr, nullptr, kAstEnd, true}); |
+ const int stack_depth = static_cast<int>(stack_.size()); |
+ control_.emplace_back(Control::Loop(pc_, stack_depth, end_env)); |
} |
void PushIf(SsaEnv* end_env, SsaEnv* false_env) { |
- int stack_depth = static_cast<int>(stack_.size()); |
- control_.push_back( |
- {pc_, stack_depth, end_env, false_env, nullptr, kAstStmt, false}); |
+ const int stack_depth = static_cast<int>(stack_.size()); |
+ control_.emplace_back(Control::If(pc_, stack_depth, end_env, false_env)); |
+ } |
+ |
+ void PushTry(SsaEnv* end_env, SsaEnv* catch_env, SsaEnv* finish_try_env) { |
+ const int stack_depth = static_cast<int>(stack_.size()); |
+ control_.emplace_back( |
+ Control::Try(pc_, stack_depth, end_env, catch_env, finish_try_env)); |
} |
int DecodeLoadMem(LocalType type, MachineType mem_type) { |
@@ -1441,6 +1651,9 @@ class WasmFullDecoder : public WasmDecoder { |
case kExprLoop: |
case kExprIf: |
case kExprBlock: |
+ case kExprTryCatch: |
+ case kExprTryCatchFinally: |
+ case kExprTryFinally: |
depth++; |
DCHECK_EQ(1, OpcodeLength(pc)); |
break; |
@@ -1589,6 +1802,9 @@ bool PrintAst(base::AccountingAllocator* allocator, const FunctionBody& body, |
case kExprElse: |
case kExprLoop: |
case kExprBlock: |
+ case kExprTryCatch: |
+ case kExprTryCatchFinally: |
+ case kExprTryFinally: |
os << " // @" << i.pc_offset(); |
control_depth++; |
break; |