[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:
parent
6f7eeec89d
commit
7f69a0daea
@ -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
|
||||
|
@ -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);
|
||||
};
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user