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
11 namespace v8 {
12 namespace internal {
13 namespace compiler {
14
BranchElimination(Editor * editor,JSGraph * js_graph,Zone * zone)15 BranchElimination::BranchElimination(Editor* editor, JSGraph* js_graph,
16 Zone* zone)
17 : AdvancedReducer(editor),
18 jsgraph_(js_graph),
19 node_conditions_(zone, js_graph->graph()->NodeCount()),
20 zone_(zone),
21 dead_(js_graph->graph()->NewNode(js_graph->common()->Dead())) {
22 NodeProperties::SetType(dead_, Type::None());
23 }
24
~BranchElimination()25 BranchElimination::~BranchElimination() {}
26
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
56
ReduceBranch(Node * node)57 Reduction BranchElimination::ReduceBranch(Node* node) {
58 Node* condition = node->InputAt(0);
59 Node* control_input = NodeProperties::GetControlInput(node, 0);
60 const ControlPathConditions* from_input = node_conditions_.Get(control_input);
61 if (from_input != nullptr) {
62 Maybe<bool> condition_value = from_input->LookupCondition(condition);
63 // If we know the condition we can discard the branch.
64 if (condition_value.IsJust()) {
65 bool known_value = condition_value.FromJust();
66 for (Node* const use : node->uses()) {
67 switch (use->opcode()) {
68 case IrOpcode::kIfTrue:
69 Replace(use, known_value ? control_input : dead());
70 break;
71 case IrOpcode::kIfFalse:
72 Replace(use, known_value ? dead() : control_input);
73 break;
74 default:
75 UNREACHABLE();
76 }
77 }
78 return Replace(dead());
79 }
80 }
81 return TakeConditionsFromFirstControl(node);
82 }
83
ReduceDeoptimizeConditional(Node * node)84 Reduction BranchElimination::ReduceDeoptimizeConditional(Node* node) {
85 DCHECK(node->opcode() == IrOpcode::kDeoptimizeIf ||
86 node->opcode() == IrOpcode::kDeoptimizeUnless);
87 bool condition_is_true = node->opcode() == IrOpcode::kDeoptimizeUnless;
88 DeoptimizeParameters p = DeoptimizeParametersOf(node->op());
89 Node* condition = NodeProperties::GetValueInput(node, 0);
90 Node* frame_state = NodeProperties::GetValueInput(node, 1);
91 Node* effect = NodeProperties::GetEffectInput(node);
92 Node* control = NodeProperties::GetControlInput(node);
93 ControlPathConditions const* conditions = node_conditions_.Get(control);
94 // If we do not know anything about the predecessor, do not propagate just
95 // yet because we will have to recompute anyway once we compute the
96 // predecessor.
97 if (conditions == nullptr) {
98 return UpdateConditions(node, conditions);
99 }
100 Maybe<bool> condition_value = conditions->LookupCondition(condition);
101 if (condition_value.IsJust()) {
102 // If we know the condition we can discard the branch.
103 if (condition_is_true == condition_value.FromJust()) {
104 // We don't update the conditions here, because we're replacing {node}
105 // with the {control} node that already contains the right information.
106 ReplaceWithValue(node, dead(), effect, control);
107 } else {
108 control = graph()->NewNode(common()->Deoptimize(p.kind(), p.reason()),
109 frame_state, effect, control);
110 // TODO(bmeurer): This should be on the AdvancedReducer somehow.
111 NodeProperties::MergeControlToEnd(graph(), common(), control);
112 Revisit(graph()->end());
113 }
114 return Replace(dead());
115 }
116 return UpdateConditions(
117 node, conditions->AddCondition(zone_, condition, condition_is_true));
118 }
119
ReduceIf(Node * node,bool is_true_branch)120 Reduction BranchElimination::ReduceIf(Node* node, bool is_true_branch) {
121 // Add the condition to the list arriving from the input branch.
122 Node* branch = NodeProperties::GetControlInput(node, 0);
123 const ControlPathConditions* from_branch = node_conditions_.Get(branch);
124 // If we do not know anything about the predecessor, do not propagate just
125 // yet because we will have to recompute anyway once we compute the
126 // predecessor.
127 if (from_branch == nullptr) {
128 return UpdateConditions(node, nullptr);
129 }
130 Node* condition = branch->InputAt(0);
131 return UpdateConditions(
132 node, from_branch->AddCondition(zone_, condition, is_true_branch));
133 }
134
135
ReduceLoop(Node * node)136 Reduction BranchElimination::ReduceLoop(Node* node) {
137 // Here we rely on having only reducible loops:
138 // The loop entry edge always dominates the header, so we can just use
139 // the information from the loop entry edge.
140 return TakeConditionsFromFirstControl(node);
141 }
142
143
ReduceMerge(Node * node)144 Reduction BranchElimination::ReduceMerge(Node* node) {
145 // Shortcut for the case when we do not know anything about some
146 // input.
147 Node::Inputs inputs = node->inputs();
148 for (Node* input : inputs) {
149 if (node_conditions_.Get(input) == nullptr) {
150 return UpdateConditions(node, nullptr);
151 }
152 }
153
154 auto input_it = inputs.begin();
155
156 DCHECK_GT(inputs.count(), 0);
157
158 const ControlPathConditions* first = node_conditions_.Get(*input_it);
159 ++input_it;
160 // Make a copy of the first input's conditions and merge with the conditions
161 // from other inputs.
162 ControlPathConditions* conditions =
163 new (zone_->New(sizeof(ControlPathConditions)))
164 ControlPathConditions(*first);
165 auto input_end = inputs.end();
166 for (; input_it != input_end; ++input_it) {
167 conditions->Merge(*(node_conditions_.Get(*input_it)));
168 }
169
170 return UpdateConditions(node, conditions);
171 }
172
173
ReduceStart(Node * node)174 Reduction BranchElimination::ReduceStart(Node* node) {
175 return UpdateConditions(node, ControlPathConditions::Empty(zone_));
176 }
177
178
179 const BranchElimination::ControlPathConditions*
Get(Node * node)180 BranchElimination::PathConditionsForControlNodes::Get(Node* node) {
181 if (static_cast<size_t>(node->id()) < info_for_node_.size()) {
182 return info_for_node_[node->id()];
183 }
184 return nullptr;
185 }
186
187
Set(Node * node,const ControlPathConditions * conditions)188 void BranchElimination::PathConditionsForControlNodes::Set(
189 Node* node, const ControlPathConditions* conditions) {
190 size_t index = static_cast<size_t>(node->id());
191 if (index >= info_for_node_.size()) {
192 info_for_node_.resize(index + 1, nullptr);
193 }
194 info_for_node_[index] = conditions;
195 }
196
197
ReduceOtherControl(Node * node)198 Reduction BranchElimination::ReduceOtherControl(Node* node) {
199 DCHECK_EQ(1, node->op()->ControlInputCount());
200 return TakeConditionsFromFirstControl(node);
201 }
202
203
TakeConditionsFromFirstControl(Node * node)204 Reduction BranchElimination::TakeConditionsFromFirstControl(Node* node) {
205 // We just propagate the information from the control input (ideally,
206 // we would only revisit control uses if there is change).
207 const ControlPathConditions* from_input =
208 node_conditions_.Get(NodeProperties::GetControlInput(node, 0));
209 return UpdateConditions(node, from_input);
210 }
211
212
UpdateConditions(Node * node,const ControlPathConditions * conditions)213 Reduction BranchElimination::UpdateConditions(
214 Node* node, const ControlPathConditions* conditions) {
215 const ControlPathConditions* original = node_conditions_.Get(node);
216 // Only signal that the node has Changed if the condition information has
217 // changed.
218 if (conditions != original) {
219 if (conditions == nullptr || original == nullptr ||
220 *conditions != *original) {
221 node_conditions_.Set(node, conditions);
222 return Changed(node);
223 }
224 }
225 return NoChange();
226 }
227
228
229 // static
230 const BranchElimination::ControlPathConditions*
Empty(Zone * zone)231 BranchElimination::ControlPathConditions::Empty(Zone* zone) {
232 return new (zone->New(sizeof(ControlPathConditions)))
233 ControlPathConditions(nullptr, 0);
234 }
235
236
Merge(const ControlPathConditions & other)237 void BranchElimination::ControlPathConditions::Merge(
238 const ControlPathConditions& other) {
239 // Change the current condition list to a longest common tail
240 // of this condition list and the other list. (The common tail
241 // should correspond to the list from the common dominator.)
242
243 // First, we throw away the prefix of the longer list, so that
244 // we have lists of the same length.
245 size_t other_size = other.condition_count_;
246 BranchCondition* other_condition = other.head_;
247 while (other_size > condition_count_) {
248 other_condition = other_condition->next;
249 other_size--;
250 }
251 while (condition_count_ > other_size) {
252 head_ = head_->next;
253 condition_count_--;
254 }
255
256 // Then we go through both lists in lock-step until we find
257 // the common tail.
258 while (head_ != other_condition) {
259 DCHECK(condition_count_ > 0);
260 condition_count_--;
261 other_condition = other_condition->next;
262 head_ = head_->next;
263 }
264 }
265
266
267 const BranchElimination::ControlPathConditions*
AddCondition(Zone * zone,Node * condition,bool is_true) const268 BranchElimination::ControlPathConditions::AddCondition(Zone* zone,
269 Node* condition,
270 bool is_true) const {
271 DCHECK(LookupCondition(condition).IsNothing());
272
273 BranchCondition* new_head = new (zone->New(sizeof(BranchCondition)))
274 BranchCondition(condition, is_true, head_);
275
276 ControlPathConditions* conditions =
277 new (zone->New(sizeof(ControlPathConditions)))
278 ControlPathConditions(new_head, condition_count_ + 1);
279 return conditions;
280 }
281
282
LookupCondition(Node * condition) const283 Maybe<bool> BranchElimination::ControlPathConditions::LookupCondition(
284 Node* condition) const {
285 for (BranchCondition* current = head_; current != nullptr;
286 current = current->next) {
287 if (current->condition == condition) {
288 return Just<bool>(current->is_true);
289 }
290 }
291 return Nothing<bool>();
292 }
293
294
operator ==(const ControlPathConditions & other) const295 bool BranchElimination::ControlPathConditions::operator==(
296 const ControlPathConditions& other) const {
297 if (condition_count_ != other.condition_count_) return false;
298 BranchCondition* this_condition = head_;
299 BranchCondition* other_condition = other.head_;
300 while (true) {
301 if (this_condition == other_condition) return true;
302 if (this_condition->condition != other_condition->condition ||
303 this_condition->is_true != other_condition->is_true) {
304 return false;
305 }
306 this_condition = this_condition->next;
307 other_condition = other_condition->next;
308 }
309 UNREACHABLE();
310 return false;
311 }
312
graph() const313 Graph* BranchElimination::graph() const { return jsgraph()->graph(); }
314
common() const315 CommonOperatorBuilder* BranchElimination::common() const {
316 return jsgraph()->common();
317 }
318
319 } // namespace compiler
320 } // namespace internal
321 } // namespace v8
322