2b96e854f5
Previously, the accumulator was at the end of liveness bitvectors, which meant that checking for accumulator liveness required a length lookup. This CL moves it to the start of the bitvector, with registers starting at index 1 -- the assumption is that the addition of 1 to the index on register liveness access can be constant folded away. As a cleanup, replace all the custom liveness printing code with a single unified ToString. This places the accumulator at the end of the printed liveness, to avoid having to change test expectations (also, the position of the accumulator is now an implementation detail). As a similar cleanup, change StateValue node building to use the BytecodeLivenessState interface rather than the underlying bitvector. These two cleanups allow us to remove the raw bitvector accessor from liveness entirely. Change-Id: Ic2744b5e8e16b8527e6a4e8d3b4ddad7096289d9 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3455144 Commit-Queue: Leszek Swirski <leszeks@chromium.org> Auto-Submit: Leszek Swirski <leszeks@chromium.org> Reviewed-by: Tobias Tebbi <tebbi@chromium.org> Cr-Commit-Position: refs/heads/main@{#79066}
238 lines
7.1 KiB
C++
238 lines
7.1 KiB
C++
// Copyright 2014 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/compiler/state-values-utils.h"
|
|
|
|
#include "src/compiler/bytecode-liveness-map.h"
|
|
#include "src/utils/bit-vector.h"
|
|
#include "test/unittests/compiler/graph-unittest.h"
|
|
#include "test/unittests/compiler/node-test-utils.h"
|
|
#include "test/unittests/test-utils.h"
|
|
#include "testing/gmock/include/gmock/gmock.h"
|
|
|
|
namespace v8 {
|
|
namespace internal {
|
|
namespace compiler {
|
|
|
|
class StateValuesIteratorTest : public GraphTest {
|
|
public:
|
|
StateValuesIteratorTest() : GraphTest(3) {}
|
|
|
|
Node* StateValuesFromVector(NodeVector* nodes) {
|
|
int count = static_cast<int>(nodes->size());
|
|
return graph()->NewNode(
|
|
common()->StateValues(count, SparseInputMask::Dense()), count,
|
|
count == 0 ? nullptr : &(nodes->front()));
|
|
}
|
|
};
|
|
|
|
|
|
TEST_F(StateValuesIteratorTest, SimpleIteration) {
|
|
NodeVector inputs(zone());
|
|
const int count = 10;
|
|
for (int i = 0; i < count; i++) {
|
|
inputs.push_back(Int32Constant(i));
|
|
}
|
|
Node* state_values = StateValuesFromVector(&inputs);
|
|
int i = 0;
|
|
for (StateValuesAccess::TypedNode node : StateValuesAccess(state_values)) {
|
|
EXPECT_THAT(node.node, IsInt32Constant(i));
|
|
i++;
|
|
}
|
|
EXPECT_EQ(count, i);
|
|
}
|
|
|
|
|
|
TEST_F(StateValuesIteratorTest, EmptyIteration) {
|
|
NodeVector inputs(zone());
|
|
Node* state_values = StateValuesFromVector(&inputs);
|
|
bool empty = true;
|
|
for (auto node : StateValuesAccess(state_values)) {
|
|
USE(node);
|
|
empty = false;
|
|
}
|
|
EXPECT_TRUE(empty);
|
|
}
|
|
|
|
|
|
TEST_F(StateValuesIteratorTest, NestedIteration) {
|
|
NodeVector inputs(zone());
|
|
int count = 0;
|
|
for (int i = 0; i < 8; i++) {
|
|
if (i == 2) {
|
|
// Single nested in index 2.
|
|
NodeVector nested_inputs(zone());
|
|
for (int j = 0; j < 8; j++) {
|
|
nested_inputs.push_back(Int32Constant(count++));
|
|
}
|
|
inputs.push_back(StateValuesFromVector(&nested_inputs));
|
|
} else if (i == 5) {
|
|
// Double nested at index 5.
|
|
NodeVector nested_inputs(zone());
|
|
for (int j = 0; j < 8; j++) {
|
|
if (j == 7) {
|
|
NodeVector doubly_nested_inputs(zone());
|
|
for (int k = 0; k < 2; k++) {
|
|
doubly_nested_inputs.push_back(Int32Constant(count++));
|
|
}
|
|
nested_inputs.push_back(StateValuesFromVector(&doubly_nested_inputs));
|
|
} else {
|
|
nested_inputs.push_back(Int32Constant(count++));
|
|
}
|
|
}
|
|
inputs.push_back(StateValuesFromVector(&nested_inputs));
|
|
} else {
|
|
inputs.push_back(Int32Constant(count++));
|
|
}
|
|
}
|
|
Node* state_values = StateValuesFromVector(&inputs);
|
|
int i = 0;
|
|
for (StateValuesAccess::TypedNode node : StateValuesAccess(state_values)) {
|
|
EXPECT_THAT(node.node, IsInt32Constant(i));
|
|
i++;
|
|
}
|
|
EXPECT_EQ(count, i);
|
|
}
|
|
|
|
|
|
TEST_F(StateValuesIteratorTest, TreeFromVector) {
|
|
int sizes[] = {0, 1, 2, 100, 5000, 30000};
|
|
TRACED_FOREACH(int, count, sizes) {
|
|
JSOperatorBuilder javascript(zone());
|
|
MachineOperatorBuilder machine(zone());
|
|
JSGraph jsgraph(isolate(), graph(), common(), &javascript, nullptr,
|
|
&machine);
|
|
|
|
// Generate the input vector.
|
|
NodeVector inputs(zone());
|
|
for (int i = 0; i < count; i++) {
|
|
inputs.push_back(Int32Constant(i));
|
|
}
|
|
|
|
// Build the tree.
|
|
StateValuesCache builder(&jsgraph);
|
|
Node* values_node = builder.GetNodeForValues(
|
|
inputs.size() == 0 ? nullptr : &(inputs.front()), inputs.size(),
|
|
nullptr);
|
|
|
|
// Check the tree contents with vector.
|
|
int i = 0;
|
|
for (StateValuesAccess::TypedNode node : StateValuesAccess(values_node)) {
|
|
EXPECT_THAT(node.node, IsInt32Constant(i));
|
|
i++;
|
|
}
|
|
EXPECT_EQ(inputs.size(), static_cast<size_t>(i));
|
|
}
|
|
}
|
|
|
|
TEST_F(StateValuesIteratorTest, TreeFromVectorWithLiveness) {
|
|
int sizes[] = {0, 1, 2, 100, 5000, 30000};
|
|
TRACED_FOREACH(int, count, sizes) {
|
|
JSOperatorBuilder javascript(zone());
|
|
MachineOperatorBuilder machine(zone());
|
|
JSGraph jsgraph(isolate(), graph(), common(), &javascript, nullptr,
|
|
&machine);
|
|
|
|
// Generate the input vector.
|
|
NodeVector inputs(zone());
|
|
for (int i = 0; i < count; i++) {
|
|
inputs.push_back(Int32Constant(i));
|
|
}
|
|
// Generate the input liveness.
|
|
BytecodeLivenessState liveness(count, zone());
|
|
for (int i = 0; i < count; i++) {
|
|
if (i % 3 == 0) {
|
|
liveness.MarkRegisterLive(i);
|
|
}
|
|
}
|
|
|
|
// Build the tree.
|
|
StateValuesCache builder(&jsgraph);
|
|
Node* values_node = builder.GetNodeForValues(
|
|
inputs.size() == 0 ? nullptr : &(inputs.front()), inputs.size(),
|
|
&liveness);
|
|
|
|
// Check the tree contents with vector.
|
|
int i = 0;
|
|
for (StateValuesAccess::iterator it =
|
|
StateValuesAccess(values_node).begin();
|
|
!it.done(); ++it) {
|
|
if (liveness.RegisterIsLive(i)) {
|
|
EXPECT_THAT(it.node(), IsInt32Constant(i));
|
|
} else {
|
|
EXPECT_EQ(it.node(), nullptr);
|
|
}
|
|
i++;
|
|
}
|
|
EXPECT_EQ(inputs.size(), static_cast<size_t>(i));
|
|
}
|
|
}
|
|
|
|
TEST_F(StateValuesIteratorTest, BuildTreeIdentical) {
|
|
int sizes[] = {0, 1, 2, 100, 5000, 30000};
|
|
TRACED_FOREACH(int, count, sizes) {
|
|
JSOperatorBuilder javascript(zone());
|
|
MachineOperatorBuilder machine(zone());
|
|
JSGraph jsgraph(isolate(), graph(), common(), &javascript, nullptr,
|
|
&machine);
|
|
|
|
// Generate the input vector.
|
|
NodeVector inputs(zone());
|
|
for (int i = 0; i < count; i++) {
|
|
inputs.push_back(Int32Constant(i));
|
|
}
|
|
|
|
// Build two trees from the same data.
|
|
StateValuesCache builder(&jsgraph);
|
|
Node* node1 = builder.GetNodeForValues(
|
|
inputs.size() == 0 ? nullptr : &(inputs.front()), inputs.size(),
|
|
nullptr);
|
|
Node* node2 = builder.GetNodeForValues(
|
|
inputs.size() == 0 ? nullptr : &(inputs.front()), inputs.size(),
|
|
nullptr);
|
|
|
|
// The trees should be equal since the data was the same.
|
|
EXPECT_EQ(node1, node2);
|
|
}
|
|
}
|
|
|
|
TEST_F(StateValuesIteratorTest, BuildTreeWithLivenessIdentical) {
|
|
int sizes[] = {0, 1, 2, 100, 5000, 30000};
|
|
TRACED_FOREACH(int, count, sizes) {
|
|
JSOperatorBuilder javascript(zone());
|
|
MachineOperatorBuilder machine(zone());
|
|
JSGraph jsgraph(isolate(), graph(), common(), &javascript, nullptr,
|
|
&machine);
|
|
|
|
// Generate the input vector.
|
|
NodeVector inputs(zone());
|
|
for (int i = 0; i < count; i++) {
|
|
inputs.push_back(Int32Constant(i));
|
|
}
|
|
// Generate the input liveness.
|
|
BytecodeLivenessState liveness(count, zone());
|
|
for (int i = 0; i < count; i++) {
|
|
if (i % 3 == 0) {
|
|
liveness.MarkRegisterLive(i);
|
|
}
|
|
}
|
|
|
|
// Build two trees from the same data.
|
|
StateValuesCache builder(&jsgraph);
|
|
Node* node1 = builder.GetNodeForValues(
|
|
inputs.size() == 0 ? nullptr : &(inputs.front()), inputs.size(),
|
|
&liveness);
|
|
Node* node2 = builder.GetNodeForValues(
|
|
inputs.size() == 0 ? nullptr : &(inputs.front()), inputs.size(),
|
|
&liveness);
|
|
|
|
// The trees should be equal since the data was the same.
|
|
EXPECT_EQ(node1, node2);
|
|
}
|
|
}
|
|
|
|
} // namespace compiler
|
|
} // namespace internal
|
|
} // namespace v8
|