v8/test/unittests/compiler/decompression-elimination-unittest.cc
Santiago Aboy Solanes 8306b26a40 [ptr-compr][turbofan] Adding Phi reductions to DecompressionElimination
This reduction replaces the Phi's input decompressions with their parent
node, if and only if all of the Phi's inputs are Decompress nodes.

Also, if we have different Decompress nodes as inputs, we need to use
a conservative decompression after the Phi.

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
Change-Id: I8cc0264f9d08fe5ad25364f18c9f305afc54529c
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1624785
Reviewed-by: Jaroslav Sevcik <jarin@chromium.org>
Commit-Queue: Santiago Aboy Solanes <solanes@chromium.org>
Cr-Commit-Position: refs/heads/master@{#61820}
2019-05-24 10:02:53 +00:00

960 lines
38 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/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"
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(Node* node) {
StrictMock<MockAdvancedReducerEditor> editor;
DecompressionElimination decompression_elimination(&editor, graph(),
machine(), common());
return decompression_elimination.Reduce(node);
}
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());
}
// -----------------------------------------------------------------------------
// 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
Reduction r = Reduce(phi);
ASSERT_TRUE(r.Changed());
EXPECT_EQ(opcodes[i], r.replacement()->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
Reduction r = Reduce(phi);
ASSERT_TRUE(r.Changed());
EXPECT_EQ(opcodes[i], r.replacement()->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
Reduction r = Reduce(phi);
ASSERT_TRUE(r.Changed());
EXPECT_EQ(IrOpcode::kChangeCompressedToTagged, r.replacement()->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
Reduction r = Reduce(phi);
ASSERT_TRUE(r.Changed());
EXPECT_EQ(IrOpcode::kChangeCompressedToTagged, r.replacement()->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