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());
return false;
}
- CreateBlocksForTryCatch(code_item);
+ // 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();
+ }
+ }
InitializeParameters(code_item.ins_size_);
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 dex_pc) {
- HBasicBlock* block = FindBlockStartingAt(dex_pc);
+void HGraphBuilder::MaybeUpdateCurrentBlock(size_t index) {
+ HBasicBlock* block = FindBlockStartingAt(index);
if (block == nullptr) {
return;
}
(*number_of_branches)++;
int32_t target = instruction.GetTargetOffset() + dex_pc;
// Create a block for the target instruction.
- FindOrCreateBlockStartingAt(target);
-
+ if (FindBlockStartingAt(target) == nullptr) {
+ block = new (arena_) HBasicBlock(graph_, target);
+ branch_targets_.Put(target, block);
+ }
dex_pc += instruction.SizeInCodeUnits();
code_ptr += instruction.SizeInCodeUnits();
// 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 {
- FindOrCreateBlockStartingAt(dex_pc);
+ } else if (FindBlockStartingAt(dex_pc) == nullptr) {
+ block = new (arena_) HBasicBlock(graph_, dex_pc);
+ branch_targets_.Put(dex_pc, block);
}
}
} else if (instruction.IsSwitch()) {
for (size_t i = 0; i < num_entries; ++i) {
// The target of the case.
uint32_t target = dex_pc + table.GetEntryAt(i + offset);
- FindOrCreateBlockStartingAt(target);
+ if (FindBlockStartingAt(target) == nullptr) {
+ block = new (arena_) HBasicBlock(graph_, target);
+ branch_targets_.Put(target, block);
+ }
// The next case gets its own block.
if (i < num_entries) {
// 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 {
- FindOrCreateBlockStartingAt(dex_pc);
+ } else if (FindBlockStartingAt(dex_pc) == nullptr) {
+ block = new (arena_) HBasicBlock(graph_, dex_pc);
+ branch_targets_.Put(dex_pc, block);
}
} else {
code_ptr += instruction.SizeInCodeUnits();
return true;
}
-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;
+HBasicBlock* HGraphBuilder::FindBlockStartingAt(int32_t index) const {
+ DCHECK_GE(index, 0);
+ return branch_targets_.Get(index);
}
template<typename T>
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 {
- // 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());
- }
+ UpdateLocal(instruction.VRegA(), latest_result_);
latest_result_ = nullptr;
}
break;
- }
case Instruction::CMP_LONG: {
Binop_23x_cmp(instruction, Primitive::kPrimLong, HCompare::kNoBias, dex_pc);
bool ComputeBranchTargets(const uint16_t* start,
const uint16_t* end,
size_t* number_of_branches);
- void MaybeUpdateCurrentBlock(size_t dex_pc);
- HBasicBlock* FindBlockStartingAt(int32_t dex_pc) const;
- HBasicBlock* FindOrCreateBlockStartingAt(int32_t dex_pc);
- bool IsBlockInPcRange(HBasicBlock* block, uint32_t dex_pc_start, uint32_t dex_pc_end);
- void CreateBlocksForTryCatch(const DexFile::CodeItem& code_item);
- void InsertTryBoundaryBlocks(const DexFile::CodeItem& code_item);
+ void MaybeUpdateCurrentBlock(size_t index);
+ HBasicBlock* FindBlockStartingAt(int32_t index) const;
void InitializeLocals(uint16_t count);
HLocal* GetLocalAt(int register_index) const;
HBasicBlock* CodeGenerator::GetNextBlockToEmit() const {
for (size_t i = current_block_index_ + 1; i < block_order_->Size(); ++i) {
HBasicBlock* block = block_order_->Get(i);
- if (!block->IsSingleJump()) {
+ if (!block->IsSingleGoto()) {
return block;
}
}
}
HBasicBlock* CodeGenerator::FirstNonEmptyBlock(HBasicBlock* block) const {
- while (block->IsSingleJump()) {
+ while (block->IsSingleGoto()) {
block = block->GetSuccessors().Get(0);
}
return block;
// Don't generate code for an empty block. Its predecessors will branch to its successor
// directly. Also, the label of that block will not be emitted, so this helps catch
// errors where we reference that label.
- if (block->IsSingleJump()) continue;
+ if (block->IsSingleGoto()) continue;
Bind(block);
for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
HInstruction* current = it.Current();
// FirstNonEmptyBlock() which could lead to adjusting a label more than once.
DCHECK_LT(static_cast<size_t>(block->GetBlockId()), block_labels_.Size());
Label* block_label = &block_labels_.GetRawStorage()[block->GetBlockId()];
- DCHECK_EQ(block_label->IsBound(), !block->IsSingleJump());
+ DCHECK_EQ(block_label->IsBound(), !block->IsSingleGoto());
if (block_label->IsBound()) {
__ AdjustLabelPosition(block_label);
}
|| !IsLeafMethod());
}
-void InstructionCodeGeneratorARM::HandleGoto(HInstruction* got, HBasicBlock* successor) {
+void LocationsBuilderARM::VisitGoto(HGoto* got) {
+ got->SetLocations(nullptr);
+}
+
+void InstructionCodeGeneratorARM::VisitGoto(HGoto* got) {
+ HBasicBlock* successor = got->GetSuccessor();
DCHECK(!successor->IsExitBlock());
HBasicBlock* block = got->GetBlock();
}
}
-void LocationsBuilderARM::VisitGoto(HGoto* got) {
- got->SetLocations(nullptr);
-}
-
-void InstructionCodeGeneratorARM::VisitGoto(HGoto* got) {
- HandleGoto(got, got->GetSuccessor());
-}
-
-void LocationsBuilderARM::VisitTryBoundary(HTryBoundary* try_boundary) {
- try_boundary->SetLocations(nullptr);
-}
-
-void InstructionCodeGeneratorARM::VisitTryBoundary(HTryBoundary* try_boundary) {
- HBasicBlock* successor = try_boundary->GetNormalFlowSuccessor();
- if (!successor->IsExitBlock()) {
- HandleGoto(try_boundary, successor);
- }
-}
-
void LocationsBuilderARM::VisitExit(HExit* exit) {
exit->SetLocations(nullptr);
}
void DivRemByPowerOfTwo(HBinaryOperation* instruction);
void GenerateDivRemWithAnyConstant(HBinaryOperation* instruction);
void GenerateDivRemConstantIntegral(HBinaryOperation* instruction);
- void HandleGoto(HInstruction* got, HBasicBlock* successor);
ArmAssembler* const assembler_;
CodeGeneratorARM* const codegen_;
// Will be generated at use site.
}
-void InstructionCodeGeneratorARM64::HandleGoto(HInstruction* got, HBasicBlock* successor) {
+void LocationsBuilderARM64::VisitGoto(HGoto* got) {
+ got->SetLocations(nullptr);
+}
+
+void InstructionCodeGeneratorARM64::VisitGoto(HGoto* got) {
+ HBasicBlock* successor = got->GetSuccessor();
DCHECK(!successor->IsExitBlock());
HBasicBlock* block = got->GetBlock();
HInstruction* previous = got->GetPrevious();
}
}
-void LocationsBuilderARM64::VisitGoto(HGoto* got) {
- got->SetLocations(nullptr);
-}
-
-void InstructionCodeGeneratorARM64::VisitGoto(HGoto* got) {
- HandleGoto(got, got->GetSuccessor());
-}
-
-void LocationsBuilderARM64::VisitTryBoundary(HTryBoundary* try_boundary) {
- try_boundary->SetLocations(nullptr);
-}
-
-void InstructionCodeGeneratorARM64::VisitTryBoundary(HTryBoundary* try_boundary) {
- HBasicBlock* successor = try_boundary->GetNormalFlowSuccessor();
- if (!successor->IsExitBlock()) {
- HandleGoto(try_boundary, successor);
- }
-}
-
void InstructionCodeGeneratorARM64::GenerateTestAndBranch(HInstruction* instruction,
vixl::Label* true_target,
vixl::Label* false_target,
void DivRemByPowerOfTwo(HBinaryOperation* instruction);
void GenerateDivRemWithAnyConstant(HBinaryOperation* instruction);
void GenerateDivRemIntegral(HBinaryOperation* instruction);
- void HandleGoto(HInstruction* got, HBasicBlock* successor);
+
Arm64Assembler* const assembler_;
CodeGeneratorARM64* const codegen_;
// Will be generated at use site.
}
-void InstructionCodeGeneratorMIPS64::HandleGoto(HInstruction* got, HBasicBlock* successor) {
+void LocationsBuilderMIPS64::VisitGoto(HGoto* got) {
+ got->SetLocations(nullptr);
+}
+
+void InstructionCodeGeneratorMIPS64::VisitGoto(HGoto* got) {
+ HBasicBlock* successor = got->GetSuccessor();
DCHECK(!successor->IsExitBlock());
HBasicBlock* block = got->GetBlock();
HInstruction* previous = got->GetPrevious();
}
}
-void LocationsBuilderMIPS64::VisitGoto(HGoto* got) {
- got->SetLocations(nullptr);
-}
-
-void InstructionCodeGeneratorMIPS64::VisitGoto(HGoto* got) {
- HandleGoto(got, got->GetSuccessor());
-}
-
-void LocationsBuilderMIPS64::VisitTryBoundary(HTryBoundary* try_boundary) {
- try_boundary->SetLocations(nullptr);
-}
-
-void InstructionCodeGeneratorMIPS64::VisitTryBoundary(HTryBoundary* try_boundary) {
- HBasicBlock* successor = try_boundary->GetNormalFlowSuccessor();
- if (!successor->IsExitBlock()) {
- HandleGoto(try_boundary, successor);
- }
-}
-
void InstructionCodeGeneratorMIPS64::GenerateTestAndBranch(HInstruction* instruction,
Label* true_target,
Label* false_target,
Label* true_target,
Label* false_target,
Label* always_true_target);
- void HandleGoto(HInstruction* got, HBasicBlock* successor);
Mips64Assembler* const assembler_;
CodeGeneratorMIPS64* const codegen_;
}
}
-void InstructionCodeGeneratorX86::HandleGoto(HInstruction* got, HBasicBlock* successor) {
+void LocationsBuilderX86::VisitGoto(HGoto* got) {
+ got->SetLocations(nullptr);
+}
+
+void InstructionCodeGeneratorX86::VisitGoto(HGoto* got) {
+ HBasicBlock* successor = got->GetSuccessor();
DCHECK(!successor->IsExitBlock());
HBasicBlock* block = got->GetBlock();
}
}
-void LocationsBuilderX86::VisitGoto(HGoto* got) {
- got->SetLocations(nullptr);
-}
-
-void InstructionCodeGeneratorX86::VisitGoto(HGoto* got) {
- HandleGoto(got, got->GetSuccessor());
-}
-
-void LocationsBuilderX86::VisitTryBoundary(HTryBoundary* try_boundary) {
- try_boundary->SetLocations(nullptr);
-}
-
-void InstructionCodeGeneratorX86::VisitTryBoundary(HTryBoundary* try_boundary) {
- HBasicBlock* successor = try_boundary->GetNormalFlowSuccessor();
- if (!successor->IsExitBlock()) {
- HandleGoto(try_boundary, successor);
- }
-}
-
void LocationsBuilderX86::VisitExit(HExit* exit) {
exit->SetLocations(nullptr);
}
Label* true_target,
Label* false_target,
Label* always_true_target);
- void HandleGoto(HInstruction* got, HBasicBlock* successor);
X86Assembler* const assembler_;
CodeGeneratorX86* const codegen_;
}
}
-void InstructionCodeGeneratorX86_64::HandleGoto(HInstruction* got, HBasicBlock* successor) {
+void LocationsBuilderX86_64::VisitGoto(HGoto* got) {
+ got->SetLocations(nullptr);
+}
+
+void InstructionCodeGeneratorX86_64::VisitGoto(HGoto* got) {
+ HBasicBlock* successor = got->GetSuccessor();
DCHECK(!successor->IsExitBlock());
HBasicBlock* block = got->GetBlock();
}
}
-void LocationsBuilderX86_64::VisitGoto(HGoto* got) {
- got->SetLocations(nullptr);
-}
-
-void InstructionCodeGeneratorX86_64::VisitGoto(HGoto* got) {
- HandleGoto(got, got->GetSuccessor());
-}
-
-void LocationsBuilderX86_64::VisitTryBoundary(HTryBoundary* try_boundary) {
- try_boundary->SetLocations(nullptr);
-}
-
-void InstructionCodeGeneratorX86_64::VisitTryBoundary(HTryBoundary* try_boundary) {
- HBasicBlock* successor = try_boundary->GetNormalFlowSuccessor();
- if (!successor->IsExitBlock()) {
- HandleGoto(try_boundary, successor);
- }
-}
-
void LocationsBuilderX86_64::VisitExit(HExit* exit) {
exit->SetLocations(nullptr);
}
Label* true_target,
Label* false_target,
Label* always_true_target);
- void HandleGoto(HInstruction* got, HBasicBlock* successor);
X86_64Assembler* const assembler_;
CodeGeneratorX86_64* const codegen_;
}
// Ensure `block` ends with a branch instruction.
- // This invariant is not enforced on non-SSA graphs. Graph built from DEX with
- // dead code that falls out of the method will not end with a control-flow
- // instruction. Such code is removed during the SSA-building DCE phase.
- if (GetGraph()->IsInSsaForm() && !block->EndsWithControlFlowInstruction()) {
+ if (!block->EndsWithControlFlowInstruction()) {
AddError(StringPrintf("Block %d does not end with a branch instruction.",
block->GetBlockId()));
}
}
}
-void GraphChecker::VisitReturn(HReturn* ret) {
- if (!ret->GetBlock()->GetSingleSuccessor()->IsExitBlock()) {
- AddError(StringPrintf("%s:%d does not jump to the exit block.",
- ret->DebugName(),
- ret->GetId()));
- }
-}
-
-void GraphChecker::VisitReturnVoid(HReturnVoid* ret) {
- if (!ret->GetBlock()->GetSingleSuccessor()->IsExitBlock()) {
- AddError(StringPrintf("%s:%d does not jump to the exit block.",
- ret->DebugName(),
- ret->GetId()));
- }
-}
-
void SSAChecker::VisitBasicBlock(HBasicBlock* block) {
super_type::VisitBasicBlock(block);
// Check that the HasBoundsChecks() flag is set for bounds checks.
void VisitBoundsCheck(HBoundsCheck* check) OVERRIDE;
- // Check that the Return and ReturnVoid jump to the exit block.
- void VisitReturn(HReturn* ret) OVERRIDE;
- void VisitReturnVoid(HReturnVoid* ret) OVERRIDE;
-
// Was the last visit of the graph valid?
bool IsValid() const {
return errors_.empty();
AddIndent();
output_ << "successors";
for (size_t i = 0, e = block->GetSuccessors().Size(); i < e; ++i) {
- if (!block->IsExceptionalSuccessor(i)) {
- HBasicBlock* successor = block->GetSuccessors().Get(i);
- output_ << " \"B" << successor->GetBlockId() << "\" ";
- }
- }
- output_<< std::endl;
- }
-
- void PrintExceptionHandlers(HBasicBlock* block) {
- AddIndent();
- output_ << "xhandlers";
- for (size_t i = 0, e = block->GetSuccessors().Size(); i < e; ++i) {
- if (block->IsExceptionalSuccessor(i)) {
- HBasicBlock* handler = block->GetSuccessors().Get(i);
- output_ << " \"B" << handler->GetBlockId() << "\" ";
- }
+ HBasicBlock* successor = block->GetSuccessors().Get(i);
+ output_ << " \"B" << successor->GetBlockId() << "\" ";
}
if (block->IsExitBlock() &&
(disasm_info_ != nullptr) &&
<< std::noboolalpha;
}
- void VisitTryBoundary(HTryBoundary* try_boundary) OVERRIDE {
- StartAttributeStream("is_entry") << std::boolalpha
- << try_boundary->IsTryEntry()
- << std::noboolalpha;
- StartAttributeStream("is_exit") << std::boolalpha
- << try_boundary->IsTryExit()
- << std::noboolalpha;
- }
-
bool IsPass(const char* name) {
return strcmp(pass_name_, name) == 0;
}
}
PrintPredecessors(block);
PrintSuccessors(block);
- PrintExceptionHandlers(block);
-
- if (block->IsCatchBlock()) {
- PrintProperty("flags", "catch_block");
- } else {
- PrintEmptyProperty("flags");
- }
-
+ PrintEmptyProperty("xhandlers");
+ PrintEmptyProperty("flags");
if (block->GetDominator() != nullptr) {
PrintProperty("dominator", "B", block->GetDominator()->GetBlockId());
}
ssa_builder.BuildSsa();
}
-HBasicBlock* HGraph::SplitEdge(HBasicBlock* block, HBasicBlock* successor) {
+void HGraph::SplitCriticalEdge(HBasicBlock* block, HBasicBlock* successor) {
+ // Insert a new node between `block` and `successor` to split the
+ // critical edge.
HBasicBlock* new_block = new (arena_) HBasicBlock(this, successor->GetDexPc());
AddBlock(new_block);
+ new_block->AddInstruction(new (arena_) HGoto());
// Use `InsertBetween` to ensure the predecessor index and successor index of
// `block` and `successor` are preserved.
new_block->InsertBetween(block, successor);
- return new_block;
-}
-
-void HGraph::SplitCriticalEdge(HBasicBlock* block, HBasicBlock* successor) {
- // Insert a new node between `block` and `successor` to split the
- // critical edge.
- HBasicBlock* new_block = SplitEdge(block, successor);
- new_block->AddInstruction(new (arena_) HGoto());
if (successor->IsLoopHeader()) {
// If we split at a back edge boundary, make the new block the back edge.
HLoopInformation* info = successor->GetLoopInformation();
}
}
-HBasicBlock* HBasicBlock::SplitBefore(HInstruction* cursor) {
- DCHECK(!graph_->IsInSsaForm()) << "Support for SSA form not implemented";
- DCHECK_EQ(cursor->GetBlock(), this);
-
- HBasicBlock* new_block = new (GetGraph()->GetArena()) HBasicBlock(GetGraph(), GetDexPc());
- new_block->instructions_.first_instruction_ = cursor;
- new_block->instructions_.last_instruction_ = instructions_.last_instruction_;
- instructions_.last_instruction_ = cursor->previous_;
- if (cursor->previous_ == nullptr) {
- instructions_.first_instruction_ = nullptr;
- } else {
- cursor->previous_->next_ = nullptr;
- cursor->previous_ = nullptr;
- }
-
- new_block->instructions_.SetBlockOfInstructions(new_block);
- AddInstruction(new (GetGraph()->GetArena()) HGoto());
-
- for (size_t i = 0, e = GetSuccessors().Size(); i < e; ++i) {
- HBasicBlock* successor = GetSuccessors().Get(i);
- new_block->successors_.Add(successor);
- successor->predecessors_.Put(successor->GetPredecessorIndexOf(this), new_block);
- }
- successors_.Reset();
- AddSuccessor(new_block);
-
- return new_block;
-}
-
HBasicBlock* HBasicBlock::SplitAfter(HInstruction* cursor) {
DCHECK(!cursor->IsControlFlow());
DCHECK_NE(instructions_.last_instruction_, cursor);
return new_block;
}
-bool HBasicBlock::IsExceptionalSuccessor(size_t idx) const {
- return !GetInstructions().IsEmpty()
- && GetLastInstruction()->IsTryBoundary()
- && GetLastInstruction()->AsTryBoundary()->IsExceptionalSuccessor(idx);
-}
-
-static bool HasOnlyOneInstruction(const HBasicBlock& block) {
- return block.GetPhis().IsEmpty()
- && !block.GetInstructions().IsEmpty()
- && block.GetFirstInstruction() == block.GetLastInstruction();
-}
-
bool HBasicBlock::IsSingleGoto() const {
- return HasOnlyOneInstruction(*this) && GetLastInstruction()->IsGoto();
-}
-
-bool HBasicBlock::IsSingleTryBoundary() const {
- return HasOnlyOneInstruction(*this) && GetLastInstruction()->IsTryBoundary();
+ HLoopInformation* loop_info = GetLoopInformation();
+ DCHECK(EndsWithControlFlowInstruction());
+ return GetPhis().IsEmpty()
+ && GetFirstInstruction() == GetLastInstruction()
+ && GetLastInstruction()->IsGoto()
+ // Back edges generate the suspend check.
+ && (loop_info == nullptr || !loop_info->IsBackEdge(*this));
}
bool HBasicBlock::EndsWithControlFlowInstruction() const {
class HDoubleConstant;
class HEnvironment;
class HFloatConstant;
-class HGraphBuilder;
class HGraphVisitor;
class HInstruction;
class HIntConstant;
// Removes `block` from the graph.
void DeleteDeadBlock(HBasicBlock* block);
- // Splits the edge between `block` and `successor` while preserving the
- // indices in the predecessor/successor lists. If there are multiple edges
- // between the blocks, the lowest indices are used.
- // Returns the new block which is empty and has the same dex pc as `successor`.
- HBasicBlock* SplitEdge(HBasicBlock* block, HBasicBlock* successor);
-
void SplitCriticalEdge(HBasicBlock* block, HBasicBlock* successor);
void SimplifyLoop(HBasicBlock* header);
}
bool IsSingleGoto() const;
- bool IsSingleTryBoundary() const;
-
- // Returns true if this block emits nothing but a jump.
- bool IsSingleJump() const {
- HLoopInformation* loop_info = GetLoopInformation();
- return (IsSingleGoto() || IsSingleTryBoundary())
- // Back edges generate a suspend check.
- && (loop_info == nullptr || !loop_info->IsBackEdge(*this));
- }
void AddBackEdge(HBasicBlock* back_edge) {
if (loop_information_ == nullptr) {
successors_.Put(1, temp);
}
- size_t GetPredecessorIndexOf(HBasicBlock* predecessor) const {
+ size_t GetPredecessorIndexOf(HBasicBlock* predecessor) {
for (size_t i = 0, e = predecessors_.Size(); i < e; ++i) {
if (predecessors_.Get(i) == predecessor) {
return i;
return -1;
}
- size_t GetSuccessorIndexOf(HBasicBlock* successor) const {
+ size_t GetSuccessorIndexOf(HBasicBlock* successor) {
for (size_t i = 0, e = successors_.Size(); i < e; ++i) {
if (successors_.Get(i) == successor) {
return i;
return -1;
}
- HBasicBlock* GetSinglePredecessor() const {
- DCHECK_EQ(GetPredecessors().Size(), 1u);
- return GetPredecessors().Get(0);
- }
-
- HBasicBlock* GetSingleSuccessor() const {
- DCHECK_EQ(GetSuccessors().Size(), 1u);
- return GetSuccessors().Get(0);
- }
-
- // Returns whether the first occurrence of `predecessor` in the list of
- // predecessors is at index `idx`.
- bool IsFirstIndexOfPredecessor(HBasicBlock* predecessor, size_t idx) const {
- DCHECK_EQ(GetPredecessors().Get(idx), predecessor);
- return GetPredecessorIndexOf(predecessor) == idx;
- }
-
- // Returns whether successor at index `idx` is an exception handler.
- bool IsExceptionalSuccessor(size_t idx) const;
-
- // Split the block into two blocks just before `cursor`. Returns the newly
- // created, latter block. Note that this method will create a Goto at the end
- // of the former block and will create an edge between them. It will not,
- // however, update the graph, reverse post order or loop information.
- HBasicBlock* SplitBefore(HInstruction* cursor);
-
// Split the block into two blocks just after `cursor`. Returns the newly
// created block. Note that this method just updates raw block information,
// like predecessors, successors, dominators, and instruction list. It does not
M(SuspendCheck, Instruction) \
M(Temporary, Instruction) \
M(Throw, Instruction) \
- M(TryBoundary, Instruction) \
M(TypeConversion, Instruction) \
M(UShr, BinaryOperation) \
M(Xor, BinaryOperation) \
bool IsControlFlow() const OVERRIDE { return true; }
HBasicBlock* GetSuccessor() const {
- return GetBlock()->GetSingleSuccessor();
+ return GetBlock()->GetSuccessors().Get(0);
}
DECLARE_INSTRUCTION(Goto);
DISALLOW_COPY_AND_ASSIGN(HIf);
};
-
-// Abstract instruction which marks the beginning and/or end of a try block and
-// links it to the respective exception handlers. Behaves the same as a Goto in
-// non-exceptional control flow.
-// Normal-flow successor is stored at index zero, exception handlers under
-// higher indices in no particular order.
-class HTryBoundary : public HTemplateInstruction<0> {
- public:
- HTryBoundary(bool is_entry, bool is_exit)
- : HTemplateInstruction(SideEffects::None()), is_entry_(is_entry), is_exit_(is_exit) {}
-
- bool IsControlFlow() const OVERRIDE { return true; }
-
- // Returns the block's non-exceptional successor (index zero).
- HBasicBlock* GetNormalFlowSuccessor() const { return GetBlock()->GetSuccessors().Get(0); }
-
- // Returns whether `handler` is among its exception handlers (non-zero index
- // successors).
- bool HasExceptionHandler(HBasicBlock* handler) const {
- DCHECK(handler->IsCatchBlock());
- return GetBlock()->GetSuccessors().Contains(handler, /* start_from */ 1);
- }
-
- // Returns whether successor at index `idx` is an exception handler.
- bool IsExceptionalSuccessor(size_t idx) const {
- DCHECK_LT(idx, GetBlock()->GetSuccessors().Size());
- bool is_handler = (idx != 0);
- DCHECK(!is_handler || GetBlock()->GetSuccessors().Get(idx)->IsCatchBlock());
- return is_handler;
- }
-
- // If not present already, adds `handler` to its block's list of exception
- // handlers.
- void AddExceptionHandler(HBasicBlock* handler) {
- if (!HasExceptionHandler(handler)) {
- GetBlock()->AddSuccessor(handler);
- }
- }
-
- bool IsTryEntry() const { return is_entry_; }
- bool IsTryExit() const { return is_exit_; }
-
- DECLARE_INSTRUCTION(TryBoundary);
-
- private:
- // Only for debugging purposes.
- bool is_entry_;
- bool is_exit_;
-
- // Only set by HGraphBuilder.
- void SetIsTryEntry() { is_entry_ = true; }
- void SetIsTryExit() { is_exit_ = true; }
-
- friend HGraphBuilder;
-
- DISALLOW_COPY_AND_ASSIGN(HTryBoundary);
-};
-
-
// Deoptimize to interpreter, upon checking a condition.
class HDeoptimize : public HTemplateInstruction<1> {
public:
}
}
- bool Contains(T value, size_t start_from = 0) const {
- for (size_t i = start_from; i < num_used_; ++i) {
+ bool Contains(T value) const {
+ for (size_t i = 0; i < num_used_; ++i) {
if (elem_list_[i] == value) {
return true;
}
+++ /dev/null
-Tests the generation of try/catch blocks in Optimizing.
\ No newline at end of file
+++ /dev/null
-# Copyright (C) 2015 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-.class public LBuilder;
-
-.super Ljava/lang/Object;
-
-# Basic test case with two try blocks and three catch handlers, one of which
-# is shared by the two tries.
-
-## CHECK-START: int Builder.testMultipleTryCatch(int, int, int) builder (after)
-
-## CHECK: name "B0"
-## CHECK: successors "<<BEnterTry1:B\d+>>"
-## CHECK: <<Minus1:i\d+>> IntConstant -1
-## CHECK: <<Minus2:i\d+>> IntConstant -2
-## CHECK: <<Minus3:i\d+>> IntConstant -3
-
-## CHECK: name "<<BTry1:B\d+>>"
-## CHECK: predecessors "<<BEnterTry1>>"
-## CHECK: successors "<<BExitTry1:B\d+>>"
-## CHECK: DivZeroCheck
-
-## CHECK: name "<<BAdd:B\d+>>"
-## CHECK: predecessors "<<BExitTry1>>"
-## CHECK: successors "<<BEnterTry2:B\d+>>"
-## CHECK: Add
-
-## CHECK: name "<<BTry2:B\d+>>"
-## CHECK: predecessors "<<BEnterTry2>>"
-## CHECK: successors "<<BExitTry2:B\d+>>"
-## CHECK: DivZeroCheck
-
-## CHECK: name "<<BReturn:B\d+>>"
-## CHECK: predecessors "<<BExitTry2>>" "<<BCatch1:B\d+>>" "<<BCatch2:B\d+>>" "<<BCatch3:B\d+>>"
-## CHECK: Return
-
-## CHECK: name "<<BCatch1>>"
-## CHECK: predecessors "<<BEnterTry1>>" "<<BExitTry1>>"
-## CHECK: successors "<<BReturn>>"
-## CHECK: flags "catch_block"
-## CHECK: StoreLocal [v0,<<Minus1>>]
-
-## CHECK: name "<<BCatch2>>"
-## CHECK: predecessors "<<BEnterTry2>>" "<<BExitTry2>>"
-## CHECK: successors "<<BReturn>>"
-## CHECK: flags "catch_block"
-## CHECK: StoreLocal [v0,<<Minus2>>]
-
-## CHECK: name "<<BCatch3>>"
-## CHECK: predecessors "<<BEnterTry1>>" "<<BExitTry1>>" "<<BEnterTry2>>" "<<BExitTry2>>"
-## CHECK: successors "<<BReturn>>"
-## CHECK: flags "catch_block"
-## CHECK: StoreLocal [v0,<<Minus3>>]
-
-## CHECK: name "<<BEnterTry1>>"
-## CHECK: predecessors "B0"
-## CHECK: successors "<<BTry1>>"
-## CHECK: xhandlers "<<BCatch1>>" "<<BCatch3>>"
-## CHECK: TryBoundary is_entry:true is_exit:false
-
-## CHECK: name "<<BExitTry1>>"
-## CHECK: predecessors "<<BTry1>>"
-## CHECK: successors "<<BAdd>>"
-## CHECK: xhandlers "<<BCatch1>>" "<<BCatch3>>"
-## CHECK: TryBoundary is_entry:false is_exit:true
-
-## CHECK: name "<<BEnterTry2>>"
-## CHECK: predecessors "<<BAdd>>"
-## CHECK: successors "<<BTry2>>"
-## CHECK: xhandlers "<<BCatch2>>" "<<BCatch3>>"
-## CHECK: TryBoundary is_entry:true is_exit:false
-
-## CHECK: name "<<BExitTry2>>"
-## CHECK: predecessors "<<BTry2>>"
-## CHECK: successors "<<BReturn>>"
-## CHECK: xhandlers "<<BCatch2>>" "<<BCatch3>>"
-## CHECK: TryBoundary is_entry:false is_exit:true
-
-.method public static testMultipleTryCatch(III)I
- .registers 3
-
- :try_start_1
- div-int/2addr p0, p1
- :try_end_1
- .catch Ljava/lang/ArithmeticException; {:try_start_1 .. :try_end_1} :catch_arith
- .catchall {:try_start_1 .. :try_end_1} :catch_other
-
- add-int/2addr p0, p0
-
- :try_start_2
- div-int/2addr p0, p2
- :try_end_2
- .catch Ljava/lang/OutOfMemoryError; {:try_start_2 .. :try_end_2} :catch_mem
- .catchall {:try_start_2 .. :try_end_2} :catch_other
-
- :return
- return p0
-
- :catch_arith
- const/4 p0, -0x1
- goto :return
-
- :catch_mem
- const/4 p0, -0x2
- goto :return
-
- :catch_other
- const/4 p0, -0x3
- goto :return
-.end method
-
-# Test that multiple try-entry blocks are generated if there are multiple entry
-# points into the try block.
-
-## CHECK-START: int Builder.testMultipleEntries(int, int, int, int) builder (after)
-
-## CHECK: name "B0"
-## CHECK: successors "<<BIf:B\d+>>"
-## CHECK: <<Minus1:i\d+>> IntConstant -1
-
-## CHECK: name "<<BIf>>"
-## CHECK: predecessors "B0"
-## CHECK: successors "<<BEnterTry2:B\d+>>" "<<BThen:B\d+>>"
-## CHECK: If
-
-## CHECK: name "<<BThen>>"
-## CHECK: predecessors "<<BIf>>"
-## CHECK: successors "<<BEnterTry1:B\d+>>"
-## CHECK: Div
-
-## CHECK: name "<<BTry1:B\d+>>"
-## CHECK: predecessors "<<BEnterTry1>>"
-## CHECK: successors "<<BTry2:B\d+>>"
-## CHECK: Div
-
-## CHECK: name "<<BTry2>>"
-## CHECK: predecessors "<<BEnterTry2>>" "<<BTry1>>"
-## CHECK: successors "<<BExitTry:B\d+>>"
-## CHECK: Div
-
-## CHECK: name "<<BReturn:B\d+>>"
-## CHECK: predecessors "<<BExitTry>>" "<<BCatch:B\d+>>"
-## CHECK: Return
-
-## CHECK: name "<<BCatch>>"
-## CHECK: predecessors "<<BEnterTry1>>" "<<BEnterTry2>>" "<<BExitTry>>"
-## CHECK: successors "<<BReturn>>"
-## CHECK: flags "catch_block"
-## CHECK: StoreLocal [v0,<<Minus1>>]
-
-## CHECK: name "<<BEnterTry1>>"
-## CHECK: predecessors "<<BThen>>"
-## CHECK: successors "<<BTry1>>"
-## CHECK: xhandlers "<<BCatch>>"
-## CHECK: TryBoundary is_entry:true is_exit:false
-
-## CHECK: name "<<BEnterTry2>>"
-## CHECK: predecessors "<<BIf>>"
-## CHECK: successors "<<BTry2>>"
-## CHECK: xhandlers "<<BCatch>>"
-## CHECK: TryBoundary is_entry:true is_exit:false
-
-## CHECK: name "<<BExitTry>>"
-## CHECK: predecessors "<<BTry2>>"
-## CHECK: successors "<<BReturn>>"
-## CHECK: xhandlers "<<BCatch>>"
-## CHECK: TryBoundary is_entry:false is_exit:true
-
-.method public static testMultipleEntries(IIII)I
- .registers 4
-
- if-eqz p2, :else
-
- div-int/2addr p0, p1
-
- :try_start
- div-int/2addr p0, p2
-
- :else
- div-int/2addr p0, p3
- :try_end
- .catchall {:try_start .. :try_end} :catch_all
-
- :return
- return p0
-
- :catch_all
- const/4 p0, -0x1
- goto :return
-
-.end method
-
-# Test that multiple try-exit blocks are generated if (normal) control flow can
-# jump out of the try block at multiple points.
-
-## CHECK-START: int Builder.testMultipleExits(int, int) builder (after)
-
-## CHECK: name "B0"
-## CHECK: successors "<<BEnterTry:B\d+>>"
-## CHECK: <<Minus1:i\d+>> IntConstant -1
-## CHECK: <<Minus2:i\d+>> IntConstant -2
-
-## CHECK: name "<<BTry:B\d+>>"
-## CHECK: predecessors "<<BEnterTry>>"
-## CHECK: successors "<<BExitTry1:B\d+>>" "<<BExitTry2:B\d+>>"
-## CHECK: Div
-## CHECK: If
-
-## CHECK: name "<<BReturn:B\d+>>"
-## CHECK: predecessors "<<BExitTry2>>" "<<BThen:B\d+>>" "<<BCatch:B\d+>>"
-## CHECK: Return
-
-## CHECK: name "<<BThen>>"
-## CHECK: predecessors "<<BExitTry1>>"
-## CHECK: successors "<<BReturn>>"
-## CHECK: StoreLocal [v0,<<Minus1>>]
-
-## CHECK: name "<<BCatch>>"
-## CHECK: predecessors "<<BEnterTry>>" "<<BExitTry1>>" "<<BExitTry2>>"
-## CHECK: successors "<<BReturn>>"
-## CHECK: flags "catch_block"
-## CHECK: StoreLocal [v0,<<Minus2>>]
-
-## CHECK: name "<<BEnterTry>>"
-## CHECK: predecessors "B0"
-## CHECK: successors "<<BTry>>"
-## CHECK: xhandlers "<<BCatch>>"
-## CHECK: TryBoundary is_entry:true is_exit:false
-
-## CHECK: name "<<BExitTry1>>"
-## CHECK: predecessors "<<BTry>>"
-## CHECK: successors "<<BThen>>"
-## CHECK: xhandlers "<<BCatch>>"
-## CHECK: TryBoundary is_entry:false is_exit:true
-
-## CHECK: name "<<BExitTry2>>"
-## CHECK: predecessors "<<BTry>>"
-## CHECK: successors "<<BReturn>>"
-## CHECK: xhandlers "<<BCatch>>"
-## CHECK: TryBoundary is_entry:false is_exit:true
-
-.method public static testMultipleExits(II)I
- .registers 2
-
- :try_start
- div-int/2addr p0, p1
- if-eqz p0, :then
- :try_end
- .catchall {:try_start .. :try_end} :catch_all
-
- :return
- return p0
-
- :then
- const/4 p0, -0x1
- goto :return
-
- :catch_all
- const/4 p0, -0x2
- goto :return
-.end method
-
-# Test that only one TryBoundary is inserted when an edge connects two different
-# try ranges.
-
-## CHECK-START: int Builder.testSharedBoundary(int, int, int) builder (after)
-
-## CHECK: name "B0"
-## CHECK: successors "<<BEnter1:B\d+>>"
-## CHECK: <<Minus1:i\d+>> IntConstant -1
-## CHECK: <<Minus2:i\d+>> IntConstant -2
-
-## CHECK: name "<<BTry1:B\d+>>"
-## CHECK: predecessors "<<BEnter1>>"
-## CHECK: successors "<<BExit1Enter2:B\d+>>"
-## CHECK: Div
-
-## CHECK: name "<<BTry2:B\d+>>"
-## CHECK: predecessors "<<BExit1Enter2>>"
-## CHECK: successors "<<BExit2:B\d+>>"
-## CHECK: Div
-
-## CHECK: name "<<BReturn:B\d+>>"
-## CHECK: predecessors "<<BExit2>>" "<<BCatch1:B\d+>>" "<<BCatch2:B\d+>>"
-## CHECK: Return
-
-## CHECK: name "<<BCatch1>>"
-## CHECK: predecessors "<<BEnter1>>" "<<BExit1Enter2>>"
-## CHECK: successors "<<BReturn>>"
-## CHECK: flags "catch_block"
-## CHECK: StoreLocal [v0,<<Minus1>>]
-
-## CHECK: name "<<BCatch2>>"
-## CHECK: predecessors "<<BExit1Enter2>>" "<<BExit2>>"
-## CHECK: successors "<<BReturn>>"
-## CHECK: flags "catch_block"
-## CHECK: StoreLocal [v0,<<Minus2>>]
-
-## CHECK: name "<<BEnter1>>"
-## CHECK: predecessors "B0"
-## CHECK: successors "<<BTry1>>"
-## CHECK: xhandlers "<<BCatch1>>"
-## CHECK: TryBoundary is_entry:true is_exit:false
-
-## CHECK: name "<<BExit1Enter2>>"
-## CHECK: predecessors "<<BTry1>>"
-## CHECK: successors "<<BTry2>>"
-## CHECK: xhandlers "<<BCatch1>>" "<<BCatch2>>"
-## CHECK: TryBoundary is_entry:true is_exit:true
-
-## CHECK: name "<<BExit2>>"
-## CHECK: predecessors "<<BTry2>>"
-## CHECK: successors "<<BReturn>>"
-## CHECK: xhandlers "<<BCatch2>>"
-## CHECK: TryBoundary is_entry:false is_exit:true
-
-.method public static testSharedBoundary(III)I
- .registers 3
-
- :try_start_1
- div-int/2addr p0, p1
- :try_end_1
- .catchall {:try_start_1 .. :try_end_1} :catch_all_1
-
- :try_start_2
- div-int/2addr p0, p2
- :try_end_2
- .catchall {:try_start_2 .. :try_end_2} :catch_all_2
-
- :return
- return p0
-
- :catch_all_1
- const/4 p0, -0x1
- goto :return
-
- :catch_all_2
- const/4 p0, -0x2
- goto :return
-.end method
-
-# Same as previous test, only the blocks are processed in the opposite order.
-
-## CHECK-START: int Builder.testSharedBoundary_Reverse(int, int, int) builder (after)
-
-## CHECK: name "B0"
-## CHECK: successors "<<BGoto:B\d+>>"
-## CHECK: <<Minus1:i\d+>> IntConstant -1
-## CHECK: <<Minus2:i\d+>> IntConstant -2
-
-## CHECK: name "<<BGoto>>"
-## CHECK: successors "<<BEnter2:B\d+>>"
-## CHECK: Goto
-
-## CHECK: name "<<BTry1:B\d+>>"
-## CHECK: predecessors "<<BExit2Enter1:B\d+>>"
-## CHECK: successors "<<BExit1:B\d+>>"
-## CHECK: Div
-
-## CHECK: name "<<BTry2:B\d+>>"
-## CHECK: predecessors "<<BEnter2>>"
-## CHECK: successors "<<BExit2Enter1>>"
-## CHECK: Div
-
-## CHECK: name "<<BReturn:B\d+>>"
-## CHECK: predecessors "<<BExit1>>" "<<BCatch1:B\d+>>" "<<BCatch2:B\d+>>"
-## CHECK: Return
-
-## CHECK: name "<<BCatch1>>"
-## CHECK: predecessors "<<BExit2Enter1>>" "<<BExit1>>"
-## CHECK: successors "<<BReturn>>"
-## CHECK: flags "catch_block"
-## CHECK: StoreLocal [v0,<<Minus1>>]
-
-## CHECK: name "<<BCatch2>>"
-## CHECK: predecessors "<<BEnter2>>" "<<BExit2Enter1>>"
-## CHECK: successors "<<BReturn>>"
-## CHECK: flags "catch_block"
-## CHECK: StoreLocal [v0,<<Minus2>>]
-
-## CHECK: name "<<BExit2Enter1>>"
-## CHECK: predecessors "<<BTry2>>"
-## CHECK: successors "<<BTry1>>"
-## CHECK: xhandlers "<<BCatch1>>" "<<BCatch2>>"
-## CHECK: TryBoundary is_entry:true is_exit:true
-
-## CHECK: name "<<BExit1>>"
-## CHECK: predecessors "<<BTry1>>"
-## CHECK: successors "<<BReturn>>"
-## CHECK: xhandlers "<<BCatch1>>"
-## CHECK: TryBoundary is_entry:false is_exit:true
-
-## CHECK: name "<<BEnter2>>"
-## CHECK: predecessors "<<BGoto>>"
-## CHECK: successors "<<BTry2>>"
-## CHECK: xhandlers "<<BCatch2>>"
-## CHECK: TryBoundary is_entry:true is_exit:false
-
-.method public static testSharedBoundary_Reverse(III)I
- .registers 3
-
- goto :try_start_2
-
- :try_start_1
- div-int/2addr p0, p1
- goto :return
- :try_end_1
- .catchall {:try_start_1 .. :try_end_1} :catch_all_1
-
- :try_start_2
- div-int/2addr p0, p2
- goto :try_start_1
- :try_end_2
- .catchall {:try_start_2 .. :try_end_2} :catch_all_2
-
- :return
- return p0
-
- :catch_all_1
- const/4 p0, -0x1
- goto :return
-
- :catch_all_2
- const/4 p0, -0x2
- goto :return
-.end method
-
-# Test that nested tries are split into non-overlapping blocks and TryBoundary
-# blocks are correctly created between them.
-
-## CHECK-START: int Builder.testNestedTry(int, int, int, int) builder (after)
-
-## CHECK: name "B0"
-## CHECK: <<Minus1:i\d+>> IntConstant -1
-## CHECK: <<Minus2:i\d+>> IntConstant -2
-
-## CHECK: name "<<BTry1:B\d+>>"
-## CHECK: predecessors "<<BEnter1:B\d+>>"
-## CHECK: successors "<<BExit1Enter2:B\d+>>"
-## CHECK: Div
-
-## CHECK: name "<<BTry2:B\d+>>"
-## CHECK: predecessors "<<BExit1Enter2>>"
-## CHECK: successors "<<BExit2Enter3:B\d+>>"
-## CHECK: Div
-
-## CHECK: name "<<BTry3:B\d+>>"
-## CHECK: predecessors "<<BExit2Enter3>>"
-## CHECK: successors "<<BExit3:B\d+>>"
-## CHECK: Div
-
-## CHECK: name "<<BReturn:B\d+>>"
-## CHECK: predecessors "<<BExit3>>" "<<BCatchArith:B\d+>>" "<<BCatchAll:B\d+>>"
-
-## CHECK: name "<<BCatchArith>>"
-## CHECK: predecessors "<<BExit1Enter2>>" "<<BExit2Enter3>>"
-## CHECK: successors "<<BReturn>>"
-## CHECK: flags "catch_block"
-## CHECK: StoreLocal [v0,<<Minus1>>]
-
-## CHECK: name "<<BCatchAll>>"
-## CHECK: predecessors "<<BEnter1>>" "<<BExit1Enter2>>" "<<BExit2Enter3>>" "<<BExit3>>"
-## CHECK: successors "<<BReturn>>"
-## CHECK: flags "catch_block"
-## CHECK: StoreLocal [v0,<<Minus2>>]
-
-## CHECK: name "<<BEnter1>>"
-## CHECK: predecessors "B0"
-## CHECK: successors "<<BTry1>>"
-## CHECK: xhandlers "<<BCatchAll>>"
-## CHECK: TryBoundary is_entry:true is_exit:false
-
-## CHECK: name "<<BExit1Enter2>>"
-## CHECK: predecessors "<<BTry1>>"
-## CHECK: successors "<<BTry2>>"
-## CHECK: xhandlers "<<BCatchAll>>" "<<BCatchArith>>"
-## CHECK: TryBoundary is_entry:true is_exit:true
-
-## CHECK: name "<<BExit2Enter3>>"
-## CHECK: predecessors "<<BTry2>>"
-## CHECK: successors "<<BTry3>>"
-## CHECK: xhandlers "<<BCatchArith>>" "<<BCatchAll>>"
-## CHECK: TryBoundary is_entry:true is_exit:true
-
-## CHECK: name "<<BExit3>>"
-## CHECK: predecessors "<<BTry3>>"
-## CHECK: successors "<<BReturn>>"
-## CHECK: xhandlers "<<BCatchAll>>"
-## CHECK: TryBoundary is_entry:false is_exit:true
-
-.method public static testNestedTry(IIII)I
- .registers 4
-
- :try_start_1
- div-int/2addr p0, p1
-
- :try_start_2
- div-int/2addr p0, p2
- :try_end_2
- .catch Ljava/lang/ArithmeticException; {:try_start_2 .. :try_end_2} :catch_arith
-
- div-int/2addr p0, p3
- :try_end_1
- .catchall {:try_start_1 .. :try_end_1} :catch_all
-
- :return
- return p0
-
- :catch_arith
- const/4 p0, -0x1
- goto :return
-
- :catch_all
- const/4 p0, -0x2
- goto :return
-.end method
-
-# Test control flow that enters a try block, leaves it and returns again.
-
-## CHECK-START: int Builder.testIncontinuousTry(int, int, int, int) builder (after)
-
-## CHECK: name "B0"
-## CHECK: <<Minus1:i\d+>> IntConstant -1
-
-## CHECK: name "<<BTry1:B\d+>>"
-## CHECK: predecessors "<<BEnterTry1:B\d+>>"
-## CHECK: successors "<<BExitTry1:B\d+>>"
-## CHECK: Div
-
-## CHECK: name "<<BTry2:B\d+>>"
-## CHECK: predecessors "<<BEnterTry2:B\d+>>"
-## CHECK: successors "<<BExitTry2:B\d+>>"
-## CHECK: Div
-
-## CHECK: name "<<BReturn:B\d+>>"
-## CHECK: predecessors "<<BExitTry2>>" "<<BCatch:B\d+>>"
-
-## CHECK: name "<<BOutside:B\d+>>"
-## CHECK: predecessors "<<BExitTry1>>"
-## CHECK: successors "<<BEnterTry2>>"
-## CHECK: Div
-
-## CHECK: name "<<BCatch>>"
-## CHECK: predecessors "<<BEnterTry1>>" "<<BExitTry1>>" "<<BEnterTry2>>" "<<BExitTry2>>"
-## CHECK: successors "<<BReturn>>"
-## CHECK: flags "catch_block"
-## CHECK: StoreLocal [v0,<<Minus1>>]
-
-## CHECK: name "<<BEnterTry1>>"
-## CHECK: predecessors "B0"
-## CHECK: successors "<<BTry1>>"
-## CHECK: xhandlers "<<BCatch>>"
-## CHECK: TryBoundary is_entry:true is_exit:false
-
-## CHECK: name "<<BExitTry1>>"
-## CHECK: predecessors "<<BTry1>>"
-## CHECK: successors "<<BOutside>>"
-## CHECK: xhandlers "<<BCatch>>"
-## CHECK: TryBoundary is_entry:false is_exit:true
-
-## CHECK: name "<<BEnterTry2>>"
-## CHECK: predecessors "<<BOutside>>"
-## CHECK: successors "<<BTry2>>"
-## CHECK: xhandlers "<<BCatch>>"
-## CHECK: TryBoundary is_entry:true is_exit:false
-
-## CHECK: name "<<BExitTry2>>"
-## CHECK: predecessors "<<BTry2>>"
-## CHECK: successors "<<BReturn>>"
-## CHECK: xhandlers "<<BCatch>>"
-## CHECK: TryBoundary is_entry:false is_exit:true
-
-.method public static testIncontinuousTry(IIII)I
- .registers 4
-
- :try_start
- div-int/2addr p0, p1
- goto :outside
-
- :inside
- div-int/2addr p0, p3
- :try_end
- .catchall {:try_start .. :try_end} :catch_all
-
- :return
- return p0
-
- :outside
- div-int/2addr p0, p2
- goto :inside
-
- :catch_all
- const/4 p0, -0x1
- goto :return
-.end method
-
-# Test that a TryBoundary is inserted between a Throw instruction and the exit
-# block when covered by a try range.
-
-## CHECK-START: int Builder.testThrow(java.lang.Exception) builder (after)
-
-## CHECK: name "B0"
-## CHECK: successors "<<BEnterTry:B\d+>>"
-## CHECK: <<Minus1:i\d+>> IntConstant -1
-
-## CHECK: name "<<BTry:B\d+>>"
-## CHECK: predecessors "<<BEnterTry>>"
-## CHECK: successors "<<BExitTry:B\d+>>"
-## CHECK: Throw
-
-## CHECK: name "<<BCatch:B\d+>>"
-## CHECK: predecessors "<<BEnterTry>>" "<<BExitTry>>"
-## CHECK: successors "<<BExit:B\d+>>"
-## CHECK: flags "catch_block"
-## CHECK: StoreLocal [v0,<<Minus1>>]
-
-## CHECK: name "<<BEnterTry>>"
-## CHECK: predecessors "B0"
-## CHECK: successors "<<BTry>>"
-## CHECK: xhandlers "<<BCatch>>"
-## CHECK: TryBoundary is_entry:true is_exit:false
-
-## CHECK: name "<<BExitTry>>"
-## CHECK: predecessors "<<BTry>>"
-## CHECK: successors "<<BExit>>"
-## CHECK: xhandlers "<<BCatch>>"
-## CHECK: TryBoundary is_entry:false is_exit:true
-
-## CHECK: name "<<BExit>>"
-## CHECK: predecessors "<<BExitTry>>" "<<BCatch>>"
-## CHECK: Exit
-
-.method public static testThrow(Ljava/lang/Exception;)I
- .registers 2
-
- :try_start
- throw p0
- :try_end
- .catchall {:try_start .. :try_end} :catch_all
-
- :catch_all
- const/4 v0, -0x1
- return v0
-.end method
-
-# Test graph with a throw/catch loop.
-
-## CHECK-START: int Builder.testCatchLoop(int, int, int) builder (after)
-
-## CHECK: name "B0"
-## CHECK: successors "<<BEnterTry:B\d+>>"
-
-## CHECK: name "<<BTry:B\d+>>"
-## CHECK: predecessors "<<BEnterTry>>" "<<BEnterTry>>" "<<BExitTry:B\d+>>"
-## CHECK: successors "<<BExitTry>>"
-## CHECK: flags "catch_block"
-## CHECK: Div
-
-## CHECK: name "<<BReturn:B\d+>>"
-## CHECK: predecessors "<<BExitTry>>"
-
-## CHECK: name "<<BEnterTry>>"
-## CHECK: predecessors "B0"
-## CHECK: successors "<<BTry>>"
-## CHECK: xhandlers "<<BTry>>"
-## CHECK: TryBoundary is_entry:true is_exit:false
-
-## CHECK: name "<<BExitTry>>"
-## CHECK: predecessors "<<BTry>>"
-## CHECK: successors "<<BReturn>>"
-## CHECK: xhandlers "<<BTry>>"
-## CHECK: TryBoundary is_entry:false is_exit:true
-
-.method public static testCatchLoop(III)I
- .registers 4
-
- :try_start
- :catch_all
- div-int/2addr p0, p2
- :try_end
- .catchall {:try_start .. :try_end} :catch_all
-
- :return
- return p0
-.end method
-
-# Test that handler edges are not split. In this scenario, the catch block is
-# only the handler of the try block.
-
-## CHECK-START: int Builder.testHandlerEdge1(int, int, int) builder (after)
-
-## CHECK: name "B0"
-## CHECK: successors "<<BEnterTry:B\d+>>"
-
-## CHECK: name "<<BTry:B\d+>>"
-## CHECK: predecessors "<<BEnterTry>>"
-## CHECK: successors "<<BCatch:B\d+>>"
-## CHECK: Div
-
-## CHECK: name "<<BCatch>>"
-## CHECK: predecessors "<<BTry>>" "<<BEnterTry>>" "<<BExitTry:B\d+>>"
-## CHECK: successors "<<BExitTry>>"
-## CHECK: flags "catch_block"
-## CHECK: Div
-
-## CHECK: name "<<BReturn:B\d+>>"
-## CHECK: predecessors "<<BExitTry>>"
-
-## CHECK: name "<<BEnterTry>>"
-## CHECK: predecessors "B0"
-## CHECK: successors "<<BTry>>"
-## CHECK: xhandlers "<<BCatch>>"
-## CHECK: TryBoundary is_entry:true is_exit:false
-
-## CHECK: name "<<BExitTry>>"
-## CHECK: predecessors "<<BCatch>>"
-## CHECK: successors "<<BReturn>>"
-## CHECK: xhandlers "<<BCatch>>"
-## CHECK: TryBoundary is_entry:false is_exit:true
-
-.method public static testHandlerEdge1(III)I
- .registers 4
-
- :try_start
- div-int/2addr p0, p1
-
- :catch_all
- div-int/2addr p0, p2
- :try_end
- .catchall {:try_start .. :try_end} :catch_all
-
- return p0
-.end method
-
-# Test that handler edges are not split. In this scenario, the catch block is
-# the handler and also the successor of the try block.
-
-## CHECK-START: int Builder.testHandlerEdge2(int, int, int) builder (after)
-
-## CHECK: name "B0"
-## CHECK: successors "<<BEnter1:B\d+>>"
-
-## CHECK: name "<<BTry1:B\d+>>"
-## CHECK: predecessors "<<BEnter1>>" "<<BExit1Enter2:B\d+>>" "<<BExit2:B\d+>>"
-## CHECK: successors "<<BExit1Enter2>>"
-## CHECK: flags "catch_block"
-## CHECK: Div
-
-## CHECK: name "<<BTry2:B\d+>>"
-## CHECK: predecessors "<<BExit1Enter2>>" "<<BEnter1>>" "<<BExit1Enter2>>"
-## CHECK: successors "<<BExit2>>"
-## CHECK: flags "catch_block"
-## CHECK: Div
-
-## CHECK: name "<<BReturn:B\d+>>"
-## CHECK: predecessors "<<BExit2>>"
-## CHECK: Return
-
-## CHECK: name "<<BEnter1>>"
-## CHECK: predecessors "B0"
-## CHECK: successors "<<BTry1>>"
-## CHECK: xhandlers "<<BTry2>>"
-## CHECK: TryBoundary is_entry:true is_exit:false
-
-## CHECK: name "<<BExit1Enter2>>"
-## CHECK: predecessors "<<BTry1>>"
-## CHECK: successors "<<BTry2>>"
-## CHECK: xhandlers "<<BTry2>>" "<<BTry1>>"
-## CHECK: TryBoundary is_entry:true is_exit:true
-
-## CHECK: name "<<BExit2>>"
-## CHECK: predecessors "<<BTry2>>"
-## CHECK: successors "<<BReturn>>"
-## CHECK: xhandlers "<<BTry1>>"
-## CHECK: TryBoundary is_entry:false is_exit:true
-
-.method public static testHandlerEdge2(III)I
- .registers 4
-
- :try_start_1
- :catch_all_1
- div-int/2addr p0, p1
- :try_end_1
- .catchall {:try_start_1 .. :try_end_1} :catch_all_2
-
- :try_start_2
- :catch_all_2
- div-int/2addr p0, p2
- :try_end_2
- .catchall {:try_start_2 .. :try_end_2} :catch_all_1
-
- return p0
-.end method
-
-# Test that a MOVE_RESULT instruction is placed into the same block as the
-# INVOKE it follows, even if there is a try boundary between them.
-
-## CHECK-START: int Builder.testMoveResult_Invoke(int, int, int) builder (after)
-
-## CHECK: <<Res:i\d+>> InvokeStaticOrDirect
-## CHECK-NEXT: StoreLocal [v0,<<Res>>]
-
-.method public static testMoveResult_Invoke(III)I
- .registers 3
-
- :try_start
- invoke-static {p0, p1, p2}, LBuilder;->testCatchLoop(III)I
- :try_end
- .catchall {:try_start .. :try_end} :catch_all
-
- move-result p0
-
- :return
- return p0
-
- :catch_all
- const/4 p0, -0x1
- goto :return
-.end method
-
-# Test that a MOVE_RESULT instruction is placed into the same block as the
-# FILLED_NEW_ARRAY it follows, even if there is a try boundary between them.
-
-## CHECK-START: int[] Builder.testMoveResult_FilledNewArray(int, int, int) builder (after)
-
-## CHECK: <<Res:l\d+>> NewArray
-## CHECK-NEXT: Temporary
-## CHECK-NEXT: StoreLocal [v0,<<Res>>]
-## CHECK: ArraySet [<<Res>>,{{i\d+}},{{i\d+}}]
-## CHECK: ArraySet [<<Res>>,{{i\d+}},{{i\d+}}]
-## CHECK: ArraySet [<<Res>>,{{i\d+}},{{i\d+}}]
-
-.method public static testMoveResult_FilledNewArray(III)[I
- .registers 3
-
- :try_start
- filled-new-array {p0, p1, p2}, [I
- :try_end
- .catchall {:try_start .. :try_end} :catch_all
-
- move-result-object p0
-
- :return
- return-object p0
-
- :catch_all
- const/4 p0, 0x0
- goto :return
-.end method
-
-# Test case for ReturnVoid inside a try block. Builder needs to move it outside
-# the try block so as to not split the ReturnVoid-Exit edge.
-# This invariant is enforced by GraphChecker.
-
-.method public static testReturnVoidInTry(II)V
- .registers 2
-
- :catch_all
- :try_start
- return-void
- :try_end
- .catchall {:try_start .. :try_end} :catch_all
-
-.end method
-
-# Test case for Return inside a try block. Builder needs to move it outside the
-# try block so as to not split the Return-Exit edge.
-# This invariant is enforced by GraphChecker.
-
-.method public static testReturnInTry(II)I
- .registers 2
-
- :try_start
- div-int/2addr p0, p1
- return p0
- :try_end
- .catchall {:try_start .. :try_end} :catch_all
-
- :catch_all
- const/4 v0, 0x0
- return v0
-.end method
-
-# Test a (dead) try block which flows out of the method. The block will be
-# removed by DCE but needs to pass post-builder GraphChecker.
-
-## CHECK-START: int Builder.testDeadEndTry(int) builder (after)
-## CHECK-NOT: TryBoundary is_exit:true
-
-.method public static testDeadEndTry(I)I
- .registers 1
-
- return p0
-
- :catch_all
- nop
-
- :try_start
- nop
- :try_end
- .catchall {:try_start .. :try_end} :catch_all
-.end method
+++ /dev/null
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-public class Main {
-
- // Workaround for b/18051191.
- class InnerClass {}
-
- public static void main(String[] args) {}
-}