Home | History | Annotate | Download | only in optimizing
      1 /*
      2  * Copyright (C) 2014 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_LOCATIONS_H_
     18 #define ART_COMPILER_OPTIMIZING_LOCATIONS_H_
     19 
     20 #include "base/arena_containers.h"
     21 #include "base/arena_object.h"
     22 #include "base/bit_field.h"
     23 #include "base/bit_utils.h"
     24 #include "base/bit_vector.h"
     25 #include "base/value_object.h"
     26 
     27 namespace art {
     28 
     29 class HConstant;
     30 class HInstruction;
     31 class Location;
     32 
     33 std::ostream& operator<<(std::ostream& os, const Location& location);
     34 
     35 /**
     36  * A Location is an abstraction over the potential location
     37  * of an instruction. It could be in register or stack.
     38  */
     39 class Location : public ValueObject {
     40  public:
     41   enum OutputOverlap {
     42     // The liveness of the output overlaps the liveness of one or
     43     // several input(s); the register allocator cannot reuse an
     44     // input's location for the output's location.
     45     kOutputOverlap,
     46     // The liveness of the output does not overlap the liveness of any
     47     // input; the register allocator is allowed to reuse an input's
     48     // location for the output's location.
     49     kNoOutputOverlap
     50   };
     51 
     52   enum Kind {
     53     kInvalid = 0,
     54     kConstant = 1,
     55     kStackSlot = 2,  // 32bit stack slot.
     56     kDoubleStackSlot = 3,  // 64bit stack slot.
     57 
     58     kRegister = 4,  // Core register.
     59 
     60     // We do not use the value 5 because it conflicts with kLocationConstantMask.
     61     kDoNotUse5 = 5,
     62 
     63     kFpuRegister = 6,  // Float register.
     64 
     65     kRegisterPair = 7,  // Long register.
     66 
     67     kFpuRegisterPair = 8,  // Double register.
     68 
     69     // We do not use the value 9 because it conflicts with kLocationConstantMask.
     70     kDoNotUse9 = 9,
     71 
     72     kSIMDStackSlot = 10,  // 128bit stack slot. TODO: generalize with encoded #bytes?
     73 
     74     // Unallocated location represents a location that is not fixed and can be
     75     // allocated by a register allocator.  Each unallocated location has
     76     // a policy that specifies what kind of location is suitable. Payload
     77     // contains register allocation policy.
     78     kUnallocated = 11,
     79   };
     80 
     81   Location() : ValueObject(), value_(kInvalid) {
     82     // Verify that non-constant location kinds do not interfere with kConstant.
     83     static_assert((kInvalid & kLocationConstantMask) != kConstant, "TagError");
     84     static_assert((kUnallocated & kLocationConstantMask) != kConstant, "TagError");
     85     static_assert((kStackSlot & kLocationConstantMask) != kConstant, "TagError");
     86     static_assert((kDoubleStackSlot & kLocationConstantMask) != kConstant, "TagError");
     87     static_assert((kSIMDStackSlot & kLocationConstantMask) != kConstant, "TagError");
     88     static_assert((kRegister & kLocationConstantMask) != kConstant, "TagError");
     89     static_assert((kFpuRegister & kLocationConstantMask) != kConstant, "TagError");
     90     static_assert((kRegisterPair & kLocationConstantMask) != kConstant, "TagError");
     91     static_assert((kFpuRegisterPair & kLocationConstantMask) != kConstant, "TagError");
     92     static_assert((kConstant & kLocationConstantMask) == kConstant, "TagError");
     93 
     94     DCHECK(!IsValid());
     95   }
     96 
     97   Location(const Location& other) = default;
     98 
     99   Location& operator=(const Location& other) = default;
    100 
    101   bool IsConstant() const {
    102     return (value_ & kLocationConstantMask) == kConstant;
    103   }
    104 
    105   static Location ConstantLocation(HConstant* constant) {
    106     DCHECK(constant != nullptr);
    107     return Location(kConstant | reinterpret_cast<uintptr_t>(constant));
    108   }
    109 
    110   HConstant* GetConstant() const {
    111     DCHECK(IsConstant());
    112     return reinterpret_cast<HConstant*>(value_ & ~kLocationConstantMask);
    113   }
    114 
    115   bool IsValid() const {
    116     return value_ != kInvalid;
    117   }
    118 
    119   bool IsInvalid() const {
    120     return !IsValid();
    121   }
    122 
    123   // Empty location. Used if there the location should be ignored.
    124   static Location NoLocation() {
    125     return Location();
    126   }
    127 
    128   // Register locations.
    129   static Location RegisterLocation(int reg) {
    130     return Location(kRegister, reg);
    131   }
    132 
    133   static Location FpuRegisterLocation(int reg) {
    134     return Location(kFpuRegister, reg);
    135   }
    136 
    137   static Location RegisterPairLocation(int low, int high) {
    138     return Location(kRegisterPair, low << 16 | high);
    139   }
    140 
    141   static Location FpuRegisterPairLocation(int low, int high) {
    142     return Location(kFpuRegisterPair, low << 16 | high);
    143   }
    144 
    145   bool IsRegister() const {
    146     return GetKind() == kRegister;
    147   }
    148 
    149   bool IsFpuRegister() const {
    150     return GetKind() == kFpuRegister;
    151   }
    152 
    153   bool IsRegisterPair() const {
    154     return GetKind() == kRegisterPair;
    155   }
    156 
    157   bool IsFpuRegisterPair() const {
    158     return GetKind() == kFpuRegisterPair;
    159   }
    160 
    161   bool IsRegisterKind() const {
    162     return IsRegister() || IsFpuRegister() || IsRegisterPair() || IsFpuRegisterPair();
    163   }
    164 
    165   int reg() const {
    166     DCHECK(IsRegister() || IsFpuRegister());
    167     return GetPayload();
    168   }
    169 
    170   int low() const {
    171     DCHECK(IsPair());
    172     return GetPayload() >> 16;
    173   }
    174 
    175   int high() const {
    176     DCHECK(IsPair());
    177     return GetPayload() & 0xFFFF;
    178   }
    179 
    180   template <typename T>
    181   T AsRegister() const {
    182     DCHECK(IsRegister());
    183     return static_cast<T>(reg());
    184   }
    185 
    186   template <typename T>
    187   T AsFpuRegister() const {
    188     DCHECK(IsFpuRegister());
    189     return static_cast<T>(reg());
    190   }
    191 
    192   template <typename T>
    193   T AsRegisterPairLow() const {
    194     DCHECK(IsRegisterPair());
    195     return static_cast<T>(low());
    196   }
    197 
    198   template <typename T>
    199   T AsRegisterPairHigh() const {
    200     DCHECK(IsRegisterPair());
    201     return static_cast<T>(high());
    202   }
    203 
    204   template <typename T>
    205   T AsFpuRegisterPairLow() const {
    206     DCHECK(IsFpuRegisterPair());
    207     return static_cast<T>(low());
    208   }
    209 
    210   template <typename T>
    211   T AsFpuRegisterPairHigh() const {
    212     DCHECK(IsFpuRegisterPair());
    213     return static_cast<T>(high());
    214   }
    215 
    216   bool IsPair() const {
    217     return IsRegisterPair() || IsFpuRegisterPair();
    218   }
    219 
    220   Location ToLow() const {
    221     if (IsRegisterPair()) {
    222       return Location::RegisterLocation(low());
    223     } else if (IsFpuRegisterPair()) {
    224       return Location::FpuRegisterLocation(low());
    225     } else {
    226       DCHECK(IsDoubleStackSlot());
    227       return Location::StackSlot(GetStackIndex());
    228     }
    229   }
    230 
    231   Location ToHigh() const {
    232     if (IsRegisterPair()) {
    233       return Location::RegisterLocation(high());
    234     } else if (IsFpuRegisterPair()) {
    235       return Location::FpuRegisterLocation(high());
    236     } else {
    237       DCHECK(IsDoubleStackSlot());
    238       return Location::StackSlot(GetHighStackIndex(4));
    239     }
    240   }
    241 
    242   static uintptr_t EncodeStackIndex(intptr_t stack_index) {
    243     DCHECK(-kStackIndexBias <= stack_index);
    244     DCHECK(stack_index < kStackIndexBias);
    245     return static_cast<uintptr_t>(kStackIndexBias + stack_index);
    246   }
    247 
    248   static Location StackSlot(intptr_t stack_index) {
    249     uintptr_t payload = EncodeStackIndex(stack_index);
    250     Location loc(kStackSlot, payload);
    251     // Ensure that sign is preserved.
    252     DCHECK_EQ(loc.GetStackIndex(), stack_index);
    253     return loc;
    254   }
    255 
    256   bool IsStackSlot() const {
    257     return GetKind() == kStackSlot;
    258   }
    259 
    260   static Location DoubleStackSlot(intptr_t stack_index) {
    261     uintptr_t payload = EncodeStackIndex(stack_index);
    262     Location loc(kDoubleStackSlot, payload);
    263     // Ensure that sign is preserved.
    264     DCHECK_EQ(loc.GetStackIndex(), stack_index);
    265     return loc;
    266   }
    267 
    268   bool IsDoubleStackSlot() const {
    269     return GetKind() == kDoubleStackSlot;
    270   }
    271 
    272   static Location SIMDStackSlot(intptr_t stack_index) {
    273     uintptr_t payload = EncodeStackIndex(stack_index);
    274     Location loc(kSIMDStackSlot, payload);
    275     // Ensure that sign is preserved.
    276     DCHECK_EQ(loc.GetStackIndex(), stack_index);
    277     return loc;
    278   }
    279 
    280   bool IsSIMDStackSlot() const {
    281     return GetKind() == kSIMDStackSlot;
    282   }
    283 
    284   intptr_t GetStackIndex() const {
    285     DCHECK(IsStackSlot() || IsDoubleStackSlot() || IsSIMDStackSlot());
    286     // Decode stack index manually to preserve sign.
    287     return GetPayload() - kStackIndexBias;
    288   }
    289 
    290   intptr_t GetHighStackIndex(uintptr_t word_size) const {
    291     DCHECK(IsDoubleStackSlot());
    292     // Decode stack index manually to preserve sign.
    293     return GetPayload() - kStackIndexBias + word_size;
    294   }
    295 
    296   Kind GetKind() const {
    297     return IsConstant() ? kConstant : KindField::Decode(value_);
    298   }
    299 
    300   bool Equals(Location other) const {
    301     return value_ == other.value_;
    302   }
    303 
    304   bool Contains(Location other) const {
    305     if (Equals(other)) {
    306       return true;
    307     } else if (IsPair() || IsDoubleStackSlot()) {
    308       return ToLow().Equals(other) || ToHigh().Equals(other);
    309     }
    310     return false;
    311   }
    312 
    313   bool OverlapsWith(Location other) const {
    314     // Only check the overlapping case that can happen with our register allocation algorithm.
    315     bool overlap = Contains(other) || other.Contains(*this);
    316     if (kIsDebugBuild && !overlap) {
    317       // Note: These are also overlapping cases. But we are not able to handle them in
    318       // ParallelMoveResolverWithSwap. Make sure that we do not meet such case with our compiler.
    319       if ((IsPair() && other.IsPair()) || (IsDoubleStackSlot() && other.IsDoubleStackSlot())) {
    320         DCHECK(!Contains(other.ToLow()));
    321         DCHECK(!Contains(other.ToHigh()));
    322       }
    323     }
    324     return overlap;
    325   }
    326 
    327   const char* DebugString() const {
    328     switch (GetKind()) {
    329       case kInvalid: return "I";
    330       case kRegister: return "R";
    331       case kStackSlot: return "S";
    332       case kDoubleStackSlot: return "DS";
    333       case kSIMDStackSlot: return "SIMD";
    334       case kUnallocated: return "U";
    335       case kConstant: return "C";
    336       case kFpuRegister: return "F";
    337       case kRegisterPair: return "RP";
    338       case kFpuRegisterPair: return "FP";
    339       case kDoNotUse5:  // fall-through
    340       case kDoNotUse9:
    341         LOG(FATAL) << "Should not use this location kind";
    342     }
    343     UNREACHABLE();
    344   }
    345 
    346   // Unallocated locations.
    347   enum Policy {
    348     kAny,
    349     kRequiresRegister,
    350     kRequiresFpuRegister,
    351     kSameAsFirstInput,
    352   };
    353 
    354   bool IsUnallocated() const {
    355     return GetKind() == kUnallocated;
    356   }
    357 
    358   static Location UnallocatedLocation(Policy policy) {
    359     return Location(kUnallocated, PolicyField::Encode(policy));
    360   }
    361 
    362   // Any free register is suitable to replace this unallocated location.
    363   static Location Any() {
    364     return UnallocatedLocation(kAny);
    365   }
    366 
    367   static Location RequiresRegister() {
    368     return UnallocatedLocation(kRequiresRegister);
    369   }
    370 
    371   static Location RequiresFpuRegister() {
    372     return UnallocatedLocation(kRequiresFpuRegister);
    373   }
    374 
    375   static Location RegisterOrConstant(HInstruction* instruction);
    376   static Location RegisterOrInt32Constant(HInstruction* instruction);
    377   static Location ByteRegisterOrConstant(int reg, HInstruction* instruction);
    378   static Location FpuRegisterOrConstant(HInstruction* instruction);
    379   static Location FpuRegisterOrInt32Constant(HInstruction* instruction);
    380 
    381   // The location of the first input to the instruction will be
    382   // used to replace this unallocated location.
    383   static Location SameAsFirstInput() {
    384     return UnallocatedLocation(kSameAsFirstInput);
    385   }
    386 
    387   Policy GetPolicy() const {
    388     DCHECK(IsUnallocated());
    389     return PolicyField::Decode(GetPayload());
    390   }
    391 
    392   bool RequiresRegisterKind() const {
    393     return GetPolicy() == kRequiresRegister || GetPolicy() == kRequiresFpuRegister;
    394   }
    395 
    396   uintptr_t GetEncoding() const {
    397     return GetPayload();
    398   }
    399 
    400  private:
    401   // Number of bits required to encode Kind value.
    402   static constexpr uint32_t kBitsForKind = 4;
    403   static constexpr uint32_t kBitsForPayload = kBitsPerIntPtrT - kBitsForKind;
    404   static constexpr uintptr_t kLocationConstantMask = 0x3;
    405 
    406   explicit Location(uintptr_t value) : value_(value) {}
    407 
    408   Location(Kind kind, uintptr_t payload)
    409       : value_(KindField::Encode(kind) | PayloadField::Encode(payload)) {}
    410 
    411   uintptr_t GetPayload() const {
    412     return PayloadField::Decode(value_);
    413   }
    414 
    415   typedef BitField<Kind, 0, kBitsForKind> KindField;
    416   typedef BitField<uintptr_t, kBitsForKind, kBitsForPayload> PayloadField;
    417 
    418   // Layout for kUnallocated locations payload.
    419   typedef BitField<Policy, 0, 3> PolicyField;
    420 
    421   // Layout for stack slots.
    422   static const intptr_t kStackIndexBias =
    423       static_cast<intptr_t>(1) << (kBitsForPayload - 1);
    424 
    425   // Location either contains kind and payload fields or a tagged handle for
    426   // a constant locations. Values of enumeration Kind are selected in such a
    427   // way that none of them can be interpreted as a kConstant tag.
    428   uintptr_t value_;
    429 };
    430 std::ostream& operator<<(std::ostream& os, const Location::Kind& rhs);
    431 std::ostream& operator<<(std::ostream& os, const Location::Policy& rhs);
    432 
    433 class RegisterSet : public ValueObject {
    434  public:
    435   static RegisterSet Empty() { return RegisterSet(); }
    436   static RegisterSet AllFpu() { return RegisterSet(0, -1); }
    437 
    438   void Add(Location loc) {
    439     if (loc.IsRegister()) {
    440       core_registers_ |= (1 << loc.reg());
    441     } else {
    442       DCHECK(loc.IsFpuRegister());
    443       floating_point_registers_ |= (1 << loc.reg());
    444     }
    445   }
    446 
    447   void Remove(Location loc) {
    448     if (loc.IsRegister()) {
    449       core_registers_ &= ~(1 << loc.reg());
    450     } else {
    451       DCHECK(loc.IsFpuRegister()) << loc;
    452       floating_point_registers_ &= ~(1 << loc.reg());
    453     }
    454   }
    455 
    456   bool ContainsCoreRegister(uint32_t id) const {
    457     return Contains(core_registers_, id);
    458   }
    459 
    460   bool ContainsFloatingPointRegister(uint32_t id) const {
    461     return Contains(floating_point_registers_, id);
    462   }
    463 
    464   static bool Contains(uint32_t register_set, uint32_t reg) {
    465     return (register_set & (1 << reg)) != 0;
    466   }
    467 
    468   size_t GetNumberOfRegisters() const {
    469     return POPCOUNT(core_registers_) + POPCOUNT(floating_point_registers_);
    470   }
    471 
    472   uint32_t GetCoreRegisters() const {
    473     return core_registers_;
    474   }
    475 
    476   uint32_t GetFloatingPointRegisters() const {
    477     return floating_point_registers_;
    478   }
    479 
    480  private:
    481   RegisterSet() : core_registers_(0), floating_point_registers_(0) {}
    482   RegisterSet(uint32_t core, uint32_t fp) : core_registers_(core), floating_point_registers_(fp) {}
    483 
    484   uint32_t core_registers_;
    485   uint32_t floating_point_registers_;
    486 };
    487 
    488 static constexpr bool kIntrinsified = true;
    489 
    490 /**
    491  * The code generator computes LocationSummary for each instruction so that
    492  * the instruction itself knows what code to generate: where to find the inputs
    493  * and where to place the result.
    494  *
    495  * The intent is to have the code for generating the instruction independent of
    496  * register allocation. A register allocator just has to provide a LocationSummary.
    497  */
    498 class LocationSummary : public ArenaObject<kArenaAllocLocationSummary> {
    499  public:
    500   enum CallKind {
    501     kNoCall,
    502     kCallOnMainAndSlowPath,
    503     kCallOnSlowPath,
    504     kCallOnMainOnly
    505   };
    506 
    507   explicit LocationSummary(HInstruction* instruction,
    508                            CallKind call_kind = kNoCall,
    509                            bool intrinsified = false);
    510 
    511   void SetInAt(uint32_t at, Location location) {
    512     inputs_[at] = location;
    513   }
    514 
    515   Location InAt(uint32_t at) const {
    516     return inputs_[at];
    517   }
    518 
    519   size_t GetInputCount() const {
    520     return inputs_.size();
    521   }
    522 
    523   // Set the output location.  Argument `overlaps` tells whether the
    524   // output overlaps any of the inputs (if so, it cannot share the
    525   // same register as one of the inputs); it is set to
    526   // `Location::kOutputOverlap` by default for safety.
    527   void SetOut(Location location, Location::OutputOverlap overlaps = Location::kOutputOverlap) {
    528     DCHECK(output_.IsInvalid());
    529     output_overlaps_ = overlaps;
    530     output_ = location;
    531   }
    532 
    533   void UpdateOut(Location location) {
    534     // There are two reasons for updating an output:
    535     // 1) Parameters, where we only know the exact stack slot after
    536     //    doing full register allocation.
    537     // 2) Unallocated location.
    538     DCHECK(output_.IsStackSlot() || output_.IsDoubleStackSlot() || output_.IsUnallocated());
    539     output_ = location;
    540   }
    541 
    542   void AddTemp(Location location) {
    543     temps_.push_back(location);
    544   }
    545 
    546   void AddRegisterTemps(size_t count) {
    547     for (size_t i = 0; i < count; ++i) {
    548       AddTemp(Location::RequiresRegister());
    549     }
    550   }
    551 
    552   Location GetTemp(uint32_t at) const {
    553     return temps_[at];
    554   }
    555 
    556   void SetTempAt(uint32_t at, Location location) {
    557     DCHECK(temps_[at].IsUnallocated() || temps_[at].IsInvalid());
    558     temps_[at] = location;
    559   }
    560 
    561   size_t GetTempCount() const {
    562     return temps_.size();
    563   }
    564 
    565   bool HasTemps() const { return !temps_.empty(); }
    566 
    567   Location Out() const { return output_; }
    568 
    569   bool CanCall() const {
    570     return call_kind_ != kNoCall;
    571   }
    572 
    573   bool WillCall() const {
    574     return call_kind_ == kCallOnMainOnly || call_kind_ == kCallOnMainAndSlowPath;
    575   }
    576 
    577   bool CallsOnSlowPath() const {
    578     return call_kind_ == kCallOnSlowPath || call_kind_ == kCallOnMainAndSlowPath;
    579   }
    580 
    581   bool OnlyCallsOnSlowPath() const {
    582     return call_kind_ == kCallOnSlowPath;
    583   }
    584 
    585   bool CallsOnMainAndSlowPath() const {
    586     return call_kind_ == kCallOnMainAndSlowPath;
    587   }
    588 
    589   bool NeedsSafepoint() const {
    590     return CanCall();
    591   }
    592 
    593   void SetCustomSlowPathCallerSaves(const RegisterSet& caller_saves) {
    594     DCHECK(OnlyCallsOnSlowPath());
    595     has_custom_slow_path_calling_convention_ = true;
    596     custom_slow_path_caller_saves_ = caller_saves;
    597   }
    598 
    599   bool HasCustomSlowPathCallingConvention() const {
    600     return has_custom_slow_path_calling_convention_;
    601   }
    602 
    603   const RegisterSet& GetCustomSlowPathCallerSaves() const {
    604     DCHECK(HasCustomSlowPathCallingConvention());
    605     return custom_slow_path_caller_saves_;
    606   }
    607 
    608   void SetStackBit(uint32_t index) {
    609     stack_mask_->SetBit(index);
    610   }
    611 
    612   void ClearStackBit(uint32_t index) {
    613     stack_mask_->ClearBit(index);
    614   }
    615 
    616   void SetRegisterBit(uint32_t reg_id) {
    617     register_mask_ |= (1 << reg_id);
    618   }
    619 
    620   uint32_t GetRegisterMask() const {
    621     return register_mask_;
    622   }
    623 
    624   bool RegisterContainsObject(uint32_t reg_id) {
    625     return RegisterSet::Contains(register_mask_, reg_id);
    626   }
    627 
    628   void AddLiveRegister(Location location) {
    629     live_registers_.Add(location);
    630   }
    631 
    632   BitVector* GetStackMask() const {
    633     return stack_mask_;
    634   }
    635 
    636   RegisterSet* GetLiveRegisters() {
    637     return &live_registers_;
    638   }
    639 
    640   size_t GetNumberOfLiveRegisters() const {
    641     return live_registers_.GetNumberOfRegisters();
    642   }
    643 
    644   bool OutputUsesSameAs(uint32_t input_index) const {
    645     return (input_index == 0)
    646         && output_.IsUnallocated()
    647         && (output_.GetPolicy() == Location::kSameAsFirstInput);
    648   }
    649 
    650   bool IsFixedInput(uint32_t input_index) const {
    651     Location input = inputs_[input_index];
    652     return input.IsRegister()
    653         || input.IsFpuRegister()
    654         || input.IsPair()
    655         || input.IsStackSlot()
    656         || input.IsDoubleStackSlot();
    657   }
    658 
    659   bool OutputCanOverlapWithInputs() const {
    660     return output_overlaps_ == Location::kOutputOverlap;
    661   }
    662 
    663   bool Intrinsified() const {
    664     return intrinsified_;
    665   }
    666 
    667  private:
    668   LocationSummary(HInstruction* instruction,
    669                   CallKind call_kind,
    670                   bool intrinsified,
    671                   ArenaAllocator* allocator);
    672 
    673   ArenaVector<Location> inputs_;
    674   ArenaVector<Location> temps_;
    675   const CallKind call_kind_;
    676   // Whether these are locations for an intrinsified call.
    677   const bool intrinsified_;
    678   // Whether the slow path has default or custom calling convention.
    679   bool has_custom_slow_path_calling_convention_;
    680   // Whether the output overlaps with any of the inputs. If it overlaps, then it cannot
    681   // share the same register as the inputs.
    682   Location::OutputOverlap output_overlaps_;
    683   Location output_;
    684 
    685   // Mask of objects that live in the stack.
    686   BitVector* stack_mask_;
    687 
    688   // Mask of objects that live in register.
    689   uint32_t register_mask_;
    690 
    691   // Registers that are in use at this position.
    692   RegisterSet live_registers_;
    693 
    694   // Custom slow path caller saves. Valid only if indicated by slow_path_calling_convention_.
    695   RegisterSet custom_slow_path_caller_saves_;
    696 
    697   friend class RegisterAllocatorTest;
    698   DISALLOW_COPY_AND_ASSIGN(LocationSummary);
    699 };
    700 
    701 }  // namespace art
    702 
    703 #endif  // ART_COMPILER_OPTIMIZING_LOCATIONS_H_
    704