• 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/js-graph.h"
9 #include "src/compiler/node-properties.h"
10 #include "src/compiler/simplified-operator.h"
11 
12 namespace v8 {
13 namespace internal {
14 namespace compiler {
15 
BranchElimination(Editor * editor,JSGraph * js_graph,Zone * zone,Phase phase)16 BranchElimination::BranchElimination(Editor* editor, JSGraph* js_graph,
17                                      Zone* zone, Phase phase)
18     : AdvancedReducer(editor),
19       jsgraph_(js_graph),
20       node_conditions_(js_graph->graph()->NodeCount(), zone),
21       reduced_(js_graph->graph()->NodeCount(), zone),
22       zone_(zone),
23       dead_(js_graph->Dead()),
24       phase_(phase) {}
25 
26 BranchElimination::~BranchElimination() = default;
27 
Reduce(Node * node)28 Reduction BranchElimination::Reduce(Node* node) {
29   switch (node->opcode()) {
30     case IrOpcode::kDead:
31       return NoChange();
32     case IrOpcode::kDeoptimizeIf:
33     case IrOpcode::kDeoptimizeUnless:
34       return ReduceDeoptimizeConditional(node);
35     case IrOpcode::kMerge:
36       return ReduceMerge(node);
37     case IrOpcode::kLoop:
38       return ReduceLoop(node);
39     case IrOpcode::kBranch:
40       return ReduceBranch(node);
41     case IrOpcode::kIfFalse:
42       return ReduceIf(node, false);
43     case IrOpcode::kIfTrue:
44       return ReduceIf(node, true);
45     case IrOpcode::kStart:
46       return ReduceStart(node);
47     default:
48       if (node->op()->ControlOutputCount() > 0) {
49         return ReduceOtherControl(node);
50       }
51       break;
52   }
53   return NoChange();
54 }
55 
SimplifyBranchCondition(Node * branch)56 void BranchElimination::SimplifyBranchCondition(Node* branch) {
57   // Try to use a phi as a branch condition if the control flow from the branch
58   // is known from previous branches. For example, in the graph below, the
59   // control flow of the second_branch is predictable because the first_branch
60   // use the same branch condition. In such case, create a new phi with constant
61   // inputs and let the second branch use the phi as its branch condition. From
62   // this transformation, more branch folding opportunities would be exposed to
63   // later passes through branch cloning in effect-control-linearizer.
64   //
65   // condition                             condition
66   //    |   \                                   |
67   //    |  first_branch                        first_branch
68   //    |   /          \                       /          \
69   //    |  /            \                     /            \
70   //    |first_true  first_false           first_true  first_false
71   //    |  \           /                      \           /
72   //    |   \         /                        \         /
73   //    |  first_merge           ==>          first_merge
74   //    |       |                                   |
75   //   second_branch                    1    0      |
76   //    /          \                     \  /       |
77   //   /            \                     phi       |
78   // second_true  second_false              \       |
79   //                                      second_branch
80   //                                      /          \
81   //                                     /            \
82   //                                   second_true  second_false
83   //
84 
85   DCHECK_EQ(IrOpcode::kBranch, branch->opcode());
86   Node* merge = NodeProperties::GetControlInput(branch);
87   if (merge->opcode() != IrOpcode::kMerge) return;
88 
89   Node* branch_condition = branch->InputAt(0);
90   Node* previous_branch;
91   bool condition_value;
92   Graph* graph = jsgraph()->graph();
93   base::SmallVector<Node*, 2> phi_inputs;
94 
95   Node::Inputs inputs = merge->inputs();
96   int input_count = inputs.count();
97   for (int i = 0; i != input_count; ++i) {
98     Node* input = inputs[i];
99     ControlPathConditions from_input = node_conditions_.Get(input);
100     if (!from_input.LookupCondition(branch_condition, &previous_branch,
101                                     &condition_value))
102       return;
103 
104     if (phase_ == kEARLY) {
105       phi_inputs.emplace_back(condition_value ? jsgraph()->TrueConstant()
106                                               : jsgraph()->FalseConstant());
107     } else {
108       phi_inputs.emplace_back(
109           condition_value
110               ? graph->NewNode(jsgraph()->common()->Int32Constant(1))
111               : graph->NewNode(jsgraph()->common()->Int32Constant(0)));
112     }
113   }
114   phi_inputs.emplace_back(merge);
115   Node* new_phi = graph->NewNode(
116       common()->Phi(phase_ == kEARLY ? MachineRepresentation::kTagged
117                                      : MachineRepresentation::kWord32,
118                     input_count),
119       input_count + 1, &phi_inputs.at(0));
120 
121   // Replace the branch condition with the new phi.
122   NodeProperties::ReplaceValueInput(branch, new_phi, 0);
123 }
124 
ReduceBranch(Node * node)125 Reduction BranchElimination::ReduceBranch(Node* node) {
126   Node* condition = node->InputAt(0);
127   Node* control_input = NodeProperties::GetControlInput(node, 0);
128   ControlPathConditions from_input = node_conditions_.Get(control_input);
129   Node* branch;
130   bool condition_value;
131   // If we know the condition we can discard the branch.
132   if (from_input.LookupCondition(condition, &branch, &condition_value)) {
133     MarkAsSafetyCheckIfNeeded(branch, node);
134     for (Node* const use : node->uses()) {
135       switch (use->opcode()) {
136         case IrOpcode::kIfTrue:
137           Replace(use, condition_value ? control_input : dead());
138           break;
139         case IrOpcode::kIfFalse:
140           Replace(use, condition_value ? dead() : control_input);
141           break;
142         default:
143           UNREACHABLE();
144       }
145     }
146     return Replace(dead());
147   }
148   SimplifyBranchCondition(node);
149   // Trigger revisits of the IfTrue/IfFalse projections, since they depend on
150   // the branch condition.
151   for (Node* const use : node->uses()) {
152     Revisit(use);
153   }
154   return TakeConditionsFromFirstControl(node);
155 }
156 
ReduceDeoptimizeConditional(Node * node)157 Reduction BranchElimination::ReduceDeoptimizeConditional(Node* node) {
158   DCHECK(node->opcode() == IrOpcode::kDeoptimizeIf ||
159          node->opcode() == IrOpcode::kDeoptimizeUnless);
160   bool condition_is_true = node->opcode() == IrOpcode::kDeoptimizeUnless;
161   DeoptimizeParameters p = DeoptimizeParametersOf(node->op());
162   Node* condition = NodeProperties::GetValueInput(node, 0);
163   Node* frame_state = NodeProperties::GetValueInput(node, 1);
164   Node* effect = NodeProperties::GetEffectInput(node);
165   Node* control = NodeProperties::GetControlInput(node);
166   // If we do not know anything about the predecessor, do not propagate just
167   // yet because we will have to recompute anyway once we compute the
168   // predecessor.
169   if (!reduced_.Get(control)) {
170     return NoChange();
171   }
172 
173   ControlPathConditions conditions = node_conditions_.Get(control);
174   bool condition_value;
175   Node* branch;
176   // If we know the condition we can discard the branch.
177   if (conditions.LookupCondition(condition, &branch, &condition_value)) {
178     MarkAsSafetyCheckIfNeeded(branch, node);
179     if (condition_is_true == condition_value) {
180       // We don't update the conditions here, because we're replacing {node}
181       // with the {control} node that already contains the right information.
182       ReplaceWithValue(node, dead(), effect, control);
183     } else {
184       control = graph()->NewNode(
185           common()->Deoptimize(p.kind(), p.reason(), p.feedback()), frame_state,
186           effect, control);
187       // TODO(bmeurer): This should be on the AdvancedReducer somehow.
188       NodeProperties::MergeControlToEnd(graph(), common(), control);
189       Revisit(graph()->end());
190     }
191     return Replace(dead());
192   }
193   return UpdateConditions(node, conditions, condition, node, condition_is_true);
194 }
195 
ReduceIf(Node * node,bool is_true_branch)196 Reduction BranchElimination::ReduceIf(Node* node, bool is_true_branch) {
197   // Add the condition to the list arriving from the input branch.
198   Node* branch = NodeProperties::GetControlInput(node, 0);
199   ControlPathConditions from_branch = node_conditions_.Get(branch);
200   // If we do not know anything about the predecessor, do not propagate just
201   // yet because we will have to recompute anyway once we compute the
202   // predecessor.
203   if (!reduced_.Get(branch)) {
204     return NoChange();
205   }
206   Node* condition = branch->InputAt(0);
207   return UpdateConditions(node, from_branch, condition, branch, is_true_branch);
208 }
209 
ReduceLoop(Node * node)210 Reduction BranchElimination::ReduceLoop(Node* node) {
211   // Here we rely on having only reducible loops:
212   // The loop entry edge always dominates the header, so we can just use
213   // the information from the loop entry edge.
214   return TakeConditionsFromFirstControl(node);
215 }
216 
ReduceMerge(Node * node)217 Reduction BranchElimination::ReduceMerge(Node* node) {
218   // Shortcut for the case when we do not know anything about some
219   // input.
220   Node::Inputs inputs = node->inputs();
221   for (Node* input : inputs) {
222     if (!reduced_.Get(input)) {
223       return NoChange();
224     }
225   }
226 
227   auto input_it = inputs.begin();
228 
229   DCHECK_GT(inputs.count(), 0);
230 
231   ControlPathConditions conditions = node_conditions_.Get(*input_it);
232   ++input_it;
233   // Merge the first input's conditions with the conditions from the other
234   // inputs.
235   auto input_end = inputs.end();
236   for (; input_it != input_end; ++input_it) {
237     // Change the current condition list to a longest common tail
238     // of this condition list and the other list. (The common tail
239     // should correspond to the list from the common dominator.)
240     conditions.ResetToCommonAncestor(node_conditions_.Get(*input_it));
241   }
242   return UpdateConditions(node, conditions);
243 }
244 
ReduceStart(Node * node)245 Reduction BranchElimination::ReduceStart(Node* node) {
246   return UpdateConditions(node, {});
247 }
248 
ReduceOtherControl(Node * node)249 Reduction BranchElimination::ReduceOtherControl(Node* node) {
250   DCHECK_EQ(1, node->op()->ControlInputCount());
251   return TakeConditionsFromFirstControl(node);
252 }
253 
TakeConditionsFromFirstControl(Node * node)254 Reduction BranchElimination::TakeConditionsFromFirstControl(Node* node) {
255   // We just propagate the information from the control input (ideally,
256   // we would only revisit control uses if there is change).
257   Node* input = NodeProperties::GetControlInput(node, 0);
258   if (!reduced_.Get(input)) return NoChange();
259   return UpdateConditions(node, node_conditions_.Get(input));
260 }
261 
UpdateConditions(Node * node,ControlPathConditions conditions)262 Reduction BranchElimination::UpdateConditions(
263     Node* node, ControlPathConditions conditions) {
264   // Only signal that the node has Changed if the condition information has
265   // changed.
266   if (reduced_.Set(node, true) | node_conditions_.Set(node, conditions)) {
267     return Changed(node);
268   }
269   return NoChange();
270 }
271 
UpdateConditions(Node * node,ControlPathConditions prev_conditions,Node * current_condition,Node * current_branch,bool is_true_branch)272 Reduction BranchElimination::UpdateConditions(
273     Node* node, ControlPathConditions prev_conditions, Node* current_condition,
274     Node* current_branch, bool is_true_branch) {
275   ControlPathConditions original = node_conditions_.Get(node);
276   // The control path for the node is the path obtained by appending the
277   // current_condition to the prev_conditions. Use the original control path as
278   // a hint to avoid allocations.
279   prev_conditions.AddCondition(zone_, current_condition, current_branch,
280                                is_true_branch, original);
281   return UpdateConditions(node, prev_conditions);
282 }
283 
AddCondition(Zone * zone,Node * condition,Node * branch,bool is_true,ControlPathConditions hint)284 void BranchElimination::ControlPathConditions::AddCondition(
285     Zone* zone, Node* condition, Node* branch, bool is_true,
286     ControlPathConditions hint) {
287   DCHECK(!LookupCondition(condition, nullptr, nullptr));
288   PushFront({condition, branch, is_true}, zone, hint);
289 }
290 
LookupCondition(Node * condition,Node ** branch,bool * is_true) const291 bool BranchElimination::ControlPathConditions::LookupCondition(
292     Node* condition, Node** branch, bool* is_true) const {
293   for (BranchCondition element : *this) {
294     if (element.condition == condition) {
295       *is_true = element.is_true;
296       *branch = element.branch;
297       return true;
298     }
299   }
300   return false;
301 }
302 
MarkAsSafetyCheckIfNeeded(Node * branch,Node * node)303 void BranchElimination::MarkAsSafetyCheckIfNeeded(Node* branch, Node* node) {
304   // Check if {branch} is dead because we might have a stale side-table entry.
305   if (!branch->IsDead() && branch->opcode() != IrOpcode::kDead) {
306     IsSafetyCheck branch_safety = IsSafetyCheckOf(branch->op());
307     IsSafetyCheck combined_safety =
308         CombineSafetyChecks(branch_safety, IsSafetyCheckOf(node->op()));
309     if (branch_safety != combined_safety) {
310       NodeProperties::ChangeOp(
311           branch, common()->MarkAsSafetyCheck(branch->op(), combined_safety));
312     }
313   }
314 }
315 
graph() const316 Graph* BranchElimination::graph() const { return jsgraph()->graph(); }
317 
isolate() const318 Isolate* BranchElimination::isolate() const { return jsgraph()->isolate(); }
319 
common() const320 CommonOperatorBuilder* BranchElimination::common() const {
321   return jsgraph()->common();
322 }
323 
324 }  // namespace compiler
325 }  // namespace internal
326 }  // namespace v8
327