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