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       node_conditions_(zone, js_graph->graph()->NodeCount()),
19       zone_(zone),
20       dead_(js_graph->graph()->NewNode(js_graph->common()->Dead())) {}
21 
22 
~BranchElimination()23 BranchElimination::~BranchElimination() {}
24 
25 
Reduce(Node * node)26 Reduction BranchElimination::Reduce(Node* node) {
27   switch (node->opcode()) {
28     case IrOpcode::kDead:
29       return NoChange();
30     case IrOpcode::kMerge:
31       return ReduceMerge(node);
32     case IrOpcode::kLoop:
33       return ReduceLoop(node);
34     case IrOpcode::kBranch:
35       return ReduceBranch(node);
36     case IrOpcode::kIfFalse:
37       return ReduceIf(node, false);
38     case IrOpcode::kIfTrue:
39       return ReduceIf(node, true);
40     case IrOpcode::kStart:
41       return ReduceStart(node);
42     default:
43       if (node->op()->ControlOutputCount() > 0) {
44         return ReduceOtherControl(node);
45       }
46       break;
47   }
48   return NoChange();
49 }
50 
51 
ReduceBranch(Node * node)52 Reduction BranchElimination::ReduceBranch(Node* node) {
53   Node* condition = node->InputAt(0);
54   Node* control_input = NodeProperties::GetControlInput(node, 0);
55   const ControlPathConditions* from_input = node_conditions_.Get(control_input);
56   if (from_input != nullptr) {
57     Maybe<bool> condition_value = from_input->LookupCondition(condition);
58     // If we know the condition we can discard the branch.
59     if (condition_value.IsJust()) {
60       bool known_value = condition_value.FromJust();
61       for (Node* const use : node->uses()) {
62         switch (use->opcode()) {
63           case IrOpcode::kIfTrue:
64             Replace(use, known_value ? control_input : dead());
65             break;
66           case IrOpcode::kIfFalse:
67             Replace(use, known_value ? dead() : control_input);
68             break;
69           default:
70             UNREACHABLE();
71         }
72       }
73       return Replace(dead());
74     }
75   }
76   return TakeConditionsFromFirstControl(node);
77 }
78 
79 
ReduceIf(Node * node,bool is_true_branch)80 Reduction BranchElimination::ReduceIf(Node* node, bool is_true_branch) {
81   // Add the condition to the list arriving from the input branch.
82   Node* branch = NodeProperties::GetControlInput(node, 0);
83   const ControlPathConditions* from_branch = node_conditions_.Get(branch);
84   // If we do not know anything about the predecessor, do not propagate just
85   // yet because we will have to recompute anyway once we compute the
86   // predecessor.
87   if (from_branch == nullptr) {
88     DCHECK(node_conditions_.Get(node) == nullptr);
89     return NoChange();
90   }
91   Node* condition = branch->InputAt(0);
92   return UpdateConditions(
93       node, from_branch->AddCondition(zone_, condition, is_true_branch));
94 }
95 
96 
ReduceLoop(Node * node)97 Reduction BranchElimination::ReduceLoop(Node* node) {
98   // Here we rely on having only reducible loops:
99   // The loop entry edge always dominates the header, so we can just use
100   // the information from the loop entry edge.
101   return TakeConditionsFromFirstControl(node);
102 }
103 
104 
ReduceMerge(Node * node)105 Reduction BranchElimination::ReduceMerge(Node* node) {
106   // Shortcut for the case when we do not know anything about some
107   // input.
108   for (int i = 0; i < node->InputCount(); i++) {
109     if (node_conditions_.Get(node->InputAt(i)) == nullptr) {
110       DCHECK(node_conditions_.Get(node) == nullptr);
111       return NoChange();
112     }
113   }
114 
115   const ControlPathConditions* first = node_conditions_.Get(node->InputAt(0));
116   // Make a copy of the first input's conditions and merge with the conditions
117   // from other inputs.
118   ControlPathConditions* conditions =
119       new (zone_->New(sizeof(ControlPathConditions)))
120           ControlPathConditions(*first);
121   for (int i = 1; i < node->InputCount(); i++) {
122     conditions->Merge(*(node_conditions_.Get(node->InputAt(i))));
123   }
124 
125   return UpdateConditions(node, conditions);
126 }
127 
128 
ReduceStart(Node * node)129 Reduction BranchElimination::ReduceStart(Node* node) {
130   return UpdateConditions(node, ControlPathConditions::Empty(zone_));
131 }
132 
133 
134 const BranchElimination::ControlPathConditions*
Get(Node * node)135 BranchElimination::PathConditionsForControlNodes::Get(Node* node) {
136   if (static_cast<size_t>(node->id()) < info_for_node_.size()) {
137     return info_for_node_[node->id()];
138   }
139   return nullptr;
140 }
141 
142 
Set(Node * node,const ControlPathConditions * conditions)143 void BranchElimination::PathConditionsForControlNodes::Set(
144     Node* node, const ControlPathConditions* conditions) {
145   size_t index = static_cast<size_t>(node->id());
146   if (index >= info_for_node_.size()) {
147     info_for_node_.resize(index + 1, nullptr);
148   }
149   info_for_node_[index] = conditions;
150 }
151 
152 
ReduceOtherControl(Node * node)153 Reduction BranchElimination::ReduceOtherControl(Node* node) {
154   DCHECK_EQ(1, node->op()->ControlInputCount());
155   return TakeConditionsFromFirstControl(node);
156 }
157 
158 
TakeConditionsFromFirstControl(Node * node)159 Reduction BranchElimination::TakeConditionsFromFirstControl(Node* node) {
160   // We just propagate the information from the control input (ideally,
161   // we would only revisit control uses if there is change).
162   const ControlPathConditions* from_input =
163       node_conditions_.Get(NodeProperties::GetControlInput(node, 0));
164   return UpdateConditions(node, from_input);
165 }
166 
167 
UpdateConditions(Node * node,const ControlPathConditions * conditions)168 Reduction BranchElimination::UpdateConditions(
169     Node* node, const ControlPathConditions* conditions) {
170   const ControlPathConditions* original = node_conditions_.Get(node);
171   // Only signal that the node has Changed if the condition information has
172   // changed.
173   if (conditions != original) {
174     if (original == nullptr || *conditions != *original) {
175       node_conditions_.Set(node, conditions);
176       return Changed(node);
177     }
178   }
179   return NoChange();
180 }
181 
182 
183 // static
184 const BranchElimination::ControlPathConditions*
Empty(Zone * zone)185 BranchElimination::ControlPathConditions::Empty(Zone* zone) {
186   return new (zone->New(sizeof(ControlPathConditions)))
187       ControlPathConditions(nullptr, 0);
188 }
189 
190 
Merge(const ControlPathConditions & other)191 void BranchElimination::ControlPathConditions::Merge(
192     const ControlPathConditions& other) {
193   // Change the current condition list to a longest common tail
194   // of this condition list and the other list. (The common tail
195   // should correspond to the list from the common dominator.)
196 
197   // First, we throw away the prefix of the longer list, so that
198   // we have lists of the same length.
199   size_t other_size = other.condition_count_;
200   BranchCondition* other_condition = other.head_;
201   while (other_size > condition_count_) {
202     other_condition = other_condition->next;
203     other_size--;
204   }
205   while (condition_count_ > other_size) {
206     head_ = head_->next;
207     condition_count_--;
208   }
209 
210   // Then we go through both lists in lock-step until we find
211   // the common tail.
212   while (head_ != other_condition) {
213     DCHECK(condition_count_ > 0);
214     condition_count_--;
215     other_condition = other_condition->next;
216     head_ = head_->next;
217   }
218 }
219 
220 
221 const BranchElimination::ControlPathConditions*
AddCondition(Zone * zone,Node * condition,bool is_true) const222 BranchElimination::ControlPathConditions::AddCondition(Zone* zone,
223                                                        Node* condition,
224                                                        bool is_true) const {
225   DCHECK(LookupCondition(condition).IsNothing());
226 
227   BranchCondition* new_head = new (zone->New(sizeof(BranchCondition)))
228       BranchCondition(condition, is_true, head_);
229 
230   ControlPathConditions* conditions =
231       new (zone->New(sizeof(ControlPathConditions)))
232           ControlPathConditions(new_head, condition_count_ + 1);
233   return conditions;
234 }
235 
236 
LookupCondition(Node * condition) const237 Maybe<bool> BranchElimination::ControlPathConditions::LookupCondition(
238     Node* condition) const {
239   for (BranchCondition* current = head_; current != nullptr;
240        current = current->next) {
241     if (current->condition == condition) {
242       return Just<bool>(current->is_true);
243     }
244   }
245   return Nothing<bool>();
246 }
247 
248 
operator ==(const ControlPathConditions & other) const249 bool BranchElimination::ControlPathConditions::operator==(
250     const ControlPathConditions& other) const {
251   if (condition_count_ != other.condition_count_) return false;
252   BranchCondition* this_condition = head_;
253   BranchCondition* other_condition = other.head_;
254   while (true) {
255     if (this_condition == other_condition) return true;
256     if (this_condition->condition != other_condition->condition ||
257         this_condition->is_true != other_condition->is_true) {
258       return false;
259     }
260     this_condition = this_condition->next;
261     other_condition = other_condition->next;
262   }
263   UNREACHABLE();
264   return false;
265 }
266 
267 }  // namespace compiler
268 }  // namespace internal
269 }  // namespace v8
270