[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}
This commit is contained in:
Santiago Aboy Solanes 2019-05-24 09:55:28 +01:00 committed by Commit Bot
parent d47db12666
commit 8306b26a40
3 changed files with 337 additions and 1 deletions

View File

@ -79,6 +79,63 @@ Reduction DecompressionElimination::ReduceCompress(Node* node) {
}
}
Reduction DecompressionElimination::ReducePhi(Node* node) {
DCHECK_EQ(node->opcode(), IrOpcode::kPhi);
const int value_input_count = node->op()->ValueInputCount();
// Check if all input are decompress nodes, and if all are the same.
bool same_decompresses = true;
IrOpcode::Value first_opcode = node->InputAt(0)->opcode();
for (int i = 0; i < value_input_count; ++i) {
Node* input = node->InputAt(i);
if (IrOpcode::IsDecompressOpcode(input->opcode())) {
same_decompresses &= first_opcode == input->opcode();
} else {
return NoChange();
}
}
// By now, we know that all inputs are decompress nodes. If all are the same,
// we can grab the first one to be used after the Phi. If we have different
// Decompress nodes as inputs, we need to use a conservative decompression
// after the Phi.
const Operator* op;
if (same_decompresses) {
op = node->InputAt(0)->op();
} else {
op = machine()->ChangeCompressedToTagged();
}
// Rewire phi's inputs to be the compressed inputs.
for (int i = 0; i < value_input_count; ++i) {
Node* input = node->InputAt(i);
DCHECK_EQ(input->InputCount(), 1);
node->ReplaceInput(i, input->InputAt(0));
}
// Update the MachineRepresentation on the Phi.
MachineRepresentation rep;
switch (op->opcode()) {
case IrOpcode::kChangeCompressedToTagged:
rep = MachineRepresentation::kCompressed;
break;
case IrOpcode::kChangeCompressedSignedToTaggedSigned:
rep = MachineRepresentation::kCompressedSigned;
break;
case IrOpcode::kChangeCompressedPointerToTaggedPointer:
rep = MachineRepresentation::kCompressedPointer;
break;
default:
UNREACHABLE();
}
NodeProperties::ChangeOp(node, common()->Phi(rep, value_input_count));
// Add a decompress after the Phi. To do this, we need to replace the Phi with
// "Phi <- Decompress".
return Replace(graph()->NewNode(op, node));
}
Reduction DecompressionElimination::ReduceTypedStateValues(Node* node) {
DCHECK_EQ(node->opcode(), IrOpcode::kTypedStateValues);
@ -139,6 +196,8 @@ Reduction DecompressionElimination::Reduce(Node* node) {
case IrOpcode::kChangeTaggedSignedToCompressedSigned:
case IrOpcode::kChangeTaggedPointerToCompressedPointer:
return ReduceCompress(node);
case IrOpcode::kPhi:
return ReducePhi(node);
case IrOpcode::kTypedStateValues:
return ReduceTypedStateValues(node);
case IrOpcode::kWord64Equal:

View File

@ -48,7 +48,11 @@ class V8_EXPORT_PRIVATE DecompressionElimination final
// Can be used for Any, Signed, and Pointer compressions.
Reduction ReduceCompress(Node* node);
// Replaces TypedStateValues's input decompressions with their parent node.
// Replaces Phi's input decompressions with their input node, if and only if
// all of the Phi's inputs are Decompress nodes.
Reduction ReducePhi(Node* node);
// Replaces TypedStateValues's input decompressions with their input node.
Reduction ReduceTypedStateValues(Node* node);
// Replaces a Word64Equal with a Word32Equal if both of its inputs are

View File

@ -250,6 +250,279 @@ TEST_F(DecompressionEliminationTest,
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