Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 #include "vm/flow_graph_optimizer.h" | 5 #include "vm/flow_graph_optimizer.h" |
| 6 | 6 |
| 7 #include "vm/bit_vector.h" | 7 #include "vm/bit_vector.h" |
| 8 #include "vm/cha.h" | 8 #include "vm/cha.h" |
| 9 #include "vm/flow_graph_builder.h" | 9 #include "vm/flow_graph_builder.h" |
| 10 #include "vm/flow_graph_compiler.h" | 10 #include "vm/flow_graph_compiler.h" |
| (...skipping 4029 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 4040 if (comma) { | 4040 if (comma) { |
| 4041 OS::Print(", "); | 4041 OS::Print(", "); |
| 4042 } | 4042 } |
| 4043 OS::Print("%s", places_[it.Current()]->ToCString()); | 4043 OS::Print("%s", places_[it.Current()]->ToCString()); |
| 4044 comma = true; | 4044 comma = true; |
| 4045 } | 4045 } |
| 4046 } | 4046 } |
| 4047 | 4047 |
| 4048 const PhiPlaceMoves* phi_moves() const { return phi_moves_; } | 4048 const PhiPlaceMoves* phi_moves() const { return phi_moves_; } |
| 4049 | 4049 |
| 4050 // Returns true if the result of AllocateObject can be aliased by some | |
| 4051 // other SSA variable and false otherwise. Currently simply checks if | |
| 4052 // this value is stored in a field, escapes to another function or | |
| 4053 // participates in a phi. | |
| 4054 static bool CanBeAliased(AllocateObjectInstr* alloc) { | |
| 4055 if (alloc->identity() == AllocateObjectInstr::kUnknown) { | |
| 4056 bool escapes = false; | |
| 4057 for (Value* use = alloc->input_use_list(); | |
| 4058 use != NULL; | |
| 4059 use = use->next_use()) { | |
| 4060 Instruction* instr = use->instruction(); | |
| 4061 if (instr->IsPushArgument() || | |
| 4062 (instr->IsStoreVMField() && (use->use_index() != 1)) || | |
|
Vyacheslav Egorov (Google)
2013/06/27 13:15:45
Good catch!
| |
| 4063 (instr->IsStoreInstanceField() && (use->use_index() != 0)) || | |
| 4064 (instr->IsStoreStaticField()) || | |
| 4065 (instr->IsPhi())) { | |
| 4066 escapes = true; | |
| 4067 break; | |
| 4068 } | |
| 4069 } | |
| 4070 | |
| 4071 alloc->set_identity(escapes ? AllocateObjectInstr::kAliased | |
| 4072 : AllocateObjectInstr::kNotAliased); | |
| 4073 } | |
| 4074 | |
| 4075 return alloc->identity() != AllocateObjectInstr::kNotAliased; | |
| 4076 } | |
| 4077 | |
| 4050 private: | 4078 private: |
| 4051 // Get id assigned to the given field. Assign a new id if the field is seen | 4079 // Get id assigned to the given field. Assign a new id if the field is seen |
| 4052 // for the first time. | 4080 // for the first time. |
| 4053 intptr_t GetFieldId(intptr_t instance_id, const Field& field) { | 4081 intptr_t GetFieldId(intptr_t instance_id, const Field& field) { |
| 4054 intptr_t id = field_ids_.Lookup(FieldIdPair::Key(instance_id, &field)); | 4082 intptr_t id = field_ids_.Lookup(FieldIdPair::Key(instance_id, &field)); |
| 4055 if (id == 0) { | 4083 if (id == 0) { |
| 4056 id = ++max_field_id_; | 4084 id = ++max_field_id_; |
| 4057 field_ids_.Insert(FieldIdPair(FieldIdPair::Key(instance_id, &field), id)); | 4085 field_ids_.Insert(FieldIdPair(FieldIdPair::Key(instance_id, &field), id)); |
| 4058 } | 4086 } |
| 4059 return id; | 4087 return id; |
| (...skipping 27 matching lines...) Expand all Loading... | |
| 4087 | 4115 |
| 4088 return GetFieldId(instance_id, field); | 4116 return GetFieldId(instance_id, field); |
| 4089 } | 4117 } |
| 4090 | 4118 |
| 4091 // Get or create an identifier for a static field. | 4119 // Get or create an identifier for a static field. |
| 4092 intptr_t GetStaticFieldId(const Field& field) { | 4120 intptr_t GetStaticFieldId(const Field& field) { |
| 4093 ASSERT(field.is_static()); | 4121 ASSERT(field.is_static()); |
| 4094 return GetFieldId(kAnyInstance, field); | 4122 return GetFieldId(kAnyInstance, field); |
| 4095 } | 4123 } |
| 4096 | 4124 |
| 4097 // Returns true if the result of AllocateObject can be aliased by some | |
| 4098 // other SSA variable and false otherwise. Currently simply checks if | |
| 4099 // this value is stored in a field, escapes to another function or | |
| 4100 // participates in a phi. | |
| 4101 bool CanBeAliased(AllocateObjectInstr* alloc) { | |
| 4102 if (alloc->identity() == AllocateObjectInstr::kUnknown) { | |
| 4103 bool escapes = false; | |
| 4104 for (Value* use = alloc->input_use_list(); | |
| 4105 use != NULL; | |
| 4106 use = use->next_use()) { | |
| 4107 Instruction* instr = use->instruction(); | |
| 4108 if (instr->IsPushArgument() || | |
| 4109 (instr->IsStoreVMField() && (use->use_index() != 0)) || | |
| 4110 (instr->IsStoreInstanceField() && (use->use_index() != 0)) || | |
| 4111 (instr->IsStoreStaticField()) || | |
| 4112 (instr->IsPhi())) { | |
| 4113 escapes = true; | |
| 4114 break; | |
| 4115 } | |
| 4116 } | |
| 4117 | |
| 4118 alloc->set_identity(escapes ? AllocateObjectInstr::kAliased | |
| 4119 : AllocateObjectInstr::kNotAliased); | |
| 4120 } | |
| 4121 | |
| 4122 return alloc->identity() != AllocateObjectInstr::kNotAliased; | |
| 4123 } | |
| 4124 | |
| 4125 // Returns true if the given load is unaffected by external side-effects. | 4125 // Returns true if the given load is unaffected by external side-effects. |
| 4126 // This essentially means that no stores to the same location can | 4126 // This essentially means that no stores to the same location can |
| 4127 // occur in other functions. | 4127 // occur in other functions. |
| 4128 bool IsIndependentFromEffects(Place* place) { | 4128 bool IsIndependentFromEffects(Place* place) { |
| 4129 if (place->IsFinalField()) { | 4129 if (place->IsFinalField()) { |
| 4130 // Note that we can't use LoadField's is_immutable attribute here because | 4130 // Note that we can't use LoadField's is_immutable attribute here because |
| 4131 // some VM-fields (those that have no corresponding Field object and | 4131 // some VM-fields (those that have no corresponding Field object and |
| 4132 // accessed through offset alone) can share offset but have different | 4132 // accessed through offset alone) can share offset but have different |
| 4133 // immutability properties. | 4133 // immutability properties. |
| 4134 // One example is the length property of growable and fixed size list. If | 4134 // One example is the length property of growable and fixed size list. If |
| (...skipping 365 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 4500 gen->RemoveAll(aliased_set_->aliased_by_effects()); | 4500 gen->RemoveAll(aliased_set_->aliased_by_effects()); |
| 4501 continue; | 4501 continue; |
| 4502 } | 4502 } |
| 4503 | 4503 |
| 4504 Definition* defn = instr->AsDefinition(); | 4504 Definition* defn = instr->AsDefinition(); |
| 4505 if (defn == NULL) { | 4505 if (defn == NULL) { |
| 4506 continue; | 4506 continue; |
| 4507 } | 4507 } |
| 4508 | 4508 |
| 4509 // For object allocation forward initial values of the fields to | 4509 // For object allocation forward initial values of the fields to |
| 4510 // subsequent loads. | 4510 // subsequent loads. For simplicity we ignore escaping objects. |
| 4511 // For simplicity we ignore escaping objects and objects that have | 4511 // |
| 4512 // type arguments. | |
| 4513 // The reason to ignore escaping objects is that final fields are | 4512 // The reason to ignore escaping objects is that final fields are |
| 4514 // initialized in constructor that potentially can be not inlined into | 4513 // initialized in constructor that potentially can be not inlined into |
| 4515 // the function that we are currently optimizing. However at the same | 4514 // the function that we are currently optimizing. However at the same |
| 4516 // time we assume that values of the final fields can be forwarded | 4515 // time we assume that values of the final fields can be forwarded |
| 4517 // across side-effects. If we add 'null' as known values for these | 4516 // across side-effects. If we add 'null' as known values for these |
| 4518 // fields here we will incorrectly propagate this null across | 4517 // fields here we will incorrectly propagate this null across |
| 4519 // constructor invocation. | 4518 // constructor invocation. |
| 4520 // TODO(vegorov): record null-values at least for not final fields of | 4519 // TODO(vegorov): record null-values at least for not final fields of |
| 4521 // escaping object. | 4520 // escaping object. |
| 4522 AllocateObjectInstr* alloc = instr->AsAllocateObject(); | 4521 AllocateObjectInstr* alloc = instr->AsAllocateObject(); |
| 4523 if ((alloc != NULL) && | 4522 if ((alloc != NULL) && |
| 4524 (alloc->identity() == AllocateObjectInstr::kNotAliased) && | 4523 !AliasedSet::CanBeAliased(alloc) && |
| 4525 HasSimpleTypeArguments(alloc)) { | 4524 HasSimpleTypeArguments(alloc)) { |
| 4526 for (Value* use = alloc->input_use_list(); | 4525 for (Value* use = alloc->input_use_list(); |
| 4527 use != NULL; | 4526 use != NULL; |
| 4528 use = use->next_use()) { | 4527 use = use->next_use()) { |
| 4529 // Look for all immediate loads from this object. | 4528 // Look for all immediate loads from this object. |
| 4530 if (use->use_index() != 0) { | 4529 if (use->use_index() != 0) { |
| 4531 continue; | 4530 continue; |
| 4532 } | 4531 } |
| 4533 | 4532 |
| 4534 LoadFieldInstr* load = use->instruction()->AsLoadField(); | 4533 LoadFieldInstr* load = use->instruction()->AsLoadField(); |
| (...skipping 2422 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 6957 block_it.Advance()) { | 6956 block_it.Advance()) { |
| 6958 BlockEntryInstr* block = block_it.Current(); | 6957 BlockEntryInstr* block = block_it.Current(); |
| 6959 for (ForwardInstructionIterator it(block); !it.Done(); it.Advance()) { | 6958 for (ForwardInstructionIterator it(block); !it.Done(); it.Advance()) { |
| 6960 AllocateObjectInstr* alloc = it.Current()->AsAllocateObject(); | 6959 AllocateObjectInstr* alloc = it.Current()->AsAllocateObject(); |
| 6961 if ((alloc != NULL) && IsAllocationSinkingCandidate(alloc)) { | 6960 if ((alloc != NULL) && IsAllocationSinkingCandidate(alloc)) { |
| 6962 if (FLAG_trace_optimization) { | 6961 if (FLAG_trace_optimization) { |
| 6963 OS::Print("discovered allocation sinking candidate: v%"Pd"\n", | 6962 OS::Print("discovered allocation sinking candidate: v%"Pd"\n", |
| 6964 alloc->ssa_temp_index()); | 6963 alloc->ssa_temp_index()); |
| 6965 } | 6964 } |
| 6966 | 6965 |
| 6967 if (alloc->identity() == AllocateObjectInstr::kAliased) { | 6966 // All sinking candidate are known to be not aliased. |
| 6968 // Allocation might have been classified as aliased earlier due to | 6967 alloc->set_identity(AllocateObjectInstr::kNotAliased); |
| 6969 // some operations that are now eliminated. | |
| 6970 alloc->set_identity(AllocateObjectInstr::kNotAliased); | |
| 6971 } | |
| 6972 | 6968 |
| 6973 candidates.Add(alloc); | 6969 candidates.Add(alloc); |
| 6974 } | 6970 } |
| 6975 } | 6971 } |
| 6976 } | 6972 } |
| 6977 | 6973 |
| 6978 // Insert MaterializeObject instructions that will describe the state of the | 6974 // Insert MaterializeObject instructions that will describe the state of the |
| 6979 // object at all deoptimization points. Each inserted materialization looks | 6975 // object at all deoptimization points. Each inserted materialization looks |
| 6980 // like this (where v_0 is allocation that we are going to eliminate): | 6976 // like this (where v_0 is allocation that we are going to eliminate): |
| 6981 // v_1 <- LoadField(v_0, field_1) | 6977 // v_1 <- LoadField(v_0, field_1) |
| (...skipping 157 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 7139 | 7135 |
| 7140 // Insert materializations at environment uses. | 7136 // Insert materializations at environment uses. |
| 7141 const Class& cls = Class::Handle(alloc->constructor().Owner()); | 7137 const Class& cls = Class::Handle(alloc->constructor().Owner()); |
| 7142 for (intptr_t i = 0; i < exits.length(); i++) { | 7138 for (intptr_t i = 0; i < exits.length(); i++) { |
| 7143 CreateMaterializationAt(exits[i], alloc, cls, *fields); | 7139 CreateMaterializationAt(exits[i], alloc, cls, *fields); |
| 7144 } | 7140 } |
| 7145 } | 7141 } |
| 7146 | 7142 |
| 7147 | 7143 |
| 7148 } // namespace dart | 7144 } // namespace dart |
| OLD | NEW |