diff --git a/src/wasm/function-body-decoder-impl.h b/src/wasm/function-body-decoder-impl.h index de17401752..f9d60beabc 100644 --- a/src/wasm/function-body-decoder-impl.h +++ b/src/wasm/function-body-decoder-impl.h @@ -163,51 +163,31 @@ struct GlobalIndexOperand { template 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(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(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(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(pc + 1, &length, "block arity"); + if (!VALIDATE(length > 0 && index >= 0)) { + decoder->error(pc + 1, "invalid block type index"); + return; } + sig_index = static_cast(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(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(sig->parameter_count()); + } + uint32_t out_arity() const { + if (type == kWasmStmt) return 0; + if (type != kWasmVar) return 1; + return static_cast(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 { break; case kExprBlock: { BlockTypeOperand 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 { case kExprTry: { CHECK_PROTOTYPE_OPCODE(eh); BlockTypeOperand 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 { } case kExprLoop: { BlockTypeOperand 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 { case kExprIf: { // Condition on top of stack. Split environments for branches. BlockTypeOperand 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 { interface_.EndControl(this, current); } + bool LookupBlockType(BlockTypeOperand* 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(this->module_ + ? this->module_->signatures.size() : 0)); + return false; + } + operand->sig = this->module_->signatures[operand->sig_index]; + } + return true; + } + void SetBlockType(Control* c, BlockTypeOperand& 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(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)); } } } diff --git a/src/wasm/function-body-decoder.cc b/src/wasm/function-body-decoder.cc index bcd57fe616..0db4f8f019 100644 --- a/src/wasm/function-body-decoder.cc +++ b/src/wasm/function-body-decoder.cc @@ -1018,8 +1018,8 @@ bool PrintRawWasmCode(AccountingAllocator* allocator, const FunctionBody& body, case kExprTry: { BlockTypeOperand 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; diff --git a/src/wasm/wasm-interpreter.cc b/src/wasm/wasm-interpreter.cc index 4269e18c8f..f9e1595a1b 100644 --- a/src/wasm/wasm-interpreter.cc +++ b/src/wasm/wasm-interpreter.cc @@ -827,24 +827,34 @@ class SideTable : public ZoneObject { case kExprLoop: { bool is_loop = opcode == kExprLoop; BlockTypeOperand 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 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; diff --git a/src/wasm/wasm-opcodes.h b/src/wasm/wasm-opcodes.h index 2401e0446c..29168934a2 100644 --- a/src/wasm/wasm-opcodes.h +++ b/src/wasm/wasm-opcodes.h @@ -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; diff --git a/src/wasm/wasm-text.cc b/src/wasm/wasm-text.cc index e1fea08d31..a68cc10d71 100644 --- a/src/wasm/wasm-text.cc +++ b/src/wasm/wasm-text.cc @@ -103,8 +103,10 @@ void PrintWasmText(const WasmModule* module, const ModuleWireBytes& wire_bytes, case kExprTry: { BlockTypeOperand 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; diff --git a/test/cctest/wasm/test-run-wasm.cc b/test/cctest/wasm/test-run-wasm.cc index 3b27c78f60..9af1612b00 100644 --- a/test/cctest/wasm/test-run-wasm.cc +++ b/test/cctest/wasm/test-run-wasm.cc @@ -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 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}; diff --git a/test/common/wasm/test-signatures.h b/test/common/wasm/test-signatures.h index c0bc7a933f..9a0a3eac49 100644 --- a/test/common/wasm/test-signatures.h +++ b/test/common/wasm/test-signatures.h @@ -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 diff --git a/test/common/wasm/wasm-macro-gen.h b/test/common/wasm/wasm-macro-gen.h index 2d15bbc815..90a5454b08 100644 --- a/test/common/wasm/wasm-macro-gen.h +++ b/test/common/wasm/wasm-macro-gen.h @@ -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(WasmOpcodes::ValueTypeCodeFor(t)), \ __VA_ARGS__, kExprEnd -#define WASM_BLOCK_TT(t1, t2, ...) \ - kExprBlock, kMultivalBlock, 0, \ - static_cast(WasmOpcodes::ValueTypeCodeFor(t1)), \ - static_cast(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(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(WasmOpcodes::ValueTypeCodeFor(t)), \ + __VA_ARGS__, kExprEnd + +#define WASM_LOOP_TT(index, ...) \ + kExprLoop, static_cast(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(WasmOpcodes::ValueTypeCodeFor(t)), tstmt, \ kExprElse, fstmt, kExprEnd -#define WASM_IF_ELSE_TT(t1, t2, cond, tstmt, fstmt) \ - cond, kExprIf, kMultivalBlock, 0, \ - static_cast(WasmOpcodes::ValueTypeCodeFor(t1)), \ - static_cast(WasmOpcodes::ValueTypeCodeFor(t2)), tstmt, kExprElse, \ - fstmt, kExprEnd +#define WASM_IF_ELSE_TT(index, cond, tstmt, fstmt) \ + cond, kExprIf, static_cast(index), tstmt, kExprElse, fstmt, kExprEnd #define WASM_IF_ELSE_I(cond, tstmt, fstmt) \ cond, kExprIf, kLocalI32, tstmt, kExprElse, fstmt, kExprEnd diff --git a/test/unittests/wasm/function-body-decoder-unittest.cc b/test/unittests/wasm/function-body-decoder-unittest.cc index bda1073281..11210344ac 100644 --- a/test/unittests/wasm/function-body-decoder-unittest.cc +++ b/test/unittests/wasm/function-body-decoder-unittest.cc @@ -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) {