Home | History | Annotate | Download | only in optimizing
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #ifndef ART_COMPILER_OPTIMIZING_LOAD_STORE_ANALYSIS_H_
     18 #define ART_COMPILER_OPTIMIZING_LOAD_STORE_ANALYSIS_H_
     19 
     20 #include "escape.h"
     21 #include "nodes.h"
     22 #include "optimization.h"
     23 
     24 namespace art {
     25 
     26 // A ReferenceInfo contains additional info about a reference such as
     27 // whether it's a singleton, returned, etc.
     28 class ReferenceInfo : public ArenaObject<kArenaAllocLSA> {
     29  public:
     30   ReferenceInfo(HInstruction* reference, size_t pos)
     31       : reference_(reference),
     32         position_(pos),
     33         is_singleton_(true),
     34         is_singleton_and_not_returned_(true),
     35         is_singleton_and_not_deopt_visible_(true) {
     36     CalculateEscape(reference_,
     37                     nullptr,
     38                     &is_singleton_,
     39                     &is_singleton_and_not_returned_,
     40                     &is_singleton_and_not_deopt_visible_);
     41   }
     42 
     43   HInstruction* GetReference() const {
     44     return reference_;
     45   }
     46 
     47   size_t GetPosition() const {
     48     return position_;
     49   }
     50 
     51   // Returns true if reference_ is the only name that can refer to its value during
     52   // the lifetime of the method. So it's guaranteed to not have any alias in
     53   // the method (including its callees).
     54   bool IsSingleton() const {
     55     return is_singleton_;
     56   }
     57 
     58   // Returns true if reference_ is a singleton and not returned to the caller or
     59   // used as an environment local of an HDeoptimize instruction.
     60   // The allocation and stores into reference_ may be eliminated for such cases.
     61   bool IsSingletonAndRemovable() const {
     62     return is_singleton_and_not_returned_ && is_singleton_and_not_deopt_visible_;
     63   }
     64 
     65   // Returns true if reference_ is a singleton and returned to the caller or
     66   // used as an environment local of an HDeoptimize instruction.
     67   bool IsSingletonAndNonRemovable() const {
     68     return is_singleton_ &&
     69            (!is_singleton_and_not_returned_ || !is_singleton_and_not_deopt_visible_);
     70   }
     71 
     72  private:
     73   HInstruction* const reference_;
     74   const size_t position_;  // position in HeapLocationCollector's ref_info_array_.
     75 
     76   // Can only be referred to by a single name in the method.
     77   bool is_singleton_;
     78   // Is singleton and not returned to caller.
     79   bool is_singleton_and_not_returned_;
     80   // Is singleton and not used as an environment local of HDeoptimize.
     81   bool is_singleton_and_not_deopt_visible_;
     82 
     83   DISALLOW_COPY_AND_ASSIGN(ReferenceInfo);
     84 };
     85 
     86 // A heap location is a reference-offset/index pair that a value can be loaded from
     87 // or stored to.
     88 class HeapLocation : public ArenaObject<kArenaAllocLSA> {
     89  public:
     90   static constexpr size_t kInvalidFieldOffset = -1;
     91   // Default value for heap locations which are not vector data.
     92   static constexpr size_t kScalar = 1;
     93   // TODO: more fine-grained array types.
     94   static constexpr int16_t kDeclaringClassDefIndexForArrays = -1;
     95 
     96   HeapLocation(ReferenceInfo* ref_info,
     97                size_t offset,
     98                HInstruction* index,
     99                size_t vector_length,
    100                int16_t declaring_class_def_index)
    101       : ref_info_(ref_info),
    102         offset_(offset),
    103         index_(index),
    104         vector_length_(vector_length),
    105         declaring_class_def_index_(declaring_class_def_index),
    106         value_killed_by_loop_side_effects_(true),
    107         has_aliased_locations_(false) {
    108     DCHECK(ref_info != nullptr);
    109     DCHECK((offset == kInvalidFieldOffset && index != nullptr) ||
    110            (offset != kInvalidFieldOffset && index == nullptr));
    111     if (ref_info->IsSingleton() && !IsArray()) {
    112       // Assume this location's value cannot be killed by loop side effects
    113       // until proven otherwise.
    114       value_killed_by_loop_side_effects_ = false;
    115     }
    116   }
    117 
    118   ReferenceInfo* GetReferenceInfo() const { return ref_info_; }
    119   size_t GetOffset() const { return offset_; }
    120   HInstruction* GetIndex() const { return index_; }
    121   size_t GetVectorLength() const { return vector_length_; }
    122 
    123   // Returns the definition of declaring class' dex index.
    124   // It's kDeclaringClassDefIndexForArrays for an array element.
    125   int16_t GetDeclaringClassDefIndex() const {
    126     return declaring_class_def_index_;
    127   }
    128 
    129   bool IsArray() const {
    130     return index_ != nullptr;
    131   }
    132 
    133   bool IsValueKilledByLoopSideEffects() const {
    134     return value_killed_by_loop_side_effects_;
    135   }
    136 
    137   void SetValueKilledByLoopSideEffects(bool val) {
    138     value_killed_by_loop_side_effects_ = val;
    139   }
    140 
    141   bool HasAliasedLocations() const {
    142     return has_aliased_locations_;
    143   }
    144 
    145   void SetHasAliasedLocations(bool val) {
    146     has_aliased_locations_ = val;
    147   }
    148 
    149  private:
    150   // Reference for instance/static field, array element or vector data.
    151   ReferenceInfo* const ref_info_;
    152   // Offset of static/instance field.
    153   // Invalid when this HeapLocation is not field.
    154   const size_t offset_;
    155   // Index of an array element or starting index of vector data.
    156   // Invalid when this HeapLocation is not array.
    157   HInstruction* const index_;
    158   // Vector length of vector data.
    159   // When this HeapLocation is not vector data, it's value is kScalar.
    160   const size_t vector_length_;
    161   // Declaring class's def's dex index.
    162   // Invalid when this HeapLocation is not field access.
    163   const int16_t declaring_class_def_index_;
    164 
    165   // Value of this location may be killed by loop side effects
    166   // because this location is stored into inside a loop.
    167   // This gives better info on whether a singleton's location
    168   // value may be killed by loop side effects.
    169   bool value_killed_by_loop_side_effects_;
    170 
    171   // Has aliased heap locations in the method, due to either the
    172   // reference is aliased or the array element is aliased via different
    173   // index names.
    174   bool has_aliased_locations_;
    175 
    176   DISALLOW_COPY_AND_ASSIGN(HeapLocation);
    177 };
    178 
    179 // A HeapLocationCollector collects all relevant heap locations and keeps
    180 // an aliasing matrix for all locations.
    181 class HeapLocationCollector : public HGraphVisitor {
    182  public:
    183   static constexpr size_t kHeapLocationNotFound = -1;
    184   // Start with a single uint32_t word. That's enough bits for pair-wise
    185   // aliasing matrix of 8 heap locations.
    186   static constexpr uint32_t kInitialAliasingMatrixBitVectorSize = 32;
    187 
    188   explicit HeapLocationCollector(HGraph* graph)
    189       : HGraphVisitor(graph),
    190         ref_info_array_(graph->GetAllocator()->Adapter(kArenaAllocLSA)),
    191         heap_locations_(graph->GetAllocator()->Adapter(kArenaAllocLSA)),
    192         aliasing_matrix_(graph->GetAllocator(),
    193                          kInitialAliasingMatrixBitVectorSize,
    194                          true,
    195                          kArenaAllocLSA),
    196         has_heap_stores_(false),
    197         has_volatile_(false),
    198         has_monitor_operations_(false) {}
    199 
    200   void CleanUp() {
    201     heap_locations_.clear();
    202     ref_info_array_.clear();
    203   }
    204 
    205   size_t GetNumberOfHeapLocations() const {
    206     return heap_locations_.size();
    207   }
    208 
    209   HeapLocation* GetHeapLocation(size_t index) const {
    210     return heap_locations_[index];
    211   }
    212 
    213   HInstruction* HuntForOriginalReference(HInstruction* ref) const {
    214     // An original reference can be transformed by instructions like:
    215     //   i0 NewArray
    216     //   i1 HInstruction(i0)  <-- NullCheck, BoundType, IntermediateAddress.
    217     //   i2 ArrayGet(i1, index)
    218     DCHECK(ref != nullptr);
    219     while (ref->IsNullCheck() || ref->IsBoundType() || ref->IsIntermediateAddress()) {
    220       ref = ref->InputAt(0);
    221     }
    222     return ref;
    223   }
    224 
    225   ReferenceInfo* FindReferenceInfoOf(HInstruction* ref) const {
    226     for (size_t i = 0; i < ref_info_array_.size(); i++) {
    227       ReferenceInfo* ref_info = ref_info_array_[i];
    228       if (ref_info->GetReference() == ref) {
    229         DCHECK_EQ(i, ref_info->GetPosition());
    230         return ref_info;
    231       }
    232     }
    233     return nullptr;
    234   }
    235 
    236   size_t GetFieldHeapLocation(HInstruction* object, const FieldInfo* field) const {
    237     DCHECK(object != nullptr);
    238     DCHECK(field != nullptr);
    239     return FindHeapLocationIndex(FindReferenceInfoOf(HuntForOriginalReference(object)),
    240                                  field->GetFieldOffset().SizeValue(),
    241                                  nullptr,
    242                                  HeapLocation::kScalar,
    243                                  field->GetDeclaringClassDefIndex());
    244   }
    245 
    246   size_t GetArrayHeapLocation(HInstruction* array,
    247                               HInstruction* index,
    248                               size_t vector_length = HeapLocation::kScalar) const {
    249     DCHECK(array != nullptr);
    250     DCHECK(index != nullptr);
    251     DCHECK_GE(vector_length, HeapLocation::kScalar);
    252     return FindHeapLocationIndex(FindReferenceInfoOf(HuntForOriginalReference(array)),
    253                                  HeapLocation::kInvalidFieldOffset,
    254                                  index,
    255                                  vector_length,
    256                                  HeapLocation::kDeclaringClassDefIndexForArrays);
    257   }
    258 
    259   bool HasHeapStores() const {
    260     return has_heap_stores_;
    261   }
    262 
    263   bool HasVolatile() const {
    264     return has_volatile_;
    265   }
    266 
    267   bool HasMonitorOps() const {
    268     return has_monitor_operations_;
    269   }
    270 
    271   // Find and return the heap location index in heap_locations_.
    272   // NOTE: When heap locations are created, potentially aliasing/overlapping
    273   // accesses are given different indexes. This find function also
    274   // doesn't take aliasing/overlapping into account. For example,
    275   // this function returns three different indexes for:
    276   // - ref_info=array, index=i, vector_length=kScalar;
    277   // - ref_info=array, index=i, vector_length=2;
    278   // - ref_info=array, index=i, vector_length=4;
    279   // In later analysis, ComputeMayAlias() and MayAlias() compute and tell whether
    280   // these indexes alias.
    281   size_t FindHeapLocationIndex(ReferenceInfo* ref_info,
    282                                size_t offset,
    283                                HInstruction* index,
    284                                size_t vector_length,
    285                                int16_t declaring_class_def_index) const {
    286     for (size_t i = 0; i < heap_locations_.size(); i++) {
    287       HeapLocation* loc = heap_locations_[i];
    288       if (loc->GetReferenceInfo() == ref_info &&
    289           loc->GetOffset() == offset &&
    290           loc->GetIndex() == index &&
    291           loc->GetVectorLength() == vector_length &&
    292           loc->GetDeclaringClassDefIndex() == declaring_class_def_index) {
    293         return i;
    294       }
    295     }
    296     return kHeapLocationNotFound;
    297   }
    298 
    299   // Returns true if heap_locations_[index1] and heap_locations_[index2] may alias.
    300   bool MayAlias(size_t index1, size_t index2) const {
    301     if (index1 < index2) {
    302       return aliasing_matrix_.IsBitSet(AliasingMatrixPosition(index1, index2));
    303     } else if (index1 > index2) {
    304       return aliasing_matrix_.IsBitSet(AliasingMatrixPosition(index2, index1));
    305     } else {
    306       DCHECK(false) << "index1 and index2 are expected to be different";
    307       return true;
    308     }
    309   }
    310 
    311   void BuildAliasingMatrix() {
    312     const size_t number_of_locations = heap_locations_.size();
    313     if (number_of_locations == 0) {
    314       return;
    315     }
    316     size_t pos = 0;
    317     // Compute aliasing info between every pair of different heap locations.
    318     // Save the result in a matrix represented as a BitVector.
    319     for (size_t i = 0; i < number_of_locations - 1; i++) {
    320       for (size_t j = i + 1; j < number_of_locations; j++) {
    321         if (ComputeMayAlias(i, j)) {
    322           aliasing_matrix_.SetBit(CheckedAliasingMatrixPosition(i, j, pos));
    323         }
    324         pos++;
    325       }
    326     }
    327   }
    328 
    329  private:
    330   // An allocation cannot alias with a name which already exists at the point
    331   // of the allocation, such as a parameter or a load happening before the allocation.
    332   bool MayAliasWithPreexistenceChecking(ReferenceInfo* ref_info1, ReferenceInfo* ref_info2) const {
    333     if (ref_info1->GetReference()->IsNewInstance() || ref_info1->GetReference()->IsNewArray()) {
    334       // Any reference that can alias with the allocation must appear after it in the block/in
    335       // the block's successors. In reverse post order, those instructions will be visited after
    336       // the allocation.
    337       return ref_info2->GetPosition() >= ref_info1->GetPosition();
    338     }
    339     return true;
    340   }
    341 
    342   bool CanReferencesAlias(ReferenceInfo* ref_info1, ReferenceInfo* ref_info2) const {
    343     if (ref_info1 == ref_info2) {
    344       return true;
    345     } else if (ref_info1->IsSingleton()) {
    346       return false;
    347     } else if (ref_info2->IsSingleton()) {
    348       return false;
    349     } else if (!MayAliasWithPreexistenceChecking(ref_info1, ref_info2) ||
    350         !MayAliasWithPreexistenceChecking(ref_info2, ref_info1)) {
    351       return false;
    352     }
    353     return true;
    354   }
    355 
    356   bool CanArrayElementsAlias(const HInstruction* idx1,
    357                              const size_t vector_length1,
    358                              const HInstruction* idx2,
    359                              const size_t vector_length2) const;
    360 
    361   // `index1` and `index2` are indices in the array of collected heap locations.
    362   // Returns the position in the bit vector that tracks whether the two heap
    363   // locations may alias.
    364   size_t AliasingMatrixPosition(size_t index1, size_t index2) const {
    365     DCHECK(index2 > index1);
    366     const size_t number_of_locations = heap_locations_.size();
    367     // It's (num_of_locations - 1) + ... + (num_of_locations - index1) + (index2 - index1 - 1).
    368     return (number_of_locations * index1 - (1 + index1) * index1 / 2 + (index2 - index1 - 1));
    369   }
    370 
    371   // An additional position is passed in to make sure the calculated position is correct.
    372   size_t CheckedAliasingMatrixPosition(size_t index1, size_t index2, size_t position) {
    373     size_t calculated_position = AliasingMatrixPosition(index1, index2);
    374     DCHECK_EQ(calculated_position, position);
    375     return calculated_position;
    376   }
    377 
    378   // Compute if two locations may alias to each other.
    379   bool ComputeMayAlias(size_t index1, size_t index2) const {
    380     DCHECK_NE(index1, index2);
    381     HeapLocation* loc1 = heap_locations_[index1];
    382     HeapLocation* loc2 = heap_locations_[index2];
    383     if (loc1->GetOffset() != loc2->GetOffset()) {
    384       // Either two different instance fields, or one is an instance
    385       // field and the other is an array data.
    386       return false;
    387     }
    388     if (loc1->GetDeclaringClassDefIndex() != loc2->GetDeclaringClassDefIndex()) {
    389       // Different types.
    390       return false;
    391     }
    392     if (!CanReferencesAlias(loc1->GetReferenceInfo(), loc2->GetReferenceInfo())) {
    393       return false;
    394     }
    395     if (loc1->IsArray() && loc2->IsArray()) {
    396       HInstruction* idx1 = loc1->GetIndex();
    397       HInstruction* idx2 = loc2->GetIndex();
    398       size_t vector_length1 = loc1->GetVectorLength();
    399       size_t vector_length2 = loc2->GetVectorLength();
    400       if (!CanArrayElementsAlias(idx1, vector_length1, idx2, vector_length2)) {
    401         return false;
    402       }
    403     }
    404     loc1->SetHasAliasedLocations(true);
    405     loc2->SetHasAliasedLocations(true);
    406     return true;
    407   }
    408 
    409   ReferenceInfo* GetOrCreateReferenceInfo(HInstruction* instruction) {
    410     ReferenceInfo* ref_info = FindReferenceInfoOf(instruction);
    411     if (ref_info == nullptr) {
    412       size_t pos = ref_info_array_.size();
    413       ref_info = new (GetGraph()->GetAllocator()) ReferenceInfo(instruction, pos);
    414       ref_info_array_.push_back(ref_info);
    415     }
    416     return ref_info;
    417   }
    418 
    419   void CreateReferenceInfoForReferenceType(HInstruction* instruction) {
    420     if (instruction->GetType() != DataType::Type::kReference) {
    421       return;
    422     }
    423     DCHECK(FindReferenceInfoOf(instruction) == nullptr);
    424     GetOrCreateReferenceInfo(instruction);
    425   }
    426 
    427   HeapLocation* GetOrCreateHeapLocation(HInstruction* ref,
    428                                         size_t offset,
    429                                         HInstruction* index,
    430                                         size_t vector_length,
    431                                         int16_t declaring_class_def_index) {
    432     HInstruction* original_ref = HuntForOriginalReference(ref);
    433     ReferenceInfo* ref_info = GetOrCreateReferenceInfo(original_ref);
    434     size_t heap_location_idx = FindHeapLocationIndex(
    435         ref_info, offset, index, vector_length, declaring_class_def_index);
    436     if (heap_location_idx == kHeapLocationNotFound) {
    437       HeapLocation* heap_loc = new (GetGraph()->GetAllocator())
    438           HeapLocation(ref_info, offset, index, vector_length, declaring_class_def_index);
    439       heap_locations_.push_back(heap_loc);
    440       return heap_loc;
    441     }
    442     return heap_locations_[heap_location_idx];
    443   }
    444 
    445   HeapLocation* VisitFieldAccess(HInstruction* ref, const FieldInfo& field_info) {
    446     if (field_info.IsVolatile()) {
    447       has_volatile_ = true;
    448     }
    449     const uint16_t declaring_class_def_index = field_info.GetDeclaringClassDefIndex();
    450     const size_t offset = field_info.GetFieldOffset().SizeValue();
    451     return GetOrCreateHeapLocation(ref,
    452                                    offset,
    453                                    nullptr,
    454                                    HeapLocation::kScalar,
    455                                    declaring_class_def_index);
    456   }
    457 
    458   void VisitArrayAccess(HInstruction* array, HInstruction* index, size_t vector_length) {
    459     GetOrCreateHeapLocation(array,
    460                             HeapLocation::kInvalidFieldOffset,
    461                             index,
    462                             vector_length,
    463                             HeapLocation::kDeclaringClassDefIndexForArrays);
    464   }
    465 
    466   void VisitInstanceFieldGet(HInstanceFieldGet* instruction) OVERRIDE {
    467     VisitFieldAccess(instruction->InputAt(0), instruction->GetFieldInfo());
    468     CreateReferenceInfoForReferenceType(instruction);
    469   }
    470 
    471   void VisitInstanceFieldSet(HInstanceFieldSet* instruction) OVERRIDE {
    472     HeapLocation* location = VisitFieldAccess(instruction->InputAt(0), instruction->GetFieldInfo());
    473     has_heap_stores_ = true;
    474     if (location->GetReferenceInfo()->IsSingleton()) {
    475       // A singleton's location value may be killed by loop side effects if it's
    476       // defined before that loop, and it's stored into inside that loop.
    477       HLoopInformation* loop_info = instruction->GetBlock()->GetLoopInformation();
    478       if (loop_info != nullptr) {
    479         HInstruction* ref = location->GetReferenceInfo()->GetReference();
    480         DCHECK(ref->IsNewInstance());
    481         if (loop_info->IsDefinedOutOfTheLoop(ref)) {
    482           // ref's location value may be killed by this loop's side effects.
    483           location->SetValueKilledByLoopSideEffects(true);
    484         } else {
    485           // ref is defined inside this loop so this loop's side effects cannot
    486           // kill its location value at the loop header since ref/its location doesn't
    487           // exist yet at the loop header.
    488         }
    489       }
    490     } else {
    491       // For non-singletons, value_killed_by_loop_side_effects_ is inited to
    492       // true.
    493       DCHECK_EQ(location->IsValueKilledByLoopSideEffects(), true);
    494     }
    495   }
    496 
    497   void VisitStaticFieldGet(HStaticFieldGet* instruction) OVERRIDE {
    498     VisitFieldAccess(instruction->InputAt(0), instruction->GetFieldInfo());
    499     CreateReferenceInfoForReferenceType(instruction);
    500   }
    501 
    502   void VisitStaticFieldSet(HStaticFieldSet* instruction) OVERRIDE {
    503     VisitFieldAccess(instruction->InputAt(0), instruction->GetFieldInfo());
    504     has_heap_stores_ = true;
    505   }
    506 
    507   // We intentionally don't collect HUnresolvedInstanceField/HUnresolvedStaticField accesses
    508   // since we cannot accurately track the fields.
    509 
    510   void VisitArrayGet(HArrayGet* instruction) OVERRIDE {
    511     HInstruction* array = instruction->InputAt(0);
    512     HInstruction* index = instruction->InputAt(1);
    513     VisitArrayAccess(array, index, HeapLocation::kScalar);
    514     CreateReferenceInfoForReferenceType(instruction);
    515   }
    516 
    517   void VisitArraySet(HArraySet* instruction) OVERRIDE {
    518     HInstruction* array = instruction->InputAt(0);
    519     HInstruction* index = instruction->InputAt(1);
    520     VisitArrayAccess(array, index, HeapLocation::kScalar);
    521     has_heap_stores_ = true;
    522   }
    523 
    524   void VisitVecLoad(HVecLoad* instruction) OVERRIDE {
    525     HInstruction* array = instruction->InputAt(0);
    526     HInstruction* index = instruction->InputAt(1);
    527     VisitArrayAccess(array, index, instruction->GetVectorLength());
    528     CreateReferenceInfoForReferenceType(instruction);
    529   }
    530 
    531   void VisitVecStore(HVecStore* instruction) OVERRIDE {
    532     HInstruction* array = instruction->InputAt(0);
    533     HInstruction* index = instruction->InputAt(1);
    534     VisitArrayAccess(array, index, instruction->GetVectorLength());
    535     has_heap_stores_ = true;
    536   }
    537 
    538   void VisitInstruction(HInstruction* instruction) OVERRIDE {
    539     // Any new-instance or new-array cannot alias with references that
    540     // pre-exist the new-instance/new-array. We append entries into
    541     // ref_info_array_ which keeps track of the order of creation
    542     // of reference values since we visit the blocks in reverse post order.
    543     //
    544     // By default, VisitXXX() (including VisitPhi()) calls VisitInstruction(),
    545     // unless VisitXXX() is overridden. VisitInstanceFieldGet() etc. above
    546     // also call CreateReferenceInfoForReferenceType() explicitly.
    547     CreateReferenceInfoForReferenceType(instruction);
    548   }
    549 
    550   void VisitMonitorOperation(HMonitorOperation* monitor ATTRIBUTE_UNUSED) OVERRIDE {
    551     has_monitor_operations_ = true;
    552   }
    553 
    554   ArenaVector<ReferenceInfo*> ref_info_array_;   // All references used for heap accesses.
    555   ArenaVector<HeapLocation*> heap_locations_;    // All heap locations.
    556   ArenaBitVector aliasing_matrix_;    // aliasing info between each pair of locations.
    557   bool has_heap_stores_;    // If there is no heap stores, LSE acts as GVN with better
    558                             // alias analysis and won't be as effective.
    559   bool has_volatile_;       // If there are volatile field accesses.
    560   bool has_monitor_operations_;    // If there are monitor operations.
    561 
    562   DISALLOW_COPY_AND_ASSIGN(HeapLocationCollector);
    563 };
    564 
    565 class LoadStoreAnalysis : public HOptimization {
    566  public:
    567   explicit LoadStoreAnalysis(HGraph* graph, const char* name = kLoadStoreAnalysisPassName)
    568     : HOptimization(graph, name),
    569       heap_location_collector_(graph) {}
    570 
    571   const HeapLocationCollector& GetHeapLocationCollector() const {
    572     return heap_location_collector_;
    573   }
    574 
    575   void Run() OVERRIDE;
    576 
    577   static constexpr const char* kLoadStoreAnalysisPassName = "load_store_analysis";
    578 
    579  private:
    580   HeapLocationCollector heap_location_collector_;
    581 
    582   DISALLOW_COPY_AND_ASSIGN(LoadStoreAnalysis);
    583 };
    584 
    585 }  // namespace art
    586 
    587 #endif  // ART_COMPILER_OPTIMIZING_LOAD_STORE_ANALYSIS_H_
    588