Chromium Code Reviews| Index: src/wasm/ast-decoder.cc |
| diff --git a/src/wasm/ast-decoder.cc b/src/wasm/ast-decoder.cc |
| index 28723c5fa6ae3410bdaf66d14ef684fe188c5c63..068f25c177aa35fc60469669633b94d56a87eb4a 100644 |
| --- a/src/wasm/ast-decoder.cc |
| +++ b/src/wasm/ast-decoder.cc |
| @@ -68,12 +68,49 @@ struct Control { |
| 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* cont_env; // continuation environment (only for try with finally). |
|
bradnelson
2016/08/11 00:39:29
This name is confusing, implies continue as in the
John
2016/08/11 11:22:27
Yeah, cont_env threw me off a few times, but I cou
|
| 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; } |
| + 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* cont_env) { |
| + return {pc, stack_depth, end_env, nullptr, catch_env, |
| + cont_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,6 +670,149 @@ 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->cont_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->cont_env is |
| + // the "continuation" environment - essentially, the environment |
| + // where the try block appeared. |
| + Goto(ssa_env_, c->end_env); |
| + |
| + PopUpTo(c->stack_depth); |
| + |
| + // The current environment becomes end_env, and cont_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 cont_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->cont_env; |
| + SetEnv("finally:begin", finally_env); |
| + c->cont_env = nullptr; |
| + |
| + break; |
| + } |
| case kExprLoop: { |
| // The break environment is the outer environment. |
| SsaEnv* break_env = ssa_env_; |
| @@ -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->cont_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* cont_env) { |
| + const int stack_depth = static_cast<int>(stack_.size()); |
| + control_.emplace_back( |
| + Control::Try(pc_, stack_depth, end_env, catch_env, cont_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; |