4423c9cc09
During computation of the side table, ignore stack effects of instructions following any unconditional jump in the same block (|unreachable|, |br|, |br_table| or |return| jump out of the block). Without this fix, the current stack height might underflow, or we compute an unnecessarily large max_stack_height_. Note that those instruction will never get executed anyway. Hence, we don't need to store any side table information for such unreachable code. R=rossberg@chromium.org BUG=chromium:716936, chromium:715990 Change-Id: I282f7f18ba1b972a112210e692f6cd05cf32308c Reviewed-on: https://chromium-review.googlesource.com/493266 Reviewed-by: Andreas Rossberg <rossberg@chromium.org> Commit-Queue: Clemens Hammacher <clemensh@chromium.org> Cr-Commit-Position: refs/heads/master@{#45059}
532 lines
13 KiB
C++
532 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 "test/unittests/test-utils.h"
|
|
#include "testing/gmock/include/gmock/gmock.h"
|
|
|
|
#include "src/v8.h"
|
|
#include "src/wasm/wasm-interpreter.h"
|
|
|
|
#include "test/common/wasm/wasm-macro-gen.h"
|
|
|
|
using testing::MakeMatcher;
|
|
using testing::Matcher;
|
|
using testing::MatcherInterface;
|
|
using testing::MatchResultListener;
|
|
using testing::StringMatchResultListener;
|
|
|
|
namespace v8 {
|
|
namespace internal {
|
|
namespace wasm {
|
|
|
|
#define B1(a) kExprBlock, a, kExprEnd
|
|
#define B2(a, b) kExprBlock, a, b, kExprEnd
|
|
#define B3(a, b, c) kExprBlock, a, b, c, kExprEnd
|
|
|
|
#define TRANSFER_VOID 0
|
|
#define TRANSFER_ONE 1
|
|
|
|
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, 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}, {6, 2, 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
|