• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2016 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/redundancy-elimination.h"
6 
7 #include "src/compiler/node-properties.h"
8 
9 namespace v8 {
10 namespace internal {
11 namespace compiler {
12 
RedundancyElimination(Editor * editor,Zone * zone)13 RedundancyElimination::RedundancyElimination(Editor* editor, Zone* zone)
14     : AdvancedReducer(editor), node_checks_(zone), zone_(zone) {}
15 
~RedundancyElimination()16 RedundancyElimination::~RedundancyElimination() {}
17 
Reduce(Node * node)18 Reduction RedundancyElimination::Reduce(Node* node) {
19   switch (node->opcode()) {
20     case IrOpcode::kCheckBounds:
21     case IrOpcode::kCheckFloat64Hole:
22     case IrOpcode::kCheckHeapObject:
23     case IrOpcode::kCheckIf:
24     case IrOpcode::kCheckNumber:
25     case IrOpcode::kCheckSmi:
26     case IrOpcode::kCheckString:
27     case IrOpcode::kCheckTaggedHole:
28     case IrOpcode::kCheckedFloat64ToInt32:
29     case IrOpcode::kCheckedInt32Add:
30     case IrOpcode::kCheckedInt32Sub:
31     case IrOpcode::kCheckedInt32Div:
32     case IrOpcode::kCheckedInt32Mod:
33     case IrOpcode::kCheckedInt32Mul:
34     case IrOpcode::kCheckedTaggedToFloat64:
35     case IrOpcode::kCheckedTaggedSignedToInt32:
36     case IrOpcode::kCheckedTaggedToInt32:
37     case IrOpcode::kCheckedUint32ToInt32:
38       return ReduceCheckNode(node);
39     case IrOpcode::kEffectPhi:
40       return ReduceEffectPhi(node);
41     case IrOpcode::kDead:
42       break;
43     case IrOpcode::kStart:
44       return ReduceStart(node);
45     default:
46       return ReduceOtherNode(node);
47   }
48   return NoChange();
49 }
50 
51 // static
52 RedundancyElimination::EffectPathChecks*
Copy(Zone * zone,EffectPathChecks const * checks)53 RedundancyElimination::EffectPathChecks::Copy(Zone* zone,
54                                               EffectPathChecks const* checks) {
55   return new (zone->New(sizeof(EffectPathChecks))) EffectPathChecks(*checks);
56 }
57 
58 // static
59 RedundancyElimination::EffectPathChecks const*
Empty(Zone * zone)60 RedundancyElimination::EffectPathChecks::Empty(Zone* zone) {
61   return new (zone->New(sizeof(EffectPathChecks))) EffectPathChecks(nullptr, 0);
62 }
63 
Equals(EffectPathChecks const * that) const64 bool RedundancyElimination::EffectPathChecks::Equals(
65     EffectPathChecks const* that) const {
66   if (this->size_ != that->size_) return false;
67   Check* this_head = this->head_;
68   Check* that_head = that->head_;
69   while (this_head != that_head) {
70     if (this_head->node != that_head->node) return false;
71     this_head = this_head->next;
72     that_head = that_head->next;
73   }
74   return true;
75 }
76 
Merge(EffectPathChecks const * that)77 void RedundancyElimination::EffectPathChecks::Merge(
78     EffectPathChecks const* that) {
79   // Change the current check list to a longest common tail of this check
80   // list and the other list.
81 
82   // First, we throw away the prefix of the longer list, so that
83   // we have lists of the same length.
84   Check* that_head = that->head_;
85   size_t that_size = that->size_;
86   while (that_size > size_) {
87     that_head = that_head->next;
88     that_size--;
89   }
90   while (size_ > that_size) {
91     head_ = head_->next;
92     size_--;
93   }
94 
95   // Then we go through both lists in lock-step until we find
96   // the common tail.
97   while (head_ != that_head) {
98     DCHECK_LT(0u, size_);
99     DCHECK_NOT_NULL(head_);
100     size_--;
101     head_ = head_->next;
102     that_head = that_head->next;
103   }
104 }
105 
106 RedundancyElimination::EffectPathChecks const*
AddCheck(Zone * zone,Node * node) const107 RedundancyElimination::EffectPathChecks::AddCheck(Zone* zone,
108                                                   Node* node) const {
109   Check* head = new (zone->New(sizeof(Check))) Check(node, head_);
110   return new (zone->New(sizeof(EffectPathChecks)))
111       EffectPathChecks(head, size_ + 1);
112 }
113 
114 namespace {
115 
IsCompatibleCheck(Node const * a,Node const * b)116 bool IsCompatibleCheck(Node const* a, Node const* b) {
117   if (a->op() != b->op()) return false;
118   for (int i = a->op()->ValueInputCount(); --i >= 0;) {
119     if (a->InputAt(i) != b->InputAt(i)) return false;
120   }
121   return true;
122 }
123 
124 }  // namespace
125 
LookupCheck(Node * node) const126 Node* RedundancyElimination::EffectPathChecks::LookupCheck(Node* node) const {
127   for (Check const* check = head_; check != nullptr; check = check->next) {
128     if (IsCompatibleCheck(check->node, node)) {
129       DCHECK(!check->node->IsDead());
130       return check->node;
131     }
132   }
133   return nullptr;
134 }
135 
136 RedundancyElimination::EffectPathChecks const*
Get(Node * node) const137 RedundancyElimination::PathChecksForEffectNodes::Get(Node* node) const {
138   size_t const id = node->id();
139   if (id < info_for_node_.size()) return info_for_node_[id];
140   return nullptr;
141 }
142 
Set(Node * node,EffectPathChecks const * checks)143 void RedundancyElimination::PathChecksForEffectNodes::Set(
144     Node* node, EffectPathChecks const* checks) {
145   size_t const id = node->id();
146   if (id >= info_for_node_.size()) info_for_node_.resize(id + 1, nullptr);
147   info_for_node_[id] = checks;
148 }
149 
ReduceCheckNode(Node * node)150 Reduction RedundancyElimination::ReduceCheckNode(Node* node) {
151   Node* const effect = NodeProperties::GetEffectInput(node);
152   EffectPathChecks const* checks = node_checks_.Get(effect);
153   // If we do not know anything about the predecessor, do not propagate just yet
154   // because we will have to recompute anyway once we compute the predecessor.
155   if (checks == nullptr) return NoChange();
156   // See if we have another check that dominates us.
157   if (Node* check = checks->LookupCheck(node)) {
158     ReplaceWithValue(node, check);
159     return Replace(check);
160   }
161   // Learn from this check.
162   return UpdateChecks(node, checks->AddCheck(zone(), node));
163 }
164 
ReduceEffectPhi(Node * node)165 Reduction RedundancyElimination::ReduceEffectPhi(Node* node) {
166   Node* const control = NodeProperties::GetControlInput(node);
167   if (control->opcode() == IrOpcode::kLoop) {
168     // Here we rely on having only reducible loops:
169     // The loop entry edge always dominates the header, so we can just use
170     // the information from the loop entry edge.
171     return TakeChecksFromFirstEffect(node);
172   }
173   DCHECK_EQ(IrOpcode::kMerge, control->opcode());
174 
175   // Shortcut for the case when we do not know anything about some input.
176   int const input_count = node->op()->EffectInputCount();
177   for (int i = 0; i < input_count; ++i) {
178     Node* const effect = NodeProperties::GetEffectInput(node, i);
179     if (node_checks_.Get(effect) == nullptr) return NoChange();
180   }
181 
182   // Make a copy of the first input's checks and merge with the checks
183   // from other inputs.
184   EffectPathChecks* checks = EffectPathChecks::Copy(
185       zone(), node_checks_.Get(NodeProperties::GetEffectInput(node, 0)));
186   for (int i = 1; i < input_count; ++i) {
187     Node* const input = NodeProperties::GetEffectInput(node, i);
188     checks->Merge(node_checks_.Get(input));
189   }
190   return UpdateChecks(node, checks);
191 }
192 
ReduceStart(Node * node)193 Reduction RedundancyElimination::ReduceStart(Node* node) {
194   return UpdateChecks(node, EffectPathChecks::Empty(zone()));
195 }
196 
ReduceOtherNode(Node * node)197 Reduction RedundancyElimination::ReduceOtherNode(Node* node) {
198   if (node->op()->EffectInputCount() == 1) {
199     if (node->op()->EffectOutputCount() == 1) {
200       return TakeChecksFromFirstEffect(node);
201     } else {
202       // Effect terminators should be handled specially.
203       return NoChange();
204     }
205   }
206   DCHECK_EQ(0, node->op()->EffectInputCount());
207   DCHECK_EQ(0, node->op()->EffectOutputCount());
208   return NoChange();
209 }
210 
TakeChecksFromFirstEffect(Node * node)211 Reduction RedundancyElimination::TakeChecksFromFirstEffect(Node* node) {
212   DCHECK_EQ(1, node->op()->EffectOutputCount());
213   Node* const effect = NodeProperties::GetEffectInput(node);
214   EffectPathChecks const* checks = node_checks_.Get(effect);
215   // If we do not know anything about the predecessor, do not propagate just yet
216   // because we will have to recompute anyway once we compute the predecessor.
217   if (checks == nullptr) return NoChange();
218   // We just propagate the information from the effect input (ideally,
219   // we would only revisit effect uses if there is change).
220   return UpdateChecks(node, checks);
221 }
222 
UpdateChecks(Node * node,EffectPathChecks const * checks)223 Reduction RedundancyElimination::UpdateChecks(Node* node,
224                                               EffectPathChecks const* checks) {
225   EffectPathChecks const* original = node_checks_.Get(node);
226   // Only signal that the {node} has Changed, if the information about {checks}
227   // has changed wrt. the {original}.
228   if (checks != original) {
229     if (original == nullptr || !checks->Equals(original)) {
230       node_checks_.Set(node, checks);
231       return Changed(node);
232     }
233   }
234   return NoChange();
235 }
236 
237 }  // namespace compiler
238 }  // namespace internal
239 }  // namespace v8
240