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