[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:
parent
d47db12666
commit
8306b26a40
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user