From 06061b7ddf4775930a8c93d0c23c5aab7decdacb Mon Sep 17 00:00:00 2001 From: Andreas Rossberg Date: Tue, 24 Oct 2017 12:47:09 +0200 Subject: [PATCH] [wasm] Support block parameters This adds support for parameters on block, loop, if, cf the multi-value proposal at: https://github.com/WebAssembly/multi-value/blob/master/proposals/multi-value/Overview.md With this CL, we ssucceed on all tests in: https://github.com/WebAssembly/multi-value/pull/2 except those involving multiple returns from functions. R=titzer@chromium.org Change-Id: I14a33e86450148f6aed2b8b8cc6bebb2303625c6 Reviewed-on: https://chromium-review.googlesource.com/712578 Commit-Queue: Andreas Rossberg Reviewed-by: Ben Titzer Cr-Commit-Position: refs/heads/master@{#48871} --- src/wasm/baseline/liftoff-compiler.cc | 14 +- src/wasm/function-body-decoder-impl.h | 253 +++++++++--------- src/wasm/function-body-decoder.cc | 41 ++- test/cctest/wasm/test-run-wasm.cc | 10 +- test/common/wasm/wasm-macro-gen.h | 29 +- test/mjsunit/wasm/multi-value.js | 233 ++++++++++++++++ test/mjsunit/wasm/wasm-constants.js | 4 + .../wasm/function-body-decoder-unittest.cc | 212 +++++++++++---- 8 files changed, 567 insertions(+), 229 deletions(-) create mode 100644 test/mjsunit/wasm/multi-value.js diff --git a/src/wasm/baseline/liftoff-compiler.cc b/src/wasm/baseline/liftoff-compiler.cc index 263aef5531f6..9856c4bde5bc 100644 --- a/src/wasm/baseline/liftoff-compiler.cc +++ b/src/wasm/baseline/liftoff-compiler.cc @@ -191,7 +191,7 @@ class LiftoffCompiler { } void FallThruTo(Decoder* decoder, Control* c) { - if (c->merge.reached) { + if (c->end_merge.reached) { __ MergeFullStackWith(c->label_state); } else { c->label_state.Split(*__ cache_state()); @@ -199,7 +199,7 @@ class LiftoffCompiler { } void PopControl(Decoder* decoder, Control* c) { - if (!c->is_loop() && c->merge.reached) { + if (!c->is_loop() && c->end_merge.reached) { __ cache_state()->Steal(c->label_state); } if (!c->label->is_bound()) { @@ -383,12 +383,12 @@ class LiftoffCompiler { unsupported(decoder, "select"); } - void BreakTo(Decoder* decoder, Control* target) { - if (!target->merge.reached) { + void Br(Decoder* decoder, Control* target) { + if (!target->br_merge()->reached) { target->label_state.InitMerge(*__ cache_state(), __ num_locals(), - target->break_arity()); + target->br_merge()->arity); } - __ MergeStackWith(target->label_state, target->break_arity()); + __ MergeStackWith(target->label_state, target->br_merge()->arity); __ jmp(target->label.get()); } @@ -397,7 +397,7 @@ class LiftoffCompiler { Register value = __ PopToRegister(kWasmI32); __ JumpIfZero(value, &cont_false); - BreakTo(decoder, target); + Br(decoder, target); __ bind(&cont_false); } diff --git a/src/wasm/function-body-decoder-impl.h b/src/wasm/function-body-decoder-impl.h index b7f944777e41..99c741a4b9f4 100644 --- a/src/wasm/function-body-decoder-impl.h +++ b/src/wasm/function-body-decoder-impl.h @@ -81,11 +81,12 @@ struct WasmException; V(I32AtomicStore8U, Uint8) \ V(I32AtomicStore16U, Uint16) -template -Vector vec2vec(std::vector& vec) { +template +Vector vec2vec(std::vector& vec) { return Vector(vec.data(), vec.size()); } + // Helpers for decoding different kinds of operands which follow bytecodes. template struct LocalIndexOperand { @@ -446,20 +447,18 @@ enum Reachability : uint8_t { // An entry on the control stack (i.e. if, block, loop, or try). template struct ControlBase { - Reachability reachability = kReachable; ControlKind kind; uint32_t stack_depth; // stack height at the beginning of the construct. const byte* pc; + Reachability reachability = kReachable; - // Values merged into the end of this control construct. - Merge merge; + // Values merged into the start or end of this control construct. + Merge start_merge; + Merge end_merge; ControlBase() = default; - ControlBase(ControlKind kind, uint32_t stack_depth, const byte* pc, - bool merge_reached = false) - : kind(kind), stack_depth(stack_depth), pc(pc), merge(merge_reached) {} - - uint32_t break_arity() const { return is_loop() ? 0 : merge.arity; } + ControlBase(ControlKind kind, uint32_t stack_depth, const byte* pc) + : kind(kind), stack_depth(stack_depth), pc(pc) {} // Check whether the current block is reachable. bool reachable() const { return reachability == kReachable; } @@ -484,6 +483,10 @@ struct ControlBase { bool is_incomplete_try() const { return kind == kControlTry; } bool is_try_catch() const { return kind == kControlTryCatch; } + inline Merge* br_merge() { + return is_loop() ? &this->start_merge : &this->end_merge; + } + // Named constructors. static ControlBase Block(const byte* pc, uint32_t stack_depth) { return {kControlBlock, stack_depth, pc}; @@ -493,8 +496,8 @@ struct ControlBase { return {kControlIf, stack_depth, pc}; } - static ControlBase Loop(const byte* pc, uint32_t stack_depth, bool reached) { - return {kControlLoop, stack_depth, pc, reached}; + static ControlBase Loop(const byte* pc, uint32_t stack_depth) { + return {kControlLoop, stack_depth, pc}; } static ControlBase Try(const byte* pc, uint32_t stack_depth) { @@ -575,7 +578,7 @@ struct ControlWithNamedConstructors : public ControlBase { F(Unreachable) \ F(Select, const Value& cond, const Value& fval, const Value& tval, \ Value* result) \ - F(BreakTo, Control* target) \ + F(Br, Control* target) \ F(BrIf, const Value& cond, Control* target) \ F(BrTable, const BranchTableOperand& operand, const Value& key) \ F(Else, Control* if_block) \ @@ -1128,6 +1131,7 @@ class WasmFullDecoder : public WasmDecoder { local_type_vec_(zone), stack_(zone), control_(zone), + args_(zone), last_end_found_(false) { this->local_types_ = &local_type_vec_; } @@ -1232,10 +1236,12 @@ class WasmFullDecoder : public WasmDecoder { return &stack_[stack_.size() - depth - 1]; } - inline Value& GetMergeValueFromStack(Control* c, uint32_t i) { - DCHECK_GT(c->merge.arity, i); - DCHECK_GE(stack_.size(), c->stack_depth + c->merge.arity); - return stack_[stack_.size() - c->merge.arity + i]; + inline Value& GetMergeValueFromStack( + Control* c, Merge* merge, uint32_t i) { + DCHECK(merge == &c->start_merge || merge == &c->end_merge); + DCHECK_GT(merge->arity, i); + DCHECK_GE(stack_.size(), c->stack_depth + merge->arity); + return stack_[stack_.size() - merge->arity + i]; } private: @@ -1248,6 +1254,7 @@ class WasmFullDecoder : public WasmDecoder { ZoneVector local_type_vec_; // types of local variables. ZoneVector stack_; // stack of values. ZoneVector control_; // stack of blocks, loops, and ifs. + ZoneVector args_; // parameters of current block or call bool last_end_found_; bool CheckHasMemory() { @@ -1276,17 +1283,10 @@ class WasmFullDecoder : public WasmDecoder { // Set up initial function block. { auto* c = PushBlock(); - c->merge.arity = static_cast(this->sig_->return_count()); - - if (c->merge.arity == 1) { - c->merge.vals.first = Value::New(this->pc_, this->sig_->GetReturn(0)); - } else if (c->merge.arity > 1) { - c->merge.vals.array = zone_->NewArray(c->merge.arity); - for (unsigned i = 0; i < c->merge.arity; i++) { - c->merge.vals.array[i] = - Value::New(this->pc_, this->sig_->GetReturn(i)); - } - } + InitMerge(&c->end_merge, + static_cast(this->sig_->return_count()), + [&] (uint32_t i) { + return Value::New(this->pc_, this->sig_->GetReturn(i)); }); CALL_INTERFACE(StartFunctionBody, c); } @@ -1311,10 +1311,12 @@ class WasmFullDecoder : public WasmDecoder { case kExprBlock: { BlockTypeOperand operand(this, this->pc_); if (!LookupBlockType(&operand)) break; + PopArgs(operand.sig); auto* block = PushBlock(); - SetBlockType(block, operand); - len = 1 + operand.length; + SetBlockType(block, operand, args_); CALL_INTERFACE_IF_REACHABLE(Block, block); + PushMergeValues(block, &block->start_merge); + len = 1 + operand.length; break; } case kExprRethrow: { @@ -1328,10 +1330,9 @@ class WasmFullDecoder : public WasmDecoder { ExceptionIndexOperand operand(this, this->pc_); len = 1 + operand.length; if (!this->Validate(this->pc_, operand)) break; - std::vector args; - PopArgs(operand.exception->ToFunctionSig(), &args); + PopArgs(operand.exception->ToFunctionSig()); CALL_INTERFACE_IF_REACHABLE(Throw, operand, &control_.back(), - vec2vec(args)); + vec2vec(args_)); EndControl(); break; } @@ -1339,10 +1340,12 @@ class WasmFullDecoder : public WasmDecoder { CHECK_PROTOTYPE_OPCODE(eh); BlockTypeOperand operand(this, this->pc_); if (!LookupBlockType(&operand)) break; + PopArgs(operand.sig); auto* try_block = PushTry(); - SetBlockType(try_block, operand); + SetBlockType(try_block, operand, args_); len = 1 + operand.length; CALL_INTERFACE_IF_REACHABLE(Try, try_block); + PushMergeValues(try_block, &try_block->start_merge); break; } case kExprCatch: { @@ -1391,21 +1394,25 @@ class WasmFullDecoder : public WasmDecoder { case kExprLoop: { BlockTypeOperand operand(this, this->pc_); if (!LookupBlockType(&operand)) break; + PopArgs(operand.sig); auto* block = PushLoop(); - SetBlockType(&control_.back(), operand); + SetBlockType(&control_.back(), operand, args_); len = 1 + operand.length; CALL_INTERFACE_IF_REACHABLE(Loop, block); + PushMergeValues(block, &block->start_merge); break; } case kExprIf: { BlockTypeOperand operand(this, this->pc_); if (!LookupBlockType(&operand)) break; auto cond = Pop(0, kWasmI32); + PopArgs(operand.sig); if (!this->ok()) break; auto* if_block = PushIf(); - SetBlockType(if_block, operand); + SetBlockType(if_block, operand, args_); CALL_INTERFACE_IF_REACHABLE(If, cond, if_block); len = 1 + operand.length; + PushMergeValues(if_block, &if_block->start_merge); break; } case kExprElse: { @@ -1424,8 +1431,8 @@ class WasmFullDecoder : public WasmDecoder { } c->kind = kControlIfElse; FallThruTo(c); - stack_.resize(c->stack_depth); CALL_INTERFACE_IF_PARENT_REACHABLE(Else, c); + PushMergeValues(c, &c->start_merge); c->reachability = control_at(1)->innerReachability(); break; } @@ -1435,30 +1442,21 @@ class WasmFullDecoder : public WasmDecoder { return; } Control* c = &control_.back(); - if (c->is_loop()) { - // A loop just leaves the values on the stack. - TypeCheckFallThru(c); - PopControl(c); + if (!VALIDATE(!c->is_incomplete_try())) { + this->error(this->pc_, "missing catch in try"); break; } if (c->is_onearmed_if()) { - // The merge point is reached if the if is not taken. - if (control_at(1)->reachable()) c->merge.reached = true; - // End the true branch of a one-armed if. - if (!VALIDATE(c->unreachable() || - stack_.size() == c->stack_depth)) { - this->error("end of if expected empty stack"); - stack_.resize(c->stack_depth); - } - if (!VALIDATE(c->merge.arity == 0)) { - this->error("non-void one-armed if"); - } - } else if (!VALIDATE(!c->is_incomplete_try())) { - this->error(this->pc_, "missing catch in try"); - break; + // Emulate empty else arm. + FallThruTo(c); + CALL_INTERFACE_IF_PARENT_REACHABLE(Else, c); + PushMergeValues(c, &c->start_merge); + c->reachability = control_at(1)->innerReachability(); } + FallThruTo(c); - PushEndValues(c); + // A loop just leaves the values on the stack. + if (!c->is_loop()) PushMergeValues(c, &c->end_merge); if (control_.size() == 1) { // If at the last (implicit) control, check we are at end. @@ -1490,8 +1488,8 @@ class WasmFullDecoder : public WasmDecoder { if (!this->Validate(this->pc_, operand, control_.size())) break; Control* c = control_at(operand.depth); if (!TypeCheckBreak(c)) break; - CALL_INTERFACE_IF_REACHABLE(BreakTo, c); - if (control_.back().reachable()) c->merge.reached = true; + CALL_INTERFACE_IF_REACHABLE(Br, c); + BreakTo(c); len = 1 + operand.length; EndControl(); break; @@ -1503,7 +1501,7 @@ class WasmFullDecoder : public WasmDecoder { Control* c = control_at(operand.depth); if (!TypeCheckBreak(c)) break; CALL_INTERFACE_IF_REACHABLE(BrIf, cond, c); - if (control_.back().reachable()) c->merge.reached = true; + BreakTo(c); len = 1 + operand.length; break; } @@ -1523,7 +1521,7 @@ class WasmFullDecoder : public WasmDecoder { } // Check that label types match up. Control* c = control_at(target); - uint32_t arity = c->break_arity(); + uint32_t arity = c->br_merge()->arity; if (i == 0) { br_arity = arity; } else if (!VALIDATE(br_arity == arity)) { @@ -1533,7 +1531,7 @@ class WasmFullDecoder : public WasmDecoder { i, br_arity, arity); } if (!TypeCheckBreak(c)) break; - if (control_.back().reachable()) c->merge.reached = true; + BreakTo(c); } CALL_INTERFACE_IF_REACHABLE(BrTable, operand, key); @@ -1726,10 +1724,9 @@ class WasmFullDecoder : public WasmDecoder { len = 1 + operand.length; if (!this->Validate(this->pc_, operand)) break; // TODO(clemensh): Better memory management. - std::vector args; - PopArgs(operand.sig, &args); + PopArgs(operand.sig); auto* returns = PushReturns(operand.sig); - CALL_INTERFACE_IF_REACHABLE(CallDirect, operand, args.data(), + CALL_INTERFACE_IF_REACHABLE(CallDirect, operand, args_.data(), returns); break; } @@ -1738,12 +1735,10 @@ class WasmFullDecoder : public WasmDecoder { len = 1 + operand.length; if (!this->Validate(this->pc_, operand)) break; auto index = Pop(0, kWasmI32); - // TODO(clemensh): Better memory management. - std::vector args; - PopArgs(operand.sig, &args); + PopArgs(operand.sig); auto* returns = PushReturns(operand.sig); CALL_INTERFACE_IF_REACHABLE(CallIndirect, index, operand, - args.data(), returns); + args_.data(), returns); break; } case kSimdPrefix: { @@ -1804,7 +1799,8 @@ class WasmFullDecoder : public WasmDecoder { default: break; } - PrintF("%u", c.merge.arity); + if (c.start_merge.arity) PrintF("%u-", c.end_merge.arity); + PrintF("%u", c.end_merge.arity); if (!c.reachable()) PrintF("%c", c.unreachable() ? '*' : '#'); } PrintF(" | "); @@ -1872,25 +1868,34 @@ class WasmFullDecoder : public WasmDecoder { return true; } - void SetBlockType(Control* c, BlockTypeOperand& operand) { - c->merge.arity = operand.out_arity(); - if (c->merge.arity == 1) { - c->merge.vals.first = Value::New(this->pc_, operand.out_type(0)); - } else if (c->merge.arity > 1) { - c->merge.vals.array = zone_->NewArray(c->merge.arity); - for (unsigned i = 0; i < c->merge.arity; i++) { - c->merge.vals.array[i] = Value::New(this->pc_, operand.out_type(i)); + template + void InitMerge(Merge* merge, uint32_t arity, func get_val) { + merge->arity = arity; + if (arity == 1) { + merge->vals.first = get_val(0); + } else if (arity > 1) { + merge->vals.array = zone_->NewArray(arity); + for (unsigned i = 0; i < arity; i++) { + merge->vals.array[i] = get_val(i); } } } - // TODO(clemensh): Better memory management. - void PopArgs(FunctionSig* sig, std::vector* result) { - DCHECK(result->empty()); - int count = static_cast(sig->parameter_count()); - result->resize(count); + void SetBlockType(Control* c, BlockTypeOperand& operand, + ZoneVector& params) { + InitMerge(&c->end_merge, operand.out_arity(), + [&] (uint32_t i) { + return Value::New(this->pc_, operand.out_type(i)); }); + InitMerge(&c->start_merge, operand.in_arity(), + [&] (uint32_t i) { return params[i]; }); + } + + // Pops arguments as required by signature into {args_}. + V8_INLINE void PopArgs(FunctionSig* sig) { + int count = sig ? static_cast(sig->parameter_count()) : 0; + args_.resize(count); for (int i = count - 1; i >= 0; --i) { - (*result)[i] = Pop(i, sig->GetParam(i)); + args_[i] = Pop(i, sig->GetParam(i)); } } @@ -1905,6 +1910,7 @@ class WasmFullDecoder : public WasmDecoder { control_.emplace_back(std::move(new_control)); Control* c = &control_.back(); c->reachability = reachability; + c->start_merge.reached = c->reachable(); return c; } @@ -1912,8 +1918,7 @@ class WasmFullDecoder : public WasmDecoder { return PushControl(Control::Block(this->pc_, stack_size())); } Control* PushLoop() { - return PushControl( - Control::Loop(this->pc_, stack_size(), control_.back().reachable())); + return PushControl(Control::Loop(this->pc_, stack_size())); } Control* PushIf() { return PushControl(Control::If(this->pc_, stack_size())); @@ -1926,7 +1931,7 @@ class WasmFullDecoder : public WasmDecoder { void PopControl(Control* c) { DCHECK_EQ(c, &control_.back()); CALL_INTERFACE_IF_PARENT_REACHABLE(PopControl, c); - bool reached = c->is_loop() ? c->reachable() : c->merge.reached; + bool reached = c->end_merge.reached; control_.pop_back(); // If the parent block was reachable before, but the popped control does not // return to here, this block becomes indirectly unreachable. @@ -2078,11 +2083,10 @@ class WasmFullDecoder : public WasmDecoder { this->error("invalid simd opcode"); break; } - std::vector args; - PopArgs(sig, &args); - auto* result = + PopArgs(sig); + auto* results = sig->return_count() == 0 ? nullptr : Push(GetReturnType(sig)); - CALL_INTERFACE_IF_REACHABLE(SimdOp, opcode, vec2vec(args), result); + CALL_INTERFACE_IF_REACHABLE(SimdOp, opcode, vec2vec(args_), results); } } return len; @@ -2113,20 +2117,16 @@ class WasmFullDecoder : public WasmDecoder { #undef CASE_ATOMIC_OP default: this->error("invalid atomic opcode"); - break; + return 0; } - // TODO(clemensh): Better memory management here. - std::vector args(sig->parameter_count()); MemoryAccessOperand operand( this, this->pc_ + 1, ElementSizeLog2Of(memtype.representation())); len += operand.length; - for (int i = static_cast(sig->parameter_count() - 1); i >= 0; --i) { - args[i] = Pop(i, sig->GetParam(i)); - } + PopArgs(sig); auto result = ret_type == MachineRepresentation::kNone ? nullptr : Push(GetReturnType(sig)); - CALL_INTERFACE_IF_REACHABLE(AtomicOp, opcode, vec2vec(args), operand, + CALL_INTERFACE_IF_REACHABLE(AtomicOp, opcode, vec2vec(args_), operand, result); } else { this->error("invalid atomic opcode"); @@ -2135,19 +2135,17 @@ class WasmFullDecoder : public WasmDecoder { } void DoReturn(Control* c, bool implicit) { - // TODO(clemensh): Optimize memory usage here (it will be mostly 0 or 1 - // returned values). int return_count = static_cast(this->sig_->return_count()); - std::vector values(return_count); + args_.resize(return_count); // Pop return values off the stack in reverse order. for (int i = return_count - 1; i >= 0; --i) { - values[i] = Pop(i, this->sig_->GetReturn(i)); + args_[i] = Pop(i, this->sig_->GetReturn(i)); } - if (this->ok() && (c->reachable() || (implicit && c->merge.reached))) { - CALL_INTERFACE(DoReturn, vec2vec(values), implicit); - } + // Simulate that an implicit return morally comes after the current block. + if (implicit && c->end_merge.reached) c->reachability = kReachable; + CALL_INTERFACE_IF_REACHABLE(DoReturn, vec2vec(args_), implicit); EndControl(); } @@ -2158,17 +2156,18 @@ class WasmFullDecoder : public WasmDecoder { return &stack_.back(); } - void PushEndValues(Control* c) { + void PushMergeValues(Control* c, Merge* merge) { DCHECK_EQ(c, &control_.back()); + DCHECK(merge == &c->start_merge || merge == &c->end_merge); stack_.resize(c->stack_depth); - if (c->merge.arity == 1) { - stack_.push_back(c->merge.vals.first); + if (merge->arity == 1) { + stack_.push_back(merge->vals.first); } else { - for (unsigned i = 0; i < c->merge.arity; i++) { - stack_.push_back(c->merge.vals.array[i]); + for (unsigned i = 0; i < merge->arity; i++) { + stack_.push_back(merge->vals.array[i]); } } - DCHECK_EQ(c->stack_depth + c->merge.arity, stack_.size()); + DCHECK_EQ(c->stack_depth + merge->arity, stack_.size()); } Value* PushReturns(FunctionSig* sig) { @@ -2211,22 +2210,26 @@ class WasmFullDecoder : public WasmDecoder { int startrel(const byte* ptr) { return static_cast(ptr - this->start_); } + inline void BreakTo(Control* c) { + if (control_.back().reachable()) c->br_merge()->reached = true; + } + void FallThruTo(Control* c) { - DCHECK(!c->is_loop()); DCHECK_EQ(c, &control_.back()); if (!TypeCheckFallThru(c)) return; if (!c->reachable()) return; - CALL_INTERFACE(FallThruTo, c); - c->merge.reached = true; + if (!c->is_loop()) CALL_INTERFACE(FallThruTo, c); + c->end_merge.reached = true; } - bool TypeCheckMergeValues(Control* c) { - DCHECK_GE(stack_.size(), c->stack_depth + c->merge.arity); - // Typecheck the topmost {c->merge.arity} values on the stack. - for (uint32_t i = 0; i < c->merge.arity; ++i) { - auto& val = GetMergeValueFromStack(c, i); - auto& old = c->merge[i]; + bool TypeCheckMergeValues(Control* c, Merge* merge) { + DCHECK(merge == &c->start_merge || merge == &c->end_merge); + DCHECK_GE(stack_.size(), c->stack_depth + merge->arity); + // Typecheck the topmost {merge->arity} values on the stack. + for (uint32_t i = 0; i < merge->arity; ++i) { + auto& val = GetMergeValueFromStack(c, merge, i); + auto& old = (*merge)[i]; if (val.type != old.type) { // If {val.type} is polymorphic, which results from unreachable, make // it more specific by using the merge value's expected type. @@ -2247,7 +2250,7 @@ class WasmFullDecoder : public WasmDecoder { bool TypeCheckFallThru(Control* c) { DCHECK_EQ(c, &control_.back()); if (!validate) return true; - uint32_t expected = c->merge.arity; + uint32_t expected = c->end_merge.arity; DCHECK_GE(stack_.size(), c->stack_depth); uint32_t actual = static_cast(stack_.size()) - c->stack_depth; // Fallthrus must match the arity of the control exactly. @@ -2259,16 +2262,12 @@ class WasmFullDecoder : public WasmDecoder { return false; } - return TypeCheckMergeValues(c); + return TypeCheckMergeValues(c, &c->end_merge); } bool TypeCheckBreak(Control* c) { - if (c->is_loop()) { - // This is the inner loop block, which does not have a value. - return true; - } // Breaks must have at least the number of values expected; can have more. - uint32_t expected = c->merge.arity; + uint32_t expected = c->br_merge()->arity; DCHECK_GE(stack_.size(), control_.back().stack_depth); uint32_t actual = static_cast(stack_.size()) - control_.back().stack_depth; @@ -2278,7 +2277,7 @@ class WasmFullDecoder : public WasmDecoder { expected, startrel(c->pc), actual); return false; } - return TypeCheckMergeValues(c); + return TypeCheckMergeValues(c, c->br_merge()); } inline bool InsertUnreachablesIfNecessary(uint32_t expected, diff --git a/src/wasm/function-body-decoder.cc b/src/wasm/function-body-decoder.cc index 1b309f264ed7..c696c807acc7 100644 --- a/src/wasm/function-body-decoder.cc +++ b/src/wasm/function-body-decoder.cc @@ -27,11 +27,6 @@ namespace wasm { namespace { -template -Vector vec2vec(ZoneVector& vec) { - return Vector(vec.data(), vec.size()); -} - // An SsaEnv environment carries the current local variable renaming // as well as the current effect and control dependency in the TF graph. // It maintains a control state that tracks whether the environment @@ -173,6 +168,12 @@ class WasmGraphBuildingInterface { // The continue environment is the inner environment. SetEnv(PrepareForLoop(decoder, finish_try_env)); ssa_env_->SetNotMerged(); + if (!decoder->ok()) return; + // Wrap input merge into phis. + for (unsigned i = 0; i < block->start_merge.arity; ++i) { + Value& val = block->start_merge[i]; + val.node = builder_->Phi(val.type, 1, &val.node, block->end_env->control); + } } void Try(Decoder* decoder, Control* block) { @@ -206,14 +207,11 @@ class WasmGraphBuildingInterface { void FallThruTo(Decoder* decoder, Control* c) { DCHECK(!c->is_loop()); - MergeValuesInto(decoder, c); + MergeValuesInto(decoder, c, &c->end_merge); } void PopControl(Decoder* decoder, Control* block) { if (!block->is_loop()) SetEnv(block->end_env); - if (block->is_onearmed_if()) { - Goto(decoder, block->false_env, block->end_env); - } } void EndControl(Decoder* decoder, Control* block) { ssa_env_->Kill(); } @@ -304,12 +302,8 @@ class WasmGraphBuildingInterface { ssa_env_->control = merge; } - void BreakTo(Decoder* decoder, Control* target) { - if (target->is_loop()) { - Goto(decoder, ssa_env_, target->end_env); - } else { - MergeValuesInto(decoder, target); - } + void Br(Decoder* decoder, Control* target) { + MergeValuesInto(decoder, target, target->br_merge()); } void BrIf(Decoder* decoder, const Value& cond, Control* target) { @@ -318,7 +312,7 @@ class WasmGraphBuildingInterface { fenv->SetNotMerged(); BUILD(BranchNoHint, cond.node, &tenv->control, &fenv->control); ssa_env_ = tenv; - BreakTo(decoder, target); + Br(decoder, target); ssa_env_ = fenv; } @@ -327,7 +321,7 @@ class WasmGraphBuildingInterface { if (operand.table_count == 0) { // Only a default target. Do the equivalent of br. uint32_t target = BranchTableIterator(decoder, operand).next(); - BreakTo(decoder, decoder->control_at(target)); + Br(decoder, decoder->control_at(target)); return; } @@ -344,7 +338,7 @@ class WasmGraphBuildingInterface { ssa_env_ = Split(decoder, copy); ssa_env_->control = (i == operand.table_count) ? BUILD(IfDefault, sw) : BUILD(IfValue, i, sw); - BreakTo(decoder, decoder->control_at(target)); + Br(decoder, decoder->control_at(target)); } DCHECK(decoder->ok()); ssa_env_ = break_env; @@ -618,7 +612,8 @@ class WasmGraphBuildingInterface { } } - void MergeValuesInto(Decoder* decoder, Control* c) { + void MergeValuesInto(Decoder* decoder, Control* c, Merge* merge) { + DCHECK(merge == &c->start_merge || merge == &c->end_merge); if (!ssa_env_->go()) return; SsaEnv* target = c->end_env; @@ -627,10 +622,10 @@ class WasmGraphBuildingInterface { uint32_t avail = decoder->stack_size() - decoder->control_at(0)->stack_depth; - uint32_t start = avail >= c->merge.arity ? 0 : c->merge.arity - avail; - for (uint32_t i = start; i < c->merge.arity; ++i) { - auto& val = decoder->GetMergeValueFromStack(c, i); - auto& old = c->merge[i]; + uint32_t start = avail >= merge->arity ? 0 : merge->arity - avail; + for (uint32_t i = start; i < merge->arity; ++i) { + auto& val = decoder->GetMergeValueFromStack(c, merge, i); + auto& old = (*merge)[i]; DCHECK_NOT_NULL(val.node); DCHECK(val.type == old.type || val.type == kWasmVar); old.node = first ? val.node diff --git a/test/cctest/wasm/test-run-wasm.cc b/test/cctest/wasm/test-run-wasm.cc index 9af1612b0073..e0a4d96efb6f 100644 --- a/test/cctest/wasm/test-run-wasm.cc +++ b/test/cctest/wasm/test-run-wasm.cc @@ -120,7 +120,7 @@ WASM_EXEC_TEST(Int32Add_P2) { WASM_EXEC_TEST(Int32Add_block1) { EXPERIMENTAL_FLAG_SCOPE(mv); static const byte code[] = { - WASM_BLOCK_TT(0, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)), + WASM_BLOCK_X(0, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)), kExprI32Add}; RunInt32AddTest(execution_mode, code, sizeof(code)); } @@ -128,7 +128,7 @@ WASM_EXEC_TEST(Int32Add_block1) { WASM_EXEC_TEST(Int32Add_block2) { EXPERIMENTAL_FLAG_SCOPE(mv); static const byte code[] = { - WASM_BLOCK_TT(0, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1), kExprBr, DEPTH_0), + WASM_BLOCK_X(0, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1), kExprBr, DEPTH_0), kExprI32Add}; RunInt32AddTest(execution_mode, code, sizeof(code)); } @@ -136,9 +136,9 @@ WASM_EXEC_TEST(Int32Add_block2) { WASM_EXEC_TEST(Int32Add_multi_if) { EXPERIMENTAL_FLAG_SCOPE(mv); static const byte code[] = { - WASM_IF_ELSE_TT(0, WASM_GET_LOCAL(0), - WASM_SEQ(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)), - WASM_SEQ(WASM_GET_LOCAL(1), WASM_GET_LOCAL(0))), + WASM_IF_ELSE_X(0, WASM_GET_LOCAL(0), + WASM_SEQ(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)), + WASM_SEQ(WASM_GET_LOCAL(1), WASM_GET_LOCAL(0))), kExprI32Add}; RunInt32AddTest(execution_mode, code, sizeof(code)); } diff --git a/test/common/wasm/wasm-macro-gen.h b/test/common/wasm/wasm-macro-gen.h index 90a5454b08b5..40718e79aa98 100644 --- a/test/common/wasm/wasm-macro-gen.h +++ b/test/common/wasm/wasm-macro-gen.h @@ -79,7 +79,7 @@ kExprBlock, static_cast(WasmOpcodes::ValueTypeCodeFor(t)), \ __VA_ARGS__, kExprEnd -#define WASM_BLOCK_TT(index, ...) \ +#define WASM_BLOCK_X(index, ...) \ kExprBlock, static_cast(index), __VA_ARGS__, kExprEnd #define WASM_INFINITE_LOOP kExprLoop, kLocalVoid, kExprBr, DEPTH_0, kExprEnd @@ -94,20 +94,20 @@ kExprLoop, static_cast(WasmOpcodes::ValueTypeCodeFor(t)), \ __VA_ARGS__, kExprEnd -#define WASM_LOOP_TT(index, ...) \ +#define WASM_LOOP_X(index, ...) \ kExprLoop, static_cast(index), __VA_ARGS__, kExprEnd -#define WASM_IF(cond, tstmt) cond, kExprIf, kLocalVoid, tstmt, kExprEnd +#define WASM_IF(cond, ...) cond, kExprIf, kLocalVoid, __VA_ARGS__, kExprEnd -#define WASM_IF_ELSE(cond, tstmt, fstmt) \ - cond, kExprIf, kLocalVoid, tstmt, kExprElse, fstmt, kExprEnd +#define WASM_IF_T(t, cond, ...) \ + cond, kExprIf, static_cast(WasmOpcodes::ValueTypeCodeFor(t)), \ + __VA_ARGS__, kExprEnd -#define WASM_IF_ELSE_T(t, cond, tstmt, fstmt) \ - cond, kExprIf, static_cast(WasmOpcodes::ValueTypeCodeFor(t)), tstmt, \ - kExprElse, fstmt, kExprEnd +#define WASM_IF_X(index, cond, ...) \ + cond, kExprIf, static_cast(index), __VA_ARGS__, kExprEnd -#define WASM_IF_ELSE_TT(index, cond, tstmt, fstmt) \ - cond, kExprIf, static_cast(index), tstmt, kExprElse, fstmt, kExprEnd +#define WASM_IF_ELSE(cond, tstmt, fstmt) \ + cond, kExprIf, kLocalVoid, tstmt, kExprElse, fstmt, kExprEnd #define WASM_IF_ELSE_I(cond, tstmt, fstmt) \ cond, kExprIf, kLocalI32, tstmt, kExprElse, fstmt, kExprEnd @@ -118,6 +118,13 @@ #define WASM_IF_ELSE_D(cond, tstmt, fstmt) \ cond, kExprIf, kLocalF64, tstmt, kExprElse, fstmt, kExprEnd +#define WASM_IF_ELSE_T(t, cond, tstmt, fstmt) \ + cond, kExprIf, static_cast(WasmOpcodes::ValueTypeCodeFor(t)), tstmt, \ + kExprElse, fstmt, kExprEnd + +#define WASM_IF_ELSE_X(index, cond, tstmt, fstmt) \ + cond, kExprIf, static_cast(index), tstmt, kExprElse, fstmt, kExprEnd + #define WASM_SELECT(tval, fval, cond) tval, fval, cond, kExprSelect #define WASM_RETURN0 kExprReturn @@ -574,7 +581,7 @@ inline WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) { #define SIZEOF_SIG_ENTRY_x_xx 6 #define SIZEOF_SIG_ENTRY_x_xxx 7 -#define WASM_BRV(depth, val) val, kExprBr, static_cast(depth) +#define WASM_BRV(depth, ...) __VA_ARGS__, kExprBr, static_cast(depth) #define WASM_BRV_IF(depth, val, cond) \ val, cond, kExprBrIf, static_cast(depth) #define WASM_BRV_IFD(depth, val, cond) \ diff --git a/test/mjsunit/wasm/multi-value.js b/test/mjsunit/wasm/multi-value.js new file mode 100644 index 000000000000..e5f081ad8124 --- /dev/null +++ b/test/mjsunit/wasm/multi-value.js @@ -0,0 +1,233 @@ +// Copyright 2017 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Flags: --experimental-wasm-mv + +load("test/mjsunit/wasm/wasm-constants.js"); +load("test/mjsunit/wasm/wasm-module-builder.js"); + +(function MultiBlockResultTest() { + print("MultiBlockResultTest"); + let builder = new WasmModuleBuilder(); + let sig_i_ii = builder.addType(kSig_i_ii); + let sig_ii_v = builder.addType(kSig_ii_v); + + builder.addFunction("main", kSig_i_ii) + .addBody([ + kExprBlock, sig_ii_v, + kExprGetLocal, 0, + kExprGetLocal, 1, + kExprEnd, + kExprI32Add]) + .exportAs("main"); + + let module = new WebAssembly.Module(builder.toBuffer()); + let instance = new WebAssembly.Instance(module); + assertEquals(instance.exports.main(1, 4), 5); +})(); + +(function MultiBlockParamTest() { + print("MultiBlockParamTest"); + let builder = new WasmModuleBuilder(); + let sig_i_ii = builder.addType(kSig_i_ii); + + builder.addFunction("main", kSig_i_ii) + .addBody([ + kExprGetLocal, 0, + kExprGetLocal, 1, + kExprBlock, sig_i_ii, + kExprI32Add, + kExprEnd]) + .exportAs("main"); + + let module = new WebAssembly.Module(builder.toBuffer()); + let instance = new WebAssembly.Instance(module); + assertEquals(instance.exports.main(1, 4), 5); +})(); + +(function MultiBlockBrTest() { + print("MultiBlockBrTest"); + let builder = new WasmModuleBuilder(); + let sig_i_ii = builder.addType(kSig_i_ii); + let sig_ii_v = builder.addType(kSig_ii_v); + + builder.addFunction("main", kSig_i_ii) + .addBody([ + kExprBlock, sig_ii_v, + kExprGetLocal, 0, + kExprGetLocal, 1, + kExprBr, 0, + kExprEnd, + kExprI32Add]) + .exportAs("main"); + + let module = new WebAssembly.Module(builder.toBuffer()); + let instance = new WebAssembly.Instance(module); + assertEquals(instance.exports.main(1, 4), 5); +})(); + + +(function MultiLoopResultTest() { + print("MultiLoopResultTest"); + let builder = new WasmModuleBuilder(); + let sig_i_ii = builder.addType(kSig_i_ii); + let sig_ii_v = builder.addType(kSig_ii_v); + + builder.addFunction("main", kSig_i_ii) + .addBody([ + kExprLoop, sig_ii_v, + kExprGetLocal, 0, + kExprGetLocal, 1, + kExprEnd, + kExprI32Add]) + .exportAs("main"); + + let module = new WebAssembly.Module(builder.toBuffer()); + let instance = new WebAssembly.Instance(module); + assertEquals(instance.exports.main(1, 4), 5); +})(); + +(function MultiLoopParamTest() { + print("MultiLoopParamTest"); + let builder = new WasmModuleBuilder(); + let sig_i_ii = builder.addType(kSig_i_ii); + + builder.addFunction("main", kSig_i_ii) + .addBody([ + kExprGetLocal, 0, + kExprGetLocal, 1, + kExprLoop, sig_i_ii, + kExprI32Add, + kExprEnd]) + .exportAs("main"); + + let module = new WebAssembly.Module(builder.toBuffer()); + let instance = new WebAssembly.Instance(module); + assertEquals(instance.exports.main(1, 4), 5); +})(); + +(function MultiLoopBrTest() { + print("MultiLoopBrTest"); + let builder = new WasmModuleBuilder(); + let sig_i_ii = builder.addType(kSig_i_ii); + let sig_ii_i = builder.addType(kSig_ii_i); + let sig_ii_ii = builder.addType(kSig_ii_ii); + + builder.addFunction("dup", kSig_ii_i) + .addBody([kExprGetLocal, 0, kExprGetLocal, 0]); + builder.addFunction("swap", kSig_ii_ii) + .addBody([kExprGetLocal, 1, kExprGetLocal, 0]); + builder.addFunction("main", kSig_i_ii) + .addBody([ + kExprGetLocal, 0, + kExprGetLocal, 1, + kExprLoop, sig_ii_ii, + kExprCallFunction, 1, // swap + kExprCallFunction, 0, // dup + kExprI32Add, + kExprCallFunction, 0, // dup + kExprI32Const, 20, + kExprI32LeU, + kExprBrIf, 0, + kExprEnd, + kExprDrop]) + .exportAs("main"); + + let module = new WebAssembly.Module(builder.toBuffer()); + let instance = new WebAssembly.Instance(module); + assertEquals(0, instance.exports.main(0, 1)); + assertEquals(16, instance.exports.main(1, 1)); + assertEquals(4, instance.exports.main(3, 1)); + assertEquals(4, instance.exports.main(4, 1)); + assertEquals(0, instance.exports.main(0, 2)); + assertEquals(16, instance.exports.main(1, 2)); + assertEquals(8, instance.exports.main(3, 2)); + assertEquals(8, instance.exports.main(4, 2)); + assertEquals(0, instance.exports.main(0, 3)); + assertEquals(8, instance.exports.main(1, 3)); + assertEquals(12, instance.exports.main(3, 3)); + assertEquals(12, instance.exports.main(4, 3)); + assertEquals(0, instance.exports.main(0, 4)); + assertEquals(8, instance.exports.main(1, 4)); + assertEquals(16, instance.exports.main(3, 4)); + assertEquals(16, instance.exports.main(4, 4)); + assertEquals(3, instance.exports.main(100, 3)); + assertEquals(6, instance.exports.main(3, 100)); +})(); + + +(function MultiIfResultTest() { + print("MultiIfResultTest"); + let builder = new WasmModuleBuilder(); + let sig_i_ii = builder.addType(kSig_i_ii); + let sig_ii_v = builder.addType(kSig_ii_v); + + builder.addFunction("main", kSig_i_ii) + .addBody([ + kExprGetLocal, 0, + kExprIf, sig_ii_v, + kExprGetLocal, 0, + kExprGetLocal, 1, + kExprElse, + kExprGetLocal, 1, + kExprGetLocal, 0, + kExprEnd, + kExprI32Sub]) + .exportAs("main"); + + let module = new WebAssembly.Module(builder.toBuffer()); + let instance = new WebAssembly.Instance(module); + assertEquals(instance.exports.main(8, 3), 5); + assertEquals(instance.exports.main(0, 3), 3); +})(); + +(function MultiIfParamTest() { + print("MultiIfParamTest"); + let builder = new WasmModuleBuilder(); + let sig_i_ii = builder.addType(kSig_i_ii); + + builder.addFunction("main", kSig_i_ii) + .addBody([ + kExprGetLocal, 0, + kExprGetLocal, 1, + kExprGetLocal, 0, + kExprIf, sig_i_ii, + kExprI32Add, + kExprElse, + kExprI32Sub, + kExprEnd]) + .exportAs("main"); + + let module = new WebAssembly.Module(builder.toBuffer()); + let instance = new WebAssembly.Instance(module); + assertEquals(instance.exports.main(1, 4), 5); + assertEquals(instance.exports.main(0, 4), -4); +})(); + +(function MultiIfBrTest() { + print("MultiIfBrTest"); + let builder = new WasmModuleBuilder(); + let sig_i_ii = builder.addType(kSig_i_ii); + let sig_ii_v = builder.addType(kSig_ii_v); + + builder.addFunction("main", kSig_i_ii) + .addBody([ + kExprGetLocal, 0, + kExprIf, sig_ii_v, + kExprGetLocal, 0, + kExprGetLocal, 1, + kExprBr, 0, + kExprElse, + kExprGetLocal, 1, + kExprGetLocal, 0, + kExprBr, 0, + kExprEnd, + kExprI32Sub]) + .exportAs("main"); + + let module = new WebAssembly.Module(builder.toBuffer()); + let instance = new WebAssembly.Instance(module); + assertEquals(instance.exports.main(8, 3), 5); + assertEquals(instance.exports.main(0, 3), 3); +})(); diff --git a/test/mjsunit/wasm/wasm-constants.js b/test/mjsunit/wasm/wasm-constants.js index df16cffe8083..f638ac1499b2 100644 --- a/test/mjsunit/wasm/wasm-constants.js +++ b/test/mjsunit/wasm/wasm-constants.js @@ -123,6 +123,10 @@ let kSig_v_l = makeSig([kWasmI64], []); let kSig_v_d = makeSig([kWasmF64], []); let kSig_v_dd = makeSig([kWasmF64, kWasmF64], []); let kSig_v_ddi = makeSig([kWasmF64, kWasmF64, kWasmI32], []); +let kSig_ii_v = makeSig([], [kWasmI32, kWasmI32]); +let kSig_iii_v = makeSig([], [kWasmI32, kWasmI32, kWasmI32]); +let kSig_ii_i = makeSig([kWasmI32], [kWasmI32, kWasmI32]); +let kSig_ii_ii = makeSig([kWasmI32, kWasmI32], [kWasmI32, kWasmI32]); let kSig_v_f = makeSig([kWasmF32], []); let kSig_f_f = makeSig([kWasmF32], [kWasmF32]); diff --git a/test/unittests/wasm/function-body-decoder-unittest.cc b/test/unittests/wasm/function-body-decoder-unittest.cc index 3a01f302bb4c..d02dca36be82 100644 --- a/test/unittests/wasm/function-body-decoder-unittest.cc +++ b/test/unittests/wasm/function-body-decoder-unittest.cc @@ -2390,14 +2390,14 @@ TEST_F(FunctionBodyDecoderTest, MultiValBlock1) { TestModuleBuilder builder; module = builder.module(); byte f0 = builder.AddSignature(sigs.ii_v()); - EXPECT_VERIFIES(i_ii, WASM_BLOCK_TT(f0, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)), + EXPECT_VERIFIES(i_ii, WASM_BLOCK_X(f0, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)), kExprI32Add); - EXPECT_FAILURE(i_ii, WASM_BLOCK_TT(f0, WASM_NOP), kExprI32Add); - EXPECT_FAILURE(i_ii, WASM_BLOCK_TT(f0, WASM_GET_LOCAL(0)), kExprI32Add); - EXPECT_FAILURE(i_ii, WASM_BLOCK_TT(f0, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1), - WASM_GET_LOCAL(0)), + EXPECT_FAILURE(i_ii, WASM_BLOCK_X(f0, WASM_NOP), kExprI32Add); + EXPECT_FAILURE(i_ii, WASM_BLOCK_X(f0, WASM_GET_LOCAL(0)), kExprI32Add); + EXPECT_FAILURE(i_ii, WASM_BLOCK_X(f0, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1), + WASM_GET_LOCAL(0)), kExprI32Add); - EXPECT_FAILURE(i_ii, WASM_BLOCK_TT(f0, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)), + EXPECT_FAILURE(i_ii, WASM_BLOCK_X(f0, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)), kExprF32Add); } @@ -2406,16 +2406,16 @@ TEST_F(FunctionBodyDecoderTest, MultiValBlock2) { TestModuleBuilder builder; module = builder.module(); byte f0 = builder.AddSignature(sigs.ii_v()); - EXPECT_VERIFIES(i_ii, WASM_BLOCK_TT(f0, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)), + EXPECT_VERIFIES(i_ii, WASM_BLOCK_X(f0, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)), WASM_I32_ADD(WASM_NOP, WASM_NOP)); - EXPECT_FAILURE(i_ii, WASM_BLOCK_TT(f0, WASM_NOP), + EXPECT_FAILURE(i_ii, WASM_BLOCK_X(f0, WASM_NOP), WASM_I32_ADD(WASM_NOP, WASM_NOP)); - EXPECT_FAILURE(i_ii, WASM_BLOCK_TT(f0, WASM_GET_LOCAL(0)), + EXPECT_FAILURE(i_ii, WASM_BLOCK_X(f0, WASM_GET_LOCAL(0)), WASM_I32_ADD(WASM_NOP, WASM_NOP)); - EXPECT_FAILURE(i_ii, WASM_BLOCK_TT(f0, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1), - WASM_GET_LOCAL(0)), + EXPECT_FAILURE(i_ii, WASM_BLOCK_X(f0, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1), + WASM_GET_LOCAL(0)), WASM_I32_ADD(WASM_NOP, WASM_NOP)); - EXPECT_FAILURE(i_ii, WASM_BLOCK_TT(f0, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)), + EXPECT_FAILURE(i_ii, WASM_BLOCK_X(f0, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)), WASM_F32_ADD(WASM_NOP, WASM_NOP)); } @@ -2424,10 +2424,10 @@ TEST_F(FunctionBodyDecoderTest, MultiValBlockBr) { TestModuleBuilder builder; module = builder.module(); byte f0 = builder.AddSignature(sigs.ii_v()); - EXPECT_FAILURE( - i_ii, WASM_BLOCK_TT(f0, WASM_GET_LOCAL(0), WASM_BR(0)), kExprI32Add); - EXPECT_VERIFIES(i_ii, WASM_BLOCK_TT(f0, WASM_GET_LOCAL(0), - WASM_GET_LOCAL(1), WASM_BR(0)), + EXPECT_FAILURE(i_ii, WASM_BLOCK_X(f0, WASM_GET_LOCAL(0), WASM_BR(0)), + kExprI32Add); + EXPECT_VERIFIES(i_ii, WASM_BLOCK_X(f0, WASM_GET_LOCAL(0), + WASM_GET_LOCAL(1), WASM_BR(0)), kExprI32Add); } @@ -2436,14 +2436,14 @@ TEST_F(FunctionBodyDecoderTest, MultiValLoop1) { TestModuleBuilder builder; module = builder.module(); byte f0 = builder.AddSignature(sigs.ii_v()); - EXPECT_VERIFIES(i_ii, WASM_LOOP_TT(f0, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)), + EXPECT_VERIFIES(i_ii, WASM_LOOP_X(f0, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)), kExprI32Add); - EXPECT_FAILURE(i_ii, WASM_LOOP_TT(f0, WASM_NOP), kExprI32Add); - EXPECT_FAILURE(i_ii, WASM_LOOP_TT(f0, WASM_GET_LOCAL(0)), kExprI32Add); - EXPECT_FAILURE(i_ii, WASM_LOOP_TT(f0, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1), - WASM_GET_LOCAL(0)), + EXPECT_FAILURE(i_ii, WASM_LOOP_X(f0, WASM_NOP), kExprI32Add); + EXPECT_FAILURE(i_ii, WASM_LOOP_X(f0, WASM_GET_LOCAL(0)), kExprI32Add); + EXPECT_FAILURE(i_ii, WASM_LOOP_X(f0, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1), + WASM_GET_LOCAL(0)), kExprI32Add); - EXPECT_FAILURE(i_ii, WASM_LOOP_TT(f0, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)), + EXPECT_FAILURE(i_ii, WASM_LOOP_X(f0, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)), kExprF32Add); } @@ -2453,63 +2453,163 @@ TEST_F(FunctionBodyDecoderTest, MultiValIf) { module = builder.module(); byte f0 = builder.AddSignature(sigs.ii_v()); EXPECT_VERIFIES( - i_ii, WASM_IF_ELSE_TT(f0, WASM_GET_LOCAL(0), - WASM_SEQ(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)), - WASM_SEQ(WASM_GET_LOCAL(1), WASM_GET_LOCAL(0))), + i_ii, WASM_IF_ELSE_X(f0, WASM_GET_LOCAL(0), + WASM_SEQ(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)), + WASM_SEQ(WASM_GET_LOCAL(1), WASM_GET_LOCAL(0))), kExprI32Add); EXPECT_FAILURE( - i_ii, WASM_IF_ELSE_TT(f0, WASM_GET_LOCAL(0), WASM_NOP, WASM_NOP), + i_ii, WASM_IF_ELSE_X(f0, WASM_GET_LOCAL(0), WASM_NOP, WASM_NOP), kExprI32Add); EXPECT_FAILURE( - i_ii, WASM_IF_ELSE_TT(f0, WASM_GET_LOCAL(0), - WASM_NOP, - WASM_SEQ(WASM_GET_LOCAL(1), WASM_GET_LOCAL(0))), + i_ii, WASM_IF_ELSE_X(f0, WASM_GET_LOCAL(0), + WASM_NOP, + WASM_SEQ(WASM_GET_LOCAL(1), WASM_GET_LOCAL(0))), kExprI32Add); EXPECT_FAILURE( - i_ii, WASM_IF_ELSE_TT(f0, WASM_GET_LOCAL(0), - WASM_SEQ(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)), - WASM_NOP), + i_ii, WASM_IF_ELSE_X(f0, WASM_GET_LOCAL(0), + WASM_SEQ(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)), + WASM_NOP), kExprI32Add); EXPECT_FAILURE( - i_ii, WASM_IF_ELSE_TT(f0, WASM_GET_LOCAL(0), - WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)), + i_ii, WASM_IF_ELSE_X(f0, WASM_GET_LOCAL(0), + WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)), kExprI32Add); EXPECT_FAILURE( - i_ii, WASM_IF_ELSE_TT(f0, WASM_GET_LOCAL(0), - WASM_GET_LOCAL(0), - WASM_SEQ(WASM_GET_LOCAL(1), WASM_GET_LOCAL(0))), + i_ii, WASM_IF_ELSE_X(f0, WASM_GET_LOCAL(0), + WASM_GET_LOCAL(0), + WASM_SEQ(WASM_GET_LOCAL(1), WASM_GET_LOCAL(0))), kExprI32Add); EXPECT_FAILURE( - i_ii, WASM_IF_ELSE_TT(f0, WASM_GET_LOCAL(0), - WASM_SEQ(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)), - WASM_GET_LOCAL(1)), + i_ii, WASM_IF_ELSE_X(f0, WASM_GET_LOCAL(0), + WASM_SEQ(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)), + WASM_GET_LOCAL(1)), kExprI32Add); EXPECT_FAILURE( - i_ii, WASM_IF_ELSE_TT(f0, WASM_GET_LOCAL(0), - WASM_SEQ(WASM_GET_LOCAL(0), WASM_GET_LOCAL(0), - WASM_GET_LOCAL(0)), - WASM_SEQ(WASM_GET_LOCAL(1), WASM_GET_LOCAL(0), - WASM_GET_LOCAL(0))), + i_ii, WASM_IF_ELSE_X(f0, WASM_GET_LOCAL(0), + WASM_SEQ(WASM_GET_LOCAL(0), WASM_GET_LOCAL(0), + WASM_GET_LOCAL(0)), + WASM_SEQ(WASM_GET_LOCAL(1), WASM_GET_LOCAL(0), + WASM_GET_LOCAL(0))), kExprI32Add); EXPECT_FAILURE( - i_ii, WASM_IF_ELSE_TT(f0, WASM_GET_LOCAL(0), - WASM_SEQ(WASM_GET_LOCAL(0), WASM_GET_LOCAL(0), - WASM_GET_LOCAL(0)), - WASM_SEQ(WASM_GET_LOCAL(1), WASM_GET_LOCAL(0))), + i_ii, WASM_IF_ELSE_X(f0, WASM_GET_LOCAL(0), + WASM_SEQ(WASM_GET_LOCAL(0), WASM_GET_LOCAL(0), + WASM_GET_LOCAL(0)), + WASM_SEQ(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1))), kExprI32Add); EXPECT_FAILURE( - i_ii, WASM_IF_ELSE_TT(f0, WASM_GET_LOCAL(0), - WASM_SEQ(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)), - WASM_SEQ(WASM_GET_LOCAL(1), WASM_GET_LOCAL(1), - WASM_GET_LOCAL(1))), + i_ii, WASM_IF_ELSE_X(f0, WASM_GET_LOCAL(0), + WASM_SEQ(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)), + WASM_SEQ(WASM_GET_LOCAL(1), WASM_GET_LOCAL(1), + WASM_GET_LOCAL(1))), kExprI32Add); EXPECT_FAILURE( - i_ii, WASM_IF_ELSE_TT(f0, WASM_GET_LOCAL(0), - WASM_SEQ(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)), - WASM_SEQ(WASM_GET_LOCAL(1), WASM_GET_LOCAL(0))), + i_ii, WASM_IF_ELSE_X(f0, WASM_GET_LOCAL(0), + WASM_SEQ(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)), + WASM_SEQ(WASM_GET_LOCAL(1), WASM_GET_LOCAL(0))), kExprF32Add); } +TEST_F(FunctionBodyDecoderTest, BlockParam) { + EXPERIMENTAL_FLAG_SCOPE(mv); + TestModuleBuilder builder; + module = builder.module(); + byte f1 = builder.AddSignature(sigs.i_i()); + byte f2 = builder.AddSignature(sigs.i_ii()); + EXPECT_VERIFIES(i_ii, WASM_GET_LOCAL(0), + WASM_BLOCK_X(f1, WASM_GET_LOCAL(1), + WASM_I32_ADD(WASM_NOP, WASM_NOP))); + EXPECT_VERIFIES(i_ii, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1), + WASM_BLOCK_X(f2, WASM_I32_ADD(WASM_NOP, WASM_NOP))); + EXPECT_VERIFIES(i_ii, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1), + WASM_BLOCK_X(f1, WASM_NOP), + WASM_I32_ADD(WASM_NOP, WASM_NOP)); + EXPECT_FAILURE(i_ii, WASM_BLOCK_X(f1, WASM_NOP), + WASM_RETURN1(WASM_GET_LOCAL(0))); + EXPECT_FAILURE(i_ii, WASM_BLOCK_X(f1, WASM_GET_LOCAL(0)), + WASM_RETURN1(WASM_GET_LOCAL(0))); + EXPECT_FAILURE(i_ii, WASM_GET_LOCAL(0), + WASM_BLOCK_X(f2, WASM_I32_ADD(WASM_NOP, WASM_NOP)), + WASM_RETURN1(WASM_GET_LOCAL(0))); + EXPECT_FAILURE(i_ii, WASM_GET_LOCAL(0), + WASM_BLOCK_X(f1, WASM_F32_NEG(WASM_NOP)), + WASM_RETURN1(WASM_GET_LOCAL(0))); +} + +TEST_F(FunctionBodyDecoderTest, LoopParam) { + EXPERIMENTAL_FLAG_SCOPE(mv); + TestModuleBuilder builder; + module = builder.module(); + byte f1 = builder.AddSignature(sigs.i_i()); + byte f2 = builder.AddSignature(sigs.i_ii()); + EXPECT_VERIFIES(i_ii, WASM_GET_LOCAL(0), + WASM_LOOP_X(f1, WASM_GET_LOCAL(1), + WASM_I32_ADD(WASM_NOP, WASM_NOP))); + EXPECT_VERIFIES(i_ii, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1), + WASM_LOOP_X(f2, WASM_I32_ADD(WASM_NOP, WASM_NOP))); + EXPECT_VERIFIES(i_ii, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1), + WASM_LOOP_X(f1, WASM_NOP), + WASM_I32_ADD(WASM_NOP, WASM_NOP)); + EXPECT_FAILURE(i_ii, WASM_LOOP_X(f1, WASM_NOP), + WASM_RETURN1(WASM_GET_LOCAL(0))); + EXPECT_FAILURE(i_ii, WASM_LOOP_X(f1, WASM_GET_LOCAL(0)), + WASM_RETURN1(WASM_GET_LOCAL(0))); + EXPECT_FAILURE(i_ii, WASM_GET_LOCAL(0), + WASM_LOOP_X(f2, WASM_I32_ADD(WASM_NOP, WASM_NOP)), + WASM_RETURN1(WASM_GET_LOCAL(0))); + EXPECT_FAILURE(i_ii, WASM_GET_LOCAL(0), + WASM_LOOP_X(f1, WASM_F32_NEG(WASM_NOP)), + WASM_RETURN1(WASM_GET_LOCAL(0))); +} + +TEST_F(FunctionBodyDecoderTest, LoopParamBr) { + EXPERIMENTAL_FLAG_SCOPE(mv); + TestModuleBuilder builder; + module = builder.module(); + byte f1 = builder.AddSignature(sigs.i_i()); + byte f2 = builder.AddSignature(sigs.i_ii()); + EXPECT_VERIFIES(i_ii, WASM_GET_LOCAL(0), + WASM_LOOP_X(f1, WASM_BR(0))); + EXPECT_VERIFIES(i_ii, WASM_GET_LOCAL(0), + WASM_LOOP_X(f1, WASM_BRV(0, WASM_GET_LOCAL(1)))); + EXPECT_VERIFIES(i_ii, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1), + WASM_LOOP_X(f2, WASM_BR(0))); + EXPECT_VERIFIES(i_ii, WASM_GET_LOCAL(0), + WASM_LOOP_X(f1, WASM_BLOCK_X(f1, WASM_BR(1)))); + EXPECT_FAILURE(i_ii, WASM_GET_LOCAL(0), + WASM_LOOP_X(f1, WASM_BLOCK(WASM_BR(1))), + WASM_RETURN1(WASM_GET_LOCAL(0))); + EXPECT_FAILURE(i_ii, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1), + WASM_LOOP_X(f2, WASM_BLOCK_X(f1, WASM_BR(1))), + WASM_RETURN1(WASM_GET_LOCAL(0))); +} + +TEST_F(FunctionBodyDecoderTest, IfParam) { + EXPERIMENTAL_FLAG_SCOPE(mv); + TestModuleBuilder builder; + module = builder.module(); + byte f1 = builder.AddSignature(sigs.i_i()); + byte f2 = builder.AddSignature(sigs.i_ii()); + EXPECT_VERIFIES(i_ii, WASM_GET_LOCAL(0), + WASM_IF_X(f1, WASM_GET_LOCAL(0), + WASM_I32_ADD(WASM_NOP, WASM_GET_LOCAL(1)))); + EXPECT_VERIFIES(i_ii, WASM_GET_LOCAL(0), + WASM_IF_ELSE_X(f1, WASM_GET_LOCAL(0), + WASM_I32_ADD(WASM_NOP, WASM_GET_LOCAL(1)), + WASM_I32_EQZ(WASM_NOP))); + EXPECT_VERIFIES(i_ii, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1), + WASM_IF_ELSE_X(f2, WASM_GET_LOCAL(0), + WASM_I32_ADD(WASM_NOP, WASM_NOP), + WASM_I32_MUL(WASM_NOP, WASM_NOP))); + EXPECT_VERIFIES(i_ii, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1), + WASM_IF_X(f1, WASM_GET_LOCAL(0), WASM_NOP), + WASM_I32_ADD(WASM_NOP, WASM_NOP)); + EXPECT_VERIFIES(i_ii, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1), + WASM_IF_ELSE_X(f1, WASM_GET_LOCAL(0), + WASM_NOP, WASM_I32_EQZ(WASM_NOP)), + WASM_I32_ADD(WASM_NOP, WASM_NOP)); +} + TEST_F(FunctionBodyDecoderTest, Regression709741) { AddLocals(kWasmI32, kV8MaxWasmFunctionLocals - 1); EXPECT_VERIFIES(v_v, WASM_NOP);