• 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/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