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 #include "ssa_liveness_analysis.h"
     18 
     19 #include "base/bit_vector-inl.h"
     20 #include "code_generator.h"
     21 #include "nodes.h"
     22 
     23 namespace art {
     24 
     25 void SsaLivenessAnalysis::Analyze() {
     26   LinearizeGraph();
     27   NumberInstructions();
     28   ComputeLiveness();
     29 }
     30 
     31 static bool IsLoop(HLoopInformation* info) {
     32   return info != nullptr;
     33 }
     34 
     35 static bool InSameLoop(HLoopInformation* first_loop, HLoopInformation* second_loop) {
     36   return first_loop == second_loop;
     37 }
     38 
     39 static bool IsInnerLoop(HLoopInformation* outer, HLoopInformation* inner) {
     40   return (inner != outer)
     41       && (inner != nullptr)
     42       && (outer != nullptr)
     43       && inner->IsIn(*outer);
     44 }
     45 
     46 static void AddToListForLinearization(ArenaVector<HBasicBlock*>* worklist, HBasicBlock* block) {
     47   HLoopInformation* block_loop = block->GetLoopInformation();
     48   auto insert_pos = worklist->rbegin();  // insert_pos.base() will be the actual position.
     49   for (auto end = worklist->rend(); insert_pos != end; ++insert_pos) {
     50     HBasicBlock* current = *insert_pos;
     51     HLoopInformation* current_loop = current->GetLoopInformation();
     52     if (InSameLoop(block_loop, current_loop)
     53         || !IsLoop(current_loop)
     54         || IsInnerLoop(current_loop, block_loop)) {
     55       // The block can be processed immediately.
     56       break;
     57     }
     58   }
     59   worklist->insert(insert_pos.base(), block);
     60 }
     61 
     62 void SsaLivenessAnalysis::LinearizeGraph() {
     63   // Create a reverse post ordering with the following properties:
     64   // - Blocks in a loop are consecutive,
     65   // - Back-edge is the last block before loop exits.
     66 
     67   // (1): Record the number of forward predecessors for each block. This is to
     68   //      ensure the resulting order is reverse post order. We could use the
     69   //      current reverse post order in the graph, but it would require making
     70   //      order queries to a GrowableArray, which is not the best data structure
     71   //      for it.
     72   ArenaVector<uint32_t> forward_predecessors(graph_->GetBlocks().size(),
     73                                              graph_->GetArena()->Adapter(kArenaAllocSsaLiveness));
     74   for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
     75     HBasicBlock* block = it.Current();
     76     size_t number_of_forward_predecessors = block->GetPredecessors().size();
     77     if (block->IsLoopHeader()) {
     78       number_of_forward_predecessors -= block->GetLoopInformation()->NumberOfBackEdges();
     79     }
     80     forward_predecessors[block->GetBlockId()] = number_of_forward_predecessors;
     81   }
     82 
     83   // (2): Following a worklist approach, first start with the entry block, and
     84   //      iterate over the successors. When all non-back edge predecessors of a
     85   //      successor block are visited, the successor block is added in the worklist
     86   //      following an order that satisfies the requirements to build our linear graph.
     87   graph_->linear_order_.reserve(graph_->GetReversePostOrder().size());
     88   ArenaVector<HBasicBlock*> worklist(graph_->GetArena()->Adapter(kArenaAllocSsaLiveness));
     89   worklist.push_back(graph_->GetEntryBlock());
     90   do {
     91     HBasicBlock* current = worklist.back();
     92     worklist.pop_back();
     93     graph_->linear_order_.push_back(current);
     94     for (HBasicBlock* successor : current->GetSuccessors()) {
     95       int block_id = successor->GetBlockId();
     96       size_t number_of_remaining_predecessors = forward_predecessors[block_id];
     97       if (number_of_remaining_predecessors == 1) {
     98         AddToListForLinearization(&worklist, successor);
     99       }
    100       forward_predecessors[block_id] = number_of_remaining_predecessors - 1;
    101     }
    102   } while (!worklist.empty());
    103 }
    104 
    105 void SsaLivenessAnalysis::NumberInstructions() {
    106   int ssa_index = 0;
    107   size_t lifetime_position = 0;
    108   // Each instruction gets a lifetime position, and a block gets a lifetime
    109   // start and end position. Non-phi instructions have a distinct lifetime position than
    110   // the block they are in. Phi instructions have the lifetime start of their block as
    111   // lifetime position.
    112   //
    113   // Because the register allocator will insert moves in the graph, we need
    114   // to differentiate between the start and end of an instruction. Adding 2 to
    115   // the lifetime position for each instruction ensures the start of an
    116   // instruction is different than the end of the previous instruction.
    117   for (HLinearOrderIterator it(*graph_); !it.Done(); it.Advance()) {
    118     HBasicBlock* block = it.Current();
    119     block->SetLifetimeStart(lifetime_position);
    120 
    121     for (HInstructionIterator inst_it(block->GetPhis()); !inst_it.Done(); inst_it.Advance()) {
    122       HInstruction* current = inst_it.Current();
    123       codegen_->AllocateLocations(current);
    124       LocationSummary* locations = current->GetLocations();
    125       if (locations != nullptr && locations->Out().IsValid()) {
    126         instructions_from_ssa_index_.push_back(current);
    127         current->SetSsaIndex(ssa_index++);
    128         current->SetLiveInterval(
    129             LiveInterval::MakeInterval(graph_->GetArena(), current->GetType(), current));
    130       }
    131       current->SetLifetimePosition(lifetime_position);
    132     }
    133     lifetime_position += 2;
    134 
    135     // Add a null marker to notify we are starting a block.
    136     instructions_from_lifetime_position_.push_back(nullptr);
    137 
    138     for (HInstructionIterator inst_it(block->GetInstructions()); !inst_it.Done();
    139          inst_it.Advance()) {
    140       HInstruction* current = inst_it.Current();
    141       codegen_->AllocateLocations(current);
    142       LocationSummary* locations = current->GetLocations();
    143       if (locations != nullptr && locations->Out().IsValid()) {
    144         instructions_from_ssa_index_.push_back(current);
    145         current->SetSsaIndex(ssa_index++);
    146         current->SetLiveInterval(
    147             LiveInterval::MakeInterval(graph_->GetArena(), current->GetType(), current));
    148       }
    149       instructions_from_lifetime_position_.push_back(current);
    150       current->SetLifetimePosition(lifetime_position);
    151       lifetime_position += 2;
    152     }
    153 
    154     block->SetLifetimeEnd(lifetime_position);
    155   }
    156   number_of_ssa_values_ = ssa_index;
    157 }
    158 
    159 void SsaLivenessAnalysis::ComputeLiveness() {
    160   for (HLinearOrderIterator it(*graph_); !it.Done(); it.Advance()) {
    161     HBasicBlock* block = it.Current();
    162     block_infos_[block->GetBlockId()] =
    163         new (graph_->GetArena()) BlockInfo(graph_->GetArena(), *block, number_of_ssa_values_);
    164   }
    165 
    166   // Compute the live ranges, as well as the initial live_in, live_out, and kill sets.
    167   // This method does not handle backward branches for the sets, therefore live_in
    168   // and live_out sets are not yet correct.
    169   ComputeLiveRanges();
    170 
    171   // Do a fixed point calculation to take into account backward branches,
    172   // that will update live_in of loop headers, and therefore live_out and live_in
    173   // of blocks in the loop.
    174   ComputeLiveInAndLiveOutSets();
    175 }
    176 
    177 static void RecursivelyProcessInputs(HInstruction* current,
    178                                      HInstruction* actual_user,
    179                                      BitVector* live_in) {
    180   for (size_t i = 0, e = current->InputCount(); i < e; ++i) {
    181     HInstruction* input = current->InputAt(i);
    182     bool has_in_location = current->GetLocations()->InAt(i).IsValid();
    183     bool has_out_location = input->GetLocations()->Out().IsValid();
    184 
    185     if (has_in_location) {
    186       DCHECK(has_out_location)
    187           << "Instruction " << current->DebugName() << current->GetId()
    188           << " expects an input value at index " << i << " but "
    189           << input->DebugName() << input->GetId() << " does not produce one.";
    190       DCHECK(input->HasSsaIndex());
    191       // `input` generates a result used by `current`. Add use and update
    192       // the live-in set.
    193       input->GetLiveInterval()->AddUse(current, /* environment */ nullptr, i, actual_user);
    194       live_in->SetBit(input->GetSsaIndex());
    195     } else if (has_out_location) {
    196       // `input` generates a result but it is not used by `current`.
    197     } else {
    198       // `input` is inlined into `current`. Walk over its inputs and record
    199       // uses at `current`.
    200       DCHECK(input->IsEmittedAtUseSite());
    201       // Check that the inlined input is not a phi. Recursing on loop phis could
    202       // lead to an infinite loop.
    203       DCHECK(!input->IsPhi());
    204       RecursivelyProcessInputs(input, actual_user, live_in);
    205     }
    206   }
    207 }
    208 
    209 void SsaLivenessAnalysis::ComputeLiveRanges() {
    210   // Do a post order visit, adding inputs of instructions live in the block where
    211   // that instruction is defined, and killing instructions that are being visited.
    212   for (HLinearPostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
    213     HBasicBlock* block = it.Current();
    214 
    215     BitVector* kill = GetKillSet(*block);
    216     BitVector* live_in = GetLiveInSet(*block);
    217 
    218     // Set phi inputs of successors of this block corresponding to this block
    219     // as live_in.
    220     for (HBasicBlock* successor : block->GetSuccessors()) {
    221       live_in->Union(GetLiveInSet(*successor));
    222       if (successor->IsCatchBlock()) {
    223         // Inputs of catch phis will be kept alive through their environment
    224         // uses, allowing the runtime to copy their values to the corresponding
    225         // catch phi spill slots when an exception is thrown.
    226         // The only instructions which may not be recorded in the environments
    227         // are constants created by the SSA builder as typed equivalents of
    228         // untyped constants from the bytecode, or phis with only such constants
    229         // as inputs (verified by GraphChecker). Their raw binary value must
    230         // therefore be the same and we only need to keep alive one.
    231       } else {
    232         size_t phi_input_index = successor->GetPredecessorIndexOf(block);
    233         for (HInstructionIterator phi_it(successor->GetPhis()); !phi_it.Done(); phi_it.Advance()) {
    234           HInstruction* phi = phi_it.Current();
    235           HInstruction* input = phi->InputAt(phi_input_index);
    236           input->GetLiveInterval()->AddPhiUse(phi, phi_input_index, block);
    237           // A phi input whose last user is the phi dies at the end of the predecessor block,
    238           // and not at the phi's lifetime position.
    239           live_in->SetBit(input->GetSsaIndex());
    240         }
    241       }
    242     }
    243 
    244     // Add a range that covers this block to all instructions live_in because of successors.
    245     // Instructions defined in this block will have their start of the range adjusted.
    246     for (uint32_t idx : live_in->Indexes()) {
    247       HInstruction* current = GetInstructionFromSsaIndex(idx);
    248       current->GetLiveInterval()->AddRange(block->GetLifetimeStart(), block->GetLifetimeEnd());
    249     }
    250 
    251     for (HBackwardInstructionIterator back_it(block->GetInstructions()); !back_it.Done();
    252          back_it.Advance()) {
    253       HInstruction* current = back_it.Current();
    254       if (current->HasSsaIndex()) {
    255         // Kill the instruction and shorten its interval.
    256         kill->SetBit(current->GetSsaIndex());
    257         live_in->ClearBit(current->GetSsaIndex());
    258         current->GetLiveInterval()->SetFrom(current->GetLifetimePosition());
    259       }
    260 
    261       // Process the environment first, because we know their uses come after
    262       // or at the same liveness position of inputs.
    263       for (HEnvironment* environment = current->GetEnvironment();
    264            environment != nullptr;
    265            environment = environment->GetParent()) {
    266         // Handle environment uses. See statements (b) and (c) of the
    267         // SsaLivenessAnalysis.
    268         for (size_t i = 0, e = environment->Size(); i < e; ++i) {
    269           HInstruction* instruction = environment->GetInstructionAt(i);
    270           bool should_be_live = ShouldBeLiveForEnvironment(current, instruction);
    271           if (should_be_live) {
    272             DCHECK(instruction->HasSsaIndex());
    273             live_in->SetBit(instruction->GetSsaIndex());
    274           }
    275           if (instruction != nullptr) {
    276             instruction->GetLiveInterval()->AddUse(
    277                 current, environment, i, /* actual_user */ nullptr, should_be_live);
    278           }
    279         }
    280       }
    281 
    282       // Process inputs of instructions.
    283       if (current->IsEmittedAtUseSite()) {
    284         if (kIsDebugBuild) {
    285           DCHECK(!current->GetLocations()->Out().IsValid());
    286           for (const HUseListNode<HInstruction*>& use : current->GetUses()) {
    287             HInstruction* user = use.GetUser();
    288             size_t index = use.GetIndex();
    289             DCHECK(!user->GetLocations()->InAt(index).IsValid());
    290           }
    291           DCHECK(!current->HasEnvironmentUses());
    292         }
    293       } else {
    294         RecursivelyProcessInputs(current, current, live_in);
    295       }
    296     }
    297 
    298     // Kill phis defined in this block.
    299     for (HInstructionIterator inst_it(block->GetPhis()); !inst_it.Done(); inst_it.Advance()) {
    300       HInstruction* current = inst_it.Current();
    301       if (current->HasSsaIndex()) {
    302         kill->SetBit(current->GetSsaIndex());
    303         live_in->ClearBit(current->GetSsaIndex());
    304         LiveInterval* interval = current->GetLiveInterval();
    305         DCHECK((interval->GetFirstRange() == nullptr)
    306                || (interval->GetStart() == current->GetLifetimePosition()));
    307         interval->SetFrom(current->GetLifetimePosition());
    308       }
    309     }
    310 
    311     if (block->IsLoopHeader()) {
    312       if (kIsDebugBuild) {
    313         CheckNoLiveInIrreducibleLoop(*block);
    314       }
    315       size_t last_position = block->GetLoopInformation()->GetLifetimeEnd();
    316       // For all live_in instructions at the loop header, we need to create a range
    317       // that covers the full loop.
    318       for (uint32_t idx : live_in->Indexes()) {
    319         HInstruction* current = GetInstructionFromSsaIndex(idx);
    320         current->GetLiveInterval()->AddLoopRange(block->GetLifetimeStart(), last_position);
    321       }
    322     }
    323   }
    324 }
    325 
    326 void SsaLivenessAnalysis::ComputeLiveInAndLiveOutSets() {
    327   bool changed;
    328   do {
    329     changed = false;
    330 
    331     for (HPostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
    332       const HBasicBlock& block = *it.Current();
    333 
    334       // The live_in set depends on the kill set (which does not
    335       // change in this loop), and the live_out set.  If the live_out
    336       // set does not change, there is no need to update the live_in set.
    337       if (UpdateLiveOut(block) && UpdateLiveIn(block)) {
    338         if (kIsDebugBuild) {
    339           CheckNoLiveInIrreducibleLoop(block);
    340         }
    341         changed = true;
    342       }
    343     }
    344   } while (changed);
    345 }
    346 
    347 bool SsaLivenessAnalysis::UpdateLiveOut(const HBasicBlock& block) {
    348   BitVector* live_out = GetLiveOutSet(block);
    349   bool changed = false;
    350   // The live_out set of a block is the union of live_in sets of its successors.
    351   for (HBasicBlock* successor : block.GetSuccessors()) {
    352     if (live_out->Union(GetLiveInSet(*successor))) {
    353       changed = true;
    354     }
    355   }
    356   return changed;
    357 }
    358 
    359 
    360 bool SsaLivenessAnalysis::UpdateLiveIn(const HBasicBlock& block) {
    361   BitVector* live_out = GetLiveOutSet(block);
    362   BitVector* kill = GetKillSet(block);
    363   BitVector* live_in = GetLiveInSet(block);
    364   // If live_out is updated (because of backward branches), we need to make
    365   // sure instructions in live_out are also in live_in, unless they are killed
    366   // by this block.
    367   return live_in->UnionIfNotIn(live_out, kill);
    368 }
    369 
    370 static int RegisterOrLowRegister(Location location) {
    371   return location.IsPair() ? location.low() : location.reg();
    372 }
    373 
    374 int LiveInterval::FindFirstRegisterHint(size_t* free_until,
    375                                         const SsaLivenessAnalysis& liveness) const {
    376   DCHECK(!IsHighInterval());
    377   if (IsTemp()) return kNoRegister;
    378 
    379   if (GetParent() == this && defined_by_ != nullptr) {
    380     // This is the first interval for the instruction. Try to find
    381     // a register based on its definition.
    382     DCHECK_EQ(defined_by_->GetLiveInterval(), this);
    383     int hint = FindHintAtDefinition();
    384     if (hint != kNoRegister && free_until[hint] > GetStart()) {
    385       return hint;
    386     }
    387   }
    388 
    389   if (IsSplit() && liveness.IsAtBlockBoundary(GetStart() / 2)) {
    390     // If the start of this interval is at a block boundary, we look at the
    391     // location of the interval in blocks preceding the block this interval
    392     // starts at. If one location is a register we return it as a hint. This
    393     // will avoid a move between the two blocks.
    394     HBasicBlock* block = liveness.GetBlockFromPosition(GetStart() / 2);
    395     size_t next_register_use = FirstRegisterUse();
    396     for (HBasicBlock* predecessor : block->GetPredecessors()) {
    397       size_t position = predecessor->GetLifetimeEnd() - 1;
    398       // We know positions above GetStart() do not have a location yet.
    399       if (position < GetStart()) {
    400         LiveInterval* existing = GetParent()->GetSiblingAt(position);
    401         if (existing != nullptr
    402             && existing->HasRegister()
    403             // It's worth using that register if it is available until
    404             // the next use.
    405             && (free_until[existing->GetRegister()] >= next_register_use)) {
    406           return existing->GetRegister();
    407         }
    408       }
    409     }
    410   }
    411 
    412   UsePosition* use = first_use_;
    413   size_t start = GetStart();
    414   size_t end = GetEnd();
    415   while (use != nullptr && use->GetPosition() <= end) {
    416     size_t use_position = use->GetPosition();
    417     if (use_position >= start && !use->IsSynthesized()) {
    418       HInstruction* user = use->GetUser();
    419       size_t input_index = use->GetInputIndex();
    420       if (user->IsPhi()) {
    421         // If the phi has a register, try to use the same.
    422         Location phi_location = user->GetLiveInterval()->ToLocation();
    423         if (phi_location.IsRegisterKind()) {
    424           DCHECK(SameRegisterKind(phi_location));
    425           int reg = RegisterOrLowRegister(phi_location);
    426           if (free_until[reg] >= use_position) {
    427             return reg;
    428           }
    429         }
    430         // If the instruction dies at the phi assignment, we can try having the
    431         // same register.
    432         if (end == user->GetBlock()->GetPredecessors()[input_index]->GetLifetimeEnd()) {
    433           for (size_t i = 0, e = user->InputCount(); i < e; ++i) {
    434             if (i == input_index) {
    435               continue;
    436             }
    437             HInstruction* input = user->InputAt(i);
    438             Location location = input->GetLiveInterval()->GetLocationAt(
    439                 user->GetBlock()->GetPredecessors()[i]->GetLifetimeEnd() - 1);
    440             if (location.IsRegisterKind()) {
    441               int reg = RegisterOrLowRegister(location);
    442               if (free_until[reg] >= use_position) {
    443                 return reg;
    444               }
    445             }
    446           }
    447         }
    448       } else {
    449         // If the instruction is expected in a register, try to use it.
    450         LocationSummary* locations = user->GetLocations();
    451         Location expected = locations->InAt(use->GetInputIndex());
    452         // We use the user's lifetime position - 1 (and not `use_position`) because the
    453         // register is blocked at the beginning of the user.
    454         size_t position = user->GetLifetimePosition() - 1;
    455         if (expected.IsRegisterKind()) {
    456           DCHECK(SameRegisterKind(expected));
    457           int reg = RegisterOrLowRegister(expected);
    458           if (free_until[reg] >= position) {
    459             return reg;
    460           }
    461         }
    462       }
    463     }
    464     use = use->GetNext();
    465   }
    466 
    467   return kNoRegister;
    468 }
    469 
    470 int LiveInterval::FindHintAtDefinition() const {
    471   if (defined_by_->IsPhi()) {
    472     // Try to use the same register as one of the inputs.
    473     const ArenaVector<HBasicBlock*>& predecessors = defined_by_->GetBlock()->GetPredecessors();
    474     for (size_t i = 0, e = defined_by_->InputCount(); i < e; ++i) {
    475       HInstruction* input = defined_by_->InputAt(i);
    476       size_t end = predecessors[i]->GetLifetimeEnd();
    477       LiveInterval* input_interval = input->GetLiveInterval()->GetSiblingAt(end - 1);
    478       if (input_interval->GetEnd() == end) {
    479         // If the input dies at the end of the predecessor, we know its register can
    480         // be reused.
    481         Location input_location = input_interval->ToLocation();
    482         if (input_location.IsRegisterKind()) {
    483           DCHECK(SameRegisterKind(input_location));
    484           return RegisterOrLowRegister(input_location);
    485         }
    486       }
    487     }
    488   } else {
    489     LocationSummary* locations = GetDefinedBy()->GetLocations();
    490     Location out = locations->Out();
    491     if (out.IsUnallocated() && out.GetPolicy() == Location::kSameAsFirstInput) {
    492       // Try to use the same register as the first input.
    493       LiveInterval* input_interval =
    494           GetDefinedBy()->InputAt(0)->GetLiveInterval()->GetSiblingAt(GetStart() - 1);
    495       if (input_interval->GetEnd() == GetStart()) {
    496         // If the input dies at the start of this instruction, we know its register can
    497         // be reused.
    498         Location location = input_interval->ToLocation();
    499         if (location.IsRegisterKind()) {
    500           DCHECK(SameRegisterKind(location));
    501           return RegisterOrLowRegister(location);
    502         }
    503       }
    504     }
    505   }
    506   return kNoRegister;
    507 }
    508 
    509 bool LiveInterval::SameRegisterKind(Location other) const {
    510   if (IsFloatingPoint()) {
    511     if (IsLowInterval() || IsHighInterval()) {
    512       return other.IsFpuRegisterPair();
    513     } else {
    514       return other.IsFpuRegister();
    515     }
    516   } else {
    517     if (IsLowInterval() || IsHighInterval()) {
    518       return other.IsRegisterPair();
    519     } else {
    520       return other.IsRegister();
    521     }
    522   }
    523 }
    524 
    525 bool LiveInterval::NeedsTwoSpillSlots() const {
    526   return type_ == Primitive::kPrimLong || type_ == Primitive::kPrimDouble;
    527 }
    528 
    529 Location LiveInterval::ToLocation() const {
    530   DCHECK(!IsHighInterval());
    531   if (HasRegister()) {
    532     if (IsFloatingPoint()) {
    533       if (HasHighInterval()) {
    534         return Location::FpuRegisterPairLocation(GetRegister(), GetHighInterval()->GetRegister());
    535       } else {
    536         return Location::FpuRegisterLocation(GetRegister());
    537       }
    538     } else {
    539       if (HasHighInterval()) {
    540         return Location::RegisterPairLocation(GetRegister(), GetHighInterval()->GetRegister());
    541       } else {
    542         return Location::RegisterLocation(GetRegister());
    543       }
    544     }
    545   } else {
    546     HInstruction* defined_by = GetParent()->GetDefinedBy();
    547     if (defined_by->IsConstant()) {
    548       return defined_by->GetLocations()->Out();
    549     } else if (GetParent()->HasSpillSlot()) {
    550       if (NeedsTwoSpillSlots()) {
    551         return Location::DoubleStackSlot(GetParent()->GetSpillSlot());
    552       } else {
    553         return Location::StackSlot(GetParent()->GetSpillSlot());
    554       }
    555     } else {
    556       return Location();
    557     }
    558   }
    559 }
    560 
    561 Location LiveInterval::GetLocationAt(size_t position) {
    562   LiveInterval* sibling = GetSiblingAt(position);
    563   DCHECK(sibling != nullptr);
    564   return sibling->ToLocation();
    565 }
    566 
    567 LiveInterval* LiveInterval::GetSiblingAt(size_t position) {
    568   LiveInterval* current = this;
    569   while (current != nullptr && !current->IsDefinedAt(position)) {
    570     current = current->GetNextSibling();
    571   }
    572   return current;
    573 }
    574 
    575 }  // namespace art
    576