• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2014 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/graph-reducer.h"
6 
7 #include <functional>
8 #include <limits>
9 
10 #include "src/codegen/tick-counter.h"
11 #include "src/compiler/graph.h"
12 #include "src/compiler/js-heap-broker.h"
13 #include "src/compiler/node-properties.h"
14 #include "src/compiler/node.h"
15 #include "src/compiler/verifier.h"
16 
17 namespace v8 {
18 namespace internal {
19 namespace compiler {
20 
21 enum class GraphReducer::State : uint8_t {
22   kUnvisited,
23   kRevisit,
24   kOnStack,
25   kVisited
26 };
27 
28 
Finalize()29 void Reducer::Finalize() {}
30 
GraphReducer(Zone * zone,Graph * graph,TickCounter * tick_counter,JSHeapBroker * broker,Node * dead)31 GraphReducer::GraphReducer(Zone* zone, Graph* graph, TickCounter* tick_counter,
32                            JSHeapBroker* broker, Node* dead)
33     : graph_(graph),
34       dead_(dead),
35       state_(graph, 4),
36       reducers_(zone),
37       revisit_(zone),
38       stack_(zone),
39       tick_counter_(tick_counter),
40       broker_(broker) {
41   if (dead != nullptr) {
42     NodeProperties::SetType(dead_, Type::None());
43   }
44 }
45 
46 GraphReducer::~GraphReducer() = default;
47 
48 
AddReducer(Reducer * reducer)49 void GraphReducer::AddReducer(Reducer* reducer) {
50   reducers_.push_back(reducer);
51 }
52 
53 
ReduceNode(Node * node)54 void GraphReducer::ReduceNode(Node* node) {
55   DCHECK(stack_.empty());
56   DCHECK(revisit_.empty());
57   Push(node);
58   for (;;) {
59     if (!stack_.empty()) {
60       // Process the node on the top of the stack, potentially pushing more or
61       // popping the node off the stack.
62       ReduceTop();
63     } else if (!revisit_.empty()) {
64       // If the stack becomes empty, revisit any nodes in the revisit queue.
65       Node* const node = revisit_.front();
66       revisit_.pop();
67       if (state_.Get(node) == State::kRevisit) {
68         // state can change while in queue.
69         Push(node);
70       }
71     } else {
72       // Run all finalizers.
73       for (Reducer* const reducer : reducers_) reducer->Finalize();
74 
75       // Check if we have new nodes to revisit.
76       if (revisit_.empty()) break;
77     }
78   }
79   DCHECK(revisit_.empty());
80   DCHECK(stack_.empty());
81 }
82 
83 
ReduceGraph()84 void GraphReducer::ReduceGraph() { ReduceNode(graph()->end()); }
85 
86 
Reduce(Node * const node)87 Reduction GraphReducer::Reduce(Node* const node) {
88   auto skip = reducers_.end();
89   for (auto i = reducers_.begin(); i != reducers_.end();) {
90     if (i != skip) {
91       tick_counter_->TickAndMaybeEnterSafepoint();
92       Reduction reduction = (*i)->Reduce(node);
93       if (!reduction.Changed()) {
94         // No change from this reducer.
95       } else if (reduction.replacement() == node) {
96         // {replacement} == {node} represents an in-place reduction. Rerun
97         // all the other reducers for this node, as now there may be more
98         // opportunities for reduction.
99         if (FLAG_trace_turbo_reduction) {
100           UnparkedScopeIfNeeded unparked(broker_);
101           // TODO(neis): Disallow racy handle dereference once we stop
102           // supporting --no-local-heaps --no-turbo-direct-heap-access.
103           AllowHandleDereference allow_deref;
104           StdoutStream{} << "- In-place update of #" << *node << " by reducer "
105                          << (*i)->reducer_name() << std::endl;
106         }
107         skip = i;
108         i = reducers_.begin();
109         continue;
110       } else {
111         // {node} was replaced by another node.
112         if (FLAG_trace_turbo_reduction) {
113           UnparkedScopeIfNeeded unparked(broker_);
114           // TODO(neis): Disallow racy handle dereference once we stop
115           // supporting --no-local-heaps --no-turbo-direct-heap-access.
116           AllowHandleDereference allow_deref;
117           StdoutStream{} << "- Replacement of #" << *node << " with #"
118                          << *(reduction.replacement()) << " by reducer "
119                          << (*i)->reducer_name() << std::endl;
120         }
121         return reduction;
122       }
123     }
124     ++i;
125   }
126   if (skip == reducers_.end()) {
127     // No change from any reducer.
128     return Reducer::NoChange();
129   }
130   // At least one reducer did some in-place reduction.
131   return Reducer::Changed(node);
132 }
133 
134 
ReduceTop()135 void GraphReducer::ReduceTop() {
136   NodeState& entry = stack_.top();
137   Node* node = entry.node;
138   DCHECK_EQ(State::kOnStack, state_.Get(node));
139 
140   if (node->IsDead()) return Pop();  // Node was killed while on stack.
141 
142   Node::Inputs node_inputs = node->inputs();
143 
144   // Recurse on an input if necessary.
145   int start = entry.input_index < node_inputs.count() ? entry.input_index : 0;
146   for (int i = start; i < node_inputs.count(); ++i) {
147     Node* input = node_inputs[i];
148     if (input != node && Recurse(input)) {
149       entry.input_index = i + 1;
150       return;
151     }
152   }
153   for (int i = 0; i < start; ++i) {
154     Node* input = node_inputs[i];
155     if (input != node && Recurse(input)) {
156       entry.input_index = i + 1;
157       return;
158     }
159   }
160 
161   // Remember the max node id before reduction.
162   NodeId const max_id = static_cast<NodeId>(graph()->NodeCount() - 1);
163 
164   // All inputs should be visited or on stack. Apply reductions to node.
165   Reduction reduction = Reduce(node);
166 
167   // If there was no reduction, pop {node} and continue.
168   if (!reduction.Changed()) return Pop();
169 
170   // Check if the reduction is an in-place update of the {node}.
171   Node* const replacement = reduction.replacement();
172   if (replacement == node) {
173     for (Node* const user : node->uses()) {
174       DCHECK_IMPLIES(user == node, state_.Get(node) != State::kVisited);
175       Revisit(user);
176     }
177 
178     // In-place update of {node}, may need to recurse on an input.
179     Node::Inputs node_inputs = node->inputs();
180     for (int i = 0; i < node_inputs.count(); ++i) {
181       Node* input = node_inputs[i];
182       if (input != node && Recurse(input)) {
183         entry.input_index = i + 1;
184         return;
185       }
186     }
187   }
188 
189   // After reducing the node, pop it off the stack.
190   Pop();
191 
192   // Check if we have a new replacement.
193   if (replacement != node) {
194     Replace(node, replacement, max_id);
195   }
196 }
197 
198 
Replace(Node * node,Node * replacement)199 void GraphReducer::Replace(Node* node, Node* replacement) {
200   Replace(node, replacement, std::numeric_limits<NodeId>::max());
201 }
202 
203 
Replace(Node * node,Node * replacement,NodeId max_id)204 void GraphReducer::Replace(Node* node, Node* replacement, NodeId max_id) {
205   if (node == graph()->start()) graph()->SetStart(replacement);
206   if (node == graph()->end()) graph()->SetEnd(replacement);
207   if (replacement->id() <= max_id) {
208     // {replacement} is an old node, so unlink {node} and assume that
209     // {replacement} was already reduced and finish.
210     for (Edge edge : node->use_edges()) {
211       Node* const user = edge.from();
212       Verifier::VerifyEdgeInputReplacement(edge, replacement);
213       edge.UpdateTo(replacement);
214       // Don't revisit this node if it refers to itself.
215       if (user != node) Revisit(user);
216     }
217     node->Kill();
218   } else {
219     // Replace all old uses of {node} with {replacement}, but allow new nodes
220     // created by this reduction to use {node}.
221     for (Edge edge : node->use_edges()) {
222       Node* const user = edge.from();
223       if (user->id() <= max_id) {
224         edge.UpdateTo(replacement);
225         // Don't revisit this node if it refers to itself.
226         if (user != node) Revisit(user);
227       }
228     }
229     // Unlink {node} if it's no longer used.
230     if (node->uses().empty()) node->Kill();
231 
232     // If there was a replacement, reduce it after popping {node}.
233     Recurse(replacement);
234   }
235 }
236 
237 
ReplaceWithValue(Node * node,Node * value,Node * effect,Node * control)238 void GraphReducer::ReplaceWithValue(Node* node, Node* value, Node* effect,
239                                     Node* control) {
240   if (effect == nullptr && node->op()->EffectInputCount() > 0) {
241     effect = NodeProperties::GetEffectInput(node);
242   }
243   if (control == nullptr && node->op()->ControlInputCount() > 0) {
244     control = NodeProperties::GetControlInput(node);
245   }
246 
247   // Requires distinguishing between value, effect and control edges.
248   for (Edge edge : node->use_edges()) {
249     Node* const user = edge.from();
250     DCHECK(!user->IsDead());
251     if (NodeProperties::IsControlEdge(edge)) {
252       if (user->opcode() == IrOpcode::kIfSuccess) {
253         Replace(user, control);
254       } else if (user->opcode() == IrOpcode::kIfException) {
255         DCHECK_NOT_NULL(dead_);
256         edge.UpdateTo(dead_);
257         Revisit(user);
258       } else {
259         DCHECK_NOT_NULL(control);
260         edge.UpdateTo(control);
261         Revisit(user);
262       }
263     } else if (NodeProperties::IsEffectEdge(edge)) {
264       DCHECK_NOT_NULL(effect);
265       edge.UpdateTo(effect);
266       Revisit(user);
267     } else {
268       DCHECK_NOT_NULL(value);
269       edge.UpdateTo(value);
270       Revisit(user);
271     }
272   }
273 }
274 
275 
Pop()276 void GraphReducer::Pop() {
277   Node* node = stack_.top().node;
278   state_.Set(node, State::kVisited);
279   stack_.pop();
280 }
281 
282 
Push(Node * const node)283 void GraphReducer::Push(Node* const node) {
284   DCHECK_NE(State::kOnStack, state_.Get(node));
285   state_.Set(node, State::kOnStack);
286   stack_.push({node, 0});
287 }
288 
289 
Recurse(Node * node)290 bool GraphReducer::Recurse(Node* node) {
291   if (state_.Get(node) > State::kRevisit) return false;
292   Push(node);
293   return true;
294 }
295 
296 
Revisit(Node * node)297 void GraphReducer::Revisit(Node* node) {
298   if (state_.Get(node) == State::kVisited) {
299     state_.Set(node, State::kRevisit);
300     revisit_.push(node);
301   }
302 }
303 
304 }  // namespace compiler
305 }  // namespace internal
306 }  // namespace v8
307