1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
|
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#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"
namespace v8 {
namespace internal {
namespace compiler {
// Forward declare.
class Graph;
// 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.
// The phase needs to be run when Machine are present in the graph, i.e
// at the very end of the pipeline. Also, since this phase may change
// the load's MachineRepresentation from Tagged to Compressed, it's best
// to run it as late as possible in order to keep the phases that know
// about Compressed MachineRepresentation to a minimum.
// As an example, if we Load a Tagged value only to Store it back again (i.e
// Load -> Store nodes, with the Load's value being the Store's value) we don't
// need to fully decompress it since the Store will ignore the top bits.
class V8_EXPORT_PRIVATE DecompressionOptimizer final {
public:
DecompressionOptimizer(Zone* zone, Graph* graph,
CommonOperatorBuilder* common,
MachineOperatorBuilder* machine);
~DecompressionOptimizer() = default;
DecompressionOptimizer(const DecompressionOptimizer&) = delete;
DecompressionOptimizer& operator=(const DecompressionOptimizer&) = delete;
// Assign States to the nodes, and then change the node's Operator to use the
// compressed version if possible.
void Reduce();
private:
// State refers to the node's state as follows:
// * kUnvisited === This node has yet to be visited.
// * kOnly32BitsObserved === This node either has been visited, or is on
// to_visit_. We couldn't find a node that observes the upper bits.
// * kEverythingObserved === This node either has been visited, or is on
// to_visit_. We found at least one node that observes the upper bits.
enum class State : uint8_t {
kUnvisited = 0,
kOnly32BitsObserved,
kEverythingObserved,
kNumberOfStates
};
// 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);
// Change node's 64-bit bitwise operator into a compressed one.
void ChangeWord64BitwiseOp(Node* const node, const Operator* new_op);
// 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
// kOnly32BitsObserved and visited, and finally marked as kEverythingObserved
// and visited.
void MarkNodes();
// Mark node's input as appropriate, according to node's opcode. Some input
// State may be updated, and therefore has to be revisited.
void MarkNodeInputs(Node* node);
void MarkAddressingBase(Node* base);
// Mark node's State to be state. We only do this if we have new information,
// 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 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) {
return states_.Get(node) == State::kEverythingObserved;
}
bool IsOnly32BitsObserved(Node* const node) {
return states_.Get(node) == State::kOnly32BitsObserved;
}
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 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_candidate_nodes_;
};
} // namespace compiler
} // namespace internal
} // namespace v8
#endif // V8_COMPILER_DECOMPRESSION_OPTIMIZER_H_
|