diff options
Diffstat (limited to 'compiler/optimizing/builder.cc')
-rw-r--r-- | compiler/optimizing/builder.cc | 249 |
1 files changed, 207 insertions, 42 deletions
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc index e527e8bc7c..742429cc55 100644 --- a/compiler/optimizing/builder.cc +++ b/compiler/optimizing/builder.cc @@ -259,6 +259,165 @@ bool HGraphBuilder::SkipCompilation(const DexFile::CodeItem& code_item, return false; } +bool HGraphBuilder::IsBlockInPcRange(HBasicBlock* block, + uint32_t dex_pc_start, + uint32_t dex_pc_end) { + uint32_t dex_pc = block->GetDexPc(); + return block != entry_block_ + && block != exit_block_ + && dex_pc >= dex_pc_start + && dex_pc < dex_pc_end; +} + +void HGraphBuilder::CreateBlocksForTryCatch(const DexFile::CodeItem& code_item) { + if (code_item.tries_size_ == 0) { + return; + } + + // Create branch targets at the start/end of the TryItem range. These are + // places where the program might fall through into/out of the a block and + // where TryBoundary instructions will be inserted later. Other edges which + // enter/exit the try blocks are a result of branches/switches. + for (size_t idx = 0; idx < code_item.tries_size_; ++idx) { + const DexFile::TryItem* try_item = DexFile::GetTryItems(code_item, idx); + uint32_t dex_pc_start = try_item->start_addr_; + uint32_t dex_pc_end = dex_pc_start + try_item->insn_count_; + FindOrCreateBlockStartingAt(dex_pc_start); + if (dex_pc_end < code_item.insns_size_in_code_units_) { + // TODO: Do not create block if the last instruction cannot fall through. + FindOrCreateBlockStartingAt(dex_pc_end); + } else { + // The TryItem spans until the very end of the CodeItem (or beyond if + // invalid) and therefore cannot have any code afterwards. + } + } + + // Create branch targets for exception handlers. + const uint8_t* handlers_ptr = DexFile::GetCatchHandlerData(code_item, 0); + uint32_t handlers_size = DecodeUnsignedLeb128(&handlers_ptr); + for (uint32_t idx = 0; idx < handlers_size; ++idx) { + CatchHandlerIterator iterator(handlers_ptr); + for (; iterator.HasNext(); iterator.Next()) { + uint32_t address = iterator.GetHandlerAddress(); + HBasicBlock* block = FindOrCreateBlockStartingAt(address); + block->SetIsCatchBlock(); + } + handlers_ptr = iterator.EndDataPointer(); + } +} + +void HGraphBuilder::InsertTryBoundaryBlocks(const DexFile::CodeItem& code_item) { + if (code_item.tries_size_ == 0) { + return; + } + + for (size_t idx = 0; idx < code_item.tries_size_; ++idx) { + const DexFile::TryItem* try_item = DexFile::GetTryItems(code_item, idx); + uint32_t try_start = try_item->start_addr_; + uint32_t try_end = try_start + try_item->insn_count_; + + // Iterate over all blocks in the dex pc range of the TryItem and: + // (a) split edges which enter/exit the try range, + // (b) create TryBoundary instructions in the new blocks, + // (c) link the new blocks to corresponding exception handlers. + for (uint32_t inner_pc = try_start; inner_pc < try_end; ++inner_pc) { + HBasicBlock* try_block = FindBlockStartingAt(inner_pc); + if (try_block == nullptr) { + continue; + } + + // Find predecessors which are not covered by the same TryItem range. Such + // edges enter the try block and will have a TryBoundary inserted. + for (size_t i = 0; i < try_block->GetPredecessors().Size(); ++i) { + HBasicBlock* predecessor = try_block->GetPredecessors().Get(i); + HTryBoundary* try_boundary = nullptr; + if (predecessor->IsSingleTryBoundary()) { + try_boundary = predecessor->GetLastInstruction()->AsTryBoundary(); + if (try_boundary->GetNormalFlowSuccessor() == try_block + && try_block->IsFirstIndexOfPredecessor(predecessor, i)) { + // The edge was already split because of an exit from a neighbouring + // TryItem and `predecessor` is the block with a TryBoundary created + // between the two original blocks. We do not split the edge again. + DCHECK(!IsBlockInPcRange(predecessor->GetSinglePredecessor(), try_start, try_end)); + DCHECK(try_boundary->IsTryExit()); + DCHECK(!try_boundary->IsTryEntry()); + try_boundary->SetIsTryEntry(); + } else { + // This is an edge between a previously created TryBoundary and its + // handler. We skip it because it is exceptional flow. + DCHECK(try_block->IsCatchBlock()); + DCHECK(try_boundary->HasExceptionHandler(try_block)); + continue; + } + } else if (!IsBlockInPcRange(predecessor, try_start, try_end)) { + // This is an entry point into the TryItem and the edge has not been + // split yet. That means that either `predecessor` is not in a TryItem, + // or it is in a different TryItem and we happened to iterate over + // this block first. We split the edge and `predecessor` may add its + // own exception handlers later. + try_boundary = new (arena_) HTryBoundary(/* is_entry */ true, /* is_exit */ false); + HBasicBlock* try_entry_block = graph_->SplitEdge(predecessor, try_block); + try_entry_block->AddInstruction(try_boundary); + } else { + // Not an edge on the boundary of the try block. + continue; + } + DCHECK(try_boundary != nullptr); + + // Link the TryBoundary block to the handlers of this TryItem. + for (CatchHandlerIterator it(code_item, *try_item); it.HasNext(); it.Next()) { + try_boundary->AddExceptionHandler(FindBlockStartingAt(it.GetHandlerAddress())); + } + } + + // Find successors which are not covered by the same TryItem range. Such + // edges exit the try block and will have a TryBoundary inserted. + for (size_t i = 0; i < try_block->GetSuccessors().Size(); ++i) { + HBasicBlock* successor = try_block->GetSuccessors().Get(i); + HTryBoundary* try_boundary = nullptr; + if (successor->IsSingleTryBoundary()) { + // The edge was already split because of an entry into a neighbouring + // TryItem. We do not split the edge again. + try_boundary = successor->GetLastInstruction()->AsTryBoundary(); + DCHECK_EQ(try_block, successor->GetSinglePredecessor()); + DCHECK(try_boundary->IsTryEntry()); + DCHECK(!try_boundary->IsTryExit()); + DCHECK(!IsBlockInPcRange(try_boundary->GetNormalFlowSuccessor(), try_start, try_end)); + try_boundary->SetIsTryExit(); + } else if (!IsBlockInPcRange(successor, try_start, try_end)) { + // This is an exit out of the TryItem and the edge has not been split + // yet. That means that either `successor` is not in a TryItem, or it + // is in a different TryItem and we happened to iterate over this + // block first. We split the edge and `successor` may add its own + // exception handlers later. + HInstruction* last_instruction = try_block->GetLastInstruction(); + if (last_instruction->IsReturn() || last_instruction->IsReturnVoid()) { + DCHECK_EQ(successor, exit_block_); + // Control flow exits the try block with a Return(Void). Because + // splitting the edge would invalidate the invariant that Return + // always jumps to Exit, we move the Return outside the try block. + HBasicBlock* return_block = try_block->SplitBefore(last_instruction); + graph_->AddBlock(return_block); + successor = return_block; + } + try_boundary = new (arena_) HTryBoundary(/* is_entry */ false, /* is_exit */ true); + HBasicBlock* try_exit_block = graph_->SplitEdge(try_block, successor); + try_exit_block->AddInstruction(try_boundary); + } else { + // Not an edge on the boundary of the try block. + continue; + } + DCHECK(try_boundary != nullptr); + + // Link the TryBoundary block to the handlers of this TryItem. + for (CatchHandlerIterator it(code_item, *try_item); it.HasNext(); it.Next()) { + try_boundary->AddExceptionHandler(FindBlockStartingAt(it.GetHandlerAddress())); + } + } + } + } +} + bool HGraphBuilder::BuildGraph(const DexFile::CodeItem& code_item) { DCHECK(graph_->GetBlocks().IsEmpty()); @@ -292,24 +451,7 @@ bool HGraphBuilder::BuildGraph(const DexFile::CodeItem& code_item) { return false; } - // Also create blocks for catch handlers. - if (code_item.tries_size_ != 0) { - const uint8_t* handlers_ptr = DexFile::GetCatchHandlerData(code_item, 0); - uint32_t handlers_size = DecodeUnsignedLeb128(&handlers_ptr); - for (uint32_t idx = 0; idx < handlers_size; ++idx) { - CatchHandlerIterator iterator(handlers_ptr); - for (; iterator.HasNext(); iterator.Next()) { - uint32_t address = iterator.GetHandlerAddress(); - HBasicBlock* block = FindBlockStartingAt(address); - if (block == nullptr) { - block = new (arena_) HBasicBlock(graph_, address); - branch_targets_.Put(address, block); - } - block->SetIsCatchBlock(); - } - handlers_ptr = iterator.EndDataPointer(); - } - } + CreateBlocksForTryCatch(code_item); InitializeParameters(code_item.ins_size_); @@ -325,18 +467,24 @@ bool HGraphBuilder::BuildGraph(const DexFile::CodeItem& code_item) { code_ptr += instruction.SizeInCodeUnits(); } - // Add the exit block at the end to give it the highest id. - graph_->AddBlock(exit_block_); - exit_block_->AddInstruction(new (arena_) HExit()); // Add the suspend check to the entry block. entry_block_->AddInstruction(new (arena_) HSuspendCheck(0)); entry_block_->AddInstruction(new (arena_) HGoto()); + // Add Exit to the exit block. + exit_block_->AddInstruction(new (arena_) HExit()); + + // Iterate over blocks covered by TryItems and insert TryBoundaries at entry + // and exit points. This requires all control-flow instructions and + // non-exceptional edges to have been created. + InsertTryBoundaryBlocks(code_item); + // Add the exit block at the end to give it the highest id. + graph_->AddBlock(exit_block_); return true; } -void HGraphBuilder::MaybeUpdateCurrentBlock(size_t index) { - HBasicBlock* block = FindBlockStartingAt(index); +void HGraphBuilder::MaybeUpdateCurrentBlock(size_t dex_pc) { + HBasicBlock* block = FindBlockStartingAt(dex_pc); if (block == nullptr) { return; } @@ -371,10 +519,8 @@ bool HGraphBuilder::ComputeBranchTargets(const uint16_t* code_ptr, (*number_of_branches)++; int32_t target = instruction.GetTargetOffset() + dex_pc; // Create a block for the target instruction. - if (FindBlockStartingAt(target) == nullptr) { - block = new (arena_) HBasicBlock(graph_, target); - branch_targets_.Put(target, block); - } + FindOrCreateBlockStartingAt(target); + dex_pc += instruction.SizeInCodeUnits(); code_ptr += instruction.SizeInCodeUnits(); @@ -383,9 +529,8 @@ bool HGraphBuilder::ComputeBranchTargets(const uint16_t* code_ptr, // In the normal case we should never hit this but someone can artificially forge a dex // file to fall-through out the method code. In this case we bail out compilation. return false; - } else if (FindBlockStartingAt(dex_pc) == nullptr) { - block = new (arena_) HBasicBlock(graph_, dex_pc); - branch_targets_.Put(dex_pc, block); + } else { + FindOrCreateBlockStartingAt(dex_pc); } } } else if (instruction.IsSwitch()) { @@ -401,10 +546,7 @@ bool HGraphBuilder::ComputeBranchTargets(const uint16_t* code_ptr, for (size_t i = 0; i < num_entries; ++i) { // The target of the case. uint32_t target = dex_pc + table.GetEntryAt(i + offset); - if (FindBlockStartingAt(target) == nullptr) { - block = new (arena_) HBasicBlock(graph_, target); - branch_targets_.Put(target, block); - } + FindOrCreateBlockStartingAt(target); // The next case gets its own block. if (i < num_entries) { @@ -421,9 +563,8 @@ bool HGraphBuilder::ComputeBranchTargets(const uint16_t* code_ptr, // file to fall-through out the method code. In this case we bail out compilation. // (A switch can fall-through so we don't need to check CanFlowThrough().) return false; - } else if (FindBlockStartingAt(dex_pc) == nullptr) { - block = new (arena_) HBasicBlock(graph_, dex_pc); - branch_targets_.Put(dex_pc, block); + } else { + FindOrCreateBlockStartingAt(dex_pc); } } else { code_ptr += instruction.SizeInCodeUnits(); @@ -433,9 +574,19 @@ bool HGraphBuilder::ComputeBranchTargets(const uint16_t* code_ptr, return true; } -HBasicBlock* HGraphBuilder::FindBlockStartingAt(int32_t index) const { - DCHECK_GE(index, 0); - return branch_targets_.Get(index); +HBasicBlock* HGraphBuilder::FindBlockStartingAt(int32_t dex_pc) const { + DCHECK_GE(dex_pc, 0); + DCHECK_LT(static_cast<size_t>(dex_pc), branch_targets_.Size()); + return branch_targets_.Get(dex_pc); +} + +HBasicBlock* HGraphBuilder::FindOrCreateBlockStartingAt(int32_t dex_pc) { + HBasicBlock* block = FindBlockStartingAt(dex_pc); + if (block == nullptr) { + block = new (arena_) HBasicBlock(graph_, dex_pc); + branch_targets_.Put(dex_pc, block); + } + return block; } template<typename T> @@ -2108,15 +2259,29 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32 case Instruction::MOVE_RESULT: case Instruction::MOVE_RESULT_WIDE: - case Instruction::MOVE_RESULT_OBJECT: + case Instruction::MOVE_RESULT_OBJECT: { if (latest_result_ == nullptr) { // Only dead code can lead to this situation, where the verifier // does not reject the method. } else { - UpdateLocal(instruction.VRegA(), latest_result_); + // An Invoke/FilledNewArray and its MoveResult could have landed in + // different blocks if there was a try/catch block boundary between + // them. We need to insert the StoreLocal after the result definition + // but before any potential Temporaries. + HStoreLocal* update_local = + new (arena_) HStoreLocal(GetLocalAt(instruction.VRegA()), latest_result_); + HBasicBlock* block = latest_result_->GetBlock(); + if (latest_result_->IsInvoke()) { + block->InsertInstructionAfter(update_local, latest_result_); + } else { + DCHECK(latest_result_->IsNewArray()); + DCHECK(latest_result_->GetNext()->IsTemporary()); + block->InsertInstructionAfter(update_local, latest_result_->GetNext()); + } latest_result_ = nullptr; } break; + } case Instruction::CMP_LONG: { Binop_23x_cmp(instruction, Primitive::kPrimLong, HCompare::kNoBias, dex_pc); |