v8/test/unittests/compiler/decompression-elimination-unittest.cc
Santiago Aboy Solanes 21eb4b9d52 [cleanup][ptr-compr][turbofan] DecompressionElimination test cleanup
This is a CL that aims to do a general cleanup of DecompressionElimination
to make it easier for devs to look at it, and to create new test cases.

Combined direct decompression & compression tests since they can be
summarized with a for loop in just one test that tries out
all the combinations.

Also created 'global' accesses to stop repeating them in every test.
Same for compression and decompression ops.

Added EXPECT in test cases that had none.

Added dots after comments.

Variables now use underscore instead of camelCase.

Cq-Include-Trybots: luci.v8.try:v8_linux64_pointer_compression_rel_ng
Cq-Include-Trybots: luci.v8.try:v8_linux64_arm64_pointer_compression_rel_ng
Bug: v8:8977, v8:7703, v8:9183
Change-Id: I38a5c6549e0b4ff89c3271ead23b626e8b6b4843
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1628788
Reviewed-by: Ross McIlroy <rmcilroy@chromium.org>
Commit-Queue: Ross McIlroy <rmcilroy@chromium.org>
Auto-Submit: Santiago Aboy Solanes <solanes@chromium.org>
Cr-Commit-Position: refs/heads/master@{#62345}
2019-06-24 21:35:06 +00:00

855 lines
33 KiB
C++

// Copyright 2019 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/decompression-elimination.h"
#include "src/compiler/node-properties.h"
#include "src/compiler/simplified-operator.h"
#include "test/unittests/compiler/graph-reducer-unittest.h"
#include "test/unittests/compiler/graph-unittest.h"
#include "test/unittests/compiler/node-test-utils.h"
#include "testing/gmock-support.h"
using testing::_;
using testing::StrictMock;
namespace v8 {
namespace internal {
namespace compiler {
class DecompressionEliminationTest : public GraphTest {
public:
DecompressionEliminationTest()
: GraphTest(),
machine_(zone(), MachineType::PointerRepresentation(),
MachineOperatorBuilder::kNoFlags),
simplified_(zone()) {}
~DecompressionEliminationTest() override = default;
protected:
Reduction Reduce(StrictMock<MockAdvancedReducerEditor>* editor, Node* node) {
DecompressionElimination decompression_elimination(editor, graph(),
machine(), common());
return decompression_elimination.Reduce(node);
}
Reduction Reduce(Node* node) {
StrictMock<MockAdvancedReducerEditor> editor;
return Reduce(&editor, node);
}
Node* GetUniqueValueUse(Node* node) {
Node* value_use = nullptr;
for (Edge edge : node->use_edges()) {
if (NodeProperties::IsValueEdge(edge)) {
if (value_use) {
return nullptr;
} else {
value_use = edge.from();
}
}
}
// Return the value use of node after the reduction, if there is exactly one
return value_use;
}
const Operator* DecompressionOpFromAccess(const ElementAccess access) {
switch (access.machine_type.representation()) {
case MachineRepresentation::kCompressed:
return machine()->ChangeCompressedToTagged();
case MachineRepresentation::kCompressedSigned:
return machine()->ChangeCompressedSignedToTaggedSigned();
case MachineRepresentation::kCompressedPointer:
return machine()->ChangeCompressedPointerToTaggedPointer();
default:
UNREACHABLE();
}
}
const Operator* CompressionOpFromAccess(const ElementAccess access) {
switch (access.machine_type.representation()) {
case MachineRepresentation::kCompressed:
return machine()->ChangeTaggedToCompressed();
case MachineRepresentation::kCompressedSigned:
return machine()->ChangeTaggedSignedToCompressedSigned();
case MachineRepresentation::kCompressedPointer:
return machine()->ChangeTaggedPointerToCompressedPointer();
default:
UNREACHABLE();
}
}
// 'Global' accesses used to simplify the tests.
ElementAccess const any_access = {kTaggedBase, kTaggedSize, Type::Any(),
MachineType::AnyCompressed(),
kNoWriteBarrier};
ElementAccess const signed_access = {kTaggedBase, kTaggedSize, Type::Any(),
MachineType::CompressedSigned(),
kNoWriteBarrier};
ElementAccess const pointer_access = {kTaggedBase, kTaggedSize, Type::Any(),
MachineType::CompressedPointer(),
kNoWriteBarrier};
const ElementAccess element_accesses[3] = {any_access, signed_access,
pointer_access};
MachineOperatorBuilder* machine() { return &machine_; }
SimplifiedOperatorBuilder* simplified() { return &simplified_; }
private:
MachineOperatorBuilder machine_;
SimplifiedOperatorBuilder simplified_;
};
// -----------------------------------------------------------------------------
// Direct Decompression & Compression.
TEST_F(DecompressionEliminationTest, BasicDecompressionCompression) {
// Skip test if pointer compression is not enabled.
if (!COMPRESS_POINTERS_BOOL) {
return;
}
// Define variables.
Node* const control = graph()->start();
Node* object = Parameter(Type::Any(), 0);
Node* effect = graph()->start();
Node* index = Parameter(Type::UnsignedSmall(), 1);
// Pairs of <load, store> accesses
const std::pair<ElementAccess, ElementAccess> accesses[] = {
{any_access, any_access}, {signed_access, any_access},
{pointer_access, any_access}, {any_access, signed_access},
{signed_access, signed_access}, {any_access, pointer_access},
{pointer_access, pointer_access}};
for (size_t i = 0; i < arraysize(accesses); ++i) {
// Create the graph.
Node* load = graph()->NewNode(simplified()->LoadElement(accesses[i].first),
object, index, effect, control);
Node* change_to_tagged =
graph()->NewNode(DecompressionOpFromAccess(accesses[i].first), load);
Node* change_to_compressed = graph()->NewNode(
CompressionOpFromAccess(accesses[i].second), change_to_tagged);
effect =
graph()->NewNode(simplified()->StoreElement(accesses[i].second), object,
index, change_to_compressed, effect, control);
// Reduce.
Reduction r = Reduce(change_to_compressed);
ASSERT_TRUE(r.Changed());
EXPECT_EQ(load, r.replacement());
}
}
// -----------------------------------------------------------------------------
// Direct Compression & Decompression
TEST_F(DecompressionEliminationTest, BasicCompressionDecompression) {
// Skip test if pointer compression is not enabled.
if (!COMPRESS_POINTERS_BOOL) {
return;
}
// Define variables.
Node* const control = graph()->start();
Node* object = Parameter(Type::Any(), 0);
Node* effect = graph()->start();
Node* index = Parameter(Type::UnsignedSmall(), 1);
// Pairs of <load, store> accesses
const std::pair<ElementAccess, ElementAccess> accesses[] = {
{any_access, any_access}, {signed_access, any_access},
{pointer_access, any_access}, {any_access, signed_access},
{signed_access, signed_access}, {any_access, pointer_access},
{pointer_access, pointer_access}};
for (size_t i = 0; i < arraysize(accesses); ++i) {
// Create the graph.
Node* load = graph()->NewNode(simplified()->LoadElement(accesses[i].first),
object, index, effect, control);
Node* change_to_compressed =
graph()->NewNode(CompressionOpFromAccess(accesses[i].first), load);
Node* change_to_tagged = graph()->NewNode(
DecompressionOpFromAccess(accesses[i].second), change_to_compressed);
effect = graph()->NewNode(simplified()->StoreElement(accesses[i].second),
object, index, change_to_tagged, effect, control);
// Reduce.
Reduction r = Reduce(change_to_tagged);
ASSERT_TRUE(r.Changed());
EXPECT_EQ(load, r.replacement());
}
}
// -----------------------------------------------------------------------------
// Compress after constant.
TEST_F(DecompressionEliminationTest, CompressionAfterInt64Constant) {
// Skip test if pointer compression is not enabled.
if (!COMPRESS_POINTERS_BOOL) {
return;
}
// Define variables.
Node* const control = graph()->start();
Node* object = Parameter(Type::Any(), 0);
Node* effect = graph()->start();
Node* index = Parameter(Type::UnsignedSmall(), 1);
const int64_t constants[] = {static_cast<int64_t>(0x0000000000000000),
static_cast<int64_t>(0x0000000000000001),
static_cast<int64_t>(0x0000FFFFFFFF0000),
static_cast<int64_t>(0x7FFFFFFFFFFFFFFF),
static_cast<int64_t>(0x8000000000000000),
static_cast<int64_t>(0x8000000000000001),
static_cast<int64_t>(0x8000FFFFFFFF0000),
static_cast<int64_t>(0x8FFFFFFFFFFFFFFF),
static_cast<int64_t>(0xFFFFFFFFFFFFFFFF)};
// For every access.
for (size_t i = 0; i < arraysize(element_accesses); ++i) {
// For every Int64Constant.
for (size_t j = 0; j < arraysize(constants); ++j) {
// Create the graph.
Node* constant = graph()->NewNode(common()->Int64Constant(constants[j]));
Node* change_to_compressed = graph()->NewNode(
CompressionOpFromAccess(element_accesses[i]), constant);
effect = graph()->NewNode(simplified()->StoreElement(element_accesses[i]),
object, index, change_to_compressed, effect,
control);
// Reduce.
Reduction r = Reduce(change_to_compressed);
ASSERT_TRUE(r.Changed());
EXPECT_EQ(r.replacement()->opcode(), IrOpcode::kInt32Constant);
}
}
}
TEST_F(DecompressionEliminationTest, CompressionAfterHeapConstant) {
// Skip test if pointer compression is not enabled.
if (!COMPRESS_POINTERS_BOOL) {
return;
}
// Define variables.
Node* const control = graph()->start();
Node* object = Parameter(Type::Any(), 0);
Node* effect = graph()->start();
Node* index = Parameter(Type::UnsignedSmall(), 1);
const Handle<HeapNumber> heap_constants[] = {
factory()->NewHeapNumber(0.0),
factory()->NewHeapNumber(-0.0),
factory()->NewHeapNumber(11.2),
factory()->NewHeapNumber(-11.2),
factory()->NewHeapNumber(3.1415 + 1.4142),
factory()->NewHeapNumber(3.1415 - 1.4142),
factory()->NewHeapNumber(0x0000000000000000),
factory()->NewHeapNumber(0x0000000000000001),
factory()->NewHeapNumber(0x0000FFFFFFFF0000),
factory()->NewHeapNumber(0x7FFFFFFFFFFFFFFF),
factory()->NewHeapNumber(0x8000000000000000),
factory()->NewHeapNumber(0x8000000000000001),
factory()->NewHeapNumber(0x8000FFFFFFFF0000),
factory()->NewHeapNumber(0x8FFFFFFFFFFFFFFF),
factory()->NewHeapNumber(0xFFFFFFFFFFFFFFFF)};
// For every access.
for (size_t i = 0; i < arraysize(element_accesses); ++i) {
// For every HeapNumber.
for (size_t j = 0; j < arraysize(heap_constants); ++j) {
// Create the graph.
Node* constant =
graph()->NewNode(common()->HeapConstant(heap_constants[j]));
Node* change_to_compressed = graph()->NewNode(
CompressionOpFromAccess(element_accesses[i]), constant);
effect = graph()->NewNode(simplified()->StoreElement(element_accesses[i]),
object, index, change_to_compressed, effect,
control);
// Reduce.
Reduction r = Reduce(change_to_compressed);
ASSERT_TRUE(r.Changed());
EXPECT_EQ(r.replacement()->opcode(), IrOpcode::kCompressedHeapConstant);
}
}
}
// -----------------------------------------------------------------------------
// Phi.
TEST_F(DecompressionEliminationTest, PhiOneDecompress) {
// Skip test if pointer compression is not enabled.
if (!COMPRESS_POINTERS_BOOL) {
return;
}
// Define variables.
Node* const control = graph()->start();
Node* object = Parameter(Type::Any(), 0);
Node* effect = graph()->start();
Node* index = Parameter(Type::UnsignedSmall(), 1);
const int number_of_inputs = 1;
// For every access.
for (size_t i = 0; i < arraysize(element_accesses); ++i) {
// Create the graph.
Node* load =
graph()->NewNode(simplified()->LoadElement(element_accesses[i]), object,
index, effect, control);
Node* change_to_tagged =
graph()->NewNode(DecompressionOpFromAccess(element_accesses[i]), load);
Node* phi = graph()->NewNode(
common()->Phi(MachineRepresentation::kTagged, number_of_inputs),
change_to_tagged, control);
// Reduce.
StrictMock<MockAdvancedReducerEditor> editor;
EXPECT_CALL(editor, ReplaceWithValue(phi, _, _, _));
Reduction r = Reduce(&editor, phi);
ASSERT_TRUE(r.Changed());
// Get the actual decompress after the Phi, and check against the expected
// one.
Node* decompress = GetUniqueValueUse(phi);
EXPECT_EQ(DecompressionOpFromAccess(element_accesses[i]), decompress->op());
}
}
TEST_F(DecompressionEliminationTest, PhiThreeDecompressSameRepresentation) {
// Skip test if pointer compression is not enabled.
if (!COMPRESS_POINTERS_BOOL) {
return;
}
// Define variables.
Node* const control = graph()->start();
Node* object = Parameter(Type::Any(), 0);
Node* effect = graph()->start();
Node* index = Parameter(Type::UnsignedSmall(), 1);
const int number_of_inputs = 3;
// For every access.
for (size_t i = 0; i < arraysize(element_accesses); ++i) {
// Create the graph.
Node* load1 =
graph()->NewNode(simplified()->LoadElement(element_accesses[i]), object,
index, effect, control);
Node* load2 =
graph()->NewNode(simplified()->LoadElement(element_accesses[i]), object,
index, effect, control);
Node* load3 =
graph()->NewNode(simplified()->LoadElement(element_accesses[i]), object,
index, effect, control);
Node* change_to_tagged_1 =
graph()->NewNode(DecompressionOpFromAccess(element_accesses[i]), load1);
Node* change_to_tagged_2 =
graph()->NewNode(DecompressionOpFromAccess(element_accesses[i]), load2);
Node* change_to_tagged_3 =
graph()->NewNode(DecompressionOpFromAccess(element_accesses[i]), load3);
Node* phi = graph()->NewNode(
common()->Phi(MachineRepresentation::kTagged, number_of_inputs),
change_to_tagged_1, change_to_tagged_2, change_to_tagged_3, control);
// Reduce.
StrictMock<MockAdvancedReducerEditor> editor;
EXPECT_CALL(editor, ReplaceWithValue(phi, _, _, _));
Reduction r = Reduce(&editor, phi);
ASSERT_TRUE(r.Changed());
// Get the actual decompress after the Phi, and check against the expected
// one.
Node* decompress = GetUniqueValueUse(phi);
EXPECT_EQ(DecompressionOpFromAccess(element_accesses[i]), decompress->op());
}
}
TEST_F(DecompressionEliminationTest, PhiThreeDecompressOneAnyRepresentation) {
// Skip test if pointer compression is not enabled.
if (!COMPRESS_POINTERS_BOOL) {
return;
}
// Define variables.
Node* const control = graph()->start();
Node* object = Parameter(Type::Any(), 0);
Node* effect = graph()->start();
Node* index = Parameter(Type::UnsignedSmall(), 1);
const int number_of_inputs = 3;
// Signed and Pointer (and not Any) accesses.
const ElementAccess not_any_accesses[] = {signed_access, pointer_access};
// For every access.
for (size_t i = 0; i < arraysize(not_any_accesses); ++i) {
// Create the graph.
Node* load1 =
graph()->NewNode(simplified()->LoadElement(not_any_accesses[i]), object,
index, effect, control);
Node* load2 =
graph()->NewNode(simplified()->LoadElement(not_any_accesses[i]), object,
index, effect, control);
// Note that load3 loads a CompressedAny instead of not_any_accesses[i]
Node* load3 = graph()->NewNode(simplified()->LoadElement(any_access),
object, index, effect, control);
Node* change_to_tagged_1 =
graph()->NewNode(DecompressionOpFromAccess(not_any_accesses[i]), load1);
Node* change_to_tagged_2 =
graph()->NewNode(DecompressionOpFromAccess(not_any_accesses[i]), load2);
Node* change_to_tagged_3 =
graph()->NewNode(machine()->ChangeCompressedToTagged(), load3);
Node* phi = graph()->NewNode(
common()->Phi(MachineRepresentation::kTagged, number_of_inputs),
change_to_tagged_1, change_to_tagged_2, change_to_tagged_3, control);
// Reduce.
StrictMock<MockAdvancedReducerEditor> editor;
EXPECT_CALL(editor, ReplaceWithValue(phi, _, _, _));
Reduction r = Reduce(&editor, phi);
ASSERT_TRUE(r.Changed());
// Get the actual decompress after the Phi, and check against the expected
// one.
Node* decompress = GetUniqueValueUse(phi);
EXPECT_EQ(machine()->ChangeCompressedToTagged(), decompress->op());
}
}
TEST_F(DecompressionEliminationTest, PhiThreeInputsOneNotDecompressed) {
// Skip test if pointer compression is not enabled.
if (!COMPRESS_POINTERS_BOOL) {
return;
}
// Define variables.
Node* const control = graph()->start();
Node* object = Parameter(Type::Any(), 0);
Node* effect = graph()->start();
Node* index = Parameter(Type::UnsignedSmall(), 1);
const int number_of_inputs = 3;
// For every access.
for (size_t i = 0; i < arraysize(element_accesses); ++i) {
// Create the graph.
Node* load1 =
graph()->NewNode(simplified()->LoadElement(element_accesses[i]), object,
index, effect, control);
Node* load2 =
graph()->NewNode(simplified()->LoadElement(element_accesses[i]), object,
index, effect, control);
Node* load3 =
graph()->NewNode(simplified()->LoadElement(element_accesses[i]), object,
index, effect, control);
Node* change_to_tagged_1 =
graph()->NewNode(DecompressionOpFromAccess(element_accesses[i]), load1);
Node* change_to_tagged_2 =
graph()->NewNode(DecompressionOpFromAccess(element_accesses[i]), load2);
Node* phi = graph()->NewNode(
common()->Phi(MachineRepresentation::kTagged, number_of_inputs),
change_to_tagged_1, change_to_tagged_2, load3, control);
// Reduce.
Reduction r = Reduce(phi);
ASSERT_FALSE(r.Changed());
}
}
// In the case of having one decompress Signed and one Pointer, we have to
// generate the conservative decompress any after the Phi.
TEST_F(DecompressionEliminationTest, PhiTwoDecompressesOneSignedOnePointer) {
// Skip test if pointer compression is not enabled.
if (!COMPRESS_POINTERS_BOOL) {
return;
}
// Define variables.
Node* const control = graph()->start();
Node* object = Parameter(Type::Any(), 0);
Node* effect = graph()->start();
Node* index = Parameter(Type::UnsignedSmall(), 1);
const int number_of_inputs = 2;
// Create the graph.
Node* load1 = graph()->NewNode(simplified()->LoadElement(signed_access),
object, index, effect, control);
Node* load2 = graph()->NewNode(simplified()->LoadElement(pointer_access),
object, index, effect, control);
Node* change_to_tagged_1 =
graph()->NewNode(DecompressionOpFromAccess(signed_access), load1);
Node* change_to_tagged_2 =
graph()->NewNode(DecompressionOpFromAccess(pointer_access), load2);
Node* phi = graph()->NewNode(
common()->Phi(MachineRepresentation::kTagged, number_of_inputs),
change_to_tagged_1, change_to_tagged_2, control);
// Reduce.
StrictMock<MockAdvancedReducerEditor> editor;
EXPECT_CALL(editor, ReplaceWithValue(phi, _, _, _));
Reduction r = Reduce(&editor, phi);
ASSERT_TRUE(r.Changed());
// Get the actual decompress after the Phi, and check against the expected
// one.
Node* decompress = GetUniqueValueUse(phi);
EXPECT_EQ(machine()->ChangeCompressedToTagged(), decompress->op());
}
// -----------------------------------------------------------------------------
// TypedStateValues.
TEST_F(DecompressionEliminationTest, TypedStateValuesOneDecompress) {
// Skip test if pointer compression is not enabled.
if (!COMPRESS_POINTERS_BOOL) {
return;
}
// Define variables.
Node* const control = graph()->start();
Node* object = Parameter(Type::Any(), 0);
Node* effect = graph()->start();
Node* index = Parameter(Type::UnsignedSmall(), 1);
const int number_of_inputs = 1;
const ZoneVector<MachineType>* types =
new (graph()->zone()->New(sizeof(ZoneVector<MachineType>)))
ZoneVector<MachineType>(number_of_inputs, graph()->zone());
SparseInputMask dense = SparseInputMask::Dense();
// For every access.
for (size_t i = 0; i < arraysize(element_accesses); ++i) {
// Create the graph.
Node* load =
graph()->NewNode(simplified()->LoadElement(element_accesses[i]), object,
index, effect, control);
Node* change_to_tagged =
graph()->NewNode(DecompressionOpFromAccess(element_accesses[i]), load);
Node* typedStateValues = graph()->NewNode(
common()->TypedStateValues(types, dense), change_to_tagged);
// Reduce.
StrictMock<MockAdvancedReducerEditor> editor;
DecompressionElimination decompression_elimination(&editor, graph(),
machine(), common());
Reduction r = decompression_elimination.Reduce(typedStateValues);
ASSERT_TRUE(r.Changed());
EXPECT_EQ(r.replacement()->InputAt(0), load);
}
}
TEST_F(DecompressionEliminationTest, TypedStateValuesTwoDecompresses) {
// Skip test if pointer compression is not enabled.
if (!COMPRESS_POINTERS_BOOL) {
return;
}
// Define variables.
Node* const control = graph()->start();
Node* object = Parameter(Type::Any(), 0);
Node* effect = graph()->start();
Node* index = Parameter(Type::UnsignedSmall(), 1);
const int number_of_inputs = 3;
const ZoneVector<MachineType>* types =
new (graph()->zone()->New(sizeof(ZoneVector<MachineType>)))
ZoneVector<MachineType>(number_of_inputs, graph()->zone());
SparseInputMask dense = SparseInputMask::Dense();
// For every access.
for (size_t i = 0; i < arraysize(element_accesses); ++i) {
// Create the graph.
Node* load1 =
graph()->NewNode(simplified()->LoadElement(element_accesses[i]), object,
index, effect, control);
Node* change_to_tagged_1 =
graph()->NewNode(DecompressionOpFromAccess(element_accesses[i]), load1);
Node* load2 =
graph()->NewNode(simplified()->LoadElement(element_accesses[i]), object,
index, effect, control);
Node* change_to_tagged_2 =
graph()->NewNode(DecompressionOpFromAccess(element_accesses[i]), load2);
Node* typedStateValues =
graph()->NewNode(common()->TypedStateValues(types, dense),
change_to_tagged_1, load1, change_to_tagged_2);
// Reduce.
StrictMock<MockAdvancedReducerEditor> editor;
DecompressionElimination decompression_elimination(&editor, graph(),
machine(), common());
Reduction r = decompression_elimination.Reduce(typedStateValues);
ASSERT_TRUE(r.Changed());
EXPECT_EQ(r.replacement()->InputAt(0), load1);
// Note that the input at index 1 didn't change.
EXPECT_EQ(r.replacement()->InputAt(1), load1);
EXPECT_EQ(r.replacement()->InputAt(2), load2);
}
}
TEST_F(DecompressionEliminationTest, TypedStateValuesAllDecompresses) {
// Skip test if pointer compression is not enabled.
if (!COMPRESS_POINTERS_BOOL) {
return;
}
// Define variables.
Node* const control = graph()->start();
Node* object = Parameter(Type::Any(), 0);
Node* effect = graph()->start();
Node* index = Parameter(Type::UnsignedSmall(), 1);
const int number_of_inputs = 3;
const ZoneVector<MachineType>* types =
new (graph()->zone()->New(sizeof(ZoneVector<MachineType>)))
ZoneVector<MachineType>(number_of_inputs, graph()->zone());
SparseInputMask dense = SparseInputMask::Dense();
// For every access.
for (size_t i = 0; i < arraysize(element_accesses); ++i) {
// Create the graph.
Node* load1 =
graph()->NewNode(simplified()->LoadElement(element_accesses[i]), object,
index, effect, control);
Node* change_to_tagged_1 =
graph()->NewNode(DecompressionOpFromAccess(element_accesses[i]), load1);
Node* load2 =
graph()->NewNode(simplified()->LoadElement(element_accesses[i]), object,
index, effect, control);
Node* change_to_tagged_2 =
graph()->NewNode(DecompressionOpFromAccess(element_accesses[i]), load2);
Node* load3 =
graph()->NewNode(simplified()->LoadElement(element_accesses[i]), object,
index, effect, control);
Node* change_to_tagged_3 =
graph()->NewNode(DecompressionOpFromAccess(element_accesses[i]), load3);
Node* typedStateValues = graph()->NewNode(
common()->TypedStateValues(types, dense), change_to_tagged_1,
change_to_tagged_2, change_to_tagged_3);
// Reduce.
StrictMock<MockAdvancedReducerEditor> editor;
DecompressionElimination decompression_elimination(&editor, graph(),
machine(), common());
Reduction r = decompression_elimination.Reduce(typedStateValues);
ASSERT_TRUE(r.Changed());
EXPECT_EQ(r.replacement()->InputAt(0), load1);
EXPECT_EQ(r.replacement()->InputAt(1), load2);
EXPECT_EQ(r.replacement()->InputAt(2), load3);
}
}
TEST_F(DecompressionEliminationTest, TypedStateValuesNoDecompresses) {
// Skip test if pointer compression is not enabled.
if (!COMPRESS_POINTERS_BOOL) {
return;
}
// Define variables.
Node* const control = graph()->start();
Node* object = Parameter(Type::Any(), 0);
Node* effect = graph()->start();
Node* index = Parameter(Type::UnsignedSmall(), 1);
const int number_of_inputs = 3;
const ZoneVector<MachineType>* types =
new (graph()->zone()->New(sizeof(ZoneVector<MachineType>)))
ZoneVector<MachineType>(number_of_inputs, graph()->zone());
SparseInputMask dense = SparseInputMask::Dense();
// For every access.
for (size_t i = 0; i < arraysize(element_accesses); ++i) {
// Create the graph.
Node* load =
graph()->NewNode(simplified()->LoadElement(element_accesses[i]), object,
index, effect, control);
Node* typedStateValues = graph()->NewNode(
common()->TypedStateValues(types, dense), load, load, load);
// Reduce.
StrictMock<MockAdvancedReducerEditor> editor;
DecompressionElimination decompression_elimination(&editor, graph(),
machine(), common());
Reduction r = decompression_elimination.Reduce(typedStateValues);
ASSERT_FALSE(r.Changed());
}
}
// -----------------------------------------------------------------------------
// Word64Equal comparison of two decompressions.
TEST_F(DecompressionEliminationTest, TwoDecompressionWord64Equal) {
// Skip test if pointer compression is not enabled.
if (!COMPRESS_POINTERS_BOOL) {
return;
}
// Define variables.
Node* const control = graph()->start();
Node* object = Parameter(Type::Any(), 0);
Node* effect = graph()->start();
Node* index = Parameter(Type::UnsignedSmall(), 1);
// For every decompression (lhs).
for (size_t i = 0; i < arraysize(element_accesses); ++i) {
// For every decompression (rhs)
for (size_t j = 0; j < arraysize(element_accesses); ++j) {
// Create the graph.
Node* load1 =
graph()->NewNode(simplified()->LoadElement(element_accesses[i]),
object, index, effect, control);
Node* change_to_tagged_1 = graph()->NewNode(
DecompressionOpFromAccess(element_accesses[i]), load1);
Node* load2 =
graph()->NewNode(simplified()->LoadElement(element_accesses[j]),
object, index, effect, control);
Node* change_to_tagged_2 = graph()->NewNode(
DecompressionOpFromAccess(element_accesses[i]), load2);
Node* comparison = graph()->NewNode(
machine()->Word64Equal(), change_to_tagged_1, change_to_tagged_2);
// Reduce.
Reduction r = Reduce(comparison);
ASSERT_TRUE(r.Changed());
EXPECT_EQ(r.replacement()->opcode(), IrOpcode::kWord32Equal);
}
}
}
// -----------------------------------------------------------------------------
// Word64Equal comparison of two decompressions, where lhs == rhs.
TEST_F(DecompressionEliminationTest, TwoDecompressionWord64EqualSameInput) {
// Skip test if pointer compression is not enabled.
if (!COMPRESS_POINTERS_BOOL) {
return;
}
// Define variables.
Node* const control = graph()->start();
Node* object = Parameter(Type::Any(), 0);
Node* effect = graph()->start();
Node* index = Parameter(Type::UnsignedSmall(), 1);
// For every access. (same for lhs and rhs)
for (size_t i = 0; i < arraysize(element_accesses); ++i) {
// Create the graph.
Node* load =
graph()->NewNode(simplified()->LoadElement(element_accesses[i]), object,
index, effect, control);
Node* change_to_tagged =
graph()->NewNode(DecompressionOpFromAccess(element_accesses[i]), load);
Node* comparison = graph()->NewNode(machine()->Word64Equal(),
change_to_tagged, change_to_tagged);
// Reduce.
Reduction r = Reduce(comparison);
ASSERT_TRUE(r.Changed());
EXPECT_EQ(r.replacement()->opcode(), IrOpcode::kWord32Equal);
}
}
// -----------------------------------------------------------------------------
// Word64Equal comparison of decompress and a constant.
TEST_F(DecompressionEliminationTest, DecompressionConstantWord64Equal) {
// Skip test if pointer compression is not enabled.
if (!COMPRESS_POINTERS_BOOL) {
return;
}
// Define variables.
Node* const control = graph()->start();
Node* object = Parameter(Type::Any(), 0);
Node* effect = graph()->start();
Node* index = Parameter(Type::UnsignedSmall(), 1);
const int64_t constants[] = {static_cast<int64_t>(0x0000000000000000),
static_cast<int64_t>(0x0000000000000001),
static_cast<int64_t>(0x0000FFFFFFFF0000),
static_cast<int64_t>(0x7FFFFFFFFFFFFFFF),
static_cast<int64_t>(0x8000000000000000),
static_cast<int64_t>(0x8000000000000001),
static_cast<int64_t>(0x8000FFFFFFFF0000),
static_cast<int64_t>(0x8FFFFFFFFFFFFFFF),
static_cast<int64_t>(0xFFFFFFFFFFFFFFFF)};
// For every decompression (lhs).
for (size_t i = 0; i < arraysize(element_accesses); ++i) {
// For every constant (rhs).
for (size_t j = 0; j < arraysize(constants); ++j) {
// Test with both (lhs, rhs) combinations.
for (bool lhs_is_decompression : {false, true}) {
// Create the graph.
Node* load =
graph()->NewNode(simplified()->LoadElement(element_accesses[i]),
object, index, effect, control);
Node* change_to_tagged = graph()->NewNode(
DecompressionOpFromAccess(element_accesses[i]), load);
Node* constant =
graph()->NewNode(common()->Int64Constant(constants[j]));
Node* lhs = lhs_is_decompression ? change_to_tagged : constant;
Node* rhs = lhs_is_decompression ? constant : change_to_tagged;
Node* comparison = graph()->NewNode(machine()->Word64Equal(), lhs, rhs);
// Reduce.
Reduction r = Reduce(comparison);
ASSERT_TRUE(r.Changed());
EXPECT_EQ(r.replacement()->opcode(), IrOpcode::kWord32Equal);
}
}
}
}
TEST_F(DecompressionEliminationTest, DecompressionHeapConstantWord64Equal) {
// Skip test if pointer compression is not enabled.
if (!COMPRESS_POINTERS_BOOL) {
return;
}
// Define variables.
Node* const control = graph()->start();
Node* object = Parameter(Type::Any(), 0);
Node* effect = graph()->start();
Node* index = Parameter(Type::UnsignedSmall(), 1);
const Handle<HeapNumber> heap_constants[] = {
factory()->NewHeapNumber(0.0),
factory()->NewHeapNumber(-0.0),
factory()->NewHeapNumber(11.2),
factory()->NewHeapNumber(-11.2),
factory()->NewHeapNumber(3.1415 + 1.4142),
factory()->NewHeapNumber(3.1415 - 1.4142),
factory()->NewHeapNumber(0x0000000000000000),
factory()->NewHeapNumber(0x0000000000000001),
factory()->NewHeapNumber(0x0000FFFFFFFF0000),
factory()->NewHeapNumber(0x7FFFFFFFFFFFFFFF),
factory()->NewHeapNumber(0x8000000000000000),
factory()->NewHeapNumber(0x8000000000000001),
factory()->NewHeapNumber(0x8000FFFFFFFF0000),
factory()->NewHeapNumber(0x8FFFFFFFFFFFFFFF),
factory()->NewHeapNumber(0xFFFFFFFFFFFFFFFF)};
// For every decompression (lhs).
for (size_t i = 0; i < arraysize(element_accesses); ++i) {
// For every constant (rhs).
for (size_t j = 0; j < arraysize(heap_constants); ++j) {
// Test with both (lhs, rhs) combinations.
for (bool lhs_is_decompression : {false, true}) {
// Create the graph.
Node* load =
graph()->NewNode(simplified()->LoadElement(element_accesses[i]),
object, index, effect, control);
Node* change_to_tagged = graph()->NewNode(
DecompressionOpFromAccess(element_accesses[i]), load);
Node* constant =
graph()->NewNode(common()->HeapConstant(heap_constants[j]));
Node* lhs = lhs_is_decompression ? change_to_tagged : constant;
Node* rhs = lhs_is_decompression ? constant : change_to_tagged;
Node* comparison = graph()->NewNode(machine()->Word64Equal(), lhs, rhs);
// Reduce.
Reduction r = Reduce(comparison);
ASSERT_TRUE(r.Changed());
EXPECT_EQ(r.replacement()->opcode(), IrOpcode::kWord32Equal);
}
}
}
}
} // namespace compiler
} // namespace internal
} // namespace v8