• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2015 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/branch-elimination.h"
6 
7 #include "src/base/small-vector.h"
8 #include "src/compiler/compiler-source-position-table.h"
9 #include "src/compiler/js-graph.h"
10 #include "src/compiler/node-properties.h"
11 #include "src/compiler/simplified-operator.h"
12 
13 namespace v8 {
14 namespace internal {
15 namespace compiler {
16 
BranchElimination(Editor * editor,JSGraph * js_graph,Zone * zone,SourcePositionTable * source_positions,Phase phase)17 BranchElimination::BranchElimination(Editor* editor, JSGraph* js_graph,
18                                      Zone* zone,
19                                      SourcePositionTable* source_positions,
20                                      Phase phase)
21     : AdvancedReducer(editor),
22       jsgraph_(js_graph),
23       node_conditions_(js_graph->graph()->NodeCount(), zone),
24       reduced_(js_graph->graph()->NodeCount(), zone),
25       zone_(zone),
26       source_positions_(source_positions),
27       dead_(js_graph->Dead()),
28       phase_(phase) {}
29 
30 BranchElimination::~BranchElimination() = default;
31 
Reduce(Node * node)32 Reduction BranchElimination::Reduce(Node* node) {
33   switch (node->opcode()) {
34     case IrOpcode::kDead:
35       return NoChange();
36     case IrOpcode::kDeoptimizeIf:
37     case IrOpcode::kDeoptimizeUnless:
38       return ReduceDeoptimizeConditional(node);
39     case IrOpcode::kMerge:
40       return ReduceMerge(node);
41     case IrOpcode::kLoop:
42       return ReduceLoop(node);
43     case IrOpcode::kBranch:
44       return ReduceBranch(node);
45     case IrOpcode::kIfFalse:
46       return ReduceIf(node, false);
47     case IrOpcode::kIfTrue:
48       return ReduceIf(node, true);
49     case IrOpcode::kTrapIf:
50     case IrOpcode::kTrapUnless:
51       return ReduceTrapConditional(node);
52     case IrOpcode::kStart:
53       return ReduceStart(node);
54     default:
55       if (node->op()->ControlOutputCount() > 0) {
56         return ReduceOtherControl(node);
57       }
58       break;
59   }
60   return NoChange();
61 }
62 
SimplifyBranchCondition(Node * branch)63 void BranchElimination::SimplifyBranchCondition(Node* branch) {
64   // Try to use a phi as a branch condition if the control flow from the branch
65   // is known from previous branches. For example, in the graph below, the
66   // control flow of the second_branch is predictable because the first_branch
67   // use the same branch condition. In such case, create a new phi with constant
68   // inputs and let the second branch use the phi as its branch condition. From
69   // this transformation, more branch folding opportunities would be exposed to
70   // later passes through branch cloning in effect-control-linearizer.
71   //
72   // condition                             condition
73   //    |   \                                   |
74   //    |  first_branch                        first_branch
75   //    |   /          \                       /          \
76   //    |  /            \                     /            \
77   //    |first_true  first_false           first_true  first_false
78   //    |  \           /                      \           /
79   //    |   \         /                        \         /
80   //    |  first_merge           ==>          first_merge
81   //    |       |                              /    |
82   //   second_branch                    1  0  /     |
83   //    /          \                     \ | /      |
84   //   /            \                     phi       |
85   // second_true  second_false              \       |
86   //                                      second_branch
87   //                                      /          \
88   //                                     /            \
89   //                                   second_true  second_false
90   //
91 
92   DCHECK_EQ(IrOpcode::kBranch, branch->opcode());
93   Node* merge = NodeProperties::GetControlInput(branch);
94   if (merge->opcode() != IrOpcode::kMerge) return;
95 
96   Node* branch_condition = branch->InputAt(0);
97   Node* previous_branch;
98   bool condition_value;
99   Graph* graph = jsgraph()->graph();
100   base::SmallVector<Node*, 2> phi_inputs;
101 
102   Node::Inputs inputs = merge->inputs();
103   int input_count = inputs.count();
104   for (int i = 0; i != input_count; ++i) {
105     Node* input = inputs[i];
106     ControlPathConditions from_input = node_conditions_.Get(input);
107     if (!from_input.LookupCondition(branch_condition, &previous_branch,
108                                     &condition_value)) {
109       return;
110     }
111 
112     if (phase_ == kEARLY) {
113       phi_inputs.emplace_back(condition_value ? jsgraph()->TrueConstant()
114                                               : jsgraph()->FalseConstant());
115     } else {
116       phi_inputs.emplace_back(
117           condition_value
118               ? graph->NewNode(jsgraph()->common()->Int32Constant(1))
119               : graph->NewNode(jsgraph()->common()->Int32Constant(0)));
120     }
121   }
122   phi_inputs.emplace_back(merge);
123   Node* new_phi = graph->NewNode(
124       common()->Phi(phase_ == kEARLY ? MachineRepresentation::kTagged
125                                      : MachineRepresentation::kWord32,
126                     input_count),
127       input_count + 1, &phi_inputs.at(0));
128 
129   // Replace the branch condition with the new phi.
130   NodeProperties::ReplaceValueInput(branch, new_phi, 0);
131 }
132 
ReduceBranch(Node * node)133 Reduction BranchElimination::ReduceBranch(Node* node) {
134   Node* condition = node->InputAt(0);
135   Node* control_input = NodeProperties::GetControlInput(node, 0);
136   if (!reduced_.Get(control_input)) return NoChange();
137   ControlPathConditions from_input = node_conditions_.Get(control_input);
138   Node* branch;
139   bool condition_value;
140   // If we know the condition we can discard the branch.
141   if (from_input.LookupCondition(condition, &branch, &condition_value)) {
142     for (Node* const use : node->uses()) {
143       switch (use->opcode()) {
144         case IrOpcode::kIfTrue:
145           Replace(use, condition_value ? control_input : dead());
146           break;
147         case IrOpcode::kIfFalse:
148           Replace(use, condition_value ? dead() : control_input);
149           break;
150         default:
151           UNREACHABLE();
152       }
153     }
154     return Replace(dead());
155   }
156   SimplifyBranchCondition(node);
157   // Trigger revisits of the IfTrue/IfFalse projections, since they depend on
158   // the branch condition.
159   for (Node* const use : node->uses()) {
160     Revisit(use);
161   }
162   return TakeConditionsFromFirstControl(node);
163 }
164 
165 // Simplify a trap following a merge.
166 // Assuming condition is in control1's path conditions, and !condition is in
167 // control2's path condtions, the following transformation takes place:
168 //
169 //            control1 control2     condition  effect1
170 //                  \   /                   \ /  |
171 //                  Merge                    X   |   control1
172 //                    |                     / \  |   /
173 //   effect1  effect2 |                    |   TrapIf     control2
174 //         \     |   /|         ==>        |         \    /
175 //          EffectPhi |                    | effect2  Merge
176 //              |    /                     |    |     /
177 //   condition  |   /                       \   |    /
178 //          \   |  /                        EffectPhi
179 //           TrapIf
180 // TODO(manoskouk): We require that the trap's effect input is the Merge's
181 // EffectPhi, so we can ensure that the new traps' effect inputs are not
182 // dominated by the Merge. Can we relax this?
TryPullTrapIntoMerge(Node * node)183 bool BranchElimination::TryPullTrapIntoMerge(Node* node) {
184   DCHECK(node->opcode() == IrOpcode::kTrapIf ||
185          node->opcode() == IrOpcode::kTrapUnless);
186   Node* merge = NodeProperties::GetControlInput(node);
187   DCHECK_EQ(merge->opcode(), IrOpcode::kMerge);
188   Node* condition = NodeProperties::GetValueInput(node, 0);
189   Node* effect_input = NodeProperties::GetEffectInput(node);
190   if (!(effect_input->opcode() == IrOpcode::kEffectPhi &&
191         NodeProperties::GetControlInput(effect_input) == merge)) {
192     return false;
193   }
194 
195   bool trapping_condition = node->opcode() == IrOpcode::kTrapIf;
196   base::SmallVector<Node*, 8> new_merge_inputs;
197   for (Edge edge : merge->input_edges()) {
198     Node* input = edge.to();
199     ControlPathConditions from_input = node_conditions_.Get(input);
200     Node* previous_branch;
201     bool condition_value;
202     if (!from_input.LookupCondition(condition, &previous_branch,
203                                     &condition_value)) {
204       return false;
205     }
206     if (condition_value == trapping_condition) {
207       Node* inputs[] = {
208           condition, NodeProperties::GetEffectInput(effect_input, edge.index()),
209           input};
210       Node* trap_clone = graph()->NewNode(node->op(), 3, inputs);
211       if (source_positions_) {
212         source_positions_->SetSourcePosition(
213             trap_clone, source_positions_->GetSourcePosition(node));
214       }
215       new_merge_inputs.emplace_back(trap_clone);
216     } else {
217       new_merge_inputs.emplace_back(input);
218     }
219   }
220 
221   for (int i = 0; i < merge->InputCount(); i++) {
222     merge->ReplaceInput(i, new_merge_inputs[i]);
223   }
224   ReplaceWithValue(node, dead(), dead(), merge);
225   node->Kill();
226   Revisit(merge);
227 
228   return true;
229 }
230 
ReduceTrapConditional(Node * node)231 Reduction BranchElimination::ReduceTrapConditional(Node* node) {
232   DCHECK(node->opcode() == IrOpcode::kTrapIf ||
233          node->opcode() == IrOpcode::kTrapUnless);
234   bool trapping_condition = node->opcode() == IrOpcode::kTrapIf;
235   Node* condition = node->InputAt(0);
236   Node* control_input = NodeProperties::GetControlInput(node, 0);
237   // If we do not know anything about the predecessor, do not propagate just
238   // yet because we will have to recompute anyway once we compute the
239   // predecessor.
240   if (!reduced_.Get(control_input)) return NoChange();
241 
242   // If the trap comes directly after a merge, pull it into the merge. This will
243   // unlock other optimizations later.
244   if (control_input->opcode() == IrOpcode::kMerge &&
245       TryPullTrapIntoMerge(node)) {
246     return Replace(dead());
247   }
248 
249   ControlPathConditions from_input = node_conditions_.Get(control_input);
250   Node* previous_branch;
251   bool condition_value;
252 
253   if (from_input.LookupCondition(condition, &previous_branch,
254                                  &condition_value)) {
255     if (condition_value == trapping_condition) {
256       // Special case: Trap directly inside a branch without sibling nodes.
257       // Replace the branch with the trap.
258       //    condition  control              condition  control
259       //     |   \      /                        \      /
260       //     |    Branch                          TrapIf
261       //     |    /    \            ==>             |
262       //     |  IfTrue IfFalse                 <subgraph2>
263       //     |  /        |
264       //    TrapIf   <subraph2>            Dead
265       //      |                              |
266       //   <subgraph1>                     <subgraph1>
267       // (and symmetrically for TrapUnless.)
268       if (((trapping_condition &&
269             control_input->opcode() == IrOpcode::kIfTrue) ||
270            (!trapping_condition &&
271             control_input->opcode() == IrOpcode::kIfFalse)) &&
272           control_input->UseCount() == 1) {
273         Node* branch = NodeProperties::GetControlInput(control_input);
274         DCHECK_EQ(branch->opcode(), IrOpcode::kBranch);
275         if (condition == NodeProperties::GetValueInput(branch, 0)) {
276           Node* other_if_branch = nullptr;
277           for (Node* use : branch->uses()) {
278             if (use != control_input) other_if_branch = use;
279           }
280           DCHECK_NOT_NULL(other_if_branch);
281 
282           node->ReplaceInput(NodeProperties::FirstControlIndex(node),
283                              NodeProperties::GetControlInput(branch));
284           ReplaceWithValue(node, dead(), dead(), dead());
285           ReplaceWithValue(other_if_branch, node, node, node);
286           other_if_branch->Kill();
287           control_input->Kill();
288           branch->Kill();
289           return Changed(node);
290         }
291       }
292 
293       // General case: This will always trap. Mark its outputs as dead and
294       // connect it to graph()->end().
295       ReplaceWithValue(node, dead(), dead(), dead());
296       Node* effect = NodeProperties::GetEffectInput(node);
297       Node* control = graph()->NewNode(common()->Throw(), effect, node);
298       NodeProperties::MergeControlToEnd(graph(), common(), control);
299       Revisit(graph()->end());
300       return Changed(node);
301     } else {
302       // This will not trap, remove it.
303       return Replace(control_input);
304     }
305   }
306   return UpdateConditions(node, from_input, condition, node,
307                           !trapping_condition, false);
308 }
309 
ReduceDeoptimizeConditional(Node * node)310 Reduction BranchElimination::ReduceDeoptimizeConditional(Node* node) {
311   DCHECK(node->opcode() == IrOpcode::kDeoptimizeIf ||
312          node->opcode() == IrOpcode::kDeoptimizeUnless);
313   bool condition_is_true = node->opcode() == IrOpcode::kDeoptimizeUnless;
314   DeoptimizeParameters p = DeoptimizeParametersOf(node->op());
315   Node* condition = NodeProperties::GetValueInput(node, 0);
316   Node* frame_state = NodeProperties::GetValueInput(node, 1);
317   Node* effect = NodeProperties::GetEffectInput(node);
318   Node* control = NodeProperties::GetControlInput(node);
319   // If we do not know anything about the predecessor, do not propagate just
320   // yet because we will have to recompute anyway once we compute the
321   // predecessor.
322   if (!reduced_.Get(control)) {
323     return NoChange();
324   }
325 
326   ControlPathConditions conditions = node_conditions_.Get(control);
327   bool condition_value;
328   Node* branch;
329   // If we know the condition we can discard the branch.
330   if (conditions.LookupCondition(condition, &branch, &condition_value)) {
331     if (condition_is_true == condition_value) {
332       // We don't update the conditions here, because we're replacing {node}
333       // with the {control} node that already contains the right information.
334       ReplaceWithValue(node, dead(), effect, control);
335     } else {
336       control = graph()->NewNode(common()->Deoptimize(p.reason(), p.feedback()),
337                                  frame_state, effect, control);
338       // TODO(bmeurer): This should be on the AdvancedReducer somehow.
339       NodeProperties::MergeControlToEnd(graph(), common(), control);
340       Revisit(graph()->end());
341     }
342     return Replace(dead());
343   }
344   return UpdateConditions(node, conditions, condition, node, condition_is_true,
345                           false);
346 }
347 
ReduceIf(Node * node,bool is_true_branch)348 Reduction BranchElimination::ReduceIf(Node* node, bool is_true_branch) {
349   // Add the condition to the list arriving from the input branch.
350   Node* branch = NodeProperties::GetControlInput(node, 0);
351   ControlPathConditions from_branch = node_conditions_.Get(branch);
352   // If we do not know anything about the predecessor, do not propagate just
353   // yet because we will have to recompute anyway once we compute the
354   // predecessor.
355   if (!reduced_.Get(branch)) {
356     return NoChange();
357   }
358   Node* condition = branch->InputAt(0);
359   return UpdateConditions(node, from_branch, condition, branch, is_true_branch,
360                           true);
361 }
362 
ReduceLoop(Node * node)363 Reduction BranchElimination::ReduceLoop(Node* node) {
364   // Here we rely on having only reducible loops:
365   // The loop entry edge always dominates the header, so we can just use
366   // the information from the loop entry edge.
367   return TakeConditionsFromFirstControl(node);
368 }
369 
ReduceMerge(Node * node)370 Reduction BranchElimination::ReduceMerge(Node* node) {
371   // Shortcut for the case when we do not know anything about some
372   // input.
373   Node::Inputs inputs = node->inputs();
374   for (Node* input : inputs) {
375     if (!reduced_.Get(input)) {
376       return NoChange();
377     }
378   }
379 
380   auto input_it = inputs.begin();
381 
382   DCHECK_GT(inputs.count(), 0);
383 
384   ControlPathConditions conditions = node_conditions_.Get(*input_it);
385   ++input_it;
386   // Merge the first input's conditions with the conditions from the other
387   // inputs.
388   auto input_end = inputs.end();
389   for (; input_it != input_end; ++input_it) {
390     // Change the current condition block list to a longest common tail of this
391     // condition list and the other list. (The common tail should correspond to
392     // the list from the common dominator.)
393     conditions.ResetToCommonAncestor(node_conditions_.Get(*input_it));
394   }
395   return UpdateConditions(node, conditions);
396 }
397 
ReduceStart(Node * node)398 Reduction BranchElimination::ReduceStart(Node* node) {
399   return UpdateConditions(node, ControlPathConditions(zone_));
400 }
401 
ReduceOtherControl(Node * node)402 Reduction BranchElimination::ReduceOtherControl(Node* node) {
403   DCHECK_EQ(1, node->op()->ControlInputCount());
404   return TakeConditionsFromFirstControl(node);
405 }
406 
TakeConditionsFromFirstControl(Node * node)407 Reduction BranchElimination::TakeConditionsFromFirstControl(Node* node) {
408   // We just propagate the information from the control input (ideally,
409   // we would only revisit control uses if there is change).
410   Node* input = NodeProperties::GetControlInput(node, 0);
411   if (!reduced_.Get(input)) return NoChange();
412   return UpdateConditions(node, node_conditions_.Get(input));
413 }
414 
UpdateConditions(Node * node,ControlPathConditions conditions)415 Reduction BranchElimination::UpdateConditions(
416     Node* node, ControlPathConditions conditions) {
417   // Only signal that the node has Changed if the condition information has
418   // changed.
419   bool reduced_changed = reduced_.Set(node, true);
420   bool node_conditions_changed = node_conditions_.Set(node, conditions);
421   if (reduced_changed || node_conditions_changed) {
422     return Changed(node);
423   }
424   return NoChange();
425 }
426 
UpdateConditions(Node * node,ControlPathConditions prev_conditions,Node * current_condition,Node * current_branch,bool is_true_branch,bool in_new_block)427 Reduction BranchElimination::UpdateConditions(
428     Node* node, ControlPathConditions prev_conditions, Node* current_condition,
429     Node* current_branch, bool is_true_branch, bool in_new_block) {
430   // The control path for the node is the path obtained by appending the
431   // current_condition to the prev_conditions. Use the original control path as
432   // a hint to avoid allocations.
433   if (in_new_block || prev_conditions.blocks_.Size() == 0) {
434     prev_conditions.AddConditionInNewBlock(zone_, current_condition,
435                                            current_branch, is_true_branch);
436   } else {
437     ControlPathConditions original = node_conditions_.Get(node);
438     prev_conditions.AddCondition(zone_, current_condition, current_branch,
439                                  is_true_branch, original);
440   }
441   return UpdateConditions(node, prev_conditions);
442 }
443 
AddCondition(Zone * zone,Node * condition,Node * branch,bool is_true,ControlPathConditions hint)444 void BranchElimination::ControlPathConditions::AddCondition(
445     Zone* zone, Node* condition, Node* branch, bool is_true,
446     ControlPathConditions hint) {
447   if (!LookupCondition(condition)) {
448     BranchCondition branch_condition(condition, branch, is_true);
449     FunctionalList<BranchCondition> prev_front = blocks_.Front();
450     if (hint.blocks_.Size() > 0) {
451       prev_front.PushFront(branch_condition, zone, hint.blocks_.Front());
452     } else {
453       prev_front.PushFront(branch_condition, zone);
454     }
455     blocks_.DropFront();
456     blocks_.PushFront(prev_front, zone);
457     conditions_.Set(condition, branch_condition);
458     SLOW_DCHECK(BlocksAndConditionsInvariant());
459   }
460 }
461 
AddConditionInNewBlock(Zone * zone,Node * condition,Node * branch,bool is_true)462 void BranchElimination::ControlPathConditions::AddConditionInNewBlock(
463     Zone* zone, Node* condition, Node* branch, bool is_true) {
464   FunctionalList<BranchCondition> new_block;
465   if (!LookupCondition(condition)) {
466     BranchCondition branch_condition(condition, branch, is_true);
467     new_block.PushFront(branch_condition, zone);
468     conditions_.Set(condition, branch_condition);
469   }
470   blocks_.PushFront(new_block, zone);
471   SLOW_DCHECK(BlocksAndConditionsInvariant());
472 }
473 
LookupCondition(Node * condition) const474 bool BranchElimination::ControlPathConditions::LookupCondition(
475     Node* condition) const {
476   return conditions_.Get(condition).IsSet();
477 }
478 
LookupCondition(Node * condition,Node ** branch,bool * is_true) const479 bool BranchElimination::ControlPathConditions::LookupCondition(
480     Node* condition, Node** branch, bool* is_true) const {
481   const BranchCondition& element = conditions_.Get(condition);
482   if (element.IsSet()) {
483     *is_true = element.is_true;
484     *branch = element.branch;
485     return true;
486   }
487   return false;
488 }
489 
ResetToCommonAncestor(ControlPathConditions other)490 void BranchElimination::ControlPathConditions::ResetToCommonAncestor(
491     ControlPathConditions other) {
492   while (other.blocks_.Size() > blocks_.Size()) other.blocks_.DropFront();
493   while (blocks_.Size() > other.blocks_.Size()) {
494     for (BranchCondition branch_condition : blocks_.Front()) {
495       conditions_.Set(branch_condition.condition, {});
496     }
497     blocks_.DropFront();
498   }
499   while (blocks_ != other.blocks_) {
500     for (BranchCondition branch_condition : blocks_.Front()) {
501       conditions_.Set(branch_condition.condition, {});
502     }
503     blocks_.DropFront();
504     other.blocks_.DropFront();
505   }
506   SLOW_DCHECK(BlocksAndConditionsInvariant());
507 }
508 
509 #if DEBUG
BlocksAndConditionsInvariant()510 bool BranchElimination::ControlPathConditions::BlocksAndConditionsInvariant() {
511   PersistentMap<Node*, BranchCondition> conditions_copy(conditions_);
512   for (auto block : blocks_) {
513     for (BranchCondition condition : block) {
514       // Every element of blocks_ has to be in conditions_.
515       if (conditions_copy.Get(condition.condition) != condition) return false;
516       conditions_copy.Set(condition.condition, {});
517     }
518   }
519   // Every element of {conditions_} has to be in {blocks_}. We removed all
520   // elements of blocks_ from condition_copy, so if it is not empty, the
521   // invariant fails.
522   return conditions_copy.begin() == conditions_copy.end();
523 }
524 #endif
525 
graph() const526 Graph* BranchElimination::graph() const { return jsgraph()->graph(); }
527 
isolate() const528 Isolate* BranchElimination::isolate() const { return jsgraph()->isolate(); }
529 
common() const530 CommonOperatorBuilder* BranchElimination::common() const {
531   return jsgraph()->common();
532 }
533 
534 }  // namespace compiler
535 }  // namespace internal
536 }  // namespace v8
537