362e792ee4
Loops can be unrolled only for innermost loops. But, the wasm graph builder builds loop exits regardless of the condition. This CL detects if the loop can be innermost using AnalyzeLoopAssignment, and do not allocate unnecessary nodes if it can't be. This reduces memory usage for the reported wasm binary from 1.3GB to 300MB. Bug: v8:13543 Change-Id: I693800071f7eee4a9991e094830f23d27a96b13f Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/4134466 Reviewed-by: Manos Koukoutos <manoskouk@chromium.org> Commit-Queue: Choongwoo Han <choongwoo.han@microsoft.com> Cr-Commit-Position: refs/heads/main@{#85122}
230 lines
7.1 KiB
C++
230 lines
7.1 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 "src/init/v8.h"
|
|
#include "src/objects/objects-inl.h"
|
|
#include "src/objects/objects.h"
|
|
#include "src/utils/bit-vector.h"
|
|
#include "src/wasm/function-body-decoder.h"
|
|
#include "src/wasm/wasm-module.h"
|
|
|
|
#include "test/common/wasm/test-signatures.h"
|
|
#include "test/common/wasm/wasm-macro-gen.h"
|
|
|
|
namespace v8 {
|
|
namespace internal {
|
|
namespace wasm {
|
|
|
|
#define WASM_SET_ZERO(i) WASM_LOCAL_SET(i, WASM_ZERO)
|
|
|
|
class WasmLoopAssignmentAnalyzerTest : public TestWithZone {
|
|
public:
|
|
WasmLoopAssignmentAnalyzerTest() : num_locals(0) {}
|
|
TestSignatures sigs;
|
|
uint32_t num_locals;
|
|
|
|
BitVector* Analyze(const byte* start, const byte* end,
|
|
bool* loop_is_innermost = nullptr) {
|
|
return AnalyzeLoopAssignmentForTesting(zone(), num_locals, start, end,
|
|
loop_is_innermost);
|
|
}
|
|
};
|
|
|
|
TEST_F(WasmLoopAssignmentAnalyzerTest, Empty0) {
|
|
byte code[] = { 0 };
|
|
BitVector* assigned = Analyze(code, code);
|
|
EXPECT_EQ(assigned, nullptr);
|
|
}
|
|
|
|
TEST_F(WasmLoopAssignmentAnalyzerTest, Empty1) {
|
|
byte code[] = {kExprLoop, kVoidCode, 0};
|
|
for (int i = 0; i < 5; i++) {
|
|
BitVector* assigned = Analyze(code, code + arraysize(code));
|
|
for (int j = 0; j < assigned->length(); j++) {
|
|
EXPECT_FALSE(assigned->Contains(j));
|
|
}
|
|
num_locals++;
|
|
}
|
|
}
|
|
|
|
TEST_F(WasmLoopAssignmentAnalyzerTest, One) {
|
|
num_locals = 5;
|
|
for (int i = 0; i < 5; i++) {
|
|
byte code[] = {WASM_LOOP(WASM_SET_ZERO(i))};
|
|
BitVector* assigned = Analyze(code, code + arraysize(code));
|
|
for (int j = 0; j < assigned->length(); j++) {
|
|
EXPECT_EQ(j == i, assigned->Contains(j));
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST_F(WasmLoopAssignmentAnalyzerTest, TeeOne) {
|
|
num_locals = 5;
|
|
for (int i = 0; i < 5; i++) {
|
|
byte code[] = {WASM_LOOP(WASM_LOCAL_TEE(i, WASM_ZERO))};
|
|
BitVector* assigned = Analyze(code, code + arraysize(code));
|
|
for (int j = 0; j < assigned->length(); j++) {
|
|
EXPECT_EQ(j == i, assigned->Contains(j));
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST_F(WasmLoopAssignmentAnalyzerTest, OneBeyond) {
|
|
num_locals = 5;
|
|
for (int i = 0; i < 5; i++) {
|
|
byte code[] = {WASM_LOOP(WASM_SET_ZERO(i)), WASM_SET_ZERO(1)};
|
|
BitVector* assigned = Analyze(code, code + arraysize(code));
|
|
for (int j = 0; j < assigned->length(); j++) {
|
|
EXPECT_EQ(j == i, assigned->Contains(j));
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST_F(WasmLoopAssignmentAnalyzerTest, Two) {
|
|
num_locals = 5;
|
|
for (int i = 0; i < 5; i++) {
|
|
for (int j = 0; j < 5; j++) {
|
|
byte code[] = {WASM_LOOP(WASM_SET_ZERO(i), WASM_SET_ZERO(j))};
|
|
BitVector* assigned = Analyze(code, code + arraysize(code));
|
|
for (int k = 0; k < assigned->length(); k++) {
|
|
bool expected = k == i || k == j;
|
|
EXPECT_EQ(expected, assigned->Contains(k));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST_F(WasmLoopAssignmentAnalyzerTest, NestedIf) {
|
|
num_locals = 5;
|
|
for (int i = 0; i < 5; i++) {
|
|
byte code[] = {WASM_LOOP(
|
|
WASM_IF_ELSE(WASM_SET_ZERO(0), WASM_SET_ZERO(i), WASM_SET_ZERO(1)))};
|
|
BitVector* assigned = Analyze(code, code + arraysize(code));
|
|
for (int j = 0; j < assigned->length(); j++) {
|
|
bool expected = i == j || j == 0 || j == 1;
|
|
EXPECT_EQ(expected, assigned->Contains(j));
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST_F(WasmLoopAssignmentAnalyzerTest, BigLocal) {
|
|
num_locals = 65000;
|
|
for (int i = 13; i < 65000; i = static_cast<int>(i * 1.5)) {
|
|
byte code[] = {WASM_LOOP(WASM_I32V_1(11), kExprLocalSet, U32V_3(i))};
|
|
|
|
BitVector* assigned = Analyze(code, code + arraysize(code));
|
|
for (int j = 0; j < assigned->length(); j++) {
|
|
bool expected = i == j;
|
|
EXPECT_EQ(expected, assigned->Contains(j));
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST_F(WasmLoopAssignmentAnalyzerTest, Break) {
|
|
num_locals = 3;
|
|
byte code[] = {
|
|
WASM_LOOP(WASM_IF(WASM_LOCAL_GET(0), WASM_BRV(1, WASM_SET_ZERO(1)))),
|
|
WASM_SET_ZERO(0)};
|
|
|
|
BitVector* assigned = Analyze(code, code + arraysize(code));
|
|
for (int j = 0; j < assigned->length(); j++) {
|
|
bool expected = j == 1;
|
|
EXPECT_EQ(expected, assigned->Contains(j));
|
|
}
|
|
}
|
|
|
|
TEST_F(WasmLoopAssignmentAnalyzerTest, Loop1) {
|
|
num_locals = 5;
|
|
byte code[] = {
|
|
WASM_LOOP(WASM_IF(
|
|
WASM_LOCAL_GET(0),
|
|
WASM_BRV(0, WASM_LOCAL_SET(3, WASM_I32_SUB(WASM_LOCAL_GET(0),
|
|
WASM_I32V_1(1)))))),
|
|
WASM_LOCAL_GET(0)};
|
|
|
|
BitVector* assigned = Analyze(code, code + arraysize(code));
|
|
for (int j = 0; j < assigned->length(); j++) {
|
|
bool expected = j == 3;
|
|
EXPECT_EQ(expected, assigned->Contains(j));
|
|
}
|
|
}
|
|
|
|
TEST_F(WasmLoopAssignmentAnalyzerTest, Loop2) {
|
|
num_locals = 6;
|
|
const byte kIter = 0;
|
|
const byte kSum = 3;
|
|
|
|
byte code[] = {WASM_BLOCK(
|
|
WASM_WHILE(
|
|
WASM_LOCAL_GET(kIter),
|
|
WASM_BLOCK(
|
|
WASM_LOCAL_SET(
|
|
kSum, WASM_F32_ADD(WASM_LOCAL_GET(kSum),
|
|
WASM_LOAD_MEM(MachineType::Float32(),
|
|
WASM_LOCAL_GET(kIter)))),
|
|
WASM_LOCAL_SET(
|
|
kIter, WASM_I32_SUB(WASM_LOCAL_GET(kIter), WASM_I32V_1(4))))),
|
|
WASM_STORE_MEM(MachineType::Float32(), WASM_ZERO, WASM_LOCAL_GET(kSum)),
|
|
WASM_LOCAL_GET(kIter))};
|
|
|
|
BitVector* assigned = Analyze(code + 2, code + arraysize(code));
|
|
for (int j = 0; j < assigned->length(); j++) {
|
|
bool expected = j == kIter || j == kSum;
|
|
EXPECT_EQ(expected, assigned->Contains(j));
|
|
}
|
|
}
|
|
|
|
TEST_F(WasmLoopAssignmentAnalyzerTest, NestedLoop) {
|
|
num_locals = 5;
|
|
byte code[] = {WASM_LOOP(WASM_LOOP(WASM_LOCAL_SET(0, 1)))};
|
|
|
|
bool outer_is_innermost = false;
|
|
BitVector* outer_assigned =
|
|
Analyze(code, code + arraysize(code), &outer_is_innermost);
|
|
for (int j = 0; j < outer_assigned->length(); j++) {
|
|
bool expected = j == 0;
|
|
EXPECT_EQ(expected, outer_assigned->Contains(j));
|
|
}
|
|
EXPECT_FALSE(outer_is_innermost);
|
|
|
|
bool inner_is_innermost = false;
|
|
BitVector* inner_assigned =
|
|
Analyze(code + 2, code + arraysize(code), &inner_is_innermost);
|
|
for (int j = 0; j < inner_assigned->length(); j++) {
|
|
bool expected = j == 0;
|
|
EXPECT_EQ(expected, inner_assigned->Contains(j));
|
|
}
|
|
EXPECT_TRUE(inner_is_innermost);
|
|
}
|
|
|
|
TEST_F(WasmLoopAssignmentAnalyzerTest, Malformed) {
|
|
byte code[] = {kExprLoop, kVoidCode, kExprF32Neg, kExprBrTable, 0x0E, 'h',
|
|
'e', 'l', 'l', 'o', ',', ' ',
|
|
'w', 'o', 'r', 'l', 'd', '!'};
|
|
BitVector* assigned = Analyze(code, code + arraysize(code));
|
|
EXPECT_EQ(assigned, nullptr);
|
|
}
|
|
|
|
TEST_F(WasmLoopAssignmentAnalyzerTest, InvalidOpcode) {
|
|
byte code[] = {WASM_LOOP(0xFF)};
|
|
BitVector* assigned = Analyze(code, code + arraysize(code));
|
|
EXPECT_EQ(assigned, nullptr);
|
|
}
|
|
|
|
TEST_F(WasmLoopAssignmentAnalyzerTest, regress_642867) {
|
|
static const byte code[] = {
|
|
WASM_LOOP(WASM_ZERO, kExprLocalSet, 0xFA, 0xFF, 0xFF, 0xFF,
|
|
0x0F)}; // local index LEB128 0xFFFFFFFA
|
|
// Just make sure that the analysis does not crash.
|
|
Analyze(code, code + arraysize(code));
|
|
}
|
|
|
|
#undef WASM_SET_ZERO
|
|
|
|
} // namespace wasm
|
|
} // namespace internal
|
|
} // namespace v8
|