diff options
Diffstat (limited to 'deps/v8/src/wasm/ast-decoder.cc')
-rw-r--r-- | deps/v8/src/wasm/ast-decoder.cc | 1047 |
1 files changed, 564 insertions, 483 deletions
diff --git a/deps/v8/src/wasm/ast-decoder.cc b/deps/v8/src/wasm/ast-decoder.cc index 0f192508ba..02d1db5bda 100644 --- a/deps/v8/src/wasm/ast-decoder.cc +++ b/deps/v8/src/wasm/ast-decoder.cc @@ -7,7 +7,7 @@ #include "src/bit-vector.h" #include "src/flags.h" #include "src/handles.h" -#include "src/zone-containers.h" +#include "src/zone/zone-containers.h" #include "src/wasm/ast-decoder.h" #include "src/wasm/decoder.h" @@ -36,6 +36,8 @@ namespace wasm { error("Invalid opcode (enable with --" #flag ")"); \ break; \ } +// TODO(titzer): this is only for intermediate migration. +#define IMPLICIT_FUNCTION_END 1 // An SsaEnv environment carries the current local variable renaming // as well as the current effect and control dependency in the TF graph. @@ -68,62 +70,82 @@ struct Value { LocalType type; }; -// 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). - 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. +struct TryInfo : public ZoneObject { + SsaEnv* catch_env; + TFNode* exception; - bool is_if() const { return *pc == kExprIf; } + explicit TryInfo(SsaEnv* c) : catch_env(c), exception(nullptr) {} +}; - bool is_try() const { - return *pc == kExprTryCatch || *pc == kExprTryCatchFinally || - *pc == kExprTryFinally; - } +struct MergeValues { + uint32_t arity; + union { + Value* array; + Value first; + } vals; // Either multiple values or a single value. - bool has_catch() const { - return *pc == kExprTryCatch || *pc == kExprTryCatchFinally; + Value& first() { + DCHECK_GT(arity, 0u); + return arity == 1 ? vals.first : vals.array[0]; } +}; - bool has_finally() const { - return *pc == kExprTryCatchFinally || *pc == kExprTryFinally; - } +static Value* NO_VALUE = nullptr; + +enum ControlKind { kControlIf, kControlBlock, kControlLoop, kControlTry }; + +// An entry on the control stack (i.e. if, block, loop). +struct Control { + const byte* pc; + ControlKind kind; + 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). + TryInfo* try_info; // Information used for compiling try statements. + int32_t previous_catch; // The previous Control (on the stack) with a catch. + + // Values merged into the end of this control construct. + MergeValues merge; + + inline bool is_if() const { return kind == kControlIf; } + inline bool is_block() const { return kind == kControlBlock; } + inline bool is_loop() const { return kind == kControlLoop; } + inline bool is_try() const { return kind == kControlTry; } // 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 Block(const byte* pc, int stack_depth, SsaEnv* end_env, + int32_t previous_catch) { + return {pc, kControlBlock, stack_depth, end_env, + nullptr, nullptr, previous_catch, {0, {NO_VALUE}}}; } 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}; + SsaEnv* false_env, int32_t previous_catch) { + return {pc, kControlIf, stack_depth, end_env, + false_env, nullptr, previous_catch, {0, {NO_VALUE}}}; } - 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 Loop(const byte* pc, int stack_depth, SsaEnv* end_env, + int32_t previous_catch) { + return {pc, kControlLoop, stack_depth, end_env, + nullptr, nullptr, previous_catch, {0, {NO_VALUE}}}; } 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}; + Zone* zone, SsaEnv* catch_env, int32_t previous_catch) { + DCHECK_NOT_NULL(catch_env); + TryInfo* try_info = new (zone) TryInfo(catch_env); + return {pc, kControlTry, stack_depth, end_env, + nullptr, try_info, previous_catch, {0, {NO_VALUE}}}; } }; // Macros that build nodes only if there is a graph and the current SSA // environment is reachable from start. This avoids problems with malformed // TF graphs when decoding inputs that have unreachable code. -#define BUILD(func, ...) (build() ? builder_->func(__VA_ARGS__) : nullptr) -#define BUILD0(func) (build() ? builder_->func() : nullptr) +#define BUILD(func, ...) \ + (build() ? CheckForException(builder_->func(__VA_ARGS__)) : nullptr) +#define BUILD0(func) (build() ? CheckForException(builder_->func()) : nullptr) // Generic Wasm bytecode decoder with utilities for decoding operands, // lengths, etc. @@ -150,17 +172,18 @@ class WasmDecoder : public Decoder { } return true; } - error(pc, pc + 1, "invalid local index"); + error(pc, pc + 1, "invalid local index: %u", operand.index); return false; } inline bool Validate(const byte* pc, GlobalIndexOperand& operand) { ModuleEnv* m = module_; if (m && m->module && operand.index < m->module->globals.size()) { - operand.type = m->module->globals[operand.index].type; + operand.global = &m->module->globals[operand.index]; + operand.type = operand.global->type; return true; } - error(pc, pc + 1, "invalid global index"); + error(pc, pc + 1, "invalid global index: %u", operand.index); return false; } @@ -175,16 +198,9 @@ class WasmDecoder : public Decoder { inline bool Validate(const byte* pc, CallFunctionOperand& operand) { if (Complete(pc, operand)) { - uint32_t expected = static_cast<uint32_t>(operand.sig->parameter_count()); - if (operand.arity != expected) { - error(pc, pc + 1, - "arity mismatch in direct function call (expected %u, got %u)", - expected, operand.arity); - return false; - } return true; } - error(pc, pc + 1, "invalid function index"); + error(pc, pc + 1, "invalid function index: %u", operand.index); return false; } @@ -199,161 +215,28 @@ class WasmDecoder : public Decoder { inline bool Validate(const byte* pc, CallIndirectOperand& operand) { if (Complete(pc, operand)) { - uint32_t expected = static_cast<uint32_t>(operand.sig->parameter_count()); - if (operand.arity != expected) { - error(pc, pc + 1, - "arity mismatch in indirect function call (expected %u, got %u)", - expected, operand.arity); - return false; - } - return true; - } - error(pc, pc + 1, "invalid signature index"); - return false; - } - - inline bool Complete(const byte* pc, CallImportOperand& operand) { - ModuleEnv* m = module_; - if (m && m->module && operand.index < m->module->import_table.size()) { - operand.sig = m->module->import_table[operand.index].sig; - return true; - } - return false; - } - - inline bool Validate(const byte* pc, CallImportOperand& operand) { - if (Complete(pc, operand)) { - uint32_t expected = static_cast<uint32_t>(operand.sig->parameter_count()); - if (operand.arity != expected) { - error(pc, pc + 1, "arity mismatch in import call (expected %u, got %u)", - expected, operand.arity); - return false; - } return true; } - error(pc, pc + 1, "invalid signature index"); + error(pc, pc + 1, "invalid signature index: #%u", operand.index); return false; } inline bool Validate(const byte* pc, BreakDepthOperand& operand, ZoneVector<Control>& control) { - if (operand.arity > 1) { - error(pc, pc + 1, "invalid arity for br or br_if"); - return false; - } if (operand.depth < control.size()) { operand.target = &control[control.size() - operand.depth - 1]; return true; } - error(pc, pc + 1, "invalid break depth"); + error(pc, pc + 1, "invalid break depth: %u", operand.depth); return false; } bool Validate(const byte* pc, BranchTableOperand& operand, size_t block_depth) { - if (operand.arity > 1) { - error(pc, pc + 1, "invalid arity for break"); - return false; - } - // Verify table. - for (uint32_t i = 0; i < operand.table_count + 1; ++i) { - uint32_t target = operand.read_entry(this, i); - if (target >= block_depth) { - error(operand.table + i * 2, "improper branch in br_table"); - return false; - } - } + // TODO(titzer): add extra redundant validation for br_table here? return true; } - unsigned OpcodeArity(const byte* pc) { -#define DECLARE_ARITY(name, ...) \ - static const LocalType kTypes_##name[] = {__VA_ARGS__}; \ - static const int kArity_##name = \ - static_cast<int>(arraysize(kTypes_##name) - 1); - - FOREACH_SIGNATURE(DECLARE_ARITY); -#undef DECLARE_ARITY - - switch (static_cast<WasmOpcode>(*pc)) { - case kExprI8Const: - case kExprI32Const: - case kExprI64Const: - case kExprF64Const: - case kExprF32Const: - case kExprGetLocal: - case kExprGetGlobal: - case kExprNop: - 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: { - BreakDepthOperand operand(this, pc); - return operand.arity; - } - case kExprBrIf: { - BreakDepthOperand operand(this, pc); - return 1 + operand.arity; - } - case kExprBrTable: { - BranchTableOperand operand(this, pc); - return 1 + operand.arity; - } - - case kExprIf: - return 1; - case kExprSelect: - return 3; - - case kExprCallFunction: { - CallFunctionOperand operand(this, pc); - return operand.arity; - } - case kExprCallIndirect: { - CallIndirectOperand operand(this, pc); - return 1 + operand.arity; - } - case kExprCallImport: { - CallImportOperand operand(this, pc); - return operand.arity; - } - case kExprReturn: { - ReturnArityOperand operand(this, pc); - return operand.arity; - } - -#define DECLARE_OPCODE_CASE(name, opcode, sig) \ - case kExpr##name: \ - return kArity_##sig; - - FOREACH_LOAD_MEM_OPCODE(DECLARE_OPCODE_CASE) - FOREACH_STORE_MEM_OPCODE(DECLARE_OPCODE_CASE) - FOREACH_MISC_MEM_OPCODE(DECLARE_OPCODE_CASE) - FOREACH_SIMPLE_OPCODE(DECLARE_OPCODE_CASE) - FOREACH_SIMPLE_MEM_OPCODE(DECLARE_OPCODE_CASE) - FOREACH_ASMJS_COMPAT_OPCODE(DECLARE_OPCODE_CASE) - FOREACH_SIMD_OPCODE(DECLARE_OPCODE_CASE) -#undef DECLARE_OPCODE_CASE - default: - UNREACHABLE(); - return 0; - } - } - unsigned OpcodeLength(const byte* pc) { switch (static_cast<WasmOpcode>(*pc)) { #define DECLARE_OPCODE_CASE(name, opcode, sig) case kExpr##name: @@ -361,7 +244,7 @@ class WasmDecoder : public Decoder { FOREACH_STORE_MEM_OPCODE(DECLARE_OPCODE_CASE) #undef DECLARE_OPCODE_CASE { - MemoryAccessOperand operand(this, pc); + MemoryAccessOperand operand(this, pc, UINT32_MAX); return 1 + operand.length; } case kExprBr: @@ -383,12 +266,17 @@ class WasmDecoder : public Decoder { CallIndirectOperand operand(this, pc); return 1 + operand.length; } - case kExprCallImport: { - CallImportOperand operand(this, pc); + + case kExprTry: + case kExprIf: // fall thru + case kExprLoop: + case kExprBlock: { + BlockTypeOperand operand(this, pc); return 1 + operand.length; } case kExprSetLocal: + case kExprTeeLocal: case kExprGetLocal: case kExprCatch: { LocalIndexOperand operand(this, pc); @@ -396,7 +284,8 @@ class WasmDecoder : public Decoder { } case kExprBrTable: { BranchTableOperand operand(this, pc); - return 1 + operand.length; + BranchTableIterator iterator(this, operand); + return 1 + iterator.length(); } case kExprI32Const: { ImmI32Operand operand(this, pc); @@ -412,17 +301,14 @@ class WasmDecoder : public Decoder { return 5; case kExprF64Const: return 9; - case kExprReturn: { - ReturnArityOperand operand(this, pc); - return 1 + operand.length; - } - default: return 1; } } }; +static const int32_t kNullCatch = -1; + // The full WASM decoder for bytecode. Both verifies bytecode and generates // a TurboFan IR graph. class WasmFullDecoder : public WasmDecoder { @@ -434,7 +320,9 @@ class WasmFullDecoder : public WasmDecoder { base_(body.base), local_type_vec_(zone), stack_(zone), - control_(zone) { + control_(zone), + last_end_found_(false), + current_catch_(kNullCatch) { local_types_ = &local_type_vec_; } @@ -447,7 +335,7 @@ class WasmFullDecoder : public WasmDecoder { control_.clear(); if (end_ < pc_) { - error(pc_, "function body end < start"); + error("function body end < start"); return false; } @@ -457,23 +345,55 @@ class WasmFullDecoder : public WasmDecoder { if (failed()) return TraceFailed(); +#if IMPLICIT_FUNCTION_END + // With implicit end support (old style), the function block + // remains on the stack. Other control blocks are an error. + if (control_.size() > 1) { + error(pc_, control_.back().pc, "unterminated control structure"); + return TraceFailed(); + } + + // Assume an implicit end to the function body block. + if (control_.size() == 1) { + Control* c = &control_.back(); + if (ssa_env_->go()) { + FallThruTo(c); + } + + if (c->end_env->go()) { + // Push the end values onto the stack. + stack_.resize(c->stack_depth); + if (c->merge.arity == 1) { + stack_.push_back(c->merge.vals.first); + } else { + for (unsigned i = 0; i < c->merge.arity; i++) { + stack_.push_back(c->merge.vals.array[i]); + } + } + + TRACE(" @%-8d #xx:%-20s|", startrel(pc_), "ImplicitReturn"); + SetEnv("function:end", c->end_env); + DoReturn(); + TRACE("\n"); + } + } +#else if (!control_.empty()) { error(pc_, control_.back().pc, "unterminated control structure"); return TraceFailed(); } - if (ssa_env_->go()) { - TRACE(" @%-6d #xx:%-20s|", startrel(pc_), "ImplicitReturn"); - DoReturn(); - if (failed()) return TraceFailed(); - TRACE("\n"); + if (!last_end_found_) { + error("function body must end with \"end\" opcode."); + return false; } +#endif if (FLAG_trace_wasm_decode_time) { double ms = decode_timer.Elapsed().InMillisecondsF(); - PrintF("wasm-decode ok (%0.3f ms)\n\n", ms); + PrintF("wasm-decode %s (%0.3f ms)\n\n", ok() ? "ok" : "failed", ms); } else { - TRACE("wasm-decode ok\n\n"); + TRACE("wasm-decode %s\n\n", ok() ? "ok" : "failed"); } return true; @@ -526,6 +446,11 @@ class WasmFullDecoder : public WasmDecoder { ZoneVector<LocalType> local_type_vec_; // types of local variables. ZoneVector<Value> stack_; // stack of values. ZoneVector<Control> control_; // stack of blocks, loops, and ifs. + bool last_end_found_; + + int32_t current_catch_; + + TryInfo* current_try_info() { return control_[current_catch_].try_info; } inline bool build() { return builder_ && ssa_env_->go(); } @@ -574,6 +499,8 @@ class WasmFullDecoder : public WasmDecoder { return builder_->Float32Constant(0); case kAstF64: return builder_->Float64Constant(0); + case kAstS128: + return builder_->DefaultS128Value(); default: UNREACHABLE(); return nullptr; @@ -603,8 +530,13 @@ class WasmFullDecoder : public WasmDecoder { } // Decode local declarations, if any. uint32_t entries = consume_u32v("local decls count"); + TRACE("local decls count: %u\n", entries); while (entries-- > 0 && pc_ < limit_) { uint32_t count = consume_u32v("local count"); + if (count > kMaxNumWasmLocals) { + error(pc_ - 1, "local count too large"); + return; + } byte code = consume_u8("local type"); LocalType type; switch (code) { @@ -620,6 +552,9 @@ class WasmFullDecoder : public WasmDecoder { case kLocalF64: type = kAstF64; break; + case kLocalS128: + type = kAstS128; + break; default: error(pc_ - 1, "invalid local type"); return; @@ -636,82 +571,68 @@ class WasmFullDecoder : public WasmDecoder { reinterpret_cast<const void*>(limit_), baserel(pc_), static_cast<int>(limit_ - start_), builder_ ? "graph building" : ""); + { + // Set up initial function block. + SsaEnv* break_env = ssa_env_; + SetEnv("initial env", Steal(break_env)); + PushBlock(break_env); + Control* c = &control_.back(); + c->merge.arity = static_cast<uint32_t>(sig_->return_count()); + + if (c->merge.arity == 1) { + c->merge.vals.first = {pc_, nullptr, sig_->GetReturn(0)}; + } else if (c->merge.arity > 1) { + c->merge.vals.array = zone_->NewArray<Value>(c->merge.arity); + for (unsigned i = 0; i < c->merge.arity; i++) { + c->merge.vals.array[i] = {pc_, nullptr, sig_->GetReturn(i)}; + } + } + } + if (pc_ >= limit_) return; // Nothing to do. while (true) { // decoding loop. unsigned len = 1; WasmOpcode opcode = static_cast<WasmOpcode>(*pc_); - TRACE(" @%-6d #%02x:%-20s|", startrel(pc_), opcode, - WasmOpcodes::ShortOpcodeName(opcode)); + if (!WasmOpcodes::IsPrefixOpcode(opcode)) { + TRACE(" @%-8d #%02x:%-20s|", startrel(pc_), opcode, + WasmOpcodes::ShortOpcodeName(opcode)); + } FunctionSig* sig = WasmOpcodes::Signature(opcode); if (sig) { - // Fast case of a simple operator. - TFNode* node; - switch (sig->parameter_count()) { - case 1: { - Value val = Pop(0, sig->GetParam(0)); - node = BUILD(Unop, opcode, val.node, position()); - break; - } - case 2: { - Value rval = Pop(1, sig->GetParam(1)); - Value lval = Pop(0, sig->GetParam(0)); - node = BUILD(Binop, opcode, lval.node, rval.node, position()); - break; - } - default: - UNREACHABLE(); - node = nullptr; - break; - } - Push(GetReturnType(sig), node); + BuildSimpleOperator(opcode, sig); } else { // Complex bytecode. switch (opcode) { case kExprNop: - Push(kAstStmt, nullptr); break; case kExprBlock: { // The break environment is the outer environment. + BlockTypeOperand operand(this, pc_); SsaEnv* break_env = ssa_env_; PushBlock(break_env); SetEnv("block:start", Steal(break_env)); + SetBlockType(&control_.back(), operand); + len = 1 + operand.length; break; } case kExprThrow: { CHECK_PROTOTYPE_OPCODE(wasm_eh_prototype); - Pop(0, kAstI32); - - // TODO(jpp): start exception propagation. + Value value = Pop(0, kAstI32); + BUILD(Throw, value.node); break; } - case kExprTryCatch: { + case kExprTry: { CHECK_PROTOTYPE_OPCODE(wasm_eh_prototype); + BlockTypeOperand operand(this, pc_); SsaEnv* outer_env = ssa_env_; SsaEnv* try_env = Steal(outer_env); - SsaEnv* catch_env = Split(try_env); - PushTry(outer_env, catch_env, nullptr); + SsaEnv* catch_env = UnreachableEnv(); + PushTry(outer_env, catch_env); SetEnv("try_catch:start", try_env); - break; - } - case kExprTryCatchFinally: { - CHECK_PROTOTYPE_OPCODE(wasm_eh_prototype); - 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: { - 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); + SetBlockType(&control_.back(), operand); + len = 1 + operand.length; break; } case kExprCatch: { @@ -720,97 +641,57 @@ class WasmFullDecoder : public WasmDecoder { len = 1 + operand.length; if (control_.empty()) { - error(pc_, "catch does not match a any try"); + error("catch does not match any try"); break; } Control* c = &control_.back(); - if (!c->has_catch()) { - error(pc_, "catch does not match a try with catch"); + if (!c->is_try()) { + error("catch does not match any try"); break; } - if (c->catch_env == nullptr) { + if (c->try_info->catch_env == nullptr) { error(pc_, "catch already present for try with catch"); break; } - Goto(ssa_env_, c->end_env); + if (ssa_env_->go()) { + MergeValuesInto(c); + } + stack_.resize(c->stack_depth); - SsaEnv* catch_env = c->catch_env; - c->catch_env = nullptr; + DCHECK_NOT_NULL(c->try_info); + SsaEnv* catch_env = c->try_info->catch_env; + c->try_info->catch_env = nullptr; SetEnv("catch:begin", catch_env); + current_catch_ = c->previous_catch; 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; + TFNode* exception_as_i32 = + BUILD(Catch, c->try_info->exception, position()); + ssa_env_->locals[operand.index] = exception_as_i32; } } - PopUpTo(c->stack_depth); - - 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: { - // The break environment is the outer environment. - SsaEnv* break_env = ssa_env_; - PushBlock(break_env); - SsaEnv* finish_try_env = Steal(break_env); + BlockTypeOperand operand(this, pc_); + SsaEnv* finish_try_env = Steal(ssa_env_); // The continue environment is the inner environment. PrepareForLoop(pc_, finish_try_env); SetEnv("loop:start", Split(finish_try_env)); ssa_env_->SetNotMerged(); PushLoop(finish_try_env); + SetBlockType(&control_.back(), operand); + len = 1 + operand.length; break; } case kExprIf: { // Condition on top of stack. Split environments for branches. + BlockTypeOperand operand(this, pc_); Value cond = Pop(0, kAstI32); TFNode* if_true = nullptr; TFNode* if_false = nullptr; @@ -822,11 +703,13 @@ class WasmFullDecoder : public WasmDecoder { true_env->control = if_true; PushIf(end_env, false_env); SetEnv("if:true", true_env); + SetBlockType(&control_.back(), operand); + len = 1 + operand.length; break; } case kExprElse: { if (control_.empty()) { - error(pc_, "else does not match any if"); + error("else does not match any if"); break; } Control* c = &control_.back(); @@ -838,31 +721,38 @@ class WasmFullDecoder : public WasmDecoder { error(pc_, c->pc, "else already present for if"); break; } - Value val = PopUpTo(c->stack_depth); - MergeInto(c->end_env, &c->node, &c->type, val); + FallThruTo(c); // Switch to environment for false branch. + stack_.resize(c->stack_depth); SetEnv("if_else:false", c->false_env); c->false_env = nullptr; // record that an else is already seen break; } case kExprEnd: { if (control_.empty()) { - error(pc_, "end does not match any if or block"); - break; + error("end does not match any if, try, or block"); + return; } 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"; - } else if (c->is_if()) { + if (c->is_loop()) { + // A loop just leaves the values on the stack. + TypeCheckLoopFallThru(c); + PopControl(); + SetEnv("loop:end", ssa_env_); + break; + } + 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); - val = {val.pc, nullptr, kAstStmt}; + if (ssa_env_->go() && stack_.size() != c->stack_depth) { + error("end of if expected empty stack"); + stack_.resize(c->stack_depth); + } + if (c->merge.arity > 0) { + error("non-void one-armed if"); + } name = "if:merge"; } else { // End the false branch of a two-armed if. @@ -871,28 +761,41 @@ class WasmFullDecoder : public WasmDecoder { } else if (c->is_try()) { 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"); + // validate that catch was seen. + if (c->try_info->catch_env != nullptr) { + error(pc_, "missing catch in try"); break; } + } + FallThruTo(c); + SetEnv(name, c->end_env); - if (c->finish_try_env != nullptr) { - error(pc_, "missing finally in try with finally"); - break; + // Push the end values onto the stack. + stack_.resize(c->stack_depth); + if (c->merge.arity == 1) { + stack_.push_back(c->merge.vals.first); + } else { + for (unsigned i = 0; i < c->merge.arity; i++) { + stack_.push_back(c->merge.vals.array[i]); } } - if (ssa_env_->go()) { - MergeInto(c->end_env, &c->node, &c->type, val); + PopControl(); + + if (control_.empty()) { + // If the last (implicit) control was popped, check we are at end. + if (pc_ + 1 != end_) { + error(pc_, pc_ + 1, "trailing code after function end"); + } + last_end_found_ = true; + if (ssa_env_->go()) { + // The result of the block is the return value. + TRACE(" @%-8d #xx:%-20s|", startrel(pc_), "ImplicitReturn"); + DoReturn(); + TRACE("\n"); + } + return; } - SetEnv(name, c->end_env); - stack_.resize(c->stack_depth); - Push(c->type, c->node); - control_.pop_back(); break; } case kExprSelect: { @@ -901,7 +804,7 @@ class WasmFullDecoder : public WasmDecoder { Value tval = Pop(); if (tval.type == kAstStmt || tval.type != fval.type) { if (tval.type != kAstEnd && fval.type != kAstEnd) { - error(pc_, "type mismatch in select"); + error("type mismatch in select"); break; } } @@ -923,39 +826,33 @@ class WasmFullDecoder : public WasmDecoder { } case kExprBr: { BreakDepthOperand operand(this, pc_); - Value val = {pc_, nullptr, kAstStmt}; - if (operand.arity) val = Pop(); if (Validate(pc_, operand, control_)) { - BreakTo(operand.target, val); + BreakTo(operand.depth); } len = 1 + operand.length; - Push(kAstEnd, nullptr); + EndControl(); break; } case kExprBrIf: { BreakDepthOperand operand(this, pc_); - Value cond = Pop(operand.arity, kAstI32); - Value val = {pc_, nullptr, kAstStmt}; - if (operand.arity == 1) val = Pop(); - if (Validate(pc_, operand, control_)) { + Value cond = Pop(0, kAstI32); + if (ok() && Validate(pc_, operand, control_)) { SsaEnv* fenv = ssa_env_; SsaEnv* tenv = Split(fenv); fenv->SetNotMerged(); BUILD(Branch, cond.node, &tenv->control, &fenv->control); ssa_env_ = tenv; - BreakTo(operand.target, val); + BreakTo(operand.depth); ssa_env_ = fenv; } len = 1 + operand.length; - Push(kAstStmt, nullptr); break; } case kExprBrTable: { BranchTableOperand operand(this, pc_); + BranchTableIterator iterator(this, operand); if (Validate(pc_, operand, control_.size())) { - Value key = Pop(operand.arity, kAstI32); - Value val = {pc_, nullptr, kAstStmt}; - if (operand.arity == 1) val = Pop(); + Value key = Pop(0, kAstI32); if (failed()) break; SsaEnv* break_env = ssa_env_; @@ -965,42 +862,43 @@ class WasmFullDecoder : public WasmDecoder { SsaEnv* copy = Steal(break_env); ssa_env_ = copy; - for (uint32_t i = 0; i < operand.table_count + 1; ++i) { - uint16_t target = operand.read_entry(this, i); + while (iterator.has_next()) { + uint32_t i = iterator.cur_index(); + const byte* pos = iterator.pc(); + uint32_t target = iterator.next(); + if (target >= control_.size()) { + error(pos, "improper branch in br_table"); + break; + } ssa_env_ = Split(copy); ssa_env_->control = (i == operand.table_count) ? BUILD(IfDefault, sw) : BUILD(IfValue, i, sw); - int depth = target; - Control* c = &control_[control_.size() - depth - 1]; - MergeInto(c->end_env, &c->node, &c->type, val); + BreakTo(target); } } else { // Only a default target. Do the equivalent of br. - uint16_t target = operand.read_entry(this, 0); - int depth = target; - Control* c = &control_[control_.size() - depth - 1]; - MergeInto(c->end_env, &c->node, &c->type, val); + const byte* pos = iterator.pc(); + uint32_t target = iterator.next(); + if (target >= control_.size()) { + error(pos, "improper branch in br_table"); + break; + } + BreakTo(target); } // br_table ends the control flow like br. ssa_env_ = break_env; - Push(kAstStmt, nullptr); } - len = 1 + operand.length; + len = 1 + iterator.length(); break; } case kExprReturn: { - ReturnArityOperand operand(this, pc_); - if (operand.arity != sig_->return_count()) { - error(pc_, pc_ + 1, "arity mismatch in return"); - } DoReturn(); - len = 1 + operand.length; break; } case kExprUnreachable: { - Push(kAstEnd, BUILD(Unreachable, position())); - ssa_env_->Kill(SsaEnv::kControlEnd); + BUILD(Unreachable, position()); + EndControl(); break; } case kExprI8Const: { @@ -1050,11 +948,24 @@ class WasmFullDecoder : public WasmDecoder { if (Validate(pc_, operand)) { Value val = Pop(0, local_type_vec_[operand.index]); if (ssa_env_->locals) ssa_env_->locals[operand.index] = val.node; + } + len = 1 + operand.length; + break; + } + case kExprTeeLocal: { + LocalIndexOperand operand(this, pc_); + if (Validate(pc_, operand)) { + Value val = Pop(0, local_type_vec_[operand.index]); + if (ssa_env_->locals) ssa_env_->locals[operand.index] = val.node; Push(val.type, val.node); } len = 1 + operand.length; break; } + case kExprDrop: { + Pop(); + break; + } case kExprGetGlobal: { GlobalIndexOperand operand(this, pc_); if (Validate(pc_, operand)) { @@ -1066,9 +977,13 @@ class WasmFullDecoder : public WasmDecoder { case kExprSetGlobal: { GlobalIndexOperand operand(this, pc_); if (Validate(pc_, operand)) { - Value val = Pop(0, operand.type); - BUILD(SetGlobal, operand.index, val.node); - Push(val.type, val.node); + if (operand.global->mutability) { + Value val = Pop(0, operand.type); + BUILD(SetGlobal, operand.index, val.node); + } else { + error(pc_, pc_ + 1, "immutable global #%u cannot be assigned", + operand.index); + } } len = 1 + operand.length; break; @@ -1088,7 +1003,6 @@ class WasmFullDecoder : public WasmDecoder { case kExprI32LoadMem: len = DecodeLoadMem(kAstI32, MachineType::Int32()); break; - case kExprI64LoadMem8S: len = DecodeLoadMem(kAstI64, MachineType::Int8()); break; @@ -1143,17 +1057,24 @@ class WasmFullDecoder : public WasmDecoder { case kExprF64StoreMem: len = DecodeStoreMem(kAstF64, MachineType::Float64()); break; - + case kExprGrowMemory: + if (module_->origin != kAsmJsOrigin) { + Value val = Pop(0, kAstI32); + Push(kAstI32, BUILD(GrowMemory, val.node)); + } else { + error("grow_memory is not supported for asmjs modules"); + } + break; case kExprMemorySize: - Push(kAstI32, BUILD(MemSize, 0)); + Push(kAstI32, BUILD(CurrentMemoryPages)); break; case kExprCallFunction: { CallFunctionOperand operand(this, pc_); if (Validate(pc_, operand)) { TFNode** buffer = PopArgs(operand.sig); - TFNode* call = - BUILD(CallDirect, operand.index, buffer, position()); - Push(GetReturnType(operand.sig), call); + TFNode** rets = nullptr; + BUILD(CallDirect, operand.index, buffer, &rets, position()); + PushReturns(operand.sig, rets); } len = 1 + operand.length; break; @@ -1161,23 +1082,12 @@ class WasmFullDecoder : public WasmDecoder { case kExprCallIndirect: { CallIndirectOperand operand(this, pc_); if (Validate(pc_, operand)) { - TFNode** buffer = PopArgs(operand.sig); Value index = Pop(0, kAstI32); - if (buffer) buffer[0] = index.node; - TFNode* call = - BUILD(CallIndirect, operand.index, buffer, position()); - Push(GetReturnType(operand.sig), call); - } - len = 1 + operand.length; - break; - } - case kExprCallImport: { - CallImportOperand operand(this, pc_); - if (Validate(pc_, operand)) { TFNode** buffer = PopArgs(operand.sig); - TFNode* call = - BUILD(CallImport, operand.index, buffer, position()); - Push(GetReturnType(operand.sig), call); + if (buffer) buffer[0] = index.node; + TFNode** rets = nullptr; + BUILD(CallIndirect, operand.index, buffer, &rets, position()); + PushReturns(operand.sig, rets); } len = 1 + operand.length; break; @@ -1187,20 +1097,34 @@ class WasmFullDecoder : public WasmDecoder { len++; byte simd_index = *(pc_ + 1); opcode = static_cast<WasmOpcode>(opcode << 8 | simd_index); - DecodeSimdOpcode(opcode); + TRACE(" @%-4d #%02x #%02x:%-20s|", startrel(pc_), kSimdPrefix, + simd_index, WasmOpcodes::ShortOpcodeName(opcode)); + len += DecodeSimdOpcode(opcode); break; } - default: - error("Invalid opcode"); - return; + default: { + // Deal with special asmjs opcodes. + if (module_ && module_->origin == kAsmJsOrigin) { + sig = WasmOpcodes::AsmjsSignature(opcode); + if (sig) { + BuildSimpleOperator(opcode, sig); + } + } else { + error("Invalid opcode"); + return; + } + } } - } // end complex bytecode + } #if DEBUG if (FLAG_trace_wasm_decoder) { for (size_t i = 0; i < stack_.size(); ++i) { Value& val = stack_[i]; WasmOpcode opcode = static_cast<WasmOpcode>(*val.pc); + if (WasmOpcodes::IsPrefixOpcode(opcode)) { + opcode = static_cast<WasmOpcode>(opcode << 8 | *(val.pc + 1)); + } PrintF(" %c@%d:%s", WasmOpcodes::ShortNameOf(val.type), static_cast<int>(val.pc - start_), WasmOpcodes::ShortOpcodeName(opcode)); @@ -1215,7 +1139,8 @@ class WasmFullDecoder : public WasmDecoder { PrintF("[%u]", operand.index); break; } - case kExprSetLocal: { + case kExprSetLocal: // fallthru + case kExprTeeLocal: { LocalIndexOperand operand(this, val.pc); PrintF("[%u]", operand.index); break; @@ -1234,7 +1159,21 @@ class WasmFullDecoder : public WasmDecoder { return; } } // end decode loop - } // end DecodeFunctionBody() + } + + void EndControl() { ssa_env_->Kill(SsaEnv::kControlEnd); } + + void SetBlockType(Control* c, BlockTypeOperand& operand) { + c->merge.arity = operand.arity; + if (c->merge.arity == 1) { + c->merge.vals.first = {pc_, nullptr, operand.read_entry(0)}; + } else if (c->merge.arity > 1) { + c->merge.vals.array = zone_->NewArray<Value>(c->merge.arity); + for (unsigned i = 0; i < c->merge.arity; i++) { + c->merge.vals.array[i] = {pc_, nullptr, operand.read_entry(i)}; + } + } + } TFNode** PopArgs(FunctionSig* sig) { if (build()) { @@ -1260,27 +1199,35 @@ class WasmFullDecoder : public WasmDecoder { void PushBlock(SsaEnv* end_env) { const int stack_depth = static_cast<int>(stack_.size()); - control_.emplace_back(Control::Block(pc_, stack_depth, end_env)); + control_.emplace_back( + Control::Block(pc_, stack_depth, end_env, current_catch_)); } void PushLoop(SsaEnv* end_env) { const int stack_depth = static_cast<int>(stack_.size()); - control_.emplace_back(Control::Loop(pc_, stack_depth, end_env)); + control_.emplace_back( + Control::Loop(pc_, stack_depth, end_env, current_catch_)); } void PushIf(SsaEnv* end_env, SsaEnv* false_env) { const int stack_depth = static_cast<int>(stack_.size()); - control_.emplace_back(Control::If(pc_, stack_depth, end_env, false_env)); + control_.emplace_back( + Control::If(pc_, stack_depth, end_env, false_env, current_catch_)); } - 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()); - control_.emplace_back( - Control::Try(pc_, stack_depth, end_env, catch_env, finish_try_env)); + control_.emplace_back(Control::Try(pc_, stack_depth, end_env, zone_, + catch_env, current_catch_)); + current_catch_ = static_cast<int32_t>(control_.size() - 1); } + void PopControl() { control_.pop_back(); } + int DecodeLoadMem(LocalType type, MachineType mem_type) { - MemoryAccessOperand operand(this, pc_); + MemoryAccessOperand operand(this, pc_, + ElementSizeLog2Of(mem_type.representation())); + Value index = Pop(0, kAstI32); TFNode* node = BUILD(LoadMem, type, mem_type, index.node, operand.offset, operand.alignment, position()); @@ -1289,24 +1236,45 @@ class WasmFullDecoder : public WasmDecoder { } int DecodeStoreMem(LocalType type, MachineType mem_type) { - MemoryAccessOperand operand(this, pc_); + MemoryAccessOperand operand(this, pc_, + ElementSizeLog2Of(mem_type.representation())); Value val = Pop(1, type); Value index = Pop(0, kAstI32); BUILD(StoreMem, mem_type, index.node, operand.offset, operand.alignment, val.node, position()); - Push(type, val.node); return 1 + operand.length; } - void DecodeSimdOpcode(WasmOpcode opcode) { - FunctionSig* sig = WasmOpcodes::Signature(opcode); - compiler::NodeVector inputs(sig->parameter_count(), zone_); - for (size_t i = sig->parameter_count(); i > 0; i--) { - Value val = Pop(static_cast<int>(i - 1), sig->GetParam(i - 1)); - inputs[i - 1] = val.node; + unsigned DecodeSimdOpcode(WasmOpcode opcode) { + unsigned len = 0; + switch (opcode) { + case kExprI32x4ExtractLane: { + uint8_t lane = this->checked_read_u8(pc_, 2, "lane number"); + if (lane < 0 || lane > 3) { + error(pc_, pc_ + 2, "invalid extract lane value"); + } + TFNode* input = Pop(0, LocalType::kSimd128).node; + TFNode* node = BUILD(SimdExtractLane, opcode, lane, input); + Push(LocalType::kWord32, node); + len++; + break; + } + default: { + FunctionSig* sig = WasmOpcodes::Signature(opcode); + if (sig != nullptr) { + compiler::NodeVector inputs(sig->parameter_count(), zone_); + for (size_t i = sig->parameter_count(); i > 0; i--) { + Value val = Pop(static_cast<int>(i - 1), sig->GetParam(i - 1)); + inputs[i - 1] = val.node; + } + TFNode* node = BUILD(SimdOp, opcode, inputs); + Push(GetReturnType(sig), node); + } else { + error("invalid simd opcode"); + } + } } - TFNode* node = BUILD(SimdOp, opcode, inputs); - Push(GetReturnType(sig), node); + return len; } void DoReturn() { @@ -1320,12 +1288,21 @@ class WasmFullDecoder : public WasmDecoder { if (buffer) buffer[i] = val.node; } - Push(kAstEnd, BUILD(Return, count, buffer)); - ssa_env_->Kill(SsaEnv::kControlEnd); + BUILD(Return, count, buffer); + EndControl(); } void Push(LocalType type, TFNode* node) { - stack_.push_back({pc_, node, type}); + if (type != kAstStmt && type != kAstEnd) { + stack_.push_back({pc_, node, type}); + } + } + + void PushReturns(FunctionSig* sig, TFNode** rets) { + for (size_t i = 0; i < sig->return_count(); i++) { + // When verifying only, then {rets} will be null, so push null. + Push(sig->GetReturn(i), rets ? rets[i] : nullptr); + } } const char* SafeOpcodeNameAt(const byte* pc) { @@ -1334,6 +1311,10 @@ class WasmFullDecoder : public WasmDecoder { } Value Pop(int index, LocalType expected) { + if (!ssa_env_->go()) { + // Unreachable code is essentially not typechecked. + return {pc_, nullptr, expected}; + } Value val = Pop(); if (val.type != expected) { if (val.type != kAstEnd) { @@ -1346,6 +1327,10 @@ class WasmFullDecoder : public WasmDecoder { } Value Pop() { + if (!ssa_env_->go()) { + // Unreachable code is essentially not typechecked. + return {pc_, nullptr, kAstEnd}; + } size_t limit = control_.empty() ? 0 : control_.back().stack_depth; if (stack_.size() <= limit) { Value val = {pc_, nullptr, kAstStmt}; @@ -1358,6 +1343,10 @@ class WasmFullDecoder : public WasmDecoder { } Value PopUpTo(int stack_depth) { + if (!ssa_env_->go()) { + // Unreachable code is essentially not typechecked. + return {pc_, nullptr, kAstEnd}; + } if (stack_depth == stack_.size()) { Value val = {pc_, nullptr, kAstStmt}; return val; @@ -1375,34 +1364,82 @@ class WasmFullDecoder : public WasmDecoder { int startrel(const byte* ptr) { return static_cast<int>(ptr - start_); } - void BreakTo(Control* block, Value& val) { - if (block->is_loop) { + void BreakTo(unsigned depth) { + if (!ssa_env_->go()) return; + Control* c = &control_[control_.size() - depth - 1]; + if (c->is_loop()) { // This is the inner loop block, which does not have a value. - Goto(ssa_env_, block->end_env); + Goto(ssa_env_, c->end_env); } else { - // Merge the value into the production for the block. - MergeInto(block->end_env, &block->node, &block->type, val); + // Merge the value(s) into the end of the block. + if (static_cast<size_t>(c->stack_depth + c->merge.arity) > + stack_.size()) { + error( + pc_, pc_, + "expected at least %d values on the stack for br to @%d, found %d", + c->merge.arity, startrel(c->pc), + static_cast<int>(stack_.size() - c->stack_depth)); + return; + } + MergeValuesInto(c); + } + } + + void FallThruTo(Control* c) { + if (!ssa_env_->go()) return; + // Merge the value(s) into the end of the block. + int arity = static_cast<int>(c->merge.arity); + if (c->stack_depth + arity != stack_.size()) { + error(pc_, pc_, "expected %d elements on the stack for fallthru to @%d", + arity, startrel(c->pc)); + return; } + MergeValuesInto(c); } - void MergeInto(SsaEnv* target, TFNode** node, LocalType* type, Value& val) { + inline Value& GetMergeValueFromStack(Control* c, int i) { + return stack_[stack_.size() - c->merge.arity + i]; + } + + void TypeCheckLoopFallThru(Control* c) { if (!ssa_env_->go()) return; - DCHECK_NE(kAstEnd, val.type); + // Fallthru must match arity exactly. + int arity = static_cast<int>(c->merge.arity); + if (c->stack_depth + arity != stack_.size()) { + error(pc_, pc_, "expected %d elements on the stack for fallthru to @%d", + arity, startrel(c->pc)); + return; + } + // Typecheck the values left on the stack. + for (unsigned i = 0; i < c->merge.arity; i++) { + Value& val = GetMergeValueFromStack(c, i); + Value& old = + c->merge.arity == 1 ? c->merge.vals.first : c->merge.vals.array[i]; + if (val.type != old.type) { + error(pc_, pc_, "type error in merge[%d] (expected %s, got %s)", i, + WasmOpcodes::TypeName(old.type), WasmOpcodes::TypeName(val.type)); + return; + } + } + } + void MergeValuesInto(Control* c) { + SsaEnv* target = c->end_env; bool first = target->state == SsaEnv::kUnreachable; Goto(ssa_env_, target); - if (first) { - // first merge to this environment; set the type and the node. - *type = val.type; - *node = val.node; - } else if (val.type == *type && val.type != kAstStmt) { - // merge with the existing value for this block. - *node = CreateOrMergeIntoPhi(*type, target->control, *node, val.node); - } else { - // types don't match, or block is already a stmt. - *type = kAstStmt; - *node = nullptr; + for (unsigned i = 0; i < c->merge.arity; i++) { + Value& val = GetMergeValueFromStack(c, i); + Value& old = + c->merge.arity == 1 ? c->merge.vals.first : c->merge.vals.array[i]; + if (val.type != old.type) { + error(pc_, pc_, "type error in merge[%d] (expected %s, got %s)", i, + WasmOpcodes::TypeName(old.type), WasmOpcodes::TypeName(val.type)); + return; + } + old.node = + first ? val.node : CreateOrMergeIntoPhi(old.type, target->control, + old.node, val.node); } } @@ -1442,6 +1479,45 @@ class WasmFullDecoder : public WasmDecoder { } } + TFNode* CheckForException(TFNode* node) { + if (node == nullptr) { + return nullptr; + } + + const bool inside_try_scope = current_catch_ != kNullCatch; + + if (!inside_try_scope) { + return node; + } + + TFNode* if_success = nullptr; + TFNode* if_exception = nullptr; + if (!builder_->ThrowsException(node, &if_success, &if_exception)) { + return node; + } + + SsaEnv* success_env = Steal(ssa_env_); + success_env->control = if_success; + + SsaEnv* exception_env = Split(success_env); + exception_env->control = if_exception; + TryInfo* try_info = current_try_info(); + Goto(exception_env, try_info->catch_env); + TFNode* exception = try_info->exception; + if (exception == nullptr) { + DCHECK_EQ(SsaEnv::kReached, try_info->catch_env->state); + try_info->exception = if_exception; + } else { + DCHECK_EQ(SsaEnv::kMerged, try_info->catch_env->state); + try_info->exception = + CreateOrMergeIntoPhi(kAstI32, try_info->catch_env->control, + try_info->exception, if_exception); + } + + SetEnv("if_success", success_env); + return node; + } + void Goto(SsaEnv* from, SsaEnv* to) { DCHECK_NOT_NULL(to); if (!from->go()) return; @@ -1630,16 +1706,15 @@ class WasmFullDecoder : public WasmDecoder { case kExprLoop: case kExprIf: case kExprBlock: - case kExprTryCatch: - case kExprTryCatchFinally: - case kExprTryFinally: + case kExprTry: + length = OpcodeLength(pc); depth++; - DCHECK_EQ(1, OpcodeLength(pc)); break; - case kExprSetLocal: { + case kExprSetLocal: // fallthru + case kExprTeeLocal: { LocalIndexOperand operand(this, pc); if (assigned->length() > 0 && - static_cast<int>(operand.index) < assigned->length()) { + operand.index < static_cast<uint32_t>(assigned->length())) { // Unverified code might have an out-of-bounds index. assigned->Add(operand.index); } @@ -1664,11 +1739,33 @@ class WasmFullDecoder : public WasmDecoder { DCHECK_EQ(pc_ - start_, offset); // overflows cannot happen return offset; } + + inline void BuildSimpleOperator(WasmOpcode opcode, FunctionSig* sig) { + TFNode* node; + switch (sig->parameter_count()) { + case 1: { + Value val = Pop(0, sig->GetParam(0)); + node = BUILD(Unop, opcode, val.node, position()); + break; + } + case 2: { + Value rval = Pop(1, sig->GetParam(1)); + Value lval = Pop(0, sig->GetParam(0)); + node = BUILD(Binop, opcode, lval.node, rval.node, position()); + break; + } + default: + UNREACHABLE(); + node = nullptr; + break; + } + Push(GetReturnType(sig), node); + } }; bool DecodeLocalDecls(AstLocalDecls& decls, const byte* start, const byte* end) { - base::AccountingAllocator allocator; + AccountingAllocator allocator; Zone tmp(&allocator); FunctionBody body = {nullptr, nullptr, nullptr, start, end}; WasmFullDecoder decoder(&tmp, nullptr, body); @@ -1686,7 +1783,7 @@ BytecodeIterator::BytecodeIterator(const byte* start, const byte* end, } } -DecodeResult VerifyWasmCode(base::AccountingAllocator* allocator, +DecodeResult VerifyWasmCode(AccountingAllocator* allocator, FunctionBody& body) { Zone zone(allocator); WasmFullDecoder decoder(&zone, nullptr, body); @@ -1694,8 +1791,8 @@ DecodeResult VerifyWasmCode(base::AccountingAllocator* allocator, return decoder.toResult<DecodeStruct*>(nullptr); } -DecodeResult BuildTFGraph(base::AccountingAllocator* allocator, - TFBuilder* builder, FunctionBody& body) { +DecodeResult BuildTFGraph(AccountingAllocator* allocator, TFBuilder* builder, + FunctionBody& body) { Zone zone(allocator); WasmFullDecoder decoder(&zone, builder, body); decoder.Decode(); @@ -1707,18 +1804,13 @@ unsigned OpcodeLength(const byte* pc, const byte* end) { return decoder.OpcodeLength(pc); } -unsigned OpcodeArity(const byte* pc, const byte* end) { - WasmDecoder decoder(nullptr, nullptr, pc, end); - return decoder.OpcodeArity(pc); -} - void PrintAstForDebugging(const byte* start, const byte* end) { - base::AccountingAllocator allocator; + AccountingAllocator allocator; OFStream os(stdout); PrintAst(&allocator, FunctionBodyForTesting(start, end), os, nullptr); } -bool PrintAst(base::AccountingAllocator* allocator, const FunctionBody& body, +bool PrintAst(AccountingAllocator* allocator, const FunctionBody& body, std::ostream& os, std::vector<std::tuple<uint32_t, int, int>>* offset_table) { Zone zone(allocator); @@ -1777,68 +1869,57 @@ bool PrintAst(base::AccountingAllocator* allocator, const FunctionBody& body, } switch (opcode) { - case kExprIf: case kExprElse: + os << " // @" << i.pc_offset(); + control_depth++; + break; case kExprLoop: + case kExprIf: case kExprBlock: - case kExprTryCatch: - case kExprTryCatchFinally: - case kExprTryFinally: + case kExprTry: { + BlockTypeOperand operand(&i, i.pc()); os << " // @" << i.pc_offset(); + for (unsigned i = 0; i < operand.arity; i++) { + os << " " << WasmOpcodes::TypeName(operand.read_entry(i)); + } control_depth++; break; + } case kExprEnd: os << " // @" << i.pc_offset(); control_depth--; break; case kExprBr: { BreakDepthOperand operand(&i, i.pc()); - os << " // arity=" << operand.arity << " depth=" << operand.depth; + os << " // depth=" << operand.depth; break; } case kExprBrIf: { BreakDepthOperand operand(&i, i.pc()); - os << " // arity=" << operand.arity << " depth" << operand.depth; + os << " // depth=" << operand.depth; break; } case kExprBrTable: { BranchTableOperand operand(&i, i.pc()); - os << " // arity=" << operand.arity - << " entries=" << operand.table_count; + os << " // entries=" << operand.table_count; break; } case kExprCallIndirect: { CallIndirectOperand operand(&i, i.pc()); + os << " // sig #" << operand.index; if (decoder.Complete(i.pc(), operand)) { - os << " // sig #" << operand.index << ": " << *operand.sig; - } else { - os << " // arity=" << operand.arity << " sig #" << operand.index; - } - break; - } - case kExprCallImport: { - CallImportOperand operand(&i, i.pc()); - if (decoder.Complete(i.pc(), operand)) { - os << " // import #" << operand.index << ": " << *operand.sig; - } else { - os << " // arity=" << operand.arity << " import #" << operand.index; + os << ": " << *operand.sig; } break; } case kExprCallFunction: { CallFunctionOperand operand(&i, i.pc()); + os << " // function #" << operand.index; if (decoder.Complete(i.pc(), operand)) { - os << " // function #" << operand.index << ": " << *operand.sig; - } else { - os << " // arity=" << operand.arity << " function #" << operand.index; + os << ": " << *operand.sig; } break; } - case kExprReturn: { - ReturnArityOperand operand(&i, i.pc()); - os << " // arity=" << operand.arity; - break; - } default: break; } |