OLD | NEW |
1 // Copyright 2016 the V8 project authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "src/compiler/load-elimination.h" | 5 #include "src/compiler/load-elimination.h" |
6 | 6 |
7 #include "src/compiler/graph.h" | |
8 #include "src/compiler/node-marker.h" | |
9 #include "src/compiler/node-properties.h" | 7 #include "src/compiler/node-properties.h" |
10 #include "src/compiler/node.h" | |
11 #include "src/compiler/simplified-operator.h" | 8 #include "src/compiler/simplified-operator.h" |
12 | 9 |
13 namespace v8 { | 10 namespace v8 { |
14 namespace internal { | 11 namespace internal { |
15 namespace compiler { | 12 namespace compiler { |
16 | 13 |
17 #ifdef DEBUG | |
18 #define TRACE(...) \ | |
19 do { \ | |
20 if (FLAG_trace_turbo_load_elimination) PrintF(__VA_ARGS__); \ | |
21 } while (false) | |
22 #else | |
23 #define TRACE(...) | |
24 #endif | |
25 | |
26 namespace { | 14 namespace { |
27 | 15 |
28 const size_t kMaxTrackedFields = 16; | |
29 | |
30 Node* ActualValue(Node* node) { | |
31 switch (node->opcode()) { | |
32 case IrOpcode::kCheckBounds: | |
33 case IrOpcode::kCheckNumber: | |
34 case IrOpcode::kCheckTaggedPointer: | |
35 case IrOpcode::kCheckTaggedSigned: | |
36 case IrOpcode::kFinishRegion: | |
37 return ActualValue(NodeProperties::GetValueInput(node, 0)); | |
38 default: | |
39 return node; | |
40 } | |
41 } | |
42 | |
43 enum Aliasing { kNoAlias, kMayAlias, kMustAlias }; | 16 enum Aliasing { kNoAlias, kMayAlias, kMustAlias }; |
44 | 17 |
45 Aliasing QueryAlias(Node* a, Node* b) { | 18 Aliasing QueryAlias(Node* a, Node* b) { |
46 if (a == b) return kMustAlias; | 19 if (a == b) return kMustAlias; |
47 if (b->opcode() == IrOpcode::kAllocate) { | 20 if (b->opcode() == IrOpcode::kAllocate) { |
48 switch (a->opcode()) { | 21 switch (a->opcode()) { |
49 case IrOpcode::kAllocate: | 22 case IrOpcode::kAllocate: |
50 case IrOpcode::kHeapConstant: | 23 case IrOpcode::kHeapConstant: |
51 case IrOpcode::kParameter: | 24 case IrOpcode::kParameter: |
52 return kNoAlias; | 25 return kNoAlias; |
| 26 case IrOpcode::kFinishRegion: |
| 27 return QueryAlias(a->InputAt(0), b); |
53 default: | 28 default: |
54 break; | 29 break; |
55 } | 30 } |
56 } | 31 } |
57 if (a->opcode() == IrOpcode::kAllocate) { | 32 if (a->opcode() == IrOpcode::kAllocate) { |
58 switch (b->opcode()) { | 33 switch (b->opcode()) { |
59 case IrOpcode::kHeapConstant: | 34 case IrOpcode::kHeapConstant: |
60 case IrOpcode::kParameter: | 35 case IrOpcode::kParameter: |
61 return kNoAlias; | 36 return kNoAlias; |
| 37 case IrOpcode::kFinishRegion: |
| 38 return QueryAlias(a, b->InputAt(0)); |
62 default: | 39 default: |
63 break; | 40 break; |
64 } | 41 } |
65 } | 42 } |
66 return kMayAlias; | 43 return kMayAlias; |
67 } | 44 } |
68 | 45 |
69 bool MayAlias(Node* a, Node* b) { return QueryAlias(a, b) != kNoAlias; } | 46 bool MayAlias(Node* a, Node* b) { return QueryAlias(a, b) != kNoAlias; } |
70 | 47 |
71 bool MustAlias(Node* a, Node* b) { return QueryAlias(a, b) == kMustAlias; } | 48 bool MustAlias(Node* a, Node* b) { return QueryAlias(a, b) == kMustAlias; } |
72 | 49 |
73 // Abstract state to approximate the current state of a certain field along the | 50 } // namespace |
74 // effect paths through the graph. | 51 |
75 class AbstractField final : public ZoneObject { | 52 Reduction LoadElimination::Reduce(Node* node) { |
76 public: | 53 switch (node->opcode()) { |
77 explicit AbstractField(Zone* zone) : info_for_node_(zone) {} | 54 case IrOpcode::kLoadField: |
78 AbstractField(Node* object, Node* value, Zone* zone) : info_for_node_(zone) { | 55 return ReduceLoadField(node); |
79 info_for_node_.insert(std::make_pair(object, value)); | 56 case IrOpcode::kStoreField: |
80 } | 57 return ReduceStoreField(node); |
81 | 58 case IrOpcode::kLoadElement: |
82 AbstractField const* Extend(Node* object, Node* value, Zone* zone) const { | 59 return ReduceLoadElement(node); |
83 AbstractField* that = new (zone) AbstractField(zone); | 60 case IrOpcode::kStoreElement: |
84 that->info_for_node_ = this->info_for_node_; | 61 return ReduceStoreElement(node); |
85 that->info_for_node_.insert(std::make_pair(object, value)); | 62 case IrOpcode::kEffectPhi: |
86 return that; | 63 return ReduceEffectPhi(node); |
87 } | 64 case IrOpcode::kDead: |
88 Node* Lookup(Node* object) const { | 65 break; |
89 for (auto pair : info_for_node_) { | 66 case IrOpcode::kStart: |
90 if (MustAlias(object, pair.first)) return pair.second; | 67 return ReduceStart(node); |
91 } | 68 default: |
92 return nullptr; | 69 return ReduceOtherNode(node); |
93 } | 70 } |
94 AbstractField const* Kill(Node* object, Zone* zone) const { | 71 return NoChange(); |
95 for (auto pair : this->info_for_node_) { | 72 } |
96 if (MayAlias(object, pair.first)) { | 73 |
97 AbstractField* that = new (zone) AbstractField(zone); | 74 Node* LoadElimination::AbstractElements::Lookup(Node* object, |
98 for (auto pair : this->info_for_node_) { | 75 Node* index) const { |
99 if (!MayAlias(object, pair.first)) that->info_for_node_.insert(pair); | 76 for (Element const element : elements_) { |
| 77 if (element.object == nullptr) continue; |
| 78 DCHECK_NOT_NULL(element.index); |
| 79 DCHECK_NOT_NULL(element.value); |
| 80 if (MustAlias(object, element.object) && MustAlias(index, element.index)) { |
| 81 return element.value; |
| 82 } |
| 83 } |
| 84 return nullptr; |
| 85 } |
| 86 |
| 87 LoadElimination::AbstractElements const* |
| 88 LoadElimination::AbstractElements::Kill(Node* object, Node* index, |
| 89 Zone* zone) const { |
| 90 for (Element const element : this->elements_) { |
| 91 if (element.object == nullptr) continue; |
| 92 if (MayAlias(object, element.object)) { |
| 93 AbstractElements* that = new (zone) AbstractElements(zone); |
| 94 for (Element const element : this->elements_) { |
| 95 if (element.object == nullptr) continue; |
| 96 DCHECK_NOT_NULL(element.index); |
| 97 DCHECK_NOT_NULL(element.value); |
| 98 if (!MayAlias(object, element.object) || |
| 99 !MayAlias(index, element.index)) { |
| 100 that->elements_[that->next_index_++] = element; |
100 } | 101 } |
101 return that; | 102 } |
102 } | 103 return that; |
103 } | 104 } |
104 return this; | 105 } |
105 } | 106 return this; |
106 bool Equals(AbstractField const* that) const { | 107 } |
107 return this == that || this->info_for_node_ == that->info_for_node_; | 108 |
108 } | 109 bool LoadElimination::AbstractElements::Equals( |
109 AbstractField const* Merge(AbstractField const* that, Zone* zone) const { | 110 AbstractElements const* that) const { |
110 if (this->Equals(that)) return this; | 111 if (this == that) return true; |
111 AbstractField* copy = new (zone) AbstractField(zone); | 112 for (size_t i = 0; i < arraysize(elements_); ++i) { |
112 for (auto this_it : this->info_for_node_) { | 113 Element this_element = this->elements_[i]; |
113 Node* this_object = this_it.first; | 114 if (this_element.object == nullptr) continue; |
114 Node* this_value = this_it.second; | 115 for (size_t j = 0;; ++j) { |
115 auto that_it = that->info_for_node_.find(this_object); | 116 if (j == arraysize(elements_)) return false; |
116 if (that_it != that->info_for_node_.end() && | 117 Element that_element = that->elements_[j]; |
117 that_it->second == this_value) { | 118 if (this_element.object == that_element.object && |
118 copy->info_for_node_.insert(this_it); | 119 this_element.index == that_element.index && |
119 } | 120 this_element.value == that_element.value) { |
120 } | 121 break; |
121 return copy; | 122 } |
122 } | 123 } |
123 | 124 } |
124 private: | 125 for (size_t i = 0; i < arraysize(elements_); ++i) { |
125 ZoneMap<Node*, Node*> info_for_node_; | 126 Element that_element = that->elements_[i]; |
126 }; | 127 if (that_element.object == nullptr) continue; |
127 | 128 for (size_t j = 0;; ++j) { |
128 // Abstract state to track the state of all fields along the effect paths | 129 if (j == arraysize(elements_)) return false; |
129 // through the graph. | 130 Element this_element = this->elements_[j]; |
130 class AbstractState final : public ZoneObject { | 131 if (that_element.object == this_element.object && |
131 public: | 132 that_element.index == this_element.index && |
132 AbstractState() { | 133 that_element.value == this_element.value) { |
133 for (size_t i = 0; i < kMaxTrackedFields; ++i) fields_[i] = nullptr; | 134 break; |
134 } | 135 } |
135 | 136 } |
136 AbstractState const* Extend(Node* object, size_t index, Node* value, | 137 } |
137 Zone* zone) const { | 138 return true; |
138 AbstractState* that = new (zone) AbstractState(*this); | 139 } |
139 AbstractField const* that_field = that->fields_[index]; | 140 |
140 if (that_field) { | 141 LoadElimination::AbstractElements const* |
141 that_field = that_field->Extend(object, value, zone); | 142 LoadElimination::AbstractElements::Merge(AbstractElements const* that, |
| 143 Zone* zone) const { |
| 144 if (this->Equals(that)) return this; |
| 145 AbstractElements* copy = new (zone) AbstractElements(zone); |
| 146 for (Element const this_element : this->elements_) { |
| 147 if (this_element.object == nullptr) continue; |
| 148 for (Element const that_element : that->elements_) { |
| 149 if (this_element.object == that_element.object && |
| 150 this_element.index == that_element.index && |
| 151 this_element.value == that_element.value) { |
| 152 copy->elements_[copy->next_index_++] = this_element; |
| 153 } |
| 154 } |
| 155 } |
| 156 return copy; |
| 157 } |
| 158 |
| 159 Node* LoadElimination::AbstractField::Lookup(Node* object) const { |
| 160 for (auto pair : info_for_node_) { |
| 161 if (MustAlias(object, pair.first)) return pair.second; |
| 162 } |
| 163 return nullptr; |
| 164 } |
| 165 |
| 166 LoadElimination::AbstractField const* LoadElimination::AbstractField::Kill( |
| 167 Node* object, Zone* zone) const { |
| 168 for (auto pair : this->info_for_node_) { |
| 169 if (MayAlias(object, pair.first)) { |
| 170 AbstractField* that = new (zone) AbstractField(zone); |
| 171 for (auto pair : this->info_for_node_) { |
| 172 if (!MayAlias(object, pair.first)) that->info_for_node_.insert(pair); |
| 173 } |
| 174 return that; |
| 175 } |
| 176 } |
| 177 return this; |
| 178 } |
| 179 |
| 180 bool LoadElimination::AbstractState::Equals(AbstractState const* that) const { |
| 181 if (this->elements_) { |
| 182 if (!that->elements_ || !that->elements_->Equals(this->elements_)) { |
| 183 return false; |
| 184 } |
| 185 } else if (that->elements_) { |
| 186 return false; |
| 187 } |
| 188 for (size_t i = 0u; i < arraysize(fields_); ++i) { |
| 189 AbstractField const* this_field = this->fields_[i]; |
| 190 AbstractField const* that_field = that->fields_[i]; |
| 191 if (this_field) { |
| 192 if (!that_field || !that_field->Equals(this_field)) return false; |
| 193 } else if (that_field) { |
| 194 return false; |
| 195 } |
| 196 } |
| 197 return true; |
| 198 } |
| 199 |
| 200 void LoadElimination::AbstractState::Merge(AbstractState const* that, |
| 201 Zone* zone) { |
| 202 // Merge the information we have about the elements. |
| 203 if (this->elements_) { |
| 204 this->elements_ = that->elements_ |
| 205 ? that->elements_->Merge(this->elements_, zone) |
| 206 : that->elements_; |
| 207 } else { |
| 208 this->elements_ = that->elements_; |
| 209 } |
| 210 |
| 211 // Merge the information we have about the fields. |
| 212 for (size_t i = 0; i < arraysize(fields_); ++i) { |
| 213 if (this->fields_[i]) { |
| 214 if (that->fields_[i]) { |
| 215 this->fields_[i] = this->fields_[i]->Merge(that->fields_[i], zone); |
| 216 } else { |
| 217 this->fields_[i] = nullptr; |
| 218 } |
| 219 } |
| 220 } |
| 221 } |
| 222 |
| 223 Node* LoadElimination::AbstractState::LookupElement(Node* object, |
| 224 Node* index) const { |
| 225 if (this->elements_) { |
| 226 return this->elements_->Lookup(object, index); |
| 227 } |
| 228 return nullptr; |
| 229 } |
| 230 |
| 231 LoadElimination::AbstractState const* |
| 232 LoadElimination::AbstractState::AddElement(Node* object, Node* index, |
| 233 Node* value, Zone* zone) const { |
| 234 AbstractState* that = new (zone) AbstractState(*this); |
| 235 if (that->elements_) { |
| 236 that->elements_ = that->elements_->Extend(object, index, value, zone); |
| 237 } else { |
| 238 that->elements_ = new (zone) AbstractElements(object, index, value, zone); |
| 239 } |
| 240 return that; |
| 241 } |
| 242 |
| 243 LoadElimination::AbstractState const* |
| 244 LoadElimination::AbstractState::KillElement(Node* object, Node* index, |
| 245 Zone* zone) const { |
| 246 if (this->elements_) { |
| 247 AbstractElements const* that_elements = |
| 248 this->elements_->Kill(object, index, zone); |
| 249 if (this->elements_ != that_elements) { |
| 250 AbstractState* that = new (zone) AbstractState(*this); |
| 251 that->elements_ = that_elements; |
| 252 return that; |
| 253 } |
| 254 } |
| 255 return this; |
| 256 } |
| 257 |
| 258 LoadElimination::AbstractState const* LoadElimination::AbstractState::AddField( |
| 259 Node* object, size_t index, Node* value, Zone* zone) const { |
| 260 AbstractState* that = new (zone) AbstractState(*this); |
| 261 if (that->fields_[index]) { |
| 262 that->fields_[index] = that->fields_[index]->Extend(object, value, zone); |
| 263 } else { |
| 264 that->fields_[index] = new (zone) AbstractField(object, value, zone); |
| 265 } |
| 266 return that; |
| 267 } |
| 268 |
| 269 LoadElimination::AbstractState const* LoadElimination::AbstractState::KillField( |
| 270 Node* object, size_t index, Zone* zone) const { |
| 271 if (AbstractField const* this_field = this->fields_[index]) { |
| 272 this_field = this_field->Kill(object, zone); |
| 273 if (this->fields_[index] != this_field) { |
| 274 AbstractState* that = new (zone) AbstractState(*this); |
| 275 that->fields_[index] = this_field; |
| 276 return that; |
| 277 } |
| 278 } |
| 279 return this; |
| 280 } |
| 281 |
| 282 Node* LoadElimination::AbstractState::LookupField(Node* object, |
| 283 size_t index) const { |
| 284 if (AbstractField const* this_field = this->fields_[index]) { |
| 285 return this_field->Lookup(object); |
| 286 } |
| 287 return nullptr; |
| 288 } |
| 289 |
| 290 LoadElimination::AbstractState const* |
| 291 LoadElimination::AbstractStateForEffectNodes::Get(Node* node) const { |
| 292 size_t const id = node->id(); |
| 293 if (id < info_for_node_.size()) return info_for_node_[id]; |
| 294 return nullptr; |
| 295 } |
| 296 |
| 297 void LoadElimination::AbstractStateForEffectNodes::Set( |
| 298 Node* node, AbstractState const* state) { |
| 299 size_t const id = node->id(); |
| 300 if (id >= info_for_node_.size()) info_for_node_.resize(id + 1, nullptr); |
| 301 info_for_node_[id] = state; |
| 302 } |
| 303 |
| 304 Reduction LoadElimination::ReduceLoadField(Node* node) { |
| 305 FieldAccess const& access = FieldAccessOf(node->op()); |
| 306 Node* const object = NodeProperties::GetValueInput(node, 0); |
| 307 Node* const effect = NodeProperties::GetEffectInput(node); |
| 308 AbstractState const* state = node_states_.Get(effect); |
| 309 if (state == nullptr) return NoChange(); |
| 310 int field_index = FieldIndexOf(access); |
| 311 if (field_index >= 0) { |
| 312 if (Node* const replacement = state->LookupField(object, field_index)) { |
| 313 // Make sure the {replacement} has at least as good type |
| 314 // as the original {node}. |
| 315 if (NodeProperties::GetType(replacement) |
| 316 ->Is(NodeProperties::GetType(node))) { |
| 317 ReplaceWithValue(node, replacement, effect); |
| 318 DCHECK(!replacement->IsDead()); |
| 319 return Replace(replacement); |
| 320 } |
| 321 } |
| 322 state = state->AddField(object, field_index, node, zone()); |
| 323 } |
| 324 return UpdateState(node, state); |
| 325 } |
| 326 |
| 327 Reduction LoadElimination::ReduceStoreField(Node* node) { |
| 328 FieldAccess const& access = FieldAccessOf(node->op()); |
| 329 Node* const object = NodeProperties::GetValueInput(node, 0); |
| 330 Node* const new_value = NodeProperties::GetValueInput(node, 1); |
| 331 Node* const effect = NodeProperties::GetEffectInput(node); |
| 332 AbstractState const* state = node_states_.Get(effect); |
| 333 if (state == nullptr) return NoChange(); |
| 334 int field_index = FieldIndexOf(access); |
| 335 if (field_index >= 0) { |
| 336 Node* const old_value = state->LookupField(object, field_index); |
| 337 if (old_value == new_value) { |
| 338 // This store is fully redundant. |
| 339 return Replace(effect); |
| 340 } |
| 341 // Kill all potentially aliasing fields and record the new value. |
| 342 state = state->KillField(object, field_index, zone()); |
| 343 state = state->AddField(object, field_index, new_value, zone()); |
| 344 } else { |
| 345 // Unsupported StoreField operator. |
| 346 state = empty_state(); |
| 347 } |
| 348 return UpdateState(node, state); |
| 349 } |
| 350 |
| 351 Reduction LoadElimination::ReduceLoadElement(Node* node) { |
| 352 Node* const object = NodeProperties::GetValueInput(node, 0); |
| 353 Node* const index = NodeProperties::GetValueInput(node, 1); |
| 354 Node* const effect = NodeProperties::GetEffectInput(node); |
| 355 AbstractState const* state = node_states_.Get(effect); |
| 356 if (state == nullptr) return NoChange(); |
| 357 if (Node* const replacement = state->LookupElement(object, index)) { |
| 358 // Make sure the {replacement} has at least as good type |
| 359 // as the original {node}. |
| 360 if (NodeProperties::GetType(replacement) |
| 361 ->Is(NodeProperties::GetType(node))) { |
| 362 ReplaceWithValue(node, replacement, effect); |
| 363 DCHECK(!replacement->IsDead()); |
| 364 return Replace(replacement); |
| 365 } |
| 366 } |
| 367 state = state->AddElement(object, index, node, zone()); |
| 368 return UpdateState(node, state); |
| 369 } |
| 370 |
| 371 Reduction LoadElimination::ReduceStoreElement(Node* node) { |
| 372 ElementAccess const& access = ElementAccessOf(node->op()); |
| 373 Node* const object = NodeProperties::GetValueInput(node, 0); |
| 374 Node* const index = NodeProperties::GetValueInput(node, 1); |
| 375 Node* const new_value = NodeProperties::GetValueInput(node, 2); |
| 376 Node* const effect = NodeProperties::GetEffectInput(node); |
| 377 AbstractState const* state = node_states_.Get(effect); |
| 378 if (state == nullptr) return NoChange(); |
| 379 Node* const old_value = state->LookupElement(object, index); |
| 380 if (old_value == new_value) { |
| 381 // This store is fully redundant. |
| 382 return Replace(effect); |
| 383 } |
| 384 // Kill all potentially aliasing elements. |
| 385 state = state->KillElement(object, index, zone()); |
| 386 // Only record the new value if the store doesn't have an implicit truncation. |
| 387 switch (access.machine_type.representation()) { |
| 388 case MachineRepresentation::kNone: |
| 389 case MachineRepresentation::kBit: |
| 390 UNREACHABLE(); |
| 391 break; |
| 392 case MachineRepresentation::kWord8: |
| 393 case MachineRepresentation::kWord16: |
| 394 case MachineRepresentation::kWord32: |
| 395 case MachineRepresentation::kWord64: |
| 396 case MachineRepresentation::kFloat32: |
| 397 // TODO(turbofan): Add support for doing the truncations. |
| 398 break; |
| 399 case MachineRepresentation::kFloat64: |
| 400 case MachineRepresentation::kSimd128: |
| 401 case MachineRepresentation::kTagged: |
| 402 state = state->AddElement(object, index, new_value, zone()); |
| 403 break; |
| 404 } |
| 405 return UpdateState(node, state); |
| 406 } |
| 407 |
| 408 Reduction LoadElimination::ReduceEffectPhi(Node* node) { |
| 409 Node* const effect0 = NodeProperties::GetEffectInput(node, 0); |
| 410 Node* const control = NodeProperties::GetControlInput(node); |
| 411 AbstractState const* state0 = node_states_.Get(effect0); |
| 412 if (state0 == nullptr) return NoChange(); |
| 413 if (control->opcode() == IrOpcode::kLoop) { |
| 414 // Here we rely on having only reducible loops: |
| 415 // The loop entry edge always dominates the header, so we can just take |
| 416 // the state from the first input, and compute the loop state based on it. |
| 417 AbstractState const* state = ComputeLoopState(node, state0); |
| 418 return UpdateState(node, state); |
| 419 } |
| 420 DCHECK_EQ(IrOpcode::kMerge, control->opcode()); |
| 421 |
| 422 // Shortcut for the case when we do not know anything about some input. |
| 423 int const input_count = node->op()->EffectInputCount(); |
| 424 for (int i = 1; i < input_count; ++i) { |
| 425 Node* const effect = NodeProperties::GetEffectInput(node, i); |
| 426 if (node_states_.Get(effect) == nullptr) return NoChange(); |
| 427 } |
| 428 |
| 429 // Make a copy of the first input's state and merge with the state |
| 430 // from other inputs. |
| 431 AbstractState* state = new (zone()) AbstractState(*state0); |
| 432 for (int i = 1; i < input_count; ++i) { |
| 433 Node* const input = NodeProperties::GetEffectInput(node, i); |
| 434 state->Merge(node_states_.Get(input), zone()); |
| 435 } |
| 436 return UpdateState(node, state); |
| 437 } |
| 438 |
| 439 Reduction LoadElimination::ReduceStart(Node* node) { |
| 440 return UpdateState(node, empty_state()); |
| 441 } |
| 442 |
| 443 Reduction LoadElimination::ReduceOtherNode(Node* node) { |
| 444 if (node->op()->EffectInputCount() == 1) { |
| 445 if (node->op()->EffectOutputCount() == 1) { |
| 446 Node* const effect = NodeProperties::GetEffectInput(node); |
| 447 AbstractState const* state = node_states_.Get(effect); |
| 448 // If we do not know anything about the predecessor, do not propagate |
| 449 // just yet because we will have to recompute anyway once we compute |
| 450 // the predecessor. |
| 451 if (state == nullptr) return NoChange(); |
| 452 // Check if this {node} has some uncontrolled side effects. |
| 453 if (!node->op()->HasProperty(Operator::kNoWrite)) { |
| 454 state = empty_state(); |
| 455 } |
| 456 return UpdateState(node, state); |
142 } else { | 457 } else { |
143 that_field = new (zone) AbstractField(object, value, zone); | 458 // Effect terminators should be handled specially. |
144 } | 459 return NoChange(); |
145 that->fields_[index] = that_field; | 460 } |
146 return that; | 461 } |
147 } | 462 DCHECK_EQ(0, node->op()->EffectInputCount()); |
148 AbstractState const* Kill(Node* object, size_t index, Zone* zone) const { | 463 DCHECK_EQ(0, node->op()->EffectOutputCount()); |
149 if (!this->fields_[index]) return this; | 464 return NoChange(); |
150 AbstractState* that = new (zone) AbstractState(*this); | 465 } |
151 that->fields_[index] = nullptr; | 466 |
152 return that; | 467 Reduction LoadElimination::UpdateState(Node* node, AbstractState const* state) { |
153 } | 468 AbstractState const* original = node_states_.Get(node); |
154 AbstractState const* Merge(AbstractState const* that, Zone* zone) const { | 469 // Only signal that the {node} has Changed, if the information about {state} |
155 if (this->Equals(that)) return this; | 470 // has changed wrt. the {original}. |
156 AbstractState* copy = new (zone) AbstractState(); | 471 if (state != original) { |
157 for (size_t i = 0; i < kMaxTrackedFields; ++i) { | 472 if (original == nullptr || !state->Equals(original)) { |
158 AbstractField const* this_field = this->fields_[i]; | 473 node_states_.Set(node, state); |
159 AbstractField const* that_field = that->fields_[i]; | 474 return Changed(node); |
160 if (this_field && that_field) { | 475 } |
161 copy->fields_[i] = this_field->Merge(that_field, zone); | 476 } |
162 } | 477 return NoChange(); |
163 } | 478 } |
164 return copy; | 479 |
165 } | 480 LoadElimination::AbstractState const* LoadElimination::ComputeLoopState( |
166 Node* Lookup(Node* object, size_t index) const { | 481 Node* node, AbstractState const* state) const { |
167 AbstractField const* this_field = this->fields_[index]; | 482 Node* const control = NodeProperties::GetControlInput(node); |
168 if (this_field) return this_field->Lookup(object); | 483 ZoneQueue<Node*> queue(zone()); |
169 return nullptr; | 484 ZoneSet<Node*> visited(zone()); |
170 } | 485 visited.insert(node); |
171 bool Equals(AbstractState const* that) const { | 486 for (int i = 1; i < control->InputCount(); ++i) { |
172 if (this == that) return true; | 487 queue.push(node->InputAt(i)); |
173 for (size_t i = 0; i < kMaxTrackedFields; ++i) { | 488 } |
174 AbstractField const* this_field = this->fields_[i]; | 489 while (!queue.empty()) { |
175 AbstractField const* that_field = that->fields_[i]; | 490 Node* const current = queue.front(); |
176 if (this_field) { | 491 queue.pop(); |
177 if (!that_field || !this_field->Equals(that_field)) return false; | 492 if (visited.find(current) == visited.end()) { |
178 } else if (that_field) { | 493 visited.insert(current); |
179 return false; | 494 if (!current->op()->HasProperty(Operator::kNoWrite)) { |
180 } | 495 switch (current->opcode()) { |
181 DCHECK(this_field == that_field || this_field->Equals(that_field)); | 496 case IrOpcode::kStoreField: { |
182 } | 497 FieldAccess const& access = FieldAccessOf(current->op()); |
183 return true; | 498 Node* const object = NodeProperties::GetValueInput(current, 0); |
184 } | 499 int field_index = FieldIndexOf(access); |
185 | 500 if (field_index < 0) return empty_state(); |
186 private: | 501 state = state->KillField(object, field_index, zone()); |
187 AbstractField const* fields_[kMaxTrackedFields]; | 502 break; |
188 }; | |
189 | |
190 class LoadEliminationAnalysis final { | |
191 public: | |
192 LoadEliminationAnalysis(Graph* graph, Zone* zone) | |
193 : candidates_(zone), | |
194 empty_state_(), | |
195 queue_(zone), | |
196 node_states_(graph->NodeCount(), zone) {} | |
197 | |
198 void Run(Node* start) { | |
199 TRACE("--{Analysis phase}--\n"); | |
200 UpdateState(start, empty_state()); | |
201 while (!queue_.empty()) { | |
202 Node* const node = queue_.top(); | |
203 queue_.pop(); | |
204 VisitNode(node); | |
205 } | |
206 TRACE("--{Replacement phase}--\n"); | |
207 ZoneMap<Node*, Node*> replacements(zone()); | |
208 for (Node* const node : candidates_) { | |
209 switch (node->opcode()) { | |
210 case IrOpcode::kLoadField: { | |
211 FieldAccess const& access = FieldAccessOf(node->op()); | |
212 Node* const object = | |
213 ActualValue(NodeProperties::GetValueInput(node, 0)); | |
214 Node* const effect = NodeProperties::GetEffectInput(node); | |
215 AbstractState const* state = GetState(effect); | |
216 int field_index = FieldIndexOf(access); | |
217 DCHECK_LE(0, field_index); | |
218 if (Node* value = state->Lookup(object, field_index)) { | |
219 auto it = replacements.find(value); | |
220 if (it != replacements.end()) value = it->second; | |
221 Type* const value_type = NodeProperties::GetType(value); | |
222 if (value_type->Is(access.type)) { | |
223 replacements.insert(std::make_pair(node, value)); | |
224 TRACE(" - Replacing redundant #%d:LoadField with #%d:%s\n", | |
225 node->id(), value->id(), value->op()->mnemonic()); | |
226 NodeProperties::ReplaceUses(node, value, effect); | |
227 node->Kill(); | |
228 } else { | |
229 TRACE( | |
230 " - Cannot replace redundant #%d:LoadField with #%d:%s," | |
231 " because types don't agree", | |
232 node->id(), value->id(), value->op()->mnemonic()); | |
233 } | |
234 } | 503 } |
235 break; | 504 case IrOpcode::kStoreElement: { |
| 505 Node* const object = NodeProperties::GetValueInput(current, 0); |
| 506 Node* const index = NodeProperties::GetValueInput(current, 1); |
| 507 state = state->KillElement(object, index, zone()); |
| 508 break; |
| 509 } |
| 510 default: |
| 511 return empty_state(); |
236 } | 512 } |
237 case IrOpcode::kStoreField: { | 513 } |
238 FieldAccess const& access = FieldAccessOf(node->op()); | 514 for (int i = 0; i < current->op()->EffectInputCount(); ++i) { |
239 Node* const object = | 515 queue.push(NodeProperties::GetEffectInput(current, i)); |
240 ActualValue(NodeProperties::GetValueInput(node, 0)); | 516 } |
241 Node* const value = | 517 } |
242 ActualValue(NodeProperties::GetValueInput(node, 1)); | 518 } |
243 Node* const effect = NodeProperties::GetEffectInput(node); | 519 return state; |
244 AbstractState const* state = GetState(effect); | 520 } |
245 int field_index = FieldIndexOf(access); | 521 |
246 DCHECK_LE(0, field_index); | 522 // static |
247 if (value == state->Lookup(object, field_index)) { | 523 int LoadElimination::FieldIndexOf(FieldAccess const& access) { |
248 TRACE(" - Killing redundant #%d:StoreField\n", node->id()); | 524 switch (access.machine_type.representation()) { |
249 NodeProperties::ReplaceUses(node, value, effect); | 525 case MachineRepresentation::kNone: |
250 node->Kill(); | 526 case MachineRepresentation::kBit: |
251 } | 527 UNREACHABLE(); |
252 break; | 528 break; |
253 } | 529 case MachineRepresentation::kWord8: |
254 default: | 530 case MachineRepresentation::kWord16: |
255 UNREACHABLE(); | 531 case MachineRepresentation::kWord32: |
256 } | 532 case MachineRepresentation::kWord64: |
257 } | 533 case MachineRepresentation::kFloat32: |
258 } | 534 return -1; // Currently untracked. |
259 | 535 case MachineRepresentation::kFloat64: |
260 private: | 536 case MachineRepresentation::kSimd128: |
261 void VisitNode(Node* node) { | 537 case MachineRepresentation::kTagged: |
262 TRACE(" - Visiting node #%d:%s\n", node->id(), node->op()->mnemonic()); | 538 // TODO(bmeurer): Check that we never do overlapping load/stores of |
263 switch (node->opcode()) { | 539 // individual parts of Float64/Simd128 values. |
264 case IrOpcode::kEffectPhi: | 540 break; |
265 return VisitEffectPhi(node); | 541 } |
266 case IrOpcode::kLoadField: | 542 DCHECK_EQ(kTaggedBase, access.base_is_tagged); |
267 return VisitLoadField(node); | 543 DCHECK_EQ(0, access.offset % kPointerSize); |
268 case IrOpcode::kStoreElement: | 544 int field_index = access.offset / kPointerSize; |
269 return VisitStoreElement(node); | 545 if (field_index >= static_cast<int>(kMaxTrackedFields)) return -1; |
270 case IrOpcode::kStoreField: | 546 return field_index; |
271 return VisitStoreField(node); | |
272 case IrOpcode::kDeoptimize: | |
273 case IrOpcode::kReturn: | |
274 case IrOpcode::kTerminate: | |
275 case IrOpcode::kThrow: | |
276 break; | |
277 default: | |
278 return VisitOtherNode(node); | |
279 } | |
280 } | |
281 | |
282 void VisitEffectPhi(Node* node) { | |
283 int const input_count = node->InputCount() - 1; | |
284 DCHECK_LT(0, input_count); | |
285 Node* const control = NodeProperties::GetControlInput(node); | |
286 Node* const effect0 = NodeProperties::GetEffectInput(node, 0); | |
287 AbstractState const* state = GetState(effect0); | |
288 if (state == nullptr) return; | |
289 if (control->opcode() == IrOpcode::kMerge) { | |
290 for (int i = 1; i < input_count; ++i) { | |
291 Node* const effecti = NodeProperties::GetEffectInput(node, i); | |
292 if (GetState(effecti) == nullptr) return; | |
293 } | |
294 } | |
295 for (int i = 1; i < input_count; ++i) { | |
296 Node* const effecti = NodeProperties::GetEffectInput(node, i); | |
297 if (AbstractState const* statei = GetState(effecti)) { | |
298 state = state->Merge(statei, zone()); | |
299 } | |
300 } | |
301 UpdateState(node, state); | |
302 } | |
303 | |
304 void VisitLoadField(Node* node) { | |
305 FieldAccess const& access = FieldAccessOf(node->op()); | |
306 Node* const object = ActualValue(NodeProperties::GetValueInput(node, 0)); | |
307 Node* const effect = NodeProperties::GetEffectInput(node); | |
308 AbstractState const* state = GetState(effect); | |
309 int field_index = FieldIndexOf(access); | |
310 if (field_index >= 0) { | |
311 Node* const value = state->Lookup(object, field_index); | |
312 if (!value) { | |
313 TRACE(" Node #%d:LoadField is not redundant\n", node->id()); | |
314 state = state->Extend(object, field_index, node, zone()); | |
315 } else if (!NodeProperties::GetType(value)->Is(access.type)) { | |
316 TRACE( | |
317 " Node #%d:LoadField is redundant for #%d:%s, but" | |
318 " types don't agree\n", | |
319 node->id(), value->id(), value->op()->mnemonic()); | |
320 state = state->Extend(object, field_index, node, zone()); | |
321 } else if (value) { | |
322 TRACE(" Node #%d:LoadField is fully redundant for #%d:%s\n", | |
323 node->id(), value->id(), value->op()->mnemonic()); | |
324 candidates_.insert(node); | |
325 } | |
326 } else { | |
327 TRACE(" Node #%d:LoadField is unsupported\n", node->id()); | |
328 } | |
329 UpdateState(node, state); | |
330 } | |
331 | |
332 void VisitStoreField(Node* node) { | |
333 FieldAccess const& access = FieldAccessOf(node->op()); | |
334 Node* const object = ActualValue(NodeProperties::GetValueInput(node, 0)); | |
335 Node* const new_value = NodeProperties::GetValueInput(node, 1); | |
336 Node* const effect = NodeProperties::GetEffectInput(node); | |
337 AbstractState const* state = GetState(effect); | |
338 int field_index = FieldIndexOf(access); | |
339 if (field_index >= 0) { | |
340 Node* const old_value = state->Lookup(object, field_index); | |
341 if (old_value == new_value) { | |
342 TRACE(" Node #%d:StoreField is fully redundant, storing #%d:%s\n", | |
343 node->id(), new_value->id(), new_value->op()->mnemonic()); | |
344 candidates_.insert(node); | |
345 } | |
346 TRACE(" Killing all potentially aliasing stores for %d on #%d:%s\n", | |
347 field_index, object->id(), object->op()->mnemonic()); | |
348 state = state->Kill(object, field_index, zone()); | |
349 TRACE(" Node #%d:StoreField provides #%d:%s for %d on #%d:%s\n", | |
350 node->id(), new_value->id(), new_value->op()->mnemonic(), | |
351 field_index, object->id(), object->op()->mnemonic()); | |
352 state = state->Extend(object, field_index, new_value, zone()); | |
353 } else { | |
354 TRACE(" Node #%d:StoreField is unsupported\n", node->id()); | |
355 state = empty_state(); | |
356 } | |
357 UpdateState(node, state); | |
358 } | |
359 | |
360 void VisitStoreElement(Node* node) { | |
361 Node* const effect = NodeProperties::GetEffectInput(node); | |
362 AbstractState const* state = GetState(effect); | |
363 UpdateState(node, state); | |
364 } | |
365 | |
366 void VisitOtherNode(Node* node) { | |
367 DCHECK_EQ(1, node->op()->EffectInputCount()); | |
368 DCHECK_EQ(1, node->op()->EffectOutputCount()); | |
369 Node* const effect = NodeProperties::GetEffectInput(node); | |
370 AbstractState const* state = node->op()->HasProperty(Operator::kNoWrite) | |
371 ? GetState(effect) | |
372 : empty_state(); | |
373 UpdateState(node, state); | |
374 } | |
375 | |
376 int FieldIndexOf(FieldAccess const& access) const { | |
377 switch (access.machine_type.representation()) { | |
378 case MachineRepresentation::kNone: | |
379 case MachineRepresentation::kBit: | |
380 UNREACHABLE(); | |
381 break; | |
382 case MachineRepresentation::kWord8: | |
383 case MachineRepresentation::kWord16: | |
384 case MachineRepresentation::kWord32: | |
385 case MachineRepresentation::kWord64: | |
386 case MachineRepresentation::kFloat32: | |
387 return -1; // Currently untracked. | |
388 case MachineRepresentation::kFloat64: | |
389 case MachineRepresentation::kSimd128: | |
390 case MachineRepresentation::kTagged: | |
391 // TODO(bmeurer): Check that we never do overlapping load/stores of | |
392 // individual parts of Float64/Simd128 values. | |
393 break; | |
394 } | |
395 DCHECK_EQ(kTaggedBase, access.base_is_tagged); | |
396 DCHECK_EQ(0, access.offset % kPointerSize); | |
397 int field_index = access.offset / kPointerSize; | |
398 if (field_index >= static_cast<int>(kMaxTrackedFields)) return -1; | |
399 return field_index; | |
400 } | |
401 | |
402 AbstractState const* GetState(Node* node) const { | |
403 return node_states_[node->id()]; | |
404 } | |
405 void SetState(Node* node, AbstractState const* state) { | |
406 node_states_[node->id()] = state; | |
407 } | |
408 | |
409 void UpdateState(Node* node, AbstractState const* new_state) { | |
410 AbstractState const* old_state = GetState(node); | |
411 if (old_state && old_state->Equals(new_state)) return; | |
412 SetState(node, new_state); | |
413 EnqueueUses(node); | |
414 } | |
415 | |
416 void EnqueueUses(Node* node) { | |
417 for (Edge const edge : node->use_edges()) { | |
418 if (NodeProperties::IsEffectEdge(edge)) { | |
419 queue_.push(edge.from()); | |
420 } | |
421 } | |
422 } | |
423 | |
424 AbstractState const* empty_state() const { return &empty_state_; } | |
425 Zone* zone() const { return node_states_.get_allocator().zone(); } | |
426 | |
427 ZoneSet<Node*> candidates_; | |
428 AbstractState const empty_state_; | |
429 ZoneStack<Node*> queue_; | |
430 ZoneVector<AbstractState const*> node_states_; | |
431 | |
432 DISALLOW_COPY_AND_ASSIGN(LoadEliminationAnalysis); | |
433 }; | |
434 | |
435 } // namespace | |
436 | |
437 void LoadElimination::Run() { | |
438 LoadEliminationAnalysis analysis(graph(), zone()); | |
439 analysis.Run(graph()->start()); | |
440 } | 547 } |
441 | 548 |
442 } // namespace compiler | 549 } // namespace compiler |
443 } // namespace internal | 550 } // namespace internal |
444 } // namespace v8 | 551 } // namespace v8 |
OLD | NEW |