140271f269
For "else" and "catch" statements, the Ref to the end label should only be added if the current block is unreachable, not the parent block. In the added regression test, the "true" block ends in an unreachable state with a stack height less than the target height of the end label. This is valid due to the semantics of unreachable code, but we should not add the Ref in this case because its stack height is invalid. R=clemensb@chromium.org Fixed: chromium:1092130 Change-Id: Iebaf5e7d6516278ccd3c8268ac331069e109d882 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2412181 Commit-Queue: Thibaud Michaud <thibaudm@chromium.org> Reviewed-by: Clemens Backes <clemensb@chromium.org> Cr-Commit-Position: refs/heads/master@{#69934}
523 lines
13 KiB
C++
523 lines
13 KiB
C++
// Copyright 2016 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.
|
|
|
|
#include "src/init/v8.h"
|
|
#include "test/common/wasm/wasm-interpreter.h"
|
|
#include "test/common/wasm/wasm-macro-gen.h"
|
|
#include "test/unittests/test-utils.h"
|
|
#include "testing/gmock/include/gmock/gmock.h"
|
|
|
|
using testing::MakeMatcher;
|
|
using testing::Matcher;
|
|
using testing::MatcherInterface;
|
|
using testing::MatchResultListener;
|
|
using testing::StringMatchResultListener;
|
|
|
|
namespace v8 {
|
|
namespace internal {
|
|
namespace wasm {
|
|
|
|
struct ExpectedControlTransfer {
|
|
pc_t pc;
|
|
pcdiff_t pc_diff;
|
|
uint32_t sp_diff;
|
|
uint32_t target_arity;
|
|
};
|
|
|
|
// For nicer error messages.
|
|
class ControlTransferMatcher
|
|
: public MatcherInterface<const ControlTransferEntry&> {
|
|
public:
|
|
explicit ControlTransferMatcher(pc_t pc,
|
|
const ExpectedControlTransfer& expected)
|
|
: pc_(pc), expected_(expected) {}
|
|
|
|
void DescribeTo(std::ostream* os) const override {
|
|
*os << "@" << pc_ << ": pcdiff = " << expected_.pc_diff
|
|
<< ", spdiff = " << expected_.sp_diff
|
|
<< ", target arity = " << expected_.target_arity;
|
|
}
|
|
|
|
bool MatchAndExplain(const ControlTransferEntry& input,
|
|
MatchResultListener* listener) const override {
|
|
if (input.pc_diff == expected_.pc_diff &&
|
|
input.sp_diff == expected_.sp_diff &&
|
|
input.target_arity == expected_.target_arity) {
|
|
return true;
|
|
}
|
|
*listener << "@" << pc_ << ": pcdiff = " << input.pc_diff
|
|
<< ", spdiff = " << input.sp_diff
|
|
<< ", target arity = " << input.target_arity;
|
|
return false;
|
|
}
|
|
|
|
private:
|
|
pc_t pc_;
|
|
const ExpectedControlTransfer& expected_;
|
|
};
|
|
|
|
class ControlTransferTest : public TestWithZone {
|
|
public:
|
|
template <int code_len>
|
|
void CheckTransfers(
|
|
const byte (&code)[code_len],
|
|
std::initializer_list<ExpectedControlTransfer> expected_transfers) {
|
|
byte code_with_end[code_len + 1]; // NOLINT: code_len is a constant here
|
|
memcpy(code_with_end, code, code_len);
|
|
code_with_end[code_len] = kExprEnd;
|
|
|
|
ControlTransferMap map = WasmInterpreter::ComputeControlTransfersForTesting(
|
|
zone(), nullptr, code_with_end, code_with_end + code_len + 1);
|
|
// Check all control targets in the map.
|
|
for (auto& expected_transfer : expected_transfers) {
|
|
pc_t pc = expected_transfer.pc;
|
|
EXPECT_TRUE(map.count(pc) > 0) << "expected control target @" << pc;
|
|
if (!map.count(pc)) continue;
|
|
auto& entry = map[pc];
|
|
EXPECT_THAT(entry, MakeMatcher(new ControlTransferMatcher(
|
|
pc, expected_transfer)));
|
|
}
|
|
|
|
// Check there are no other control targets.
|
|
CheckNoOtherTargets(code_with_end, code_with_end + code_len + 1, map,
|
|
expected_transfers);
|
|
}
|
|
|
|
void CheckNoOtherTargets(
|
|
const byte* start, const byte* end, const ControlTransferMap& map,
|
|
std::initializer_list<ExpectedControlTransfer> targets) {
|
|
// Check there are no other control targets.
|
|
for (pc_t pc = 0; start + pc < end; pc++) {
|
|
bool found = false;
|
|
for (auto& target : targets) {
|
|
if (target.pc == pc) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (found) continue;
|
|
EXPECT_TRUE(map.count(pc) == 0) << "expected no control @ +" << pc;
|
|
}
|
|
}
|
|
};
|
|
|
|
TEST_F(ControlTransferTest, SimpleIf) {
|
|
byte code[] = {
|
|
kExprI32Const, // @0
|
|
0, // @1
|
|
kExprIf, // @2
|
|
kLocalVoid, // @3
|
|
kExprEnd // @4
|
|
};
|
|
CheckTransfers(code, {{2, 2, 0, 0}});
|
|
}
|
|
|
|
TEST_F(ControlTransferTest, SimpleIf1) {
|
|
byte code[] = {
|
|
kExprI32Const, // @0
|
|
0, // @1
|
|
kExprIf, // @2
|
|
kLocalVoid, // @3
|
|
kExprNop, // @4
|
|
kExprEnd // @5
|
|
};
|
|
CheckTransfers(code, {{2, 3, 0, 0}});
|
|
}
|
|
|
|
TEST_F(ControlTransferTest, SimpleIf2) {
|
|
byte code[] = {
|
|
kExprI32Const, // @0
|
|
0, // @1
|
|
kExprIf, // @2
|
|
kLocalVoid, // @3
|
|
kExprNop, // @4
|
|
kExprNop, // @5
|
|
kExprEnd // @6
|
|
};
|
|
CheckTransfers(code, {{2, 4, 0, 0}});
|
|
}
|
|
|
|
TEST_F(ControlTransferTest, SimpleIfElse) {
|
|
byte code[] = {
|
|
kExprI32Const, // @0
|
|
0, // @1
|
|
kExprIf, // @2
|
|
kLocalVoid, // @3
|
|
kExprElse, // @4
|
|
kExprEnd // @5
|
|
};
|
|
CheckTransfers(code, {{2, 3, 0, 0}, {4, 2, 0, 0}});
|
|
}
|
|
|
|
TEST_F(ControlTransferTest, SimpleIfElse_v1) {
|
|
byte code[] = {
|
|
kExprI32Const, // @0
|
|
0, // @1
|
|
kExprIf, // @2
|
|
kLocalVoid, // @3
|
|
kExprI32Const, // @4
|
|
0, // @5
|
|
kExprElse, // @6
|
|
kExprI32Const, // @7
|
|
0, // @8
|
|
kExprEnd // @9
|
|
};
|
|
CheckTransfers(code, {{2, 5, 0, 0}, {6, 4, 1, 0}});
|
|
}
|
|
|
|
TEST_F(ControlTransferTest, SimpleIfElse1) {
|
|
byte code[] = {
|
|
kExprI32Const, // @0
|
|
0, // @1
|
|
kExprIf, // @2
|
|
kLocalVoid, // @3
|
|
kExprElse, // @4
|
|
kExprNop, // @5
|
|
kExprEnd // @6
|
|
};
|
|
CheckTransfers(code, {{2, 3, 0, 0}, {4, 3, 0, 0}});
|
|
}
|
|
|
|
TEST_F(ControlTransferTest, IfBr) {
|
|
byte code[] = {
|
|
kExprI32Const, // @0
|
|
0, // @1
|
|
kExprIf, // @2
|
|
kLocalVoid, // @3
|
|
kExprBr, // @4
|
|
0, // @5
|
|
kExprEnd // @6
|
|
};
|
|
CheckTransfers(code, {{2, 4, 0, 0}, {4, 3, 0, 0}});
|
|
}
|
|
|
|
TEST_F(ControlTransferTest, IfBrElse) {
|
|
byte code[] = {
|
|
kExprI32Const, // @0
|
|
0, // @1
|
|
kExprIf, // @2
|
|
kLocalVoid, // @3
|
|
kExprBr, // @4
|
|
0, // @5
|
|
kExprElse, // @6
|
|
kExprEnd // @7
|
|
};
|
|
CheckTransfers(code, {{2, 5, 0, 0}, {4, 4, 0, 0}});
|
|
}
|
|
|
|
TEST_F(ControlTransferTest, IfElseBr) {
|
|
byte code[] = {
|
|
kExprI32Const, // @0
|
|
0, // @1
|
|
kExprIf, // @2
|
|
kLocalVoid, // @3
|
|
kExprElse, // @4
|
|
kExprBr, // @5
|
|
0, // @6
|
|
kExprEnd // @7
|
|
};
|
|
CheckTransfers(code, {{2, 3, 0, 0}, {4, 4, 0, 0}, {5, 3, 0, 0}});
|
|
}
|
|
|
|
TEST_F(ControlTransferTest, BlockEmpty) {
|
|
byte code[] = {
|
|
kExprBlock, // @0
|
|
kLocalVoid, // @1
|
|
kExprEnd // @2
|
|
};
|
|
CheckTransfers(code, {});
|
|
}
|
|
|
|
TEST_F(ControlTransferTest, Br0) {
|
|
byte code[] = {
|
|
kExprBlock, // @0
|
|
kLocalVoid, // @1
|
|
kExprBr, // @2
|
|
0, // @3
|
|
kExprEnd // @4
|
|
};
|
|
CheckTransfers(code, {{2, 3, 0, 0}});
|
|
}
|
|
|
|
TEST_F(ControlTransferTest, Br1) {
|
|
byte code[] = {
|
|
kExprBlock, // @0
|
|
kLocalVoid, // @1
|
|
kExprNop, // @2
|
|
kExprBr, // @3
|
|
0, // @4
|
|
kExprEnd // @5
|
|
};
|
|
CheckTransfers(code, {{3, 3, 0, 0}});
|
|
}
|
|
|
|
TEST_F(ControlTransferTest, Br_v1a) {
|
|
byte code[] = {
|
|
kExprBlock, // @0
|
|
kLocalVoid, // @1
|
|
kExprI32Const, // @2
|
|
0, // @3
|
|
kExprBr, // @4
|
|
0, // @5
|
|
kExprEnd // @6
|
|
};
|
|
CheckTransfers(code, {{4, 3, 1, 0}});
|
|
}
|
|
|
|
TEST_F(ControlTransferTest, Br_v1b) {
|
|
byte code[] = {
|
|
kExprBlock, // @0
|
|
kLocalVoid, // @1
|
|
kExprI32Const, // @2
|
|
0, // @3
|
|
kExprBr, // @4
|
|
0, // @5
|
|
kExprEnd // @6
|
|
};
|
|
CheckTransfers(code, {{4, 3, 1, 0}});
|
|
}
|
|
|
|
TEST_F(ControlTransferTest, Br_v1c) {
|
|
byte code[] = {
|
|
kExprI32Const, // @0
|
|
0, // @1
|
|
kExprBlock, // @2
|
|
kLocalVoid, // @3
|
|
kExprBr, // @4
|
|
0, // @5
|
|
kExprEnd // @6
|
|
};
|
|
CheckTransfers(code, {{4, 3, 0, 0}});
|
|
}
|
|
|
|
TEST_F(ControlTransferTest, Br_v1d) {
|
|
byte code[] = {
|
|
kExprBlock, // @0
|
|
kLocalI32, // @1
|
|
kExprI32Const, // @2
|
|
0, // @3
|
|
kExprBr, // @4
|
|
0, // @5
|
|
kExprEnd // @6
|
|
};
|
|
CheckTransfers(code, {{4, 3, 1, 1}});
|
|
}
|
|
|
|
TEST_F(ControlTransferTest, Br2) {
|
|
byte code[] = {
|
|
kExprBlock, // @0
|
|
kLocalVoid, // @1
|
|
kExprNop, // @2
|
|
kExprNop, // @3
|
|
kExprBr, // @4
|
|
0, // @5
|
|
kExprEnd // @6
|
|
};
|
|
CheckTransfers(code, {{4, 3, 0, 0}});
|
|
}
|
|
|
|
TEST_F(ControlTransferTest, Br0b) {
|
|
byte code[] = {
|
|
kExprBlock, // @0
|
|
kLocalVoid, // @1
|
|
kExprBr, // @2
|
|
0, // @3
|
|
kExprNop, // @4
|
|
kExprEnd // @5
|
|
};
|
|
CheckTransfers(code, {{2, 4, 0, 0}});
|
|
}
|
|
|
|
TEST_F(ControlTransferTest, Br0c) {
|
|
byte code[] = {
|
|
kExprBlock, // @0
|
|
kLocalVoid, // @1
|
|
kExprBr, // @2
|
|
0, // @3
|
|
kExprNop, // @4
|
|
kExprNop, // @5
|
|
kExprEnd // @6
|
|
};
|
|
CheckTransfers(code, {{2, 5, 0, 0}});
|
|
}
|
|
|
|
TEST_F(ControlTransferTest, SimpleLoop1) {
|
|
byte code[] = {
|
|
kExprLoop, // @0
|
|
kLocalVoid, // @1
|
|
kExprBr, // @2
|
|
0, // @3
|
|
kExprEnd // @4
|
|
};
|
|
CheckTransfers(code, {{2, -2, 0, 0}});
|
|
}
|
|
|
|
TEST_F(ControlTransferTest, SimpleLoop2) {
|
|
byte code[] = {
|
|
kExprLoop, // @0
|
|
kLocalVoid, // @1
|
|
kExprNop, // @2
|
|
kExprBr, // @3
|
|
0, // @4
|
|
kExprEnd // @5
|
|
};
|
|
CheckTransfers(code, {{3, -3, 0, 0}});
|
|
}
|
|
|
|
TEST_F(ControlTransferTest, SimpleLoopExit1) {
|
|
byte code[] = {
|
|
kExprLoop, // @0
|
|
kLocalVoid, // @1
|
|
kExprBr, // @2
|
|
1, // @3
|
|
kExprEnd // @4
|
|
};
|
|
CheckTransfers(code, {{2, 4, 0, 0}});
|
|
}
|
|
|
|
TEST_F(ControlTransferTest, SimpleLoopExit2) {
|
|
byte code[] = {
|
|
kExprLoop, // @0
|
|
kLocalVoid, // @1
|
|
kExprNop, // @2
|
|
kExprBr, // @3
|
|
1, // @4
|
|
kExprEnd // @5
|
|
};
|
|
CheckTransfers(code, {{3, 4, 0, 0}});
|
|
}
|
|
|
|
TEST_F(ControlTransferTest, BrTable0) {
|
|
byte code[] = {
|
|
kExprBlock, // @0
|
|
kLocalVoid, // @1
|
|
kExprI32Const, // @2
|
|
0, // @3
|
|
kExprBrTable, // @4
|
|
0, // @5
|
|
U32V_1(0), // @6
|
|
kExprEnd // @7
|
|
};
|
|
CheckTransfers(code, {{4, 4, 0, 0}});
|
|
}
|
|
|
|
TEST_F(ControlTransferTest, BrTable0_v1a) {
|
|
byte code[] = {
|
|
kExprBlock, // @0
|
|
kLocalVoid, // @1
|
|
kExprI32Const, // @2
|
|
0, // @3
|
|
kExprI32Const, // @4
|
|
0, // @5
|
|
kExprBrTable, // @6
|
|
0, // @7
|
|
U32V_1(0), // @8
|
|
kExprEnd // @9
|
|
};
|
|
CheckTransfers(code, {{6, 4, 1, 0}});
|
|
}
|
|
|
|
TEST_F(ControlTransferTest, BrTable0_v1b) {
|
|
byte code[] = {
|
|
kExprBlock, // @0
|
|
kLocalVoid, // @1
|
|
kExprI32Const, // @2
|
|
0, // @3
|
|
kExprI32Const, // @4
|
|
0, // @5
|
|
kExprBrTable, // @6
|
|
0, // @7
|
|
U32V_1(0), // @8
|
|
kExprEnd // @9
|
|
};
|
|
CheckTransfers(code, {{6, 4, 1, 0}});
|
|
}
|
|
|
|
TEST_F(ControlTransferTest, BrTable1) {
|
|
byte code[] = {
|
|
kExprBlock, // @0
|
|
kLocalVoid, // @1
|
|
kExprI32Const, // @2
|
|
0, // @3
|
|
kExprBrTable, // @4
|
|
1, // @5
|
|
U32V_1(0), // @6
|
|
U32V_1(0), // @7
|
|
kExprEnd // @8
|
|
};
|
|
CheckTransfers(code, {{4, 5, 0, 0}, {5, 4, 0, 0}});
|
|
}
|
|
|
|
TEST_F(ControlTransferTest, BrTable2) {
|
|
byte code[] = {
|
|
kExprBlock, // @0
|
|
kLocalVoid, // @1
|
|
kExprBlock, // @2
|
|
kLocalVoid, // @3
|
|
kExprI32Const, // @4
|
|
0, // @5
|
|
kExprBrTable, // @6
|
|
2, // @7
|
|
U32V_1(0), // @8
|
|
U32V_1(0), // @9
|
|
U32V_1(1), // @10
|
|
kExprEnd, // @11
|
|
kExprEnd // @12
|
|
};
|
|
CheckTransfers(code, {{6, 6, 0, 0}, {7, 5, 0, 0}, {8, 5, 0, 0}});
|
|
}
|
|
|
|
TEST_F(ControlTransferTest, BiggerSpDiffs) {
|
|
byte code[] = {
|
|
kExprBlock, // @0
|
|
kLocalI32, // @1
|
|
kExprI32Const, // @2
|
|
0, // @3
|
|
kExprBlock, // @4
|
|
kLocalVoid, // @5
|
|
kExprI32Const, // @6
|
|
0, // @7
|
|
kExprI32Const, // @8
|
|
0, // @9
|
|
kExprI32Const, // @10
|
|
0, // @11
|
|
kExprBrIf, // @12
|
|
0, // @13
|
|
kExprBr, // @14
|
|
1, // @15
|
|
kExprEnd, // @16
|
|
kExprEnd // @17
|
|
};
|
|
CheckTransfers(code, {{12, 5, 2, 0}, {14, 4, 3, 1}});
|
|
}
|
|
|
|
TEST_F(ControlTransferTest, NoInfoForUnreachableCode) {
|
|
byte code[] = {
|
|
kExprBlock, // @0
|
|
kLocalVoid, // @1
|
|
kExprBr, // @2
|
|
0, // @3
|
|
kExprBr, // @4 -- no control transfer entry!
|
|
1, // @5
|
|
kExprEnd, // @6
|
|
kExprBlock, // @7
|
|
kLocalVoid, // @8
|
|
kExprUnreachable, // @9
|
|
kExprI32Const, // @10
|
|
0, // @11
|
|
kExprIf, // @12 -- no control transfer entry!
|
|
kLocalVoid, // @13
|
|
kExprBr, // @14 -- no control transfer entry!
|
|
0, // @15
|
|
kExprElse, // @16 -- no control transfer entry!
|
|
kExprEnd, // @17
|
|
kExprEnd // @18
|
|
};
|
|
CheckTransfers(code, {{2, 5, 0, 0}});
|
|
}
|
|
|
|
} // namespace wasm
|
|
} // namespace internal
|
|
} // namespace v8
|