1 // Copyright 2019 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "src/compiler/decompression-optimizer.h"
6
7 #include "src/compiler/graph.h"
8 #include "src/compiler/node-properties.h"
9
10 namespace v8 {
11 namespace internal {
12 namespace compiler {
13
14 namespace {
15
IsMachineLoad(Node * const node)16 bool IsMachineLoad(Node* const node) {
17 const IrOpcode::Value opcode = node->opcode();
18 return opcode == IrOpcode::kLoad || opcode == IrOpcode::kProtectedLoad ||
19 opcode == IrOpcode::kUnalignedLoad ||
20 opcode == IrOpcode::kLoadImmutable;
21 }
22
IsTaggedMachineLoad(Node * const node)23 bool IsTaggedMachineLoad(Node* const node) {
24 return IsMachineLoad(node) &&
25 CanBeTaggedPointer(LoadRepresentationOf(node->op()).representation());
26 }
27
IsHeapConstant(Node * const node)28 bool IsHeapConstant(Node* const node) {
29 return node->opcode() == IrOpcode::kHeapConstant;
30 }
31
IsTaggedPhi(Node * const node)32 bool IsTaggedPhi(Node* const node) {
33 if (node->opcode() == IrOpcode::kPhi) {
34 return CanBeTaggedPointer(PhiRepresentationOf(node->op()));
35 }
36 return false;
37 }
38
CanBeCompressed(Node * const node)39 bool CanBeCompressed(Node* const node) {
40 return IsHeapConstant(node) || IsTaggedMachineLoad(node) || IsTaggedPhi(node);
41 }
42
43 } // anonymous namespace
44
DecompressionOptimizer(Zone * zone,Graph * graph,CommonOperatorBuilder * common,MachineOperatorBuilder * machine)45 DecompressionOptimizer::DecompressionOptimizer(Zone* zone, Graph* graph,
46 CommonOperatorBuilder* common,
47 MachineOperatorBuilder* machine)
48 : graph_(graph),
49 common_(common),
50 machine_(machine),
51 states_(graph, static_cast<uint32_t>(State::kNumberOfStates)),
52 to_visit_(zone),
53 compressed_candidate_nodes_(zone) {}
54
MarkNodes()55 void DecompressionOptimizer::MarkNodes() {
56 MaybeMarkAndQueueForRevisit(graph()->end(), State::kOnly32BitsObserved);
57 while (!to_visit_.empty()) {
58 Node* const node = to_visit_.front();
59 to_visit_.pop_front();
60 MarkNodeInputs(node);
61 }
62 }
63
MarkNodeInputs(Node * node)64 void DecompressionOptimizer::MarkNodeInputs(Node* node) {
65 // Mark the value inputs.
66 switch (node->opcode()) {
67 // UNOPS.
68 case IrOpcode::kBitcastTaggedToWord:
69 case IrOpcode::kBitcastTaggedToWordForTagAndSmiBits:
70 // Replicate the bitcast's state for its input.
71 DCHECK_EQ(node->op()->ValueInputCount(), 1);
72 MaybeMarkAndQueueForRevisit(node->InputAt(0),
73 states_.Get(node)); // value
74 break;
75 case IrOpcode::kTruncateInt64ToInt32:
76 DCHECK_EQ(node->op()->ValueInputCount(), 1);
77 MaybeMarkAndQueueForRevisit(node->InputAt(0),
78 State::kOnly32BitsObserved); // value
79 break;
80 // BINOPS.
81 case IrOpcode::kInt32LessThan:
82 case IrOpcode::kInt32LessThanOrEqual:
83 case IrOpcode::kUint32LessThan:
84 case IrOpcode::kUint32LessThanOrEqual:
85 case IrOpcode::kWord32And:
86 case IrOpcode::kWord32Equal:
87 case IrOpcode::kWord32Shl:
88 DCHECK_EQ(node->op()->ValueInputCount(), 2);
89 MaybeMarkAndQueueForRevisit(node->InputAt(0),
90 State::kOnly32BitsObserved); // value_0
91 MaybeMarkAndQueueForRevisit(node->InputAt(1),
92 State::kOnly32BitsObserved); // value_1
93 break;
94 // SPECIAL CASES.
95 // SPECIAL CASES - Store.
96 case IrOpcode::kStore:
97 case IrOpcode::kProtectedStore:
98 case IrOpcode::kUnalignedStore: {
99 DCHECK_EQ(node->op()->ValueInputCount(), 3);
100 MaybeMarkAndQueueForRevisit(node->InputAt(0),
101 State::kEverythingObserved); // base pointer
102 MaybeMarkAndQueueForRevisit(node->InputAt(1),
103 State::kEverythingObserved); // index
104 // TODO(v8:7703): When the implementation is done, check if this ternary
105 // operator is too restrictive, since we only mark Tagged stores as 32
106 // bits.
107 MachineRepresentation representation =
108 node->opcode() == IrOpcode::kUnalignedStore
109 ? UnalignedStoreRepresentationOf(node->op())
110 : StoreRepresentationOf(node->op()).representation();
111 MaybeMarkAndQueueForRevisit(node->InputAt(2),
112 IsAnyTagged(representation)
113 ? State::kOnly32BitsObserved
114 : State::kEverythingObserved); // value
115 } break;
116 // SPECIAL CASES - Variable inputs.
117 // The deopt code knows how to handle Compressed inputs, both
118 // MachineRepresentation kCompressed values and CompressedHeapConstants.
119 case IrOpcode::kFrameState: // Fall through.
120 // TODO(v8:7703): kStateValues doesn't appear in any test linked to Loads or
121 // HeapConstants. Do we care about this case?
122 case IrOpcode::kStateValues: // Fall through.
123 case IrOpcode::kTypedStateValues:
124 for (int i = 0; i < node->op()->ValueInputCount(); ++i) {
125 MaybeMarkAndQueueForRevisit(node->InputAt(i),
126 State::kOnly32BitsObserved);
127 }
128 break;
129 case IrOpcode::kPhi: {
130 // Replicate the phi's state for its inputs.
131 State curr_state = states_.Get(node);
132 for (int i = 0; i < node->op()->ValueInputCount(); ++i) {
133 MaybeMarkAndQueueForRevisit(node->InputAt(i), curr_state);
134 }
135 break;
136 }
137 default:
138 // To be conservative, we assume that all value inputs need to be 64 bits
139 // unless noted otherwise.
140 for (int i = 0; i < node->op()->ValueInputCount(); ++i) {
141 MaybeMarkAndQueueForRevisit(node->InputAt(i),
142 State::kEverythingObserved);
143 }
144 break;
145 }
146
147 // We always mark the non-value input nodes as kOnly32BitsObserved so that
148 // they will be visited. If they need to be kEverythingObserved, they will be
149 // marked as such in a future pass.
150 for (int i = node->op()->ValueInputCount(); i < node->InputCount(); ++i) {
151 MaybeMarkAndQueueForRevisit(node->InputAt(i), State::kOnly32BitsObserved);
152 }
153 }
154
MaybeMarkAndQueueForRevisit(Node * const node,State state)155 void DecompressionOptimizer::MaybeMarkAndQueueForRevisit(Node* const node,
156 State state) {
157 DCHECK_NE(state, State::kUnvisited);
158 State previous_state = states_.Get(node);
159 // Only update the state if we have relevant new information.
160 if (previous_state == State::kUnvisited ||
161 (previous_state == State::kOnly32BitsObserved &&
162 state == State::kEverythingObserved)) {
163 states_.Set(node, state);
164 to_visit_.push_back(node);
165
166 if (state == State::kOnly32BitsObserved && CanBeCompressed(node)) {
167 compressed_candidate_nodes_.push_back(node);
168 }
169 }
170 }
171
ChangeHeapConstant(Node * const node)172 void DecompressionOptimizer::ChangeHeapConstant(Node* const node) {
173 DCHECK(IsHeapConstant(node));
174 NodeProperties::ChangeOp(
175 node, common()->CompressedHeapConstant(HeapConstantOf(node->op())));
176 }
177
ChangePhi(Node * const node)178 void DecompressionOptimizer::ChangePhi(Node* const node) {
179 DCHECK(IsTaggedPhi(node));
180
181 MachineRepresentation mach_rep = PhiRepresentationOf(node->op());
182 if (mach_rep == MachineRepresentation::kTagged) {
183 mach_rep = MachineRepresentation::kCompressed;
184 } else {
185 DCHECK_EQ(mach_rep, MachineRepresentation::kTaggedPointer);
186 mach_rep = MachineRepresentation::kCompressedPointer;
187 }
188
189 NodeProperties::ChangeOp(
190 node, common()->Phi(mach_rep, node->op()->ValueInputCount()));
191 }
192
ChangeLoad(Node * const node)193 void DecompressionOptimizer::ChangeLoad(Node* const node) {
194 DCHECK(IsMachineLoad(node));
195 // Change to a Compressed MachRep to avoid the full decompression.
196 LoadRepresentation load_rep = LoadRepresentationOf(node->op());
197 LoadRepresentation compressed_load_rep;
198 if (load_rep == MachineType::AnyTagged()) {
199 compressed_load_rep = MachineType::AnyCompressed();
200 } else {
201 DCHECK_EQ(load_rep, MachineType::TaggedPointer());
202 compressed_load_rep = MachineType::CompressedPointer();
203 }
204
205 // Change to the Operator with the Compressed MachineRepresentation.
206 switch (node->opcode()) {
207 case IrOpcode::kLoad:
208 NodeProperties::ChangeOp(node, machine()->Load(compressed_load_rep));
209 break;
210 case IrOpcode::kLoadImmutable:
211 NodeProperties::ChangeOp(node,
212 machine()->LoadImmutable(compressed_load_rep));
213 break;
214 case IrOpcode::kProtectedLoad:
215 NodeProperties::ChangeOp(node,
216 machine()->ProtectedLoad(compressed_load_rep));
217 break;
218 case IrOpcode::kUnalignedLoad:
219 NodeProperties::ChangeOp(node,
220 machine()->UnalignedLoad(compressed_load_rep));
221 break;
222 default:
223 UNREACHABLE();
224 }
225 }
226
ChangeNodes()227 void DecompressionOptimizer::ChangeNodes() {
228 for (Node* const node : compressed_candidate_nodes_) {
229 // compressed_candidate_nodes_ contains all the nodes that once had the
230 // State::kOnly32BitsObserved. If we later updated the state to be
231 // State::IsEverythingObserved, then we have to ignore them. This is less
232 // costly than removing them from the compressed_candidate_nodes_ NodeVector
233 // when we update them to State::IsEverythingObserved.
234 if (IsEverythingObserved(node)) continue;
235
236 switch (node->opcode()) {
237 case IrOpcode::kHeapConstant:
238 ChangeHeapConstant(node);
239 break;
240 case IrOpcode::kPhi:
241 ChangePhi(node);
242 break;
243 default:
244 ChangeLoad(node);
245 break;
246 }
247 }
248 }
249
Reduce()250 void DecompressionOptimizer::Reduce() {
251 MarkNodes();
252 ChangeNodes();
253 }
254
255 } // namespace compiler
256 } // namespace internal
257 } // namespace v8
258