Commit c7a7ed22 authored by Santiago Aboy Solanes's avatar Santiago Aboy Solanes Committed by Commit Bot

[ptr-compr] Add Phi case in DecompressionOptimizer

Phis act as proxys: a phi's input has only 32 bits observed iff
the phi's output has only 32 bits observed. When the Tagged Phi
has only 32 bits observed, the Phi's MachineRepresentation
changes to the Compressed counterpart.

Also, update machine graph verifier so that Phis of Compressed
accept Tagged inputs as well.

Bug: v8:7703
Change-Id: I365d0b38f76edbaecbfea29f603abd2ce2224878
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1879943Reviewed-by: 's avatarTobias Tebbi <tebbi@chromium.org>
Commit-Queue: Santiago Aboy Solanes <solanes@chromium.org>
Cr-Commit-Position: refs/heads/master@{#64802}
parent 702f2bad
......@@ -20,15 +20,24 @@ bool IsMachineLoad(Node* const node) {
opcode == IrOpcode::kUnalignedLoad;
}
bool IsTaggedMachineLoad(Node* const node) {
return IsMachineLoad(node) &&
CanBeTaggedPointer(LoadRepresentationOf(node->op()).representation());
}
bool IsHeapConstant(Node* const node) {
return node->opcode() == IrOpcode::kHeapConstant;
}
bool IsTaggedPhi(Node* const node) {
if (node->opcode() == IrOpcode::kPhi) {
return CanBeTaggedPointer(PhiRepresentationOf(node->op()));
}
return false;
}
bool CanBeCompressed(Node* const node) {
return IsHeapConstant(node) ||
(IsMachineLoad(node) &&
CanBeTaggedPointer(
LoadRepresentationOf(node->op()).representation()));
return IsHeapConstant(node) || IsTaggedMachineLoad(node) || IsTaggedPhi(node);
}
} // anonymous namespace
......@@ -74,6 +83,7 @@ void DecompressionOptimizer::MarkNodeInputs(Node* node) {
State::kOnly32BitsObserved); // value_1
break;
// SPECIAL CASES.
// SPECIAL CASES - Store.
case IrOpcode::kStore:
case IrOpcode::kProtectedStore:
case IrOpcode::kUnalignedStore:
......@@ -91,6 +101,7 @@ void DecompressionOptimizer::MarkNodeInputs(Node* node) {
? State::kOnly32BitsObserved
: State::kEverythingObserved); // value
break;
// SPECIAL CASES - Variable inputs.
// The deopt code knows how to handle Compressed inputs, both
// MachineRepresentation kCompressed values and CompressedHeapConstants.
case IrOpcode::kFrameState: // Fall through.
......@@ -103,6 +114,14 @@ void DecompressionOptimizer::MarkNodeInputs(Node* node) {
State::kOnly32BitsObserved);
}
break;
case IrOpcode::kPhi: {
// Replicate the phi's state for its inputs.
State curr_state = states_.Get(node);
for (int i = 0; i < node->op()->ValueInputCount(); ++i) {
MaybeMarkAndQueueForRevisit(node->InputAt(i), curr_state);
}
break;
}
default:
// To be conservative, we assume that all value inputs need to be 64 bits
// unless noted otherwise.
......@@ -144,6 +163,21 @@ void DecompressionOptimizer::ChangeHeapConstant(Node* const node) {
node, common()->CompressedHeapConstant(HeapConstantOf(node->op())));
}
void DecompressionOptimizer::ChangePhi(Node* const node) {
DCHECK(IsTaggedPhi(node));
MachineRepresentation mach_rep = PhiRepresentationOf(node->op());
if (mach_rep == MachineRepresentation::kTagged) {
mach_rep = MachineRepresentation::kCompressed;
} else {
DCHECK_EQ(mach_rep, MachineRepresentation::kTaggedPointer);
mach_rep = MachineRepresentation::kCompressedPointer;
}
NodeProperties::ChangeOp(
node, common()->Phi(mach_rep, node->op()->ValueInputCount()));
}
void DecompressionOptimizer::ChangeLoad(Node* const node) {
DCHECK(IsMachineLoad(node));
// Change to a Compressed MachRep to avoid the full decompression.
......@@ -187,10 +221,16 @@ void DecompressionOptimizer::ChangeNodes() {
// when we update them to State::IsEverythingObserved.
if (IsEverythingObserved(node)) continue;
if (IsHeapConstant(node)) {
ChangeHeapConstant(node);
} else {
ChangeLoad(node);
switch (node->opcode()) {
case IrOpcode::kHeapConstant:
ChangeHeapConstant(node);
break;
case IrOpcode::kPhi:
ChangePhi(node);
break;
default:
ChangeLoad(node);
break;
}
}
}
......
......@@ -63,6 +63,9 @@ class V8_EXPORT_PRIVATE DecompressionOptimizer final {
// Change node's op from HeapConstant to CompressedHeapConstant.
void ChangeHeapConstant(Node* const node);
// Change the phi's representation from Tagged to Compressed.
void ChangePhi(Node* const node);
// Change node's load into a compressed one.
void ChangeLoad(Node* const node);
......
......@@ -679,7 +679,11 @@ class MachineRepresentationChecker {
case MachineRepresentation::kCompressedPointer:
case MachineRepresentation::kCompressedSigned:
for (int i = 0; i < node->op()->ValueInputCount(); ++i) {
CheckValueInputIsCompressed(node, i);
if (FLAG_turbo_decompression_elimination) {
CheckValueInputIsCompressed(node, i);
} else {
CheckValueInputIsCompressedOrTagged(node, i);
}
}
break;
case MachineRepresentation::kWord32:
......
......@@ -308,6 +308,159 @@ TEST_F(DecompressionOptimizerTest, TypedStateValues) {
}
}
// -----------------------------------------------------------------------------
// Phi
TEST_F(DecompressionOptimizerTest, PhiDecompressOrNot) {
// Skip test if decompression elimination is enabled.
if (FLAG_turbo_decompression_elimination) {
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;
// Test for both AnyTagged and TaggedPointer.
for (size_t i = 0; i < arraysize(types); ++i) {
for (size_t j = 0; j < arraysize(heap_constants); ++j) {
// Create the graph.
// Base pointer
Node* load_1 = graph()->NewNode(machine()->Load(types[i]), object, index,
effect, control);
Node* constant_1 =
graph()->NewNode(common()->HeapConstant(heap_constants[j]));
Node* phi_1 = graph()->NewNode(
common()->Phi(types[i].representation(), number_of_inputs), load_1,
constant_1, control);
// Value
Node* load_2 = graph()->NewNode(machine()->Load(types[i]), object, index,
effect, control);
Node* constant_2 =
graph()->NewNode(common()->HeapConstant(heap_constants[j]));
Node* phi_2 = graph()->NewNode(
common()->Phi(types[i].representation(), number_of_inputs), load_2,
constant_2, control);
graph()->SetEnd(
graph()->NewNode(machine()->Store(CreateStoreRep(types[i])), phi_1,
index, phi_2, effect, control));
// Change the nodes, and test the change.
Reduce();
// Base pointer should not be compressed.
EXPECT_EQ(LoadMachRep(load_1), types[i].representation());
EXPECT_EQ(constant_1->opcode(), IrOpcode::kHeapConstant);
EXPECT_EQ(PhiRepresentationOf(phi_1->op()), types[i].representation());
// Value should be compressed.
EXPECT_EQ(LoadMachRep(load_2), CompressedMachRep(types[i]));
EXPECT_EQ(constant_2->opcode(), IrOpcode::kCompressedHeapConstant);
EXPECT_EQ(PhiRepresentationOf(phi_2->op()), CompressedMachRep(types[i]));
}
}
}
TEST_F(DecompressionOptimizerTest, CascadingPhi) {
// Skip test if decompression elimination is enabled.
if (FLAG_turbo_decompression_elimination) {
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;
// Test for both AnyTagged and TaggedPointer.
for (size_t i = 0; i < arraysize(types); ++i) {
// Create the graph.
Node* load_1 = graph()->NewNode(machine()->Load(types[i]), object, index,
effect, control);
Node* load_2 = graph()->NewNode(machine()->Load(types[i]), object, index,
effect, control);
Node* load_3 = graph()->NewNode(machine()->Load(types[i]), object, index,
effect, control);
Node* load_4 = graph()->NewNode(machine()->Load(types[i]), object, index,
effect, control);
Node* phi_1 = graph()->NewNode(
common()->Phi(types[i].representation(), number_of_inputs), load_1,
load_2, control);
Node* phi_2 = graph()->NewNode(
common()->Phi(types[i].representation(), number_of_inputs), load_3,
load_4, control);
Node* final_phi = graph()->NewNode(
common()->Phi(types[i].representation(), number_of_inputs), phi_1,
phi_2, control);
// Value
graph()->SetEnd(final_phi);
// Change the nodes, and test the change.
Reduce();
// Loads are all compressed
EXPECT_EQ(LoadMachRep(load_1), CompressedMachRep(types[i]));
EXPECT_EQ(LoadMachRep(load_2), CompressedMachRep(types[i]));
EXPECT_EQ(LoadMachRep(load_3), CompressedMachRep(types[i]));
EXPECT_EQ(LoadMachRep(load_4), CompressedMachRep(types[i]));
// Phis too
EXPECT_EQ(PhiRepresentationOf(phi_1->op()), CompressedMachRep(types[i]));
EXPECT_EQ(PhiRepresentationOf(phi_2->op()), CompressedMachRep(types[i]));
EXPECT_EQ(PhiRepresentationOf(final_phi->op()),
CompressedMachRep(types[i]));
}
}
TEST_F(DecompressionOptimizerTest, PhiWithOneCompressedAndOneTagged) {
// If the phi is Compressed but one of the inputs is Tagged, then we insert a
// ChangeTaggedToCompressed node.
// Skip test if decompression elimination is enabled.
if (FLAG_turbo_decompression_elimination) {
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;
// Test for both AnyTagged and TaggedPointer.
for (size_t i = 0; i < arraysize(types); ++i) {
for (size_t j = 0; j < arraysize(heap_constants); ++j) {
// Create the graph.
// Base pointer in load_2, and phi input for value
Node* load_1 = graph()->NewNode(machine()->Load(types[i]), object, index,
effect, control);
// load_2 blocks load_1 from being compressed.
Node* load_2 = graph()->NewNode(machine()->Load(types[i]), load_1, index,
effect, control);
Node* phi = graph()->NewNode(
common()->Phi(types[i].representation(), number_of_inputs), load_1,
load_2, control);
graph()->SetEnd(
graph()->NewNode(machine()->Store(CreateStoreRep(types[i])), object,
index, phi, effect, control));
// Change the nodes, and test the change.
Reduce();
EXPECT_EQ(LoadMachRep(load_1), types[i].representation());
EXPECT_EQ(LoadMachRep(load_2), CompressedMachRep(types[i]));
EXPECT_EQ(PhiRepresentationOf(phi->op()), CompressedMachRep(types[i]));
}
}
}
} // namespace compiler
} // namespace internal
} // namespace v8
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment