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