[turbofan] Compress HeapConstants in DecompressionOptimizer

We should be encountering this due to TaggedEquality.
DecompressionElimination used to take care of this, but it will not be
present in the new system.

Bug: v8:7703
Change-Id: I9fe00ee116ed1514cb4c465a8d19df6e785ef913
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1868623
Reviewed-by: Tobias Tebbi <tebbi@chromium.org>
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Commit-Queue: Santiago Aboy Solanes <solanes@chromium.org>
Cr-Commit-Position: refs/heads/master@{#64471}
This commit is contained in:
Santiago Aboy Solanes 2019-10-22 12:05:43 +01:00 committed by Commit Bot
parent 6f7eeec89d
commit 7f69a0daea
4 changed files with 95 additions and 57 deletions

View File

@ -20,15 +20,28 @@ bool IsMachineLoad(Node* const node) {
opcode == IrOpcode::kUnalignedLoad;
}
bool IsHeapConstant(Node* const node) {
return node->opcode() == IrOpcode::kHeapConstant;
}
bool CanBeCompressed(Node* const node) {
return IsHeapConstant(node) ||
(IsMachineLoad(node) &&
CanBeTaggedPointer(
LoadRepresentationOf(node->op()).representation()));
}
} // anonymous namespace
DecompressionOptimizer::DecompressionOptimizer(Zone* zone, Graph* graph,
CommonOperatorBuilder* common,
MachineOperatorBuilder* machine)
: graph_(graph),
common_(common),
machine_(machine),
states_(graph, static_cast<uint32_t>(State::kNumberOfStates)),
to_visit_(zone),
compressed_loads_(zone) {}
compressed_candidate_nodes_(zone) {}
void DecompressionOptimizer::MarkNodes() {
MaybeMarkAndQueueForRevisit(graph()->end(), State::kOnly32BitsObserved);
@ -89,60 +102,72 @@ void DecompressionOptimizer::MaybeMarkAndQueueForRevisit(Node* const node,
states_.Set(node, state);
to_visit_.push_back(node);
// In the case of a TaggedPointer or TaggedAny Load that can be done in 32
// bits, we save it in compressed_loads_ to be changed later if necessary.
if (state == State::kOnly32BitsObserved && IsMachineLoad(node) &&
CanBeTaggedPointer(LoadRepresentationOf(node->op()).representation())) {
compressed_loads_.push_back(node);
if (state == State::kOnly32BitsObserved && CanBeCompressed(node)) {
compressed_candidate_nodes_.push_back(node);
}
}
}
void DecompressionOptimizer::ChangeLoads() {
for (Node* const node : compressed_loads_) {
// compressed_loads_ contains all the nodes that once had the
void DecompressionOptimizer::ChangeHeapConstant(Node* const node) {
DCHECK(IsHeapConstant(node));
NodeProperties::ChangeOp(
node, common()->CompressedHeapConstant(HeapConstantOf(node->op())));
}
void DecompressionOptimizer::ChangeLoad(Node* const node) {
DCHECK(IsMachineLoad(node));
// Change to a Compressed MachRep to avoid the full decompression.
LoadRepresentation load_rep = LoadRepresentationOf(node->op());
LoadRepresentation compressed_load_rep;
if (load_rep == MachineType::AnyTagged()) {
compressed_load_rep = MachineType::AnyCompressed();
} else {
DCHECK_EQ(load_rep, MachineType::TaggedPointer());
compressed_load_rep = MachineType::CompressedPointer();
}
// Change to the Operator with the Compressed MachineRepresentation.
switch (node->opcode()) {
case IrOpcode::kLoad:
NodeProperties::ChangeOp(node, machine()->Load(compressed_load_rep));
break;
case IrOpcode::kPoisonedLoad:
NodeProperties::ChangeOp(node,
machine()->PoisonedLoad(compressed_load_rep));
break;
case IrOpcode::kProtectedLoad:
NodeProperties::ChangeOp(node,
machine()->ProtectedLoad(compressed_load_rep));
break;
case IrOpcode::kUnalignedLoad:
NodeProperties::ChangeOp(node,
machine()->UnalignedLoad(compressed_load_rep));
break;
default:
UNREACHABLE();
}
}
void DecompressionOptimizer::ChangeNodes() {
for (Node* const node : compressed_candidate_nodes_) {
// compressed_candidate_nodes_ contains all the nodes that once had the
// State::kOnly32BitsObserved. If we later updated the state to be
// State::IsEverythingObserved, then we have to ignore them. This is less
// costly than removing them from the compressed_loads_ NodeVector when we
// update them to State::IsEverythingObserved.
// costly than removing them from the compressed_candidate_nodes_ NodeVector
// when we update them to State::IsEverythingObserved.
if (IsEverythingObserved(node)) continue;
// Change to a Compressed MachRep to avoid the full decompression.
LoadRepresentation load_rep = LoadRepresentationOf(node->op());
LoadRepresentation compressed_load_rep;
if (load_rep == MachineType::AnyTagged()) {
compressed_load_rep = MachineType::AnyCompressed();
if (IsHeapConstant(node)) {
ChangeHeapConstant(node);
} else {
DCHECK_EQ(load_rep, MachineType::TaggedPointer());
compressed_load_rep = MachineType::CompressedPointer();
}
// Change to the Operator with the Compressed MachineRepresentation.
switch (node->opcode()) {
case IrOpcode::kLoad:
NodeProperties::ChangeOp(node, machine()->Load(compressed_load_rep));
break;
case IrOpcode::kPoisonedLoad:
NodeProperties::ChangeOp(node,
machine()->PoisonedLoad(compressed_load_rep));
break;
case IrOpcode::kProtectedLoad:
NodeProperties::ChangeOp(node,
machine()->ProtectedLoad(compressed_load_rep));
break;
case IrOpcode::kUnalignedLoad:
NodeProperties::ChangeOp(node,
machine()->UnalignedLoad(compressed_load_rep));
break;
default:
UNREACHABLE();
ChangeLoad(node);
}
}
}
void DecompressionOptimizer::Reduce() {
MarkNodes();
ChangeLoads();
ChangeNodes();
}
} // namespace compiler

View File

@ -5,6 +5,7 @@
#ifndef V8_COMPILER_DECOMPRESSION_OPTIMIZER_H_
#define V8_COMPILER_DECOMPRESSION_OPTIMIZER_H_
#include "src/compiler/common-operator.h"
#include "src/compiler/machine-operator.h"
#include "src/compiler/node-marker.h"
@ -15,9 +16,11 @@ namespace compiler {
// Forward declare.
class Graph;
// DecompressionOptimizer purpose is to avoid the full decompression on Loads
// whenever possible. Its scope is narrowed down to TaggedPointer and AnyTagged,
// since TaggedSigned avoids full decompression always.
// DecompressionOptimizer purpose is to hide the distinction between 32 bit and
// 64 bit tagged values, while being able to use the compressed version of nodes
// whenever possible. Its scope is narrowed down to loads of TaggedPointer and
// AnyTagged (since TaggedSigned avoids full decompression always), and
// HeapConstants.
// DecompressionOptimizer will run only when pointer compression is enabled. For
// the moment, it's also requires FLAG_turbo_decompression_elimination to be
@ -35,11 +38,12 @@ class Graph;
class V8_EXPORT_PRIVATE DecompressionOptimizer final {
public:
DecompressionOptimizer(Zone* zone, Graph* graph,
CommonOperatorBuilder* common,
MachineOperatorBuilder* machine);
~DecompressionOptimizer() = default;
// Assign States to the nodes, and then change the loads' Operator to avoid
// decompression if possible.
// Assign States to the nodes, and then change the node's Operator to use the
// compressed version if possible.
void Reduce();
private:
@ -56,9 +60,15 @@ class V8_EXPORT_PRIVATE DecompressionOptimizer final {
kNumberOfStates
};
// Go through the already marked nodes and changed the operation for the loads
// that can avoid the full decompression.
void ChangeLoads();
// Change node's op from HeapConstant to CompressedHeapConstant.
void ChangeHeapConstant(Node* const node);
// Change node's load into a compressed one.
void ChangeLoad(Node* const node);
// Go through the already marked nodes and changed the operation for the nodes
// that can use compressed outputs.
void ChangeNodes();
// Goes through the nodes to mark them all as appropriate. It will visit each
// node at most twice: only when the node was unvisited, then marked as
@ -74,9 +84,9 @@ class V8_EXPORT_PRIVATE DecompressionOptimizer final {
// i.e either if:
// * We are marking an unvisited node, or
// * We are marking a node as needing 64 bits when we previously had the
// information that it could output 32 bits. Also, we store the TaggedPointer
// and AnyTagged loads that have their state set as kOnly32BitsObserved.
// If the node's state changes, we queue it for revisit.
// information that it could output 32 bits. Also, we store the HeapConstant
// and TaggedPointer and AnyTagged loads that have their state set as
// kOnly32BitsObserved. If the node's state changes, we queue it for revisit.
void MaybeMarkAndQueueForRevisit(Node* const node, State state);
bool IsEverythingObserved(Node* const node) {
@ -84,19 +94,21 @@ class V8_EXPORT_PRIVATE DecompressionOptimizer final {
}
Graph* graph() const { return graph_; }
CommonOperatorBuilder* common() const { return common_; }
MachineOperatorBuilder* machine() const { return machine_; }
Graph* const graph_;
CommonOperatorBuilder* const common_;
MachineOperatorBuilder* const machine_;
NodeMarker<State> states_;
// to_visit_ is a Deque but it's used as if it were a Queue. The reason why we
// are using NodeDeque is because it attempts to reuse 'freed' zone memory
// instead of always allocating a new region.
NodeDeque to_visit_;
// Contains the AnyTagged and TaggedPointer loads that can avoid the full
// decompression. In a way, it functions as a NodeSet since each node will be
// Contains the nodes that can be changed into a compressed version of
// themselves. In a way, it functions as a NodeSet since each node will be
// contained at most once. It's a Vector since we care about insertion speed.
NodeVector compressed_loads_;
NodeVector compressed_candidate_nodes_;
DISALLOW_COPY_AND_ASSIGN(DecompressionOptimizer);
};

View File

@ -1799,8 +1799,8 @@ struct DecompressionOptimizationPhase {
void Run(PipelineData* data, Zone* temp_zone) {
if (COMPRESS_POINTERS_BOOL && !FLAG_turbo_decompression_elimination) {
DecompressionOptimizer decompression_optimizer(temp_zone, data->graph(),
data->machine());
DecompressionOptimizer decompression_optimizer(
temp_zone, data->graph(), data->common(), data->machine());
decompression_optimizer.Reduce();
}
}

View File

@ -20,7 +20,8 @@ class DecompressionOptimizerTest : public GraphTest {
protected:
void Reduce() {
DecompressionOptimizer decompression_optimizer(zone(), graph(), machine());
DecompressionOptimizer decompression_optimizer(zone(), graph(), common(),
machine());
decompression_optimizer.Reduce();
}