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