| 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;
|
|
|