v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
branch-elimination.cc
Go to the documentation of this file.
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
6
13
14namespace v8 {
15namespace internal {
16namespace compiler {
17
19 Zone* zone, Phase phase)
20 : AdvancedReducerWithControlPathState(editor, zone, js_graph->graph()),
21 jsgraph_(js_graph),
22 dead_(js_graph->Dead()),
23 phase_(phase) {}
24
26
28 switch (node->opcode()) {
29 case IrOpcode::kDead:
30 return NoChange();
31 case IrOpcode::kDeoptimizeIf:
32 case IrOpcode::kDeoptimizeUnless:
33 return ReduceDeoptimizeConditional(node);
34 case IrOpcode::kMerge:
35 return ReduceMerge(node);
36 case IrOpcode::kLoop:
37 return ReduceLoop(node);
38 case IrOpcode::kBranch:
39 return ReduceBranch(node);
40 case IrOpcode::kIfFalse:
41 return ReduceIf(node, false);
42 case IrOpcode::kIfTrue:
43 return ReduceIf(node, true);
44 case IrOpcode::kTrapIf:
45 case IrOpcode::kTrapUnless:
46 return ReduceTrapConditional(node);
47 case IrOpcode::kStart:
48 return ReduceStart(node);
49 default:
50 if (node->op()->ControlOutputCount() > 0) {
51 return ReduceOtherControl(node);
52 } else {
53 return NoChange();
54 }
55 }
56}
57
59 // Try to use a phi as a branch condition if the control flow from the branch
60 // is known from previous branches. For example, in the graph below, the
61 // control flow of the second_branch is predictable because the first_branch
62 // use the same branch condition. In such case, create a new phi with constant
63 // inputs and let the second branch use the phi as its branch condition. From
64 // this transformation, more branch folding opportunities would be exposed to
65 // later passes through branch cloning in effect-control-linearizer.
66 //
67 // condition condition
68 // | \ |
69 // | first_branch first_branch
70 // | / \ / \
71 // | / \ / \
72 // |first_true first_false first_true first_false
73 // | \ / \ /
74 // | \ / \ /
75 // | first_merge ==> first_merge
76 // | | / |
77 // second_branch 1 0 / |
78 // / \ \ | / |
79 // / \ phi |
80 // second_true second_false \ |
81 // second_branch
82 // / \
83 // / \
84 // second_true second_false
85 //
86
87 auto SemanticsOf = [phase = this->phase_](Node* branch) {
89 if (branch->opcode() == IrOpcode::kBranch) {
90 semantics = BranchParametersOf(branch->op()).semantics();
91 }
92 if (semantics == BranchSemantics::kUnspecified) {
93 semantics =
95 }
96 return semantics;
97 };
98
99 DCHECK_EQ(IrOpcode::kBranch, branch->opcode());
100 Node* merge = NodeProperties::GetControlInput(branch);
101 if (merge->opcode() != IrOpcode::kMerge) return;
102
103 Node* condition = branch->InputAt(0);
104 BranchSemantics semantics = SemanticsOf(branch);
105 TFGraph* graph = jsgraph()->graph();
107
108 Node::Inputs inputs = merge->inputs();
109 int input_count = inputs.count();
110 for (int i = 0; i != input_count; ++i) {
111 Node* input = inputs[i];
112 ControlPathConditions from_input = GetState(input);
113
114 BranchCondition branch_condition = from_input.LookupState(condition);
115 if (!branch_condition.IsSet()) return;
116 if (SemanticsOf(branch_condition.branch) != semantics) return;
117 bool condition_value = branch_condition.is_true;
118
119 if (semantics == BranchSemantics::kJS) {
120 phi_inputs.emplace_back(jsgraph()->BooleanConstant(condition_value));
121 } else {
123 phi_inputs.emplace_back(
124 condition_value
125 ? graph->NewNode(jsgraph()->common()->Int32Constant(1))
126 : graph->NewNode(jsgraph()->common()->Int32Constant(0)));
127 }
128 }
129 phi_inputs.emplace_back(merge);
130 Node* new_phi =
131 graph->NewNode(common()->Phi(semantics == BranchSemantics::kJS
134 input_count),
135 input_count + 1, &phi_inputs.at(0));
136
137 // Replace the branch condition with the new phi.
138 NodeProperties::ReplaceValueInput(branch, new_phi, 0);
139}
140
142 Node* phi,
143 Node* merge) {
144 // If the condition of the branch comes from two constant values,
145 // then try to merge the branches successors into its predecessors,
146 // and eliminate the (branch, phi, merge) nodes.
147 //
148 // pred0 pred1
149 // \ /
150 // merge 0 1
151 // | \___________ | /
152 // | \ | / pred0 pred1
153 // | phi | |
154 // | _____________/ => | |
155 // | / | |
156 // branch succ0 succ1
157 // / \
158 // false true
159 // | |
160 // succ0 succ1
161 //
162
163 DCHECK_EQ(branch->opcode(), IrOpcode::kBranch);
164 DCHECK_EQ(phi->opcode(), IrOpcode::kPhi);
165 DCHECK_EQ(merge->opcode(), IrOpcode::kMerge);
167 if (!phi->OwnedBy(branch)) return false;
168 if (phi->InputCount() != 3) return false;
169 if (phi->InputAt(2) != merge) return false;
170 if (merge->UseCount() != 2) return false;
171
172 Node::Inputs phi_inputs = phi->inputs();
173 Node* first_value = phi_inputs[0];
174 Node* second_value = phi_inputs[1];
175 if (first_value->opcode() != IrOpcode::kInt32Constant ||
176 second_value->opcode() != IrOpcode::kInt32Constant) {
177 return false;
178 }
179 Node::Inputs merge_inputs = merge->inputs();
180 Node* predecessor0 = merge_inputs[0];
181 Node* predecessor1 = merge_inputs[1];
182 DCHECK_EQ(branch->op()->ControlOutputCount(), 2);
183 Node** projections = zone()->AllocateArray<Node*>(2);
184 NodeProperties::CollectControlProjections(branch, projections, 2);
185 Node* branch_true = projections[0];
186 Node* branch_false = projections[1];
187 DCHECK_EQ(branch_true->opcode(), IrOpcode::kIfTrue);
188 DCHECK_EQ(branch_false->opcode(), IrOpcode::kIfFalse);
189
190 // The input values of phi should be true(1) and false(0).
191 Int32Matcher mfirst_value(first_value);
192 Int32Matcher msecond_value(second_value);
193 Node* predecessor_true = nullptr;
194 Node* predecessor_false = nullptr;
195 if (mfirst_value.Is(1) && msecond_value.Is(0)) {
196 predecessor_true = predecessor0;
197 predecessor_false = predecessor1;
198 } else if (mfirst_value.Is(0) && msecond_value.Is(1)) {
199 predecessor_true = predecessor1;
200 predecessor_false = predecessor0;
201 } else {
202 return false;
203 }
204
205 // Merge the branches successors into its predecessors.
206 for (Edge edge : branch_true->use_edges()) {
207 edge.UpdateTo(predecessor_true);
208 }
209 for (Edge edge : branch_false->use_edges()) {
210 edge.UpdateTo(predecessor_false);
211 }
212
213 branch_true->Kill();
214 branch_false->Kill();
215 branch->Kill();
216 phi->Kill();
217 merge->Kill();
218 return true;
219}
220
222 Node* condition = node->InputAt(0);
223 Node* control_input = NodeProperties::GetControlInput(node, 0);
224 if (!IsReduced(control_input)) return NoChange();
225 ControlPathConditions from_input = GetState(control_input);
226 // If we know the condition we can discard the branch.
227 BranchCondition branch_condition = from_input.LookupState(condition);
228 if (branch_condition.IsSet()) {
229 bool condition_value = branch_condition.is_true;
230 for (Node* const use : node->uses()) {
231 switch (use->opcode()) {
232 case IrOpcode::kIfTrue:
233 Replace(use, condition_value ? control_input : dead());
234 break;
235 case IrOpcode::kIfFalse:
236 Replace(use, condition_value ? dead() : control_input);
237 break;
238 default:
239 UNREACHABLE();
240 }
241 }
242 return Replace(dead());
243 }
245 // Try to reduce the pattern that branch condition comes from phi node.
246 if (condition->opcode() == IrOpcode::kPhi &&
247 control_input->opcode() == IrOpcode::kMerge) {
248 if (TryEliminateBranchWithPhiCondition(node, condition, control_input)) {
249 return Replace(dead());
250 }
251 }
252 // Trigger revisits of the IfTrue/IfFalse projections, since they depend on
253 // the branch condition.
254 for (Node* const use : node->uses()) {
255 Revisit(use);
256 }
257 return TakeStatesFromFirstControl(node);
258}
259
261 DCHECK(node->opcode() == IrOpcode::kTrapIf ||
262 node->opcode() == IrOpcode::kTrapUnless);
263 bool trapping_condition = node->opcode() == IrOpcode::kTrapIf;
264 Node* condition = node->InputAt(0);
265 Node* control_input = NodeProperties::GetControlInput(node, 0);
266 // If we do not know anything about the predecessor, do not propagate just
267 // yet because we will have to recompute anyway once we compute the
268 // predecessor.
269 if (!IsReduced(control_input)) return NoChange();
270
271 ControlPathConditions from_input = GetState(control_input);
272
273 BranchCondition branch_condition = from_input.LookupState(condition);
274 if (branch_condition.IsSet()) {
275 bool condition_value = branch_condition.is_true;
276 if (condition_value == trapping_condition) {
277 // This will always trap. Mark its outputs as dead and connect it to
278 // graph()->end().
279 ReplaceWithValue(node, dead(), dead(), dead());
280 Node* control = graph()->NewNode(common()->Throw(), node, node);
281 MergeControlToEnd(graph(), common(), control);
282 return Changed(node);
283 } else {
284 // This will not trap, remove it by relaxing effect/control.
285 RelaxEffectsAndControls(node);
286 Node* control = NodeProperties::GetControlInput(node);
287 node->Kill();
288 return Replace(control); // Irrelevant argument
289 }
290 }
291 return UpdateStatesHelper(node, from_input, condition, node,
292 !trapping_condition, false);
293}
294
296 DCHECK(node->opcode() == IrOpcode::kDeoptimizeIf ||
297 node->opcode() == IrOpcode::kDeoptimizeUnless);
298 bool condition_is_true = node->opcode() == IrOpcode::kDeoptimizeUnless;
301 Node* frame_state = NodeProperties::GetValueInput(node, 1);
302 Node* effect = NodeProperties::GetEffectInput(node);
303 Node* control = NodeProperties::GetControlInput(node);
304 // If we do not know anything about the predecessor, do not propagate just
305 // yet because we will have to recompute anyway once we compute the
306 // predecessor.
307 if (!IsReduced(control)) {
308 return NoChange();
309 }
310
311 ControlPathConditions conditions = GetState(control);
312 BranchCondition branch_condition = conditions.LookupState(condition);
313 if (branch_condition.IsSet()) {
314 // If we know the condition we can discard the branch.
315 bool condition_value = branch_condition.is_true;
316 if (condition_is_true == condition_value) {
317 // We don't update the conditions here, because we're replacing {node}
318 // with the {control} node that already contains the right information.
319 ReplaceWithValue(node, dead(), effect, control);
320 } else {
321 control = graph()->NewNode(common()->Deoptimize(p.reason(), p.feedback()),
322 frame_state, effect, control);
323 MergeControlToEnd(graph(), common(), control);
324 }
325 return Replace(dead());
326 }
327 return UpdateStatesHelper(node, conditions, condition, node,
328 condition_is_true, false);
329}
330
331Reduction BranchElimination::ReduceIf(Node* node, bool is_true_branch) {
332 // Add the condition to the list arriving from the input branch.
333 Node* branch = NodeProperties::GetControlInput(node, 0);
334 ControlPathConditions from_branch = GetState(branch);
335 // If we do not know anything about the predecessor, do not propagate just
336 // yet because we will have to recompute anyway once we compute the
337 // predecessor.
338 if (!IsReduced(branch)) {
339 return NoChange();
340 }
341 Node* condition = branch->InputAt(0);
342 return UpdateStatesHelper(node, from_branch, condition, branch,
343 is_true_branch, true);
344}
345
347 // Here we rely on having only reducible loops:
348 // The loop entry edge always dominates the header, so we can just use
349 // the information from the loop entry edge.
350 return TakeStatesFromFirstControl(node);
351}
352
354 // Shortcut for the case when we do not know anything about some
355 // input.
356 Node::Inputs inputs = node->inputs();
357 for (Node* input : inputs) {
358 if (!IsReduced(input)) {
359 return NoChange();
360 }
361 }
362
363 auto input_it = inputs.begin();
364
365 DCHECK_GT(inputs.count(), 0);
366
367 ControlPathConditions conditions = GetState(*input_it);
368 ++input_it;
369 // Merge the first input's conditions with the conditions from the other
370 // inputs.
371 auto input_end = inputs.end();
372 for (; input_it != input_end; ++input_it) {
373 // Change the current condition block list to a longest common tail of this
374 // condition list and the other list. (The common tail should correspond to
375 // the list from the common dominator.)
376 conditions.ResetToCommonAncestor(GetState(*input_it));
377 }
378 return UpdateStates(node, conditions);
379}
380
382 return UpdateStates(node, ControlPathConditions(zone()));
383}
384
386 DCHECK_EQ(1, node->op()->ControlInputCount());
387 return TakeStatesFromFirstControl(node);
388}
389
391
393
397
398// Workaround a gcc bug causing link errors.
399// Related issue: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105848
400template bool DefaultConstruct<bool>(Zone* zone);
401
402} // namespace compiler
403} // namespace internal
404} // namespace v8
void emplace_back(Args &&... args)
T & at(size_t index)
Reduction UpdateStatesHelper(Node *node, ControlPathConditions prev_conditions, Node *current_condition, Node *current_branch, bool is_true_branch, bool in_new_block)
bool TryEliminateBranchWithPhiCondition(Node *branch, Node *phi, Node *merge)
Reduction ReduceIf(Node *node, bool is_true_branch)
ControlPathState< BranchCondition, kUniqueInstance > ControlPathConditions
BranchElimination(Editor *editor, JSGraph *js_graph, Zone *zone, Phase phase=kLATE)
void ResetToCommonAncestor(ControlPathState other)
const FeedbackSource & feedback() const
Isolate * isolate() const
Definition js-graph.h:106
CommonOperatorBuilder * common() const
static Node * GetEffectInput(Node *node, int index=0)
static void CollectControlProjections(Node *node, Node **proj, size_t count)
static Node * GetValueInput(Node *node, int index)
static void ReplaceValueInput(Node *node, Node *value, int index)
static Node * GetControlInput(Node *node, int index=0)
const_iterator begin() const
Definition node.h:600
const_iterator end() const
Definition node.h:605
constexpr IrOpcode::Value opcode() const
Definition node.h:52
Inputs inputs() const
Definition node.h:478
const Operator * op() const
Definition node.h:50
Node * InputAt(int index) const
Definition node.h:70
Node * NewNode(const Operator *op, int input_count, Node *const *inputs, bool incomplete=false)
const BranchParameters & BranchParametersOf(const Operator *const op)
template bool DefaultConstruct< bool >(Zone *zone)
DeoptimizeParameters const & DeoptimizeParametersOf(Operator const *const op)
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_EQ(v1, v2)
Definition logging.h:485
#define DCHECK_GT(v1, v2)
Definition logging.h:487
bool Is(const T &value) const