Commit 06061b7d authored by Andreas Rossberg's avatar Andreas Rossberg Committed by Commit Bot

[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 <rossberg@chromium.org>
Reviewed-by: 's avatarBen Titzer <titzer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#48871}
parent d75ecf13
...@@ -191,7 +191,7 @@ class LiftoffCompiler { ...@@ -191,7 +191,7 @@ class LiftoffCompiler {
} }
void FallThruTo(Decoder* decoder, Control* c) { void FallThruTo(Decoder* decoder, Control* c) {
if (c->merge.reached) { if (c->end_merge.reached) {
__ MergeFullStackWith(c->label_state); __ MergeFullStackWith(c->label_state);
} else { } else {
c->label_state.Split(*__ cache_state()); c->label_state.Split(*__ cache_state());
...@@ -199,7 +199,7 @@ class LiftoffCompiler { ...@@ -199,7 +199,7 @@ class LiftoffCompiler {
} }
void PopControl(Decoder* decoder, Control* c) { 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); __ cache_state()->Steal(c->label_state);
} }
if (!c->label->is_bound()) { if (!c->label->is_bound()) {
...@@ -383,12 +383,12 @@ class LiftoffCompiler { ...@@ -383,12 +383,12 @@ class LiftoffCompiler {
unsupported(decoder, "select"); unsupported(decoder, "select");
} }
void BreakTo(Decoder* decoder, Control* target) { void Br(Decoder* decoder, Control* target) {
if (!target->merge.reached) { if (!target->br_merge()->reached) {
target->label_state.InitMerge(*__ cache_state(), __ num_locals(), 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()); __ jmp(target->label.get());
} }
...@@ -397,7 +397,7 @@ class LiftoffCompiler { ...@@ -397,7 +397,7 @@ class LiftoffCompiler {
Register value = __ PopToRegister(kWasmI32); Register value = __ PopToRegister(kWasmI32);
__ JumpIfZero(value, &cont_false); __ JumpIfZero(value, &cont_false);
BreakTo(decoder, target); Br(decoder, target);
__ bind(&cont_false); __ bind(&cont_false);
} }
......
...@@ -81,11 +81,12 @@ struct WasmException; ...@@ -81,11 +81,12 @@ struct WasmException;
V(I32AtomicStore8U, Uint8) \ V(I32AtomicStore8U, Uint8) \
V(I32AtomicStore16U, Uint16) V(I32AtomicStore16U, Uint16)
template <typename T> template <typename T, typename Allocator>
Vector<T> vec2vec(std::vector<T>& vec) { Vector<T> vec2vec(std::vector<T, Allocator>& vec) {
return Vector<T>(vec.data(), vec.size()); return Vector<T>(vec.data(), vec.size());
} }
// Helpers for decoding different kinds of operands which follow bytecodes. // Helpers for decoding different kinds of operands which follow bytecodes.
template <Decoder::ValidateFlag validate> template <Decoder::ValidateFlag validate>
struct LocalIndexOperand { struct LocalIndexOperand {
...@@ -446,20 +447,18 @@ enum Reachability : uint8_t { ...@@ -446,20 +447,18 @@ enum Reachability : uint8_t {
// An entry on the control stack (i.e. if, block, loop, or try). // An entry on the control stack (i.e. if, block, loop, or try).
template <typename Value> template <typename Value>
struct ControlBase { struct ControlBase {
Reachability reachability = kReachable;
ControlKind kind; ControlKind kind;
uint32_t stack_depth; // stack height at the beginning of the construct. uint32_t stack_depth; // stack height at the beginning of the construct.
const byte* pc; const byte* pc;
Reachability reachability = kReachable;
// Values merged into the end of this control construct. // Values merged into the start or end of this control construct.
Merge<Value> merge; Merge<Value> start_merge;
Merge<Value> end_merge;
ControlBase() = default; ControlBase() = default;
ControlBase(ControlKind kind, uint32_t stack_depth, const byte* pc, ControlBase(ControlKind kind, uint32_t stack_depth, const byte* pc)
bool merge_reached = false) : kind(kind), stack_depth(stack_depth), pc(pc) {}
: kind(kind), stack_depth(stack_depth), pc(pc), merge(merge_reached) {}
uint32_t break_arity() const { return is_loop() ? 0 : merge.arity; }
// Check whether the current block is reachable. // Check whether the current block is reachable.
bool reachable() const { return reachability == kReachable; } bool reachable() const { return reachability == kReachable; }
...@@ -484,6 +483,10 @@ struct ControlBase { ...@@ -484,6 +483,10 @@ struct ControlBase {
bool is_incomplete_try() const { return kind == kControlTry; } bool is_incomplete_try() const { return kind == kControlTry; }
bool is_try_catch() const { return kind == kControlTryCatch; } bool is_try_catch() const { return kind == kControlTryCatch; }
inline Merge<Value>* br_merge() {
return is_loop() ? &this->start_merge : &this->end_merge;
}
// Named constructors. // Named constructors.
static ControlBase Block(const byte* pc, uint32_t stack_depth) { static ControlBase Block(const byte* pc, uint32_t stack_depth) {
return {kControlBlock, stack_depth, pc}; return {kControlBlock, stack_depth, pc};
...@@ -493,8 +496,8 @@ struct ControlBase { ...@@ -493,8 +496,8 @@ struct ControlBase {
return {kControlIf, stack_depth, pc}; return {kControlIf, stack_depth, pc};
} }
static ControlBase Loop(const byte* pc, uint32_t stack_depth, bool reached) { static ControlBase Loop(const byte* pc, uint32_t stack_depth) {
return {kControlLoop, stack_depth, pc, reached}; return {kControlLoop, stack_depth, pc};
} }
static ControlBase Try(const byte* pc, uint32_t stack_depth) { static ControlBase Try(const byte* pc, uint32_t stack_depth) {
...@@ -575,7 +578,7 @@ struct ControlWithNamedConstructors : public ControlBase<Value> { ...@@ -575,7 +578,7 @@ struct ControlWithNamedConstructors : public ControlBase<Value> {
F(Unreachable) \ F(Unreachable) \
F(Select, const Value& cond, const Value& fval, const Value& tval, \ F(Select, const Value& cond, const Value& fval, const Value& tval, \
Value* result) \ Value* result) \
F(BreakTo, Control* target) \ F(Br, Control* target) \
F(BrIf, const Value& cond, Control* target) \ F(BrIf, const Value& cond, Control* target) \
F(BrTable, const BranchTableOperand<validate>& operand, const Value& key) \ F(BrTable, const BranchTableOperand<validate>& operand, const Value& key) \
F(Else, Control* if_block) \ F(Else, Control* if_block) \
...@@ -1128,6 +1131,7 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -1128,6 +1131,7 @@ class WasmFullDecoder : public WasmDecoder<validate> {
local_type_vec_(zone), local_type_vec_(zone),
stack_(zone), stack_(zone),
control_(zone), control_(zone),
args_(zone),
last_end_found_(false) { last_end_found_(false) {
this->local_types_ = &local_type_vec_; this->local_types_ = &local_type_vec_;
} }
...@@ -1232,10 +1236,12 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -1232,10 +1236,12 @@ class WasmFullDecoder : public WasmDecoder<validate> {
return &stack_[stack_.size() - depth - 1]; return &stack_[stack_.size() - depth - 1];
} }
inline Value& GetMergeValueFromStack(Control* c, uint32_t i) { inline Value& GetMergeValueFromStack(
DCHECK_GT(c->merge.arity, i); Control* c, Merge<Value>* merge, uint32_t i) {
DCHECK_GE(stack_.size(), c->stack_depth + c->merge.arity); DCHECK(merge == &c->start_merge || merge == &c->end_merge);
return stack_[stack_.size() - c->merge.arity + i]; DCHECK_GT(merge->arity, i);
DCHECK_GE(stack_.size(), c->stack_depth + merge->arity);
return stack_[stack_.size() - merge->arity + i];
} }
private: private:
...@@ -1248,6 +1254,7 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -1248,6 +1254,7 @@ class WasmFullDecoder : public WasmDecoder<validate> {
ZoneVector<ValueType> local_type_vec_; // types of local variables. ZoneVector<ValueType> local_type_vec_; // types of local variables.
ZoneVector<Value> stack_; // stack of values. ZoneVector<Value> stack_; // stack of values.
ZoneVector<Control> control_; // stack of blocks, loops, and ifs. ZoneVector<Control> control_; // stack of blocks, loops, and ifs.
ZoneVector<Value> args_; // parameters of current block or call
bool last_end_found_; bool last_end_found_;
bool CheckHasMemory() { bool CheckHasMemory() {
...@@ -1276,17 +1283,10 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -1276,17 +1283,10 @@ class WasmFullDecoder : public WasmDecoder<validate> {
// Set up initial function block. // Set up initial function block.
{ {
auto* c = PushBlock(); auto* c = PushBlock();
c->merge.arity = static_cast<uint32_t>(this->sig_->return_count()); InitMerge(&c->end_merge,
static_cast<uint32_t>(this->sig_->return_count()),
if (c->merge.arity == 1) { [&] (uint32_t i) {
c->merge.vals.first = Value::New(this->pc_, this->sig_->GetReturn(0)); return Value::New(this->pc_, this->sig_->GetReturn(i)); });
} 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] =
Value::New(this->pc_, this->sig_->GetReturn(i));
}
}
CALL_INTERFACE(StartFunctionBody, c); CALL_INTERFACE(StartFunctionBody, c);
} }
...@@ -1311,10 +1311,12 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -1311,10 +1311,12 @@ class WasmFullDecoder : public WasmDecoder<validate> {
case kExprBlock: { case kExprBlock: {
BlockTypeOperand<validate> operand(this, this->pc_); BlockTypeOperand<validate> operand(this, this->pc_);
if (!LookupBlockType(&operand)) break; if (!LookupBlockType(&operand)) break;
PopArgs(operand.sig);
auto* block = PushBlock(); auto* block = PushBlock();
SetBlockType(block, operand); SetBlockType(block, operand, args_);
len = 1 + operand.length;
CALL_INTERFACE_IF_REACHABLE(Block, block); CALL_INTERFACE_IF_REACHABLE(Block, block);
PushMergeValues(block, &block->start_merge);
len = 1 + operand.length;
break; break;
} }
case kExprRethrow: { case kExprRethrow: {
...@@ -1328,10 +1330,9 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -1328,10 +1330,9 @@ class WasmFullDecoder : public WasmDecoder<validate> {
ExceptionIndexOperand<Decoder::kValidate> operand(this, this->pc_); ExceptionIndexOperand<Decoder::kValidate> operand(this, this->pc_);
len = 1 + operand.length; len = 1 + operand.length;
if (!this->Validate(this->pc_, operand)) break; if (!this->Validate(this->pc_, operand)) break;
std::vector<Value> args; PopArgs(operand.exception->ToFunctionSig());
PopArgs(operand.exception->ToFunctionSig(), &args);
CALL_INTERFACE_IF_REACHABLE(Throw, operand, &control_.back(), CALL_INTERFACE_IF_REACHABLE(Throw, operand, &control_.back(),
vec2vec(args)); vec2vec(args_));
EndControl(); EndControl();
break; break;
} }
...@@ -1339,10 +1340,12 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -1339,10 +1340,12 @@ class WasmFullDecoder : public WasmDecoder<validate> {
CHECK_PROTOTYPE_OPCODE(eh); CHECK_PROTOTYPE_OPCODE(eh);
BlockTypeOperand<validate> operand(this, this->pc_); BlockTypeOperand<validate> operand(this, this->pc_);
if (!LookupBlockType(&operand)) break; if (!LookupBlockType(&operand)) break;
PopArgs(operand.sig);
auto* try_block = PushTry(); auto* try_block = PushTry();
SetBlockType(try_block, operand); SetBlockType(try_block, operand, args_);
len = 1 + operand.length; len = 1 + operand.length;
CALL_INTERFACE_IF_REACHABLE(Try, try_block); CALL_INTERFACE_IF_REACHABLE(Try, try_block);
PushMergeValues(try_block, &try_block->start_merge);
break; break;
} }
case kExprCatch: { case kExprCatch: {
...@@ -1391,21 +1394,25 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -1391,21 +1394,25 @@ class WasmFullDecoder : public WasmDecoder<validate> {
case kExprLoop: { case kExprLoop: {
BlockTypeOperand<validate> operand(this, this->pc_); BlockTypeOperand<validate> operand(this, this->pc_);
if (!LookupBlockType(&operand)) break; if (!LookupBlockType(&operand)) break;
PopArgs(operand.sig);
auto* block = PushLoop(); auto* block = PushLoop();
SetBlockType(&control_.back(), operand); SetBlockType(&control_.back(), operand, args_);
len = 1 + operand.length; len = 1 + operand.length;
CALL_INTERFACE_IF_REACHABLE(Loop, block); CALL_INTERFACE_IF_REACHABLE(Loop, block);
PushMergeValues(block, &block->start_merge);
break; break;
} }
case kExprIf: { case kExprIf: {
BlockTypeOperand<validate> operand(this, this->pc_); BlockTypeOperand<validate> operand(this, this->pc_);
if (!LookupBlockType(&operand)) break; if (!LookupBlockType(&operand)) break;
auto cond = Pop(0, kWasmI32); auto cond = Pop(0, kWasmI32);
PopArgs(operand.sig);
if (!this->ok()) break; if (!this->ok()) break;
auto* if_block = PushIf(); auto* if_block = PushIf();
SetBlockType(if_block, operand); SetBlockType(if_block, operand, args_);
CALL_INTERFACE_IF_REACHABLE(If, cond, if_block); CALL_INTERFACE_IF_REACHABLE(If, cond, if_block);
len = 1 + operand.length; len = 1 + operand.length;
PushMergeValues(if_block, &if_block->start_merge);
break; break;
} }
case kExprElse: { case kExprElse: {
...@@ -1424,8 +1431,8 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -1424,8 +1431,8 @@ class WasmFullDecoder : public WasmDecoder<validate> {
} }
c->kind = kControlIfElse; c->kind = kControlIfElse;
FallThruTo(c); FallThruTo(c);
stack_.resize(c->stack_depth);
CALL_INTERFACE_IF_PARENT_REACHABLE(Else, c); CALL_INTERFACE_IF_PARENT_REACHABLE(Else, c);
PushMergeValues(c, &c->start_merge);
c->reachability = control_at(1)->innerReachability(); c->reachability = control_at(1)->innerReachability();
break; break;
} }
...@@ -1435,30 +1442,21 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -1435,30 +1442,21 @@ class WasmFullDecoder : public WasmDecoder<validate> {
return; return;
} }
Control* c = &control_.back(); Control* c = &control_.back();
if (c->is_loop()) { if (!VALIDATE(!c->is_incomplete_try())) {
// A loop just leaves the values on the stack. this->error(this->pc_, "missing catch in try");
TypeCheckFallThru(c);
PopControl(c);
break; break;
} }
if (c->is_onearmed_if()) { if (c->is_onearmed_if()) {
// The merge point is reached if the if is not taken. // Emulate empty else arm.
if (control_at(1)->reachable()) c->merge.reached = true; FallThruTo(c);
// End the true branch of a one-armed if. CALL_INTERFACE_IF_PARENT_REACHABLE(Else, c);
if (!VALIDATE(c->unreachable() || PushMergeValues(c, &c->start_merge);
stack_.size() == c->stack_depth)) { c->reachability = control_at(1)->innerReachability();
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;
} }
FallThruTo(c); 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 (control_.size() == 1) {
// If at the last (implicit) control, check we are at end. // If at the last (implicit) control, check we are at end.
...@@ -1490,8 +1488,8 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -1490,8 +1488,8 @@ class WasmFullDecoder : public WasmDecoder<validate> {
if (!this->Validate(this->pc_, operand, control_.size())) break; if (!this->Validate(this->pc_, operand, control_.size())) break;
Control* c = control_at(operand.depth); Control* c = control_at(operand.depth);
if (!TypeCheckBreak(c)) break; if (!TypeCheckBreak(c)) break;
CALL_INTERFACE_IF_REACHABLE(BreakTo, c); CALL_INTERFACE_IF_REACHABLE(Br, c);
if (control_.back().reachable()) c->merge.reached = true; BreakTo(c);
len = 1 + operand.length; len = 1 + operand.length;
EndControl(); EndControl();
break; break;
...@@ -1503,7 +1501,7 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -1503,7 +1501,7 @@ class WasmFullDecoder : public WasmDecoder<validate> {
Control* c = control_at(operand.depth); Control* c = control_at(operand.depth);
if (!TypeCheckBreak(c)) break; if (!TypeCheckBreak(c)) break;
CALL_INTERFACE_IF_REACHABLE(BrIf, cond, c); CALL_INTERFACE_IF_REACHABLE(BrIf, cond, c);
if (control_.back().reachable()) c->merge.reached = true; BreakTo(c);
len = 1 + operand.length; len = 1 + operand.length;
break; break;
} }
...@@ -1523,7 +1521,7 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -1523,7 +1521,7 @@ class WasmFullDecoder : public WasmDecoder<validate> {
} }
// Check that label types match up. // Check that label types match up.
Control* c = control_at(target); Control* c = control_at(target);
uint32_t arity = c->break_arity(); uint32_t arity = c->br_merge()->arity;
if (i == 0) { if (i == 0) {
br_arity = arity; br_arity = arity;
} else if (!VALIDATE(br_arity == arity)) { } else if (!VALIDATE(br_arity == arity)) {
...@@ -1533,7 +1531,7 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -1533,7 +1531,7 @@ class WasmFullDecoder : public WasmDecoder<validate> {
i, br_arity, arity); i, br_arity, arity);
} }
if (!TypeCheckBreak(c)) break; if (!TypeCheckBreak(c)) break;
if (control_.back().reachable()) c->merge.reached = true; BreakTo(c);
} }
CALL_INTERFACE_IF_REACHABLE(BrTable, operand, key); CALL_INTERFACE_IF_REACHABLE(BrTable, operand, key);
...@@ -1726,10 +1724,9 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -1726,10 +1724,9 @@ class WasmFullDecoder : public WasmDecoder<validate> {
len = 1 + operand.length; len = 1 + operand.length;
if (!this->Validate(this->pc_, operand)) break; if (!this->Validate(this->pc_, operand)) break;
// TODO(clemensh): Better memory management. // TODO(clemensh): Better memory management.
std::vector<Value> args; PopArgs(operand.sig);
PopArgs(operand.sig, &args);
auto* returns = PushReturns(operand.sig); auto* returns = PushReturns(operand.sig);
CALL_INTERFACE_IF_REACHABLE(CallDirect, operand, args.data(), CALL_INTERFACE_IF_REACHABLE(CallDirect, operand, args_.data(),
returns); returns);
break; break;
} }
...@@ -1738,12 +1735,10 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -1738,12 +1735,10 @@ class WasmFullDecoder : public WasmDecoder<validate> {
len = 1 + operand.length; len = 1 + operand.length;
if (!this->Validate(this->pc_, operand)) break; if (!this->Validate(this->pc_, operand)) break;
auto index = Pop(0, kWasmI32); auto index = Pop(0, kWasmI32);
// TODO(clemensh): Better memory management. PopArgs(operand.sig);
std::vector<Value> args;
PopArgs(operand.sig, &args);
auto* returns = PushReturns(operand.sig); auto* returns = PushReturns(operand.sig);
CALL_INTERFACE_IF_REACHABLE(CallIndirect, index, operand, CALL_INTERFACE_IF_REACHABLE(CallIndirect, index, operand,
args.data(), returns); args_.data(), returns);
break; break;
} }
case kSimdPrefix: { case kSimdPrefix: {
...@@ -1804,7 +1799,8 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -1804,7 +1799,8 @@ class WasmFullDecoder : public WasmDecoder<validate> {
default: default:
break; 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() ? '*' : '#'); if (!c.reachable()) PrintF("%c", c.unreachable() ? '*' : '#');
} }
PrintF(" | "); PrintF(" | ");
...@@ -1872,25 +1868,34 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -1872,25 +1868,34 @@ class WasmFullDecoder : public WasmDecoder<validate> {
return true; return true;
} }
void SetBlockType(Control* c, BlockTypeOperand<validate>& operand) { template<typename func>
c->merge.arity = operand.out_arity(); void InitMerge(Merge<Value>* merge, uint32_t arity, func get_val) {
if (c->merge.arity == 1) { merge->arity = arity;
c->merge.vals.first = Value::New(this->pc_, operand.out_type(0)); if (arity == 1) {
} else if (c->merge.arity > 1) { merge->vals.first = get_val(0);
c->merge.vals.array = zone_->NewArray<Value>(c->merge.arity); } else if (arity > 1) {
for (unsigned i = 0; i < c->merge.arity; i++) { merge->vals.array = zone_->NewArray<Value>(arity);
c->merge.vals.array[i] = Value::New(this->pc_, operand.out_type(i)); for (unsigned i = 0; i < arity; i++) {
merge->vals.array[i] = get_val(i);
} }
} }
} }
// TODO(clemensh): Better memory management. void SetBlockType(Control* c, BlockTypeOperand<validate>& operand,
void PopArgs(FunctionSig* sig, std::vector<Value>* result) { ZoneVector<Value>& params) {
DCHECK(result->empty()); InitMerge(&c->end_merge, operand.out_arity(),
int count = static_cast<int>(sig->parameter_count()); [&] (uint32_t i) {
result->resize(count); 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<int>(sig->parameter_count()) : 0;
args_.resize(count);
for (int i = count - 1; i >= 0; --i) { 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<validate> { ...@@ -1905,6 +1910,7 @@ class WasmFullDecoder : public WasmDecoder<validate> {
control_.emplace_back(std::move(new_control)); control_.emplace_back(std::move(new_control));
Control* c = &control_.back(); Control* c = &control_.back();
c->reachability = reachability; c->reachability = reachability;
c->start_merge.reached = c->reachable();
return c; return c;
} }
...@@ -1912,8 +1918,7 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -1912,8 +1918,7 @@ class WasmFullDecoder : public WasmDecoder<validate> {
return PushControl(Control::Block(this->pc_, stack_size())); return PushControl(Control::Block(this->pc_, stack_size()));
} }
Control* PushLoop() { Control* PushLoop() {
return PushControl( return PushControl(Control::Loop(this->pc_, stack_size()));
Control::Loop(this->pc_, stack_size(), control_.back().reachable()));
} }
Control* PushIf() { Control* PushIf() {
return PushControl(Control::If(this->pc_, stack_size())); return PushControl(Control::If(this->pc_, stack_size()));
...@@ -1926,7 +1931,7 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -1926,7 +1931,7 @@ class WasmFullDecoder : public WasmDecoder<validate> {
void PopControl(Control* c) { void PopControl(Control* c) {
DCHECK_EQ(c, &control_.back()); DCHECK_EQ(c, &control_.back());
CALL_INTERFACE_IF_PARENT_REACHABLE(PopControl, c); 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(); control_.pop_back();
// If the parent block was reachable before, but the popped control does not // If the parent block was reachable before, but the popped control does not
// return to here, this block becomes indirectly unreachable. // return to here, this block becomes indirectly unreachable.
...@@ -2078,11 +2083,10 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -2078,11 +2083,10 @@ class WasmFullDecoder : public WasmDecoder<validate> {
this->error("invalid simd opcode"); this->error("invalid simd opcode");
break; break;
} }
std::vector<Value> args; PopArgs(sig);
PopArgs(sig, &args); auto* results =
auto* result =
sig->return_count() == 0 ? nullptr : Push(GetReturnType(sig)); 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; return len;
...@@ -2113,20 +2117,16 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -2113,20 +2117,16 @@ class WasmFullDecoder : public WasmDecoder<validate> {
#undef CASE_ATOMIC_OP #undef CASE_ATOMIC_OP
default: default:
this->error("invalid atomic opcode"); this->error("invalid atomic opcode");
break; return 0;
} }
// TODO(clemensh): Better memory management here.
std::vector<Value> args(sig->parameter_count());
MemoryAccessOperand<validate> operand( MemoryAccessOperand<validate> operand(
this, this->pc_ + 1, ElementSizeLog2Of(memtype.representation())); this, this->pc_ + 1, ElementSizeLog2Of(memtype.representation()));
len += operand.length; len += operand.length;
for (int i = static_cast<int>(sig->parameter_count() - 1); i >= 0; --i) { PopArgs(sig);
args[i] = Pop(i, sig->GetParam(i));
}
auto result = ret_type == MachineRepresentation::kNone auto result = ret_type == MachineRepresentation::kNone
? nullptr ? nullptr
: Push(GetReturnType(sig)); : Push(GetReturnType(sig));
CALL_INTERFACE_IF_REACHABLE(AtomicOp, opcode, vec2vec(args), operand, CALL_INTERFACE_IF_REACHABLE(AtomicOp, opcode, vec2vec(args_), operand,
result); result);
} else { } else {
this->error("invalid atomic opcode"); this->error("invalid atomic opcode");
...@@ -2135,19 +2135,17 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -2135,19 +2135,17 @@ class WasmFullDecoder : public WasmDecoder<validate> {
} }
void DoReturn(Control* c, bool implicit) { 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<int>(this->sig_->return_count()); int return_count = static_cast<int>(this->sig_->return_count());
std::vector<Value> values(return_count); args_.resize(return_count);
// Pop return values off the stack in reverse order. // Pop return values off the stack in reverse order.
for (int i = return_count - 1; i >= 0; --i) { 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))) { // Simulate that an implicit return morally comes after the current block.
CALL_INTERFACE(DoReturn, vec2vec(values), implicit); if (implicit && c->end_merge.reached) c->reachability = kReachable;
} CALL_INTERFACE_IF_REACHABLE(DoReturn, vec2vec(args_), implicit);
EndControl(); EndControl();
} }
...@@ -2158,17 +2156,18 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -2158,17 +2156,18 @@ class WasmFullDecoder : public WasmDecoder<validate> {
return &stack_.back(); return &stack_.back();
} }
void PushEndValues(Control* c) { void PushMergeValues(Control* c, Merge<Value>* merge) {
DCHECK_EQ(c, &control_.back()); DCHECK_EQ(c, &control_.back());
DCHECK(merge == &c->start_merge || merge == &c->end_merge);
stack_.resize(c->stack_depth); stack_.resize(c->stack_depth);
if (c->merge.arity == 1) { if (merge->arity == 1) {
stack_.push_back(c->merge.vals.first); stack_.push_back(merge->vals.first);
} else { } else {
for (unsigned i = 0; i < c->merge.arity; i++) { for (unsigned i = 0; i < merge->arity; i++) {
stack_.push_back(c->merge.vals.array[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) { Value* PushReturns(FunctionSig* sig) {
...@@ -2211,22 +2210,26 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -2211,22 +2210,26 @@ class WasmFullDecoder : public WasmDecoder<validate> {
int startrel(const byte* ptr) { return static_cast<int>(ptr - this->start_); } int startrel(const byte* ptr) { return static_cast<int>(ptr - this->start_); }
inline void BreakTo(Control* c) {
if (control_.back().reachable()) c->br_merge()->reached = true;
}
void FallThruTo(Control* c) { void FallThruTo(Control* c) {
DCHECK(!c->is_loop());
DCHECK_EQ(c, &control_.back()); DCHECK_EQ(c, &control_.back());
if (!TypeCheckFallThru(c)) return; if (!TypeCheckFallThru(c)) return;
if (!c->reachable()) return; if (!c->reachable()) return;
CALL_INTERFACE(FallThruTo, c); if (!c->is_loop()) CALL_INTERFACE(FallThruTo, c);
c->merge.reached = true; c->end_merge.reached = true;
} }
bool TypeCheckMergeValues(Control* c) { bool TypeCheckMergeValues(Control* c, Merge<Value>* merge) {
DCHECK_GE(stack_.size(), c->stack_depth + c->merge.arity); DCHECK(merge == &c->start_merge || merge == &c->end_merge);
// Typecheck the topmost {c->merge.arity} values on the stack. DCHECK_GE(stack_.size(), c->stack_depth + merge->arity);
for (uint32_t i = 0; i < c->merge.arity; ++i) { // Typecheck the topmost {merge->arity} values on the stack.
auto& val = GetMergeValueFromStack(c, i); for (uint32_t i = 0; i < merge->arity; ++i) {
auto& old = c->merge[i]; auto& val = GetMergeValueFromStack(c, merge, i);
auto& old = (*merge)[i];
if (val.type != old.type) { if (val.type != old.type) {
// If {val.type} is polymorphic, which results from unreachable, make // If {val.type} is polymorphic, which results from unreachable, make
// it more specific by using the merge value's expected type. // it more specific by using the merge value's expected type.
...@@ -2247,7 +2250,7 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -2247,7 +2250,7 @@ class WasmFullDecoder : public WasmDecoder<validate> {
bool TypeCheckFallThru(Control* c) { bool TypeCheckFallThru(Control* c) {
DCHECK_EQ(c, &control_.back()); DCHECK_EQ(c, &control_.back());
if (!validate) return true; if (!validate) return true;
uint32_t expected = c->merge.arity; uint32_t expected = c->end_merge.arity;
DCHECK_GE(stack_.size(), c->stack_depth); DCHECK_GE(stack_.size(), c->stack_depth);
uint32_t actual = static_cast<uint32_t>(stack_.size()) - c->stack_depth; uint32_t actual = static_cast<uint32_t>(stack_.size()) - c->stack_depth;
// Fallthrus must match the arity of the control exactly. // Fallthrus must match the arity of the control exactly.
...@@ -2259,16 +2262,12 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -2259,16 +2262,12 @@ class WasmFullDecoder : public WasmDecoder<validate> {
return false; return false;
} }
return TypeCheckMergeValues(c); return TypeCheckMergeValues(c, &c->end_merge);
} }
bool TypeCheckBreak(Control* c) { 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. // 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); DCHECK_GE(stack_.size(), control_.back().stack_depth);
uint32_t actual = uint32_t actual =
static_cast<uint32_t>(stack_.size()) - control_.back().stack_depth; static_cast<uint32_t>(stack_.size()) - control_.back().stack_depth;
...@@ -2278,7 +2277,7 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -2278,7 +2277,7 @@ class WasmFullDecoder : public WasmDecoder<validate> {
expected, startrel(c->pc), actual); expected, startrel(c->pc), actual);
return false; return false;
} }
return TypeCheckMergeValues(c); return TypeCheckMergeValues(c, c->br_merge());
} }
inline bool InsertUnreachablesIfNecessary(uint32_t expected, inline bool InsertUnreachablesIfNecessary(uint32_t expected,
......
...@@ -27,11 +27,6 @@ namespace wasm { ...@@ -27,11 +27,6 @@ namespace wasm {
namespace { namespace {
template <typename T>
Vector<T> vec2vec(ZoneVector<T>& vec) {
return Vector<T>(vec.data(), vec.size());
}
// An SsaEnv environment carries the current local variable renaming // An SsaEnv environment carries the current local variable renaming
// as well as the current effect and control dependency in the TF graph. // as well as the current effect and control dependency in the TF graph.
// It maintains a control state that tracks whether the environment // It maintains a control state that tracks whether the environment
...@@ -173,6 +168,12 @@ class WasmGraphBuildingInterface { ...@@ -173,6 +168,12 @@ class WasmGraphBuildingInterface {
// The continue environment is the inner environment. // The continue environment is the inner environment.
SetEnv(PrepareForLoop(decoder, finish_try_env)); SetEnv(PrepareForLoop(decoder, finish_try_env));
ssa_env_->SetNotMerged(); 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) { void Try(Decoder* decoder, Control* block) {
...@@ -206,14 +207,11 @@ class WasmGraphBuildingInterface { ...@@ -206,14 +207,11 @@ class WasmGraphBuildingInterface {
void FallThruTo(Decoder* decoder, Control* c) { void FallThruTo(Decoder* decoder, Control* c) {
DCHECK(!c->is_loop()); DCHECK(!c->is_loop());
MergeValuesInto(decoder, c); MergeValuesInto(decoder, c, &c->end_merge);
} }
void PopControl(Decoder* decoder, Control* block) { void PopControl(Decoder* decoder, Control* block) {
if (!block->is_loop()) SetEnv(block->end_env); 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(); } void EndControl(Decoder* decoder, Control* block) { ssa_env_->Kill(); }
...@@ -304,12 +302,8 @@ class WasmGraphBuildingInterface { ...@@ -304,12 +302,8 @@ class WasmGraphBuildingInterface {
ssa_env_->control = merge; ssa_env_->control = merge;
} }
void BreakTo(Decoder* decoder, Control* target) { void Br(Decoder* decoder, Control* target) {
if (target->is_loop()) { MergeValuesInto(decoder, target, target->br_merge());
Goto(decoder, ssa_env_, target->end_env);
} else {
MergeValuesInto(decoder, target);
}
} }
void BrIf(Decoder* decoder, const Value& cond, Control* target) { void BrIf(Decoder* decoder, const Value& cond, Control* target) {
...@@ -318,7 +312,7 @@ class WasmGraphBuildingInterface { ...@@ -318,7 +312,7 @@ class WasmGraphBuildingInterface {
fenv->SetNotMerged(); fenv->SetNotMerged();
BUILD(BranchNoHint, cond.node, &tenv->control, &fenv->control); BUILD(BranchNoHint, cond.node, &tenv->control, &fenv->control);
ssa_env_ = tenv; ssa_env_ = tenv;
BreakTo(decoder, target); Br(decoder, target);
ssa_env_ = fenv; ssa_env_ = fenv;
} }
...@@ -327,7 +321,7 @@ class WasmGraphBuildingInterface { ...@@ -327,7 +321,7 @@ class WasmGraphBuildingInterface {
if (operand.table_count == 0) { if (operand.table_count == 0) {
// Only a default target. Do the equivalent of br. // Only a default target. Do the equivalent of br.
uint32_t target = BranchTableIterator<validate>(decoder, operand).next(); uint32_t target = BranchTableIterator<validate>(decoder, operand).next();
BreakTo(decoder, decoder->control_at(target)); Br(decoder, decoder->control_at(target));
return; return;
} }
...@@ -344,7 +338,7 @@ class WasmGraphBuildingInterface { ...@@ -344,7 +338,7 @@ class WasmGraphBuildingInterface {
ssa_env_ = Split(decoder, copy); ssa_env_ = Split(decoder, copy);
ssa_env_->control = (i == operand.table_count) ? BUILD(IfDefault, sw) ssa_env_->control = (i == operand.table_count) ? BUILD(IfDefault, sw)
: BUILD(IfValue, i, sw); : BUILD(IfValue, i, sw);
BreakTo(decoder, decoder->control_at(target)); Br(decoder, decoder->control_at(target));
} }
DCHECK(decoder->ok()); DCHECK(decoder->ok());
ssa_env_ = break_env; ssa_env_ = break_env;
...@@ -618,7 +612,8 @@ class WasmGraphBuildingInterface { ...@@ -618,7 +612,8 @@ class WasmGraphBuildingInterface {
} }
} }
void MergeValuesInto(Decoder* decoder, Control* c) { void MergeValuesInto(Decoder* decoder, Control* c, Merge<Value>* merge) {
DCHECK(merge == &c->start_merge || merge == &c->end_merge);
if (!ssa_env_->go()) return; if (!ssa_env_->go()) return;
SsaEnv* target = c->end_env; SsaEnv* target = c->end_env;
...@@ -627,10 +622,10 @@ class WasmGraphBuildingInterface { ...@@ -627,10 +622,10 @@ class WasmGraphBuildingInterface {
uint32_t avail = uint32_t avail =
decoder->stack_size() - decoder->control_at(0)->stack_depth; decoder->stack_size() - decoder->control_at(0)->stack_depth;
uint32_t start = avail >= c->merge.arity ? 0 : c->merge.arity - avail; uint32_t start = avail >= merge->arity ? 0 : merge->arity - avail;
for (uint32_t i = start; i < c->merge.arity; ++i) { for (uint32_t i = start; i < merge->arity; ++i) {
auto& val = decoder->GetMergeValueFromStack(c, i); auto& val = decoder->GetMergeValueFromStack(c, merge, i);
auto& old = c->merge[i]; auto& old = (*merge)[i];
DCHECK_NOT_NULL(val.node); DCHECK_NOT_NULL(val.node);
DCHECK(val.type == old.type || val.type == kWasmVar); DCHECK(val.type == old.type || val.type == kWasmVar);
old.node = first ? val.node old.node = first ? val.node
......
...@@ -120,7 +120,7 @@ WASM_EXEC_TEST(Int32Add_P2) { ...@@ -120,7 +120,7 @@ WASM_EXEC_TEST(Int32Add_P2) {
WASM_EXEC_TEST(Int32Add_block1) { WASM_EXEC_TEST(Int32Add_block1) {
EXPERIMENTAL_FLAG_SCOPE(mv); EXPERIMENTAL_FLAG_SCOPE(mv);
static const byte code[] = { 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}; kExprI32Add};
RunInt32AddTest(execution_mode, code, sizeof(code)); RunInt32AddTest(execution_mode, code, sizeof(code));
} }
...@@ -128,7 +128,7 @@ WASM_EXEC_TEST(Int32Add_block1) { ...@@ -128,7 +128,7 @@ WASM_EXEC_TEST(Int32Add_block1) {
WASM_EXEC_TEST(Int32Add_block2) { WASM_EXEC_TEST(Int32Add_block2) {
EXPERIMENTAL_FLAG_SCOPE(mv); EXPERIMENTAL_FLAG_SCOPE(mv);
static const byte code[] = { 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}; kExprI32Add};
RunInt32AddTest(execution_mode, code, sizeof(code)); RunInt32AddTest(execution_mode, code, sizeof(code));
} }
...@@ -136,9 +136,9 @@ WASM_EXEC_TEST(Int32Add_block2) { ...@@ -136,9 +136,9 @@ WASM_EXEC_TEST(Int32Add_block2) {
WASM_EXEC_TEST(Int32Add_multi_if) { WASM_EXEC_TEST(Int32Add_multi_if) {
EXPERIMENTAL_FLAG_SCOPE(mv); EXPERIMENTAL_FLAG_SCOPE(mv);
static const byte code[] = { static const byte code[] = {
WASM_IF_ELSE_TT(0, 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(0), WASM_GET_LOCAL(1)),
WASM_SEQ(WASM_GET_LOCAL(1), WASM_GET_LOCAL(0))), WASM_SEQ(WASM_GET_LOCAL(1), WASM_GET_LOCAL(0))),
kExprI32Add}; kExprI32Add};
RunInt32AddTest(execution_mode, code, sizeof(code)); RunInt32AddTest(execution_mode, code, sizeof(code));
} }
......
...@@ -79,7 +79,7 @@ ...@@ -79,7 +79,7 @@
kExprBlock, static_cast<byte>(WasmOpcodes::ValueTypeCodeFor(t)), \ kExprBlock, static_cast<byte>(WasmOpcodes::ValueTypeCodeFor(t)), \
__VA_ARGS__, kExprEnd __VA_ARGS__, kExprEnd
#define WASM_BLOCK_TT(index, ...) \ #define WASM_BLOCK_X(index, ...) \
kExprBlock, static_cast<byte>(index), __VA_ARGS__, kExprEnd kExprBlock, static_cast<byte>(index), __VA_ARGS__, kExprEnd
#define WASM_INFINITE_LOOP kExprLoop, kLocalVoid, kExprBr, DEPTH_0, kExprEnd #define WASM_INFINITE_LOOP kExprLoop, kLocalVoid, kExprBr, DEPTH_0, kExprEnd
...@@ -94,20 +94,20 @@ ...@@ -94,20 +94,20 @@
kExprLoop, static_cast<byte>(WasmOpcodes::ValueTypeCodeFor(t)), \ kExprLoop, static_cast<byte>(WasmOpcodes::ValueTypeCodeFor(t)), \
__VA_ARGS__, kExprEnd __VA_ARGS__, kExprEnd
#define WASM_LOOP_TT(index, ...) \ #define WASM_LOOP_X(index, ...) \
kExprLoop, static_cast<byte>(index), __VA_ARGS__, kExprEnd kExprLoop, static_cast<byte>(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) \ #define WASM_IF_T(t, cond, ...) \
cond, kExprIf, kLocalVoid, tstmt, kExprElse, fstmt, kExprEnd cond, kExprIf, static_cast<byte>(WasmOpcodes::ValueTypeCodeFor(t)), \
__VA_ARGS__, kExprEnd
#define WASM_IF_ELSE_T(t, cond, tstmt, fstmt) \ #define WASM_IF_X(index, cond, ...) \
cond, kExprIf, static_cast<byte>(WasmOpcodes::ValueTypeCodeFor(t)), tstmt, \ cond, kExprIf, static_cast<byte>(index), __VA_ARGS__, kExprEnd
kExprElse, fstmt, kExprEnd
#define WASM_IF_ELSE_TT(index, cond, tstmt, fstmt) \ #define WASM_IF_ELSE(cond, tstmt, fstmt) \
cond, kExprIf, static_cast<byte>(index), tstmt, kExprElse, fstmt, kExprEnd cond, kExprIf, kLocalVoid, tstmt, kExprElse, fstmt, kExprEnd
#define WASM_IF_ELSE_I(cond, tstmt, fstmt) \ #define WASM_IF_ELSE_I(cond, tstmt, fstmt) \
cond, kExprIf, kLocalI32, tstmt, kExprElse, fstmt, kExprEnd cond, kExprIf, kLocalI32, tstmt, kExprElse, fstmt, kExprEnd
...@@ -118,6 +118,13 @@ ...@@ -118,6 +118,13 @@
#define WASM_IF_ELSE_D(cond, tstmt, fstmt) \ #define WASM_IF_ELSE_D(cond, tstmt, fstmt) \
cond, kExprIf, kLocalF64, tstmt, kExprElse, fstmt, kExprEnd cond, kExprIf, kLocalF64, tstmt, kExprElse, fstmt, kExprEnd
#define WASM_IF_ELSE_T(t, cond, tstmt, fstmt) \
cond, kExprIf, static_cast<byte>(WasmOpcodes::ValueTypeCodeFor(t)), tstmt, \
kExprElse, fstmt, kExprEnd
#define WASM_IF_ELSE_X(index, cond, tstmt, fstmt) \
cond, kExprIf, static_cast<byte>(index), tstmt, kExprElse, fstmt, kExprEnd
#define WASM_SELECT(tval, fval, cond) tval, fval, cond, kExprSelect #define WASM_SELECT(tval, fval, cond) tval, fval, cond, kExprSelect
#define WASM_RETURN0 kExprReturn #define WASM_RETURN0 kExprReturn
...@@ -574,7 +581,7 @@ inline WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) { ...@@ -574,7 +581,7 @@ inline WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) {
#define SIZEOF_SIG_ENTRY_x_xx 6 #define SIZEOF_SIG_ENTRY_x_xx 6
#define SIZEOF_SIG_ENTRY_x_xxx 7 #define SIZEOF_SIG_ENTRY_x_xxx 7
#define WASM_BRV(depth, val) val, kExprBr, static_cast<byte>(depth) #define WASM_BRV(depth, ...) __VA_ARGS__, kExprBr, static_cast<byte>(depth)
#define WASM_BRV_IF(depth, val, cond) \ #define WASM_BRV_IF(depth, val, cond) \
val, cond, kExprBrIf, static_cast<byte>(depth) val, cond, kExprBrIf, static_cast<byte>(depth)
#define WASM_BRV_IFD(depth, val, cond) \ #define WASM_BRV_IFD(depth, val, cond) \
......
// 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);
})();
...@@ -123,6 +123,10 @@ let kSig_v_l = makeSig([kWasmI64], []); ...@@ -123,6 +123,10 @@ let kSig_v_l = makeSig([kWasmI64], []);
let kSig_v_d = makeSig([kWasmF64], []); let kSig_v_d = makeSig([kWasmF64], []);
let kSig_v_dd = makeSig([kWasmF64, kWasmF64], []); let kSig_v_dd = makeSig([kWasmF64, kWasmF64], []);
let kSig_v_ddi = makeSig([kWasmF64, kWasmF64, kWasmI32], []); 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_v_f = makeSig([kWasmF32], []);
let kSig_f_f = makeSig([kWasmF32], [kWasmF32]); let kSig_f_f = makeSig([kWasmF32], [kWasmF32]);
......
...@@ -2390,14 +2390,14 @@ TEST_F(FunctionBodyDecoderTest, MultiValBlock1) { ...@@ -2390,14 +2390,14 @@ TEST_F(FunctionBodyDecoderTest, MultiValBlock1) {
TestModuleBuilder builder; TestModuleBuilder builder;
module = builder.module(); module = builder.module();
byte f0 = builder.AddSignature(sigs.ii_v()); 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); kExprI32Add);
EXPECT_FAILURE(i_ii, WASM_BLOCK_TT(f0, WASM_NOP), kExprI32Add); EXPECT_FAILURE(i_ii, WASM_BLOCK_X(f0, WASM_NOP), kExprI32Add);
EXPECT_FAILURE(i_ii, WASM_BLOCK_TT(f0, WASM_GET_LOCAL(0)), kExprI32Add); EXPECT_FAILURE(i_ii, WASM_BLOCK_X(f0, 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),
WASM_GET_LOCAL(0)), WASM_GET_LOCAL(0)),
kExprI32Add); 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); kExprF32Add);
} }
...@@ -2406,16 +2406,16 @@ TEST_F(FunctionBodyDecoderTest, MultiValBlock2) { ...@@ -2406,16 +2406,16 @@ TEST_F(FunctionBodyDecoderTest, MultiValBlock2) {
TestModuleBuilder builder; TestModuleBuilder builder;
module = builder.module(); module = builder.module();
byte f0 = builder.AddSignature(sigs.ii_v()); 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)); 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)); 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)); 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_GET_LOCAL(0)), WASM_GET_LOCAL(0)),
WASM_I32_ADD(WASM_NOP, WASM_NOP)); 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)); WASM_F32_ADD(WASM_NOP, WASM_NOP));
} }
...@@ -2424,10 +2424,10 @@ TEST_F(FunctionBodyDecoderTest, MultiValBlockBr) { ...@@ -2424,10 +2424,10 @@ TEST_F(FunctionBodyDecoderTest, MultiValBlockBr) {
TestModuleBuilder builder; TestModuleBuilder builder;
module = builder.module(); module = builder.module();
byte f0 = builder.AddSignature(sigs.ii_v()); byte f0 = builder.AddSignature(sigs.ii_v());
EXPECT_FAILURE( EXPECT_FAILURE(i_ii, WASM_BLOCK_X(f0, WASM_GET_LOCAL(0), WASM_BR(0)),
i_ii, WASM_BLOCK_TT(f0, WASM_GET_LOCAL(0), WASM_BR(0)), kExprI32Add); kExprI32Add);
EXPECT_VERIFIES(i_ii, WASM_BLOCK_TT(f0, WASM_GET_LOCAL(0), EXPECT_VERIFIES(i_ii, WASM_BLOCK_X(f0, WASM_GET_LOCAL(0),
WASM_GET_LOCAL(1), WASM_BR(0)), WASM_GET_LOCAL(1), WASM_BR(0)),
kExprI32Add); kExprI32Add);
} }
...@@ -2436,14 +2436,14 @@ TEST_F(FunctionBodyDecoderTest, MultiValLoop1) { ...@@ -2436,14 +2436,14 @@ TEST_F(FunctionBodyDecoderTest, MultiValLoop1) {
TestModuleBuilder builder; TestModuleBuilder builder;
module = builder.module(); module = builder.module();
byte f0 = builder.AddSignature(sigs.ii_v()); 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); kExprI32Add);
EXPECT_FAILURE(i_ii, WASM_LOOP_TT(f0, WASM_NOP), kExprI32Add); EXPECT_FAILURE(i_ii, WASM_LOOP_X(f0, WASM_NOP), kExprI32Add);
EXPECT_FAILURE(i_ii, WASM_LOOP_TT(f0, WASM_GET_LOCAL(0)), kExprI32Add); EXPECT_FAILURE(i_ii, WASM_LOOP_X(f0, 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),
WASM_GET_LOCAL(0)), WASM_GET_LOCAL(0)),
kExprI32Add); 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); kExprF32Add);
} }
...@@ -2453,63 +2453,163 @@ TEST_F(FunctionBodyDecoderTest, MultiValIf) { ...@@ -2453,63 +2453,163 @@ TEST_F(FunctionBodyDecoderTest, MultiValIf) {
module = builder.module(); module = builder.module();
byte f0 = builder.AddSignature(sigs.ii_v()); byte f0 = builder.AddSignature(sigs.ii_v());
EXPECT_VERIFIES( EXPECT_VERIFIES(
i_ii, WASM_IF_ELSE_TT(f0, 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(0), WASM_GET_LOCAL(1)),
WASM_SEQ(WASM_GET_LOCAL(1), WASM_GET_LOCAL(0))), WASM_SEQ(WASM_GET_LOCAL(1), WASM_GET_LOCAL(0))),
kExprI32Add); kExprI32Add);
EXPECT_FAILURE( 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); kExprI32Add);
EXPECT_FAILURE( EXPECT_FAILURE(
i_ii, WASM_IF_ELSE_TT(f0, WASM_GET_LOCAL(0), i_ii, WASM_IF_ELSE_X(f0, WASM_GET_LOCAL(0),
WASM_NOP, WASM_NOP,
WASM_SEQ(WASM_GET_LOCAL(1), WASM_GET_LOCAL(0))), WASM_SEQ(WASM_GET_LOCAL(1), WASM_GET_LOCAL(0))),
kExprI32Add); kExprI32Add);
EXPECT_FAILURE( EXPECT_FAILURE(
i_ii, WASM_IF_ELSE_TT(f0, 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(0), WASM_GET_LOCAL(1)),
WASM_NOP), WASM_NOP),
kExprI32Add); kExprI32Add);
EXPECT_FAILURE( EXPECT_FAILURE(
i_ii, WASM_IF_ELSE_TT(f0, WASM_GET_LOCAL(0), i_ii, WASM_IF_ELSE_X(f0, WASM_GET_LOCAL(0),
WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)), WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)),
kExprI32Add); kExprI32Add);
EXPECT_FAILURE( EXPECT_FAILURE(
i_ii, WASM_IF_ELSE_TT(f0, WASM_GET_LOCAL(0), i_ii, WASM_IF_ELSE_X(f0, WASM_GET_LOCAL(0),
WASM_GET_LOCAL(0), WASM_GET_LOCAL(0),
WASM_SEQ(WASM_GET_LOCAL(1), WASM_GET_LOCAL(0))), WASM_SEQ(WASM_GET_LOCAL(1), WASM_GET_LOCAL(0))),
kExprI32Add); kExprI32Add);
EXPECT_FAILURE( EXPECT_FAILURE(
i_ii, WASM_IF_ELSE_TT(f0, 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(0), WASM_GET_LOCAL(1)),
WASM_GET_LOCAL(1)), WASM_GET_LOCAL(1)),
kExprI32Add); kExprI32Add);
EXPECT_FAILURE( EXPECT_FAILURE(
i_ii, WASM_IF_ELSE_TT(f0, 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_SEQ(WASM_GET_LOCAL(0), WASM_GET_LOCAL(0),
WASM_GET_LOCAL(0)), WASM_GET_LOCAL(0)),
WASM_SEQ(WASM_GET_LOCAL(1), WASM_GET_LOCAL(0), WASM_SEQ(WASM_GET_LOCAL(1), WASM_GET_LOCAL(0),
WASM_GET_LOCAL(0))), WASM_GET_LOCAL(0))),
kExprI32Add); kExprI32Add);
EXPECT_FAILURE( EXPECT_FAILURE(
i_ii, WASM_IF_ELSE_TT(f0, 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_SEQ(WASM_GET_LOCAL(0), WASM_GET_LOCAL(0),
WASM_GET_LOCAL(0)), WASM_GET_LOCAL(0)),
WASM_SEQ(WASM_GET_LOCAL(1), WASM_GET_LOCAL(0))), WASM_SEQ(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1))),
kExprI32Add); kExprI32Add);
EXPECT_FAILURE( EXPECT_FAILURE(
i_ii, WASM_IF_ELSE_TT(f0, 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(0), WASM_GET_LOCAL(1)),
WASM_SEQ(WASM_GET_LOCAL(1), WASM_GET_LOCAL(1), WASM_SEQ(WASM_GET_LOCAL(1), WASM_GET_LOCAL(1),
WASM_GET_LOCAL(1))), WASM_GET_LOCAL(1))),
kExprI32Add); kExprI32Add);
EXPECT_FAILURE( EXPECT_FAILURE(
i_ii, WASM_IF_ELSE_TT(f0, 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(0), WASM_GET_LOCAL(1)),
WASM_SEQ(WASM_GET_LOCAL(1), WASM_GET_LOCAL(0))), WASM_SEQ(WASM_GET_LOCAL(1), WASM_GET_LOCAL(0))),
kExprF32Add); 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) { TEST_F(FunctionBodyDecoderTest, Regression709741) {
AddLocals(kWasmI32, kV8MaxWasmFunctionLocals - 1); AddLocals(kWasmI32, kV8MaxWasmFunctionLocals - 1);
EXPECT_VERIFIES(v_v, WASM_NOP); EXPECT_VERIFIES(v_v, WASM_NOP);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment