Commit e44fdc70 authored by Andreas Rossberg's avatar Andreas Rossberg Committed by Commit Bot

[wasm] [multival] Allow function types as block types

Changes the binary encoding of multi-return blocks to contain a function type index instead of a vector of value types.

Cf. https://github.com/WebAssembly/multi-value/blob/master/proposals/multi-value/Overview.md#binary-format

Bug: v8:6672
Change-Id: I506d9323bfd6dba1e7a24c8590bcf5a08b68c433
Reviewed-on: https://chromium-review.googlesource.com/599807Reviewed-by: 's avatarBen Titzer <titzer@chromium.org>
Commit-Queue: Andreas Rossberg <rossberg@chromium.org>
Cr-Commit-Position: refs/heads/master@{#48453}
parent 183eb36b
...@@ -163,51 +163,31 @@ struct GlobalIndexOperand { ...@@ -163,51 +163,31 @@ struct GlobalIndexOperand {
template <bool validate> template <bool validate>
struct BlockTypeOperand { struct BlockTypeOperand {
uint32_t arity = 0;
const byte* types = nullptr; // pointer to encoded types for the block.
unsigned length = 1; unsigned length = 1;
ValueType type = kWasmStmt;
uint32_t sig_index;
FunctionSig* sig = nullptr;
inline BlockTypeOperand(Decoder* decoder, const byte* pc) { inline BlockTypeOperand(Decoder* decoder, const byte* pc) {
uint8_t val = decoder->read_u8<validate>(pc + 1, "block type"); uint8_t val = decoder->read_u8<validate>(pc + 1, "block type");
ValueType type = kWasmStmt; if (!decode_local_type(val, &type)) {
if (decode_local_type(val, &type)) {
arity = type == kWasmStmt ? 0 : 1;
types = pc + 1;
} else {
// Handle multi-value blocks. // Handle multi-value blocks.
if (!VALIDATE(FLAG_experimental_wasm_mv)) { if (!VALIDATE(FLAG_experimental_wasm_mv)) {
decoder->error(pc + 1, "invalid block arity > 1");
return;
}
if (!VALIDATE(val == kMultivalBlock)) {
decoder->error(pc + 1, "invalid block type"); decoder->error(pc + 1, "invalid block type");
return; return;
} }
// Decode and check the types vector of the block. int32_t index =
unsigned len = 0; decoder->read_i32v<validate>(pc + 1, &length, "block arity");
uint32_t count = if (!VALIDATE(length > 0 && index >= 0)) {
decoder->read_u32v<validate>(pc + 2, &len, "block arity"); decoder->error(pc + 1, "invalid block type index");
// {count} is encoded as {arity-2}, so that a {0} count here corresponds return;
// to a block with 2 values. This makes invalid/redundant encodings
// impossible.
arity = count + 2;
length = 1 + len + arity;
types = pc + 1 + 1 + len;
for (uint32_t i = 0; i < arity; i++) {
uint32_t offset = 1 + 1 + len + i;
val = decoder->read_u8<validate>(pc + offset, "block type");
decode_local_type(val, &type);
if (!VALIDATE(type != kWasmStmt)) {
decoder->error(pc + offset, "invalid block type");
return;
}
} }
sig_index = static_cast<uint32_t>(index);
} }
} }
// Decode a byte representing a local type. Return {false} if the encoded // Decode a byte representing a local type. Return {false} if the encoded
// byte was invalid or {kMultivalBlock}. // byte was invalid or the start of a type index.
inline bool decode_local_type(uint8_t val, ValueType* result) { inline bool decode_local_type(uint8_t val, ValueType* result) {
switch (static_cast<ValueTypeCode>(val)) { switch (static_cast<ValueTypeCode>(val)) {
case kLocalVoid: case kLocalVoid:
...@@ -229,18 +209,29 @@ struct BlockTypeOperand { ...@@ -229,18 +209,29 @@ struct BlockTypeOperand {
*result = kWasmS128; *result = kWasmS128;
return true; return true;
default: default:
*result = kWasmStmt; *result = kWasmVar;
return false; return false;
} }
} }
ValueType read_entry(unsigned index) { uint32_t in_arity() const {
DCHECK_LT(index, arity); if (type != kWasmVar) return 0;
ValueType result; return static_cast<uint32_t>(sig->parameter_count());
bool success = decode_local_type(types[index], &result); }
DCHECK(success); uint32_t out_arity() const {
USE(success); if (type == kWasmStmt) return 0;
return result; if (type != kWasmVar) return 1;
return static_cast<uint32_t>(sig->return_count());
}
ValueType in_type(uint32_t index) {
DCHECK_EQ(kWasmVar, type);
return sig->GetParam(index);
}
ValueType out_type(uint32_t index) {
if (type == kWasmVar) return sig->GetReturn(index);
DCHECK_NE(kWasmStmt, type);
DCHECK_EQ(0, index);
return type;
} }
}; };
...@@ -1265,6 +1256,7 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -1265,6 +1256,7 @@ class WasmFullDecoder : public WasmDecoder<validate> {
break; break;
case kExprBlock: { case kExprBlock: {
BlockTypeOperand<validate> operand(this, this->pc_); BlockTypeOperand<validate> operand(this, this->pc_);
if (!LookupBlockType(&operand)) break;
auto* block = PushBlock(); auto* block = PushBlock();
SetBlockType(block, operand); SetBlockType(block, operand);
len = 1 + operand.length; len = 1 + operand.length;
...@@ -1290,6 +1282,7 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -1290,6 +1282,7 @@ class WasmFullDecoder : public WasmDecoder<validate> {
case kExprTry: { case kExprTry: {
CHECK_PROTOTYPE_OPCODE(eh); CHECK_PROTOTYPE_OPCODE(eh);
BlockTypeOperand<validate> operand(this, this->pc_); BlockTypeOperand<validate> operand(this, this->pc_);
if (!LookupBlockType(&operand)) break;
auto* try_block = PushTry(); auto* try_block = PushTry();
SetBlockType(try_block, operand); SetBlockType(try_block, operand);
len = 1 + operand.length; len = 1 + operand.length;
...@@ -1339,6 +1332,7 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -1339,6 +1332,7 @@ class WasmFullDecoder : public WasmDecoder<validate> {
} }
case kExprLoop: { case kExprLoop: {
BlockTypeOperand<validate> operand(this, this->pc_); BlockTypeOperand<validate> operand(this, this->pc_);
if (!LookupBlockType(&operand)) break;
// The continue environment is the inner environment. // The continue environment is the inner environment.
auto* block = PushLoop(); auto* block = PushLoop();
SetBlockType(&control_.back(), operand); SetBlockType(&control_.back(), operand);
...@@ -1349,6 +1343,7 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -1349,6 +1343,7 @@ class WasmFullDecoder : public WasmDecoder<validate> {
case kExprIf: { case kExprIf: {
// Condition on top of stack. Split environments for branches. // Condition on top of stack. Split environments for branches.
BlockTypeOperand<validate> operand(this, this->pc_); BlockTypeOperand<validate> operand(this, this->pc_);
if (!LookupBlockType(&operand)) break;
auto cond = Pop(0, kWasmI32); auto cond = Pop(0, kWasmI32);
auto* if_block = PushIf(); auto* if_block = PushIf();
SetBlockType(if_block, operand); SetBlockType(if_block, operand);
...@@ -1808,14 +1803,30 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -1808,14 +1803,30 @@ class WasmFullDecoder : public WasmDecoder<validate> {
interface_.EndControl(this, current); interface_.EndControl(this, current);
} }
bool LookupBlockType(BlockTypeOperand<validate>* operand) {
if (operand->type == kWasmVar) {
if (!VALIDATE(this->module_ &&
operand->sig_index < this->module_->signatures.size())) {
this->errorf(
this->pc_, "block type index %u out of bounds (%d signatures)",
operand->sig_index,
static_cast<int>(this->module_
? this->module_->signatures.size() : 0));
return false;
}
operand->sig = this->module_->signatures[operand->sig_index];
}
return true;
}
void SetBlockType(Control* c, BlockTypeOperand<validate>& operand) { void SetBlockType(Control* c, BlockTypeOperand<validate>& operand) {
c->merge.arity = operand.arity; c->merge.arity = operand.out_arity();
if (c->merge.arity == 1) { if (c->merge.arity == 1) {
c->merge.vals.first = Value::New(this->pc_, operand.read_entry(0)); c->merge.vals.first = Value::New(this->pc_, operand.out_type(0));
} else if (c->merge.arity > 1) { } else if (c->merge.arity > 1) {
c->merge.vals.array = zone_->NewArray<Value>(c->merge.arity); c->merge.vals.array = zone_->NewArray<Value>(c->merge.arity);
for (unsigned i = 0; i < c->merge.arity; i++) { for (unsigned i = 0; i < c->merge.arity; i++) {
c->merge.vals.array[i] = Value::New(this->pc_, operand.read_entry(i)); c->merge.vals.array[i] = Value::New(this->pc_, operand.out_type(i));
} }
} }
} }
......
...@@ -1018,8 +1018,8 @@ bool PrintRawWasmCode(AccountingAllocator* allocator, const FunctionBody& body, ...@@ -1018,8 +1018,8 @@ bool PrintRawWasmCode(AccountingAllocator* allocator, const FunctionBody& body,
case kExprTry: { case kExprTry: {
BlockTypeOperand<false> operand(&i, i.pc()); BlockTypeOperand<false> operand(&i, i.pc());
os << " // @" << i.pc_offset(); os << " // @" << i.pc_offset();
for (unsigned i = 0; i < operand.arity; i++) { for (unsigned i = 0; i < operand.out_arity(); i++) {
os << " " << WasmOpcodes::TypeName(operand.read_entry(i)); os << " " << WasmOpcodes::TypeName(operand.out_type(i));
} }
control_depth++; control_depth++;
break; break;
......
...@@ -827,24 +827,34 @@ class SideTable : public ZoneObject { ...@@ -827,24 +827,34 @@ class SideTable : public ZoneObject {
case kExprLoop: { case kExprLoop: {
bool is_loop = opcode == kExprLoop; bool is_loop = opcode == kExprLoop;
BlockTypeOperand<false> operand(&i, i.pc()); BlockTypeOperand<false> operand(&i, i.pc());
TRACE("control @%u: %s, arity %d\n", i.pc_offset(), if (operand.type == kWasmVar) {
is_loop ? "Loop" : "Block", operand.arity); operand.sig = module->signatures[operand.sig_index];
}
TRACE("control @%u: %s, arity %d->%d\n", i.pc_offset(),
is_loop ? "Loop" : "Block",
operand.in_arity(), operand.out_arity());
CLabel* label = CLabel::New(&control_transfer_zone, stack_height, CLabel* label = CLabel::New(&control_transfer_zone, stack_height,
is_loop ? 0 : operand.arity); is_loop ? operand.in_arity()
control_stack.emplace_back(i.pc(), label, operand.arity); : operand.out_arity());
control_stack.emplace_back(i.pc(), label, operand.out_arity());
copy_unreachable(); copy_unreachable();
if (is_loop) label->Bind(i.pc()); if (is_loop) label->Bind(i.pc());
break; break;
} }
case kExprIf: { case kExprIf: {
TRACE("control @%u: If\n", i.pc_offset());
BlockTypeOperand<false> operand(&i, i.pc()); BlockTypeOperand<false> operand(&i, i.pc());
if (operand.type == kWasmVar) {
operand.sig = module->signatures[operand.sig_index];
}
TRACE("control @%u: If, arity %d->%d\n", i.pc_offset(),
operand.in_arity(), operand.out_arity());
CLabel* end_label = CLabel* end_label =
CLabel::New(&control_transfer_zone, stack_height, operand.arity); CLabel::New(&control_transfer_zone, stack_height,
operand.out_arity());
CLabel* else_label = CLabel* else_label =
CLabel::New(&control_transfer_zone, stack_height, 0); CLabel::New(&control_transfer_zone, stack_height, 0);
control_stack.emplace_back(i.pc(), end_label, else_label, control_stack.emplace_back(i.pc(), end_label, else_label,
operand.arity); operand.out_arity());
copy_unreachable(); copy_unreachable();
if (!unreachable) else_label->Ref(i.pc(), stack_height); if (!unreachable) else_label->Ref(i.pc(), stack_height);
break; break;
......
...@@ -28,9 +28,6 @@ enum ValueTypeCode { ...@@ -28,9 +28,6 @@ enum ValueTypeCode {
kLocalS128 = 0x7b kLocalS128 = 0x7b
}; };
// Type code for multi-value block types.
static const uint8_t kMultivalBlock = 0x41;
// We reuse the internal machine type to represent WebAssembly types. // We reuse the internal machine type to represent WebAssembly types.
// A typedef improves readability without adding a whole new type system. // A typedef improves readability without adding a whole new type system.
using ValueType = MachineRepresentation; using ValueType = MachineRepresentation;
......
...@@ -103,8 +103,10 @@ void PrintWasmText(const WasmModule* module, const ModuleWireBytes& wire_bytes, ...@@ -103,8 +103,10 @@ void PrintWasmText(const WasmModule* module, const ModuleWireBytes& wire_bytes,
case kExprTry: { case kExprTry: {
BlockTypeOperand<false> operand(&i, i.pc()); BlockTypeOperand<false> operand(&i, i.pc());
os << WasmOpcodes::OpcodeName(opcode); os << WasmOpcodes::OpcodeName(opcode);
for (unsigned i = 0; i < operand.arity; i++) { if (operand.type == kWasmVar) {
os << " " << WasmOpcodes::TypeName(operand.read_entry(i)); os << " (type " << operand.sig_index << ")";
} else if (operand.out_arity() > 0) {
os << " " << WasmOpcodes::TypeName(operand.out_type(0));
} }
control_depth++; control_depth++;
break; break;
......
...@@ -96,7 +96,10 @@ WASM_EXEC_TEST(Int32Add_P_fallthru) { ...@@ -96,7 +96,10 @@ WASM_EXEC_TEST(Int32Add_P_fallthru) {
static void RunInt32AddTest(WasmExecutionMode execution_mode, const byte* code, static void RunInt32AddTest(WasmExecutionMode execution_mode, const byte* code,
size_t size) { size_t size) {
TestSignatures sigs;
WasmRunner<int32_t, int32_t, int32_t> r(execution_mode); WasmRunner<int32_t, int32_t, int32_t> r(execution_mode);
r.builder().AddSignature(sigs.ii_v());
r.builder().AddSignature(sigs.iii_v());
r.Build(code, code + size); r.Build(code, code + size);
FOR_INT32_INPUTS(i) { FOR_INT32_INPUTS(i) {
FOR_INT32_INPUTS(j) { FOR_INT32_INPUTS(j) {
...@@ -117,7 +120,7 @@ WASM_EXEC_TEST(Int32Add_P2) { ...@@ -117,7 +120,7 @@ WASM_EXEC_TEST(Int32Add_P2) {
WASM_EXEC_TEST(Int32Add_block1) { WASM_EXEC_TEST(Int32Add_block1) {
EXPERIMENTAL_FLAG_SCOPE(mv); EXPERIMENTAL_FLAG_SCOPE(mv);
static const byte code[] = { static const byte code[] = {
WASM_BLOCK_TT(kWasmI32, kWasmI32, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)), WASM_BLOCK_TT(0, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)),
kExprI32Add}; kExprI32Add};
RunInt32AddTest(execution_mode, code, sizeof(code)); RunInt32AddTest(execution_mode, code, sizeof(code));
} }
...@@ -125,8 +128,7 @@ WASM_EXEC_TEST(Int32Add_block1) { ...@@ -125,8 +128,7 @@ WASM_EXEC_TEST(Int32Add_block1) {
WASM_EXEC_TEST(Int32Add_block2) { WASM_EXEC_TEST(Int32Add_block2) {
EXPERIMENTAL_FLAG_SCOPE(mv); EXPERIMENTAL_FLAG_SCOPE(mv);
static const byte code[] = { static const byte code[] = {
WASM_BLOCK_TT(kWasmI32, kWasmI32, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1), WASM_BLOCK_TT(0, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1), kExprBr, DEPTH_0),
kExprBr, DEPTH_0),
kExprI32Add}; kExprI32Add};
RunInt32AddTest(execution_mode, code, sizeof(code)); RunInt32AddTest(execution_mode, code, sizeof(code));
} }
...@@ -134,7 +136,7 @@ WASM_EXEC_TEST(Int32Add_block2) { ...@@ -134,7 +136,7 @@ WASM_EXEC_TEST(Int32Add_block2) {
WASM_EXEC_TEST(Int32Add_multi_if) { WASM_EXEC_TEST(Int32Add_multi_if) {
EXPERIMENTAL_FLAG_SCOPE(mv); EXPERIMENTAL_FLAG_SCOPE(mv);
static const byte code[] = { static const byte code[] = {
WASM_IF_ELSE_TT(kWasmI32, kWasmI32, WASM_GET_LOCAL(0), WASM_IF_ELSE_TT(0, WASM_GET_LOCAL(0),
WASM_SEQ(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)), WASM_SEQ(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)),
WASM_SEQ(WASM_GET_LOCAL(1), WASM_GET_LOCAL(0))), WASM_SEQ(WASM_GET_LOCAL(1), WASM_GET_LOCAL(0))),
kExprI32Add}; kExprI32Add};
......
...@@ -38,7 +38,9 @@ class TestSignatures { ...@@ -38,7 +38,9 @@ class TestSignatures {
sig_v_i(0, 1, kIntTypes4), sig_v_i(0, 1, kIntTypes4),
sig_v_ii(0, 2, kIntTypes4), sig_v_ii(0, 2, kIntTypes4),
sig_v_iii(0, 3, kIntTypes4), sig_v_iii(0, 3, kIntTypes4),
sig_s_i(1, 1, kSimd128IntTypes4) { sig_s_i(1, 1, kSimd128IntTypes4),
sig_ii_v(2, 0, kIntTypes4),
sig_iii_v(3, 0, kIntTypes4) {
// I used C++ and you won't believe what happened next.... // I used C++ and you won't believe what happened next....
for (int i = 0; i < 4; i++) kIntTypes4[i] = kWasmI32; for (int i = 0; i < 4; i++) kIntTypes4[i] = kWasmI32;
for (int i = 0; i < 4; i++) kLongTypes4[i] = kWasmI64; for (int i = 0; i < 4; i++) kLongTypes4[i] = kWasmI64;
...@@ -80,6 +82,9 @@ class TestSignatures { ...@@ -80,6 +82,9 @@ class TestSignatures {
FunctionSig* v_iii() { return &sig_v_iii; } FunctionSig* v_iii() { return &sig_v_iii; }
FunctionSig* s_i() { return &sig_s_i; } FunctionSig* s_i() { return &sig_s_i; }
FunctionSig* ii_v() { return &sig_ii_v; }
FunctionSig* iii_v() { return &sig_iii_v; }
FunctionSig* many(Zone* zone, ValueType ret, ValueType param, int count) { FunctionSig* many(Zone* zone, ValueType ret, ValueType param, int count) {
FunctionSig::Builder builder(zone, ret == kWasmStmt ? 0 : 1, count); FunctionSig::Builder builder(zone, ret == kWasmStmt ? 0 : 1, count);
if (ret != kWasmStmt) builder.AddReturn(ret); if (ret != kWasmStmt) builder.AddReturn(ret);
...@@ -124,6 +129,9 @@ class TestSignatures { ...@@ -124,6 +129,9 @@ class TestSignatures {
FunctionSig sig_v_ii; FunctionSig sig_v_ii;
FunctionSig sig_v_iii; FunctionSig sig_v_iii;
FunctionSig sig_s_i; FunctionSig sig_s_i;
FunctionSig sig_ii_v;
FunctionSig sig_iii_v;
}; };
} // namespace wasm } // namespace wasm
} // namespace internal } // namespace internal
......
...@@ -70,21 +70,17 @@ ...@@ -70,21 +70,17 @@
#define ARITY_2 2 #define ARITY_2 2
#define WASM_BLOCK(...) kExprBlock, kLocalVoid, __VA_ARGS__, kExprEnd #define WASM_BLOCK(...) kExprBlock, kLocalVoid, __VA_ARGS__, kExprEnd
#define WASM_BLOCK_I(...) kExprBlock, kLocalI32, __VA_ARGS__, kExprEnd
#define WASM_BLOCK_L(...) kExprBlock, kLocalI64, __VA_ARGS__, kExprEnd
#define WASM_BLOCK_F(...) kExprBlock, kLocalF32, __VA_ARGS__, kExprEnd
#define WASM_BLOCK_D(...) kExprBlock, kLocalF64, __VA_ARGS__, kExprEnd
#define WASM_BLOCK_T(t, ...) \ #define WASM_BLOCK_T(t, ...) \
kExprBlock, static_cast<byte>(WasmOpcodes::ValueTypeCodeFor(t)), \ kExprBlock, static_cast<byte>(WasmOpcodes::ValueTypeCodeFor(t)), \
__VA_ARGS__, kExprEnd __VA_ARGS__, kExprEnd
#define WASM_BLOCK_TT(t1, t2, ...) \ #define WASM_BLOCK_TT(index, ...) \
kExprBlock, kMultivalBlock, 0, \ kExprBlock, static_cast<byte>(index), __VA_ARGS__, kExprEnd
static_cast<byte>(WasmOpcodes::ValueTypeCodeFor(t1)), \
static_cast<byte>(WasmOpcodes::ValueTypeCodeFor(t2)), __VA_ARGS__, \
kExprEnd
#define WASM_BLOCK_I(...) kExprBlock, kLocalI32, __VA_ARGS__, kExprEnd
#define WASM_BLOCK_L(...) kExprBlock, kLocalI64, __VA_ARGS__, kExprEnd
#define WASM_BLOCK_F(...) kExprBlock, kLocalF32, __VA_ARGS__, kExprEnd
#define WASM_BLOCK_D(...) kExprBlock, kLocalF64, __VA_ARGS__, kExprEnd
#define WASM_INFINITE_LOOP kExprLoop, kLocalVoid, kExprBr, DEPTH_0, kExprEnd #define WASM_INFINITE_LOOP kExprLoop, kLocalVoid, kExprBr, DEPTH_0, kExprEnd
...@@ -94,6 +90,13 @@ ...@@ -94,6 +90,13 @@
#define WASM_LOOP_F(...) kExprLoop, kLocalF32, __VA_ARGS__, kExprEnd #define WASM_LOOP_F(...) kExprLoop, kLocalF32, __VA_ARGS__, kExprEnd
#define WASM_LOOP_D(...) kExprLoop, kLocalF64, __VA_ARGS__, kExprEnd #define WASM_LOOP_D(...) kExprLoop, kLocalF64, __VA_ARGS__, kExprEnd
#define WASM_LOOP_T(t, ...) \
kExprLoop, static_cast<byte>(WasmOpcodes::ValueTypeCodeFor(t)), \
__VA_ARGS__, kExprEnd
#define WASM_LOOP_TT(index, ...) \
kExprLoop, static_cast<byte>(index), __VA_ARGS__, kExprEnd
#define WASM_IF(cond, tstmt) cond, kExprIf, kLocalVoid, tstmt, kExprEnd #define WASM_IF(cond, tstmt) cond, kExprIf, kLocalVoid, tstmt, kExprEnd
#define WASM_IF_ELSE(cond, tstmt, fstmt) \ #define WASM_IF_ELSE(cond, tstmt, fstmt) \
...@@ -103,11 +106,8 @@ ...@@ -103,11 +106,8 @@
cond, kExprIf, static_cast<byte>(WasmOpcodes::ValueTypeCodeFor(t)), tstmt, \ cond, kExprIf, static_cast<byte>(WasmOpcodes::ValueTypeCodeFor(t)), tstmt, \
kExprElse, fstmt, kExprEnd kExprElse, fstmt, kExprEnd
#define WASM_IF_ELSE_TT(t1, t2, cond, tstmt, fstmt) \ #define WASM_IF_ELSE_TT(index, cond, tstmt, fstmt) \
cond, kExprIf, kMultivalBlock, 0, \ cond, kExprIf, static_cast<byte>(index), tstmt, kExprElse, fstmt, kExprEnd
static_cast<byte>(WasmOpcodes::ValueTypeCodeFor(t1)), \
static_cast<byte>(WasmOpcodes::ValueTypeCodeFor(t2)), tstmt, kExprElse, \
fstmt, kExprEnd
#define WASM_IF_ELSE_I(cond, tstmt, fstmt) \ #define WASM_IF_ELSE_I(cond, tstmt, fstmt) \
cond, kExprIf, kLocalI32, tstmt, kExprElse, fstmt, kExprEnd cond, kExprIf, kLocalI32, tstmt, kExprElse, fstmt, kExprEnd
......
...@@ -2387,45 +2387,127 @@ TEST_F(FunctionBodyDecoderTest, TryCatch) { ...@@ -2387,45 +2387,127 @@ TEST_F(FunctionBodyDecoderTest, TryCatch) {
TEST_F(FunctionBodyDecoderTest, MultiValBlock1) { TEST_F(FunctionBodyDecoderTest, MultiValBlock1) {
EXPERIMENTAL_FLAG_SCOPE(mv); EXPERIMENTAL_FLAG_SCOPE(mv);
EXPECT_VERIFIES(i_ii, WASM_BLOCK_TT(kWasmI32, kWasmI32, WASM_GET_LOCAL(0), TestModuleBuilder builder;
WASM_GET_LOCAL(1)), 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)),
kExprI32Add); 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)),
kExprI32Add);
EXPECT_FAILURE(i_ii, WASM_BLOCK_TT(f0, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)),
kExprF32Add);
} }
TEST_F(FunctionBodyDecoderTest, MultiValBlock2) { TEST_F(FunctionBodyDecoderTest, MultiValBlock2) {
EXPERIMENTAL_FLAG_SCOPE(mv); EXPERIMENTAL_FLAG_SCOPE(mv);
EXPECT_VERIFIES(i_ii, WASM_BLOCK_TT(kWasmI32, kWasmI32, WASM_GET_LOCAL(0), TestModuleBuilder builder;
WASM_GET_LOCAL(1)), 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)),
WASM_I32_ADD(WASM_NOP, WASM_NOP)); WASM_I32_ADD(WASM_NOP, WASM_NOP));
} EXPECT_FAILURE(i_ii, WASM_BLOCK_TT(f0, WASM_NOP),
WASM_I32_ADD(WASM_NOP, WASM_NOP));
TEST_F(FunctionBodyDecoderTest, MultiValBlockBr1) { EXPECT_FAILURE(i_ii, WASM_BLOCK_TT(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)),
WASM_I32_ADD(WASM_NOP, WASM_NOP));
EXPECT_FAILURE(i_ii, WASM_BLOCK_TT(f0, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)),
WASM_F32_ADD(WASM_NOP, WASM_NOP));
}
TEST_F(FunctionBodyDecoderTest, MultiValBlockBr) {
EXPERIMENTAL_FLAG_SCOPE(mv); EXPERIMENTAL_FLAG_SCOPE(mv);
TestModuleBuilder builder;
module = builder.module();
byte f0 = builder.AddSignature(sigs.ii_v());
EXPECT_FAILURE( EXPECT_FAILURE(
i_ii, WASM_BLOCK_TT(kWasmI32, kWasmI32, WASM_GET_LOCAL(0), WASM_BR(0)), i_ii, WASM_BLOCK_TT(f0, WASM_GET_LOCAL(0), WASM_BR(0)), kExprI32Add);
kExprI32Add); EXPECT_VERIFIES(i_ii, WASM_BLOCK_TT(f0, WASM_GET_LOCAL(0),
EXPECT_VERIFIES(i_ii, WASM_BLOCK_TT(kWasmI32, kWasmI32, WASM_GET_LOCAL(0),
WASM_GET_LOCAL(1), WASM_BR(0)), WASM_GET_LOCAL(1), WASM_BR(0)),
kExprI32Add); kExprI32Add);
} }
TEST_F(FunctionBodyDecoderTest, MultiValIf1) { TEST_F(FunctionBodyDecoderTest, MultiValLoop1) {
EXPERIMENTAL_FLAG_SCOPE(mv); EXPERIMENTAL_FLAG_SCOPE(mv);
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)),
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)),
kExprI32Add);
EXPECT_FAILURE(i_ii, WASM_LOOP_TT(f0, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)),
kExprF32Add);
}
TEST_F(FunctionBodyDecoderTest, MultiValIf) {
EXPERIMENTAL_FLAG_SCOPE(mv);
TestModuleBuilder builder;
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))),
kExprI32Add);
EXPECT_FAILURE( EXPECT_FAILURE(
i_ii, WASM_IF_ELSE_TT(kWasmI32, kWasmI32, WASM_GET_LOCAL(0), i_ii, WASM_IF_ELSE_TT(f0, WASM_GET_LOCAL(0), WASM_NOP, WASM_NOP),
WASM_SEQ(WASM_GET_LOCAL(0)), 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))), WASM_SEQ(WASM_GET_LOCAL(1), WASM_GET_LOCAL(0))),
kExprI32Add); kExprI32Add);
EXPECT_FAILURE(i_ii, EXPECT_FAILURE(
WASM_IF_ELSE_TT(kWasmI32, kWasmI32, WASM_GET_LOCAL(0), 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))),
kExprI32Add);
EXPECT_VERIFIES(
i_ii, WASM_IF_ELSE_TT(kWasmI32, kWasmI32, WASM_GET_LOCAL(0),
WASM_SEQ(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)), WASM_SEQ(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)),
WASM_NOP),
kExprI32Add);
EXPECT_FAILURE(
i_ii, WASM_IF_ELSE_TT(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))), WASM_SEQ(WASM_GET_LOCAL(1), WASM_GET_LOCAL(0))),
kExprI32Add); 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)),
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))),
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))),
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))),
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))),
kExprF32Add);
} }
TEST_F(FunctionBodyDecoderTest, Regression709741) { TEST_F(FunctionBodyDecoderTest, Regression709741) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment