v8/test/unittests/compiler/decompression-elimination-unittest.cc
Georg Schmid 389c2e3ccc [ptr-compr] Extend Decompression Elimination to Compress/Decompress pairs
We previously only optimized cases like

  Parent <- Decompression <- Compression <- Child

to

  Parent <- Child

This CL also adds the complementary optimization, namely, it reduces

  Parent <- Compression <- Decompression <- Child

as above.

Such a cases became apparent after a recent extension of CSA load elimination (see https://chromium-review.googlesource.com/c/v8/v8/+/1660626), breaking a load elimination test case and thus the pointer compression build.

R=jarin@chromium.org, solanes@chromium.org

Change-Id: Ic730d05175f214e7055f94704141744ca44fefe5
Bug: v8:9353
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1664070
Commit-Queue: Georg Schmid <gsps@google.com>
Reviewed-by: Michael Stanton <mvstanton@chromium.org>
Cr-Commit-Position: refs/heads/master@{#62246}
2019-06-18 14:30:04 +00:00

1152 lines
45 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;
}
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);
ElementAccess const access = {kTaggedBase, kTaggedSize, Type::Any(),
MachineType::AnyTagged(), kNoWriteBarrier};
// Create the graph
Node* load = graph()->NewNode(simplified()->LoadElement(access), object,
index, effect, control);
Node* changeToTagged =
graph()->NewNode(machine()->ChangeCompressedToTagged(), load);
Node* changeToCompressed =
graph()->NewNode(machine()->ChangeTaggedToCompressed(), changeToTagged);
effect = graph()->NewNode(simplified()->StoreElement(access), object, index,
changeToCompressed, effect, control);
// Reduce
Reduction r = Reduce(changeToCompressed);
ASSERT_TRUE(r.Changed());
EXPECT_EQ(load, r.replacement());
}
TEST_F(DecompressionEliminationTest, BasicDecompressionCompressionSigned) {
// 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);
ElementAccess const access = {kTaggedBase, kTaggedSize, Type::Any(),
MachineType::TaggedSigned(), kNoWriteBarrier};
// Create the graph
Node* load = graph()->NewNode(simplified()->LoadElement(access), object,
index, effect, control);
Node* changeToTagged =
graph()->NewNode(machine()->ChangeCompressedSignedToTaggedSigned(), load);
Node* changeToCompressed = graph()->NewNode(
machine()->ChangeTaggedSignedToCompressedSigned(), changeToTagged);
effect = graph()->NewNode(simplified()->StoreElement(access), object, index,
changeToCompressed, effect, control);
// Reduce
Reduction r = Reduce(changeToCompressed);
ASSERT_TRUE(r.Changed());
EXPECT_EQ(load, r.replacement());
}
TEST_F(DecompressionEliminationTest, BasicDecompressionCompressionPointer) {
// 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);
ElementAccess const access = {kTaggedBase, kTaggedSize, Type::Any(),
MachineType::TaggedPointer(), kNoWriteBarrier};
// Create the graph
Node* load = graph()->NewNode(simplified()->LoadElement(access), object,
index, effect, control);
Node* changeToTagged = graph()->NewNode(
machine()->ChangeCompressedPointerToTaggedPointer(), load);
Node* changeToCompressed = graph()->NewNode(
machine()->ChangeTaggedPointerToCompressedPointer(), changeToTagged);
effect = graph()->NewNode(simplified()->StoreElement(access), object, index,
changeToCompressed, effect, control);
// Reduce
Reduction r = Reduce(changeToCompressed);
ASSERT_TRUE(r.Changed());
EXPECT_EQ(load, r.replacement());
}
// -----------------------------------------------------------------------------
// Direct Decompression & Compression - border cases
// For example, if we are lowering a CheckedCompressedToTaggedPointer in the
// effect linearization phase we will change that to
// ChangeCompressedPointerToTaggedPointer. Then, we might end up with a chain of
// Parent <- ChangeCompressedPointerToTaggedPointer <- ChangeTaggedToCompressed
// <- Child.
// Similarly, we have cases with Signed instead of pointer.
// The following border case tests will test that the functionality is robust
// enough to handle that.
TEST_F(DecompressionEliminationTest,
BasicDecompressionCompressionBorderCaseSigned) {
// 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);
ElementAccess const loadAccess = {kTaggedBase, kTaggedSize, Type::Any(),
MachineType::AnyTagged(), kNoWriteBarrier};
ElementAccess const storeAccess = {kTaggedBase, kTaggedSize, Type::Any(),
MachineType::TaggedSigned(),
kNoWriteBarrier};
// Create the graph
Node* load = graph()->NewNode(simplified()->LoadElement(loadAccess), object,
index, effect, control);
Node* changeToTagged =
graph()->NewNode(machine()->ChangeCompressedSignedToTaggedSigned(), load);
Node* changeToCompressed =
graph()->NewNode(machine()->ChangeTaggedToCompressed(), changeToTagged);
effect = graph()->NewNode(simplified()->StoreElement(storeAccess), object,
index, changeToCompressed, effect, control);
// Reduce
Reduction r = Reduce(changeToCompressed);
ASSERT_TRUE(r.Changed());
EXPECT_EQ(load, r.replacement());
}
TEST_F(DecompressionEliminationTest,
BasicDecompressionCompressionBorderCasePointer) {
// 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);
ElementAccess const loadAccess = {kTaggedBase, kTaggedSize, Type::Any(),
MachineType::AnyTagged(), kNoWriteBarrier};
ElementAccess const storeAccess = {kTaggedBase, kTaggedSize, Type::Any(),
MachineType::TaggedPointer(),
kNoWriteBarrier};
// Create the graph
Node* load = graph()->NewNode(simplified()->LoadElement(loadAccess), object,
index, effect, control);
Node* changeToTagged = graph()->NewNode(
machine()->ChangeCompressedPointerToTaggedPointer(), load);
Node* changeToCompressed =
graph()->NewNode(machine()->ChangeTaggedToCompressed(), changeToTagged);
effect = graph()->NewNode(simplified()->StoreElement(storeAccess), object,
index, changeToCompressed, effect, control);
// Reduce
Reduction r = Reduce(changeToCompressed);
ASSERT_TRUE(r.Changed());
EXPECT_EQ(load, r.replacement());
}
// We also have cases of ChangeCompressedToTagged <-
// ChangeTaggedPointerToCompressedPointer, where the
// ChangeTaggedPointerToCompressedPointer was introduced while lowering a
// NewConsString on effect control linearizer
TEST_F(DecompressionEliminationTest,
BasicDecompressionCompressionBorderCasePointerDecompression) {
// 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);
ElementAccess const loadAccess = {kTaggedBase, kTaggedSize, Type::Any(),
MachineType::TaggedPointer(),
kNoWriteBarrier};
ElementAccess const storeAccess = {kTaggedBase, kTaggedSize, Type::Any(),
MachineType::AnyTagged(), kNoWriteBarrier};
// Create the graph
Node* load = graph()->NewNode(simplified()->LoadElement(loadAccess), object,
index, effect, control);
Node* changeToTagged = graph()->NewNode(
machine()->ChangeCompressedPointerToTaggedPointer(), load);
Node* changeToCompressed =
graph()->NewNode(machine()->ChangeTaggedToCompressed(), changeToTagged);
effect = graph()->NewNode(simplified()->StoreElement(storeAccess), object,
index, changeToCompressed, effect, control);
// Reduce
Reduction r = Reduce(changeToCompressed);
ASSERT_TRUE(r.Changed());
EXPECT_EQ(load, r.replacement());
}
// -----------------------------------------------------------------------------
// Compress after constant
TEST_F(DecompressionEliminationTest,
DecompressionConstantStoreElementInt64Constant) {
// 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 ElementAccess element_accesses[] = {
{kTaggedBase, kTaggedSize, Type::Any(), MachineType::AnyCompressed(),
kNoWriteBarrier},
{kTaggedBase, kTaggedSize, Type::Any(), MachineType::CompressedSigned(),
kNoWriteBarrier},
{kTaggedBase, kTaggedSize, Type::Any(), MachineType::CompressedPointer(),
kNoWriteBarrier}};
const Operator* compression_ops[] = {
machine()->ChangeTaggedToCompressed(),
machine()->ChangeTaggedSignedToCompressedSigned(),
machine()->ChangeTaggedPointerToCompressedPointer()};
ASSERT_EQ(arraysize(compression_ops), arraysize(element_accesses));
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 compression.
for (size_t i = 0; i < arraysize(compression_ops); ++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* changeToCompressed = graph()->NewNode(compression_ops[i], constant);
effect =
graph()->NewNode(simplified()->StoreElement(element_accesses[i]),
object, index, changeToCompressed, effect, control);
// Reduce.
Reduction r = Reduce(changeToCompressed);
ASSERT_TRUE(r.Changed());
EXPECT_EQ(r.replacement()->opcode(), IrOpcode::kInt32Constant);
}
}
}
TEST_F(DecompressionEliminationTest,
DecompressionConstantStoreElementHeapConstant) {
// 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 ElementAccess element_accesses[] = {
{kTaggedBase, kTaggedSize, Type::Any(), MachineType::AnyCompressed(),
kNoWriteBarrier},
{kTaggedBase, kTaggedSize, Type::Any(), MachineType::CompressedSigned(),
kNoWriteBarrier},
{kTaggedBase, kTaggedSize, Type::Any(), MachineType::CompressedPointer(),
kNoWriteBarrier}};
const Operator* compression_ops[] = {
machine()->ChangeTaggedToCompressed(),
machine()->ChangeTaggedSignedToCompressedSigned(),
machine()->ChangeTaggedPointerToCompressedPointer()};
ASSERT_EQ(arraysize(compression_ops), arraysize(element_accesses));
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 compression.
for (size_t i = 0; i < arraysize(compression_ops); ++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* changeToCompressed = graph()->NewNode(compression_ops[i], constant);
effect =
graph()->NewNode(simplified()->StoreElement(element_accesses[i]),
object, index, changeToCompressed, effect, control);
// Reduce.
Reduction r = Reduce(changeToCompressed);
ASSERT_TRUE(r.Changed());
EXPECT_EQ(r.replacement()->opcode(), IrOpcode::kCompressedHeapConstant);
}
}
}
// -----------------------------------------------------------------------------
// 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);
ElementAccess const access = {kTaggedBase, kTaggedSize, Type::Any(),
MachineType::AnyTagged(), kNoWriteBarrier};
// Create the graph
Node* load = graph()->NewNode(simplified()->LoadElement(access), object,
index, effect, control);
Node* changeToCompressed =
graph()->NewNode(machine()->ChangeTaggedToCompressed(), load);
Node* changeToTagged = graph()->NewNode(machine()->ChangeCompressedToTagged(),
changeToCompressed);
effect = graph()->NewNode(simplified()->StoreElement(access), object, index,
changeToTagged, effect, control);
// Reduce
Reduction r = Reduce(changeToTagged);
ASSERT_TRUE(r.Changed());
EXPECT_EQ(load, r.replacement());
}
// -----------------------------------------------------------------------------
// 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;
const Operator* decompression_ops[] = {
machine()->ChangeCompressedToTagged(),
machine()->ChangeCompressedSignedToTaggedSigned(),
machine()->ChangeCompressedPointerToTaggedPointer()};
const ElementAccess element_accesses[] = {
{kTaggedBase, kTaggedSize, Type::Any(), MachineType::AnyCompressed(),
kNoWriteBarrier},
{kTaggedBase, kTaggedSize, Type::Any(), MachineType::TaggedSigned(),
kNoWriteBarrier},
{kTaggedBase, kTaggedSize, Type::Any(), MachineType::TaggedPointer(),
kNoWriteBarrier}};
const IrOpcode::Value opcodes[] = {
IrOpcode::kChangeCompressedToTagged,
IrOpcode::kChangeCompressedSignedToTaggedSigned,
IrOpcode::kChangeCompressedPointerToTaggedPointer};
ASSERT_EQ(arraysize(decompression_ops), arraysize(element_accesses));
ASSERT_EQ(arraysize(opcodes), arraysize(element_accesses));
// 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(decompression_ops[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());
Node* decompress = GetUniqueValueUse(phi);
EXPECT_NE(nullptr, decompress);
EXPECT_EQ(opcodes[i], decompress->opcode());
}
}
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;
const Operator* decompression_ops[] = {
machine()->ChangeCompressedToTagged(),
machine()->ChangeCompressedSignedToTaggedSigned(),
machine()->ChangeCompressedPointerToTaggedPointer()};
const ElementAccess element_accesses[] = {
{kTaggedBase, kTaggedSize, Type::Any(), MachineType::AnyCompressed(),
kNoWriteBarrier},
{kTaggedBase, kTaggedSize, Type::Any(), MachineType::CompressedSigned(),
kNoWriteBarrier},
{kTaggedBase, kTaggedSize, Type::Any(), MachineType::CompressedPointer(),
kNoWriteBarrier}};
const IrOpcode::Value opcodes[] = {
IrOpcode::kChangeCompressedToTagged,
IrOpcode::kChangeCompressedSignedToTaggedSigned,
IrOpcode::kChangeCompressedPointerToTaggedPointer};
ASSERT_EQ(arraysize(decompression_ops), arraysize(element_accesses));
ASSERT_EQ(arraysize(opcodes), arraysize(element_accesses));
// 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_tagged1 = graph()->NewNode(decompression_ops[i], load1);
Node* change_to_tagged2 = graph()->NewNode(decompression_ops[i], load2);
Node* change_to_tagged3 = graph()->NewNode(decompression_ops[i], load3);
Node* phi = graph()->NewNode(
common()->Phi(MachineRepresentation::kTagged, number_of_inputs),
change_to_tagged1, change_to_tagged2, change_to_tagged3, control);
// Reduce
StrictMock<MockAdvancedReducerEditor> editor;
EXPECT_CALL(editor, ReplaceWithValue(phi, _, _, _));
Reduction r = Reduce(&editor, phi);
ASSERT_TRUE(r.Changed());
Node* decompress = GetUniqueValueUse(phi);
EXPECT_NE(nullptr, decompress);
EXPECT_EQ(opcodes[i], decompress->opcode());
}
}
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;
const Operator* decompression_ops[] = {
machine()->ChangeCompressedSignedToTaggedSigned(),
machine()->ChangeCompressedPointerToTaggedPointer()};
const ElementAccess element_accesses[] = {
{kTaggedBase, kTaggedSize, Type::Any(), MachineType::CompressedSigned(),
kNoWriteBarrier},
{kTaggedBase, kTaggedSize, Type::Any(), MachineType::CompressedPointer(),
kNoWriteBarrier}};
const ElementAccess any_access = {kTaggedBase, kTaggedSize, Type::Any(),
MachineType::AnyCompressed(),
kNoWriteBarrier};
ASSERT_EQ(arraysize(decompression_ops), arraysize(element_accesses));
// 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);
// Note that load3 loads a CompressedAny instead of element_accesses[i]
Node* load3 = graph()->NewNode(simplified()->LoadElement(any_access),
object, index, effect, control);
Node* change_to_tagged1 = graph()->NewNode(decompression_ops[i], load1);
Node* change_to_tagged2 = graph()->NewNode(decompression_ops[i], load2);
Node* change_to_tagged3 =
graph()->NewNode(machine()->ChangeCompressedToTagged(), load3);
Node* phi = graph()->NewNode(
common()->Phi(MachineRepresentation::kTagged, number_of_inputs),
change_to_tagged1, change_to_tagged2, change_to_tagged3, control);
// Reduce
StrictMock<MockAdvancedReducerEditor> editor;
EXPECT_CALL(editor, ReplaceWithValue(phi, _, _, _));
Reduction r = Reduce(&editor, phi);
ASSERT_TRUE(r.Changed());
Node* decompress = GetUniqueValueUse(phi);
EXPECT_NE(nullptr, decompress);
EXPECT_EQ(IrOpcode::kChangeCompressedToTagged, decompress->opcode());
}
}
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;
const Operator* decompression_ops[] = {
machine()->ChangeCompressedToTagged(),
machine()->ChangeCompressedSignedToTaggedSigned(),
machine()->ChangeCompressedPointerToTaggedPointer()};
const ElementAccess element_accesses[] = {
{kTaggedBase, kTaggedSize, Type::Any(), MachineType::AnyCompressed(),
kNoWriteBarrier},
{kTaggedBase, kTaggedSize, Type::Any(), MachineType::CompressedSigned(),
kNoWriteBarrier},
{kTaggedBase, kTaggedSize, Type::Any(), MachineType::CompressedPointer(),
kNoWriteBarrier}};
const IrOpcode::Value opcodes[] = {
IrOpcode::kChangeCompressedToTagged,
IrOpcode::kChangeCompressedSignedToTaggedSigned,
IrOpcode::kChangeCompressedPointerToTaggedPointer};
ASSERT_EQ(arraysize(decompression_ops), arraysize(element_accesses));
ASSERT_EQ(arraysize(opcodes), arraysize(element_accesses));
// 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_tagged1 = graph()->NewNode(decompression_ops[i], load1);
Node* change_to_tagged2 = graph()->NewNode(decompression_ops[i], load2);
Node* phi = graph()->NewNode(
common()->Phi(MachineRepresentation::kTagged, number_of_inputs),
change_to_tagged1, change_to_tagged2, 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;
const ElementAccess signed_access = {kTaggedBase, kTaggedSize, Type::Any(),
MachineType::CompressedSigned(),
kNoWriteBarrier};
const ElementAccess pointer_access = {kTaggedBase, kTaggedSize, Type::Any(),
MachineType::CompressedPointer(),
kNoWriteBarrier};
// 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_tagged1 = graph()->NewNode(
machine()->ChangeCompressedSignedToTaggedSigned(), load1);
Node* change_to_tagged2 = graph()->NewNode(
machine()->ChangeCompressedPointerToTaggedPointer(), load2);
Node* phi = graph()->NewNode(
common()->Phi(MachineRepresentation::kTagged, number_of_inputs),
change_to_tagged1, change_to_tagged2, control);
// Reduce
StrictMock<MockAdvancedReducerEditor> editor;
EXPECT_CALL(editor, ReplaceWithValue(phi, _, _, _));
Reduction r = Reduce(&editor, phi);
ASSERT_TRUE(r.Changed());
Node* decompress = GetUniqueValueUse(phi);
EXPECT_NE(nullptr, decompress);
EXPECT_EQ(IrOpcode::kChangeCompressedToTagged, decompress->opcode());
}
// -----------------------------------------------------------------------------
// 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 numberOfInputs = 1;
const ZoneVector<MachineType>* types =
new (graph()->zone()->New(sizeof(ZoneVector<MachineType>)))
ZoneVector<MachineType>(numberOfInputs, graph()->zone());
SparseInputMask dense = SparseInputMask::Dense();
const ElementAccess ElementAccesses[] = {
{kTaggedBase, kTaggedSize, Type::Any(), MachineType::AnyTagged(),
kNoWriteBarrier},
{kTaggedBase, kTaggedSize, Type::Any(), MachineType::TaggedSigned(),
kNoWriteBarrier},
{kTaggedBase, kTaggedSize, Type::Any(), MachineType::TaggedPointer(),
kNoWriteBarrier}};
// For every access
for (size_t i = 0; i < arraysize(ElementAccesses); ++i) {
// Create the graph
Node* load = graph()->NewNode(simplified()->LoadElement(ElementAccesses[i]),
object, index, effect, control);
Node* changeToTagged = graph()->NewNode(
machine()->ChangeCompressedPointerToTaggedPointer(), load);
Node* typedStateValuesOneDecompress = graph()->NewNode(
common()->TypedStateValues(types, dense), changeToTagged);
// Reduce
StrictMock<MockAdvancedReducerEditor> editor;
DecompressionElimination decompression_elimination(&editor, graph(),
machine(), common());
Reduction r =
decompression_elimination.Reduce(typedStateValuesOneDecompress);
ASSERT_TRUE(r.Changed());
}
}
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 numberOfInputs = 3;
const ZoneVector<MachineType>* types =
new (graph()->zone()->New(sizeof(ZoneVector<MachineType>)))
ZoneVector<MachineType>(numberOfInputs, graph()->zone());
SparseInputMask dense = SparseInputMask::Dense();
const ElementAccess ElementAccesses[] = {
{kTaggedBase, kTaggedSize, Type::Any(), MachineType::AnyTagged(),
kNoWriteBarrier},
{kTaggedBase, kTaggedSize, Type::Any(), MachineType::TaggedSigned(),
kNoWriteBarrier},
{kTaggedBase, kTaggedSize, Type::Any(), MachineType::TaggedPointer(),
kNoWriteBarrier}};
// For every access
for (size_t i = 0; i < arraysize(ElementAccesses); ++i) {
// Create the graph
Node* load1 =
graph()->NewNode(simplified()->LoadElement(ElementAccesses[i]), object,
index, effect, control);
Node* changeToTagged1 = graph()->NewNode(
machine()->ChangeCompressedPointerToTaggedPointer(), load1);
Node* load2 =
graph()->NewNode(simplified()->LoadElement(ElementAccesses[i]), object,
index, effect, control);
Node* changeToTagged2 = graph()->NewNode(
machine()->ChangeCompressedPointerToTaggedPointer(), load2);
Node* typedStateValuesOneDecompress =
graph()->NewNode(common()->TypedStateValues(types, dense),
changeToTagged1, load1, changeToTagged2);
// Reduce
StrictMock<MockAdvancedReducerEditor> editor;
DecompressionElimination decompression_elimination(&editor, graph(),
machine(), common());
Reduction r =
decompression_elimination.Reduce(typedStateValuesOneDecompress);
ASSERT_TRUE(r.Changed());
}
}
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 numberOfInputs = 3;
const ZoneVector<MachineType>* types =
new (graph()->zone()->New(sizeof(ZoneVector<MachineType>)))
ZoneVector<MachineType>(numberOfInputs, graph()->zone());
SparseInputMask dense = SparseInputMask::Dense();
const ElementAccess ElementAccesses[] = {
{kTaggedBase, kTaggedSize, Type::Any(), MachineType::AnyTagged(),
kNoWriteBarrier},
{kTaggedBase, kTaggedSize, Type::Any(), MachineType::TaggedSigned(),
kNoWriteBarrier},
{kTaggedBase, kTaggedSize, Type::Any(), MachineType::TaggedPointer(),
kNoWriteBarrier}};
// For every access
for (size_t i = 0; i < arraysize(ElementAccesses); ++i) {
// Create the graph
Node* load1 =
graph()->NewNode(simplified()->LoadElement(ElementAccesses[i]), object,
index, effect, control);
Node* changeToTagged1 = graph()->NewNode(
machine()->ChangeCompressedPointerToTaggedPointer(), load1);
Node* load2 =
graph()->NewNode(simplified()->LoadElement(ElementAccesses[i]), object,
index, effect, control);
Node* changeToTagged2 = graph()->NewNode(
machine()->ChangeCompressedPointerToTaggedPointer(), load2);
Node* load3 =
graph()->NewNode(simplified()->LoadElement(ElementAccesses[i]), object,
index, effect, control);
Node* changeToTagged3 = graph()->NewNode(
machine()->ChangeCompressedPointerToTaggedPointer(), load3);
Node* typedStateValuesOneDecompress =
graph()->NewNode(common()->TypedStateValues(types, dense),
changeToTagged1, changeToTagged2, changeToTagged3);
// Reduce
StrictMock<MockAdvancedReducerEditor> editor;
DecompressionElimination decompression_elimination(&editor, graph(),
machine(), common());
Reduction r =
decompression_elimination.Reduce(typedStateValuesOneDecompress);
ASSERT_TRUE(r.Changed());
}
}
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 numberOfInputs = 3;
const ZoneVector<MachineType>* types =
new (graph()->zone()->New(sizeof(ZoneVector<MachineType>)))
ZoneVector<MachineType>(numberOfInputs, graph()->zone());
SparseInputMask dense = SparseInputMask::Dense();
const ElementAccess ElementAccesses[] = {
{kTaggedBase, kTaggedSize, Type::Any(), MachineType::AnyTagged(),
kNoWriteBarrier},
{kTaggedBase, kTaggedSize, Type::Any(), MachineType::TaggedSigned(),
kNoWriteBarrier},
{kTaggedBase, kTaggedSize, Type::Any(), MachineType::TaggedPointer(),
kNoWriteBarrier}};
// For every access
for (size_t i = 0; i < arraysize(ElementAccesses); ++i) {
// Create the graph
Node* load = graph()->NewNode(simplified()->LoadElement(ElementAccesses[i]),
object, index, effect, control);
Node* typedStateValuesOneDecompress = 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(typedStateValuesOneDecompress);
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);
const Operator* DecompressionOps[] = {
machine()->ChangeCompressedToTagged(),
machine()->ChangeCompressedSignedToTaggedSigned(),
machine()->ChangeCompressedPointerToTaggedPointer()};
const ElementAccess ElementAccesses[] = {
{kTaggedBase, kTaggedSize, Type::Any(), MachineType::AnyTagged(),
kNoWriteBarrier},
{kTaggedBase, kTaggedSize, Type::Any(), MachineType::TaggedSigned(),
kNoWriteBarrier},
{kTaggedBase, kTaggedSize, Type::Any(), MachineType::TaggedPointer(),
kNoWriteBarrier}};
ASSERT_EQ(arraysize(DecompressionOps), arraysize(ElementAccesses));
// For every decompression (lhs)
for (size_t j = 0; j < arraysize(DecompressionOps); ++j) {
// For every decompression (rhs)
for (size_t k = 0; k < arraysize(DecompressionOps); ++k) {
// Create the graph
Node* load1 =
graph()->NewNode(simplified()->LoadElement(ElementAccesses[j]),
object, index, effect, control);
Node* changeToTagged1 = graph()->NewNode(DecompressionOps[j], load1);
Node* load2 =
graph()->NewNode(simplified()->LoadElement(ElementAccesses[k]),
object, index, effect, control);
Node* changeToTagged2 = graph()->NewNode(DecompressionOps[j], load2);
Node* comparison = graph()->NewNode(machine()->Word64Equal(),
changeToTagged1, changeToTagged2);
// 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);
const Operator* DecompressionOps[] = {
machine()->ChangeCompressedToTagged(),
machine()->ChangeCompressedSignedToTaggedSigned(),
machine()->ChangeCompressedPointerToTaggedPointer()};
const ElementAccess ElementAccesses[] = {
{kTaggedBase, kTaggedSize, Type::Any(), MachineType::AnyTagged(),
kNoWriteBarrier},
{kTaggedBase, kTaggedSize, Type::Any(), MachineType::TaggedSigned(),
kNoWriteBarrier},
{kTaggedBase, kTaggedSize, Type::Any(), MachineType::TaggedPointer(),
kNoWriteBarrier}};
ASSERT_EQ(arraysize(DecompressionOps), arraysize(ElementAccesses));
// For every decompression (same for lhs and rhs)
for (size_t j = 0; j < arraysize(DecompressionOps); ++j) {
// Create the graph
Node* load = graph()->NewNode(simplified()->LoadElement(ElementAccesses[j]),
object, index, effect, control);
Node* changeToTagged = graph()->NewNode(DecompressionOps[j], load);
Node* comparison = graph()->NewNode(machine()->Word64Equal(),
changeToTagged, changeToTagged);
// 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 Operator* DecompressionOps[] = {
machine()->ChangeCompressedToTagged(),
machine()->ChangeCompressedSignedToTaggedSigned(),
machine()->ChangeCompressedPointerToTaggedPointer()};
const ElementAccess ElementAccesses[] = {
{kTaggedBase, kTaggedSize, Type::Any(), MachineType::AnyTagged(),
kNoWriteBarrier},
{kTaggedBase, kTaggedSize, Type::Any(), MachineType::TaggedSigned(),
kNoWriteBarrier},
{kTaggedBase, kTaggedSize, Type::Any(), MachineType::TaggedPointer(),
kNoWriteBarrier}};
ASSERT_EQ(arraysize(DecompressionOps), arraysize(ElementAccesses));
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 j = 0; j < arraysize(DecompressionOps); ++j) {
// For every constant (rhs)
for (size_t k = 0; k < arraysize(constants); ++k) {
// Test with both (lhs, rhs) combinations
for (bool lhsIsDecompression : {false, true}) {
// Create the graph
Node* load =
graph()->NewNode(simplified()->LoadElement(ElementAccesses[j]),
object, index, effect, control);
Node* changeToTagged = graph()->NewNode(DecompressionOps[j], load);
Node* constant =
graph()->NewNode(common()->Int64Constant(constants[k]));
Node* lhs = lhsIsDecompression ? changeToTagged : constant;
Node* rhs = lhsIsDecompression ? constant : changeToTagged;
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 Operator* DecompressionOps[] = {
machine()->ChangeCompressedToTagged(),
machine()->ChangeCompressedSignedToTaggedSigned(),
machine()->ChangeCompressedPointerToTaggedPointer()};
const ElementAccess ElementAccesses[] = {
{kTaggedBase, kTaggedSize, Type::Any(), MachineType::AnyTagged(),
kNoWriteBarrier},
{kTaggedBase, kTaggedSize, Type::Any(), MachineType::TaggedSigned(),
kNoWriteBarrier},
{kTaggedBase, kTaggedSize, Type::Any(), MachineType::TaggedPointer(),
kNoWriteBarrier}};
ASSERT_EQ(arraysize(DecompressionOps), arraysize(ElementAccesses));
const Handle<HeapNumber> heapConstants[] = {
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 j = 0; j < arraysize(DecompressionOps); ++j) {
// For every constant (rhs)
for (size_t k = 0; k < arraysize(heapConstants); ++k) {
// Test with both (lhs, rhs) combinations
for (bool lhsIsDecompression : {false, true}) {
// Create the graph
Node* load =
graph()->NewNode(simplified()->LoadElement(ElementAccesses[j]),
object, index, effect, control);
Node* changeToTagged = graph()->NewNode(DecompressionOps[j], load);
Node* constant =
graph()->NewNode(common()->HeapConstant(heapConstants[k]));
Node* lhs = lhsIsDecompression ? changeToTagged : constant;
Node* rhs = lhsIsDecompression ? constant : changeToTagged;
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