summaryrefslogtreecommitdiff
path: root/compiler/optimizing/builder.cc
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/optimizing/builder.cc')
-rw-r--r--compiler/optimizing/builder.cc623
1 files changed, 136 insertions, 487 deletions
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index b6b8322f03..53158588de 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -20,6 +20,7 @@
#include "base/arena_bit_vector.h"
#include "base/bit_vector-inl.h"
#include "base/logging.h"
+#include "bytecode_utils.h"
#include "class_linker.h"
#include "dex/verified_method.h"
#include "dex_file-inl.h"
@@ -41,9 +42,10 @@ namespace art {
void HGraphBuilder::InitializeLocals(uint16_t count) {
graph_->SetNumberOfVRegs(count);
locals_.resize(count);
+ HBasicBlock* entry_block = graph_->GetEntryBlock();
for (int i = 0; i < count; i++) {
HLocal* local = new (arena_) HLocal(i);
- entry_block_->AddInstruction(local);
+ entry_block->AddInstruction(local);
locals_[i] = local;
}
}
@@ -54,6 +56,8 @@ void HGraphBuilder::InitializeParameters(uint16_t number_of_parameters) {
return;
}
+ HBasicBlock* entry_block = graph_->GetEntryBlock();
+
graph_->SetNumberOfInVRegs(number_of_parameters);
const char* shorty = dex_compilation_unit_->GetShorty();
int locals_index = locals_.size() - number_of_parameters;
@@ -68,9 +72,9 @@ void HGraphBuilder::InitializeParameters(uint16_t number_of_parameters) {
parameter_index++,
Primitive::kPrimNot,
true);
- entry_block_->AddInstruction(parameter);
+ entry_block->AddInstruction(parameter);
HLocal* local = GetLocalAt(locals_index++);
- entry_block_->AddInstruction(new (arena_) HStoreLocal(local, parameter, local->GetDexPc()));
+ entry_block->AddInstruction(new (arena_) HStoreLocal(local, parameter, local->GetDexPc()));
number_of_parameters--;
}
@@ -84,11 +88,11 @@ void HGraphBuilder::InitializeParameters(uint16_t number_of_parameters) {
Primitive::GetType(shorty[shorty_pos]),
false);
++shorty_pos;
- entry_block_->AddInstruction(parameter);
+ entry_block->AddInstruction(parameter);
HLocal* local = GetLocalAt(locals_index++);
// Store the parameter value in the local that the dex code will use
// to reference that parameter.
- entry_block_->AddInstruction(new (arena_) HStoreLocal(local, parameter, local->GetDexPc()));
+ entry_block->AddInstruction(new (arena_) HStoreLocal(local, parameter, local->GetDexPc()));
bool is_wide = (parameter->GetType() == Primitive::kPrimLong)
|| (parameter->GetType() == Primitive::kPrimDouble);
if (is_wide) {
@@ -101,36 +105,22 @@ void HGraphBuilder::InitializeParameters(uint16_t number_of_parameters) {
template<typename T>
void HGraphBuilder::If_22t(const Instruction& instruction, uint32_t dex_pc) {
- int32_t target_offset = instruction.GetTargetOffset();
- HBasicBlock* branch_target = FindBlockStartingAt(dex_pc + target_offset);
- HBasicBlock* fallthrough_target = FindBlockStartingAt(dex_pc + instruction.SizeInCodeUnits());
- DCHECK(branch_target != nullptr);
- DCHECK(fallthrough_target != nullptr);
HInstruction* first = LoadLocal(instruction.VRegA(), Primitive::kPrimInt, dex_pc);
HInstruction* second = LoadLocal(instruction.VRegB(), Primitive::kPrimInt, dex_pc);
T* comparison = new (arena_) T(first, second, dex_pc);
current_block_->AddInstruction(comparison);
HInstruction* ifinst = new (arena_) HIf(comparison, dex_pc);
current_block_->AddInstruction(ifinst);
- current_block_->AddSuccessor(branch_target);
- current_block_->AddSuccessor(fallthrough_target);
current_block_ = nullptr;
}
template<typename T>
void HGraphBuilder::If_21t(const Instruction& instruction, uint32_t dex_pc) {
- int32_t target_offset = instruction.GetTargetOffset();
- HBasicBlock* branch_target = FindBlockStartingAt(dex_pc + target_offset);
- HBasicBlock* fallthrough_target = FindBlockStartingAt(dex_pc + instruction.SizeInCodeUnits());
- DCHECK(branch_target != nullptr);
- DCHECK(fallthrough_target != nullptr);
HInstruction* value = LoadLocal(instruction.VRegA(), Primitive::kPrimInt, dex_pc);
T* comparison = new (arena_) T(value, graph_->GetIntConstant(0, dex_pc), dex_pc);
current_block_->AddInstruction(comparison);
HInstruction* ifinst = new (arena_) HIf(comparison, dex_pc);
current_block_->AddInstruction(ifinst);
- current_block_->AddSuccessor(branch_target);
- current_block_->AddSuccessor(fallthrough_target);
current_block_ = nullptr;
}
@@ -140,28 +130,32 @@ void HGraphBuilder::MaybeRecordStat(MethodCompilationStat compilation_stat) {
}
}
-bool HGraphBuilder::SkipCompilation(const DexFile::CodeItem& code_item,
- size_t number_of_branches) {
+bool HGraphBuilder::SkipCompilation(size_t number_of_branches) {
+ if (compiler_driver_ == nullptr) {
+ // Note that the compiler driver is null when unit testing.
+ return false;
+ }
+
const CompilerOptions& compiler_options = compiler_driver_->GetCompilerOptions();
CompilerFilter::Filter compiler_filter = compiler_options.GetCompilerFilter();
if (compiler_filter == CompilerFilter::kEverything) {
return false;
}
- if (compiler_options.IsHugeMethod(code_item.insns_size_in_code_units_)) {
+ if (compiler_options.IsHugeMethod(code_item_.insns_size_in_code_units_)) {
VLOG(compiler) << "Skip compilation of huge method "
<< PrettyMethod(dex_compilation_unit_->GetDexMethodIndex(), *dex_file_)
- << ": " << code_item.insns_size_in_code_units_ << " code units";
+ << ": " << code_item_.insns_size_in_code_units_ << " code units";
MaybeRecordStat(MethodCompilationStat::kNotCompiledHugeMethod);
return true;
}
// If it's large and contains no branches, it's likely to be machine generated initialization.
- if (compiler_options.IsLargeMethod(code_item.insns_size_in_code_units_)
+ if (compiler_options.IsLargeMethod(code_item_.insns_size_in_code_units_)
&& (number_of_branches == 0)) {
VLOG(compiler) << "Skip compilation of large method with no branch "
<< PrettyMethod(dex_compilation_unit_->GetDexMethodIndex(), *dex_file_)
- << ": " << code_item.insns_size_in_code_units_ << " code units";
+ << ": " << code_item_.insns_size_in_code_units_ << " code units";
MaybeRecordStat(MethodCompilationStat::kNotCompiledLargeMethodNoBranches);
return true;
}
@@ -169,194 +163,19 @@ bool HGraphBuilder::SkipCompilation(const DexFile::CodeItem& code_item,
return false;
}
-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->SetTryCatchInformation(
- new (arena_) TryCatchInformation(iterator.GetHandlerTypeIndex(), *dex_file_));
- }
- handlers_ptr = iterator.EndDataPointer();
- }
-}
-
-// Returns the TryItem stored for `block` or nullptr if there is no info for it.
-static const DexFile::TryItem* GetTryItem(
- HBasicBlock* block,
- const ArenaSafeMap<uint32_t, const DexFile::TryItem*>& try_block_info) {
- auto iterator = try_block_info.find(block->GetBlockId());
- return (iterator == try_block_info.end()) ? nullptr : iterator->second;
-}
-
-void HGraphBuilder::LinkToCatchBlocks(HTryBoundary* try_boundary,
- const DexFile::CodeItem& code_item,
- const DexFile::TryItem* try_item) {
- for (CatchHandlerIterator it(code_item, *try_item); it.HasNext(); it.Next()) {
- try_boundary->AddExceptionHandler(FindBlockStartingAt(it.GetHandlerAddress()));
- }
-}
-
-void HGraphBuilder::InsertTryBoundaryBlocks(const DexFile::CodeItem& code_item) {
- if (code_item.tries_size_ == 0) {
- return;
- }
-
- // Keep a map of all try blocks and their respective TryItems. We do not use
- // the block's pointer but rather its id to ensure deterministic iteration.
- ArenaSafeMap<uint32_t, const DexFile::TryItem*> try_block_info(
- std::less<uint32_t>(), arena_->Adapter(kArenaAllocGraphBuilder));
-
- // Obtain TryItem information for blocks with throwing instructions, and split
- // blocks which are both try & catch to simplify the graph.
- // NOTE: We are appending new blocks inside the loop, so we need to use index
- // because iterators can be invalidated. We remember the initial size to avoid
- // iterating over the new blocks which cannot throw.
- for (size_t i = 0, e = graph_->GetBlocks().size(); i < e; ++i) {
- HBasicBlock* block = graph_->GetBlocks()[i];
-
- // Do not bother creating exceptional edges for try blocks which have no
- // throwing instructions. In that case we simply assume that the block is
- // not covered by a TryItem. This prevents us from creating a throw-catch
- // loop for synchronized blocks.
- if (block->HasThrowingInstructions()) {
- // Try to find a TryItem covering the block.
- DCHECK_NE(block->GetDexPc(), kNoDexPc) << "Block must have a dex_pc to find its TryItem.";
- const int32_t try_item_idx = DexFile::FindTryItem(code_item, block->GetDexPc());
- if (try_item_idx != -1) {
- // Block throwing and in a TryItem. Store the try block information.
- HBasicBlock* throwing_block = block;
- if (block->IsCatchBlock()) {
- // Simplify blocks which are both try and catch, otherwise we would
- // need a strategy for splitting exceptional edges. We split the block
- // after the move-exception (if present) and mark the first part not
- // throwing. The normal-flow edge between them will be split later.
- throwing_block = block->SplitCatchBlockAfterMoveException();
- // Move-exception does not throw and the block has throwing insructions
- // so it must have been possible to split it.
- DCHECK(throwing_block != nullptr);
- }
-
- try_block_info.Put(throwing_block->GetBlockId(),
- DexFile::GetTryItems(code_item, try_item_idx));
- }
- }
- }
-
- // Do a pass over the try blocks and insert entering TryBoundaries where at
- // least one predecessor is not covered by the same TryItem as the try block.
- // We do not split each edge separately, but rather create one boundary block
- // that all predecessors are relinked to. This preserves loop headers (b/23895756).
- for (auto entry : try_block_info) {
- HBasicBlock* try_block = graph_->GetBlocks()[entry.first];
- for (HBasicBlock* predecessor : try_block->GetPredecessors()) {
- if (GetTryItem(predecessor, try_block_info) != entry.second) {
- // Found a predecessor not covered by the same TryItem. Insert entering
- // boundary block.
- HTryBoundary* try_entry =
- new (arena_) HTryBoundary(HTryBoundary::BoundaryKind::kEntry, try_block->GetDexPc());
- try_block->CreateImmediateDominator()->AddInstruction(try_entry);
- LinkToCatchBlocks(try_entry, code_item, entry.second);
- break;
- }
- }
- }
-
- // Do a second pass over the try blocks and insert exit TryBoundaries where
- // the successor is not in the same TryItem.
- for (auto entry : try_block_info) {
- HBasicBlock* try_block = graph_->GetBlocks()[entry.first];
- // NOTE: Do not use iterators because SplitEdge would invalidate them.
- for (size_t i = 0, e = try_block->GetSuccessors().size(); i < e; ++i) {
- HBasicBlock* successor = try_block->GetSuccessors()[i];
-
- // If the successor is a try block, all of its predecessors must be
- // covered by the same TryItem. Otherwise the previous pass would have
- // created a non-throwing boundary block.
- if (GetTryItem(successor, try_block_info) != nullptr) {
- DCHECK_EQ(entry.second, GetTryItem(successor, try_block_info));
- continue;
- }
-
- // Preserve the invariant that Return(Void) always jumps to Exit by moving
- // it outside the try block if necessary.
- HInstruction* last_instruction = try_block->GetLastInstruction();
- if (last_instruction->IsReturn() || last_instruction->IsReturnVoid()) {
- DCHECK_EQ(successor, exit_block_);
- successor = try_block->SplitBefore(last_instruction);
- }
-
- // Insert TryBoundary and link to catch blocks.
- HTryBoundary* try_exit =
- new (arena_) HTryBoundary(HTryBoundary::BoundaryKind::kExit, successor->GetDexPc());
- graph_->SplitEdge(try_block, successor)->AddInstruction(try_exit);
- LinkToCatchBlocks(try_exit, code_item, entry.second);
- }
+static bool BlockIsNotPopulated(HBasicBlock* block) {
+ if (!block->GetPhis().IsEmpty()) {
+ return false;
+ } else if (block->IsLoopHeader()) {
+ // Suspend checks were inserted into loop headers during building of dominator tree.
+ DCHECK(block->GetFirstInstruction()->IsSuspendCheck());
+ return block->GetFirstInstruction() == block->GetLastInstruction();
+ } else {
+ return block->GetInstructions().IsEmpty();
}
}
-GraphAnalysisResult HGraphBuilder::BuildGraph(const DexFile::CodeItem& code_item,
- StackHandleScopeCollection* handles) {
- DCHECK(graph_->GetBlocks().empty());
-
- const uint16_t* code_ptr = code_item.insns_;
- const uint16_t* code_end = code_item.insns_ + code_item.insns_size_in_code_units_;
- code_start_ = code_ptr;
-
- // Setup the graph with the entry block and exit block.
- entry_block_ = new (arena_) HBasicBlock(graph_, 0);
- graph_->AddBlock(entry_block_);
- exit_block_ = new (arena_) HBasicBlock(graph_, kNoDexPc);
- graph_->SetEntryBlock(entry_block_);
- graph_->SetExitBlock(exit_block_);
-
- graph_->SetHasTryCatch(code_item.tries_size_ != 0);
-
- InitializeLocals(code_item.registers_size_);
- graph_->SetMaximumNumberOfOutVRegs(code_item.outs_size_);
-
- // Compute the number of dex instructions, blocks, and branches. We will
- // check these values against limits given to the compiler.
- size_t number_of_branches = 0;
-
- // To avoid splitting blocks, we compute ahead of time the instructions that
- // start a new block, and create these blocks.
- if (!ComputeBranchTargets(code_ptr, code_end, &number_of_branches)) {
- MaybeRecordStat(MethodCompilationStat::kNotCompiledBranchOutsideMethodCode);
- return kAnalysisInvalidBytecode;
- }
-
- // Note that the compiler driver is null when unit testing.
- if ((compiler_driver_ != nullptr) && SkipCompilation(code_item, number_of_branches)) {
- return kAnalysisInvalidBytecode;
- }
-
+bool HGraphBuilder::GenerateInstructions() {
// Find locations where we want to generate extra stackmaps for native debugging.
// This allows us to generate the info only at interesting points (for example,
// at start of java statement) rather than before every dex instruction.
@@ -364,74 +183,93 @@ GraphAnalysisResult HGraphBuilder::BuildGraph(const DexFile::CodeItem& code_item
compiler_driver_->GetCompilerOptions().GetNativeDebuggable();
ArenaBitVector* native_debug_info_locations;
if (native_debuggable) {
- const uint32_t num_instructions = code_item.insns_size_in_code_units_;
+ const uint32_t num_instructions = code_item_.insns_size_in_code_units_;
native_debug_info_locations =
ArenaBitVector::Create(arena_, num_instructions, false, kArenaAllocGraphBuilder);
- FindNativeDebugInfoLocations(code_item, native_debug_info_locations);
+ FindNativeDebugInfoLocations(native_debug_info_locations);
}
- CreateBlocksForTryCatch(code_item);
+ InitializeLocals(code_item_.registers_size_);
+ InitializeParameters(code_item_.ins_size_);
- InitializeParameters(code_item.ins_size_);
+ // Add the suspend check to the entry block.
+ current_block_ = graph_->GetEntryBlock();
+ current_block_->AddInstruction(new (arena_) HSuspendCheck(0));
- size_t dex_pc = 0;
- while (code_ptr < code_end) {
- // Update the current block if dex_pc starts a new block.
- MaybeUpdateCurrentBlock(dex_pc);
- const Instruction& instruction = *Instruction::At(code_ptr);
- if (native_debuggable && native_debug_info_locations->IsBitSet(dex_pc)) {
+ for (CodeItemIterator it(code_item_); !it.Done(); it.Advance()) {
+ uint32_t dex_pc = it.CurrentDexPc();
+
+ HBasicBlock* next_block = FindBlockStartingAt(dex_pc);
+ if (next_block != nullptr && next_block->GetGraph() != nullptr) {
if (current_block_ != nullptr) {
- current_block_->AddInstruction(new (arena_) HNativeDebugInfo(dex_pc));
+ // Branching instructions clear current_block, so we know
+ // the last instruction of the current block is not a branching
+ // instruction. We add an unconditional goto to the found block.
+ current_block_->AddInstruction(new (arena_) HGoto(dex_pc));
}
+ DCHECK(BlockIsNotPopulated(next_block));
+ current_block_ = next_block;
+ }
+
+ if (current_block_ == nullptr) {
+ // Unreachable code.
+ continue;
}
- if (!AnalyzeDexInstruction(instruction, dex_pc)) {
- return kAnalysisInvalidBytecode;
+
+ if (native_debuggable && native_debug_info_locations->IsBitSet(dex_pc)) {
+ current_block_->AddInstruction(new (arena_) HNativeDebugInfo(dex_pc));
+ }
+
+ if (!AnalyzeDexInstruction(it.CurrentInstruction(), dex_pc)) {
+ return false;
}
- dex_pc += instruction.SizeInCodeUnits();
- code_ptr += instruction.SizeInCodeUnits();
}
// Add Exit to the 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 the exit block at the end.
- graph_->AddBlock(exit_block_);
+ HBasicBlock* exit_block = graph_->GetExitBlock();
+ if (exit_block == nullptr) {
+ // Unreachable exit block was removed.
+ } else {
+ exit_block->AddInstruction(new (arena_) HExit());
+ }
+
+ return true;
+}
+
+GraphAnalysisResult HGraphBuilder::BuildGraph(StackHandleScopeCollection* handles) {
+ DCHECK(graph_->GetBlocks().empty());
+ graph_->SetMaximumNumberOfOutVRegs(code_item_.outs_size_);
+ graph_->SetHasTryCatch(code_item_.tries_size_ != 0);
+ graph_->InitializeInexactObjectRTI(handles);
+
+ // 1) Create basic blocks and link them together. Basic blocks are left
+ // unpopulated with the exception of synthetic blocks, e.g. HTryBoundaries.
+ if (!block_builder_.Build()) {
+ return kAnalysisInvalidBytecode;
+ }
- // 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);
+ // 2) Decide whether to skip this method based on its code size and number
+ // of branches.
+ if (SkipCompilation(block_builder_.GetNumberOfBranches())) {
+ return kAnalysisSkipped;
+ }
+ // 3) Build the dominator tree and fill in loop and try/catch metadata.
GraphAnalysisResult result = graph_->BuildDominatorTree();
if (result != kAnalysisSuccess) {
return result;
}
- graph_->InitializeInexactObjectRTI(handles);
- return SsaBuilder(graph_, handles).BuildSsa();
-}
-
-void HGraphBuilder::MaybeUpdateCurrentBlock(size_t dex_pc) {
- HBasicBlock* block = FindBlockStartingAt(dex_pc);
- if (block == nullptr) {
- return;
+ // 4) Populate basic blocks with instructions.
+ if (!GenerateInstructions()) {
+ return kAnalysisInvalidBytecode;
}
- if (current_block_ != nullptr) {
- // Branching instructions clear current_block, so we know
- // the last instruction of the current block is not a branching
- // instruction. We add an unconditional goto to the found block.
- current_block_->AddInstruction(new (arena_) HGoto(dex_pc));
- current_block_->AddSuccessor(block);
- }
- graph_->AddBlock(block);
- current_block_ = block;
+ // 5) Type the graph and eliminate dead/redundant phis.
+ return SsaBuilder(graph_, code_item_, handles).BuildSsa();
}
-void HGraphBuilder::FindNativeDebugInfoLocations(const DexFile::CodeItem& code_item,
- ArenaBitVector* locations) {
+void HGraphBuilder::FindNativeDebugInfoLocations(ArenaBitVector* locations) {
// The callback gets called when the line number changes.
// In other words, it marks the start of new java statement.
struct Callback {
@@ -440,20 +278,20 @@ void HGraphBuilder::FindNativeDebugInfoLocations(const DexFile::CodeItem& code_i
return false;
}
};
- dex_file_->DecodeDebugPositionInfo(&code_item, Callback::Position, locations);
+ dex_file_->DecodeDebugPositionInfo(&code_item_, Callback::Position, locations);
// Instruction-specific tweaks.
- const Instruction* const begin = Instruction::At(code_item.insns_);
- const Instruction* const end = begin->RelativeAt(code_item.insns_size_in_code_units_);
+ const Instruction* const begin = Instruction::At(code_item_.insns_);
+ const Instruction* const end = begin->RelativeAt(code_item_.insns_size_in_code_units_);
for (const Instruction* inst = begin; inst < end; inst = inst->Next()) {
switch (inst->Opcode()) {
case Instruction::MOVE_EXCEPTION: {
// Stop in native debugger after the exception has been moved.
// The compiler also expects the move at the start of basic block so
// we do not want to interfere by inserting native-debug-info before it.
- locations->ClearBit(inst->GetDexPc(code_item.insns_));
+ locations->ClearBit(inst->GetDexPc(code_item_.insns_));
const Instruction* next = inst->Next();
if (next < end) {
- locations->SetBit(next->GetDexPc(code_item.insns_));
+ locations->SetBit(next->GetDexPc(code_item_.insns_));
}
break;
}
@@ -463,93 +301,6 @@ void HGraphBuilder::FindNativeDebugInfoLocations(const DexFile::CodeItem& code_i
}
}
-bool HGraphBuilder::ComputeBranchTargets(const uint16_t* code_ptr,
- const uint16_t* code_end,
- size_t* number_of_branches) {
- branch_targets_.resize(code_end - code_ptr, nullptr);
-
- // Create the first block for the dex instructions, single successor of the entry block.
- HBasicBlock* block = new (arena_) HBasicBlock(graph_, 0);
- branch_targets_[0] = block;
- entry_block_->AddSuccessor(block);
-
- // Iterate over all instructions and find branching instructions. Create blocks for
- // the locations these instructions branch to.
- uint32_t dex_pc = 0;
- while (code_ptr < code_end) {
- const Instruction& instruction = *Instruction::At(code_ptr);
- if (instruction.IsBranch()) {
- (*number_of_branches)++;
- int32_t target = instruction.GetTargetOffset() + dex_pc;
- // Create a block for the target instruction.
- FindOrCreateBlockStartingAt(target);
-
- dex_pc += instruction.SizeInCodeUnits();
- code_ptr += instruction.SizeInCodeUnits();
-
- if (instruction.CanFlowThrough()) {
- if (code_ptr >= code_end) {
- // 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 (instruction.IsSwitch()) {
- SwitchTable table(instruction, dex_pc, instruction.Opcode() == Instruction::SPARSE_SWITCH);
-
- uint16_t num_entries = table.GetNumEntries();
-
- // In a packed-switch, the entry at index 0 is the starting key. In a sparse-switch, the
- // entry at index 0 is the first key, and values are after *all* keys.
- size_t offset = table.GetFirstValueIndex();
-
- // Use a larger loop counter type to avoid overflow issues.
- 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);
-
- // Create a block for the switch-case logic. The block gets the dex_pc
- // of the SWITCH instruction because it is part of its semantics.
- block = new (arena_) HBasicBlock(graph_, dex_pc);
- branch_targets_[table.GetDexPcForIndex(i)] = block;
- }
-
- // Fall-through. Add a block if there is more code afterwards.
- dex_pc += instruction.SizeInCodeUnits();
- code_ptr += instruction.SizeInCodeUnits();
- if (code_ptr >= code_end) {
- // 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.
- // (A switch can fall-through so we don't need to check CanFlowThrough().)
- return false;
- } else {
- FindOrCreateBlockStartingAt(dex_pc);
- }
- } else {
- code_ptr += instruction.SizeInCodeUnits();
- dex_pc += instruction.SizeInCodeUnits();
- }
- }
- return true;
-}
-
-HBasicBlock* HGraphBuilder::FindBlockStartingAt(int32_t dex_pc) const {
- DCHECK_GE(dex_pc, 0);
- return branch_targets_[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_[dex_pc] = block;
- }
- return block;
-}
-
template<typename T>
void HGraphBuilder::Unop_12x(const Instruction& instruction,
Primitive::Type type,
@@ -645,6 +396,43 @@ static bool RequiresConstructorBarrier(const DexCompilationUnit* cu, const Compi
&& driver.RequiresConstructorBarrier(self, cu->GetDexFile(), cu->GetClassDefIndex());
}
+// Returns true if `block` has only one successor which starts at the next
+// dex_pc after `instruction` at `dex_pc`.
+static bool IsFallthroughInstruction(const Instruction& instruction,
+ uint32_t dex_pc,
+ HBasicBlock* block) {
+ uint32_t next_dex_pc = dex_pc + instruction.SizeInCodeUnits();
+ return block->GetSingleSuccessor()->GetDexPc() == next_dex_pc;
+}
+
+void HGraphBuilder::BuildSwitch(const Instruction& instruction, uint32_t dex_pc) {
+ HInstruction* value = LoadLocal(instruction.VRegA(), Primitive::kPrimInt, dex_pc);
+ DexSwitchTable table(instruction, dex_pc);
+
+ if (table.GetNumEntries() == 0) {
+ // Empty Switch. Code falls through to the next block.
+ DCHECK(IsFallthroughInstruction(instruction, dex_pc, current_block_));
+ current_block_->AddInstruction(new (arena_) HGoto(dex_pc));
+ } else if (table.ShouldBuildDecisionTree()) {
+ for (DexSwitchTableIterator it(table); !it.Done(); it.Advance()) {
+ HInstruction* case_value = graph_->GetIntConstant(it.CurrentKey(), dex_pc);
+ HEqual* comparison = new (arena_) HEqual(value, case_value, dex_pc);
+ current_block_->AddInstruction(comparison);
+ HInstruction* ifinst = new (arena_) HIf(comparison, dex_pc);
+ current_block_->AddInstruction(ifinst);
+
+ if (!it.IsLast()) {
+ current_block_ = FindBlockStartingAt(it.GetDexPcForCurrentIndex());
+ }
+ }
+ } else {
+ current_block_->AddInstruction(
+ new (arena_) HPackedSwitch(table.GetEntryAt(0), table.GetNumEntries(), value, dex_pc));
+ }
+
+ current_block_ = nullptr;
+}
+
void HGraphBuilder::BuildReturn(const Instruction& instruction,
Primitive::Type type,
uint32_t dex_pc) {
@@ -662,7 +450,6 @@ void HGraphBuilder::BuildReturn(const Instruction& instruction,
HInstruction* value = LoadLocal(instruction.VRegA(), type, dex_pc);
current_block_->AddInstruction(new (arena_) HReturn(value, dex_pc));
}
- current_block_->AddSuccessor(exit_block_);
current_block_ = nullptr;
}
@@ -1697,130 +1484,6 @@ bool HGraphBuilder::NeedsAccessCheck(uint32_t type_index, bool* finalizable) con
dex_compilation_unit_->GetDexMethodIndex(), *dex_file_, type_index, finalizable);
}
-void HGraphBuilder::BuildSwitchJumpTable(const SwitchTable& table,
- const Instruction& instruction,
- HInstruction* value,
- uint32_t dex_pc) {
- // Add the successor blocks to the current block.
- uint16_t num_entries = table.GetNumEntries();
- for (size_t i = 1; i <= num_entries; i++) {
- int32_t target_offset = table.GetEntryAt(i);
- HBasicBlock* case_target = FindBlockStartingAt(dex_pc + target_offset);
- DCHECK(case_target != nullptr);
-
- // Add the target block as a successor.
- current_block_->AddSuccessor(case_target);
- }
-
- // Add the default target block as the last successor.
- HBasicBlock* default_target = FindBlockStartingAt(dex_pc + instruction.SizeInCodeUnits());
- DCHECK(default_target != nullptr);
- current_block_->AddSuccessor(default_target);
-
- // Now add the Switch instruction.
- int32_t starting_key = table.GetEntryAt(0);
- current_block_->AddInstruction(
- new (arena_) HPackedSwitch(starting_key, num_entries, value, dex_pc));
- // This block ends with control flow.
- current_block_ = nullptr;
-}
-
-void HGraphBuilder::BuildPackedSwitch(const Instruction& instruction, uint32_t dex_pc) {
- // Verifier guarantees that the payload for PackedSwitch contains:
- // (a) number of entries (may be zero)
- // (b) first and lowest switch case value (entry 0, always present)
- // (c) list of target pcs (entries 1 <= i <= N)
- SwitchTable table(instruction, dex_pc, false);
-
- // Value to test against.
- HInstruction* value = LoadLocal(instruction.VRegA(), Primitive::kPrimInt, dex_pc);
-
- // Starting key value.
- int32_t starting_key = table.GetEntryAt(0);
-
- // Retrieve number of entries.
- uint16_t num_entries = table.GetNumEntries();
- if (num_entries == 0) {
- return;
- }
-
- // Don't use a packed switch if there are very few entries.
- if (num_entries > kSmallSwitchThreshold) {
- BuildSwitchJumpTable(table, instruction, value, dex_pc);
- } else {
- // Chained cmp-and-branch, starting from starting_key.
- for (size_t i = 1; i <= num_entries; i++) {
- BuildSwitchCaseHelper(instruction,
- i,
- i == num_entries,
- table,
- value,
- starting_key + i - 1,
- table.GetEntryAt(i),
- dex_pc);
- }
- }
-}
-
-void HGraphBuilder::BuildSparseSwitch(const Instruction& instruction, uint32_t dex_pc) {
- // Verifier guarantees that the payload for SparseSwitch contains:
- // (a) number of entries (may be zero)
- // (b) sorted key values (entries 0 <= i < N)
- // (c) target pcs corresponding to the switch values (entries N <= i < 2*N)
- SwitchTable table(instruction, dex_pc, true);
-
- // Value to test against.
- HInstruction* value = LoadLocal(instruction.VRegA(), Primitive::kPrimInt, dex_pc);
-
- uint16_t num_entries = table.GetNumEntries();
-
- for (size_t i = 0; i < num_entries; i++) {
- BuildSwitchCaseHelper(instruction, i, i == static_cast<size_t>(num_entries) - 1, table, value,
- table.GetEntryAt(i), table.GetEntryAt(i + num_entries), dex_pc);
- }
-}
-
-void HGraphBuilder::BuildSwitchCaseHelper(const Instruction& instruction, size_t index,
- bool is_last_case, const SwitchTable& table,
- HInstruction* value, int32_t case_value_int,
- int32_t target_offset, uint32_t dex_pc) {
- HBasicBlock* case_target = FindBlockStartingAt(dex_pc + target_offset);
- DCHECK(case_target != nullptr);
-
- // The current case's value.
- HInstruction* this_case_value = graph_->GetIntConstant(case_value_int, dex_pc);
-
- // Compare value and this_case_value.
- HEqual* comparison = new (arena_) HEqual(value, this_case_value, dex_pc);
- current_block_->AddInstruction(comparison);
- HInstruction* ifinst = new (arena_) HIf(comparison, dex_pc);
- current_block_->AddInstruction(ifinst);
-
- // Case hit: use the target offset to determine where to go.
- current_block_->AddSuccessor(case_target);
-
- // Case miss: go to the next case (or default fall-through).
- // When there is a next case, we use the block stored with the table offset representing this
- // case (that is where we registered them in ComputeBranchTargets).
- // When there is no next case, we use the following instruction.
- // TODO: Find a good way to peel the last iteration to avoid conditional, but still have re-use.
- if (!is_last_case) {
- HBasicBlock* next_case_target = FindBlockStartingAt(table.GetDexPcForIndex(index));
- DCHECK(next_case_target != nullptr);
- current_block_->AddSuccessor(next_case_target);
-
- // Need to manually add the block, as there is no dex-pc transition for the cases.
- graph_->AddBlock(next_case_target);
-
- current_block_ = next_case_target;
- } else {
- HBasicBlock* default_target = FindBlockStartingAt(dex_pc + instruction.SizeInCodeUnits());
- DCHECK(default_target != nullptr);
- current_block_->AddSuccessor(default_target);
- current_block_ = nullptr;
- }
-}
-
bool HGraphBuilder::CanDecodeQuickenedInfo() const {
return interpreter_metadata_ != nullptr;
}
@@ -1833,10 +1496,6 @@ uint16_t HGraphBuilder::LookupQuickenedInfo(uint32_t dex_pc) {
}
bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32_t dex_pc) {
- if (current_block_ == nullptr) {
- return true; // Dead code
- }
-
switch (instruction.Opcode()) {
case Instruction::CONST_4: {
int32_t register_index = instruction.VRegA();
@@ -1949,11 +1608,7 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32
case Instruction::GOTO:
case Instruction::GOTO_16:
case Instruction::GOTO_32: {
- int32_t offset = instruction.GetTargetOffset();
- HBasicBlock* target = FindBlockStartingAt(offset + dex_pc);
- DCHECK(target != nullptr);
current_block_->AddInstruction(new (arena_) HGoto(dex_pc));
- current_block_->AddSuccessor(target);
current_block_ = nullptr;
break;
}
@@ -2793,8 +2448,6 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32
case Instruction::THROW: {
HInstruction* exception = LoadLocal(instruction.VRegA_11x(), Primitive::kPrimNot, dex_pc);
current_block_->AddInstruction(new (arena_) HThrow(exception, dex_pc));
- // A throw instruction must branch to the exit block.
- current_block_->AddSuccessor(exit_block_);
// We finished building this block. Set the current block to null to avoid
// adding dead instructions to it.
current_block_ = nullptr;
@@ -2832,13 +2485,9 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32
break;
}
+ case Instruction::SPARSE_SWITCH:
case Instruction::PACKED_SWITCH: {
- BuildPackedSwitch(instruction, dex_pc);
- break;
- }
-
- case Instruction::SPARSE_SWITCH: {
- BuildSparseSwitch(instruction, dex_pc);
+ BuildSwitch(instruction, dex_pc);
break;
}