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/compiler/js-graph.h"
8#include "src/compiler/node-properties.h"
9#include "src/compiler/simplified-operator.h"
10
11namespace v8 {
12namespace internal {
13namespace compiler {
14
15BranchElimination::BranchElimination(Editor* editor, JSGraph* js_graph,
16 Zone* zone)
17 : AdvancedReducer(editor),
18 jsgraph_(js_graph),
19 node_conditions_(js_graph->graph()->NodeCount(), zone),
20 reduced_(js_graph->graph()->NodeCount(), zone),
21 zone_(zone),
22 dead_(js_graph->Dead()) {}
23
24BranchElimination::~BranchElimination() = default;
25
26
27Reduction BranchElimination::Reduce(Node* node) {
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::kStart:
45 return ReduceStart(node);
46 default:
47 if (node->op()->ControlOutputCount() > 0) {
48 return ReduceOtherControl(node);
49 }
50 break;
51 }
52 return NoChange();
53}
54
55
56Reduction BranchElimination::ReduceBranch(Node* node) {
57 Node* condition = node->InputAt(0);
58 Node* control_input = NodeProperties::GetControlInput(node, 0);
59 ControlPathConditions from_input = node_conditions_.Get(control_input);
60 Node* branch;
61 bool condition_value;
62 // If we know the condition we can discard the branch.
63 if (from_input.LookupCondition(condition, &branch, &condition_value)) {
64 // Mark the branch as a safety check if necessary.
65 // Check if {branch} is dead because we might have a stale side-table entry.
66 if (!branch->IsDead() && branch->opcode() != IrOpcode::kDead) {
67 IsSafetyCheck branch_safety = IsSafetyCheckOf(branch->op());
68 IsSafetyCheck combined_safety =
69 CombineSafetyChecks(branch_safety, IsSafetyCheckOf(node->op()));
70 if (branch_safety != combined_safety) {
71 NodeProperties::ChangeOp(
72 branch, common()->MarkAsSafetyCheck(branch->op(), combined_safety));
73 }
74 }
75
76 for (Node* const use : node->uses()) {
77 switch (use->opcode()) {
78 case IrOpcode::kIfTrue:
79 Replace(use, condition_value ? control_input : dead());
80 break;
81 case IrOpcode::kIfFalse:
82 Replace(use, condition_value ? dead() : control_input);
83 break;
84 default:
85 UNREACHABLE();
86 }
87 }
88 return Replace(dead());
89 }
90 return TakeConditionsFromFirstControl(node);
91}
92
93Reduction BranchElimination::ReduceDeoptimizeConditional(Node* node) {
94 DCHECK(node->opcode() == IrOpcode::kDeoptimizeIf ||
95 node->opcode() == IrOpcode::kDeoptimizeUnless);
96 bool condition_is_true = node->opcode() == IrOpcode::kDeoptimizeUnless;
97 DeoptimizeParameters p = DeoptimizeParametersOf(node->op());
98 Node* condition = NodeProperties::GetValueInput(node, 0);
99 Node* frame_state = NodeProperties::GetValueInput(node, 1);
100 Node* effect = NodeProperties::GetEffectInput(node);
101 Node* control = NodeProperties::GetControlInput(node);
102 // If we do not know anything about the predecessor, do not propagate just
103 // yet because we will have to recompute anyway once we compute the
104 // predecessor.
105 if (!reduced_.Get(control)) {
106 return NoChange();
107 }
108
109 ControlPathConditions conditions = node_conditions_.Get(control);
110 bool condition_value;
111 Node* branch;
112 if (conditions.LookupCondition(condition, &branch, &condition_value)) {
113 // Mark the branch as a safety check.
114 IsSafetyCheck branch_safety = IsSafetyCheckOf(branch->op());
115 IsSafetyCheck combined_safety =
116 CombineSafetyChecks(branch_safety, p.is_safety_check());
117 if (branch_safety != combined_safety) {
118 NodeProperties::ChangeOp(
119 branch, common()->MarkAsSafetyCheck(branch->op(), combined_safety));
120 }
121
122 // If we know the condition we can discard the branch.
123 if (condition_is_true == condition_value) {
124 // We don't update the conditions here, because we're replacing {node}
125 // with the {control} node that already contains the right information.
126 ReplaceWithValue(node, dead(), effect, control);
127 } else {
128 control = graph()->NewNode(
129 common()->Deoptimize(p.kind(), p.reason(), p.feedback()), frame_state,
130 effect, control);
131 // TODO(bmeurer): This should be on the AdvancedReducer somehow.
132 NodeProperties::MergeControlToEnd(graph(), common(), control);
133 Revisit(graph()->end());
134 }
135 return Replace(dead());
136 }
137 return UpdateConditions(node, conditions, condition, node, condition_is_true);
138}
139
140Reduction BranchElimination::ReduceIf(Node* node, bool is_true_branch) {
141 // Add the condition to the list arriving from the input branch.
142 Node* branch = NodeProperties::GetControlInput(node, 0);
143 ControlPathConditions from_branch = node_conditions_.Get(branch);
144 // If we do not know anything about the predecessor, do not propagate just
145 // yet because we will have to recompute anyway once we compute the
146 // predecessor.
147 if (!reduced_.Get(branch)) {
148 return NoChange();
149 }
150 Node* condition = branch->InputAt(0);
151 return UpdateConditions(node, from_branch, condition, branch, is_true_branch);
152}
153
154
155Reduction BranchElimination::ReduceLoop(Node* node) {
156 // Here we rely on having only reducible loops:
157 // The loop entry edge always dominates the header, so we can just use
158 // the information from the loop entry edge.
159 return TakeConditionsFromFirstControl(node);
160}
161
162
163Reduction BranchElimination::ReduceMerge(Node* node) {
164 // Shortcut for the case when we do not know anything about some
165 // input.
166 Node::Inputs inputs = node->inputs();
167 for (Node* input : inputs) {
168 if (!reduced_.Get(input)) {
169 return NoChange();
170 }
171 }
172
173 auto input_it = inputs.begin();
174
175 DCHECK_GT(inputs.count(), 0);
176
177 ControlPathConditions conditions = node_conditions_.Get(*input_it);
178 ++input_it;
179 // Merge the first input's conditions with the conditions from the other
180 // inputs.
181 auto input_end = inputs.end();
182 for (; input_it != input_end; ++input_it) {
183 // Change the current condition list to a longest common tail
184 // of this condition list and the other list. (The common tail
185 // should correspond to the list from the common dominator.)
186 conditions.ResetToCommonAncestor(node_conditions_.Get(*input_it));
187 }
188 return UpdateConditions(node, conditions);
189}
190
191
192Reduction BranchElimination::ReduceStart(Node* node) {
193 return UpdateConditions(node, {});
194}
195
196
197Reduction BranchElimination::ReduceOtherControl(Node* node) {
198 DCHECK_EQ(1, node->op()->ControlInputCount());
199 return TakeConditionsFromFirstControl(node);
200}
201
202
203Reduction BranchElimination::TakeConditionsFromFirstControl(Node* node) {
204 // We just propagate the information from the control input (ideally,
205 // we would only revisit control uses if there is change).
206 Node* input = NodeProperties::GetControlInput(node, 0);
207 if (!reduced_.Get(input)) return NoChange();
208 return UpdateConditions(node, node_conditions_.Get(input));
209}
210
211Reduction BranchElimination::UpdateConditions(
212 Node* node, ControlPathConditions conditions) {
213 // Only signal that the node has Changed if the condition information has
214 // changed.
215 if (reduced_.Set(node, true) | node_conditions_.Set(node, conditions)) {
216 return Changed(node);
217 }
218 return NoChange();
219}
220
221Reduction BranchElimination::UpdateConditions(
222 Node* node, ControlPathConditions prev_conditions, Node* current_condition,
223 Node* current_branch, bool is_true_branch) {
224 ControlPathConditions original = node_conditions_.Get(node);
225 // The control path for the node is the path obtained by appending the
226 // current_condition to the prev_conditions. Use the original control path as
227 // a hint to avoid allocations.
228 prev_conditions.AddCondition(zone_, current_condition, current_branch,
229 is_true_branch, original);
230 return UpdateConditions(node, prev_conditions);
231}
232
233void BranchElimination::ControlPathConditions::AddCondition(
234 Zone* zone, Node* condition, Node* branch, bool is_true,
235 ControlPathConditions hint) {
236 DCHECK_EQ(false, LookupCondition(condition, nullptr, nullptr));
237 PushFront({condition, branch, is_true}, zone, hint);
238}
239
240bool BranchElimination::ControlPathConditions::LookupCondition(
241 Node* condition, Node** branch, bool* is_true) const {
242 for (BranchCondition element : *this) {
243 if (element.condition == condition) {
244 *is_true = element.is_true;
245 *branch = element.branch;
246 return true;
247 }
248 }
249 return false;
250}
251
252Graph* BranchElimination::graph() const { return jsgraph()->graph(); }
253
254Isolate* BranchElimination::isolate() const { return jsgraph()->isolate(); }
255
256CommonOperatorBuilder* BranchElimination::common() const {
257 return jsgraph()->common();
258}
259
260} // namespace compiler
261} // namespace internal
262} // namespace v8
263