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

Only change over original: Init sig_index to 0 at
function-body-decoder-impl.h:168, to make MSAN happy on error path.

R=titzer@chromium.org

Change-Id: I9ac17215360523b656b10d2466201001b65992c0
Reviewed-on: https://chromium-review.googlesource.com/712655
Reviewed-by: Ben Titzer <titzer@chromium.org>
Commit-Queue: Andreas Rossberg <rossberg@chromium.org>
Cr-Commit-Position: refs/heads/master@{#48463}
This commit is contained in:
Andreas Rossberg 2017-10-11 15:01:17 +02:00 committed by Commit Bot
parent b0ced92695
commit 5d3dfc855d
9 changed files with 207 additions and 95 deletions

View File

@ -163,51 +163,31 @@ struct GlobalIndexOperand {
template <bool validate>
struct BlockTypeOperand {
uint32_t arity = 0;
const byte* types = nullptr; // pointer to encoded types for the block.
unsigned length = 1;
ValueType type = kWasmStmt;
uint32_t sig_index = 0;
FunctionSig* sig = nullptr;
inline BlockTypeOperand(Decoder* decoder, const byte* pc) {
uint8_t val = decoder->read_u8<validate>(pc + 1, "block type");
ValueType type = kWasmStmt;
if (decode_local_type(val, &type)) {
arity = type == kWasmStmt ? 0 : 1;
types = pc + 1;
} else {
if (!decode_local_type(val, &type)) {
// Handle multi-value blocks.
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");
return;
}
// Decode and check the types vector of the block.
unsigned len = 0;
uint32_t count =
decoder->read_u32v<validate>(pc + 2, &len, "block arity");
// {count} is encoded as {arity-2}, so that a {0} count here corresponds
// 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;
}
int32_t index =
decoder->read_i32v<validate>(pc + 1, &length, "block arity");
if (!VALIDATE(length > 0 && index >= 0)) {
decoder->error(pc + 1, "invalid block type index");
return;
}
sig_index = static_cast<uint32_t>(index);
}
}
// 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) {
switch (static_cast<ValueTypeCode>(val)) {
case kLocalVoid:
@ -229,18 +209,29 @@ struct BlockTypeOperand {
*result = kWasmS128;
return true;
default:
*result = kWasmStmt;
*result = kWasmVar;
return false;
}
}
ValueType read_entry(unsigned index) {
DCHECK_LT(index, arity);
ValueType result;
bool success = decode_local_type(types[index], &result);
DCHECK(success);
USE(success);
return result;
uint32_t in_arity() const {
if (type != kWasmVar) return 0;
return static_cast<uint32_t>(sig->parameter_count());
}
uint32_t out_arity() const {
if (type == kWasmStmt) return 0;
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> {
break;
case kExprBlock: {
BlockTypeOperand<validate> operand(this, this->pc_);
if (!LookupBlockType(&operand)) break;
auto* block = PushBlock();
SetBlockType(block, operand);
len = 1 + operand.length;
@ -1290,6 +1282,7 @@ class WasmFullDecoder : public WasmDecoder<validate> {
case kExprTry: {
CHECK_PROTOTYPE_OPCODE(eh);
BlockTypeOperand<validate> operand(this, this->pc_);
if (!LookupBlockType(&operand)) break;
auto* try_block = PushTry();
SetBlockType(try_block, operand);
len = 1 + operand.length;
@ -1339,6 +1332,7 @@ class WasmFullDecoder : public WasmDecoder<validate> {
}
case kExprLoop: {
BlockTypeOperand<validate> operand(this, this->pc_);
if (!LookupBlockType(&operand)) break;
// The continue environment is the inner environment.
auto* block = PushLoop();
SetBlockType(&control_.back(), operand);
@ -1349,6 +1343,7 @@ class WasmFullDecoder : public WasmDecoder<validate> {
case kExprIf: {
// Condition on top of stack. Split environments for branches.
BlockTypeOperand<validate> operand(this, this->pc_);
if (!LookupBlockType(&operand)) break;
auto cond = Pop(0, kWasmI32);
auto* if_block = PushIf();
SetBlockType(if_block, operand);
@ -1808,14 +1803,30 @@ class WasmFullDecoder : public WasmDecoder<validate> {
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) {
c->merge.arity = operand.arity;
c->merge.arity = operand.out_arity();
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) {
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.read_entry(i));
c->merge.vals.array[i] = Value::New(this->pc_, operand.out_type(i));
}
}
}

View File

@ -1018,8 +1018,8 @@ bool PrintRawWasmCode(AccountingAllocator* allocator, const FunctionBody& body,
case kExprTry: {
BlockTypeOperand<false> operand(&i, i.pc());
os << " // @" << i.pc_offset();
for (unsigned i = 0; i < operand.arity; i++) {
os << " " << WasmOpcodes::TypeName(operand.read_entry(i));
for (unsigned i = 0; i < operand.out_arity(); i++) {
os << " " << WasmOpcodes::TypeName(operand.out_type(i));
}
control_depth++;
break;

View File

@ -827,24 +827,34 @@ class SideTable : public ZoneObject {
case kExprLoop: {
bool is_loop = opcode == kExprLoop;
BlockTypeOperand<false> operand(&i, i.pc());
TRACE("control @%u: %s, arity %d\n", i.pc_offset(),
is_loop ? "Loop" : "Block", operand.arity);
if (operand.type == kWasmVar) {
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,
is_loop ? 0 : operand.arity);
control_stack.emplace_back(i.pc(), label, operand.arity);
is_loop ? operand.in_arity()
: operand.out_arity());
control_stack.emplace_back(i.pc(), label, operand.out_arity());
copy_unreachable();
if (is_loop) label->Bind(i.pc());
break;
}
case kExprIf: {
TRACE("control @%u: If\n", i.pc_offset());
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::New(&control_transfer_zone, stack_height, operand.arity);
CLabel::New(&control_transfer_zone, stack_height,
operand.out_arity());
CLabel* else_label =
CLabel::New(&control_transfer_zone, stack_height, 0);
control_stack.emplace_back(i.pc(), end_label, else_label,
operand.arity);
operand.out_arity());
copy_unreachable();
if (!unreachable) else_label->Ref(i.pc(), stack_height);
break;

View File

@ -28,9 +28,6 @@ enum ValueTypeCode {
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.
// A typedef improves readability without adding a whole new type system.
using ValueType = MachineRepresentation;

View File

@ -103,8 +103,10 @@ void PrintWasmText(const WasmModule* module, const ModuleWireBytes& wire_bytes,
case kExprTry: {
BlockTypeOperand<false> operand(&i, i.pc());
os << WasmOpcodes::OpcodeName(opcode);
for (unsigned i = 0; i < operand.arity; i++) {
os << " " << WasmOpcodes::TypeName(operand.read_entry(i));
if (operand.type == kWasmVar) {
os << " (type " << operand.sig_index << ")";
} else if (operand.out_arity() > 0) {
os << " " << WasmOpcodes::TypeName(operand.out_type(0));
}
control_depth++;
break;

View File

@ -96,7 +96,10 @@ WASM_EXEC_TEST(Int32Add_P_fallthru) {
static void RunInt32AddTest(WasmExecutionMode execution_mode, const byte* code,
size_t size) {
TestSignatures sigs;
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);
FOR_INT32_INPUTS(i) {
FOR_INT32_INPUTS(j) {
@ -117,7 +120,7 @@ WASM_EXEC_TEST(Int32Add_P2) {
WASM_EXEC_TEST(Int32Add_block1) {
EXPERIMENTAL_FLAG_SCOPE(mv);
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};
RunInt32AddTest(execution_mode, code, sizeof(code));
}
@ -125,8 +128,7 @@ WASM_EXEC_TEST(Int32Add_block1) {
WASM_EXEC_TEST(Int32Add_block2) {
EXPERIMENTAL_FLAG_SCOPE(mv);
static const byte code[] = {
WASM_BLOCK_TT(kWasmI32, kWasmI32, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1),
kExprBr, DEPTH_0),
WASM_BLOCK_TT(0, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1), kExprBr, DEPTH_0),
kExprI32Add};
RunInt32AddTest(execution_mode, code, sizeof(code));
}
@ -134,7 +136,7 @@ WASM_EXEC_TEST(Int32Add_block2) {
WASM_EXEC_TEST(Int32Add_multi_if) {
EXPERIMENTAL_FLAG_SCOPE(mv);
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(1), WASM_GET_LOCAL(0))),
kExprI32Add};

View File

@ -38,7 +38,9 @@ class TestSignatures {
sig_v_i(0, 1, kIntTypes4),
sig_v_ii(0, 2, 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....
for (int i = 0; i < 4; i++) kIntTypes4[i] = kWasmI32;
for (int i = 0; i < 4; i++) kLongTypes4[i] = kWasmI64;
@ -80,6 +82,9 @@ class TestSignatures {
FunctionSig* v_iii() { return &sig_v_iii; }
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::Builder builder(zone, ret == kWasmStmt ? 0 : 1, count);
if (ret != kWasmStmt) builder.AddReturn(ret);
@ -124,6 +129,9 @@ class TestSignatures {
FunctionSig sig_v_ii;
FunctionSig sig_v_iii;
FunctionSig sig_s_i;
FunctionSig sig_ii_v;
FunctionSig sig_iii_v;
};
} // namespace wasm
} // namespace internal

View File

@ -70,21 +70,17 @@
#define ARITY_2 2
#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, ...) \
kExprBlock, static_cast<byte>(WasmOpcodes::ValueTypeCodeFor(t)), \
__VA_ARGS__, kExprEnd
#define WASM_BLOCK_TT(t1, t2, ...) \
kExprBlock, kMultivalBlock, 0, \
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_BLOCK_TT(index, ...) \
kExprBlock, static_cast<byte>(index), __VA_ARGS__, kExprEnd
#define WASM_INFINITE_LOOP kExprLoop, kLocalVoid, kExprBr, DEPTH_0, kExprEnd
@ -94,6 +90,13 @@
#define WASM_LOOP_F(...) kExprLoop, kLocalF32, __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_ELSE(cond, tstmt, fstmt) \
@ -103,11 +106,8 @@
cond, kExprIf, static_cast<byte>(WasmOpcodes::ValueTypeCodeFor(t)), tstmt, \
kExprElse, fstmt, kExprEnd
#define WASM_IF_ELSE_TT(t1, t2, cond, tstmt, fstmt) \
cond, kExprIf, kMultivalBlock, 0, \
static_cast<byte>(WasmOpcodes::ValueTypeCodeFor(t1)), \
static_cast<byte>(WasmOpcodes::ValueTypeCodeFor(t2)), 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

View File

@ -2387,45 +2387,127 @@ TEST_F(FunctionBodyDecoderTest, TryCatch) {
TEST_F(FunctionBodyDecoderTest, MultiValBlock1) {
EXPERIMENTAL_FLAG_SCOPE(mv);
EXPECT_VERIFIES(i_ii, WASM_BLOCK_TT(kWasmI32, kWasmI32, WASM_GET_LOCAL(0),
WASM_GET_LOCAL(1)),
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)),
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) {
EXPERIMENTAL_FLAG_SCOPE(mv);
EXPECT_VERIFIES(i_ii, WASM_BLOCK_TT(kWasmI32, kWasmI32, WASM_GET_LOCAL(0),
WASM_GET_LOCAL(1)),
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)),
WASM_I32_ADD(WASM_NOP, WASM_NOP));
EXPECT_FAILURE(i_ii, WASM_BLOCK_TT(f0, WASM_NOP),
WASM_I32_ADD(WASM_NOP, WASM_NOP));
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, MultiValBlockBr1) {
TEST_F(FunctionBodyDecoderTest, MultiValBlockBr) {
EXPERIMENTAL_FLAG_SCOPE(mv);
TestModuleBuilder builder;
module = builder.module();
byte f0 = builder.AddSignature(sigs.ii_v());
EXPECT_FAILURE(
i_ii, WASM_BLOCK_TT(kWasmI32, kWasmI32, WASM_GET_LOCAL(0), WASM_BR(0)),
kExprI32Add);
EXPECT_VERIFIES(i_ii, WASM_BLOCK_TT(kWasmI32, kWasmI32, WASM_GET_LOCAL(0),
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)),
kExprI32Add);
}
TEST_F(FunctionBodyDecoderTest, MultiValIf1) {
TEST_F(FunctionBodyDecoderTest, MultiValLoop1) {
EXPERIMENTAL_FLAG_SCOPE(mv);
EXPECT_FAILURE(
i_ii, WASM_IF_ELSE_TT(kWasmI32, kWasmI32, WASM_GET_LOCAL(0),
WASM_SEQ(WASM_GET_LOCAL(0)),
WASM_SEQ(WASM_GET_LOCAL(1), WASM_GET_LOCAL(0))),
kExprI32Add);
EXPECT_FAILURE(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(1))),
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(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), WASM_GET_LOCAL(0))),
kExprI32Add);
EXPECT_FAILURE(
i_ii, WASM_IF_ELSE_TT(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))),
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),
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))),
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) {