[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: Ben Titzer <titzer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#48871}
This commit is contained in:
Andreas Rossberg 2017-10-24 12:47:09 +02:00 committed by Commit Bot
parent d75ecf1303
commit 06061b7ddf
8 changed files with 568 additions and 230 deletions

View File

@ -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);
}

View File

@ -81,11 +81,12 @@ struct WasmException;
V(I32AtomicStore8U, Uint8) \
V(I32AtomicStore16U, Uint16)
template <typename T>
Vector<T> vec2vec(std::vector<T>& vec) {
template <typename T, typename Allocator>
Vector<T> vec2vec(std::vector<T, Allocator>& vec) {
return Vector<T>(vec.data(), vec.size());
}
// Helpers for decoding different kinds of operands which follow bytecodes.
template <Decoder::ValidateFlag validate>
struct LocalIndexOperand {
@ -446,20 +447,18 @@ enum Reachability : uint8_t {
// An entry on the control stack (i.e. if, block, loop, or try).
template <typename Value>
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<Value> merge;
// Values merged into the start or end of this control construct.
Merge<Value> start_merge;
Merge<Value> 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<Value>* 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<Value> {
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<validate>& operand, const Value& key) \
F(Else, Control* if_block) \
@ -1128,6 +1131,7 @@ class WasmFullDecoder : public WasmDecoder<validate> {
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<validate> {
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<Value>* 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<validate> {
ZoneVector<ValueType> local_type_vec_; // types of local variables.
ZoneVector<Value> stack_; // stack of values.
ZoneVector<Control> control_; // stack of blocks, loops, and ifs.
ZoneVector<Value> args_; // parameters of current block or call
bool last_end_found_;
bool CheckHasMemory() {
@ -1276,17 +1283,10 @@ class WasmFullDecoder : public WasmDecoder<validate> {
// Set up initial function block.
{
auto* c = PushBlock();
c->merge.arity = static_cast<uint32_t>(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<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));
}
}
InitMerge(&c->end_merge,
static_cast<uint32_t>(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<validate> {
case kExprBlock: {
BlockTypeOperand<validate> 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<validate> {
ExceptionIndexOperand<Decoder::kValidate> operand(this, this->pc_);
len = 1 + operand.length;
if (!this->Validate(this->pc_, operand)) break;
std::vector<Value> 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<validate> {
CHECK_PROTOTYPE_OPCODE(eh);
BlockTypeOperand<validate> 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<validate> {
case kExprLoop: {
BlockTypeOperand<validate> 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<validate> 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<validate> {
}
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<validate> {
return;
}
Control* c = &control_.back();
if (c->is_loop()) {
// A loop just leaves the values on the stack.
TypeCheckFallThru(c);
PopControl(c);
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())) {
if (!VALIDATE(!c->is_incomplete_try())) {
this->error(this->pc_, "missing catch in try");
break;
}
if (c->is_onearmed_if()) {
// 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<validate> {
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<validate> {
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<validate> {
}
// 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<validate> {
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<validate> {
len = 1 + operand.length;
if (!this->Validate(this->pc_, operand)) break;
// TODO(clemensh): Better memory management.
std::vector<Value> 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<validate> {
len = 1 + operand.length;
if (!this->Validate(this->pc_, operand)) break;
auto index = Pop(0, kWasmI32);
// TODO(clemensh): Better memory management.
std::vector<Value> 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<validate> {
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<validate> {
return true;
}
void SetBlockType(Control* c, BlockTypeOperand<validate>& 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<Value>(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<typename func>
void InitMerge(Merge<Value>* 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<Value>(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<Value>* result) {
DCHECK(result->empty());
int count = static_cast<int>(sig->parameter_count());
result->resize(count);
void SetBlockType(Control* c, BlockTypeOperand<validate>& operand,
ZoneVector<Value>& 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<int>(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<validate> {
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<validate> {
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<validate> {
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<validate> {
this->error("invalid simd opcode");
break;
}
std::vector<Value> 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<validate> {
#undef CASE_ATOMIC_OP
default:
this->error("invalid atomic opcode");
break;
return 0;
}
// TODO(clemensh): Better memory management here.
std::vector<Value> args(sig->parameter_count());
MemoryAccessOperand<validate> operand(
this, this->pc_ + 1, ElementSizeLog2Of(memtype.representation()));
len += operand.length;
for (int i = static_cast<int>(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<validate> {
}
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());
std::vector<Value> 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<validate> {
return &stack_.back();
}
void PushEndValues(Control* c) {
void PushMergeValues(Control* c, Merge<Value>* 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<validate> {
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) {
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<Value>* 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<validate> {
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<uint32_t>(stack_.size()) - c->stack_depth;
// Fallthrus must match the arity of the control exactly.
@ -2259,16 +2262,12 @@ class WasmFullDecoder : public WasmDecoder<validate> {
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<uint32_t>(stack_.size()) - control_.back().stack_depth;
@ -2278,7 +2277,7 @@ class WasmFullDecoder : public WasmDecoder<validate> {
expected, startrel(c->pc), actual);
return false;
}
return TypeCheckMergeValues(c);
return TypeCheckMergeValues(c, c->br_merge());
}
inline bool InsertUnreachablesIfNecessary(uint32_t expected,

View File

@ -27,11 +27,6 @@ namespace wasm {
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
// 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<validate>(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<Value>* 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

View File

@ -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));
}

View File

@ -79,7 +79,7 @@
kExprBlock, static_cast<byte>(WasmOpcodes::ValueTypeCodeFor(t)), \
__VA_ARGS__, kExprEnd
#define WASM_BLOCK_TT(index, ...) \
#define WASM_BLOCK_X(index, ...) \
kExprBlock, static_cast<byte>(index), __VA_ARGS__, kExprEnd
#define WASM_INFINITE_LOOP kExprLoop, kLocalVoid, kExprBr, DEPTH_0, kExprEnd
@ -94,21 +94,21 @@
kExprLoop, static_cast<byte>(WasmOpcodes::ValueTypeCodeFor(t)), \
__VA_ARGS__, kExprEnd
#define WASM_LOOP_TT(index, ...) \
#define WASM_LOOP_X(index, ...) \
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_T(t, cond, ...) \
cond, kExprIf, static_cast<byte>(WasmOpcodes::ValueTypeCodeFor(t)), \
__VA_ARGS__, kExprEnd
#define WASM_IF_X(index, cond, ...) \
cond, kExprIf, static_cast<byte>(index), __VA_ARGS__, kExprEnd
#define WASM_IF_ELSE(cond, tstmt, fstmt) \
cond, kExprIf, kLocalVoid, 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_TT(index, cond, tstmt, fstmt) \
cond, kExprIf, static_cast<byte>(index), tstmt, kExprElse, fstmt, kExprEnd
#define WASM_IF_ELSE_I(cond, tstmt, fstmt) \
cond, kExprIf, kLocalI32, tstmt, kExprElse, fstmt, kExprEnd
#define WASM_IF_ELSE_L(cond, tstmt, fstmt) \
@ -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<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_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<byte>(depth)
#define WASM_BRV(depth, ...) __VA_ARGS__, kExprBr, static_cast<byte>(depth)
#define WASM_BRV_IF(depth, val, cond) \
val, cond, kExprBrIf, static_cast<byte>(depth)
#define WASM_BRV_IFD(depth, val, cond) \

View File

@ -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);
})();

View File

@ -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]);

View File

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