[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:
parent
d75ecf1303
commit
06061b7ddf
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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) \
|
||||
|
233
test/mjsunit/wasm/multi-value.js
Normal file
233
test/mjsunit/wasm/multi-value.js
Normal 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);
|
||||
})();
|
@ -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]);
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user